172 lines
5.0 KiB
Python
172 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import cast
|
|
from unittest import mock
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from opengait.demo.input import create_source
|
|
from opengait.demo.visualizer import (
|
|
DISPLAY_HEIGHT,
|
|
DISPLAY_WIDTH,
|
|
ImageArray,
|
|
OpenCVVisualizer,
|
|
)
|
|
from opengait.demo.window import select_person
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
SAMPLE_VIDEO_PATH = REPO_ROOT / "assets" / "sample.mp4"
|
|
YOLO_MODEL_PATH = REPO_ROOT / "ckpt" / "yolo11n-seg.pt"
|
|
|
|
|
|
def test_prepare_raw_view_float_mask_has_visible_signal() -> None:
|
|
viz = OpenCVVisualizer()
|
|
|
|
mask_float = np.zeros((64, 64), dtype=np.float32)
|
|
mask_float[16:48, 16:48] = 1.0
|
|
|
|
rendered = viz._prepare_raw_view(cast(ImageArray, mask_float))
|
|
assert rendered.dtype == np.uint8
|
|
assert rendered.shape == (256, 176, 3)
|
|
|
|
mask_zero = np.zeros((64, 64), dtype=np.float32)
|
|
rendered_zero = viz._prepare_raw_view(cast(ImageArray, mask_zero))
|
|
|
|
roi = slice(0, DISPLAY_HEIGHT - 40)
|
|
diff = np.abs(rendered[roi].astype(np.int16) - rendered_zero[roi].astype(np.int16))
|
|
assert int(np.count_nonzero(diff)) > 0
|
|
|
|
|
|
def test_prepare_raw_view_handles_values_slightly_above_one() -> None:
|
|
viz = OpenCVVisualizer()
|
|
|
|
mask = np.zeros((64, 64), dtype=np.float32)
|
|
mask[20:40, 20:40] = 1.0001
|
|
rendered = viz._prepare_raw_view(cast(ImageArray, mask))
|
|
|
|
roi = rendered[: DISPLAY_HEIGHT - 40, :, 0]
|
|
assert int(np.count_nonzero(roi)) > 0
|
|
|
|
|
|
def test_segmentation_view_is_normalized_only_shape() -> None:
|
|
viz = OpenCVVisualizer()
|
|
mask = np.zeros((480, 640), dtype=np.uint8)
|
|
sil = np.random.rand(64, 44).astype(np.float32)
|
|
|
|
seg = viz._prepare_segmentation_view(cast(ImageArray, mask), sil, (0, 0, 100, 100))
|
|
assert seg.shape == (DISPLAY_HEIGHT, DISPLAY_WIDTH, 3)
|
|
|
|
|
|
def test_update_toggles_raw_window_with_r_key() -> None:
|
|
viz = OpenCVVisualizer()
|
|
frame = np.zeros((240, 320, 3), dtype=np.uint8)
|
|
mask = np.zeros((240, 320), dtype=np.uint8)
|
|
mask[20:100, 30:120] = 255
|
|
sil = np.random.rand(64, 44).astype(np.float32)
|
|
seg_input = np.random.rand(4, 64, 44).astype(np.float32)
|
|
|
|
with (
|
|
mock.patch("cv2.namedWindow") as named_window,
|
|
mock.patch("cv2.imshow"),
|
|
mock.patch("cv2.destroyWindow") as destroy_window,
|
|
mock.patch("cv2.waitKey", side_effect=[ord("r"), ord("r"), ord("q")]),
|
|
):
|
|
assert viz.update(
|
|
frame,
|
|
(10, 10, 120, 150),
|
|
(10, 10, 120, 150),
|
|
1,
|
|
cast(ImageArray, mask),
|
|
sil,
|
|
seg_input,
|
|
None,
|
|
None,
|
|
15.0,
|
|
)
|
|
assert viz.show_raw_window is True
|
|
assert viz._raw_window_created is True
|
|
|
|
assert viz.update(
|
|
frame,
|
|
(10, 10, 120, 150),
|
|
(10, 10, 120, 150),
|
|
1,
|
|
cast(ImageArray, mask),
|
|
sil,
|
|
seg_input,
|
|
None,
|
|
None,
|
|
15.0,
|
|
)
|
|
assert viz.show_raw_window is False
|
|
assert viz._raw_window_created is False
|
|
assert destroy_window.called
|
|
|
|
assert (
|
|
viz.update(
|
|
frame,
|
|
(10, 10, 120, 150),
|
|
(10, 10, 120, 150),
|
|
1,
|
|
cast(ImageArray, mask),
|
|
sil,
|
|
seg_input,
|
|
None,
|
|
None,
|
|
15.0,
|
|
)
|
|
is False
|
|
)
|
|
assert named_window.called
|
|
|
|
|
|
def test_sample_video_raw_mask_shape_range_and_render_signal() -> None:
|
|
if not SAMPLE_VIDEO_PATH.is_file():
|
|
pytest.skip(f"Missing sample video: {SAMPLE_VIDEO_PATH}")
|
|
if not YOLO_MODEL_PATH.is_file():
|
|
pytest.skip(f"Missing YOLO model file: {YOLO_MODEL_PATH}")
|
|
|
|
ultralytics = pytest.importorskip("ultralytics")
|
|
yolo_cls = getattr(ultralytics, "YOLO")
|
|
|
|
viz = OpenCVVisualizer()
|
|
detector = yolo_cls(str(YOLO_MODEL_PATH))
|
|
|
|
masks_seen = 0
|
|
rendered_nonzero: list[int] = []
|
|
|
|
for frame, _meta in create_source(str(SAMPLE_VIDEO_PATH), max_frames=30):
|
|
detections = detector.track(
|
|
frame,
|
|
persist=True,
|
|
verbose=False,
|
|
classes=[0],
|
|
device="cpu",
|
|
)
|
|
if not isinstance(detections, list) or not detections:
|
|
continue
|
|
|
|
selected = select_person(detections[0])
|
|
if selected is None:
|
|
continue
|
|
|
|
mask_raw, _, _, _ = selected
|
|
masks_seen += 1
|
|
|
|
arr = np.asarray(mask_raw)
|
|
assert arr.ndim == 2
|
|
assert arr.shape[0] > 0 and arr.shape[1] > 0
|
|
assert np.issubdtype(arr.dtype, np.number)
|
|
assert float(arr.min()) >= 0.0
|
|
assert float(arr.max()) <= 255.0
|
|
assert int(np.count_nonzero(arr)) > 0
|
|
|
|
rendered = viz._prepare_raw_view(arr)
|
|
roi = rendered[: DISPLAY_HEIGHT - 40, :, 0]
|
|
rendered_nonzero.append(int(np.count_nonzero(roi)))
|
|
|
|
assert masks_seen > 0
|
|
assert min(rendered_nonzero) > 0
|