实现 finger 单测点手动试验采集

将精确 UV 选点、实时预览与逐档人工确认接入同一页面,并保存每次试验的视频、事件和帧采集时间。通过离线视频、页面及记录回归测试。
This commit is contained in:
ykcheungab
2026-10-11 15:55:45 +08:00
parent 32dcecb270
commit dc7e290489
12 changed files with 896 additions and 15 deletions
@@ -68,7 +68,8 @@ def test_video_controls_use_shared_worker_and_show_processed_frame(
def test_sensor_tab_is_live_panel_and_paths_are_saved(make_window, tmp_dir):
window = make_window()
assert window.workspace.page("sensor") is window.finger_source_panel
assert window.workspace.page("sensor") is window.calibration_panel
assert window.calibration_panel.source_panel is window.finger_source_panel
window.finger_source_panel.video_path.setText(str(tmp_dir / "input.mp4"))
window.finger_source_panel.config_ref.setText(str(tmp_dir / "sensor.json"))
window.finger_source_panel.source_kind.setCurrentIndex(1)
+161
View File
@@ -0,0 +1,161 @@
"""A manually confirmed site produces one traceable trial directory."""
from __future__ import annotations
import csv
import json
import time
from types import SimpleNamespace
import numpy as np
import pytest
from orisys_app_shared.acquisition.models import SdkPacket
from orisys_autocal.trial import ManualTrial, load_path, site_at, uv_origin
class Worker:
def __init__(self) -> None:
self.is_running = True
self.sinks = []
self.packet = SdkPacket(
raw_frame=np.zeros((32, 32, 3), dtype=np.uint8),
processed_frame=np.zeros((32, 32, 3), dtype=np.uint8),
timestamp=time.time(),
fps=30,
process_ms=1,
session_id=2,
frame_id=1,
mono_t=1.0,
capture_mono_ns=time.perf_counter_ns(),
capture_utc_ns=time.time_ns(),
source_frame_id=7,
time_basis="host_camera",
normal_force_raw=1.25,
)
def latest(self):
return self.packet
def add_sink(self, sink):
self.sinks.append(sink)
def remove_sink(self, sink):
self.sinks.remove(sink)
def emit(self):
for sink in self.sinks:
sink(self.packet)
def test_uv_coordinates_are_centered_and_masked():
mask = np.full((5, 7), -1, dtype=np.int32)
mask[1:4, 2:5] = 2
flat = SimpleNamespace(
width=7,
height=5,
face_index=mask,
sample=lambda x, y: (
(np.array([x, y, 3.0]), np.array([0, 0, 1]), 2)
if 0 <= x < 7 and 0 <= y < 5 and mask[y, x] >= 0
else None
),
)
assert uv_origin(flat) == (3, 3)
site = site_at(flat, -1, 2)
assert site.raster_xy_px == (2, 1)
assert site.position_sensor_mm == (2.0, 1.0, 3.0)
with pytest.raises(ValueError):
site_at(flat, -3, 0)
def test_manual_trial_records_video_stamps_and_load_unload_events(tmp_dir):
worker = Worker()
trial = ManualTrial(worker)
bundle = tmp_dir / "bundle"
bundle.mkdir()
(bundle / "mesh.npz").write_bytes(b"mesh")
config = tmp_dir / "sensor.json"
config.write_text("{}", encoding="utf-8")
site = SimpleNamespace(
u_px=0,
v_px=1,
position_sensor_mm=(1.0, 2.0, 3.0),
face_index=2,
raster_xy_px=(3, 2),
uv_origin_raster_xy_px=(3, 3),
)
assert load_path([5, 10]) == [
("load", 5),
("load", 10),
("unload", 5),
("unload", 0),
]
directory = trial.start(
root=tmp_dir,
site=site,
levels=[5, 10],
hold_s=20,
sensor_sn="F-001",
bundle_path=str(bundle),
source={"kind": "camera", "value": 0},
config_ref=str(config),
fps=30,
)
worker.emit()
for value in (5, 10, 5, 0):
trial.confirm(value)
trial.finish()
report = trial.recorder.stop(wait=True)
assert report.ok
assert trial.poll() == "complete"
assert len(list(directory.glob("raw_*.mp4"))) == 1
with (directory / "frames.csv").open(encoding="utf-8", newline="") as handle:
row = next(csv.DictReader(handle))
assert row["t_host_mono_ns"] == str(worker.packet.capture_mono_ns)
assert row["source_frame_id"] == "7"
assert row["time_basis"] == "host_camera"
assert row["capture_dropped"] == "0"
with (directory / "events.csv").open(encoding="utf-8", newline="") as handle:
events = list(csv.DictReader(handle))
reached = [event for event in events if event["kind"] == "step_reached"]
assert len(reached) == 4
assert [json.loads(event["detail"])["branch"] for event in reached] == [
"load",
"load",
"unload",
"unload",
]
with (directory / "session.json").open(encoding="utf-8") as handle:
session = json.load(handle)
assert session["site"]["uv_xy_px"] == [0, 1]
assert session["site"]["mesh_sha256"]
def test_calibration_page_keeps_precise_uv_site_when_switching_to_live_view(
make_window,
):
from orisys_autocal.sensor.uv_flat import FlatSurface
window = make_window()
mask = np.full((5, 7), -1, dtype=np.int32)
mask[1:4, 2:5] = 2
positions = np.zeros((5, 7, 3), dtype=np.float64)
positions[1, 2] = (2, 1, 3)
flat = FlatSurface(
size_hw=(5, 7),
image_rgb=np.zeros((5, 7, 3), dtype=np.uint8),
positions_mm=positions,
normals=np.zeros_like(positions),
face_index=mask,
uv_min=(0, 0),
uv_max=(1, 1),
)
panel = window.calibration_panel
panel.set_flat(flat, "bundle")
panel._picked(2, 1)
assert (panel.u_spin.value(), panel.v_spin.value()) == (-1, 2)
assert panel._site.raster_xy_px == (2, 1)
assert "2.000" in panel.position_value.text()
panel.views.setCurrentWidget(panel.source_panel)
assert panel._site.raster_xy_px == (2, 1)
window.close()