图像三维人体重建(SAM 3D Body)
更新时间:2026-09-14
简介
SAM 3D Body 人体三维重建算子。对单张图像做人体检测 → (可选)分割 → FOV 估计 → SAM 3D Body 前向,输出每个人的三维网格、二维/三维关键点、MHR 姿态参数与相机平移。三方源码与权重需提前就位,算子内零网络零安装。
功能描述
- 四级流水线:
vitdet人体检测 →sam2人体分割(可选)→moge2FOV 估计 → SAM 3D Body 重建,三个子模型通过tools/build_detector.py/tools/build_sam.py/tools/build_fov_estimator.py按文件路径加载(spec_from_file_location) - sam-3d-body 源码目录由
sam_3d_body_repo_path指定,sys.path只在构建 estimator 与单张推理期间临时插入、用完即撤;源码目录不存在直接FileNotFoundError,运行期不会 clone - 主模型 ckpt 与 MHR 资源可显式给路径,为空时按
<model_path>/<model_name>/model.ckpt和<model_path>/<model_name>/assets/mhr_model.pt推导,两者缺一即FileNotFoundError - 只有传了
segmentor_path才会构建分割器,因此use_mask=True必须同时给segmentor_path;detector_name置空则跳过检测 - 上游
process_one_image返回的是每个人一个 dict 的 list,逐人取bbox/focal_length/pred_vertices/pred_keypoints_3d/pred_keypoints_2d/mhr_model_params/pred_cam_t;上游没有检测分数字段,输出里也不编造 - 张量统一存成
{shape, data}:形状 + 一维 float32 展平数据,不用多层嵌套 list - 两个输出体积开关:
max_instances限制每张图保留的实例数,if_output_vertices=False时不输出网格顶点(实测每人约 1.8 万顶点,人多时网格是输出体积主体) - 未检出人体时
instances为空列表、status为success: no person detected;单张图异常时该行instances为空、status为error: <异常信息>,不中断整列 - 输入支持
image_url(含 BOS 路径,先落到本地临时路径再推理)、image_base64、image_binary;后两者写成/tmp下的临时 jpg 文件后推理,用完即删 - 可视化(pyrender + EGL)本期不做
算子参数
输入
| 输入 | 含义 |
|---|---|
| images | 图像列。按 image_src_type 取值分别是图像路径/URL(含 bos://)、base64 字符串或二进制内容 |
输出
输出为一个 struct 列,字段如下(tensor 均指 struct<shape: list<int32>, data: list<float32>>,data 是按行优先展平的 float32;缺失时为 {"shape": [], "data": []}):
| 输出 | 含义 |
|---|---|
| instances | list |
| instances[].bbox | list |
| instances[].focal_length | float32,该实例使用的焦距(上游 focal_length),取不到时为 0.0 |
| instances[].vertices | tensor,人体网格顶点 (V,3),实测 [18439,3];if_output_vertices=False 时为空 |
| instances[].keypoints_3d | tensor,三维关键点 (70,3) |
| instances[].keypoints_2d | tensor,二维关键点 (70,2) |
| instances[].pose_params | tensor,MHR 模型参数 mhr_model_params,实测 (204,) |
| instances[].translation | tensor,相机坐标系下的平移 pred_cam_t,(3,) |
| status | string,success / success: no person detected / error: <异常信息> |
参数
| 参数名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| image_src_type | str | "image_url" | 输入图像编码形式:image_url / image_base64 / image_binary,其他取值抛 ValueError |
| model_path | str | "/opt/aihc/model" | 权重根目录 |
| model_name | str | "facebook/sam-3d-body-dinov3" | 相对 model_path 的 SAM 3D Body 权重子目录 |
| sam_3d_body_repo_path | str | "/opt/aihc/thirdparty/sam-3d-body" | 预置的 sam-3d-body 源码目录,不存在抛 FileNotFoundError |
| checkpoint_path | str | "" | 主模型 ckpt 路径,空则取 <model_path>/<model_name>/model.ckpt |
| mhr_path | str | "" | MHR 资源路径,空则取 <model_path>/<model_name>/assets/mhr_model.pt |
| detector_name | str | "vitdet" | 人体检测器名;置空则不做检测 |
| segmentor_name | str | "sam2" | 人体分割器名 |
| fov_name | str | "moge2" | FOV 估计器名;置空则不建 FOV 估计器 |
| detector_path | str | "" | 检测器权重目录,空则用上游默认 |
| segmentor_path | str | "" | 分割器权重目录;为空时不构建分割器 |
| fov_path | str | "" | FOV 模型权重路径,须是 MoGe 的 model.pt 文件 |
| bbox_thresh | float | 0.8 | 人体检测框置信度阈值,透传上游 process_one_image(bbox_thr=...) |
| use_mask | bool | False | 是否把分割掩码喂给重建,需要同时配 segmentor_path |
| max_instances | int 或 None | None | 每张图最多保留的人体实例数,None 表示不限 |
| if_output_vertices | bool | True | 是否输出人体网格顶点,关掉可大幅缩小输出体积 |
| rank | int | 0 | 多卡场景 worker 序号,设备取 cuda:rank % cuda_device_count |
注意事项
- 算子内零网络零安装:运行期不下载权重、不安装依赖、不拉源码;
sam_3d_body_repo_path指向的源码目录或权重缺失时,构造阶段直接抛FileNotFoundError。 detector_path传目录,fov_path必须传model.pt文件:vitdet 的detector_path指向包含model_final_f05665.pkl的目录;MoGe 的from_pretrained接受.pt文件或 HF repo id,fov_path传目录会IsADirectoryError。- sm_120(Blackwell)上 detectron2 需要自编 CUDA 扩展:现成 wheel 不含 sm_120 kernel,需要按目标 GPU 架构自行编好 detectron2,算子内不做编译。
- 按
concurrency=1起:这条链路的并发安全性未经验证;本算子测试与建议用法均为num_cpus=1, num_gpus=1, concurrency=1, batch_size=1。 - 依赖面很大:detectron2 + pytorch-lightning + hydra + pyrender 等 30 余个包,建议给它单独的运行环境。
- 输出体积:单人网格
[18439,3]就是 5.5 万个 float32,人多的图建议配max_instances,只要姿态参数与关键点时把if_output_vertices关掉。
调用示例
Python
1from __future__ import annotations
2
3import os
4
5import daft
6from daft import col
7
8from daft.aihc.common.udf import aihc_udf
9from daft.aihc.functions.embodied.perception.image_sam_3d_body import ImageSam3dBody
10
11MODEL_PATH = "/path/to/models"
12
13if __name__ == "__main__":
14 if os.getenv("DAFT_RUNNER", "native") == "ray":
15 import ray
16 ray.init(dashboard_host="0.0.0.0", ignore_reinit_error=True)
17 daft.set_runner_ray()
18 daft.set_execution_config(actor_udf_ready_timeout=6000, min_cpu_per_task=0)
19
20ds = daft.from_pydict({"image": ["bos://your-bucket/sample.jpg"]})
21 ds = ds.with_column(
22 "body",
23 aihc_udf(
24 ImageSam3dBody,
25 construct_args={
26 "image_src_type": "image_url",
27 "model_path": MODEL_PATH,
28 "model_name": "facebook/sam-3d-body-dinov3",
29 "sam_3d_body_repo_path": "/path/to/thirdparty/sam-3d-body",
30 # 检测器传目录(内含 model_final_f05665.pkl)
31 "detector_path": f"{MODEL_PATH}/sam3d_vitdet",
32 # FOV 传 MoGe 的权重文件,不能传目录
33 "fov_path": f"{MODEL_PATH}/Ruicheng/moge-2-vitl/model.pt",
34 "bbox_thresh": 0.5,
35 "max_instances": 4,
36 },
37 num_cpus=1,
38 num_gpus=1,
39 concurrency=1,
40 batch_size=1,
41 )(col("image")),
42 )
43 ds = ds.with_column("status", col("body")["status"])
44 ds.show()
评价此篇文章
