Initial project import
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
tests/test_fingerprinting.py — Unit tests for src/cv/fingerprinting.py
|
||||
|
||||
Tests run WITHOUT requiring real video files.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from src.cv.fingerprinting import (
|
||||
text_safe_crop,
|
||||
extract_hs_histograms,
|
||||
compare_histograms,
|
||||
hist_to_bytes,
|
||||
bytes_to_hist,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pytest.fixture
|
||||
def solid_blue_frame() -> np.ndarray:
|
||||
"""256×256 solid blue BGR frame."""
|
||||
frame = np.zeros((256, 256, 3), dtype=np.uint8)
|
||||
frame[:, :] = (255, 0, 0) # BGR blue
|
||||
return frame
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def solid_red_frame() -> np.ndarray:
|
||||
"""256×256 solid red BGR frame."""
|
||||
frame = np.zeros((256, 256, 3), dtype=np.uint8)
|
||||
frame[:, :] = (0, 0, 255) # BGR red
|
||||
return frame
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# text_safe_crop
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestTextSafeCrop:
|
||||
def test_removes_correct_rows(self, solid_blue_frame: np.ndarray) -> None:
|
||||
cropped = text_safe_crop(solid_blue_frame, crop_top=0.15, crop_bottom=0.30)
|
||||
h = solid_blue_frame.shape[0] # 256
|
||||
expected_h = int(h * (1.0 - 0.30)) - int(h * 0.15)
|
||||
assert cropped.shape[0] == expected_h
|
||||
|
||||
def test_zero_crop_returns_same_size(self, solid_blue_frame: np.ndarray) -> None:
|
||||
cropped = text_safe_crop(solid_blue_frame, crop_top=0.0, crop_bottom=0.0)
|
||||
assert cropped.shape == solid_blue_frame.shape
|
||||
|
||||
def test_invalid_top_raises(self, solid_blue_frame: np.ndarray) -> None:
|
||||
with pytest.raises(ValueError, match="crop_top"):
|
||||
text_safe_crop(solid_blue_frame, crop_top=1.0, crop_bottom=0.0)
|
||||
|
||||
def test_invalid_bottom_raises(self, solid_blue_frame: np.ndarray) -> None:
|
||||
with pytest.raises(ValueError, match="crop_bottom"):
|
||||
text_safe_crop(solid_blue_frame, crop_top=0.0, crop_bottom=-0.1)
|
||||
|
||||
def test_overlapping_crops_raise(self, solid_blue_frame: np.ndarray) -> None:
|
||||
with pytest.raises(ValueError, match="must be < 1.0"):
|
||||
text_safe_crop(solid_blue_frame, crop_top=0.6, crop_bottom=0.5)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestHistograms:
|
||||
def test_output_shape(self, solid_blue_frame: np.ndarray) -> None:
|
||||
luma, sat = extract_hs_histograms(solid_blue_frame, bins_hue=50, bins_sat=60)
|
||||
assert luma.shape == (50,)
|
||||
assert sat.shape == (60,)
|
||||
|
||||
def test_normalised(self, solid_blue_frame: np.ndarray) -> None:
|
||||
import numpy as np
|
||||
luma, sat = extract_hs_histograms(solid_blue_frame, bins_hue=50, bins_sat=60)
|
||||
# L2-normalised → norm ≈ 1.0
|
||||
assert np.linalg.norm(luma) == pytest.approx(1.0, abs=1e-5)
|
||||
assert np.linalg.norm(sat) == pytest.approx(1.0, abs=1e-5)
|
||||
|
||||
def test_same_frame_correl_is_one(self, solid_blue_frame: np.ndarray) -> None:
|
||||
import cv2
|
||||
luma, _ = extract_hs_histograms(solid_blue_frame, bins_hue=50, bins_sat=60)
|
||||
score = compare_histograms(luma, luma, method=cv2.HISTCMP_CORREL)
|
||||
assert score == pytest.approx(1.0, abs=1e-5)
|
||||
|
||||
def test_different_frames_correl_lower(
|
||||
self,
|
||||
solid_blue_frame: np.ndarray,
|
||||
solid_red_frame: np.ndarray,
|
||||
) -> None:
|
||||
import cv2
|
||||
luma_b, _ = extract_hs_histograms(solid_blue_frame, 50, 60)
|
||||
luma_r, _ = extract_hs_histograms(solid_red_frame, 50, 60)
|
||||
score = compare_histograms(luma_b, luma_r, method=cv2.HISTCMP_CORREL)
|
||||
assert score < 1.0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Serialisation round-trip
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestSerialisation:
|
||||
def test_round_trip(self, solid_blue_frame: np.ndarray) -> None:
|
||||
luma, _ = extract_hs_histograms(solid_blue_frame, 50, 60)
|
||||
restored = bytes_to_hist(hist_to_bytes(luma))
|
||||
np.testing.assert_array_almost_equal(luma, restored)
|
||||
Reference in New Issue
Block a user