该仓库实现了一个轻量级的虚拟试衣间原型,目标是在浏览器端通过几何建模和简单的物理模拟,将用户上传的全身照与指定衣物进行“试穿”,并输出可交互的 3D 模型和预览图。项目分为三个核心部分:
姿态估计(Pose) – 使用预定义的标准骨骼根据输入图像尺寸按比例缩放,得到用户的三维关键点和骨架边。
几何与服装生成(Geometry & Garment) – 根据姿态构造身体的低多边形网格,再根据服装定义生成衣物网格,可选地使用预训练的服装变形模型进一步细化形状。
布料模拟与渲染(Physics & Render) – 对衣物网格进行弹簧—质点模拟以展现动态下垂效果,最终导出 glTF 模型和 SVG 预览,并通过 FastAPI 提供异步接口。
项目链接(Github):
姿态估计:标准骨骼缩放
项目采用固定的标准骨架(canonical skeleton),其关键点名称与三维坐标在 CANONICAL_POINTS 中定义。这些坐标以单位身高为基准,覆盖骨盆、脊柱、颈部、头顶、肩、肘、腕、髋、膝、踝等部位。骨骼边连接关系在 POSE_EDGES 中列出,用于渲染骨架结构。
PoseEstimator.estimate 的核心逻辑是根据输入图片的高度 H 计算缩放因子,然后将所有标准骨骼点乘以该比例,得到与用户身高相匹配的三维关键点。最后以所有点的最小/最大 x、y 坐标构成 bounding box,并返回 PoseResult 包含关键点、边、边界框和缩放因子。
这一方法避免了复杂的姿态检测模型,利用固定比例在视觉效果上提供近似的人体比例,既轻量又适用于试衣场景。
几何与身体网格建模
Mesh 数据结构
项目通过 Mesh 类表示三维网格,包含顶点列表、法向量列表、颜色列表和索引列表。索引列表的三元组按逆时针顺序组成三角面。Mesh.merge 方法将另一个网格合并到当前网格并调整索引偏移
盒子构造与身体网格
身体网格是由多个盒子(矩形平行六面体)组成。create_box 根据中心和尺寸计算半长,然后生成六个面,每个面由四个角和一个法向量定义。例如朝 +z 方向的面顶点为(-h_x, -h_y, h_z), (h_x, -h_y, h_z), (h_x, h_y, h_z), (-h_x, h_y, h_z),法向量为 (0,0,1)(0,0,1)(0,0,1)。函数将这些局部坐标平移到中心并写入 vertices、normals、colors 列表,并按顺序存入索引列表。一个面由两组三角形索引 [0,1,2],[0,2,3][0,1,2],[0,2,3][0,1,2],[0,2,3] 组成,确保正面朝外。身体的躯干和头部通过调用 create_box 产生;四肢则通过 _limb_box 生成细长的盒子,长度取关键点 y 坐标差,粗细按肩宽比例缩放
create_body_mesh 使用姿态中的关键点计算肩宽,躯干高度,并根据比例生成头部、躯干、手臂和腿的盒子,将它们合并为一个网格。这种低多边形建模方式可快速生成近似人体的几何模型,用于与衣服结合。
服装管理与生成
服饰目录与配置
服装信息存储在 assets/garments/garments.json 中,其中每件衣物包含:ID、名称、类别、宽度因子 width_factor、高度因子 height_factor、厚度 depth、可选的尺寸比例表和多种配色。例如 tshirt_basic 的宽度因子为 1.2,高度因子为 1.45,深度 0.12,对应尺寸 S/M/L 的比例分别为 0.92/1.0/1.08。颜色表示为十六进制,GarmentManager 在加载时会将其解析为 RGBA 值
构建衣物网格
GarmentManager.build_garment_mesh 负责根据姿态和服饰配置生成衣物网格,流程如下:
获取衣物参数:读取
width_factor、height_factor和depth,根据用户选择的尺寸倍率调整整体缩放。肩宽 wsw_sws 用于计算衣物宽度 w = w_s \times \text{width_factor};衣物高度 h = h_s \times \text{height_factor} 以髋和肩的 y 坐标差为基准。计算衣物中心:衣物中心
center位于肩中点和髋中点的中间,z 坐标为 0;若没有具体点则基于 scale 生成默认值。姿态特征:调用
compute_pose_features根据关键点计算特征,如肩宽;
躯干长度;
手臂伸展程度 ;
动作强度 ;
遮挡度:根据手腕与肩中点的水平距离计算左右侧遮挡比例。
这些特征后续将用于服装变形和布料模拟参数。
生成网格:若存在与服装 ID 对应的预训练模型,则调用
PretrainedLibrary.generate使用基模型和多个变形组件合成衣物;否则退回到解析的几何尺寸,通过create_box构建简单的盒子网格。输出:返回衣物网格、选定的配色和元数据(包括 pinned_vertices、姿态特征及生成模式)。
布料模拟:质点–弹簧系统
为了让衣物在动画或动图中呈现自然的垂坠和摆动效果,项目实现了一个简化的质点–弹簧布料模拟。ClothSimulator 通过以下步骤生成多个时间帧的顶点状态:
初始化:将顶点列表分块为 3D 位置数组,并为每个点初始化速度为零。调用
_build_springs根据三角索引构建边集合,每条边构成一根弹簧。同时计算每根弹簧的初始长度 L0。外力:在每个模拟步(共有
steps次)中,对每个质点施加重力、风力和侧向摇摆。重力和风力的强度取决于姿态特征中的躯干长度和运动强度。外力向量计算公式:\mathbf{f}_{\mathrm{gravity}} = -g \hat{y} \mathbf{f}_{\mathrm{wind}} = w \sin(\phi) \hat{z} + w \cos(\phi) \hat{x}弹簧力:对每根弹簧,计算当前长度。根据胡克定律,弹簧力为
\mathbf{F}_{ij} = k (L - L_0) \frac{\mathbf{P}_j - \mathbf{P}_i}{L},积分与阻尼:对每个质点,根据总力更新速度并乘以阻尼系数
damping,然后更新位置固定点:对于标记为
pinned_vertices的顶点,将其位置强制恢复为初始位置并将速度置零。记录帧:每个步骤后将顶点数组展平并记录时间戳与顶点数据,构成帧序列。帧率为
1/\mathrm{time\_step}
通过上述迭代,系统生成一组随时间变化的衣物顶点,可供前端播放布料动画。布料模拟简化了空气动力学及碰撞检测,但在服装试穿的演示场景中已足够直观。
渲染与导出
渲染模块提供两种输出:
glTF 导出 –
export_gltf将网格的顶点、法向量、颜色、索引等打包为二进制缓冲区,并生成符合 glTF 2.0 规范的 JSON 结构。它计算最小/最大坐标用于访问器,选择索引类型(16 位或 32 位)。数据采用 4 字节对齐合并;缓冲区使用 Base64 URI 嵌入到 glTF 文件中。SVG 预览 –
render_preview根据姿态关键点将三维坐标投影到 2D 平面:函数绘制骨架线条并在肩、髋关键点之间绘制带圆角的衣物矩形作为填充色块。生成的 SVG 文件体积小,可用作浏览器端快速预览
后端 API 与任务系统
FastAPI 负责提供 REST 接口:
GET /health返回服务器状态;GET /garments返回当前可用衣物列表;POST /tryon提交试衣请求,需要上传图片和衣物 ID,可选尺寸和颜色;服务器生成唯一task_id,将任务添加到后台队列,并立即返回任务编号;GET /result/{task_id}查询任务状态和结果。若任务完成,响应中包含预览图 URL、glTF 模型 URL、以及元数据。
TaskManager 提供线程安全的任务存储,记录每个任务的状态(排队、运行、完成、失败)、结果和错误信息。后台通过 BackgroundTasks 调用 _process_tryon_task,该函数执行管道并在出错时捕获异常,将任务状态设为失败。