人体模型部位分割:从 SCHP平面解析 映射到 3D 模型
目录1. 先 2D 分割再映射 3D2. 三维上踩的坑3. 第一步从 3D 模型切出平面图片3.1 多视角采集3.2 前景掩码3.3 正交投影导出为部位点集服务4. 第二步SCHP 二维人体解析融合前质检5. 第三步结果反射——标签穿透映射到顶点5.1 投影采样规则核心代码片段单视角反射6. 第四步多视角加权融合6.1 权重设计6.2 手动采样融合核心代码片段加权投票7. 第五步网格后处理与补全7.1 正交穿透补标7.2 小连通域离岛抑制7.3 未分类点就近归类核心代码片段柱穿透补标8. 第六步正/侧面部位点云导出8.1 PartPlanePoints 结构8.2 PartPlanePointGroups9. 3D 可视化让分割「长在模型上」10. 不同模型与边界情况11. 小结对人体扫描网格做语义部位分割常见做法是直接在三维点云或网格上跑分割网络——我试过很多次效果都不太理想。 最终发现先把模型「切」成平面渲染图用成熟的 SCHP 做二维人体解析再把标签沿视线反射回网格 稳定、可控。工程实现HelixToolkit SharpDX · SCHP-Pascal-7 ONNX1. 先 2D 分割再映射 3D人体扫描网格结构光、多视角重建、点云融合通常具有不规则拓扑、噪声、孔洞、非标准姿态。 若在此类数据上直接做三维语义分割网络需要同时解决几何噪声、采样密度不均、类别边界模糊三个难题。相比之下把网格渲染成与视口一致的 RGB 快照后问题退化为成熟的「人体解析Human Parsing」 输入是规则图像SCHP 等模型在 Pascal-Person-Part 等数据集上已充分训练对头/躯干/四肢的边界极其稳定—— 即便图像分辨率有限、光照不理想分割质量仍远好于我们在 mesh 上试过的多种方案。发现窍门「部位是什么」交给 2D「这个部位在模型空间的哪里」交给相机投影 标签反射。2. 三维上踩的坑与部位分割直接相关的结论如下尝试思路分割结论直接在 3D 网格/参数化模型上定位部位边界抖动大肩背、腋下错分严重另一种 3D 标签传播策略单视角不稳定融合后仍碎块多纯二维 SCHP 实验不涉及映射验证 SCHP 分割质量极好引入 SCHP 单视角反射可行但背面与侧面标签缺失多视角融合 穿透补标 拓扑后处理最建议的方案开源 SMPL 模板顶点索引、3D human parsing 预训练权重也试过——在标准 T-pose 模型上尚可 一旦换成真实扫描网格肩线、腰线、臀线对应的顶点簇与视觉语义严重错位。3. 第一步从 3D 模型切出平面图片「切图」并非简单截图——必须保证后续映射时相机外参与截图时刻完全一致。 人体网格挂载于可旋转的转盘Transform上相机固定 每次 SCHP 推理前会保存ViewSnapshot相机位置、朝向等。3.1 多视角采集「多角度 2D SCHP」流程按预定步进角旋转模型逐帧渲染视口并送入 ONNX。 每一帧产出RGB 渲染图、512×512 标签图、对应ViewState。 这些帧存入_parsedFrames供单帧「结果反射」或多帧「加权融合」使用。3.2 前景掩码视口底色为纯色如红底 按与背景色的色差提取前景避免把阴影误判为背景的同时尽量保留暗部身体区域。 SCHP 输入还可做对称扩边默认四边各扩若干像素减轻缩放到 512 时的边缘截断效应。3.3 正交投影导出为部位点集服务分割完成后按模型包围盒生成两张正交投影图720×560正面图X–Y 平面每一列取最靠近观察者的标签画家算法侧面图Z–Y 平面同样按深度取可见标签导出前会对标签做透视确保侧面图上前缘与后缘都有像素让 2D 映射结果在投影平面上可读。4. 第二步SCHP 二维人体解析推理管线Schp→ ONNX Runtime → argmax 得逐像素类别 →BlendOverlay与原图混合供人眼检查。单帧 SCHP 在当前视角可见表面上几乎完美但扫描网格是闭合流形单帧无法覆盖背侧。 因此 SCHP 输出本身不是终点——它是多视角融合的原材料。融合前质检并非每一帧都参与融合。过滤器会剔除明显错误的分割例如前景面积过小几乎没拍到人可见身体类少于 5/6关键部位大面积缺失图像顶部条带以「小腿」为主且头部极少纵向逻辑崩溃中间条带「大腿」占比过高而躯干极少躯干被腿类霸占被否决的帧仍可在 UI 中浏览但不写入voteScores避免污染全局标签。5. 第三步结果反射——标签穿透映射到顶点「结果反射」是单视角下最直接的理解方式点击 Form1 的「结果反射」按钮 系统回放该帧保存的ViewState调用ApplySchpLabelReflection。5.1 投影采样规则对每个网格顶点局部坐标 → 转盘 Transform → 世界坐标View.Project(world)得屏幕像素 (sx, sy)按 SCHP 标签图分辨率缩放为 (lx, ly)读取labels[ly * labelW lx]不做法线朝向过滤——同一屏幕坐标上的前后表面顶点取相同2D 标签第 4 点是有意为之人体是薄壳前后投影重叠区域应语义一致旋转模型后背面顶点色应与 SCHP 截面图对齐。核心代码片段单视角反射// SmplViewport.ApplySchpLabelReflection — 简化示意 for (int i 0; i vertexCount; i) { var world meshTransform.Transform(localPosition[i]); var sp viewport.Project(world); int lx (int)(sp.X * labelW / viewportWidth); int ly (int)(sp.Y * labelH / viewportHeight); perVertexLab[i] labels[Clamp(lx, ly)]; // 沿视线穿透不区分正反面 } FillParallelPenetrationAlongDepth(perVertexLab, positions); // 柱穿透补标 RefineSmallLabelIslands(perVertexLab, adjacency); // 拓扑离岛抑制 if (fillNearest) FillUnclassifiedByNearestRegion(...); // 可选未投影区扩散6. 第四步多视角加权融合单视角只能可靠覆盖朝向相机的一半表面。多角度融合为每个顶点维护一个长度为 7 的得分向量voteScores[vertex, class]每接受一帧便累加权重。6.1 权重设计AccumulateWeightedSchpVotesForPrimaryMesh中权重 ∝ max(0,n · v)p — 法线越朝向相机该视角标签越可信背景类额外乘以backgroundVoteScale ≈ 0.38降低「背景误投到网格」的影响低于minFacingDot的顶点在该帧不计票侧对相机时本就看不清融合结束后对每个顶点取 argmax 得最终 byte 标签fused[i]。6.2 手动采样融合除自动转盘批处理外还支持操作员手动旋转到「正面 / 侧面 / 背侧」等关键角度 点击采样加入_manualFusionSamples再一键「从采样融合」—— 适合姿态特殊、自动步进角覆盖不足的情况。核心代码片段加权投票// 每个顶点、每一帧 SCHP float facing Dot(normalize(vertexNormal), normalize(cameraPos - vertexPos)); if (facing minFacingDot) continue; float w Pow(Max(0, facing), normalDotPower); byte label SampleSchpLabel(projectedPixel); if (label 0) w * backgroundVoteScale; voteScores[vertexIndex, label] w; // 全部帧结束后 fused[vertexIndex] ArgMax(voteScores[vertexIndex, *]);7. 第五步网格后处理与补全融合或单帧反射得到的 per-vertex 标签会统一经过三道后处理。7.1 正交穿透补标将顶点按 (X,Y) 量化到柱体默认 4096 bins同一柱内所有顶点共享该柱已标签顶点的多数票类别。 作用多角度融合仅对朝向相机的面加权时背侧顶点常无票柱穿透把「前侧已知标签」沿深度 Z 传播到后侧。7.2 小连通域离岛抑制在三角网格邻接图上对每一语义类做连通分量分析。小于阈值的分量默认 ≥ max(72, 顶点数/900)视为 「2D 误分经投影形成的曲面碎块」——典型如肩背处被错标为头部的粉红色小岛—— 整体改标为环邻域中出现最多的其它类别。默认仅处理头部(1)避免误删合理的手臂/腿细条。7.3 未分类点就近归类投影区外、或始终未被任何帧看到的顶点 label0。多源 BFS 从已分类顶点向外扩散 按拓扑距离赋最近有效语义——我们也可启用「未着色点就近归类」。核心代码片段柱穿透补标// 同一 (Qx(X), Qy(Y)) 柱内统计类别直方图 long columnKey Pack(Qx(pos.X), Qy(pos.Y)); columnVotes[columnKey][label]; // 柱内每个顶点含背面继承该柱多数票 for (int i 0; i vertexCount; i) { long key Pack(Qx(pos[i].X), Qy(pos[i].Y)); labels[i] ArgMax(columnVotes[key]); // 背面与前面共享语义 }8. 第六步正/侧面部位点云导出顶点级标签就绪后系统将其再投影到两张正交平面形成结构化的PartPlanePoints集合——这是第一期与第二期之间的数据接口。8.1 PartPlanePoints 结构每个 SCHP 部位头、躯干、上臂…对应一条记录FrontPlanePixels[]— 正面图 X–Y 上该部位所有可见顶点的像素坐标SidePlanePixels[]— 侧面图 Z–Y 上的对应像素坐标坐标系与 PNG 一致X 向右Y 向下(0,0) 为左上角。8.2 PartPlanePointGroups在正面图上上臂/大腿按躯干 X 中心拆分为解剖学左/右图像右侧 人体左侧 供肩-臂连接点、左右上腿范围等几何规则使用——细节见第二期但点集本身来自第一期的分割质量。9. 3D 可视化让分割「长在模型上」最直观的效果呈现是带 SCHP 顶点色的旋转人体模型。 将七类标签转为顶点 diffuse 色 在 HelixToolkit 视口中实时渲染可从任意角度检查分割。10. 不同模型与边界情况场景分割风险应对肥胖 / 宽松衣物躯干与上臂粘连多视角融合 离岛抑制手动补采样侧面消瘦 / 含胸躯干面积过小被质检拒绝放宽SchpMultiViewFusionFrameFilter或手动融合非 A-pose 抬臂上臂遮挡躯干增加背侧视角帧柱穿透补全背面扫描孔洞 / 非流形BFS 扩散越界关闭就近归类或降低网格简化程度长发 / 配饰头部类向外扩张头部离岛抑制第二期颈围算法单独处理11. 小结整理流程概括为3D 渲染切图→SCHP 2D 解析→视线反射 / 多视角融合→柱穿透 拓扑后处理→正/侧面部位点集→3D 顶点色可视化。基于以上的比较稳定的部位分割我们就可以进行身体不同部位的测量工作。在侧面轮廓上用手工可控的特征点算法定位颈/胸/腰/臀测量平面 在 3D 网格上截取倾斜截面并计算围度。我们将在下篇尝试进行3D部位测量处理。