拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SHARP:基于SMPL-X先验的宽松衣物三维人体重建方法解析

做单图三维人体重建的人大概率都经历过这种场景输入一张照片想恢复出衣着完整的人体模型结果重建出来的表面不是漏风就是某个部位鼓起一块。尤其是宽松衣物比如大衣、裙子、卫衣几乎算得上隐式重建方法的“翻车重灾区”。SHARP这篇工作当初就是冲着这个问题来的全称是 Shape-Aware Reconstruction of People in Loose Clothing核心思路很清晰既然参数化人体模型在宽松衣物下不够用那就先让模型“感知”身体各个部位的局部形状再配合隐式函数去细化衣物表面最后拿到一个既保留身体姿态、又能表达衣物形变的三维模型。如果你现在正准备跑通官方源码、或者只是想知道它在技术上到底做了什么这篇文章会把论文方法、源码目录、训练推理的关键环节、以及我踩过的坑都拆开讲清楚希望能帮你省下不少折腾时间。1. 项目概述与核心设计思路拆解1.1 SHARP到底解决什么问题常规的3D人体重建大致分成两条路线一条是参数化模型路线用SMPL、SMPL-X这类带着表情和手部参数的网格模板去拟合图像优点是稳定、轻量、动画友好缺点也很明显——它只能表达裸体或者紧身衣的体型遇到宽松衣物就彻底没辙因为衣物的几何形变远远超出了参数模型能表达的拓扑范围。另一条是隐式函数路线像PIFu、DeepHuman这些方法直接用神经网络预测空间里每个点是“在人体内”还是“在人体外”最后用Marching Cubes提取等值面。这种方案在表达自由拓扑上非常有优势衣服褶皱、裙摆都能做出来但问题在于纯靠图像特征去推断全局形状在遮挡多、视角少的情况下很容易出现形状不稳定尤其是肢体末梢和宽松衣物区域。SHARP属于典型的“两边都想要”的设计它先基于SMPL-X参数化人体模型估计出一个粗略的身体形状然后用这个形状去引导隐式函数的学习。这样既保留了参数模型带来的姿态稳定性和强先验又用隐式占用场去表达宽松衣物的复杂表面。换句话说参数模型负责“不出大错”隐式函数负责“把细节垒上去”。1.2 方法总览从SMPL-X到shape-aware隐式场SHARP的整体流程可以概括成三个阶段。第一阶段是人体拟合输入一张RGB图片先跑一个现成的SMPL-X拟合方法论文里用的是SPIN或者SMPLify-X这类方案得到一组姿态、体型、表情参数并生成一个与图片中人脸姿态大致对齐的三角网格。第二阶段是shape-aware特征提取这一步是SHARP区别于PIFu的关键。PIFu对空间中的每一个查询点都是去图像上投影像素取像素对齐的特征。SHARP的思路不同它对每个3D查询点先在SMPL-X网格上找到最近的面片然后通过重心坐标把查询点和这个网格局部区域“绑定”起来。这样一来网络拿到的特征不是简单的位置编码而是带有“身体局部坐标系”的形状感知特征。第三阶段是隐式占用场预测。把上面得到的局部特征输入一个MLP输出该点是实心还是空心的概率值训练时用扫描点云作为监督推理时在整个空间里密集采样最后Marching Cubes提表面。我自己第一次读完这篇论文时的感觉是方法本身不复杂贵在把“网格先验”和“隐式表达”的接口设计得很巧妙。而这个接口恰恰也是源码里最值得逐行读的部分。1.3 和PIFu、PaMIR这些基线方法的差异如果对照PIFu来理解SHARP会更直观。PIFu的核心是pixel-aligned特征查询一个3D点就把它投影回图像平面用图像卷积特征去引导占用预测。这个方案优点是能够保留非常细致的图像纹理线索但问题是图像特征本身是在2D平面上提取的面对大面积的宽松衣物自遮挡网络容易学成“看起来像衣服就行”几何上就会飘。PaMIR则是在PIFu基础上加入了参数化身体体素作为额外条件通过在参数化模型采样体素再和图像特征融合。SHARP和PaMIR有相似之处但SHARP没有把身体模型体素化而是直接在查询点与网格表面之间建立几何关系用最近面片和重心坐标做逐点特征编码。这样做的好处是内存开销更小而且对网格拓扑的依赖性更低身体网格即使面数不算太多也不影响特征精度。当然SHARP也有它的局限性。毕竟整个方法强依赖第一阶段的SMPL-X拟合质量。如果姿态估计偏差很大后续的shape-aware特征也会跟着偏重建结果就会出现肢体错位。这一点在实际复现的时候需要特别注意图片裁剪、相机参数估计这些前处理环节常常比模型本身的调参更影响最终效果。2. 核心细节解析与实操要点2.1 SMPL-X身体先验及其作用要理解SHARP必须先搞明白SMPL-X是什么。SMPL-X是SMPL的扩展版本除了躯干和四肢还加入了手部(15个关节每只手)和面部(下颌等)的参数化控制总关节数大约50多个表面顶点数一万出头。它是通过蒙皮权重(blend weights)驱动顶点变形的参数化模型也就是说给定姿态、体型、表情参数就能生成对应的三角网格。SHARP拿SMPL-X网格来做什么表面上看是提供一个“标准身体形状”。但这个标准形状不仅仅是视觉上的参考它在几何计算层面起到两个作用。第一个作用是最近面片索引。对任意一个3D查询点SHARP需要知道它在身体表面的哪个局部区域附近。通过查找SMPL-X网格上的最近三角形可以拿到一个面片编号面片编号对应身体的局部区域比如左臂、右腿、躯干某一块。这样查询点就被赋予了语义意义。第二个作用是重心坐标插值。SMPL-X网格每个顶点带有蒙皮权重和顶点坐标找到最近面片后可以通过重心坐标把查询点在面片内部的位置表达出来然后在三个顶点之间插值拿到这个点的局部特征。这个过程相当于给每个空间点建立了一个“相对身体表面的偏移坐标”比单纯输入绝对3D坐标要稳定得多因为身体姿态变化时查询点跟着身体局部走网络不需要重新学习姿态带来的坐标变化。所以SMPL-X在这里更像是一个“空间坐标系的锚点”而不是最终输出。理解这一点后面看源码会轻松很多。2.2 shape-aware空间变换与局部特征编码源码里最绕的部分我个人觉得是空间变换这一块。SHARP并不是直接把3D点的绝对坐标丢进MLP而是做了一个“逆蒙皮变形”的操作。通俗地讲网格顶点是带着蒙皮权重被渲染到当前姿态下的如果已知蒙皮权重和当前姿态我们可以把任意空间点从“当前姿态空间”变换回“标准模板空间”。实现这个变换的前提是知道每个点相对于网格表面的局部信息。SHARP在源码中大致是这么做的对每个查询点先找到最近的三角形然后计算该点到三角形的重心坐标并用重心坐标插值出该位置的蒙皮权重。拿到蒙皮权重后再用当前姿态的关节变换矩阵的逆矩阵把查询点变换回T-pose空间。这个空间中的坐标变化主要反映“衣物相对身体的那层厚度和偏移”而不是姿态变化带来的大幅位移MLP学起来自然容易很多。我在复现时的一个体会是这个变换对数学实现的要求虽然不高但对并行计算效率很敏感。因为每个查询点的最近面片索引、重心坐标、蒙皮权重插值都不同很难直接用常规的批量矩阵乘法统一计算。源码里一般会先把所有查询点做一次最近邻搜索得到索引然后利用torch.index_select或者gather去聚合顶点数据避免for循环带来的性能瓶颈。如果你自己动手实现建议优先考虑把批量维度合并然后用矩阵运算一次性处理速度能快一个数量级。2.3 隐式occupancy网络与Marching Cubes提取在shape-aware特征拿到之后后面的工作就清爽多了。SHARP的占用预测部分是一个带有残差连接的多层MLP输入是局部坐标特征、点位置编码和图像全局特征如果有的拼接输出是logits通过sigmoid映射成[0,1]的占用概率。这里有一个容易踩的细节训练时查询点的采样方式会直接影响模型效果。SHARP使用的是在线难例挖掘思路也就是在扫描表面附近多采样点并在远离表面的区域也随机采样一部分作为负样本。采样比例的设置很关键负样本太多网络会倾向于把所有点都预测为空心表面会收缩负样本太少表面又会膨胀。常见的做法是把表面附近点与均匀随机点的比例控制在1比2左右同时用高斯噪声绕着表面点加一些扰动让网络学到更平滑的决策边界。推理时Marching Cubes的参数也要注意。占用阈值一般取0.5但如果模型训练得不够收敛表面会有大量噪点此时可以适当调高阈值到0.6甚至0.7能过滤掉一部分误判为实心的游离点。不过阈值调太高真正薄的衣物区域比如裙摆边缘会被削掉所以阈值本身也是一个需要反复实验的参数。2.4 损失函数与训练策略SHARP的监督信息是3D扫描的占用值或者点云采样。最直接的loss是二元交叉熵损失网络输出的占用概率和查询点真实标签做BCE。除了占用损失不少复现版本还会加入法向量一致性损失(normal consistency loss)或者拉普拉斯平滑损失用来让表面更光滑。原始论文里主要用的是BCE Loss后续的第三方实现往往会加上一些正则项来提升视觉效果。训练策略上SHARP建议使用两阶段训练。第一阶段先固定SMPL-X拟合的网格只训练特征编码和MLP让网络学会利用身体先验。第二阶段再对姿态参数进行微调让网格和重建表面更好匹配。如果你直接从随机初始化开始联合训练容易陷入姿态估计和表面重建相互拖后腿的死锁状态实践中很难收敛。这一点我建议复现时不要省。3. 源码结构分析与复现准备3.1 仓库结构与环境配置官方源码github上搜索SHARP对应Sai Kumar Dwivedi等人的实现是一个标准的PyTorch工程目录结构大致如下SHARP/ ├── configs/ │ ├── sharp.yaml │ └── sharp_eval.yaml ├── lib/ │ ├── dataset/ │ │ ├── thuman_dataset.py │ │ └── cape_dataset.py │ ├── model/ │ │ ├── sharp.py │ │ ├── geometry.py │ │ ├── resnet.py │ │ └── smplx_wrapper.py │ ├── utils/ │ │ ├── mesh.py │ │ ├── rotation.py │ │ └── io_utils.py │ └── net_util.py ├── data/ │ ├── smplx_models/ │ ├── scans/ │ └── images/ ├── train.py ├── test.py └── requirements.txt环境配置上我没有遇到特别刁钻的依赖要求。Python 3.8PyTorch 1.8左右CUDA 10.2或11.x都可以跑通关键依赖是trimesh、pyrender、open3d、scikit-image这些几何处理和视觉相关的库。项目中如果有pytorch3d需要注意和PyTorch版本的对应关系pytorch3d对版本要求很严格经常是环境配置里最花时间的一步。3.2 数据准备与预处理SHARP训练需要两样东西图像和对应的3D扫描。常用的数据集包括THuman和CAPE。THuman提供的是静态扫描加多视角渲染图CAPE则提供了不同动作下的服装扫描序列非常适合做宽松衣物的训练。如果你只是复现推理官方一般会提供预训练模型和测试样例你只需要准备好一张图片以及对应的SMPL-X拟合结果。注意SMPL-X模型文件不是随便下载就能用的它需要从SMPL官网申请而且有商业许可限制。申请通过后会得到一个npz文件放到data/smplx_models目录下即可。预处理阶段需要把图像中的人体区域裁剪出来估计相机内外参然后跑SMPL-X拟合。这一步非常影响最终效果。我在测试中发现如果直接用全身图而不做紧致裁剪相机焦距估计不准重建出来的人体比例会明显不对。建议先用现成的2D姿态估计或人体分割模型把人物框出来放大到合适的分辨率再做拟合。3.3 训练与推理脚本的运行方式配置文件的组织方式比较直白。sharp.yaml里面主要包含数据路径、训练分辨率、采样点数、batch size、学习率这些常规信息。训练启动命令大致是python train.py --config configs/sharp.yaml推理脚本的使用方式也类似python test.py --config configs/sharp_eval.yaml --checkpoint path/to/checkpoint.pt --input path/to/image.png输出通常是一个ply文件可以用MeshLab或者Blender直接打开查看。需要留意的是脚本里可能默认只输出稠密点云需要额外调用Marching Cubes模块生成mesh。在最新版本里这个功能通常已经内置了如果版本老需要自己从lib/utils/mesh.py调用相关接口。3.4 关键代码模块逐段解读lib/model/sharp.py是核心模块SHARP类负责串联整个流程。我简化一下它的forward结构帮助理解class SHARP(nn.Module): def __init__(self, config): super().__init__() self.body_encoder SMPLXWrapper(config) # 加载SMPL-X并生成网格 self.implicit_net ImplicitOccupancyNet(config) # 占用预测MLP def forward(self, image, cam, body_params, points): body_vertices, body_faces self.body_encoder(body_params) # 1. 查找最近面片索引和重心坐标 closest_faces, bary_coords query_closest_faces(points, body_vertices, body_faces) # 2. 根据重心坐标插值出蒙皮权重和顶点特征 blend_weights interpolate_blend_weights(...) # 3. 对每个查询点做逆蒙皮变换 canonical_points inverse_skin(points, blend_weights, body_params) # 4. 编码特征并预测占用 local_feat encode_local(canonical_points, bary_coords) occupancy self.implicit_net(local_feat) return occupancylib/model/geometry.py里面是真正硬核的部分。query_closest_faces函数一般会用到批量最近邻搜索。如果你的数据量比较大比如一次采几百万个点这个函数可能会成为性能瓶颈。我在源码里看到过用open3d的compute_point_cloud_distance来做预筛选的优化但官方版本更多是直接用PyTorch的cdist加min操作显存占用比较大。如果跑不动建议分批采样。train.py里的训练循环没有太多黑魔法核心是采样点、算loss、反向传播。不过有一点值得一说输入图像在这个方法里主要用来做全局身份特征或者姿态线索不像PIFu那样做逐像素特征融合。所以训练时如果图像分辨率不够高影响可能没有PIFu那么大但图像被遮挡严重的区域重建质量依然会下降因为SMPL-X拟合在那里本身就不可靠。4. 常见问题与排查技巧实录这部分我直接整理成表格方便你对照排查。都是我实际跑代码时遇到过的场景。问题现象可能原因解决办法SMPL-X模型文件加载报错模型文件下载不完整或路径不对检查data/smplx_models目录确认为官方npz文件不要用SMPL的模型替代训练时CUDA OOM查询点采样数量过大或batch size太大减少采样点数(如从32768降到8192)减小batch size或者用梯度累积推理时速度特别慢空间采样网格太密或最近面片查询没走GPU实现将Marching Cubes分辨率从512降到256先用粗网格看效果再细调重建表面有大量空洞占用阈值判断偏差或者负样本比例不均衡调低Marching Cubes阈值到0.4训练时增加表面邻近点采样比例重建出来的人体和图片姿势对不上SMPL-X拟合精度不够相机外参不准检查第一阶段拟合结果对比2D关键点重投影误差误差过大的图片直接筛掉宽松衣物区域严重内凹隐式网络没有学到query point的局部偏移确认逆蒙皮变换是否把所有点都映射到了T-pose空间而不是保留绝对坐标训练Loss降不下去采样点中负样本占比过高或者蒙皮权重插值实现有bug检查采样分布可视化查询点和标签确认蒙皮权重插值结果在视觉上连续排错时候我的习惯是先不要直接改网络结构先用小分辨率、小数据量把流程跑通再逐步放大。SHARP的代码链路比较长从图像到网格到特征到占用预测任何一个环节出问题最终输出的mesh都会异常。所以建议把中间结果全部可视化出来比如把SMPL-X网格和重建mesh叠在一起看把query point的占用分值渲染成体素这些手段可以大幅缩短定位问题的时间。另外要特别留意的是坐标系的坑。图像估计的相机参数、SMPL-X网格顶点、3D扫描点云这三者到底是在OpenCV坐标系还是OpenGL坐标系不同数据集给的标注常常不一致。很多复现失败的案例最终都定位在左右手坐标系上——看着人脸朝向是对的其实深度方向反了。处理方法很简单用几个已知特征点比如鼻尖、左右耳分别在扫描点云和SMPL-X网格上标出来检查它们的三轴方向是否一致。5. 关于这个项目的几点个人体会这套方法本身我认为是“网格先验隐式表面”这个技术路线里做得比较扎实的代表作。它的源码并不复杂不像现在很多大模型工程那样动辄几十万行只要用点心一个周末就能把整个训练推理链路读通。但也正因为简洁它对前处理的质量要求非常高。我跑过几次之后最大的体会是如果你想在自定义数据上获得更好的效果花在SMPL-X拟合调参上的时间往往比花在SHARP模型本身上的时间还要多。如果想要继续扩展有几个方向值得尝试。一是把SMPL-X换成更精细的个性化模板比如结合用户的体型扫描建立一个semantic body prior二是把静态单图的隐式场扩展到视频序列利用时序信息解决遮挡问题三是和最近很火的3DGS结合用SHARP生成初始几何再用高斯泼溅去渲染外观。无论往哪个方向走理解SHARP怎么在网格与隐式场之间搭桥都是一笔值得的投资。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门