Add EgoRenderer synthetic data, public progress, version history and reader
Browse files- .gitattributes +3 -0
- README.md +25 -0
- data/egorender-progress.json +1 -0
- data/egorender-registry.json +0 -0
- data/egorender.json +54 -0
- deployment_manifest.json +74 -13
- downloads/egorender_reader.py +118 -0
- index.html +14 -2
- media/egorender/bimanual.png +3 -0
- media/egorender/mirror.png +3 -0
- media/egorender/multiple.png +3 -0
- src/page.html +14 -2
- src/synthetic.css +83 -0
- src/synthetic.js +144 -0
- src/vendor/lucide.min.js +0 -0
.gitattributes
CHANGED
|
@@ -104,3 +104,6 @@ media/figures/show3d_0_0.webp filter=lfs diff=lfs merge=lfs -text
|
|
| 104 |
media/figures/show3d_1_0.webp filter=lfs diff=lfs merge=lfs -text
|
| 105 |
media/figures/taco_0_0.webp filter=lfs diff=lfs merge=lfs -text
|
| 106 |
media/figures/xperience_0_0.webp filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
media/figures/show3d_1_0.webp filter=lfs diff=lfs merge=lfs -text
|
| 105 |
media/figures/taco_0_0.webp filter=lfs diff=lfs merge=lfs -text
|
| 106 |
media/figures/xperience_0_0.webp filter=lfs diff=lfs merge=lfs -text
|
| 107 |
+
media/egorender/bimanual.png filter=lfs diff=lfs merge=lfs -text
|
| 108 |
+
media/egorender/mirror.png filter=lfs diff=lfs merge=lfs -text
|
| 109 |
+
media/egorender/multiple.png filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -37,6 +37,31 @@ pinned: false
|
|
| 37 |
| 标注方法 | `#methods` | 8 种标注方法的流程、可信度、常见问题、SOMA 路线 |
|
| 38 |
| SOMA 转换 | `#soma` | 从各类真值到 SOMA 原生 shape + pose 的路线 R0 到 R7 |
|
| 39 |
| 样例素材 | `#samples` | 抽样分类、类别计数和素材覆盖 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
## 目录
|
| 42 |
|
|
|
|
| 37 |
| 标注方法 | `#methods` | 8 种标注方法的流程、可信度、常见问题、SOMA 路线 |
|
| 38 |
| SOMA 转换 | `#soma` | 从各类真值到 SOMA 原生 shape + pose 的路线 R0 到 R7 |
|
| 39 |
| 样例素材 | `#samples` | 抽样分类、类别计数和素材覆盖 |
|
| 40 |
+
| 合成数据 | `#synthetic/progress` | EgoRenderer只读进度、队列、主机资源与明确的更新时间 |
|
| 41 |
+
| 合成版本 | `#synthetic/versions` | V001–V006特性、V006关键修订、可搜索完整执行登记簿 |
|
| 42 |
+
| 数据读取 | `#synthetic/reading` | 双深度、相机、2-bit可见分割、原生手参数和历史路径读取 |
|
| 43 |
+
| 合成样例 | `#synthetic/samples` | 三种Hard Case的实际历史预览与标签语义 |
|
| 44 |
+
|
| 45 |
+
## EgoRenderer进度与文档
|
| 46 |
+
|
| 47 |
+
`data/egorender.json`保存审定的版本/数据语义;`data/egorender-registry.json`
|
| 48 |
+
仅包含执行登记簿的公开字段,不能把方法状态当作当前队列状态。
|
| 49 |
+
`downloads/egorender_reader.py`是只依赖NumPy的逐帧读取器,缺少任一深度
|
| 50 |
+
通道时明确报错,不伪造GT。本文档不分发原始数据、模型权重或整套合成包。
|
| 51 |
+
|
| 52 |
+
公共进度来自独立只读数据仓库`DarthSoluke/EgoRendererStatus/status.json`。
|
| 53 |
+
页面每60秒读取;超过3分钟没有更新就标记过期。发布器只读取当前SQLite、
|
| 54 |
+
少量控制记录和主机统计,不提供任务启动、停止、删除等API。
|
| 55 |
+
公开快照不含主机地址、PID、SSH凭据、内部文件路径或原始错误堆栈。
|
| 56 |
+
|
| 57 |
+
内部工作区的`tools/publish_progress.py`运行在操作员本机,复用项目唯一SSH master,
|
| 58 |
+
Hugging Face凭据只从本机授权缓存读取,不传到PRO或浏览器。
|
| 59 |
+
一次前台快照用`--once`;有限后台服务用`--background --hours 168 --interval 60`。
|
| 60 |
+
它不创建cron或聊天自动任务;本机睡眠/断线/服务结束后,网站保留旧快照并显示过期。
|
| 61 |
+
发布器有singleton锁,已有服务运行时不要再启动第二个。
|
| 62 |
+
|
| 63 |
+
采集、发布、登记簿导出和内部测试脚本保留在团队BEDLAM2工作区,不随此公开Space部署。
|
| 64 |
+
公开Space只包含静态页面、脱敏摘要、样例和独立读取器。页面源码更新后运行`tools/build.py`。
|
| 65 |
|
| 66 |
## 目录
|
| 67 |
|
data/egorender-progress.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"schema": "egorenderer.public_progress.v1", "collected_at": 1793560254.9093049, "refresh_seconds": 60, "dataset_release": "V006", "execution_revision": "350", "queue_alive": true, "admission_held": false, "counts": {"complete": 153, "running": 9, "ready": 735, "failed": 6, "total": 903}, "scope": {"sources": 301, "source_frames": 218273, "variants": 3, "expected_variant_frames": 654819}, "coverage": {"completed_variant_frames": 112294, "sources_with_any_variant": 151, "sources_with_all_variants": 1}, "throughput": {"packages_last_hour": 14, "queued_eta_hours": 53.1}, "training_ready": null, "known_gt_missing": [{"source": "arctic_s01_capsulemachine_grab_01", "variant": 1}, {"source": "arctic_s09_laptop_use_03", "variant": 1}], "auxiliary_fits": "PENDING_UPSTREAM_HANDOFF", "jobs": [{"source": "arctic_s01_laptop_use_04", "variant": 1, "state": "FAILED", "frames": 1012, "phase": "FAILED", "storage": null}, {"source": "arctic_s01_mixer_grab_01", "variant": 1, "state": "FAILED", "frames": 593, "phase": "FAILED", "storage": null}, {"source": "arctic_s01_mixer_use_01", "variant": 1, "state": "FAILED", "frames": 638, "phase": "FAILED", "storage": null}, {"source": "arctic_s01_waffleiron_grab_01", "variant": 1, "state": "FAILED", "frames": 600, "phase": "FAILED", "storage": null}, {"source": "arctic_s02_waffleiron_use_02", "variant": 1, "state": "FAILED", "frames": 652, "phase": "FAILED", "storage": null}, {"source": "arctic_s05_scissors_use_02", "variant": 1, "state": "FAILED", "frames": 687, "phase": "FAILED", "storage": "SSD"}, {"source": "arctic_s06_microwave_use_02", "variant": 1, "state": "RUNNING", "frames": 965, "phase": "CYCLES_OPTIX_16SPP_GPU_OIDN", "storage": "NAS"}, {"source": "arctic_s06_mixer_grab_01", "variant": 1, "state": "RUNNING", "frames": 734, "phase": "CYCLES_OPTIX_16SPP_GPU_OIDN", "storage": "NAS"}, {"source": "arctic_s06_mixer_use_01", "variant": 1, "state": "RUNNING", "frames": 836, "phase": "VIDEO_POST_AND_DEPTH_OVERLAPPED", "storage": "SSD"}, {"source": "arctic_s06_notebook_grab_01", "variant": 1, "state": "RUNNING", "frames": 589, "phase": "VIDEO_POST_AND_DEPTH_OVERLAPPED", "storage": "NAS"}, {"source": "arctic_s06_notebook_use_01", "variant": 1, "state": "RUNNING", "frames": 681, "phase": "VIDEO_POST_AND_DEPTH_OVERLAPPED", "storage": "SSD"}, {"source": "arctic_s06_notebook_use_02", "variant": 1, "state": "RUNNING", "frames": 624, "phase": "CYCLES_OPTIX_16SPP_GPU_OIDN", "storage": "NAS"}, {"source": "arctic_s06_phone_grab_01", "variant": 1, "state": "RUNNING", "frames": 637, "phase": "VIDEO_POST_AND_DEPTH_OVERLAPPED", "storage": "SSD"}, {"source": "arctic_s06_phone_use_01", "variant": 1, "state": "RUNNING", "frames": 753, "phase": "CYCLES_OPTIX_16SPP_GPU_OIDN", "storage": "SSD"}, {"source": "arctic_s06_phone_use_02", "variant": 1, "state": "RUNNING", "frames": 701, "phase": "V5_THREE_MOUNT_INDEPENDENT_CAMERA", "storage": "SSD"}], "recent": [{"at": 1793560245.401714, "source": "arctic_s06_mixer_use_02", "variant": 1}, {"at": 1793560036.1191676, "source": "arctic_s06_laptop_use_02_retake", "variant": 1}, {"at": 1793559677.3499768, "source": "arctic_s06_espressomachine_use_04", "variant": 1}, {"at": 1793559110.8940485, "source": "arctic_s06_espressomachine_use_01", "variant": 1}, {"at": 1793559050.4718637, "source": "arctic_s06_laptop_use_01_retake", "variant": 1}, {"at": 1793559007.6832476, "source": "arctic_s06_microwave_use_01", "variant": 1}, {"at": 1793558635.1213675, "source": "arctic_s06_microwave_grab_01", "variant": 1}, {"at": 1793558334.3597572, "source": "arctic_s06_ketchup_use_02", "variant": 1}], "hardware": {"gpus": [{"index": 0, "utilization": 1, "used_mib": 7090, "total_mib": 97887}, {"index": 1, "utilization": 10, "used_mib": 10023, "total_mib": 97887}, {"index": 2, "utilization": 24, "used_mib": 16369, "total_mib": 97887}, {"index": 3, "utilization": 98, "used_mib": 15712, "total_mib": 97887}], "cpu_percent": 27.6, "cpu_logical": 72}, "storage": {"ssd_available_gib": 1242.7, "nas_available_gib": 3879.1, "ssd_budget_gib": 896.0, "ssd_reserved_gib": 790.9, "nas_active": 4, "nas_limit": 4}, "hardcases": {"complete": 15, "expected": 15, "frames": 10838, "training_ready": false}, "oakink_assets": {"counts": {"COMPLETE": 571, "FAILED": 13, "READY": 38}, "total": 622, "final_render_count": null}, "history": [{"at": 1793560194.9028523, "complete": 152}, {"at": 1793560254.9093049, "complete": 153}]}
|
data/egorender-registry.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/egorender.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"updated": "2026-11-02",
|
| 3 |
+
"status_url": "https://huggingface.co/datasets/DarthSoluke/EgoRendererStatus/resolve/main/status.json",
|
| 4 |
+
"release": "V006",
|
| 5 |
+
"versions": [
|
| 6 |
+
{"id":"001","title":"参数标注基线","state":"历史方法","scope":"GRAB个体化手表面 → 标准MANO辅助参数","features":["左右手独立;每手每序列共享10维beta,每帧48维轴角pose。","778个对应顶点均匀L2,原始逐帧translation固定;无joint loss、姿态prior或额外对齐。","沿用明确交付时间轴;拟合参数不是原始shape/pose GT。"],"read":"同时读取beta、拟合pose、原始translation和source_frame_indices;不能把拟合pose与原始pose混用。"},
|
| 7 |
+
{"id":"002","title":"主动双目深度试验","state":"有限试验,非全量发布","scope":"LingBot-Depth论文启发的主动双目","features":["Blender共享投影散斑,原可见光BSDF作为灰度近似,不是实测红外材料。","OpenCV StereoSGBM MODE_HH、8路径;左右一致性、散斑滤除。","试验配置768×432、基线0.05m;没有使用GT辅助填洞。"],"read":"输出为左相机轴向Z,米;0表示无效。只代表试验覆盖,不代表全部GRAB已经补齐此深度。"},
|
| 8 |
+
{"id":"003","title":"稀疏深度观测试验","state":"有限试验,非当前深度分支","scope":"PromptDA式稀疏锚点与插值","features":["192×256原生网格,保持比例letterbox;稀疏网格加入固定种子扰动。","RGB+XY特征空间中8近邻反距离平方插值,保留锚点。","GT只提供稀疏锚点;不是当前V006的主动双目模拟。"],"read":"区分原生低分辨率观测、上采样深度和有效掩码;不可与V002/V006的双目噪声语义混用。"},
|
| 9 |
+
{"id":"004","title":"左手基底修正试验","state":"单序列试验","scope":"GRAB s7/camera_browse_1左手,275个交付帧","features":["标准左MANO的shapedirs[:,0,:]在内存中翻转一次;不修改原模型文件。","右手复用V001;纯顶点L2、固定原始translation。","试验没有替换全部历史数据包,也没有重启RGB渲染。"],"read":"左手参数必须与修正后的基底配套解码;不得用未经修正的模型直接重建。"},
|
| 10 |
+
{"id":"005","title":"GRAB完整参数更新","state":"历史交付完成","scope":"1,314个源拟合组;2,425个含历史变体的数据包","features":["将V004左手基底修正扩展到完整已接收GRAB交付范围。","右手V001参数逐字节复用;RGB、原深度、mask、相机、接触标注沿用既有渲染。","REPLACEMENT_COMPLETE指参数附件替换完成,不是一次全新的像素渲染。","1,314源组与2,425包是不同计数口径;后续V006 GRAB新模拟深度尚未据此宣称完成。"],"read":"包内mano_fit_current.json指向mano_beta_pose_fit_only_v005.npz。左右beta/pose与相应基底成套读取;原MANO与接触label保持独立。"},
|
| 11 |
+
{"id":"006","title":"原生手部的强随机化合成","state":"当前数据版本","scope":"ARCTIC 301公开源动作 × 3变体;OakInk2 622源动作独立管理","features":["渲染使用数据集原生手,不使用拟合MANO/SOMA驱动像素;辅助拟合另存并标明非GT。","ARCTIC使用原始SMPL-X身体与原始MANO手的批准资产;OakInk使用当前275人体/276衣物及原生手部组装路线。","1536×864、30fps、Cycles OptiX 16 SPP、GPU OIDN、实际DLSS5 Feature18、时序history 0.9、最终CRF10。","固定种子随机环境、材质、光照与道具;头/胸/肩三部位,相机平移与旋转扰动解耦,保留原HOI运动与源帧。","当前深度为干净GT与带噪传感器两套;都采用毫米量化和快速level1压缩。","SSD工作集与NAS自适应并行;350等编号是执行修订,不是新的数据发布版本。"],"read":"以当前INDEX.json及每包metadata.json、depth_bundle.json为入口;按各包实际文件/schema读取,不能把整个V006假定为同一种深度历史格式。"}
|
| 12 |
+
],
|
| 13 |
+
"milestones": [
|
| 14 |
+
["279","原生表面","修复ARCTIC原始MANO与SMPL-X腕部表面;属于资产交付,不等同最终RGB合成完成。"],
|
| 15 |
+
["287","主队列","301源动作、3个随机变体、903个计划包。"],
|
| 16 |
+
["320","Hard Cases","双手交叉、真实镜面、多人各5条;15条、10,838主源帧,独立于903主队列。"],
|
| 17 |
+
["330","分阶段调度","GPU阶段与CPU尾处理分离,最多12条在途,仍受共享物理槽位限制。"],
|
| 18 |
+
["333–338","已废止深度策略","曾出现sensor-only输出及转换;不再作为当前双深度交付标准。"],
|
| 19 |
+
["339–340","双深度","恢复干净GT;随后GT也毫米量化,按帧扩展整数位宽,不截断远背景。"],
|
| 20 |
+
["341–344","SSD工作集","完整SSD工作集、多GPU试跑及仅补深度恢复;没有达到10分钟端到端目标。"],
|
| 21 |
+
["345–348","容量与接管","修复容量等待/旧日志接管;896GiB总配额,200GiB空闲底线,主GPU优先。"],
|
| 22 |
+
["349","CPU/I/O并行","同卡2个工作目录;双通道独立压缩写入、4帧预读、运动向量4路预���。"],
|
| 23 |
+
["350","SSD+NAS","NAS并行1至4条自适应;保留完整父预留,双目临时缓冲占256MiB SSD。" ]
|
| 24 |
+
],
|
| 25 |
+
"channels": [
|
| 26 |
+
["RGB","rgb.mp4","1536×864 · 30fps","最终后处理图像,不是raw辐射度;以包内metadata为准。"],
|
| 27 |
+
["干净GT深度","depth_gt_mm.npz","camera-Z · 1mm量化","无传感器噪声;逐帧uint16/32/64,远背景可超过65.535m,不静默截断。"],
|
| 28 |
+
["带噪深度","depth_sensor_u16.npz","camera-Z · uint16毫米","主动双目模拟结果;带有效掩码,不以GT补洞,不是标定过的商用相机。"],
|
| 29 |
+
["可见分割","masks_visible_2bit.npz","每像素2 bit","按classes和bitorder解码;只表示可见类别,不等同接触标注。"],
|
| 30 |
+
["相机","camera_parameters.npz","K · world_to_camera · camera_to_world","同时保存frame_index、source_frame_index、image_size和坐标约定;不是只能从视频猜相机。"],
|
| 31 |
+
["原生手参数","mano_parameters.npz","原始MANO参数","逐手读取原始旋转、pose、translation、shape及源帧;不要混入辅助拟合参数。"],
|
| 32 |
+
["场景与追溯","metadata.json / scene_randomization.json","版本 · 种子 · SHA","按包追踪生成身份;嵌套旧SSD路径需显式artifact_locations解析。"],
|
| 33 |
+
["辅助拟合","按上游handoff登记","MANO/SOMA · 非GT","不参与渲染;缺失时不可宣称完整训练包就绪。" ]
|
| 34 |
+
],
|
| 35 |
+
"quality": [
|
| 36 |
+
"完成计数表示渲染与深度流程终态,不代表逐帧视觉验收、全部GT缺口已恢复或辅助标签齐全。",
|
| 37 |
+
"ARCTIC覆盖301条公开完整动作;官方38条隐藏test GT不在此标注覆盖中。随机合成相机不是全部官方原始相机图像的重渲染承诺。",
|
| 38 |
+
"V006历史包存在深度格式差异;必须检查每包文件、有效掩码和时钟。两个已知旧包曾缺GT,实时页单独显示当前仍缺失项。",
|
| 39 |
+
"训练/验证划分应按原始source分组,同一动作的不同随机变体不能分到训练与验证两侧。",
|
| 40 |
+
"模拟深度采用可见光材质灰度代理与调用方选择的噪声参数;不是LingBot作者原始模拟器,也不是某型号相机的物理标定。",
|
| 41 |
+
"原始数据、模型权重与派生数据使用仍受各上游许可约束;此页提供文档和状态,不分发整套数据。"
|
| 42 |
+
],
|
| 43 |
+
"samples": [
|
| 44 |
+
{"title":"双手交叉","image":"media/egorender/bimanual.png","source":"ARCTIC · Hard Case参考帧","note":"真实源动作筛选;同源动作的历史交叉参考帧。"},
|
| 45 |
+
{"title":"镜面反射","image":"media/egorender/mirror.png","source":"ARCTIC · Hard Case参考帧","note":"实际镜面几何;镜中人不是额外的人体实例。镜面GT为首交点深度。"},
|
| 46 |
+
{"title":"多人","image":"media/egorender/multiple.png","source":"ARCTIC · Hard Case参考帧","note":"两套源人体与对应衣物;每个人保留独立源帧映射。"}
|
| 47 |
+
],
|
| 48 |
+
"references": [
|
| 49 |
+
["LingBot-Depth §3.1","https://arxiv.org/html/2601.17895v1#S3.SS1"],
|
| 50 |
+
["LingBot官方代码","https://github.com/Robbyant/lingbot-depth"],
|
| 51 |
+
["ARCTIC","https://arctic.is.tue.mpg.de/"],
|
| 52 |
+
["只读进度数据","https://huggingface.co/datasets/DarthSoluke/EgoRendererStatus"]
|
| 53 |
+
]
|
| 54 |
+
}
|
deployment_manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
"repo_id": "DarthSoluke/EgoNetDatasetResearch",
|
| 3 |
"repo_type": "space",
|
| 4 |
-
"base_revision": "
|
| 5 |
"sdk": "static",
|
| 6 |
"app_file": "index.html",
|
| 7 |
"original_archive_sha256": "9d437cf8f781dd25354e3de6bdccf4a3e63c5eeb01efab5943ecfee709769160",
|
|
@@ -22,14 +22,29 @@
|
|
| 22 |
},
|
| 23 |
{
|
| 24 |
"path": "README.md",
|
| 25 |
-
"bytes":
|
| 26 |
-
"sha256": "
|
| 27 |
},
|
| 28 |
{
|
| 29 |
"path": "data/datasets.json",
|
| 30 |
"bytes": 44434,
|
| 31 |
"sha256": "ba13d1302517047df8051e330c277afe704dabb17be38d3a7b57ce3017453992"
|
| 32 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
{
|
| 34 |
"path": "data/figures.json",
|
| 35 |
"bytes": 80223,
|
|
@@ -50,6 +65,11 @@
|
|
| 50 |
"bytes": 335515,
|
| 51 |
"sha256": "841f2aba69b09394f75c1645c49a8ac5f84f7dbc3439974afb24cdf7111c9fd5"
|
| 52 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
{
|
| 54 |
"path": "data/sampling.json",
|
| 55 |
"bytes": 27158,
|
|
@@ -60,10 +80,15 @@
|
|
| 60 |
"bytes": 27441,
|
| 61 |
"sha256": "13095d6debfc8df33353c3d1825115397af17f770d01b083972b84931db5251f"
|
| 62 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
{
|
| 64 |
"path": "index.html",
|
| 65 |
-
"bytes":
|
| 66 |
-
"sha256": "
|
| 67 |
},
|
| 68 |
{
|
| 69 |
"path": "media/.gitkeep",
|
|
@@ -310,6 +335,21 @@
|
|
| 310 |
"bytes": 42356,
|
| 311 |
"sha256": "5308a14db33fef704c22d5fb9753dd32f96f58515c0e71ac50f9fa12f78cfff4"
|
| 312 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
{
|
| 314 |
"path": "media/egorenderer/egorenderer_v005_s1_mug_drink_1_233_gt.jpg",
|
| 315 |
"bytes": 28660,
|
|
@@ -1402,8 +1442,23 @@
|
|
| 1402 |
},
|
| 1403 |
{
|
| 1404 |
"path": "src/page.html",
|
| 1405 |
-
"bytes":
|
| 1406 |
-
"sha256": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1407 |
},
|
| 1408 |
{
|
| 1409 |
"path": "tools/add_preview.py",
|
|
@@ -1434,14 +1489,20 @@
|
|
| 1434 |
"path": "tools/server_sync.py",
|
| 1435 |
"bytes": 6502,
|
| 1436 |
"sha256": "710eee5a04f8a9e06d7101975a1fd8e2e89d55c07c6d9934265a7ca3fdb2699f"
|
| 1437 |
-
},
|
| 1438 |
-
{
|
| 1439 |
-
"path": "data/publication.json",
|
| 1440 |
-
"bytes": 652,
|
| 1441 |
-
"sha256": "9e14b55fc17ef3b63053340170a9c5548418636a88b7d098c5cbd71ab01d9318"
|
| 1442 |
}
|
| 1443 |
],
|
| 1444 |
"host_managed_files": [
|
| 1445 |
".gitattributes"
|
| 1446 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1447 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"repo_id": "DarthSoluke/EgoNetDatasetResearch",
|
| 3 |
"repo_type": "space",
|
| 4 |
+
"base_revision": "84afe8026fdafe13d3bde52ff441f1a4e29e9781",
|
| 5 |
"sdk": "static",
|
| 6 |
"app_file": "index.html",
|
| 7 |
"original_archive_sha256": "9d437cf8f781dd25354e3de6bdccf4a3e63c5eeb01efab5943ecfee709769160",
|
|
|
|
| 22 |
},
|
| 23 |
{
|
| 24 |
"path": "README.md",
|
| 25 |
+
"bytes": 6107,
|
| 26 |
+
"sha256": "62f7e420397ae3278205de5eb65beea6ce24a5922a4fbb91d66a016d7f3ab3ca"
|
| 27 |
},
|
| 28 |
{
|
| 29 |
"path": "data/datasets.json",
|
| 30 |
"bytes": 44434,
|
| 31 |
"sha256": "ba13d1302517047df8051e330c277afe704dabb17be38d3a7b57ce3017453992"
|
| 32 |
},
|
| 33 |
+
{
|
| 34 |
+
"path": "data/egorender-progress.json",
|
| 35 |
+
"bytes": 4406,
|
| 36 |
+
"sha256": "c50578bd011e8675bc7d76216710c813b224aa90ec318ddd91bf1c2de7ba082d"
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"path": "data/egorender-registry.json",
|
| 40 |
+
"bytes": 103691,
|
| 41 |
+
"sha256": "dbcfd6bc403aff85b29872d486ab53ac968268cdf2e31c7f914a8e61cc04b5b1"
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"path": "data/egorender.json",
|
| 45 |
+
"bytes": 8318,
|
| 46 |
+
"sha256": "fc323c20af1c5c62448931390afa3ff1f29674f214ad82c5231467142751dd74"
|
| 47 |
+
},
|
| 48 |
{
|
| 49 |
"path": "data/figures.json",
|
| 50 |
"bytes": 80223,
|
|
|
|
| 65 |
"bytes": 335515,
|
| 66 |
"sha256": "841f2aba69b09394f75c1645c49a8ac5f84f7dbc3439974afb24cdf7111c9fd5"
|
| 67 |
},
|
| 68 |
+
{
|
| 69 |
+
"path": "data/publication.json",
|
| 70 |
+
"bytes": 652,
|
| 71 |
+
"sha256": "9e14b55fc17ef3b63053340170a9c5548418636a88b7d098c5cbd71ab01d9318"
|
| 72 |
+
},
|
| 73 |
{
|
| 74 |
"path": "data/sampling.json",
|
| 75 |
"bytes": 27158,
|
|
|
|
| 80 |
"bytes": 27441,
|
| 81 |
"sha256": "13095d6debfc8df33353c3d1825115397af17f770d01b083972b84931db5251f"
|
| 82 |
},
|
| 83 |
+
{
|
| 84 |
+
"path": "downloads/egorender_reader.py",
|
| 85 |
+
"bytes": 5205,
|
| 86 |
+
"sha256": "14e89b041942ac37d3bd199b7b320968d76cebec1893ad117e50f3f6c405435e"
|
| 87 |
+
},
|
| 88 |
{
|
| 89 |
"path": "index.html",
|
| 90 |
+
"bytes": 51650,
|
| 91 |
+
"sha256": "384511ae956b1b66db2caac15dfaf6e83acc34fbaa2f3098f58b44aef4b34e22"
|
| 92 |
},
|
| 93 |
{
|
| 94 |
"path": "media/.gitkeep",
|
|
|
|
| 335 |
"bytes": 42356,
|
| 336 |
"sha256": "5308a14db33fef704c22d5fb9753dd32f96f58515c0e71ac50f9fa12f78cfff4"
|
| 337 |
},
|
| 338 |
+
{
|
| 339 |
+
"path": "media/egorender/bimanual.png",
|
| 340 |
+
"bytes": 1535120,
|
| 341 |
+
"sha256": "9c7a584bc8f48d63afbd69532388f164aa65494f2689f49df7dd2235802a7b9a"
|
| 342 |
+
},
|
| 343 |
+
{
|
| 344 |
+
"path": "media/egorender/mirror.png",
|
| 345 |
+
"bytes": 1506794,
|
| 346 |
+
"sha256": "8e1804b8396766b25ca61d42d09b3cca3482d867cb83cb74897ba1f22a054a26"
|
| 347 |
+
},
|
| 348 |
+
{
|
| 349 |
+
"path": "media/egorender/multiple.png",
|
| 350 |
+
"bytes": 1843154,
|
| 351 |
+
"sha256": "2326bceaf25c4bea3aab582909f4d1b21b3de9952dd83b5b18c8d49114afb9c5"
|
| 352 |
+
},
|
| 353 |
{
|
| 354 |
"path": "media/egorenderer/egorenderer_v005_s1_mug_drink_1_233_gt.jpg",
|
| 355 |
"bytes": 28660,
|
|
|
|
| 1442 |
},
|
| 1443 |
{
|
| 1444 |
"path": "src/page.html",
|
| 1445 |
+
"bytes": 51301,
|
| 1446 |
+
"sha256": "93caabf3872a471989e9dbad5bce5ee81fea62811aff02312c04e67b5cd57809"
|
| 1447 |
+
},
|
| 1448 |
+
{
|
| 1449 |
+
"path": "src/synthetic.css",
|
| 1450 |
+
"bytes": 7113,
|
| 1451 |
+
"sha256": "5ee57a96284232bb2a2b0d4c5edac56f271acbbf0e777af5e640f62947fc225f"
|
| 1452 |
+
},
|
| 1453 |
+
{
|
| 1454 |
+
"path": "src/synthetic.js",
|
| 1455 |
+
"bytes": 19884,
|
| 1456 |
+
"sha256": "a802e2b571af7606d6fff77b83e917bb43bd14c1c3f60b42525942ee6376f697"
|
| 1457 |
+
},
|
| 1458 |
+
{
|
| 1459 |
+
"path": "src/vendor/lucide.min.js",
|
| 1460 |
+
"bytes": 357796,
|
| 1461 |
+
"sha256": "3411692820cb8d47543f69496aa25fd603a358f4498046f41c508a5a3342210e"
|
| 1462 |
},
|
| 1463 |
{
|
| 1464 |
"path": "tools/add_preview.py",
|
|
|
|
| 1489 |
"path": "tools/server_sync.py",
|
| 1490 |
"bytes": 6502,
|
| 1491 |
"sha256": "710eee5a04f8a9e06d7101975a1fd8e2e89d55c07c6d9934265a7ca3fdb2699f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1492 |
}
|
| 1493 |
],
|
| 1494 |
"host_managed_files": [
|
| 1495 |
".gitattributes"
|
| 1496 |
+
],
|
| 1497 |
+
"synthetic_module": {
|
| 1498 |
+
"data_release": "V006",
|
| 1499 |
+
"documented_releases": 6,
|
| 1500 |
+
"execution_entries": 350,
|
| 1501 |
+
"progress_repository": "DarthSoluke/EgoRendererStatus",
|
| 1502 |
+
"public_refresh_seconds": 60,
|
| 1503 |
+
"stale_after_seconds": 180,
|
| 1504 |
+
"read_only": true,
|
| 1505 |
+
"updated": "2026-11-02",
|
| 1506 |
+
"original_catalog_entries_preserved": 46
|
| 1507 |
+
}
|
| 1508 |
}
|
downloads/egorender_reader.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Read frame-indexed EgoRenderer V006 depth and visible masks with NumPy only.
|
| 2 |
+
|
| 3 |
+
No pickle loading, network access, fitting or rendering. Keep one reader open
|
| 4 |
+
per sequence/loader worker. These formats do not cover every historical V005 NPZ.
|
| 5 |
+
"""
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import hashlib
|
| 8 |
+
import json
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class DepthReader:
|
| 13 |
+
def __init__(self, path):
|
| 14 |
+
self.archive = np.load(path, allow_pickle=False)
|
| 15 |
+
try:
|
| 16 |
+
self.frames = self.archive['source_frame_index']
|
| 17 |
+
self.width = int(self.archive['width'])
|
| 18 |
+
self.shape = tuple(self.archive['frame_shape'])
|
| 19 |
+
self.dtype = np.dtype(str(self.archive['depth_dtype']))
|
| 20 |
+
self.scale = None
|
| 21 |
+
self.frame_dtypes = (self.dtype,)
|
| 22 |
+
self.adaptive = False
|
| 23 |
+
if self.dtype == np.uint16:
|
| 24 |
+
if str(self.archive['quantization']) != 'uint16_mm_v1':
|
| 25 |
+
raise ValueError('Unknown quantized sensor format')
|
| 26 |
+
self.scale = float(self.archive['depth_scale_m'])
|
| 27 |
+
elif self.dtype == np.uint64:
|
| 28 |
+
if str(self.archive['quantization']) != 'adaptive_uint_mm_v1':
|
| 29 |
+
raise ValueError('Unknown adaptive GT format')
|
| 30 |
+
self.frame_dtypes = tuple(np.dtype(x) for x in self.archive['frame_dtypes'])
|
| 31 |
+
if self.frame_dtypes != (np.dtype('uint16'), np.dtype('uint32'), np.dtype('uint64')):
|
| 32 |
+
raise ValueError('Unexpected adaptive integer types')
|
| 33 |
+
self.scale = float(self.archive['depth_scale_m'])
|
| 34 |
+
self.adaptive = True
|
| 35 |
+
elif self.dtype not in (np.float16, np.float32):
|
| 36 |
+
raise ValueError('Unsupported stored depth type')
|
| 37 |
+
if self.scale is not None and self.scale != .001:
|
| 38 |
+
raise ValueError('Unknown depth scale')
|
| 39 |
+
except BaseException:
|
| 40 |
+
self.archive.close()
|
| 41 |
+
raise
|
| 42 |
+
|
| 43 |
+
def __len__(self):
|
| 44 |
+
return len(self.frames)
|
| 45 |
+
|
| 46 |
+
def frame(self, index):
|
| 47 |
+
if not 0 <= index < len(self.frames):
|
| 48 |
+
raise IndexError(index)
|
| 49 |
+
depth = self.archive[f'depth/{index:06d}']
|
| 50 |
+
bits = self.archive[f'valid_bits/{index:06d}']
|
| 51 |
+
if depth.shape != self.shape or depth.dtype not in self.frame_dtypes:
|
| 52 |
+
raise ValueError('Frame differs from archive metadata')
|
| 53 |
+
valid = np.unpackbits(bits, axis=1, bitorder='little')[:, :self.width].astype(bool)
|
| 54 |
+
if self.scale is not None:
|
| 55 |
+
if not np.array_equal(depth > 0, valid):
|
| 56 |
+
raise ValueError('Quantized depth validity differs')
|
| 57 |
+
depth = ((depth.astype(np.float64)*self.scale).astype(np.float32) if self.adaptive
|
| 58 |
+
else depth.astype(np.float32)*np.float32(self.scale))
|
| 59 |
+
return depth, valid, int(self.frames[index])
|
| 60 |
+
|
| 61 |
+
def close(self):
|
| 62 |
+
self.archive.close()
|
| 63 |
+
|
| 64 |
+
def __enter__(self):
|
| 65 |
+
return self
|
| 66 |
+
|
| 67 |
+
def __exit__(self, *_):
|
| 68 |
+
self.close()
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def depth_pair(package):
|
| 72 |
+
package = Path(package)
|
| 73 |
+
gt = next((package/n for n in ('depth_gt_mm.npz', 'depth_gt.npz') if (package/n).is_file()), None)
|
| 74 |
+
sensor = next((package/n for n in ('depth_sensor_u16.npz', 'depth_sensor.npz') if (package/n).is_file()), None)
|
| 75 |
+
if gt is None or sensor is None:
|
| 76 |
+
raise FileNotFoundError('Both clean GT and simulated sensor depth are required')
|
| 77 |
+
return gt, sensor
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def unpack_class_frame(packed, shape):
|
| 81 |
+
size = int(np.prod(shape))
|
| 82 |
+
bits = np.unpackbits(packed, bitorder='little')[:size*2].reshape(size, 2)
|
| 83 |
+
return (bits[:, 0] | (bits[:, 1] << 1)).reshape(shape)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class DatasetLocations:
|
| 87 |
+
"""Explicitly resolve bound historical paths without editing immutable JSON."""
|
| 88 |
+
def __init__(self, manifest):
|
| 89 |
+
value = json.loads(Path(manifest).read_text())
|
| 90 |
+
if value['schema'] != 'egorenderer.dataset_relocations.v1' or not value['complete']:
|
| 91 |
+
raise ValueError('A complete location manifest is required')
|
| 92 |
+
self.root = Path(value['dataset_root'])
|
| 93 |
+
self.mappings = sorted([(Path(r['source']), Path(r['target'])) for r in value['mappings']],
|
| 94 |
+
key=lambda pair: len(pair[0].parts), reverse=True)
|
| 95 |
+
if any(not target.is_relative_to(self.root) for _, target in self.mappings):
|
| 96 |
+
raise ValueError('Cross-dataset relocation')
|
| 97 |
+
|
| 98 |
+
def path(self, value):
|
| 99 |
+
original = Path(value)
|
| 100 |
+
for source, target in self.mappings:
|
| 101 |
+
if original.is_relative_to(source):
|
| 102 |
+
return target / original.relative_to(source)
|
| 103 |
+
return original
|
| 104 |
+
|
| 105 |
+
def references(self, value):
|
| 106 |
+
if isinstance(value, str) and value.startswith('/'):
|
| 107 |
+
return str(self.path(value))
|
| 108 |
+
if isinstance(value, list):
|
| 109 |
+
return [self.references(v) for v in value]
|
| 110 |
+
if isinstance(value, dict):
|
| 111 |
+
return {self.references(k): self.references(v) for k, v in value.items()}
|
| 112 |
+
return value
|
| 113 |
+
|
| 114 |
+
def read_bound_json(self, binding):
|
| 115 |
+
raw = self.path(binding['path']).read_bytes()
|
| 116 |
+
if hashlib.sha256(raw).hexdigest() != binding['sha256']:
|
| 117 |
+
raise ValueError('Immutable JSON SHA mismatch')
|
| 118 |
+
return self.references(json.loads(raw))
|
index.html
CHANGED
|
@@ -5,6 +5,7 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
| 6 |
<style>:root{padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}img{max-width:100%}[hidden]{display:none!important}</style>
|
| 7 |
<title>SOMA 手部数据图谱</title>
|
|
|
|
| 8 |
<meta name="claude:viewport-fit-sides" content="cover">
|
| 9 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -274,6 +275,9 @@ footer { font-size: 0.8rem; color: var(--muted); border-top: 1px solid var(--lin
|
|
| 274 |
</head>
|
| 275 |
<body>
|
| 276 |
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
<header class="top">
|
| 279 |
<div class="top-in">
|
|
@@ -284,6 +288,7 @@ footer { font-size: 0.8rem; color: var(--muted); border-top: 1px solid var(--lin
|
|
| 284 |
<a href="#methods" data-v="methods">标注方法</a>
|
| 285 |
<a href="#soma" data-v="soma">转到 SOMA</a>
|
| 286 |
<a href="#samples" data-v="samples">样例</a>
|
|
|
|
| 287 |
</nav>
|
| 288 |
</div>
|
| 289 |
</header>
|
|
@@ -596,16 +601,23 @@ footer { font-size: 0.8rem; color: var(--muted); border-top: 1px solid var(--lin
|
|
| 596 |
/* ---------- 路由 ---------- */
|
| 597 |
var pendingJump = null;
|
| 598 |
function render() {
|
|
|
|
| 599 |
var hash = (location.hash || "#overview").slice(1), view = hash, html;
|
| 600 |
-
if (hash.indexOf("
|
|
|
|
| 601 |
else if (hash === "catalog") html = vCatalog();
|
| 602 |
else if (hash === "methods") html = vMethods();
|
| 603 |
else if (hash === "soma") html = vSoma();
|
| 604 |
else if (hash === "samples") html = vSamples();
|
| 605 |
else { html = vOverview(); view = "overview"; }
|
| 606 |
document.querySelectorAll("video").forEach(function (v) { v.pause(); });
|
| 607 |
-
app.innerHTML = html + '<footer>整理于 ' + esc(DATA.meta.updated) + "。数值来自论文、项目页和团队图册;标「待核」的字段尚未确认。</footer>";
|
|
|
|
|
|
|
|
|
|
| 608 |
document.querySelectorAll("nav.tabs a").forEach(function (a) { if (a.getAttribute("data-v") === view) a.setAttribute("aria-current", "page"); else a.removeAttribute("aria-current"); });
|
|
|
|
|
|
|
| 609 |
if (hash === "catalog") bindCatalog();
|
| 610 |
if (hash.indexOf("ds-") === 0 && BYID[hash.slice(3)]) {
|
| 611 |
var id = hash.slice(3);
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
| 6 |
<style>:root{padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}img{max-width:100%}[hidden]{display:none!important}</style>
|
| 7 |
<title>SOMA 手部数据图谱</title>
|
| 8 |
+
<link rel="icon" href="data:,">
|
| 9 |
<meta name="claude:viewport-fit-sides" content="cover">
|
| 10 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 11 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
|
| 275 |
</head>
|
| 276 |
<body>
|
| 277 |
|
| 278 |
+
<link rel="stylesheet" href="src/synthetic.css">
|
| 279 |
+
<script src="src/vendor/lucide.min.js"></script>
|
| 280 |
+
<script src="src/synthetic.js"></script>
|
| 281 |
|
| 282 |
<header class="top">
|
| 283 |
<div class="top-in">
|
|
|
|
| 288 |
<a href="#methods" data-v="methods">标注方法</a>
|
| 289 |
<a href="#soma" data-v="soma">转到 SOMA</a>
|
| 290 |
<a href="#samples" data-v="samples">样例</a>
|
| 291 |
+
<a href="#synthetic/progress" data-v="synthetic">合成数据</a>
|
| 292 |
</nav>
|
| 293 |
</div>
|
| 294 |
</header>
|
|
|
|
| 601 |
/* ---------- 路由 ---------- */
|
| 602 |
var pendingJump = null;
|
| 603 |
function render() {
|
| 604 |
+
if (window.EgoRendererPage) window.EgoRendererPage.unmount();
|
| 605 |
var hash = (location.hash || "#overview").slice(1), view = hash, html;
|
| 606 |
+
if (hash.indexOf("synthetic") === 0) { html = window.EgoRendererPage ? window.EgoRendererPage.render() : '<p class="err">合成数据模块未加载,请刷新重试。</p>'; view = "synthetic"; }
|
| 607 |
+
else if (hash.indexOf("ds-") === 0) { html = vDetail(hash.slice(3)); view = "catalog"; }
|
| 608 |
else if (hash === "catalog") html = vCatalog();
|
| 609 |
else if (hash === "methods") html = vMethods();
|
| 610 |
else if (hash === "soma") html = vSoma();
|
| 611 |
else if (hash === "samples") html = vSamples();
|
| 612 |
else { html = vOverview(); view = "overview"; }
|
| 613 |
document.querySelectorAll("video").forEach(function (v) { v.pause(); });
|
| 614 |
+
app.innerHTML = html + (view === "synthetic" ? '' : '<footer>整理于 ' + esc(DATA.meta.updated) + "。数值来自论文、项目页和团队图册;标「待核」的字段尚未确认。</footer>");
|
| 615 |
+
document.getElementById("upd").textContent = view === "synthetic" ? "文档 2026-11-02" : "更新于 " + DATA.meta.updated;
|
| 616 |
+
document.title = view === "synthetic" ? "EgoRenderer · SOMA 手部数据图谱" : "SOMA 手部数据图谱";
|
| 617 |
+
if (view === "synthetic" && window.EgoRendererPage) window.EgoRendererPage.mount();
|
| 618 |
document.querySelectorAll("nav.tabs a").forEach(function (a) { if (a.getAttribute("data-v") === view) a.setAttribute("aria-current", "page"); else a.removeAttribute("aria-current"); });
|
| 619 |
+
var activeTab = document.querySelector('nav.tabs a[aria-current="page"]');
|
| 620 |
+
if (activeTab) activeTab.scrollIntoView({block:"nearest", inline:"nearest"});
|
| 621 |
if (hash === "catalog") bindCatalog();
|
| 622 |
if (hash.indexOf("ds-") === 0 && BYID[hash.slice(3)]) {
|
| 623 |
var id = hash.slice(3);
|
media/egorender/bimanual.png
ADDED
|
Git LFS Details
|
media/egorender/mirror.png
ADDED
|
Git LFS Details
|
media/egorender/multiple.png
ADDED
|
Git LFS Details
|
src/page.html
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
<title>SOMA 手部数据图谱</title>
|
|
|
|
| 2 |
<meta name="claude:viewport-fit-sides" content="cover">
|
| 3 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 4 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -265,6 +266,9 @@ footer { font-size: 0.8rem; color: var(--muted); border-top: 1px solid var(--lin
|
|
| 265 |
@media(max-width:520px) { .preview-pair { gap:5px; } .preview-pair figcaption { padding:6px; font-size:.7rem; } .preview-tools input[type="range"] { width:100px; } .preview-tools label { flex-wrap:wrap; } }
|
| 266 |
|
| 267 |
</style>
|
|
|
|
|
|
|
|
|
|
| 268 |
|
| 269 |
<header class="top">
|
| 270 |
<div class="top-in">
|
|
@@ -275,6 +279,7 @@ footer { font-size: 0.8rem; color: var(--muted); border-top: 1px solid var(--lin
|
|
| 275 |
<a href="#methods" data-v="methods">标注方法</a>
|
| 276 |
<a href="#soma" data-v="soma">转到 SOMA</a>
|
| 277 |
<a href="#samples" data-v="samples">样例</a>
|
|
|
|
| 278 |
</nav>
|
| 279 |
</div>
|
| 280 |
</header>
|
|
@@ -587,16 +592,23 @@ footer { font-size: 0.8rem; color: var(--muted); border-top: 1px solid var(--lin
|
|
| 587 |
/* ---------- 路由 ---------- */
|
| 588 |
var pendingJump = null;
|
| 589 |
function render() {
|
|
|
|
| 590 |
var hash = (location.hash || "#overview").slice(1), view = hash, html;
|
| 591 |
-
if (hash.indexOf("
|
|
|
|
| 592 |
else if (hash === "catalog") html = vCatalog();
|
| 593 |
else if (hash === "methods") html = vMethods();
|
| 594 |
else if (hash === "soma") html = vSoma();
|
| 595 |
else if (hash === "samples") html = vSamples();
|
| 596 |
else { html = vOverview(); view = "overview"; }
|
| 597 |
document.querySelectorAll("video").forEach(function (v) { v.pause(); });
|
| 598 |
-
app.innerHTML = html + '<footer>整理于 ' + esc(DATA.meta.updated) + "。数值来自论文、项目页和团队图册;标「待核」的字段尚未确认。</footer>";
|
|
|
|
|
|
|
|
|
|
| 599 |
document.querySelectorAll("nav.tabs a").forEach(function (a) { if (a.getAttribute("data-v") === view) a.setAttribute("aria-current", "page"); else a.removeAttribute("aria-current"); });
|
|
|
|
|
|
|
| 600 |
if (hash === "catalog") bindCatalog();
|
| 601 |
if (hash.indexOf("ds-") === 0 && BYID[hash.slice(3)]) {
|
| 602 |
var id = hash.slice(3);
|
|
|
|
| 1 |
<title>SOMA 手部数据图谱</title>
|
| 2 |
+
<link rel="icon" href="data:,">
|
| 3 |
<meta name="claude:viewport-fit-sides" content="cover">
|
| 4 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 5 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
|
| 266 |
@media(max-width:520px) { .preview-pair { gap:5px; } .preview-pair figcaption { padding:6px; font-size:.7rem; } .preview-tools input[type="range"] { width:100px; } .preview-tools label { flex-wrap:wrap; } }
|
| 267 |
|
| 268 |
</style>
|
| 269 |
+
<link rel="stylesheet" href="src/synthetic.css">
|
| 270 |
+
<script src="src/vendor/lucide.min.js"></script>
|
| 271 |
+
<script src="src/synthetic.js"></script>
|
| 272 |
|
| 273 |
<header class="top">
|
| 274 |
<div class="top-in">
|
|
|
|
| 279 |
<a href="#methods" data-v="methods">标注方法</a>
|
| 280 |
<a href="#soma" data-v="soma">转到 SOMA</a>
|
| 281 |
<a href="#samples" data-v="samples">样例</a>
|
| 282 |
+
<a href="#synthetic/progress" data-v="synthetic">合成数据</a>
|
| 283 |
</nav>
|
| 284 |
</div>
|
| 285 |
</header>
|
|
|
|
| 592 |
/* ---------- 路由 ---------- */
|
| 593 |
var pendingJump = null;
|
| 594 |
function render() {
|
| 595 |
+
if (window.EgoRendererPage) window.EgoRendererPage.unmount();
|
| 596 |
var hash = (location.hash || "#overview").slice(1), view = hash, html;
|
| 597 |
+
if (hash.indexOf("synthetic") === 0) { html = window.EgoRendererPage ? window.EgoRendererPage.render() : '<p class="err">合成数据模块未加载,请刷新重试。</p>'; view = "synthetic"; }
|
| 598 |
+
else if (hash.indexOf("ds-") === 0) { html = vDetail(hash.slice(3)); view = "catalog"; }
|
| 599 |
else if (hash === "catalog") html = vCatalog();
|
| 600 |
else if (hash === "methods") html = vMethods();
|
| 601 |
else if (hash === "soma") html = vSoma();
|
| 602 |
else if (hash === "samples") html = vSamples();
|
| 603 |
else { html = vOverview(); view = "overview"; }
|
| 604 |
document.querySelectorAll("video").forEach(function (v) { v.pause(); });
|
| 605 |
+
app.innerHTML = html + (view === "synthetic" ? '' : '<footer>整理于 ' + esc(DATA.meta.updated) + "。数值来自论文、项目页和团队图册;标「待核」的字段尚未确认。</footer>");
|
| 606 |
+
document.getElementById("upd").textContent = view === "synthetic" ? "文档 2026-11-02" : "更新于 " + DATA.meta.updated;
|
| 607 |
+
document.title = view === "synthetic" ? "EgoRenderer · SOMA 手部数据图谱" : "SOMA 手部数据图谱";
|
| 608 |
+
if (view === "synthetic" && window.EgoRendererPage) window.EgoRendererPage.mount();
|
| 609 |
document.querySelectorAll("nav.tabs a").forEach(function (a) { if (a.getAttribute("data-v") === view) a.setAttribute("aria-current", "page"); else a.removeAttribute("aria-current"); });
|
| 610 |
+
var activeTab = document.querySelector('nav.tabs a[aria-current="page"]');
|
| 611 |
+
if (activeTab) activeTab.scrollIntoView({block:"nearest", inline:"nearest"});
|
| 612 |
if (hash === "catalog") bindCatalog();
|
| 613 |
if (hash.indexOf("ds-") === 0 && BYID[hash.slice(3)]) {
|
| 614 |
var id = hash.slice(3);
|
src/synthetic.css
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.synth { letter-spacing:0; }
|
| 2 |
+
.synth * { letter-spacing:0; }
|
| 3 |
+
.synth-head { display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; }
|
| 4 |
+
.synth-head h1 { font-family:var(--f-body); font-size:26px; }
|
| 5 |
+
.synth-head .synth-identity { font:13px var(--f-mono); color:var(--muted); margin-top:5px; }
|
| 6 |
+
.synth-nav { display:flex; gap:22px; border-bottom:1px solid var(--line); overflow:auto; }
|
| 7 |
+
.synth-nav a { white-space:nowrap; padding:10px 0; text-decoration:none; color:var(--muted); border-bottom:2px solid transparent; }
|
| 8 |
+
.synth-nav a[aria-current=page] { color:var(--ink); border-color:var(--accent); }
|
| 9 |
+
.synth-status { display:flex; align-items:center; gap:10px; font-size:13px; flex-wrap:wrap; }
|
| 10 |
+
.synth-dot { width:7px; height:7px; display:inline-block; border-radius:50%; background:#137667; }
|
| 11 |
+
.synth-status[data-state=stale] .synth-dot,.synth-status[data-state=error] .synth-dot { background:#b86a15; }
|
| 12 |
+
.synth-status[data-state=stopped] .synth-dot { background:#7d8791; }
|
| 13 |
+
.synth-icon { width:34px; height:34px; border:1px solid var(--line); border-radius:4px; background:var(--surface); color:var(--ink); display:inline-grid; place-items:center; cursor:pointer; flex:none; }
|
| 14 |
+
.synth-icon svg { width:16px; height:16px; }
|
| 15 |
+
.synth-icon:disabled { opacity:.45; cursor:wait; }
|
| 16 |
+
.synth-kpis { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
|
| 17 |
+
.synth-kpi { padding:20px 16px; min-width:0; border-right:1px solid var(--line); }
|
| 18 |
+
.synth-kpi:first-child { padding-left:0; }
|
| 19 |
+
.synth-kpi:last-child { border-right:0; }
|
| 20 |
+
.synth-kpi strong { display:block; font:500 30px/1.4 var(--f-mono); }
|
| 21 |
+
.synth-kpi span { color:var(--muted); font-size:13px; }
|
| 22 |
+
.synth-progress { display:flex; height:9px; background:var(--sunk); width:100%; overflow:hidden; }
|
| 23 |
+
.synth-progress > span { height:100%; }
|
| 24 |
+
.synth-progress .done { background:#167d70; }.synth-progress .running { background:#488ab1; }.synth-progress .failed { background:#c66740; }
|
| 25 |
+
.synth-facts { display:flex; gap:12px 28px; flex-wrap:wrap; font-size:13px; color:var(--muted); }
|
| 26 |
+
.synth-facts b { color:var(--ink); font-family:var(--f-mono); font-weight:500; }
|
| 27 |
+
.synth-section { padding-top:20px; border-top:1px solid var(--line); display:grid; gap:14px; min-width:0; }
|
| 28 |
+
.synth-section:first-child { padding-top:0; border-top:0; }
|
| 29 |
+
.synth-section h2 { font-family:var(--f-body); font-size:18px; }
|
| 30 |
+
.synth-table { overflow:auto; min-width:0; }
|
| 31 |
+
.synth-table table { width:100%; border-collapse:collapse; font-size:13px; }
|
| 32 |
+
.synth-table th { text-align:left; font-weight:500; color:var(--muted); border-bottom:1px solid var(--line); padding:9px 12px 9px 0; }
|
| 33 |
+
.synth-table td { border-bottom:1px solid var(--line); padding:12px 12px 12px 0; vertical-align:top; overflow-wrap:anywhere; }
|
| 34 |
+
.synth-table td:last-child,.synth-table th:last-child { padding-right:0; }
|
| 35 |
+
.synth-table th { white-space:nowrap; }.synth-table .source { min-width:190px; font-family:var(--f-mono); font-size:12px; }
|
| 36 |
+
.synth-table .num { font-family:var(--f-mono); white-space:nowrap; }
|
| 37 |
+
.synth-table .status-text { white-space:nowrap; }
|
| 38 |
+
.synth-empty { color:var(--muted); padding:18px 0; }
|
| 39 |
+
.synth-toolbar { display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap; }
|
| 40 |
+
.synth-toolbar select,.synth-toolbar input { font:inherit; font-size:13px; color:var(--ink); background:var(--surface); border:1px solid var(--line); border-radius:4px; padding:7px 10px; min-width:0; }
|
| 41 |
+
.synth-toolbar input { width:220px; max-width:100%; }
|
| 42 |
+
.synth-resource { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:20px; }
|
| 43 |
+
.synth-resource dl { margin:0; display:grid; grid-template-columns:1fr auto; gap:7px; font-size:13px; }
|
| 44 |
+
.synth-resource dt { color:var(--muted); }.synth-resource dd { margin:0; font-family:var(--f-mono); }
|
| 45 |
+
.synth-resource progress { width:100%; height:5px; accent-color:#488ab1; grid-column:1/-1; }
|
| 46 |
+
.synth-callout { border-left:3px solid #b86a15; padding:4px 0 4px 14px; font-size:13px; line-height:1.8; }
|
| 47 |
+
.synth-version-picker { display:flex; gap:0; flex-wrap:wrap; width:fit-content; border:1px solid var(--line); border-radius:4px; overflow:hidden; }
|
| 48 |
+
.synth-version-picker button { border:0; border-right:1px solid var(--line); min-width:66px; padding:9px 12px; cursor:pointer; font:13px var(--f-mono); color:var(--ink); background:var(--surface); }
|
| 49 |
+
.synth-version-picker button:last-child { border-right:0; }
|
| 50 |
+
.synth-version-picker button[aria-pressed=true] { background:var(--ink); color:var(--surface); }
|
| 51 |
+
.synth-version-detail { display:grid; gap:16px; min-height:280px; }
|
| 52 |
+
.synth-version-detail ul { padding-left:20px; display:grid; gap:9px; font-size:14px; }
|
| 53 |
+
.synth-version-detail h2 { font:700 20px var(--f-body); }
|
| 54 |
+
.synth-meta { display:flex; gap:10px 22px; color:var(--muted); font-size:13px; flex-wrap:wrap; }
|
| 55 |
+
.synth-code { background:var(--sunk); border:1px solid var(--line); border-radius:4px; padding:16px; position:relative; min-width:0; }
|
| 56 |
+
.synth-code pre { margin:0; padding-right:34px; overflow:auto; font:12px/1.8 var(--f-mono); white-space:pre; tab-size:4; }
|
| 57 |
+
.synth-code .synth-icon { position:absolute; right:8px; top:8px; }
|
| 58 |
+
.synth-code-title { font-size:14px; font-weight:500; }
|
| 59 |
+
.synth-steps { display:grid; gap:24px; }
|
| 60 |
+
.synth-steps section { display:grid; gap:12px; min-width:0; }
|
| 61 |
+
.synth-links { display:flex; gap:12px 22px; flex-wrap:wrap; font-size:13px; }
|
| 62 |
+
.synth details { border-bottom:1px solid var(--line); padding:12px 0; }
|
| 63 |
+
.synth summary { cursor:pointer; font-size:14px; }.synth details > p { padding-top:10px; font-size:13px; color:var(--muted); }
|
| 64 |
+
.synth-samples { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:22px; }
|
| 65 |
+
.synth-samples figure { margin:0; min-width:0; }
|
| 66 |
+
.synth-samples img { width:100%; aspect-ratio:16/9; object-fit:contain; background:#10161c; border:1px solid var(--line); }
|
| 67 |
+
.synth-samples figcaption { padding-top:12px; display:grid; gap:6px; font-size:13px; }
|
| 68 |
+
.synth-samples small { color:var(--muted); }
|
| 69 |
+
.synth-error { color:#b34b35; }.synth-good { color:#167d70; }
|
| 70 |
+
.synth-skeleton { min-height:350px; padding-top:30px; color:var(--muted); }
|
| 71 |
+
.synth .subtle { font-size:13px; color:var(--muted); }
|
| 72 |
+
.synth-pager { display:flex; align-items:center; justify-content:flex-end; gap:8px; padding-top:12px; font:12px var(--f-mono); }
|
| 73 |
+
.synth-pager span { margin-right:8px; }
|
| 74 |
+
@media(max-width:760px) {
|
| 75 |
+
.synth-kpis { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
| 76 |
+
.synth-kpi { padding:14px 12px; }.synth-kpi:nth-child(2) { border-right:0; }.synth-kpi:nth-child(n+3) { border-top:1px solid var(--line); }
|
| 77 |
+
.synth-kpi:nth-child(3) { padding-left:0; }.synth-kpi strong { font-size:25px; }
|
| 78 |
+
.synth-resource { grid-template-columns:repeat(2,minmax(0,1fr)); gap:20px; }
|
| 79 |
+
.synth-samples { grid-template-columns:1fr; }.synth-samples img { max-height:330px; }
|
| 80 |
+
.synth-table table { min-width:600px; }.synth-nav { gap:20px; }
|
| 81 |
+
.synth-version-picker { width:100%; }.synth-version-picker button { flex:1; min-width:44px; padding:8px 5px; }
|
| 82 |
+
.synth-version-detail { min-height:0; }.synth-head h1 { font-size:24px; }
|
| 83 |
+
}
|
src/synthetic.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
'use strict';
|
| 3 |
+
let content, registry = [], snapshot, root, timer, ageTimer, request, generation = 0;
|
| 4 |
+
let selectedVersion = '006', selectedJobs = 'active', registryQuery = '', registryPage = 0;
|
| 5 |
+
const esc = x => String(x == null ? '' : x).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
| 6 |
+
const number = x => Number.isFinite(x) ? x.toLocaleString('zh-CN', {maximumFractionDigits:1}) : '—';
|
| 7 |
+
const clock = t => new Date(t * 1000).toLocaleString('zh-CN', {timeZone:'Asia/Kuala_Lumpur',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',hour12:false}) + ' MYT';
|
| 8 |
+
const icon = (name, label, attr = '') => `<button class="synth-icon" title="${esc(label)}" aria-label="${esc(label)}" ${attr}><i data-lucide="${name}" aria-hidden="true"></i></button>`;
|
| 9 |
+
const table = (heads, rows) => `<div class="synth-table"><table><thead><tr>${heads.map(h=>`<th>${esc(h)}</th>`).join('')}</tr></thead><tbody>${rows.join('')}</tbody></table></div>`;
|
| 10 |
+
const phases = {WAIT_SSD_CAPACITY:'等待存储准入',WAIT_CAPACITY:'等待容量',WAIT_SHARED_GPU_FOR_CYCLES:'等待GPU',V5_APPEARANCE_CANONICAL_SURFACE:'表面与材质',V5_THREE_MOUNT_INDEPENDENT_CAMERA:'相机轨迹',V5_RANDOMIZED_SCENE_CANONICAL_SURFACE:'场景组装',CYCLES_OPTIX_16SPP_GPU_OIDN:'RGB渲染',VIDEO_POST_AND_DEPTH_OVERLAPPED:'视频后处理 / 深度',PACKAGE_NATIVE_RENDER_CHANNELS:'打包',VERIFIED_SEQUENTIAL_NAS_PUBLICATION:'NAS归档',INITIALIZING:'初始化',FAILED:'待定点修复'};
|
| 11 |
+
const sections = [['progress','进度'],['versions','版本'],['reading','数据读取'],['samples','样例']];
|
| 12 |
+
const tab = () => (location.hash.split('/')[1] || 'progress');
|
| 13 |
+
function section(title, body) { return `<section class="synth-section"><h2>${esc(title)}</h2>${body}</section>`; }
|
| 14 |
+
function code(title, text) { return `<section><h3 class="synth-code-title">${esc(title)}</h3><div class="synth-code">${icon('copy','复制代码','data-copy')}<pre><code>${esc(text)}</code></pre></div></section>`; }
|
| 15 |
+
function icons() { if (window.lucide) window.lucide.createIcons(); }
|
| 16 |
+
function valid(value) {
|
| 17 |
+
const c = value && value.counts;
|
| 18 |
+
return value && value.schema === 'egorenderer.public_progress.v1' && Number.isFinite(value.collected_at) && c &&
|
| 19 |
+
['complete','running','ready','failed','total'].every(k=>Number.isInteger(c[k]) && c[k]>=0) &&
|
| 20 |
+
c.total>0 && c.complete+c.running+c.ready+c.failed===c.total;
|
| 21 |
+
}
|
| 22 |
+
function header() {
|
| 23 |
+
return `<div class="synth-head"><div><h1>EgoRenderer</h1><div class="synth-identity">V006 <span id="synth-execution"></span></div></div><div class="synth-status" id="synth-status" aria-live="polite"><span class="synth-dot"></span><span id="synth-freshness">读取快照</span>${icon('refresh-cw','刷新进度','id="synth-refresh"')}</div></div>
|
| 24 |
+
<nav class="synth-nav" aria-label="合成数据">${sections.map(([id,label])=>`<a href="#synthetic/${id}" ${tab()===id?'aria-current="page"':''}>${label}</a>`).join('')}</nav>`;
|
| 25 |
+
}
|
| 26 |
+
function progressView() {
|
| 27 |
+
return `<div id="synth-summary" class="synth-kpis" aria-live="polite"></div><div class="part"><div id="synth-progress" class="synth-progress" role="progressbar" aria-label="ARCTIC随机化包生成进度"></div><div id="synth-facts" class="synth-facts"></div></div>
|
| 28 |
+
<div id="synth-notices"></div>${section('数据范围','<div id="synth-cohorts"></div>')}
|
| 29 |
+
<section class="synth-section"><div class="synth-toolbar"><h2>当前任务</h2><select id="synth-job-filter" aria-label="任务状态"><option value="active">运行中</option><option value="failed">失败</option><option value="all">运行与失败</option></select></div><div id="synth-jobs"></div></section>
|
| 30 |
+
${section('主机资源','<div id="synth-resources" class="synth-resource"></div><div id="synth-storage" class="synth-facts"></div>')}
|
| 31 |
+
${section('最近归档','<div id="synth-recent"></div>')}`;
|
| 32 |
+
}
|
| 33 |
+
function updateFreshness(error) {
|
| 34 |
+
if (!root) return;
|
| 35 |
+
const status=root.querySelector('#synth-status'), label=root.querySelector('#synth-freshness');
|
| 36 |
+
if (!snapshot) { status.dataset.state='error';label.textContent=error?'状态暂不可用':'读取快照';return; }
|
| 37 |
+
const stale=Date.now()/1000-snapshot.collected_at>180;
|
| 38 |
+
status.dataset.state=error||stale?'stale':snapshot.queue_alive&&!snapshot.admission_held?'live':'stopped';
|
| 39 |
+
label.textContent=(error?'连接中断 · ':stale?'更新已过期 · ':!snapshot.queue_alive?'队列已停 · ':snapshot.admission_held?'暂停新增 · ':'')+clock(snapshot.collected_at);
|
| 40 |
+
root.querySelector('#synth-execution').textContent=' / 执行 '+snapshot.execution_revision;
|
| 41 |
+
}
|
| 42 |
+
function updateProgress() {
|
| 43 |
+
if (!root || tab()!=='progress')return;
|
| 44 |
+
if (!snapshot) { root.querySelector('#synth-summary').innerHTML='<p class="synth-empty">状态暂不可用。不会以零值替代上次进度。</p>';return; }
|
| 45 |
+
const s=snapshot,c=s.counts;
|
| 46 |
+
root.querySelector('#synth-summary').innerHTML=[['渲染与深度完成',`${number(c.complete)} / ${number(c.total)}`],['运行中',number(c.running)],['待领取',number(c.ready)],['失败',number(c.failed)]].map(([k,v])=>`<div class="synth-kpi"><strong>${v}</strong><span>${k}</span></div>`).join('');
|
| 47 |
+
const progress=root.querySelector('#synth-progress');progress.setAttribute('aria-valuemin','0');progress.setAttribute('aria-valuemax',c.total);progress.setAttribute('aria-valuenow',c.complete);
|
| 48 |
+
progress.innerHTML=[['done',c.complete],['running',c.running],['failed',c.failed]].map(([cl,n])=>`<span class="${cl}" style="width:${100*n/c.total}%"></span>`).join('');
|
| 49 |
+
root.querySelector('#synth-facts').innerHTML=`<span>完成 <b>${(100*c.complete/c.total).toFixed(1)}%</b></span><span>变体帧 <b>${number(s.coverage.completed_variant_frames)} / ${number(s.scope.expected_variant_frames)}</b></span><span>最近1小时 <b>${number(s.throughput.packages_last_hour)}包</b></span><span>队列预计(不含失败) <b>${s.throughput.queued_eta_hours==null?'—':number(s.throughput.queued_eta_hours)+'小时'}</b></span>`;
|
| 50 |
+
root.querySelector('#synth-notices').innerHTML=`<div class="synth-callout">训练就绪状态未确认;辅助拟合待上游交付。${s.known_gt_missing.length?`另有 <b>${s.known_gt_missing.length}</b> 个已知历史包仍缺干净GT深度。`:''}</div>`;
|
| 51 |
+
const oak=s.oakink_assets, hc=s.hardcases;
|
| 52 |
+
root.querySelector('#synth-cohorts').innerHTML=table(['数据','规模','当前口径'],[
|
| 53 |
+
`<tr><td>ARCTIC · V006</td><td class="num">${number(s.scope.sources)}源 / ${number(c.total)}包</td><td>${number(s.scope.source_frames)}源帧;${number(s.coverage.sources_with_all_variants)}条动作完成全部3变体</td></tr>`,
|
| 54 |
+
`<tr><td>Hard Cases · V006</td><td class="num">${hc.complete} / ${hc.expected}条样例</td><td>${number(hc.frames)}主源帧;三类各5条,独立于主队列</td></tr>`,
|
| 55 |
+
`<tr><td>OakInk2 · V006</td><td class="num">${number(oak.total)}源</td><td>${number(oak.counts.COMPLETE||0)}条人体资产完成;不是最终合成包计数</td></tr>`,
|
| 56 |
+
'<tr><td>GRAB · V005</td><td class="num">1,314源组 / 2,425包</td><td>历史参数附件更新完成;V006新深度尚未据此宣称完成</td></tr>'
|
| 57 |
+
]);
|
| 58 |
+
const selected=root.querySelector('#synth-job-filter');selected.value=selectedJobs;
|
| 59 |
+
const jobs=s.jobs.filter(j=>selectedJobs==='all'||(selectedJobs==='failed'?j.state==='FAILED':j.state!=='FAILED'));
|
| 60 |
+
root.querySelector('#synth-jobs').innerHTML=jobs.length?table(['源动作','变体','帧数','存储','阶段'],jobs.map(j=>`<tr><td class="source">${esc(j.source)}</td><td class="num">${j.variant}</td><td class="num">${number(j.frames)}</td><td>${esc(j.storage||'待准入')}</td><td class="status-text ${j.state==='FAILED'?'synth-error':''}">${esc(phases[j.phase]||j.phase)}</td></tr>`)):'<p class="synth-empty">当前没有此类任务。</p>';
|
| 61 |
+
const gpu=s.hardware.gpus||[];
|
| 62 |
+
root.querySelector('#synth-resources').innerHTML=gpu.map(g=>`<dl><dt>GPU ${g.index}</dt><dd>${g.utilization}%</dd><progress aria-label="GPU ${g.index}利用率" max="100" value="${g.utilization}"></progress><dt>显存 GiB</dt><dd>${number(g.used_mib/1024)} / ${number(g.total_mib/1024)}</dd></dl>`).join('');
|
| 63 |
+
root.querySelector('#synth-storage').innerHTML=`<span>CPU(主机) <b>${number(s.hardware.cpu_percent)}%</b></span><span>SSD预留 <b>${number(s.storage.ssd_reserved_gib)} / ${number(s.storage.ssd_budget_gib)} GiB</b></span><span>SSD可用 <b>${number(s.storage.ssd_available_gib)} GiB</b></span><span>NAS可用 <b>${number(s.storage.nas_available_gib/1024)} TiB</b></span><span>NAS通道 <b>${s.storage.nas_active} / ${s.storage.nas_limit}</b></span>`;
|
| 64 |
+
root.querySelector('#synth-recent').innerHTML=table(['源动作','变体','归档时间'],s.recent.map(j=>`<tr><td class="source">${esc(j.source)}</td><td class="num">${j.variant}</td><td>${clock(j.at)}</td></tr>`));
|
| 65 |
+
}
|
| 66 |
+
function versionDetail() {
|
| 67 |
+
const v=content.versions.find(x=>x.id===selectedVersion);
|
| 68 |
+
return `<h2>V${v.id} · ${esc(v.title)}</h2><div class="synth-meta"><span>${esc(v.state)}</span><span>${esc(v.scope)}</span></div><ul>${v.features.map(f=>`<li>${esc(f)}</li>`).join('')}</ul><div class="synth-callout">${esc(v.read)}</div>`;
|
| 69 |
+
}
|
| 70 |
+
function versionsView() {
|
| 71 |
+
return `<section class="synth-section"><h2>数据与方法版本</h2><div class="synth-version-picker" role="group" aria-label="版本">${content.versions.map(v=>`<button data-version="${v.id}" aria-pressed="${v.id===selectedVersion}">V${v.id}</button>`).join('')}</div><div id="synth-version-detail" class="synth-version-detail">${versionDetail()}</div></section>
|
| 72 |
+
${section('V006关键执行修订',table(['修订','主题','变化'],content.milestones.map(r=>`<tr>${r.map(x=>`<td>${esc(x)}</td>`).join('')}</tr>`)))}
|
| 73 |
+
<section class="synth-section"><div class="synth-toolbar"><h2>执行登记簿</h2><input id="synth-registry-search" type="search" aria-label="搜索执行修订" placeholder="版本、模块或状态" value="${esc(registryQuery)}"></div><div class="subtle">登记状态���是实时任务状态;执行修订不等同数据发布版本。</div><div id="synth-registry"></div></section>`;
|
| 74 |
+
}
|
| 75 |
+
function updateRegistry() {
|
| 76 |
+
const target=root.querySelector('#synth-registry');if(!target)return;
|
| 77 |
+
const rows=registry.filter(r=>[r.id,r.title,r.status,r.scope].join(' ').toLowerCase().includes(registryQuery.toLowerCase())).slice().reverse();
|
| 78 |
+
registryPage=Math.max(0,Math.min(registryPage,Math.ceil(rows.length/25)-1));
|
| 79 |
+
const start=registryPage*25;
|
| 80 |
+
target.innerHTML=table(['修订','模块','登记状态 / 范围'],rows.slice(start,start+25).map(r=>`<tr><td class="num">${esc(r.id)}</td><td>${esc(r.title)}</td><td><details><summary>${esc(r.status)}</summary><p>${esc(r.scope||'范围见对应冻结合同。')}</p><p class="mono">SHA256 ${esc(r.sha256)}</p></details></td></tr>`))+`<div class="synth-pager"><span>${rows.length?start+1:0}–${Math.min(start+25,rows.length)} / ${rows.length}</span>${icon('chevron-left','上一页','data-registry-prev')}${icon('chevron-right','下一页','data-registry-next')}</div>`;
|
| 81 |
+
const prev=target.querySelector('[data-registry-prev]'),next=target.querySelector('[data-registry-next]');
|
| 82 |
+
prev.disabled=registryPage===0;next.disabled=start+25>=rows.length;
|
| 83 |
+
prev.onclick=()=>{registryPage--;updateRegistry();};next.onclick=()=>{registryPage++;updateRegistry();};icons();
|
| 84 |
+
}
|
| 85 |
+
function readingView() {
|
| 86 |
+
const index=`import json, os\nfrom pathlib import Path\n\nroot = Path(os.environ["EGORENDER_ROOT"])\nindex = json.loads((root / "ARCTIC/packages/"\n "final_surface279_v287_20261030/INDEX.json").read_text())\nfor item in index["complete"]:\n package = Path(item["package"])\n meta = json.loads((package / "metadata.json").read_text())\n print(meta["sequence_index"], meta["frames"], meta["dataset_release"])\n # complete 是渲染终态;另查深度缺口与辅助标注。`;
|
| 87 |
+
const depth=`from egorender_reader import DepthReader, depth_pair\nimport numpy as np\n\ngt_path, sensor_path = depth_pair(package)\nwith DepthReader(gt_path) as gt, DepthReader(sensor_path) as sensor:\n assert np.array_equal(gt.frames, sensor.frames)\n z_gt, valid_gt, source_frame = gt.frame(0)\n z_sensor, valid_sensor, other_frame = sensor.frame(0)\n assert source_frame == other_frame\n # z: float32 米,相机轴向Z;分别使用各自的valid。\n # 每个DataLoader worker长期持有reader,不逐帧重新打开整个NPZ。`;
|
| 88 |
+
const camera=`with np.load(package / "camera_parameters.npz", allow_pickle=False) as c:\n source_frames = c["source_frame_index"]\n K = c["K"]\n world_to_camera = c["world_to_camera"]\n camera_to_world = c["camera_to_world"]\n print(c["camera_coordinates"], c["world_coordinates"])\n # 先核对坐标约定和K是否逐帧,不额外套一次坐标轴翻转。\n\nfrom egorender_reader import unpack_class_frame\nwith np.load(package / "masks_visible_2bit.npz", allow_pickle=False) as m:\n width, height = map(int, m["image_size"])\n classes = unpack_class_frame(m["packed_classes"][0], (height, width))\n print(m["classes"], m["bitorder"])\n # 当前常见: 0背景 / 1人体及衣物 / 2交互物体 / 3桌面;以文件为准。`;
|
| 89 |
+
const native=`with np.load(package / "mano_parameters.npz", allow_pickle=False) as h:\n print(h.files)\n left_pose = h["left_pose"]\n left_translation = h["left_trans"]\n source_frames = h["source_frames"]\n # left/right_rot, pose, trans, shape来自原始数据集参数。\n # 参数化/手均值/坐标系遵循原始模型与包内来源,不把shape当拟合真值。\n\n# V005辅助附件不是render输入:\nfit_ref = json.loads((legacy_package / "mano_fit_current.json").read_text())\n# 按fit_ref和对应合同加载参数;左手必须搭配V005修正基底。`;
|
| 90 |
+
const relocation=`from egorender_reader import DatasetLocations\n\nloc = DatasetLocations(package / "artifact_locations.json")\n# 给定包内记录的 {"path": ..., "sha256": ...} 引用:\nresolved = loc.read_bound_json(binding)\n# 先检查原JSON字节SHA,再解析历史路径及path-keyed字典。\n# 不覆盖immutable报告,也不要通过字符串替换伪造新SHA。`;
|
| 91 |
+
return `${section('文件与语义',table(['通道','文件','表示','读取约束'],content.channels.map(r=>`<tr>${r.map(x=>`<td>${esc(x)}</td>`).join('')}</tr>`)))}
|
| 92 |
+
<section class="synth-section"><div class="synth-toolbar"><h2>Python读取</h2><a href="downloads/egorender_reader.py" download>下载读取器.py</a></div><div class="synth-steps">${code('1. 按包索引定位',index)}${code('2. 两套深度与有效掩码',depth)}${code('3. 相机与可见分割',camera)}${code('4. 原始手参数与辅助拟合分开',native)}${code('5. 历史路径显式解析',relocation)}</div></section>
|
| 93 |
+
${section('训练边界',content.quality.map((q,i)=>`<details ${i===0?'open':''}><summary>${['完成与训练资格','公开split与视角','历史深度格式','防止变体泄漏','模拟传感器边界','数据许可'][i]}</summary><p>${esc(q)}</p></details>`).join(''))}
|
| 94 |
+
${section('来源',`<div class="synth-links">${content.references.map(([name,url])=>`<a href="${esc(url)}" target="_blank" rel="noopener noreferrer">${esc(name)}</a>`).join('')}</div>`)}`;
|
| 95 |
+
}
|
| 96 |
+
function samplesView() {
|
| 97 |
+
return `${section('Hard Cases · V006',`<div class="synth-facts"><span>三类各 <b>5</b> 条</span><span><b>10,838</b> 主源帧</span><span>执行修订 <b>320</b></span></div><div class="synth-samples">${content.samples.map(s=>`<figure><a href="${esc(s.image)}" target="_blank" rel="noopener"><img src="${esc(s.image)}" alt="${esc(s.title)}的实际合成场景预览" loading="lazy"></a><figcaption><b>${esc(s.title)}</b><small>${esc(s.source)}</small><p>${esc(s.note)}</p></figcaption></figure>`).join('')}</div>`)}
|
| 98 |
+
<div class="synth-callout">这些是同源动作的历史参考帧,不是当前全部随机变体的逐帧验收。镜面样例在完整生产前另外加入固定种子的污渍材质。</div>
|
| 99 |
+
${section('样本组织',table(['类别','场景来源','读取差异'],[
|
| 100 |
+
'<tr><td>bimanual_crossing</td><td>公开源动作中的真实双手交叉</td><td>不改变原始手部运动来制造交叉</td></tr>',
|
| 101 |
+
'<tr><td>mirror_reflection</td><td>真实镜面几何与反射</td><td>镜面深度为物理首交点;反射的人不新增instance</td></tr>',
|
| 102 |
+
'<tr><td>multiple_people</td><td>多个源人体及各自衣物</td><td>检查scene_instances.json的每人源时钟;主源帧数不能直接当所有演员帧总数</td></tr>'
|
| 103 |
+
]))}`;
|
| 104 |
+
}
|
| 105 |
+
function paint() {
|
| 106 |
+
if (!root || !content)return;
|
| 107 |
+
const body=tab()==='versions'?versionsView():tab()==='reading'?readingView():tab()==='samples'?samplesView():progressView();
|
| 108 |
+
root.innerHTML=header()+body;
|
| 109 |
+
root.querySelector('#synth-refresh').onclick=()=>refresh();
|
| 110 |
+
root.querySelectorAll('[data-version]').forEach(b=>b.onclick=()=>{selectedVersion=b.dataset.version;paint();});
|
| 111 |
+
const filter=root.querySelector('#synth-job-filter');if(filter)filter.onchange=()=>{selectedJobs=filter.value;updateProgress();};
|
| 112 |
+
const search=root.querySelector('#synth-registry-search');if(search)search.oninput=()=>{registryQuery=search.value;registryPage=0;updateRegistry();};
|
| 113 |
+
root.querySelectorAll('[data-copy]').forEach(b=>b.onclick=async()=>{try{await navigator.clipboard.writeText(b.parentNode.querySelector('code').textContent);b.title='已复制';b.setAttribute('aria-label','已复制');}catch(_){b.title='复制失败';}});
|
| 114 |
+
updateFreshness();updateProgress();updateRegistry();icons();
|
| 115 |
+
}
|
| 116 |
+
async function refresh() {
|
| 117 |
+
if (!content || !root || request)return;
|
| 118 |
+
const own=generation,button=root.querySelector('#synth-refresh');button.disabled=true;
|
| 119 |
+
const controller=new AbortController();request=controller;const timeout=setTimeout(()=>controller.abort(),10000);
|
| 120 |
+
try {
|
| 121 |
+
const response=await fetch(content.status_url+'?t='+Date.now(),{cache:'no-store',signal:controller.signal});
|
| 122 |
+
if(!response.ok)throw new Error('HTTP');const value=await response.json();if(!valid(value))throw new Error('schema');
|
| 123 |
+
if(own!==generation)return;
|
| 124 |
+
if(!snapshot || value.collected_at>=snapshot.collected_at)snapshot=value;
|
| 125 |
+
updateFreshness();updateProgress();
|
| 126 |
+
} catch(_) { if(own===generation)updateFreshness(true); }
|
| 127 |
+
finally { clearTimeout(timeout);if(request===controller)request=null;if(own===generation&&root)root.querySelector('#synth-refresh').disabled=false; }
|
| 128 |
+
}
|
| 129 |
+
function unmount() { generation++;clearInterval(timer);clearInterval(ageTimer);if(request)request.abort();request=null;root=null; }
|
| 130 |
+
async function mount() {
|
| 131 |
+
root=document.getElementById('synthetic-root');const own=generation;
|
| 132 |
+
try {
|
| 133 |
+
if(!content){
|
| 134 |
+
const responses=await Promise.all(['egorender','egorender-registry','egorender-progress'].map(n=>fetch('data/'+n+'.json')));
|
| 135 |
+
if(!responses[0].ok)throw new Error('content');
|
| 136 |
+
content=await responses[0].json();registry=responses[1].ok?await responses[1].json():[];
|
| 137 |
+
const cached=responses[2].ok?await responses[2].json():null;if(valid(cached)&&(!snapshot||cached.collected_at>snapshot.collected_at))snapshot=cached;
|
| 138 |
+
}
|
| 139 |
+
if(own!==generation || !root)return;
|
| 140 |
+
paint();refresh();timer=setInterval(refresh,60000);ageTimer=setInterval(()=>updateFreshness(),15000);
|
| 141 |
+
} catch(_) {if(root)root.innerHTML='<p class="synth-error">合成数据文档暂不可用,请刷新重试。</p>';}
|
| 142 |
+
}
|
| 143 |
+
window.EgoRendererPage={render:()=>'<section class="view synth" id="synthetic-root"><div class="synth-skeleton">正在读取合成数据…</div></section>',mount,unmount};
|
| 144 |
+
})();
|
src/vendor/lucide.min.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|