"""Tests for capture region-of-interest (ROI) cropping.""" import numpy as np from ledgrab.core.capture.calibration import ( CalibrationConfig, calibration_from_dict, calibration_to_dict, ) from ledgrab.core.capture.screen_capture import ScreenCapture, crop_screen_capture def _cfg(**kw) -> CalibrationConfig: return CalibrationConfig(layout="clockwise", start_position="bottom_left", leds_top=10, **kw) def _sc(w: int = 100, h: int = 80) -> ScreenCapture: return ScreenCapture( image=np.zeros((h, w, 3), dtype=np.uint8), width=w, height=h, display_index=0 ) def test_full_frame_returns_same_object(): sc = _sc() assert crop_screen_capture(sc, 0.0, 0.0, 1.0, 1.0) is sc def test_center_crop_dimensions(): out = crop_screen_capture(_sc(100, 80), 0.25, 0.25, 0.5, 0.5) assert out.width == 50 and out.height == 40 assert out.image.shape[:2] == (40, 50) assert out.display_index == 0 def test_crop_returns_a_view_of_the_source(): sc = _sc(100, 80) out = crop_screen_capture(sc, 0.0, 0.0, 0.5, 0.5) out.image[0, 0] = (9, 9, 9) assert (sc.image[0, 0] == 9).all() # mutating the view touches the source pixels def test_partial_width_only_keeps_full_height(): out = crop_screen_capture(_sc(100, 80), 0.1, 0.0, 0.8, 1.0) assert out.width == 80 and out.height == 80 def test_degenerate_roi_clamped_to_at_least_one_pixel(): out = crop_screen_capture(_sc(100, 80), 0.999, 0.999, 0.0, 0.0) assert out.width >= 1 and out.height >= 1 def test_out_of_range_roi_is_clamped(): out = crop_screen_capture(_sc(100, 80), -0.5, -0.5, 2.0, 2.0) # x<=0,y<=0,w>=1,h>=1 hits the full-frame fast path assert out.width == 100 and out.height == 80 # --- CalibrationConfig ROI serialization --- def test_has_roi_property(): assert _cfg().has_roi is False assert _cfg(roi_width=0.5).has_roi is True assert _cfg(roi_x=0.1).has_roi is True assert _cfg(roi_height=0.9).has_roi is True def test_roi_round_trips_through_dict(): cfg = _cfg(roi_x=0.1, roi_y=0.2, roi_width=0.6, roi_height=0.7) d = calibration_to_dict(cfg) assert d["roi_x"] == 0.1 and d["roi_width"] == 0.6 back = calibration_from_dict(d) assert (back.roi_x, back.roi_y, back.roi_width, back.roi_height) == (0.1, 0.2, 0.6, 0.7) def test_full_frame_roi_omitted_from_dict(): d = calibration_to_dict(_cfg()) assert "roi_x" not in d and "roi_width" not in d def test_legacy_dict_without_roi_defaults_to_full_frame(): cfg = calibration_from_dict( {"mode": "simple", "layout": "clockwise", "start_position": "bottom_left", "leds_top": 10} ) assert cfg.has_roi is False assert (cfg.roi_x, cfg.roi_y, cfg.roi_width, cfg.roi_height) == (0.0, 0.0, 1.0, 1.0)