回放与实时统一为一套界面;修复 30 fps 设备被以 16.7 fps 消费的计时器缺陷 - What: 1) 删除 ui/replay_bar.py(进度条、录制目录选择、重算深度按钮、提示行)与回放条的全部接线:历史回放现在与实时监测同布局(分割比例同为 0,360,1080,0)、同一条状态轨、同一套曲线手势,唯一差别是来源槽——实时是摄像机,回放是「选择录制视频…」(toolbar.btn_replay_video,与文件菜单「打开视频…」同一条 _load_replay_video 路径:视频所在目录即录制目录,曲线自动配对同目录 forces.csv)。时间轴改为曲线游标本身:表头新增播放/暂停图标按钮(icons.play/pause,空格键同效)以 1× 推进(按墙钟计时,漏拍不变慢),到末尾自动停;拖动游标、框选、滚轮平移或拖总览条一律自动暂停(ForcePlotsPanel.pause_playback)。回放的降采样/跨版本提示移入来源摘要 tooltip,打开录制时另发一条 6 秒通知。修复视频文件名残留在实时视图的问题:_refresh_status_rail 只在 _view_preset == "replay" 时写视频名。2) 录制格式不变:raw_*.mp4 与 forces.csv/frames.csv/session.json 继续一起写。按「两者都要」新增 视图 → 由视频重算力值(act_recompute):默认曲线读录制的 CSV,打开后把录制视频重新跑一遍视觉管线(进 SessionStore、2D 面板交给管线绘制、只能顺序前进,播放按钮即开始/暂停,关闭即挂回 CSV),启动路径唯一(set_playing → _on_chart_play_toggled → _start_run),并让文件源的时间轴改为视频自身时间(mono_t = 帧号 × 1/fps),否则重算结果会落在与 CSV 不同的横轴上。回放 2D 面板不再提供深度视图(REPLAY_VIEW_MODES=("recorded",)),离线深度重算引擎保留但无界面入口。3) 采集只有 16.67 fps 的根因是上一轮引入的等待:Windows 默认计时器分辨率 15.6 ms,time.sleep(0.002) 实测睡 15.5 ms,重复帧等两拍即跨过 30 fps 的 33 ms 周期,30 fps 设备被以 ~16.7 fps 消费并隔帧丢弃(配置本身正确:mini/sensor.json 写 camera.fps=30;离线实测整套管线 15.2 ms/帧≈66 fps,余量充足)。新增 _high_resolution_timer() 在会话期间把分辨率提到 1 ms(结束还原),并把重复帧等待改为 min(距下一帧时间, 半个周期)、到期只 sleep(0)。 - Why: 用户评审 3 点——回放界面与实时不一致且拖拽条效果一般、不想为回放录制深度信息、想同时保留视频与 CSV 记录、以及采集帧率只有 16.67 fps(应为 30)。 - Verification: ui_check.py 562 项 0 失败(新增两视图同布局/无 bar、来源槽随视图切换且无残留、播放暂停与手动手势暂停、重算模式切换与视频源、回放提示在 tooltip);test_ui_smoke.py 73 项 0 失败(新增视图统一、传输控制、重算模式);drive_shared.py 150 项 0 失败(新增 shared/tests/test_worker_frame_rate.py::test_a_file_source_runs_on_the_video_s_own_timeline,并把 30 fps 用例改成必须落在 27–31.5 fps;同文件实测无 1 ms 计时器 21.6 fps、有则 30.0 fps,同一帧返回 3 次时 11317:39);locale_studio.py 266 键齐全、zh==en;reproduce.py 退出 0;ui_snapshot.py 三张复核图(统一后的回放、实时、选区中英)。 - Not in this change: 录制文件格式与 CSV 列序不变;离线深度重算引擎(ReplayController.request_depth / DepthRecomputer)保留并有独立测试,但界面已无入口,可下一步删除;重算模式用有损 mp4,数值与原 CSV 会有差异(这正是它要显式呈现的)。

This commit is contained in:
ykcheungab
2026-09-13 22:04:01 +08:00
parent e6a1e77a5b
commit 9b3c5fe46c
17 changed files with 896 additions and 387 deletions
+14 -15
View File
@@ -34,6 +34,8 @@
"settings": "Settings area",
"plot_settings": "Plot settings…",
"rotate_arrows": "Rotate arrows 90°",
"recompute": "Recompute forces from the video",
"recompute_tip": "Off: the charts read the recorded forces.csv. On: re-run the vision pipeline over the recorded video (an algorithm check, forward-only).",
"run": "Run / Stop",
"stop": "Cancel acquisition",
"preview": "Preview / Stop preview",
@@ -61,7 +63,9 @@
"search_cameras": "Search",
"cam_id": "Camera",
"use_camera": "Use camera",
"use_camera_tip": "Go back to acquiring from a camera (a video file is in use)",
"use_camera_tip": "Go back to acquiring from a camera (the data source is a video file)",
"choose_video": "Choose a recorded video…",
"choose_video_tip": "Open a recorded video: the 2D panel plays it and the charts read the forces.csv beside it",
"preview": "Preview",
"preview_stop": "Stop preview",
"run": "Run",
@@ -110,8 +114,8 @@
"view_depth": "Depth map",
"view_recorded": "Recorded image",
"view_mode_tip": "Image display mode while running",
"replay_pipeline": "Recorded video → depth recomputed",
"replay_pipeline_tip": "Replay shows the recorded raw camera image; the depth map is recomputed offline from that video and the config, with no live arrow overlay",
"replay_pipeline": "Recorded video (raw camera image)",
"replay_pipeline_tip": "Replay shows the recorded raw camera image with no live arrow overlay; to check the algorithm, use View → Recompute forces from the video",
"replay_empty": "No recording open\nPress “Open recording…” below",
"arrow_empty": "No image yet\nPick a video or camera, then press Run",
"arrow_empty_tip": "The deformation arrow image appears here while running or previewing",
@@ -138,6 +142,10 @@
"fx_name": "Shear X",
"fy_name": "Shear Y",
"window_readout": "{start} – {end} s / {total} s total",
"play": "Play",
"play_tip": "Play this recording from the cursor at 1× (space bar); dragging the cursor pauses",
"pause": "Pause",
"pause_tip": "Pause playback (dragging the cursor, box zoom or the wheel also pauses)",
"window_tip": "Drag the highlighted region to move the window: this strip is the timeline of the whole session",
"no_data": "—",
"mode_follow": "Following live",
@@ -165,22 +173,13 @@
},
"playback": {
"title": "Replay recording",
"open": "Open recording…",
"open_tip": "Pick a recording directory under outputs/recordings (video + curves + metadata)",
"scrub_tip": "Drag the progress bar to move the playhead: video, depth and the chart cursor follow",
"loaded_short": "{name} · {frames} frames / {duration} s",
"open": "Open a recorded video…",
"video_filter": "Recorded video (*.mp4 *.avi *.mov *.mkv);;All files (*)",
"no_video": "This recording has no usable video segment",
"open_failed": "Could not open that recording: {error}",
"none_found": "No recording found under outputs/recordings",
"loaded": "{name} — {frames} frames / {duration} s",
"empty": "Pick a recording directory to start replay",
"position": "{position} / {duration} s",
"show_newton": "Show newtons",
"show_raw": "Show raw",
"recompute_depth": "Recompute depth",
"recompute_depth_tip": "Depth is not stored with a recording; recompute this frame offline from the video and the config",
"frame_dropped": "This frame never reached the video (the record queue overflowed)",
"frame_unreadable": "This frame could not be decoded from the video",
"depth_hint": "Depth is not stored: press Recompute depth to rebuild it from the video and the config",
"depth_running": "Recomputing depth… (on a worker thread)",
"depth_busy": "The previous recomputation is still running",
"depth_empty": "No depth data for this frame",