Store the depth channel in an analysis result and serve it to the 2D depth view
A replay could draw arrows but not depth, and the recording view said there was nothing to draw even when a result existed. Depth is now quantised per frame (14 B empty, 486 B at the worst frame seen), read from the cache by the same draw path live uses, and the raw-recording note appears only when no result exists. Format version 2; see apps/studio/docs/replay-analysis.md.
This commit is contained in:
@@ -12,8 +12,12 @@
|
||||
|
||||
| 模式 | 画面来源 | 说明 |
|
||||
|---|---|---|
|
||||
| `recording` | 录像自己的 `raw_*.mp4`,按需解码 | 只有原始相机图像:没有光流,也没有深度 |
|
||||
| `analysis` | 存下来的分析结果,按帧读 | 有 pass 记录下来的光流样本与候选网格 |
|
||||
| `recording` | 录像自己的 `raw_*.mp4`,按需解码 | 这个画面是原始相机图像:它自己没有光流也没有深度 |
|
||||
| `analysis` | 存下来的分析结果,按帧读 | 有 pass 记录下来的光流样本、候选网格与深度图 |
|
||||
|
||||
模式说的是**画面从哪里读**,而"有没有可画的东西"是另一件事:只要有结果,二维面板的模式
|
||||
选择器就提供 深度图/箭头(时间轴在录像上时连"原始图像"一起提供),选了派生模式的那些帧
|
||||
一律从结果读、按窗口唯一的 2D 出口画 —— 绝不会把原始图像当成箭头图交出去。
|
||||
|
||||
图表、二维画布、三维面板都读这一个游标,模式由 `ReplayEngine.mode()` 回答;「从视频重算」
|
||||
因此不再是第三种来源,而是**显式重跑**这一遍的开关(第 10 节)。时间基准是**信号时间**
|
||||
@@ -47,9 +51,16 @@
|
||||
| 视图 | 读什么 | 怎么读 |
|
||||
|---|---|---|
|
||||
| 图表 | `forces.csv`(记录值) | `charts.set_cursor(t)` 用同一游标;结果里不存力 |
|
||||
| 二维画布 | 结果的 `flow` 与 `valid_mask` | `ImageFrame(source="analysis")` 走窗口唯一的 2D 出口 `_show_image_frame`,箭头、暗点、配色与域边界与实时/重算**同一套代码** |
|
||||
| 二维画布 | 结果的 `flow`、`depth` 与 `valid_mask` | `ImageFrame(source="analysis")` 走窗口唯一的 2D 出口 `_show_image_frame`,箭头、暗点、配色、域边界与深度视图与实时/重算**同一套代码** |
|
||||
| 三维面板 | 同一帧的 `SdkPacket` | `session.packet(frame)` → `Scene3DView.submit_packet`,走同一个 adapter、域掩码 latch 与有效性求交 |
|
||||
|
||||
**深度视图也由结果喂。** `AnalysedFrame.depth` 就是第 1 节那幅量化平面重建出来的
|
||||
`float32` 图(空帧是形状正确的全零),`depth_valid` 因此是 `True` 而不像以前那样恒为
|
||||
`False`;深度着色、色标锁定、空/缺数据的区分全在窗口既有的那条路径上,回放没有第二条绘制
|
||||
代码。"这一帧深度是精确的零"由面板说出来(`panels.depth_empty`,带 `{pixels}`),与
|
||||
"这一帧根本没有深度通道"(`panels.depth_unavailable`)是两句不同的话,因为它们是两个不同的
|
||||
事实。
|
||||
|
||||
**画面是重建的,并且是精确重建。** 二维需要的 `[H,W,2]` 流场是把存下来的 `dx`/`dy` 写回
|
||||
**记录下来的候选位置**、其余格子填 `NaN`(选区器把非有限值当作非法,邻居格子因此不可能贡献
|
||||
出 pass 没记录过的样本)。写回要除以
|
||||
@@ -102,6 +113,22 @@ on_progress)`(`chain_spec_for` 从录像自己的 `session.json` 取链路,
|
||||
`test_the_replay_reset_is_the_display_reset_and_never_the_device` 失败过 —— 合成录像的配置
|
||||
缺失让 pass 在断言之前结束并覆盖了 `复位显示` 的提示;修的是消息落在哪里。
|
||||
|
||||
**深度那一片加的验收**(同一个文件再 +8 项,另加 `shared/tests/test_analysis_depth.py` 10 项):
|
||||
|
||||
* **深度往返**:另跑一遍链路,逐帧比 `depth_map`,误差上界是该帧自己的 `(max − min) / 510`,
|
||||
在 box 录像(33 帧,1 帧非空、76 px)和 264 帧的 `long_8x.mp4`(241 帧空、23 帧非空、
|
||||
最坏 336 px)上都是 0 失败;非零计数、min/max 与链路的完全相等。
|
||||
* **跳转等于顺序播放(深度模式)**:把上面那条测试扩到深度 —— 顺序播放记下每帧渲染出的深度
|
||||
图,再跳到 5 个位置,帧 id、`depth`、统计与**渲染出的字节**逐项相等。
|
||||
* **尺寸**:空帧 4,048 B(固定 4,042 + 6 B 载荷头),最坏非空帧 4,534 B(+486 B);
|
||||
按实测比例 4,090 B/帧,十分钟 70.2 MB(上限 81.6 MB)。断言写死了这三个数。
|
||||
* **模式切换不写结果**:四个模式各画一次,`scan_results(cache)` 的目录集合不变。
|
||||
* **"没什么可画"只在该说的时候说**:无结果时 `plan_image` 只给 `recorded` 并带
|
||||
`replay_raw_note`;有结果时该行消失、四个模式都在,默认仍是原始图像。两种语言各断言一次,
|
||||
且文案必须点出产生结果的动作。
|
||||
* **派生模式走结果**:时间轴在录像上、模式选深度时,送到 2D 出口的是**结果那一帧**(含深度),
|
||||
不是解码出来的视频帧;切回"原始图像"仍是录像自己的画面。
|
||||
|
||||
真实 GL 快照(`QT_QPA_PLATFORM=windows` + `QT_OPENGL=desktop`,真实窗口路径、pass 写入
|
||||
scratch cache、游标停在存下来的帧 id 上):
|
||||
|
||||
@@ -115,11 +142,27 @@ scratch cache、游标停在存下来的帧 id 上):
|
||||
二维画面是候选网格上的暗点阵(该帧活动样本很少),三维是手指岛面加同一套样本点;"同一个样本
|
||||
集"由上面的逐元素断言保证,快照证明这条链路真的走到了 GL。
|
||||
|
||||
**深度那一片的快照**(`.scratch/replay-analysis-a2/snapshots/`,同一个窗口、同一个游标、
|
||||
同一个模式选择器;时间轴停在录像上,所以"原始图像"是真的相机图,两个派生模式读结果):
|
||||
|
||||
| 文件 | 内容 |
|
||||
|---|---|
|
||||
| `analysis-finger-frame-33-2d-raw.png` / `-arrows.png` / `-depth.png` | finger 链路帧 33,深度非零 76 px(峰值 0.0242) |
|
||||
| `analysis-box-frame-27-2d-raw.png` / `-arrows.png` / `-depth.png` | box 链路帧 27,`depth_map` 365×365 **恰好全零**,面板显示"本帧深度全为 0(133225 px 中 0 个非零)" |
|
||||
|
||||
同一帧三张图的大小不同(raw 96.6 kB 是相机图,arrows 4.9 kB,depth 4.2 kB),这本身就是
|
||||
"三个模式画的是三样东西"的证据。
|
||||
|
||||
## 15. 已知限制
|
||||
|
||||
* 结果里没有力,也没有深度:力曲线始终读记录的 `forces.csv`,深度视图在回放模式下不可用(没有
|
||||
可显示的数据,面板按空处理而不是画一张假的)。
|
||||
* 结果里没有力:力曲线始终读记录的 `forces.csv`。深度有了(第 1、11 节),但**三维面板仍只
|
||||
拿光流**:`session.packet()` 的 `depth` 依旧是 `None`、`depth_valid` 依旧是 `False`,
|
||||
因为把深度接进三维会改变三维图形的构成,而那不在这一片里。二维深度视图是这一片交付的东西。
|
||||
* 结果覆盖的帧是 pass 走过的那些:录像比结果长时游标可以离开结果范围(面板说"这个分析结果里
|
||||
没有可显示的帧"),不会静默重复最后一帧。
|
||||
* 栅格类显示设置不生效(第 12 节);换栅格就是重跑一遍,那是**另一个键**。
|
||||
* 一个进程同时只有一遍分析在跑;采集会话运行期间不启动分析。
|
||||
* 时间轴与模式选择是两件事,而"有没有结果"是录像的属性、不是游标的属性:
|
||||
`ReplayController.available_result()` 查的是**本录像**在 `ResultLibrary` 里的那一份,所以
|
||||
即使 `ReplayEngine.use_recording()` 让引擎不再借用 session(目前 UI 没有第二条路会走到
|
||||
这个状态),二维面板照样能画箭头和深度,`replay_raw_note` 也不会重新出现。
|
||||
|
||||
Reference in New Issue
Block a user