update dd06 display
This commit is contained in:
+3
-3
@@ -9,8 +9,8 @@
|
||||
],
|
||||
"fps": 30,
|
||||
"resolution_stitch": [
|
||||
390,
|
||||
550
|
||||
195,
|
||||
275
|
||||
],
|
||||
"source_scale": 0.4,
|
||||
"old": [
|
||||
@@ -55,7 +55,7 @@
|
||||
"raft",
|
||||
"dis"
|
||||
],
|
||||
"hhd_downsample": 1
|
||||
"hhd_downsample": 3
|
||||
},
|
||||
"contact": {
|
||||
"min_contour_area": 3000,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"video_source": "D:/repo/orisys-sdk/data/dd02-0703/1.mp4",
|
||||
"video_source": "D:/repo/orisys-sdk/data/dd02/0607_1.mp4",
|
||||
"config_path": "./config/dd02-ov.json",
|
||||
"finger_assets_dir": "D:/repo/orisys-sdk/assets/finger/30fps-0703",
|
||||
"csv_save_dir": "./outputs/csv",
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
"""2D Qt sensor monitor (image + force curves; no finger 3D mesh)."""
|
||||
|
||||
from .backend import (
|
||||
PURE_COLOR_BG_BGR,
|
||||
ArrowBackground,
|
||||
ForceHistory,
|
||||
ForcePlotState,
|
||||
QtViewerBackend,
|
||||
ViewerFrame,
|
||||
build_display_image,
|
||||
image_shape_meta,
|
||||
panel_width_for_aspect,
|
||||
select_display_image,
|
||||
@@ -12,11 +15,14 @@ from .backend import (
|
||||
from .window import QtViewerWindow, run_viewer
|
||||
|
||||
__all__ = [
|
||||
"PURE_COLOR_BG_BGR",
|
||||
"ArrowBackground",
|
||||
"ForceHistory",
|
||||
"ForcePlotState",
|
||||
"QtViewerBackend",
|
||||
"QtViewerWindow",
|
||||
"ViewerFrame",
|
||||
"build_display_image",
|
||||
"image_shape_meta",
|
||||
"panel_width_for_aspect",
|
||||
"run_viewer",
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -66,6 +66,40 @@ QLineEdit {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
QLabel#toolbarCaption {
|
||||
color: #777777;
|
||||
font-family: "SimHei";
|
||||
font-size: 13px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
QComboBox {
|
||||
background-color: #0d0d0d;
|
||||
color: #999999;
|
||||
border: 1px solid #333333;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
font-family: "SimHei";
|
||||
font-size: 13px;
|
||||
min-width: 96px;
|
||||
}
|
||||
QComboBox:hover {
|
||||
background-color: #1a1a1a;
|
||||
color: #cccccc;
|
||||
border: 1px solid #555555;
|
||||
}
|
||||
QComboBox::drop-down {
|
||||
border: none;
|
||||
width: 20px;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background-color: #0d0d0d;
|
||||
color: #999999;
|
||||
border: 1px solid #333333;
|
||||
selection-background-color: #1a3a4a;
|
||||
selection-color: #cccccc;
|
||||
}
|
||||
|
||||
/* ---- 力曲线容器 ---- */
|
||||
GraphicsLayoutWidget {
|
||||
border: 1px solid #262626;
|
||||
|
||||
@@ -13,6 +13,7 @@ from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtGui import QFont, QIcon
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QFileDialog,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
@@ -35,7 +36,12 @@ from gui.sensors import create_sensor_adapter
|
||||
from gui.widgets.log_panel import TeeStream, WidgetLogHandler, make_log_widget
|
||||
from gui.widgets.qt_pixmap import ndarray_to_pixmap, pixmap_keep_aspect
|
||||
|
||||
from .backend import PLOT_BUFFER_POINTS, PLOT_WINDOW_SEC, QtViewerBackend, panel_width_for_aspect
|
||||
from .backend import (
|
||||
PLOT_BUFFER_POINTS,
|
||||
PLOT_WINDOW_SEC,
|
||||
QtViewerBackend,
|
||||
panel_width_for_aspect,
|
||||
)
|
||||
from .styles import CURVE_THEME_COLOR, STYLE
|
||||
|
||||
|
||||
@@ -127,6 +133,17 @@ class QtViewerWindow(QMainWindow):
|
||||
self.btn_stop.clicked.connect(self._on_stop)
|
||||
bar.addWidget(self.btn_stop)
|
||||
|
||||
bg_caption = QLabel("背景")
|
||||
bg_caption.setObjectName("toolbarCaption")
|
||||
bar.addWidget(bg_caption)
|
||||
self.bg_combo = QComboBox()
|
||||
self.bg_combo.setObjectName("bgCombo")
|
||||
self.bg_combo.addItem("纯色背景", "pure_color")
|
||||
self.bg_combo.addItem("采集图像", "captured")
|
||||
self.bg_combo.setCurrentIndex(0)
|
||||
self.bg_combo.currentIndexChanged.connect(self._on_arrow_background_changed)
|
||||
bar.addWidget(self.bg_combo)
|
||||
|
||||
root_layout.addLayout(bar)
|
||||
|
||||
v_splitter = QSplitter(Qt.Vertical)
|
||||
@@ -391,6 +408,23 @@ class QtViewerWindow(QMainWindow):
|
||||
self.sensor_adapter.reset()
|
||||
print("追踪器已重置。")
|
||||
|
||||
def _on_arrow_background_changed(self, _index: int) -> None:
|
||||
value = self.bg_combo.currentData()
|
||||
if value not in ("pure_color", "captured"):
|
||||
return
|
||||
self.viewer_backend.set_arrow_background(value)
|
||||
if self._last_snapshot is None:
|
||||
return
|
||||
viewer_frame = self.viewer_backend.rebuild_display(self._last_snapshot)
|
||||
self._last_viewer_frame = viewer_frame
|
||||
aspect_changed = abs(viewer_frame.aspect_ratio - self._display_aspect) > 1e-3
|
||||
self._display_aspect = viewer_frame.aspect_ratio
|
||||
self._source_pixmap = ndarray_to_pixmap(viewer_frame.display_image, bgr=True)
|
||||
if aspect_changed:
|
||||
self.adjust_image_panel()
|
||||
else:
|
||||
self._render_display_pixmap()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
key = event.key()
|
||||
if key == Qt.Key_Escape:
|
||||
|
||||
Reference in New Issue
Block a user