
🏆 第二届3D视觉创新应用竞赛优秀作品 | 🎯 核心技术:人物抠像 · 动作捕捉 · 骨架识别与跟踪 | 📷 硬件平台:Astro Pro 相机
> 开源地址:[GitHub – Skeleton_3D-virtual-drag]
🧠 项目概述
网购衣服最痛的点是什么?“买家秀”与“卖家秀”的惨烈对比。因为无法试穿,近56%的消费者在网购衣物时踩过坑,导致退货率居高不下。传统的虚拟试衣系统要么建模成本高昂,要么就是贴图式的“2D换头术”,毫无真实感可言。
Skeleton_3D-virtual-drag 正是为了打破这一痛点而生。本项目立足于 Astro Pro 深度相机,结合 OpenPose 三维骨架追踪技术与 ADAM 人体模型,打造了一套“衣随人动、360度无死角”的 AR 虚拟试衣系统。不需要脱衣,往镜头前一站,你就是元宇宙里最靓的仔!
✨ 核心亮点:
– 真实体型拟合:基于特征识别与自动抠图,提取真实身材数据,告别“均码模型”;
– 毫秒级动捕:20个关键关节实时追踪,3D模型与人体动作神同步,照镜子即视感;
– 物理级布料渲染:布料解算加持,衣服不再是死板的贴图,而是有动态质感的“真衣服”。
🏗️ 系统架构
本系统采用经典的多线程流水线架构,基于 OpenPose 的 Worker 机制将繁重的计算任务拆解,确保实时性。整体数据流如下:
| 阶段 | 模块 | 职责 | 技术选型 |
|---|---|---|---|
| 1. 感知层 | 图像采集与标定 | 获取多视角图像,去畸变,提取内外参 | Astro Pro, OpenCV calib3d |
| 2. 识别层 | 2D骨架提取 | 检测人体20个关键点 | OpenPose Pose 模块 |
| 3. 重建层 | 3D姿态三角化 | 多视角2D点融合为3D空间坐标 | OpenPose PoseTriangulation |
| 4. 拟合层 | 人体模型驱动 | 将3D关键点映射至 ADAM 人体模型 | JointAngleEstimation, Eigen |
| 5. 渲染层 | 抠图与布料解算 | 前景提取,布料物理仿真叠加 | 贝叶斯抠图,AR渲染引擎 |

🧩 核心模块说明
为了避免把大家淹没在源码的汪洋大海中,我们提炼了系统中最硬核的几个模块设计:
1. 多目相机标定与去畸变 (CameraParameterReader)
想算准3D骨架,相机的内外参必须拿捏得死死的。这个模块不仅负责读取 XML 格式的相机参数,还内置了去畸变功能。
– 设计取舍:采用 PIMPL(指向实现的指针)惯用法,将 OpenCV 的 Matrix 等重型依赖隐藏在 cpp 文件中,保持了头文件的轻量化,编译速度 ++。
class OP_API CameraParameterReader
{
public:
explicit CameraParameterReader();
virtual ~CameraParameterReader();
// cameraExtrinsics is optional
explicit CameraParameterReader(const std::string& serialNumber,
const Matrix& cameraIntrinsics,
const Matrix& cameraDistortion,
const Matrix& cameraExtrinsics = Matrix(),
const Matrix& cameraExtrinsicsInitial = Matrix());
// serialNumbers is optional. If empty, it will load all the XML files available in the
// cameraParameterPath folder
void readParameters(const std::string& cameraParameterPath,
const std::vector<std::string>& serialNumbers = {});
// It simply calls the previous readParameters with a single element
void readParameters(const std::string& cameraParameterPath,
const std::string& serialNumber);
void writeParameters(const std::string& cameraParameterPath) const;
unsigned long long getNumberCameras() const;
const std::vector<std::string>& getCameraSerialNumbers() const;
const std::vector<Matrix>& getCameraMatrices() const;
const std::vector<Matrix>& getCameraDistortions() const;
const std::vector<Matrix>& getCameraIntrinsics() const;
const std::vector<Matrix>& getCameraExtrinsics() const;
const std::vector<Matrix>& getCameraExtrinsicsInitial() const;
bool getUndistortImage() const;
void setUndistortImage(const bool undistortImage);
void undistort(Matrix& frame, const unsigned int cameraIndex = 0u);
private:
// PIMPL idiom
// http://www.cppsamples.com/common-tasks/pimpl.html
struct ImplCameraParameterReader;
std::shared_ptr<ImplCameraParameterReader> spImpl;
DELETE_COPY(CameraParameterReader);
};
2. 3D骨架三角化 (PoseTriangulation)
单目相机只能猜深度,多目相机才能算真值。该模块接收多台相机的 2D 关键点向量,通过三角测量重建出真实的 3D 骨架。
– 关键参数:minViews3d。至少需要几台相机看到同一个点才进行重建?这是精度与鲁棒性的博弈,默认设置为 2,在遮挡严重时可适当调高。
class OP_API PoseTriangulation
{
public:
PoseTriangulation(const int minViews3d);
virtual ~PoseTriangulation();
void initializationOnThread();
Array<float> reconstructArray(
const std::vector<Array<float>>& keypointsVector, const std::vector<Matrix>& cameraMatrices,
const std::vector<Point<int>>& imageSizes) const;
std::vector<Array<float>> reconstructArray(
const std::vector<std::vector<Array<float>>>& keypointsVector, const std::vector<Matrix>& cameraMatrices,
const std::vector<Point<int>>& imageSizes) const;
private:
const int mMinViews3d;
};
3. 关节角度估算与模型驱动 (JointAngleEstimation)
这是从“骨架”走向“皮肉”的关键一步。3D关键点只是火柴人,怎么变成有血有肉的虚拟人?
– 核心逻辑:利用 adamFastFit 函数,将 OpenPose 的 3D Keypoints 快速拟合到 ADAM 参数化人体模型上,计算出 ADAM 模型的姿态参数和位移向量,从而驱动整个网格变形。
class OP_API JointAngleEstimation
{
public:
static const std::shared_ptr<const TotalModel> getTotalModel();
JointAngleEstimation(const bool returnJacobian);
virtual ~JointAngleEstimation();
void initializationOnThread();
void adamFastFit(Eigen::Matrix<double, 62, 3, Eigen::RowMajor>& adamPose,
Eigen::Vector3d& adamTranslation,
Eigen::Matrix<double, Eigen::Dynamic, 1>& vtVec,
Eigen::Matrix<double, Eigen::Dynamic, 1>& j0Vec,
Eigen::VectorXd& adamFacecoeffsExp,
const Array<float>& poseKeypoints3D,
const Array<float>& faceKeypoints3D,
const std::array<Array<float>, 2>& handKeypoints3D);
private:
// PIMPL idiom
// http://www.cppsamples.com/common-tasks/pimpl.html
struct ImplJointAngleEstimation;
std::shared_ptr<ImplJointAngleEstimation> spImpl;
// PIMP requires DELETE_COPY & destructor, or extra code
// http://oliora.github.io/2015/12/29/pimpl-and-rule-of-zero.html
DELETE_COPY(JointAngleEstimation);
};
4. 异步流水线调度 (W* 系列 Worker)
仔细看代码,你会发现 WPoseTriangulation 和 WJointAngleEstimation 都继承了 Worker<TDatums>。这是 OpenPose 的多线程魔法,每个 Worker 跑在独立线程中,通过 tDatums(数据包)传递数据。
class WPoseTriangulation : public Worker<TDatums>
{
public:
explicit WPoseTriangulation(const std::shared_ptr<PoseTriangulation>& poseTriangulation);
virtual ~WPoseTriangulation();
void initializationOnThread();
void work(TDatums& tDatums);
private:
const std::shared_ptr<PoseTriangulation> spPoseTriangulation;
DELETE_COPY(WPoseTriangulation);
};
class WJointAngleEstimation : public Worker<TDatums>
{
public:
explicit WJointAngleEstimation(const std::shared_ptr<JointAngleEstimation>& jointAngleEstimation);
virtual ~WJointAngleEstimation();
void initializationOnThread();
void work(TDatums& tDatums);
private:
const std::shared_ptr<JointAngleEstimation> spJointAngleEstimation;
DELETE_COPY(WJointAngleEstimation);
};
– 优势:图像采集、2D推理、3D重建、渲染互不阻塞,极大提升了帧率。
📑 设计要点
系统在算法选型上做了一些非常务实的考量:
🔪 特征识别与自动抠图
虚拟试衣的第一步是把人从背景里“抠”出来。系统采用了经典的图像抠图融合方程:
$C = alpha F + (1 – alpha) B $
其中 $C$ 是原图,$F$ 是前景,$B$ 是背景,$alpha$ 是透明度。
– 算法选择:文档中重点提及了贝叶斯抠图。相比于深度学习的分割模型,贝叶斯抠图在边缘处理(如头发丝与衣服交界处)的 $\alpha$ 估计上更加细腻,通过最大化似然准则联合求解 $F, B, \alpha$,避免了边缘的“毛边”现象,这对试衣的视觉真实感至关重要。
👗 布料解算与运动同步
“衣随人动”不是简单的刚体绑定。当人体关节运动时,衣服需要基于物理引擎进行解算(如重力、风力、碰撞检测)。系统将 ADAM 模型的运动数据作为边界条件输入布料解算器,实现了衣服的动态褶皱和形变。
🚀 使用示例
想要跑起来这个项目?只需简单的几步配置。我们在 flags.hpp 中预留了丰富的命令行参数(基于 gflags):
# 1. 克隆项目
git clone https://github.com/3DCVdeveloper/Skeleton_3D-virtual-drag.git
# 2. 运行核心程序
./3d_virtual_drag
--camera_resolution 1280x720
--logging_level 3 # 0-255,3是默认值,只输出重要信息
--disable_multi_thread false # 保持多线程开启,帧率更高
⚙️ 参数调优小贴士:
- 如果你的 GPU 不够顶,出现明显延迟,可以尝试将
--disable_multi_thread设为true,牺牲帧率换取低延迟;
--profile_speed 1000可以开启性能分析,帮你找到流水线中的性能瓶颈。
🧭 后续规划
虽然当前系统已经实现了完整的闭环,但作为追求极致的具身智能工程师,我们还有更远的路要走:
1. 轻量化部署:目前依赖 OpenPose 完整框架,略显笨重。计划将 2D/3D 推理部分替换为 TensorRT 加速的轻量级网络,向边缘计算设备(如 Jetson 系列)迁移;
2. 无标定重建:当前多目系统依赖严格的相机标定(calibration 模块),未来希望引入 Self-calibration 算法,让用户随便摆两台相机就能用;
3. OpenCV 版本升级:项目底层依赖较老的 OpenCV 2.4.9(从附带的参考手册可以看出),后续将全面升级至 OpenCV 4.x,享受更现代的 DNN 和 Mat 操控体验;
4. 手指精细操作:目前动捕聚焦于身体 20 关节,未来计划引入 hand 模块,让用户能够用手“虚拟拖拽(virtual drag)”衣服的拉链和纽扣,实现真正的交互试穿!
> 💡 技术让试衣更便捷,代码让世界更有趣。欢迎 Star & PR!