first commit

This commit is contained in:
ykcheungab
2026-08-06 15:18:27 +08:00
commit a5a73d76d1
37 changed files with 2697 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
{
"name": "prototype",
"version": "1.0",
"camera": {
"resolution_raw": [1920, 1080],
"fps": 30,
"units": 15,
"units_used": 9,
"resolution_stitch": [365, 365]
},
"stitcher": {
"min_area_frac": 0.01,
"max_area_frac": 0.05,
"max_aspect_ratio": 50,
"index": [1, 2, 3, 6, 7, 8, 11, 12, 13],
"margin": 10,
"n": [3, 3],
"box_size": [250, 250],
"blend": "numba"
},
"tracker": {
"adaptive": true,
"lower_threshold": 12.0,
"upper_threshold": 18.0,
"algorithm": "dis",
"dis_preset": "medium"
},
"contact": {
"min_contour_area": 3000,
"flow_threshold": 5,
"div_threshold": 0.05
}
}
+5
View File
@@ -0,0 +1,5 @@
[dshow @ 000001] DirectShow video devices (some may be both video and audio devices)
[dshow @ 000001] "USB Camera"
[dshow @ 000001] Alternative name "@device_pnp_\\\\?\\usb#vid_0000"
[dshow @ 000001] DirectShow audio devices
[dshow @ 000001] "Microphone"
+7
View File
@@ -0,0 +1,7 @@
[in#0 @ 000001] "USB Camera" (video)
[in#0 @ 000001] Alternative name "@device_pnp_\\?\usb#vid_1bcf&pid_2281&mi_00#b&1d67a3f1&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[in#0 @ 000001] "OBS Virtual Camera" (none)
[in#0 @ 000001] Alternative name "@device_sw_{860BB310-5D01-11D0-BD3B-00A0C911CE86}\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}"
[in#0 @ 000001] "Microphone (SunplusIT Inc)" (audio)
[in#0 @ 000001] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{47A9A322-5BE2-4304-9CA5-BFEC5748F002}"
Error opening input file dummy.
+5
View File
@@ -0,0 +1,5 @@
[dshow @ 000001] pixel_format=yuyv422 min s=640x480 fps=30 max s=640x480 fps=30
[dshow @ 000001] pixel_format=yuyv422 min s=1280x720 fps=10 max s=1280x720 fps=10
[dshow @ 000001] vcodec=mjpeg min s=640x480 fps=30 max s=640x480 fps=30
[dshow @ 000001] vcodec=mjpeg min s=1280x720 fps=30 max s=1280x720 fps=30
[dshow @ 000001] vcodec=mjpeg min s=1920x1080 fps=30 max s=1920x1080 fps=30
+172
View File
@@ -0,0 +1,172 @@
"""Smoke tests for Sensor Bench SDK acquisition and UI construction."""
from __future__ import annotations
import os
import sys
import time
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import numpy as np
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
def test_load_fixed_mode() -> None:
from sensor_bench.acquisition import load_fixed_mode
mode = load_fixed_mode(ROOT / "tests" / "fixtures" / "ddjx01_min.json")
assert mode.width == 1920
assert mode.height == 1080
assert mode.fps == 30.0
assert mode.stitch_width == 365
assert "1920x1080" in mode.label()
def test_translator_chinese_default() -> None:
from sensor_bench.i18n import DEFAULT_LOCALE, Translator
from sensor_bench.paths import default_locales_dir
assert DEFAULT_LOCALE == "zh_CN"
tr = Translator(default_locales_dir())
assert tr.locale == "zh_CN"
assert "传感器" in tr.t("app.title")
tr.set_locale("en_US")
assert tr.t("app.title") == "Sensor Bench"
assert "Power on" in tr.step_title("power_on")
def test_sdk_worker_packet_mapping() -> None:
from sensor_bench.acquisition import SdkAcquisitionWorker
raw = np.zeros((1080, 1920, 3), dtype=np.uint8)
processed = np.full((365, 365, 3), 40, dtype=np.uint8)
class FakeSensor:
def __init__(self, *args, **kwargs):
self.frame = raw.copy()
self.info = SimpleNamespace(FNORMAL=1, FSHEARX=2, FSHEARY=3)
def get_img(self):
self.frame = raw.copy()
return processed.copy()
def compute_deformation(self, **kwargs):
return True
def compute_contact(self):
return True
def read_info(self, *args):
return 1.0, 2.0, 3.0
def disconnect(self):
pass
fake_orisys = MagicMock()
fake_orisys.Sensor = FakeSensor
fake_orisys.__version__ = "test"
worker = SdkAcquisitionWorker()
with patch.dict(sys.modules, {"orisys": fake_orisys}):
with patch("sensor_bench.acquisition.orisys_importable", return_value=(True, "test")):
mode = worker.start(
0,
config_path=ROOT / "tests" / "fixtures" / "ddjx01_min.json",
backend="cpu",
)
assert mode.width == 1920
deadline = time.time() + 2.0
packet = None
while time.time() < deadline:
packet = worker.latest()
if packet is not None:
break
time.sleep(0.02)
worker.stop()
assert packet is not None
assert packet.width == 1920
assert packet.height == 1080
assert packet.processed_width == 365
assert packet.processed_height == 365
assert packet.normal_force == 1.0
assert packet.shear_x == 2.0
assert packet.shear_y == 3.0
assert packet.raw_frame.shape[:2] == (1080, 1920)
assert packet.processed_frame.shape[:2] == (365, 365)
def test_mainwindow_offscreen() -> None:
from PyQt5.QtWidgets import QApplication
from sensor_bench.app import MainWindow
from sensor_bench.acquisition import FixedCameraMode
from sensor_bench.i18n import DEFAULT_LOCALE
app = QApplication.instance() or QApplication([])
win = MainWindow()
assert win.tr.locale == DEFAULT_LOCALE == "zh_CN"
assert "传感器" in win.windowTitle()
assert win.btn_start.text() == "开始预览"
assert win.camera_index.value() == 0
assert win.btn_next is not None
assert win.btn_revert is not None
assert win.btn_reset is not None
assert win.sensor_type_combo.count() == 2
assert win.indentor_combo.count() == 2
assert win.platform_combo.count() == 3
assert win.selected_sensor_options()["sensor_type"] == "BOX"
assert win.selected_sensor_options()["indentor"] == "1mm_circle"
assert win.selected_sensor_options()["platform"] == "manual"
assert not hasattr(win, "resolution_combo")
assert not hasattr(win, "btn_search")
assert win._current_step_index == 0
assert not win.btn_revert.isEnabled()
assert win.btn_next.isEnabled()
# Language switch refreshes labels without restart.
win.language_combo.setCurrentIndex(1)
assert win.tr.locale == "en_US"
assert win.windowTitle() == "Sensor Bench"
assert win.btn_start.text() == "Start Preview"
# Workflow next / revert / reset.
total = len(win.workflow.steps)
win._on_step_next()
assert win._current_step_index == 1
assert win.btn_revert.isEnabled()
win._on_step_revert()
assert win._current_step_index == 0
for _ in range(total):
win._on_step_next()
assert win._current_step_index == total
assert not win.btn_next.isEnabled()
assert "completed" in win.workflow_status.text().lower() or "完成" in win.workflow_status.text()
win._on_step_reset()
assert win._current_step_index == 0
assert win.btn_next.isEnabled()
win._fixed_mode = FixedCameraMode(
width=1920,
height=1080,
fps=30.0,
config_ref=str(ROOT / "tests" / "fixtures" / "ddjx01_min.json"),
stitch_width=365,
stitch_height=365,
)
win.mode_label.setText(win.t("labels.fixed_mode", mode=win._fixed_mode.label()))
assert "1920x1080" in win.mode_label.text()
win.close()
if __name__ == "__main__":
test_load_fixed_mode()
test_translator_chinese_default()
test_sdk_worker_packet_mapping()
test_mainwindow_offscreen()
print("ok")