1. tuned arrow mesh drawing
2. centralixzed arrow config in layout.py 3. seperate mesh render and display resolution to speed up (~40 fps)
This commit is contained in:
@@ -23,9 +23,11 @@ class PipelineConfig:
|
||||
threshold: float | None = None
|
||||
viz_pose: str = DEFAULT_FINGER_VIZ_POSE
|
||||
enable_3d: bool = True
|
||||
use_color: bool = True
|
||||
use_color: bool = False
|
||||
lowpass_cutoff_hz: float = 5.0
|
||||
force_sample_rate_hz: float = 30.0
|
||||
profile: bool = False
|
||||
profile_every: int = 30
|
||||
|
||||
@classmethod
|
||||
def from_args(cls, args) -> "PipelineConfig":
|
||||
@@ -39,6 +41,8 @@ class PipelineConfig:
|
||||
viz_pose=args.viz_pose,
|
||||
enable_3d=not args.no_3d,
|
||||
lowpass_cutoff_hz=getattr(args, "cutoff", 5.0),
|
||||
profile=getattr(args, "profile", False),
|
||||
profile_every=getattr(args, "profile_every", 30),
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Shared stage timing helpers for finger example pipelines."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class StageProfiler:
|
||||
"""Accumulate per-stage wall times and print rolling averages."""
|
||||
|
||||
DEFAULT_ORDER = (
|
||||
"capture",
|
||||
"uv_unroll",
|
||||
"flow",
|
||||
"hhd",
|
||||
"contact",
|
||||
"read_info",
|
||||
"pipeline",
|
||||
"mesh",
|
||||
"arrows",
|
||||
"charts",
|
||||
"display",
|
||||
"total",
|
||||
)
|
||||
|
||||
def __init__(self, *, report_every: int = 30, stage_order: tuple[str, ...] | None = None):
|
||||
self.report_every = max(1, int(report_every))
|
||||
self.stage_order = stage_order or self.DEFAULT_ORDER
|
||||
self.frame_count = 0
|
||||
self.totals: dict[str, float] = defaultdict(float)
|
||||
self.last: dict[str, float] = {}
|
||||
|
||||
def record(self, name: str, ms: float) -> None:
|
||||
self.totals[name] += ms
|
||||
self.last[name] = ms
|
||||
|
||||
def tick(self) -> None:
|
||||
self.frame_count += 1
|
||||
if self.frame_count % self.report_every == 0:
|
||||
self.print_report(prefix=f"[profile {self.frame_count} frames]")
|
||||
|
||||
def print_report(self, *, prefix: str = "[profile summary]") -> None:
|
||||
if self.frame_count == 0:
|
||||
return
|
||||
n = self.frame_count
|
||||
parts = []
|
||||
for name in self.stage_order:
|
||||
if name in self.totals:
|
||||
parts.append(f"{name}={self.totals[name] / n:.2f}ms")
|
||||
total_ms = self.totals.get("total", 0.0) / n
|
||||
if total_ms > 0:
|
||||
parts.append(f"fps={1000.0 / total_ms:.1f}")
|
||||
print(f"\n{prefix} " + " | ".join(parts))
|
||||
|
||||
|
||||
def run_deformation_timed(tactile, *, check_motion=True, threshold=0, tail_frames=10):
|
||||
"""Run flow + gated HHD; return (flow_ms, hhd_ms). Mirrors Sensor.compute_deformation."""
|
||||
sensor = tactile.raw_sensor
|
||||
|
||||
t0 = time.perf_counter()
|
||||
sensor.compute_flow()
|
||||
flow_ms = (time.perf_counter() - t0) * 1000.0
|
||||
|
||||
run_hhd = True
|
||||
if check_motion:
|
||||
is_motion = sensor.detect_motion(threshold=threshold)
|
||||
try:
|
||||
tail_frames = max(0, int(tail_frames))
|
||||
except (TypeError, ValueError):
|
||||
tail_frames = 0
|
||||
|
||||
if is_motion:
|
||||
sensor.tail_frames_count = 0
|
||||
elif sensor.tail_frames_count < tail_frames:
|
||||
sensor.tail_frames_count += 1
|
||||
else:
|
||||
run_hhd = False
|
||||
sensor.tail_frames_count = tail_frames
|
||||
|
||||
hhd_ms = 0.0
|
||||
if run_hhd:
|
||||
t1 = time.perf_counter()
|
||||
sensor.compute_hhd()
|
||||
hhd_ms = (time.perf_counter() - t1) * 1000.0
|
||||
|
||||
return flow_ms, hhd_ms
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
from orisys.finger import FingerRuntime
|
||||
@@ -11,6 +12,18 @@ from viz import FrameRecorder, build_uv_surface_mesh, resolve_viz_pose_path
|
||||
|
||||
from .config import PipelineConfig
|
||||
from .frame import FingerFrameView
|
||||
from .profiling import StageProfiler, run_deformation_timed
|
||||
|
||||
|
||||
@dataclass
|
||||
class TickTimings:
|
||||
capture_ms: float = 0.0
|
||||
uv_unroll_ms: float = 0.0
|
||||
flow_ms: float = 0.0
|
||||
hhd_ms: float = 0.0
|
||||
contact_ms: float = 0.0
|
||||
read_info_ms: float = 0.0
|
||||
pipeline_ms: float = 0.0
|
||||
|
||||
|
||||
class FingerPipeline:
|
||||
@@ -24,6 +37,9 @@ class FingerPipeline:
|
||||
self.uv_mesh = None
|
||||
self.world_to_cam: np.ndarray | None = None
|
||||
self.mesh_lookat: np.ndarray | None = None
|
||||
self.profiler: StageProfiler | None = (
|
||||
StageProfiler(report_every=config.profile_every) if config.profile else None
|
||||
)
|
||||
|
||||
@property
|
||||
def is_open(self) -> bool:
|
||||
@@ -60,6 +76,8 @@ class FingerPipeline:
|
||||
self.mesh_lookat = self.uv_mesh.rest_vertices.mean(axis=0)
|
||||
|
||||
def close(self) -> None:
|
||||
if self.profiler is not None and self.profiler.frame_count:
|
||||
self.profiler.print_report(prefix="[profile final]")
|
||||
if self.runtime is not None:
|
||||
self.runtime.close()
|
||||
self.runtime = None
|
||||
@@ -69,33 +87,17 @@ class FingerPipeline:
|
||||
if self.runtime is not None:
|
||||
self.runtime.tactile.reset()
|
||||
|
||||
def tick(self) -> FingerFrameView | None:
|
||||
if self.runtime is None or self._start_time is None:
|
||||
return None
|
||||
|
||||
ok, unrolled = self.runtime.grab_and_process(color=self.config.use_color)
|
||||
if not ok:
|
||||
return None
|
||||
|
||||
tactile = self.runtime.tactile
|
||||
self.recorder.write_if_recording(tactile.frame)
|
||||
|
||||
tactile.compute_deformation(decompose=True, check_motion=True, threshold=0)
|
||||
tactile.compute_contact()
|
||||
|
||||
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = tactile.read_info(
|
||||
def _read_frame_view(self, tactile, unrolled) -> FingerFrameView:
|
||||
fps, flow, vnormal, depth_map, centroid, fnormal, fshearx, fsheary = tactile.read_info(
|
||||
tactile.info.FPS,
|
||||
tactile.info.VRAW,
|
||||
tactile.info.VNORMAL,
|
||||
tactile.info.IMG,
|
||||
tactile.info.CONTOUR,
|
||||
tactile.info.CENTROID,
|
||||
tactile.info.DEPTH,
|
||||
tactile.info.CENTROID,
|
||||
tactile.info.FNORMAL,
|
||||
tactile.info.FSHEARX,
|
||||
tactile.info.FSHEARY,
|
||||
)
|
||||
|
||||
return FingerFrameView(
|
||||
timestamp=time.time() - self._start_time,
|
||||
fps=float(fps),
|
||||
@@ -103,8 +105,8 @@ class FingerPipeline:
|
||||
flow=flow,
|
||||
vnormal=vnormal,
|
||||
depth_map=depth_map,
|
||||
img=img,
|
||||
contour=contour,
|
||||
img=np.array([]),
|
||||
contour=np.array([]),
|
||||
centroid=centroid,
|
||||
fnormal=float(fnormal),
|
||||
fshearx=float(fshearx),
|
||||
@@ -114,6 +116,72 @@ class FingerPipeline:
|
||||
raw_frame=tactile.frame,
|
||||
)
|
||||
|
||||
def tick(self) -> FingerFrameView | None:
|
||||
frame, _timings = self.tick_timed()
|
||||
return frame
|
||||
|
||||
def tick_timed(self) -> tuple[FingerFrameView | None, TickTimings]:
|
||||
timings = TickTimings()
|
||||
if self.runtime is None or self._start_time is None:
|
||||
return None, timings
|
||||
|
||||
pipeline_t0 = time.perf_counter()
|
||||
profile = self.profiler is not None
|
||||
|
||||
if profile:
|
||||
t0 = time.perf_counter()
|
||||
ok, frame = self.runtime.read_source()
|
||||
timings.capture_ms = (time.perf_counter() - t0) * 1000.0
|
||||
if not ok:
|
||||
return None, timings
|
||||
t0 = time.perf_counter()
|
||||
unrolled = self.runtime.process_frame(frame, color=self.config.use_color)
|
||||
timings.uv_unroll_ms = (time.perf_counter() - t0) * 1000.0
|
||||
else:
|
||||
ok, unrolled = self.runtime.grab_and_process(color=self.config.use_color)
|
||||
if not ok:
|
||||
return None, timings
|
||||
|
||||
tactile = self.runtime.tactile
|
||||
self.recorder.write_if_recording(tactile.frame)
|
||||
|
||||
if profile:
|
||||
flow_ms, hhd_ms = run_deformation_timed(
|
||||
tactile, check_motion=True, threshold=0
|
||||
)
|
||||
timings.flow_ms = flow_ms
|
||||
timings.hhd_ms = hhd_ms
|
||||
self.profiler.record("capture", timings.capture_ms)
|
||||
self.profiler.record("uv_unroll", timings.uv_unroll_ms)
|
||||
self.profiler.record("flow", timings.flow_ms)
|
||||
self.profiler.record("hhd", timings.hhd_ms)
|
||||
else:
|
||||
tactile.compute_deformation(decompose=True, check_motion=True, threshold=0)
|
||||
|
||||
t0 = time.perf_counter()
|
||||
tactile.compute_contact()
|
||||
timings.contact_ms = (time.perf_counter() - t0) * 1000.0
|
||||
if profile:
|
||||
self.profiler.record("contact", timings.contact_ms)
|
||||
|
||||
t0 = time.perf_counter()
|
||||
view = self._read_frame_view(tactile, unrolled)
|
||||
timings.read_info_ms = (time.perf_counter() - t0) * 1000.0
|
||||
if profile:
|
||||
self.profiler.record("read_info", timings.read_info_ms)
|
||||
|
||||
timings.pipeline_ms = (time.perf_counter() - pipeline_t0) * 1000.0
|
||||
if profile:
|
||||
self.profiler.record("pipeline", timings.pipeline_ms)
|
||||
|
||||
return view, timings
|
||||
|
||||
def record_total_frame(self, total_ms: float) -> None:
|
||||
if self.profiler is None:
|
||||
return
|
||||
self.profiler.record("total", total_ms)
|
||||
self.profiler.tick()
|
||||
|
||||
def start_recording(self) -> bool:
|
||||
if self.runtime is None:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user