Files
aitrailer/tests/test_fingerprinting.py
T
2026-05-02 09:07:41 +02:00

113 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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)