人体模型部位分割:从 SCHP平面解析 映射到 3D 模型

目录

1. 先 2D 分割,再映射 3D

2. 三维上踩的坑

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 PartPlanePointGroups

9. 3D 可视化:让分割「长在模型上」

10. 不同模型与边界情况

11. 小结


对人体扫描网格做语义部位分割,常见做法是直接在三维点云或网格上跑分割网络——我试过很多次,效果都不太理想。 最终发现:先把模型「切」成平面渲染图,用成熟的 SCHP 做二维人体解析,再把标签沿视线反射回网格, 稳定、可控。

工程实现:HelixToolkit + SharpDX · SCHP-Pascal-7 ONNX


1. 先 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 投影采样规则

对每个网格顶点:

  1. 局部坐标 → 转盘 Transform → 世界坐标
  2. View.Project(world) 得屏幕像素 (sx, sy)
  3. 按 SCHP 标签图分辨率缩放为 (lx, ly),读取 labels[ly * labelW + lx]
  4. 不做法线朝向过滤——同一屏幕坐标上的前后表面顶点取相同 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 未分类点就近归类

投影区外、或始终未被任何帧看到的顶点 label=0。多源 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部位测量处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值