fixed display problem

This commit is contained in:
ykcheungab
2026-09-25 17:53:07 +08:00
parent 12781854ca
commit 3daee6d407
7 changed files with 367 additions and 5 deletions
+28 -1
View File
@@ -30,6 +30,7 @@ from orisys_app_shared.acquisition.image_backend import (
SOURCE_LIVE,
SOURCE_PREVIEW,
SOURCE_RECOMPUTE,
DomainMaskLatch,
ImageFrame,
plan_image,
render_image,
@@ -222,6 +223,11 @@ class StudioMainWindow(QMainWindow):
self._last_calibration_export = ""
self._depth_range_value: tuple[float, float] | None = None
self._last_depth_frame = None
#: The 2D domain boundary, latched for the session the way the depth range is:
#: the island is the device's own geometry and is rebuilt from the same remap
#: every frame, so re-deriving the edge per frame redrew a boundary that had not
#: moved (see ``image_backend.DomainMaskLatch``).
self._domain_mask = DomainMaskLatch()
icon = QIcon(str(logo_path()))
if not icon.isNull():
@@ -2028,6 +2034,10 @@ class StudioMainWindow(QMainWindow):
self._last_display_available = None
self._depth_range_value = None
self._last_depth_frame = None
# The new session has not established a domain boundary yet; the previous
# session's island must not survive into it (a different sensor is a different
# island, and the latch compares against what it holds).
self._domain_mask.reset()
# The session's own bookkeeping (its counter and telemetry) is the
# controller's: telling it a run starts is what drops the previous run's
# numbers, so none of them can be read as this run's.
@@ -2461,6 +2471,19 @@ class StudioMainWindow(QMainWindow):
return ""
return t("panels.depth_scale", low=f"{span[0]:.3f}", high=f"{span[1]:.3f}")
def _latched_domain_mask(self, frame: "ImageFrame"):
"""The 2D domain boundary to draw, latched for the session.
Exactly the treatment ``_depth_range`` gives the colour scale, and for the same
reason: the boundary is not a per-frame measurement. It is the device's own UV
island — geometry, rebuilt from the same remap on every frame — and the depth
view paints it as its "no depth" grey, so rebuilding it per frame redrew an edge
that had not moved while any difference in ``frame.valid_mask`` repainted the
whole boundary, which reads as lag. The latch holds the boundary until a frame
no longer describes the same island (see ``DomainMaskLatch``).
"""
return self._domain_mask.update(frame.valid_mask)
def _replay_owns_image(self) -> bool:
"""True while the 2D panel shows the replay, not the live camera.
@@ -2518,7 +2541,10 @@ class StudioMainWindow(QMainWindow):
draw_style = self._draw_style()
# The legend is decided by the same inputs as the picture: the arrow
# magnification of this sensor's draw style, and whether this frame
# carries a valid-area mask.
# carries a valid-area mask. It stays the *frame's* statement rather than the
# latched boundary's, because that is what the chip claims: the arrows are
# gated by this frame's own mask on every path, latched boundary or not.
boundary = self._latched_domain_mask(frame)
self.arrow_panel.set_arrow_scale(arrow_display_scale(draw_style))
self.arrow_panel.set_valid_area(frame.valid_mask is not None)
if mode == DEPTH:
@@ -2534,6 +2560,7 @@ class StudioMainWindow(QMainWindow):
draw_style=draw_style,
depth_range=self._depth_range(),
canvas_bgr=hex_to_bgr(color(self._theme, "arrow_canvas", "#0B1830")),
domain_mask=boundary,
)
if rendered is None:
# Never substitute another picture here: a missing depth map that showed
+53
View File
@@ -925,6 +925,59 @@ def test_depth_view_reports_missing_data_and_fixes_its_scale(make_window):
_finish(window, stuck)
def test_the_domain_boundary_is_latched_for_the_session(make_window):
"""The 2D boundary is the device's island: rebuilt per frame, decided once.
Same treatment as the depth colour scale above, and for the same reason — a mask
that is re-derived every frame redraws an edge that has not moved, and any pixel of
difference repaints the whole boundary (measured: a 0.73% area wobble repaints 69%
of the boundary band).
"""
import numpy as np
window, stuck = _running_window(make_window)
window.arrow_panel.set_view_mode("arrows_pure")
island = np.zeros((8, 8), dtype=bool)
island[1:7, 1:7] = True
island_wobble = island.copy()
island_wobble[1, 1] = False # one pixel of rim, i.e. 1.6% of a 36-pixel island
window._last_rendered_view_mode = None
stuck.emit(ui_check._fake_packet(0, valid_mask=island))
window._on_run_tick()
assert np.array_equal(window._domain_mask.mask, island), (
"the session never established the device's island"
)
window._last_rendered_view_mode = None
stuck.emit(ui_check._fake_packet(1, valid_mask=island_wobble))
window._on_run_tick()
assert np.array_equal(window._domain_mask.mask, island), (
"a rebuilt mask moved the session's boundary"
)
assert (window._domain_mask.adoptions, window._domain_mask.held) == (1, 1)
# An island that really changed is adopted rather than frozen out.
moved = np.zeros((8, 8), dtype=bool)
moved[2:8, 2:8] = True
window._last_rendered_view_mode = None
stuck.emit(ui_check._fake_packet(2, valid_mask=moved))
window._on_run_tick()
assert np.array_equal(window._domain_mask.mask, moved), (
"a moved island kept the old boundary on screen"
)
assert window._domain_mask.adoptions == 2
# A new run is a new session: the previous island must not survive into it.
_finish(window, stuck)
window._start_run()
assert window._domain_mask.mask is None, (
"the previous session's island survived into a new run"
)
window._run_timer.stop()
window._stop_run(reason="user_stop")
# ------------------------------------------------------------- experiment record
def test_recording_writes_the_documented_experiment_layout(make_window, record_root):
import csv
+4
View File
@@ -177,6 +177,7 @@ def _fake_packet(
mono_t: float | None = None,
shear_n: float | None = None,
frame=None,
valid_mask=None,
):
"""A packet shaped like the real worker's, for the record-channel checks.
@@ -213,6 +214,9 @@ def _fake_packet(
invalid_reason=reason,
raw_available=True,
depth=depth,
# The device's own UV-island mask, which the real workers pass through from the
# runtime (``runtime.valid_mask``); the 2D boundary is drawn from it.
valid_mask=valid_mask,
# Per-field provenance: a producer has to say whether the arrays it hands over
# are this frame's own (the 3D sink refuses an unstated field rather than
# colouring something it cannot claim). A failed frame is stale here, exactly