Files
orisys-app/apps/autocal/tests/test_manual_trial.py
T
ykcheungab 69c1322616 调整 Autocal 标定记录格式与目录命名
将人工参考载荷按档位区间保存,重排帧字段并明确界面操作。已通过相关测试、代码和文档检查。
2026-10-11 16:21:49 +08:00

199 lines
6.1 KiB
Python

"""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 (
REFERENCE_HEADER,
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_model="Finger D40",
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 "Finger-D40_F-001" in directory.name
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 / "reference.csv").open(encoding="utf-8", newline="") as handle:
reader = csv.DictReader(handle)
assert tuple(reader.fieldnames) == REFERENCE_HEADER
references = list(reader)
assert [row["branch"] for row in references] == ["load", "load", "unload", "unload"]
assert [row["F_ref_normal_N"] for row in references] == [
"5.0",
"10.0",
"5.0",
"0.0",
]
assert all(
row["F_ref_x_N"] == row["F_ref_y_N"] == row["F_ref_z_N"] == ""
for row in references
)
assert all(row["stability_verified"] == "0" for row in references)
assert all(
int(row["t_start_host_mono_ns"]) < int(row["t_end_host_mono_ns"])
for row in references
)
assert all(
previous["t_end_host_mono_ns"] == following["t_start_host_mono_ns"]
for previous, following in zip(references, references[1:])
)
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["sensor_model"] == "Finger D40"
assert session["reference_file"] == "reference.csv"
assert session["site"]["mesh_sha256"]
def test_calibration_page_keeps_precise_uv_site_when_switching_to_live_view(
make_window,
):
from orisys_app_shared.i18n import t
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
assert panel.start_button.text() == t("calibration.start")
assert panel.target_label.text() == t("calibration.before_start")
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()