HOT3D QUEST3 / GROUND TRUTH
HOT3D 构建 GT
从原始手部参数、双目图像和相机标定出发,经过 GT 一致性筛选、可见性审核与 ScaleHP 21 点转换,得到可追溯的 Test200 题集。
01 · 数据从哪里来
当前候选池取自固定版本 HOT3D-Clips 的 train_quest3 清单。官方清单含 1,288 段,本地已完整保存并校验其中 1,281 段,覆盖 7 位参与者、133 段录制、192,150 帧。Test200 是在这些候选中构建的平台评测集合,并非 HOT3D 官方 test split。
| 输入 | 提供什么 | 构建用途 |
|---|---|---|
| 两路灰度 JPG | 1201-1 / 1201-2,每 Case 150 帧 | 冻结双目输入及人工检查原始画面 |
__hand_shapes.json__ | 个体 MANO / UmeTrack 手形 | 解码相同个体的手部几何 |
*.hands.json | 左右手姿态参数与腕部变换 | MANO 顶点/关节与 UmeTrack 关键点 |
*.cameras.json | 逐帧相机内参、模型与外参 | 世界 → 相机变换及双目投影 |
*.hand_crops.json | 官方手部目标条目 | 确定该手帧是否 eligible |
*.info.json | 原始参考时间戳 | 同步帧身份与时序评分 |
候选池中的 annotations/*.tar.gz 只是从原 TAR 逐字节抽取的 JSON 成员,不包含图像,也不是重新标注的 GT。解码使用已同步的 MANO 资产及固定 toolkit;不通过模型预测反推 GT。
来源版本与缺失候选
HOT3D 来源版本:30fe9674782f32e1e5edba98476b6ff4300132c5
Hand Tracking Toolkit:950d64f7e8d2ba1fd38cd2ceede6608a8fa7f5aa
未计入候选的 7 段:000239, 000328, 000335, 000454, 000527, 000578, 001091(均为 clip- 前缀)。不把不完整源文件当作可用样本。
官方背景:HOT3D Toolkit · 固定版本 Hand Tracking Toolkit。本报告的筛选阈值和 Test200 规则以平台冻结证据为准。
02 · GT 如何筛选
筛选只依赖 GT 参数、相机投影与人工图像检查。预测误差、预测缺手、榜单分数均不参与筛选或候补排序。
第一层:MANO 与 UmeTrack 的指尖一致性
在同一帧、同一只手、同一坐标系内,计算两种参数化手模型对应五个指尖的绝对欧氏距离,单位 mm。不做刚体对齐,腕点不纳入这层筛选。
d(h, finger, t) = 1000 × ‖MANO_official_tip − UmeTrack_tip‖₂ D = max(h, finger) median_t d(h, finger, t) T = max(h, finger) P95_t d(h, finger, t)
| 要求 | 当前阈值 | 判定范围 |
|---|---|---|
| 有效配对 | 每只出现的手 ≥ 80 帧 | 150 帧内;官方目标配对完整 |
| 最差指尖中位数 D | ≤ 12 mm | 取所有有效手 × 五指中的最大值 |
| 最差指尖 P95 T | ≤ 16 mm | 每手每指先沿时间统计,再取最大值 |
| 最差手拇指 P95 | ≤ 12 mm | 对拇指单独收紧约束 |
[744, 320, 443, 554, 671]。它评估两种 GT 表示是否一致,不等于独立的物理精度证明,也不能保证图像中手部可见。历史初筛曾使用 T≤20 mm;当前最终规则为 T≤16 mm。第二层:小手与低活动同时命中
每只手独立检查 60 帧窗口,步长 15 帧,起点为 0、15、…、90。窗口至少包含 48 个 eligible 手帧,以下两个条件同时成立时命中;任一只手任一窗口命中,剔除整个 Case。
| 条件 | 计算方式 | 阈值 |
|---|---|---|
| 小手 | 每帧在两路相机分别计算 GT21 未裁剪包围框长边 / 图像短边,再取两路较大值 | 尺度严格 <8% 的有效帧占比 ≥80% |
| 低活动 | 在掌部局部坐标中,21 点相对窗口逐坐标中位数的平均位移,再对各帧取 P95 | P95 ≤5 mm |
局部坐标与百分位的准确实现
origin = P[5] # 腕点 x = normalize(P[8] − P[17]) # 食指 MCP − 小指 MCP v = P[11] − P[5] # 中指 MCP − 腕点 y = normalize(v − dot(v, x) × x) z = cross(x, y) local = (P − P[5]) @ stack(x, y, z) center = coordinatewise_median(local over eligible frames) shape_t = mean_j ‖local[t,j] − center[j]‖₂ × 1000 shape_P95 = percentile(shape_t, 95, method="linear")
该指标去除了整体平移和旋转,测量手形变化;不是腕部速度。没有“必须在画面下方”“必须垂直下垂”的位置条件。单独低活动不会触发剔除,窗口不足 48 个有效帧也不能按此规则判定。
原 Test200 的复筛中,这条规则命中 31 个 Case、183 个窗口。这是对原 200 个 Case 的结果,不是对全候选池的统计。最新 200 个 Case 的同规则扫描未命中。
第三层:人工可见性审核与候补
逐段查看双目原图、四个手部特写和三维骨架,记录遮挡、不可见、出画、贴合异常等问题。官方 crop 条目表示手帧可计分,不能替代真实可见性判断;数值门槛通过后仍可能被人工剔除。
保留通过审核的原 Case。候补在满足相同数值门槛和既有剔除清单后,按当前参与者数量、参与者 ID、当前 sequence 数量、拇指 P95、D、T、clip ID 确定性排序;每段 sequence 最多 5 个 Case。候补还须经过图像预检及人工确认。
03 · 参数如何转换为冻结 GT
MANO 解码
读取个体 10 维手形、逐帧 15 维 PCA 姿态及腕部旋转/平移,使用对应左右手 MANO 模型做前向计算。沿用 toolkit 的左手 shapedirs 修正及非 flat hand mean 配置,得到每只手的 778 个顶点与原生关节。这里是 GT 参数解码,不是运行 ScaleHP 预测模型。
保留前 16 个 MANO 关节,追加五个 ScaleHP 指尖顶点,再按下节的明确顺序重排。左右手使用相同语义索引,不通过预测匹配交换左右手。
世界坐标 → 左相机 native 坐标
P_ref = inverse(T_world_from_cam0) × P_world P_cam1 = inverse(T_world_from_cam1) × T_world_from_cam0 × P_ref
最终 points_scalehp21 固定为逐帧左相机 1201-1 的 native 坐标,单位 米。右相机投影使用该帧外参变换。3D 不因为图像旋转而旋转坐标轴,显示误差时才从米转为毫米。
双目投影与图像方向
使用原生相机模型投影(保留畸变模型),每路分别从世界点得到 native 像素,再对像素和原图做顺时针 90° 旋转。若 native 图像为 W×H:
(u_upright, v_upright) = (H − 1 − v_native, u_native) (W_upright, H_upright) = (H, W)
只做 CW90,不额外缩放、裁剪、拉伸或去畸变。二维点使用 upright 像素坐标;越界/不可投影点保留可用性语义,不能把它们拉回图像内以制造可见性。
可计分与时间同步
每手帧的 eligible 由官方 hand_crops 目标条目确定,并核对 MANO 参数是否可解码。缺少官方目标的手帧可有几何数据,但不据此升级为 eligible。逐帧保留 ref_timestamp_ns;视频、标定、GT 和播放帧编号共用 0…149 的身份。
04 · ScaleHP 的 21 点取点规则
此处“ScaleHP”指平台冻结的 scalehp-mano21-tips-v1 点定义。索引从 0 开始。五个指尖在前,腕点是 5,拇指 CMC 是 20。
tips = [744, 333, 444, 555, 672] # 拇指、食指、中指、无名指、小指 raw = concat(J_mano[:16], V_mano[tips]) order = [16,17,18,19,20,0,14,15,1,2,3,4,5,6,10,11,12,7,8,9,13] points_scalehp21 = raw[order]
拇指链:5 → 20 → 6 → 7 → 0。其余手指从腕点连接各自 MCP、PIP、DIP、指尖。完整骨架共 20 条边。
| 输出索引 | 语义 | MANO 来源 |
|---|---|---|
| 0 | 拇指指尖thumb_tip | 顶点 V[744] |
| 1 | 食指指尖index_tip | 顶点 V[333] |
| 2 | 中指指尖middle_tip | 顶点 V[444] |
| 3 | 无名指指尖ring_tip | 顶点 V[555] |
| 4 | 小指指尖pinky_tip | 顶点 V[672] |
| 5 | 腕点wrist | 关节 J[0] |
| 6 | 拇指 MCPthumb_mcp | 关节 J[14] |
| 7 | 拇指 IPthumb_ip | 关节 J[15] |
| 8 | 食指 MCPindex_mcp | 关节 J[1] |
| 9 | 食指 PIPindex_pip | 关节 J[2] |
| 10 | 食指 DIPindex_dip | 关节 J[3] |
| 11 | 中指 MCPmiddle_mcp | 关节 J[4] |
| 12 | 中指 PIPmiddle_pip | 关节 J[5] |
| 13 | 中指 DIPmiddle_dip | 关节 J[6] |
| 14 | 无名指 MCPring_mcp | 关节 J[10] |
| 15 | 无名指 PIPring_pip | 关节 J[11] |
| 16 | 无名指 DIPring_dip | 关节 J[12] |
| 17 | 小指 MCPpinky_mcp | 关节 J[7] |
| 18 | 小指 PIPpinky_pip | 关节 J[8] |
| 19 | 小指 DIPpinky_dip | 关节 J[9] |
| 20 | 拇指 CMCthumb_cmc | 关节 J[13] |
| 指尖 | 一致性筛选:官方 MANO | 最终 GT:ScaleHP |
|---|---|---|
| 拇指 | 744 | 744 |
| 食指 | 320 | 333 |
| 中指 | 443 | 444 |
| 无名指 | 554 | 555 |
| 小指 | 671 | 672 |
points_scalehp21;历史 points_canonical20 只是前 20 点,缺少拇指 CMC;原生 UmeTrack 的索引 20 是掌心,而非拇指 CMC。三者不能直接互换。报告中的 MANO 网格、UmeTrack21 为独立展示图层,评分仍使用冻结 ScaleHP21。05 · 如何冻结、验证与评分
先在独立开发数据根构建,再发布新版本目录。对于沿用 Case,保留 GT、标定、时间戳、视频输入及预览的原始字节;新增 Case 从同一来源解码 GT、旋转原 JPG 并编码为 FFV1 无损 MKV。PNG 精确帧通过按帧号解码该 MKV 获得。
| 产物 | 内容与约束 |
|---|---|
| GT JSONL | 150 帧;每手 eligible、ScaleHP21、双目 UV、原始时间戳 |
| frames / calibration | 图像帧与时间戳对应、逐帧相机参数、CW90 像素变换 |
| selection / inputs manifest | 明确 Case 清单、输入身份、GT 版本、点定义和来源 |
| review / approval / audit | 保留原审核记录、后续确认、筛选规则与解码审计;不伪造历史审核状态 |
| 冻结输入与工具 ZIP | 输入图像、清单、模板及工具;正式输入下载包不含 GT |
参数重建支持 --verify-against:核对 150 帧身份、eligible 和坐标,XYZ 最大差异要求 ≤10⁻⁷ m;独立投影核对要求 <0.02 px。冻结/发布验证绑定来源、标定、时间戳、清单和文件身份,拒绝覆写已有版本。
0,10,…,140 共 15 帧;时序评分使用全部 150 帧。可视化报告的 Abs-MPJPE21 曲线展示 150 帧,正式 Case 汇总仍沿用原评测的 15 帧口径;不可用帧断线,不填零。Abs-MPJPE21 不做对齐;RR-MPJPE21 分别减去 GT/预测的腕点 5,仍对全部 21 点求均值,包含归零的腕点。当前报告只在 GT、输入、标定和时间戳完全一致的 199 个 Case 上对照历史 v2 预测;新增 clip-000607 无预测。
06 · 当前题集与版本边界
| 版本 | 这次演进做了什么 | 历史处理 |
|---|---|---|
| Test200 v1 | 原冻结 200 Case,后续复筛发现 31 个“小手/低活动”候选 | 保留原题集与成绩 |
| Test200 v2 | 叠加小手规则及人工复核,最终保留 v1 的 165 个、加入 35 个;85 个 sequence | 独立 GT / 输入身份;不回写 v1 |
| Test200 v3 · 当前 | 保留 v2 的 199 个;因不可见剔除 clip-000422,加入已确认的 clip-000607;84 个 sequence | 保留 v2 中的 clip-000422 和旧评测 |
当前参与者分布:P0002 17、P0003 53、P0010 22、P0013 50、P0017 24、P0018 14、P0021 20。每 sequence 最多 5 个 Case。
新题集改变的是样本集合及其冻结身份,不改变 ScaleHP 21 点定义、米制 native 坐标或原八项评分口径。199 个重叠 Case 的历史分数不合成为“新 200 Case 成绩”;完整 v3 成绩应来自新的完整提交。
Test200 的选样不是随机抽样,也不保证与模型训练参与者隔离。解读结果时应同时报告题集版本与来源;MANO/UmeTrack 一致性和人工可见性审核均有各自局限。
07 · 实现入口与复现路径
| 实现 / 证据 | 职责 |
|---|---|
metadata/candidate-index.jsonl | 候选来源索引,含 D/T/拇指摘要及原始参数相对路径 |
history/reselection/screening/scan_official_pool.py | 官方指尖与 UmeTrack 的逐帧差异统计;历史扫描的 T≤20 标签不能当作当前最终通过判定 |
evidence/selection-policy.json | 当前 D12/T16/拇指12、候补与人工剔除的冻结政策;以 approval.json 记录后续确认 |
scripts/screen_hot3d_gt.py | 可复现的小手/低活动窗口判定 |
scripts/decode_hot3d_gt.py | MANO 解码、ScaleHP 取点、坐标与双目投影、重建一致性验证 |
scripts/freeze_hot3d_review.py | 在开发写根暂存新题集、媒体、清单、下载包和审计 |
scripts/publish_hot3d_revision.py | 只读预演;显式授权后发布新版本资产 |
scripts/build_hot3d_gt_review.pyscripts/hot3d_review_geometry.py | 从冻结 GT / 历史证据生成 review 数据、MANO 网格和原生 UmeTrack21 |
src/ego_platform/hot3d_pose.pydocs/benchmark.md | 21 点语义、连边、评分帧与题集契约 |
复现单 Case 的 GT 解码与窗口筛选
使用有 NumPy、torch、MANO 的环境;先配置 EGO_ENV=dev 和自己的 EGO_DATA_ROOT。以下为参数示例,输出必须在独立开发写根内。
PYTHONPATH=src python -B scripts/decode_hot3d_gt.py \ --pool /path/to/hot3d-gt-candidate-pool \ --clip-id clip-000607 \ --output /path/to/own-dev-root/tmp/clip-000607.jsonl \ --verify-against /path/to/frozen/clip-000607/gt.jsonl PYTHONPATH=src python scripts/screen_hot3d_gt.py \ --gt /path/to/own-dev-root/tmp/clip-000607.jsonl \ --calibration /path/to/frozen/clip-000607/calibration.json
解码和筛选不会自动选满 200 个、覆盖正式 GT 或触发模型评测。后续仍须按相同标准选样、人工确认、冻结新版本并按发布流程上线。
来源校验保留已有参数、GT 与冻结证据身份检查。已有大输入的校验回执可以在满足冻结发布工具的复用条件时显式复用;不把复用回执描述成重新计算了 SHA,也不为展示报告反复扫描原 TAR/MKV。
固定版本 GT 解码实现 · 固定版本小手筛选实现 · 题集规范与版本记录(内部仓库链接需相应访问权限)