Merge branch 'main' of ssh://892887cvmk08.aicp.io:24928/orisys-tech/orisys-app
This commit is contained in:
@@ -78,16 +78,16 @@ _ARROW_DRAW_KW = dict(
|
||||
below_threshold_color=hex_to_bgr(FLOW_IDLE_HEX),
|
||||
)
|
||||
|
||||
#: ``draw_arrows`` reads every point of this grid, including those under its threshold
|
||||
#: (they become the idle dots). The cap is only a guard: a stitch image does not have
|
||||
#: this many points, and a lower cap would drop cells the live painter still draws.
|
||||
#: Box/Mini live and analysis walk every point of this grid, including those under
|
||||
#: the threshold (they become the idle dots). The cap is only a guard: a stitch image
|
||||
#: does not have this many points, and a lower cap would drop cells the painter still draws.
|
||||
SDK_BOX_MAX_ARROWS = 100_000
|
||||
|
||||
|
||||
def sdk_box_policy():
|
||||
"""The sample grid :func:`orisys.util.draw_arrows` walks for a box frame.
|
||||
"""The sample grid a box frame is selected and drawn with.
|
||||
|
||||
Live box arrows are not the shared selector's default (spacing 10, 400 strongest).
|
||||
Box arrows are not the finger selector's default (spacing 10, 400 strongest).
|
||||
A stored result has to record *this* grid or the replay cannot hand the same cells
|
||||
back to the same painter.
|
||||
"""
|
||||
@@ -98,8 +98,7 @@ def sdk_box_policy():
|
||||
flow_off_px=float(_ARROW_DRAW_KW["threshold"]) * 0.75,
|
||||
flow_grid_spacing=int(_ARROW_DRAW_KW["grid_spacing"]),
|
||||
flow_arrow_scale=float(_ARROW_DRAW_KW["arrow_scale"]),
|
||||
# ``draw_arrows`` still folds the border cells into its colour range, then
|
||||
# refuses to draw them. Recording them (edge 0) is what keeps that range.
|
||||
# Edge 0 keeps border cells in the colour range the box pass has always used.
|
||||
flow_edge_margin=0,
|
||||
flow_length_margin=0.8,
|
||||
max_arrows=SDK_BOX_MAX_ARROWS,
|
||||
@@ -107,7 +106,7 @@ def sdk_box_policy():
|
||||
|
||||
|
||||
def uses_sdk_box_grid(policy) -> bool:
|
||||
"""True when ``policy`` is the box painter's grid, not the shared selector's."""
|
||||
"""True when ``policy`` is the box sample grid, not the finger selector's."""
|
||||
if policy is None:
|
||||
return False
|
||||
spacing = getattr(policy, "flow_grid_spacing", None)
|
||||
@@ -223,8 +222,8 @@ def flow_colormap(theme: str = "dark") -> np.ndarray:
|
||||
def arrow_display_scale(draw_style: str = "box") -> float:
|
||||
"""How much the drawn arrows amplify the measured displacement.
|
||||
|
||||
``orisys.util.draw_arrows`` divides the displacement by ``arrow_scale``, so
|
||||
the effective display magnification is its reciprocal (box ``0.5`` -> ``2x``,
|
||||
The shared selector divides displacement by ``flow_arrow_scale``, so the
|
||||
effective display magnification is its reciprocal (box ``0.5`` -> ``2x``,
|
||||
finger ``1.0`` -> ``1x``). The drawn length is additionally capped at
|
||||
``grid_spacing * 0.8`` pixels, so the returned factor describes the mapping
|
||||
only while an arrow has not hit that cap — the UI says so in its tooltip.
|
||||
@@ -345,9 +344,7 @@ def _policy_from_style(
|
||||
|
||||
|
||||
#: The pixels ``cv2.circle(..., radius=1, thickness=-1)`` fills, as ``(dy, dx)`` offsets
|
||||
#: from the centre it is given. One marker shape for every sensor type: the SDK's
|
||||
#: ``orisys.util.draw_arrows`` paints a below-threshold sample with exactly that call, so
|
||||
#: a dot on the finger canvas has to be the same plus-shaped 5-pixel blob a box dot is.
|
||||
#: from the centre it is given. One plus-shaped 5-pixel blob for every sensor type.
|
||||
#: Painting the offsets in one vectorised write, instead of one ``cv2.circle`` per dot,
|
||||
#: is what keeps a full finger grid cheap: on a 276-candidate island the pass costs 47 us
|
||||
#: against 223 us for the call loop, and the whole draw gains only ~35 us.
|
||||
@@ -482,7 +479,6 @@ def draw_arrow_display(
|
||||
magnitude_range: tuple[float, float] | None = None,
|
||||
per_frame_colour: bool = False,
|
||||
idle_dots: bool | None = None,
|
||||
shared_painter: bool = False,
|
||||
) -> np.ndarray:
|
||||
"""Draw flow arrows on a solid canvas or over the processed frame.
|
||||
|
||||
@@ -498,9 +494,8 @@ def draw_arrow_display(
|
||||
|
||||
``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.
|
||||
Passing it is what makes the two figures trigger at the same strength. Without it,
|
||||
finger style falls back to the finger defaults and box style to :func:`sdk_box_policy`.
|
||||
|
||||
``magnitude_range`` fixes the colour scale, in the same unit as ``flow_on_px``
|
||||
(uv pixels of displacement); the default is :data:`FLOW_MAGNITUDE_RANGE` and a
|
||||
@@ -508,11 +503,8 @@ def draw_arrow_display(
|
||||
back in to normalising every frame against its own maximum, which makes one
|
||||
magnitude a different colour from one frame to the next.
|
||||
|
||||
``idle_dots`` shows or hides the below-threshold markers of the **shared finger
|
||||
canvas**; ``None`` means :data:`DRAW_IDLE_GRID_DOTS`, which is on. Box/Mini draw
|
||||
through the SDK's own ``orisys.util.draw_arrows``, which paints its own below-threshold
|
||||
dots (same tint, same radius-1 marker) and has no such switch: this one governs the
|
||||
canvas that draws here, and Box keeps the appearance it has.
|
||||
``idle_dots`` shows or hides the below-threshold markers; ``None`` means
|
||||
:data:`DRAW_IDLE_GRID_DOTS`, which is on.
|
||||
|
||||
Returns the drawn image, which carries ``frame_diagnostic``: ``None`` when the frame
|
||||
was drawn, and otherwise a :class:`~orisys_app_shared.scene3d.contracts.Diagnostic`
|
||||
@@ -521,22 +513,12 @@ def draw_arrow_display(
|
||||
that shows it without reading the attribute reports "nothing measured" for "nothing
|
||||
could be drawn". A ``flow`` of ``None`` is a legitimate empty frame and leaves the
|
||||
attribute ``None``.
|
||||
|
||||
``shared_painter`` forces this module's per-sample painter for a field that came from
|
||||
somewhere other than a live chain — a *recovered* field, finite only at the cells a
|
||||
stored analysis result recorded. The SDK painter walks the dense grid, which on such a
|
||||
field is almost entirely ``NaN``; measured on one stored box frame it paints **0 px**
|
||||
where the shared painter paints **1,486 px** (the frame's own candidate cells). It is a
|
||||
statement about the input, not about the sensor: the box *style* still decides the
|
||||
magnification and the idle-dot tint, and a live box frame keeps the SDK path.
|
||||
"""
|
||||
base = _ensure_bgr(processed_frame)
|
||||
if flow is None:
|
||||
return _flow_result(base)
|
||||
finger = str(draw_style).lower().startswith("f") or bool(shared_painter)
|
||||
finger = str(draw_style).lower().startswith("f")
|
||||
draw_kw = dict(_FINGER_ARROW_DRAW_KW if finger else _ARROW_DRAW_KW)
|
||||
if colormap is not None:
|
||||
draw_kw["colormap"] = colormap
|
||||
try:
|
||||
if background == "pure_color":
|
||||
color_bgr = ARROW_BG_BGR if canvas_bgr is None else tuple(int(v) for v in canvas_bgr)
|
||||
@@ -568,39 +550,33 @@ def draw_arrow_display(
|
||||
else:
|
||||
canvas = base
|
||||
|
||||
if finger:
|
||||
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,
|
||||
)
|
||||
if policy is not None:
|
||||
selected = policy
|
||||
elif finger:
|
||||
selected = _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 _flow_result(
|
||||
_paint_shared_flow_arrows(
|
||||
canvas,
|
||||
np.asarray(flow),
|
||||
valid_mask=valid_mask,
|
||||
policy=selected,
|
||||
colormap=colormap,
|
||||
below_threshold_color=tuple(draw_kw["below_threshold_color"]),
|
||||
magnitude_range=magnitude_range,
|
||||
per_frame_colour=per_frame_colour,
|
||||
# Read here rather than as a parameter default: the module constant is
|
||||
# the display decision, and a caller that flips it (a build, a test)
|
||||
# must be obeyed without every call site having to pass it.
|
||||
idle_dots=DRAW_IDLE_GRID_DOTS if idle_dots is None else bool(idle_dots),
|
||||
)
|
||||
)
|
||||
|
||||
import orisys
|
||||
|
||||
else:
|
||||
selected = sdk_box_policy()
|
||||
return _flow_result(
|
||||
orisys.util.draw_arrows(canvas, flow, valid_mask=valid_mask, **draw_kw)
|
||||
_paint_shared_flow_arrows(
|
||||
canvas,
|
||||
np.asarray(flow),
|
||||
valid_mask=valid_mask,
|
||||
policy=selected,
|
||||
colormap=colormap,
|
||||
below_threshold_color=tuple(draw_kw["below_threshold_color"]),
|
||||
magnitude_range=magnitude_range,
|
||||
per_frame_colour=per_frame_colour,
|
||||
# Read here rather than as a parameter default: the module constant is
|
||||
# the display decision, and a caller that flips it (a build, a test)
|
||||
# must be obeyed without every call site having to pass it.
|
||||
idle_dots=DRAW_IDLE_GRID_DOTS if idle_dots is None else bool(idle_dots),
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
# The frame is *not* idle: it failed to draw. The image alone cannot say so, so
|
||||
|
||||
@@ -33,7 +33,6 @@ from orisys_app_shared.acquisition.arrows import (
|
||||
depth_map_to_bgr,
|
||||
draw_arrow_display,
|
||||
flow_colormap,
|
||||
uses_sdk_box_grid,
|
||||
)
|
||||
from orisys_app_shared.acquisition.display_orientation import (
|
||||
ORIENTATION_IDENTITY,
|
||||
@@ -396,26 +395,14 @@ def _draw_flow(
|
||||
domain_mask: np.ndarray | None,
|
||||
arrow_policy,
|
||||
canvas_bgr: Sequence[int] | None = None,
|
||||
sdk_result: bool | None = None,
|
||||
) -> np.ndarray:
|
||||
"""Arrows on ``base``, using the same painter the pure-arrow views use."""
|
||||
if sdk_result is None:
|
||||
box_style = not str(draw_style).lower().startswith("f")
|
||||
sdk_result = (
|
||||
box_style
|
||||
and str(frame.source or "") in RESULT_SOURCES
|
||||
and uses_sdk_box_grid(arrow_policy)
|
||||
)
|
||||
flow = frame.flow
|
||||
if sdk_result and flow is not None:
|
||||
flow = np.nan_to_num(np.asarray(flow, dtype=np.float64), nan=0.0)
|
||||
return draw_arrow_display(
|
||||
base,
|
||||
flow,
|
||||
frame.flow,
|
||||
background=background,
|
||||
canvas_bgr=canvas_bgr,
|
||||
draw_style=draw_style,
|
||||
shared_painter=str(frame.source or "") in RESULT_SOURCES and not sdk_result,
|
||||
valid_mask=frame.valid_mask,
|
||||
domain_mask=domain_mask,
|
||||
colormap=flow_colormap(theme),
|
||||
|
||||
@@ -534,9 +534,8 @@ class AnalysisService:
|
||||
)
|
||||
#: The policy the samples are selected with. Recorded, never keyed (see
|
||||
#: ``model.py``): the shipped defaults make a stored result independent of the
|
||||
#: operator's display preferences. A box chain records the live painter's own
|
||||
#: grid; the shared defaults are the finger painter's and would drop every cell
|
||||
#: ``draw_arrows`` still draws.
|
||||
#: operator's display preferences. A box chain records the box sample grid;
|
||||
#: the finger defaults would drop cells that grid still draws.
|
||||
if policy is None and chain.sensor_type == "box":
|
||||
from orisys_app_shared.acquisition.arrows import sdk_box_policy
|
||||
|
||||
|
||||
Reference in New Issue
Block a user