File size: 4,942 Bytes
831819e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { esc, external, section, table, tier, thumbnail } from "./ui.js";

const methodTiers = {
  scan: "S1",
  marker: "S2",
  rgbd: "S3",
  mvkp: "S4",
  device: "-",
  synth: "SY",
  manual2d: "-",
  pseudo: "S5",
};

export function methodsView(context) {
  return `<section class="view"><header class="page-heading"><h1>标注方法</h1></header><nav class="section-tabs" aria-label="标注方法">${context.data.meta.methods.map((m) => `<a href="#methods/${m.id}">${esc(m.short)}</a>`).join("")}</nav>
    ${context.data.meta.methods
      .map((method) => {
        const datasets = context.data.datasets.filter(
          (d) => d.method === method.id,
        );
        return `<article class="method-section section" id="method-${method.id}"><header class="section-heading"><h2>${esc(method.name)}</h2>${tier(methodTiers[method.id], context.meta)}</header><ol class="flow">${method.steps.map((s) => `<li>${esc(s)}</li>`).join("")}</ol><dl class="facts"><div><dt>可信度</dt><dd>${esc(method.trust)}</dd></div><div><dt>常见问题</dt><dd>${esc(method.issues)}</dd></div><div><dt>SOMA路线</dt><dd><a href="#soma">${esc(method.route)}</a></dd></div></dl><div class="dataset-links">${datasets.map((d) => `<a href="#ds-${d.id}">${esc(d.name)}</a>`).join("")}</div></article>`;
      })
      .join("")}</section>`;
}

export function somaView(context) {
  return `<section class="view"><header class="page-heading"><h1>SOMA转换</h1></header>
    <p class="intro">原始标注、拓扑映射、SOMA原生拟合与训练监督分别记录。EgoRenderer的辅助拟合不参与渲染。</p>
    <ol class="pipeline-steps"><li><b>原始标注</b><span>扫描、MANO、SMPL-X、MHR、关键点</span></li><li><b>拓扑与关节对应</b><span>SOMAHandLayer后端或显式对应表</span></li><li><b>原生参数拟合</b><span>Identity、pose与逐帧残差</span></li><li><b>训练监督</b><span>按真值来源区分shape、pose与弱监督</span></li></ol>
    <div class="conversion-list">${context.data.meta.routes.map((route) => `<article id="route-${route.id}" class="conversion-row"><header><span class="mono route-id">${route.id}</span><h2>${esc(route.name)}</h2></header><p>${esc(route.how)}</p><div class="conversion-meta"><span>${route.shape ? "可监督shape" : "不监督shape"}</span><a href="#catalog?route=${route.id}">${context.data.datasets.filter((d) => d.route === route.id).length} 个数据集</a></div></article>`).join("")}</div>
    ${section("监督边界", `<ul class="body-list"><li>MANO 21点的指尖是网格顶点;与SOMA关节定义不完全相同,关键点监督须保留对应表。</li><li>只有明确的扫描、逐人标定、深度约束或合成来源用于shape监督。伪标签不能当成shape真值。</li><li>单目shape与尺度耦合,标准姿态顶点误差与骨长误差应分别记录。</li><li>映射到SOMA拓扑不等于已得到SOMA原生identity与pose;仍需原生拟合和残差记录。</li></ul>`)}
    ${section("参考", `<div class="link-list">${external("SOMA-X", "https://github.com/NVlabs/SOMA-X")}${external("SOMA论文", "https://arxiv.org/abs/2603.16858")}${external("SAM 3D Body", "https://arxiv.org/abs/2602.15989")}</div>`)}</section>`;
}

export function samplesView(context) {
  const datasets = context.data.datasets
    .slice()
    .sort((a, b) => a.name.localeCompare(b.name));
  const count = Object.values(context.previews).reduce(
    (total, list) => total + list.length,
    0,
  );
  const withPreview = datasets.filter((d) => context.previews[d.id]?.length);
  return `<section class="view"><header class="page-heading"><h1>样例</h1><span class="muted">${count} 组 · ${withPreview.length} 个数据集</span></header>
    <div class="sample-grid">${withPreview.map((dataset) => `<a class="sample-tile" href="#ds-${dataset.id}">${thumbnail(dataset, context)}<div><h2>${esc(dataset.name)}</h2><span>${context.previews[dataset.id].length} 组${context.videos[dataset.id]?.length ? ` · ${context.videos[dataset.id].length} 段视频` : ""}</span></div></a>`).join("")}</div>
    <details class="reference-details"><summary>素材覆盖与抽样</summary><p>${esc(context.sampling.strategy)} ${esc(context.sampling.dimensions_note)} ${esc(context.sampling.scope)}</p><div class="link-list">${external("抽样记录", "data/sampling.json")}${external("素材来源", "data/preview_sources.json")}</div>
    ${table(
      ["数据集", "展示 / 素材池", "主分类", "视频", "官方图"],
      datasets.map((d) => {
        const sample = context.sampling.datasets?.[d.id];
        return `<tr><td><a href="#ds-${d.id}">${esc(d.name)}</a></td><td class="mono">${sample ? `${sample.selected} / ${sample.pool}` : "未取得"}</td><td>${esc(sample?.primary_dimension || "—")}</td><td>${context.videos[d.id]?.length || 0}</td><td>${context.figures[d.id]?.figures?.length || 0}</td></tr>`;
      }),
    )}</details></section>`;
}