Skip to content

Commit 2b5f3c8

Browse files
committed
Add main display skeleton overlay tests
Introduce a new test module for main window display behavior focused on skeleton overlays. The tests cover default skeleton UI values, restoring skeleton settings from config, collecting visualization settings from UI, delegating frame rendering to the overlay renderer, clearing overlay runtime state when inference starts, and an end-to-end renderer check that verifies skeleton lines are drawn without mutating the input frame.
1 parent 0199633 commit 2b5f3c8

1 file changed

Lines changed: 203 additions & 0 deletions

File tree

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass
4+
5+
import numpy as np
6+
from PySide6.QtGui import QPixmap
7+
8+
from dlclivegui.config import (
9+
SkeletonColorMode,
10+
SkeletonStyle,
11+
)
12+
from dlclivegui.display.overlays import (
13+
BoundingBoxOverlaySettings,
14+
OverlayRenderer,
15+
OverlayRenderResult,
16+
OverlaySettings,
17+
PoseOverlaySettings,
18+
SkeletonOverlaySettings,
19+
)
20+
from dlclivegui.gui.misc import color_dropdowns as color_ui
21+
22+
23+
def test_skeleton_controls_use_default_settings(window) -> None:
24+
assert not window.show_skeleton_checkbox.isChecked()
25+
assert window.skeleton_thickness_spin.value() == 2
26+
27+
mode, color = (
28+
window._skeleton_style_from_ui().color_mode,
29+
window._skeleton_style_from_ui().color_bgr,
30+
)
31+
32+
assert mode == SkeletonColorMode.SOLID
33+
assert color == (0, 255, 255)
34+
35+
36+
def test_apply_config_restores_skeleton_settings(
37+
window,
38+
) -> None:
39+
config = window._config.model_copy(deep=True)
40+
config.visualization.show_pose = False
41+
config.visualization.show_skeleton = True
42+
config.visualization.skeleton_style = SkeletonStyle(
43+
color_mode=SkeletonColorMode.GRADIENT_KEYPOINTS,
44+
thickness=6,
45+
gradient_steps=24,
46+
scale_with_zoom=False,
47+
)
48+
49+
window._apply_config(config)
50+
51+
assert not window.show_predictions_checkbox.isChecked()
52+
assert window.show_skeleton_checkbox.isChecked()
53+
assert window.skeleton_thickness_spin.value() == 6
54+
55+
style = window._skeleton_style_from_ui()
56+
assert style.color_mode == SkeletonColorMode.GRADIENT_KEYPOINTS
57+
assert style.gradient_steps == 24
58+
assert style.scale_with_zoom is False
59+
60+
61+
def test_visualization_settings_from_ui_include_skeleton(
62+
window,
63+
) -> None:
64+
window.show_predictions_checkbox.setChecked(False)
65+
window.show_skeleton_checkbox.setChecked(True)
66+
window.skeleton_thickness_spin.setValue(5)
67+
68+
color_ui.set_skeleton_combo_from_style(
69+
window.skeleton_color_combo,
70+
mode="solid",
71+
color=(0, 255, 0),
72+
)
73+
74+
settings = window._visualization_settings_from_ui()
75+
76+
assert settings.show_pose is False
77+
assert settings.show_skeleton is True
78+
assert settings.skeleton_style.thickness == 5
79+
assert settings.skeleton_style.color_bgr == (0, 255, 0)
80+
81+
82+
def test_update_video_display_delegates_to_overlay_renderer(
83+
window,
84+
monkeypatch,
85+
) -> None:
86+
frame = np.zeros((20, 30, 3), dtype=np.uint8)
87+
calls = []
88+
89+
def fake_render(
90+
rendered_frame,
91+
**kwargs,
92+
):
93+
calls.append((rendered_frame, kwargs))
94+
return OverlayRenderResult(
95+
frame=rendered_frame.copy(),
96+
)
97+
98+
monkeypatch.setattr(
99+
window._overlay_renderer,
100+
"render",
101+
fake_render,
102+
)
103+
monkeypatch.setattr(
104+
"dlclivegui.gui.main_window.frame_to_pixmap",
105+
lambda _frame, _size: QPixmap(10, 10),
106+
)
107+
108+
window._update_video_display(frame)
109+
110+
assert len(calls) == 1
111+
assert calls[0][1]["offset"] == window._dlc_tile_offset
112+
assert calls[0][1]["scale"] == window._dlc_tile_scale
113+
114+
115+
def test_start_inference_clears_overlay_runtime_state(
116+
window,
117+
monkeypatch,
118+
) -> None:
119+
cleared = False
120+
121+
def fake_clear() -> None:
122+
nonlocal cleared
123+
cleared = True
124+
125+
monkeypatch.setattr(
126+
window._overlay_renderer,
127+
"clear_runtime_state",
128+
fake_clear,
129+
)
130+
monkeypatch.setattr(
131+
window.multi_camera_controller,
132+
"is_running",
133+
lambda: True,
134+
)
135+
monkeypatch.setattr(
136+
window,
137+
"_configure_dlc",
138+
lambda: True,
139+
)
140+
monkeypatch.setattr(
141+
window._dlc,
142+
"reset",
143+
lambda: None,
144+
)
145+
146+
window._start_inference()
147+
148+
assert cleared
149+
150+
151+
@dataclass
152+
class PacketStub:
153+
keypoint_names: list[str] | None
154+
skeleton_id: str | None
155+
skeleton_edges: tuple[tuple[str, str], ...] | None
156+
157+
158+
def test_end_to_end_skeleton_overlay() -> None:
159+
renderer = OverlayRenderer()
160+
frame = np.zeros((64, 64, 3), dtype=np.uint8)
161+
pose = np.array(
162+
[
163+
[10.0, 10.0, 0.9],
164+
[50.0, 50.0, 0.9],
165+
],
166+
dtype=np.float32,
167+
)
168+
packet = PacketStub(
169+
keypoint_names=["start", "end"],
170+
skeleton_id="test.line",
171+
skeleton_edges=(("start", "end"),),
172+
)
173+
settings = OverlaySettings(
174+
pose=PoseOverlaySettings(
175+
visible=False,
176+
p_cutoff=0.5,
177+
colormap="viridis",
178+
),
179+
skeleton=SkeletonOverlaySettings(
180+
visible=True,
181+
style=SkeletonStyle(
182+
color_bgr=(0, 255, 0),
183+
thickness=2,
184+
scale_with_zoom=False,
185+
),
186+
),
187+
bounding_box=BoundingBoxOverlaySettings(
188+
visible=False,
189+
coordinates=(0, 0, 0, 0),
190+
color_bgr=(0, 0, 255),
191+
),
192+
)
193+
194+
result = renderer.render(
195+
frame,
196+
pose=pose,
197+
packet=packet,
198+
overlay_settings=settings,
199+
)
200+
201+
assert result.warning is None
202+
assert np.any(result.frame != 0)
203+
assert not np.any(frame)

0 commit comments

Comments
 (0)