fix Studio reset, 3D base colour and arrow-trigger defects
The tracker reset was decided by which panel was on screen rather than by the session, the automatic colour range moved the 3D surface's base colour on the first press, and the 2D canvas and the 3D overlay applied different flow thresholds (622 arrows against 432 on a real recording). ui_check 600/0; apps/studio 380 items and shared 390 items with no failures; real-GL 18 items, only the pre-existing colour-scale failure; AutoCal 358 passed; both views now select identical arrows on all 42 frames of a real video.
This commit is contained in:
@@ -254,6 +254,7 @@ def draw_arrow_display(
|
||||
flow_off_px: float | None = None,
|
||||
flow_grid_spacing: int | None = None,
|
||||
max_arrows: int | None = None,
|
||||
policy=None,
|
||||
) -> np.ndarray:
|
||||
"""Draw flow arrows on a solid canvas or over the processed frame.
|
||||
|
||||
@@ -267,8 +268,11 @@ def draw_arrow_display(
|
||||
:func:`select_flow_samples`: the arrows stay this frame's own measurement, so
|
||||
latching the boundary cannot put an arrow on a sample the device does not vouch for.
|
||||
|
||||
Finger style uses the shared :mod:`visual_policy` sampler so 2D and 3D arrows
|
||||
share the same UV sample points and thresholds. Box style keeps the SDK path.
|
||||
``policy`` is the **shared** :class:`~orisys_app_shared.scene3d.visual_policy.VisualPolicy`
|
||||
the 3D surface also draws its overlay from (``visual_policy.policy_for_settings``).
|
||||
Passing it is what makes the two figures trigger at the same strength; the
|
||||
finger-style defaults below are only the fallback for a caller that has no pane
|
||||
(a tool, a test). Box style keeps the SDK path either way.
|
||||
"""
|
||||
base = _ensure_bgr(processed_frame)
|
||||
if flow is None:
|
||||
@@ -309,18 +313,22 @@ def draw_arrow_display(
|
||||
canvas = base
|
||||
|
||||
if finger:
|
||||
policy = _policy_from_style(
|
||||
draw_style,
|
||||
flow_on_px=flow_on_px,
|
||||
flow_off_px=flow_off_px,
|
||||
flow_grid_spacing=flow_grid_spacing,
|
||||
max_arrows=max_arrows,
|
||||
selected = (
|
||||
policy
|
||||
if policy is not None
|
||||
else _policy_from_style(
|
||||
draw_style,
|
||||
flow_on_px=flow_on_px,
|
||||
flow_off_px=flow_off_px,
|
||||
flow_grid_spacing=flow_grid_spacing,
|
||||
max_arrows=max_arrows,
|
||||
)
|
||||
)
|
||||
return _paint_shared_flow_arrows(
|
||||
canvas,
|
||||
np.asarray(flow),
|
||||
valid_mask=valid_mask,
|
||||
policy=policy,
|
||||
policy=selected,
|
||||
colormap=colormap,
|
||||
below_threshold_color=tuple(draw_kw["below_threshold_color"]),
|
||||
)
|
||||
|
||||
@@ -285,6 +285,7 @@ def render_image(
|
||||
depth_range: tuple[float, float] | None = None,
|
||||
canvas_bgr: Sequence[int] | None = None,
|
||||
domain_mask: np.ndarray | None = None,
|
||||
arrow_policy=None,
|
||||
) -> np.ndarray | None:
|
||||
"""Draw ``frame`` in ``mode``; ``None`` when there is nothing to draw.
|
||||
|
||||
@@ -298,6 +299,11 @@ def render_image(
|
||||
latch to should do. It never gates the arrows or the depth values: those stay this
|
||||
frame's own data, so a sample the device does not vouch for is still not drawn just
|
||||
because the boundary around it was latched.
|
||||
|
||||
``arrow_policy`` is the shared flow-arrow policy (``visual_policy.policy_for_settings``
|
||||
over the pane's display settings): the same object the 3D surface samples its overlay
|
||||
with, so both figures trigger at the same strength. ``None`` means the draw style's
|
||||
own defaults.
|
||||
"""
|
||||
plan = plan or plan_image(frame)
|
||||
wanted = plan.resolve(mode)
|
||||
@@ -328,6 +334,7 @@ def render_image(
|
||||
valid_mask=frame.valid_mask,
|
||||
domain_mask=domain_mask,
|
||||
colormap=flow_colormap(theme),
|
||||
policy=arrow_policy,
|
||||
)
|
||||
# ``recorded``: the frame is the picture, with nothing drawn over it.
|
||||
return ensure_bgr(np.asarray(frame.image_bgr))
|
||||
|
||||
@@ -57,7 +57,10 @@ from orisys_app_shared.scene3d.sampling import (
|
||||
check_field_domain,
|
||||
sample_vertex_displacement,
|
||||
)
|
||||
from orisys_app_shared.scene3d.visual_policy import VisualPolicy, select_flow_samples
|
||||
from orisys_app_shared.scene3d.visual_policy import (
|
||||
policy_for_settings,
|
||||
select_flow_samples,
|
||||
)
|
||||
|
||||
#: Reported when a display gain is applied to a displacement that is only
|
||||
#: approximate. Informational: the picture is legitimate as an illustration, and
|
||||
@@ -89,8 +92,6 @@ class SceneComposer:
|
||||
#: draw, the picture is the asset's own geometry, and recomputing its normals
|
||||
#: every frame was pure cost (measured as the bulk of a 20k-vertex compose).
|
||||
self._rest_geometry: dict[str, tuple[np.ndarray, np.ndarray]] = {}
|
||||
#: Last UV grid of "arrow was on" samples for flow hysteresis.
|
||||
self._flow_previously_on: np.ndarray | None = None
|
||||
#: The resolved ``#RRGGBB,...`` ramp both views build their lookup table from.
|
||||
#: Set by the view when the theme changes; empty means this build's default.
|
||||
self.ramp = str(ramp)
|
||||
@@ -252,12 +253,7 @@ class SceneComposer:
|
||||
)
|
||||
return flow_arrows, contact_arrow
|
||||
|
||||
policy = VisualPolicy(
|
||||
flow_on_px=float(settings.flow_on_px),
|
||||
flow_off_px=float(settings.flow_off_px),
|
||||
flow_grid_spacing=int(settings.flow_grid_spacing),
|
||||
max_arrows=int(settings.max_arrows),
|
||||
).normalised()
|
||||
policy = policy_for_settings(settings)
|
||||
if (
|
||||
settings.flow_arrows_enabled
|
||||
and frame.flow_uv is not None
|
||||
@@ -265,20 +261,17 @@ class SceneComposer:
|
||||
):
|
||||
try:
|
||||
flow = np.asarray(frame.flow_uv)
|
||||
h, w = int(flow.shape[0]), int(flow.shape[1])
|
||||
previously_on = self._flow_previously_on
|
||||
if previously_on is not None and previously_on.shape != (h, w):
|
||||
previously_on = None
|
||||
# Selection is a pure function of (field, mask, policy): the 2D canvas
|
||||
# runs the same one on the same frame, so the two figures show the same
|
||||
# arrows. Hysteresis used to live only here, which made this figure
|
||||
# trigger at ``flow_off_px`` (0.3 px) where the canvas needed
|
||||
# ``flow_on_px`` (0.4 px) — measured on a real recording as 622 arrows
|
||||
# here against 432 there, and a difference on 34 of 41 frames.
|
||||
samples = select_flow_samples(
|
||||
flow,
|
||||
frame.flow_valid_mask,
|
||||
policy,
|
||||
previously_on=previously_on,
|
||||
)
|
||||
on_mask = np.zeros((h, w), dtype=bool)
|
||||
if samples.rows.size:
|
||||
on_mask[samples.rows, samples.cols] = True
|
||||
self._flow_previously_on = on_mask
|
||||
flow_arrows, extra = build_flow_overlay(
|
||||
samples,
|
||||
mapping,
|
||||
@@ -294,9 +287,6 @@ class SceneComposer:
|
||||
)
|
||||
)
|
||||
flow_arrows = None
|
||||
self._flow_previously_on = None
|
||||
else:
|
||||
self._flow_previously_on = None
|
||||
|
||||
if settings.contact_arrow_enabled and frame.contact_valid:
|
||||
contact_arrow, extra = build_normal_overlay(
|
||||
|
||||
@@ -13,6 +13,7 @@ import numpy as np
|
||||
|
||||
from orisys_app_shared.scene3d.contracts import (
|
||||
Diagnostic,
|
||||
DisplaySettings,
|
||||
Mask,
|
||||
diagnostic,
|
||||
error,
|
||||
@@ -58,6 +59,32 @@ class VisualPolicy:
|
||||
)
|
||||
|
||||
|
||||
def policy_for_settings(settings: DisplaySettings) -> VisualPolicy:
|
||||
"""The **one** flow-arrow policy both views use, from the display settings.
|
||||
|
||||
The 2D canvas and the 3D surface draw the same measurement, so they have to
|
||||
trigger at the same strength: "arrows in one figure and not the other" was a
|
||||
difference in the policy each path built for itself, not a difference in the
|
||||
data. One mapping, from one settings object, is what removes it — the pane's
|
||||
``DisplaySettings`` is where the thresholds and the sample grid are chosen (and
|
||||
persisted), so neither view is free to invent its own.
|
||||
|
||||
``flow_off_px`` travels with the policy but neither view applies hysteresis today.
|
||||
A band only one of the two views honoured was the mismatch itself (measured on a
|
||||
real recording: 622 arrows in the 3D overlay against 432 in the canvas, differing
|
||||
on 34 of 41 frames), and the band was never an operator-facing control. It stays in
|
||||
the value so a *shared* stateful selection can use it later; a state owned by one
|
||||
view cannot be shared with the other anyway — they run on different threads at
|
||||
different rates, which is what made the two selections diverge in the first place.
|
||||
"""
|
||||
return VisualPolicy(
|
||||
flow_on_px=float(settings.flow_on_px),
|
||||
flow_off_px=float(settings.flow_off_px),
|
||||
flow_grid_spacing=int(settings.flow_grid_spacing),
|
||||
max_arrows=int(settings.max_arrows),
|
||||
).normalised()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FlowSamples:
|
||||
"""Shared UV sample points for 2D and 3D flow arrows."""
|
||||
|
||||
Reference in New Issue
Block a user