Lint the commits being pushed and scope overlay failures per sensor
The pre-push gate linted only the dirty tree, so the commits about to be pushed went unchecked; and every failed overlay projection was sticky, which cost a finger result its overlay over one bad frame while box results were retried nowhere. gate.ps1 now takes -LintBase (default origin/main, exit 2 on an unknown ref), the projector declares whether its own failures are permanent, and serve_stored_view decides before it sets the view. Verified with gate.ps1 (PASS: 569 studio + 485 shared tests, 632 ui_check checks) and the two round-2 red samples kept in scratch.
This commit is contained in:
@@ -389,8 +389,13 @@ class AnalysisSession:
|
||||
"""The live-shaped processed image for this camera frame, or ``None``.
|
||||
|
||||
Opens a session-scoped projector from the result's provenance on first use.
|
||||
Failure is sticky for the session: overlay views then report capture
|
||||
unavailable rather than falling back to a resized fisheye.
|
||||
**How long a failure lasts is the projector's answer, not this method's guess:**
|
||||
a projector that declares its projection failures permanent (a box stitch that
|
||||
cannot rebuild the lenslet grid on this footage) is recorded as unavailable and
|
||||
not asked again; one that declares them transient (a finger unroll that lost a
|
||||
single frame) skips that frame and is tried again on the next. The alternative —
|
||||
making every failure sticky — cost a finger result its whole overlay view over
|
||||
one bad frame (reported as R2 in docs/test-repair-rework.zh.md).
|
||||
"""
|
||||
if self._overlay_failed:
|
||||
return None
|
||||
@@ -401,16 +406,11 @@ class AnalysisSession:
|
||||
self.reader.provenance
|
||||
)
|
||||
if self._overlay is None:
|
||||
# Nothing can be opened for this result at all: permanent either way.
|
||||
self._overlay_failed = True
|
||||
return None
|
||||
projected = self._overlay.project(video_bgr, frame_id=frame_id)
|
||||
if projected is None:
|
||||
# Sticky, as the docstring above says. A projector that cannot map this
|
||||
# result's footage — measured: every frame of a real box recording, where the
|
||||
# stitch rebuild raises inside the SDK — will not start working mid-session,
|
||||
# and retrying per cursor move would rebuild the same failing stitch on every
|
||||
# scrub. The session records the answer so a caller can pick a view this
|
||||
# result *can* draw instead of leaving a refusal on screen.
|
||||
if projected is None and self._overlay.projection_failures_are_permanent:
|
||||
self._overlay_failed = True
|
||||
return projected
|
||||
|
||||
@@ -420,12 +420,12 @@ class AnalysisSession:
|
||||
|
||||
The overlay views (``arrows_stitched``, ``depth_arrows``) paint on the same
|
||||
UV-unrolled or stitched picture live publishes, which a stored result does not
|
||||
contain: it is rebuilt from the result's provenance on first use. A **box**
|
||||
result has no stored unroll map, and the box stitch rebuild can fail on real
|
||||
footage, so the answer is only known after one attempt — and it is then sticky
|
||||
for the session (see :meth:`overlay_base`). The caller that *picks* the first
|
||||
view for an adopted result reads this, so the operator is not left on a view
|
||||
that cannot be drawn.
|
||||
contain: it is rebuilt from the result's provenance on first use. The answer is
|
||||
only known after an attempt, and it is sticky **only for a failure the projector
|
||||
declares permanent** — a box result whose stitch cannot rebuild, or a projector
|
||||
that would not open at all. A finger unroll that loses one frame is not sticky
|
||||
(see :meth:`overlay_base`). The caller that *picks* the first view for an adopted
|
||||
result reads this, so the operator is not left on a view that cannot be drawn.
|
||||
"""
|
||||
return bool(self._overlay_failed)
|
||||
|
||||
|
||||
@@ -1034,13 +1034,21 @@ class ReplayController(QObject):
|
||||
texture live uses. A derived selection the operator already chose is left
|
||||
alone — that mode is not this method's to override.
|
||||
|
||||
**But only a view this result can actually draw.** The base is rebuilt from the
|
||||
result's provenance, which a box result cannot always do (no stored unroll map,
|
||||
and the box stitch rebuild can fail on real footage: measured, every frame of the
|
||||
restored 33-frame box recording). Offering the overlay there would leave the
|
||||
operator on a blank panel reading "capture unavailable" while the pure-colour
|
||||
arrows sit one step away, so the fallback is the drawable view — the overlay is
|
||||
still in the selector, and choosing it still says why it cannot be drawn.
|
||||
**But only a view this result can actually draw, decided before it is set.** The
|
||||
base is rebuilt from the result's provenance, which a box result cannot always do
|
||||
(no stored unroll map, and the box stitch rebuild can fail on real footage:
|
||||
measured, every frame of the restored 33-frame box recording). Offering the
|
||||
overlay there would leave the operator on a blank panel reading "capture
|
||||
unavailable" while the pure-colour arrows sit one step away, so the fallback is
|
||||
the drawable view — the overlay is still in the selector, and choosing it still
|
||||
says why it cannot be drawn.
|
||||
|
||||
The probe runs *before* the view is set (see :meth:`_overlay_base_ready`) so the
|
||||
panel is set once and drawn once: setting the overlay view first and switching
|
||||
after the first render flashed the refusal on screen during adoption (R3 of
|
||||
docs/test-repair-rework.zh.md). The base it asks for is the one the render that
|
||||
follows will ask for, and the projector caches by frame id, so deciding costs no
|
||||
second projection.
|
||||
"""
|
||||
current = str(getattr(self._image_panel, "view_mode", lambda: "")() or "")
|
||||
if current in DERIVED_MODES:
|
||||
@@ -1048,16 +1056,29 @@ class ReplayController(QObject):
|
||||
session = self.available_result()
|
||||
# The plan for the frame just drawn offers the derived modes (the frame carries
|
||||
# ``result_available``), so this selection is one the panel can honour.
|
||||
overlay_failed = bool(getattr(session, "overlay_unavailable", False))
|
||||
self._image_panel.set_view_mode(ARROW_PURE if overlay_failed else ARROW_STITCHED)
|
||||
drawable = True if session is None else self._overlay_base_ready(session)
|
||||
self._image_panel.set_view_mode(ARROW_STITCHED if drawable else ARROW_PURE)
|
||||
self.render_frame()
|
||||
if not overlay_failed and getattr(session, "overlay_unavailable", False):
|
||||
# The first attempt is what tells us: the base could not be rebuilt, so draw
|
||||
# the view that needs no base instead of leaving a refusal on screen.
|
||||
self._image_panel.set_view_mode(ARROW_PURE)
|
||||
self.render_frame()
|
||||
return True
|
||||
|
||||
def _overlay_base_ready(self, session) -> bool:
|
||||
"""Whether ``render_frame`` can paint this result's overlay base, without drawing.
|
||||
|
||||
Asks the session for the same base, for the same frame, that the render will ask
|
||||
for. ``True`` when there is no frame to judge: an unknown answer must not invent a
|
||||
fallback view the operator did not ask for. A session that has already declared the
|
||||
overlay unavailable is answered without another projection attempt.
|
||||
"""
|
||||
if bool(getattr(session, "overlay_unavailable", False)):
|
||||
return False
|
||||
if self.analysis_active():
|
||||
frame = self._engine.analysed_frame()
|
||||
else:
|
||||
frame = session.frame_at(self._engine.position())
|
||||
if frame is None:
|
||||
return True
|
||||
return self._processed_base_for(frame, session) is not None
|
||||
|
||||
def _display_policy(self) -> Any:
|
||||
from orisys_app_shared.scene3d.visual_policy import policy_for_settings
|
||||
|
||||
|
||||
Reference in New Issue
Block a user