update dd06 display
This commit is contained in:
@@ -8,10 +8,11 @@ Does not import camera, ROI, or `orisys.Sensor`.
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Sequence, Tuple
|
||||
from typing import Literal, Optional, Sequence, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import orisys
|
||||
from scipy.signal import butter, filtfilt
|
||||
|
||||
FORCE_SAMPLE_RATE_HZ = 30.0
|
||||
@@ -19,6 +20,17 @@ PLOT_BUFFER_POINTS = 120
|
||||
PLOT_WINDOW_SEC = PLOT_BUFFER_POINTS / FORCE_SAMPLE_RATE_HZ
|
||||
Y_AXIS_MIN_SPAN = 20000.0
|
||||
|
||||
ArrowBackground = Literal["pure_color", "captured"]
|
||||
# #0B1016 as BGR (matches demo_wuxi / promote_video theme)
|
||||
PURE_COLOR_BG_BGR = (22, 16, 11)
|
||||
|
||||
_ARROW_DRAW_KW = dict(
|
||||
threshold=2,
|
||||
grid_spacing=20,
|
||||
arrow_scale=0.5,
|
||||
below_threshold_color=(160, 160, 160),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ViewerFrame:
|
||||
@@ -92,6 +104,14 @@ def image_shape_meta(img: np.ndarray) -> Tuple[int, int, float]:
|
||||
return h, w, aspect
|
||||
|
||||
|
||||
def _base_bgr(snapshot) -> Optional[np.ndarray]:
|
||||
for attr in ("image", "processed_image", "raw_image"):
|
||||
out = ensure_bgr(getattr(snapshot, attr, None))
|
||||
if out is not None:
|
||||
return out
|
||||
return None
|
||||
|
||||
|
||||
def select_display_image(snapshot) -> np.ndarray:
|
||||
"""Pick flow overlay / processed / raw image for the left panel."""
|
||||
arrows = getattr(snapshot, "flow_overlay", None)
|
||||
@@ -109,6 +129,29 @@ def select_display_image(snapshot) -> np.ndarray:
|
||||
return np.zeros((400, 400, 3), dtype=np.uint8)
|
||||
|
||||
|
||||
def build_display_image(
|
||||
snapshot,
|
||||
background: ArrowBackground = "pure_color",
|
||||
) -> np.ndarray:
|
||||
"""Build left-panel image: arrows on pure color or on the captured frame."""
|
||||
flow = getattr(snapshot, "flow", None)
|
||||
base = _base_bgr(snapshot)
|
||||
|
||||
if flow is not None and base is not None:
|
||||
if background == "pure_color":
|
||||
h, w = base.shape[:2]
|
||||
canvas = np.empty((h, w, 3), dtype=np.uint8)
|
||||
canvas[:, :] = PURE_COLOR_BG_BGR
|
||||
return orisys.util.draw_arrows(canvas, flow, **_ARROW_DRAW_KW)
|
||||
|
||||
overlay = ensure_bgr(getattr(snapshot, "flow_overlay", None))
|
||||
if overlay is not None:
|
||||
return overlay
|
||||
return orisys.util.draw_arrows(base, flow, **_ARROW_DRAW_KW)
|
||||
|
||||
return select_display_image(snapshot)
|
||||
|
||||
|
||||
def bgr_to_rgb_bytes(img_bgr: np.ndarray) -> Tuple[np.ndarray, int, int]:
|
||||
"""Convert BGR uint8 image to contiguous RGB for QImage."""
|
||||
rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
|
||||
@@ -255,6 +298,7 @@ class QtViewerBackend:
|
||||
npoints: int = PLOT_BUFFER_POINTS,
|
||||
lowpass_cutoff_hz: float = 10.0,
|
||||
y_axis_min_span: float = Y_AXIS_MIN_SPAN,
|
||||
arrow_background: ArrowBackground = "pure_color",
|
||||
):
|
||||
self.force_history = ForceHistory(
|
||||
npoints=npoints,
|
||||
@@ -263,6 +307,50 @@ class QtViewerBackend:
|
||||
)
|
||||
self._start_time: Optional[float] = None
|
||||
self.last_viewer_frame: Optional[ViewerFrame] = None
|
||||
self.arrow_background: ArrowBackground = arrow_background
|
||||
|
||||
def set_arrow_background(self, background: ArrowBackground) -> None:
|
||||
self.arrow_background = background
|
||||
|
||||
def rebuild_display(self, snapshot) -> ViewerFrame:
|
||||
"""Re-render display image without advancing force history."""
|
||||
display = build_display_image(snapshot, self.arrow_background)
|
||||
h, w, aspect = image_shape_meta(display)
|
||||
prev = self.last_viewer_frame
|
||||
if prev is not None:
|
||||
frame = ViewerFrame(
|
||||
display_image=display,
|
||||
image_height=h,
|
||||
image_width=w,
|
||||
aspect_ratio=aspect,
|
||||
depth=prev.depth,
|
||||
normal_force=prev.normal_force,
|
||||
shear_x=prev.shear_x,
|
||||
shear_y=prev.shear_y,
|
||||
normal_force_calibrated=prev.normal_force_calibrated,
|
||||
fps=prev.fps,
|
||||
timestamp=prev.timestamp,
|
||||
record_frame=prev.record_frame,
|
||||
)
|
||||
else:
|
||||
frame = ViewerFrame(
|
||||
display_image=display,
|
||||
image_height=h,
|
||||
image_width=w,
|
||||
aspect_ratio=aspect,
|
||||
depth=getattr(snapshot, "depth", None),
|
||||
normal_force=float(getattr(snapshot, "normal_force", 0.0)),
|
||||
shear_x=float(getattr(snapshot, "shear_x", 0.0)),
|
||||
shear_y=float(getattr(snapshot, "shear_y", 0.0)),
|
||||
normal_force_calibrated=float(
|
||||
getattr(snapshot, "normal_force_calibrated", 0.0)
|
||||
),
|
||||
fps=float(getattr(snapshot, "fps", 0.0)),
|
||||
timestamp=float(getattr(snapshot, "timestamp", 0.0) or 0.0),
|
||||
record_frame=ensure_bgr(getattr(snapshot, "record_frame", None)),
|
||||
)
|
||||
self.last_viewer_frame = frame
|
||||
return frame
|
||||
|
||||
def reset(self) -> None:
|
||||
self.force_history.reset()
|
||||
@@ -271,7 +359,7 @@ class QtViewerBackend:
|
||||
|
||||
def ingest(self, snapshot, *, now: Optional[float] = None) -> ViewerFrame:
|
||||
"""Convert a FrameSnapshot into ViewerFrame and update force history."""
|
||||
display = select_display_image(snapshot)
|
||||
display = build_display_image(snapshot, self.arrow_background)
|
||||
h, w, aspect = image_shape_meta(display)
|
||||
|
||||
fn = float(getattr(snapshot, "normal_force", 0.0))
|
||||
|
||||
Reference in New Issue
Block a user