
SAM 3D Body MLX 移植版实战指南在 Apple Silicon 上用纯 MLX 从单张图片重建 3D 人体网格【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm本文围绕 mlx-vlm 仓库中 SAM 3D Body 模块 展开介绍如何将 Meta 的单图 3D 人体网格估计模型SAM 3D BodyarXiv:2603.15603完整移植到 Apple Silicon 上实现从单张 RGB 图片直接输出 18,439 顶点人体网格、127 关节骨架与 70 个 3D 关键点且推理全程不依赖 PyTorch。读完本文你将掌握该模块的安装加载、单图推理、网格导出、骨架/网格叠加渲染、视频逐帧处理、CLI 使用、权重转换流程以及从 DINOv3 骨干到 MHR 参数化人体的完整架构与源码级实现原理。模块定位单图 3D 人体网格估计而非分割在进入细节之前需要先厘清一个容易混淆的点SAM 3D Body 与仓库中的 SAM 3 / SAM 3.1 完全不同。后者是分割segmentation模型输出 mask而 SAM 3D Body 是人体网格估计mesh estimation模型从一张裁剪出的人体图片512×384出发最终输出输出形状说明网格顶点 Mesh vertices(18439, 3)全身表面网格骨架 Skeleton(127, 4, 4)关节世界变换FK 链3D 关键点(70, 3)身体 手 面部关键点相机参数 Camera(3,)弱透视相机scale, tx, ty整个管线——骨干网络、解码器、正向运动学FK、蒙皮skinning——全部以纯 MLX 实现推理时零 PyTorch 依赖。模型参数量约 720M。Quick Start加载与使用模块遵循 mlx-vlm 的模型约定提供高层封装SAM3DPredictor位于 generate.py只需指定权重目录即可加载from mlx_vlm.models.sam3d_body.generate import SAM3DPredictor predictor SAM3DPredictor.from_pretrained(/path/to/sam3d-mlx-weights)from_pretrained内部会读取权重目录下的config.json通过 config.py 中的SAM3DConfig.load实例化 model.py 中的SAM3DBody再调用load_all_weights加载 safetensors 权重。配置中的关键默认值包括骨干embed_dim1280、depth32、num_heads20、patch 尺寸 16、输入image_size(512, 384)、解码器 6 层 1024 维、MHR 参数num_joints127、num_vertices18439、num_faces36874、num_shape_comps45、num_face_comps72、姿态输出维度 519。单图推理Predictor APISAM3DPredictor.predict()接受一个 RGB uint8 numpy 数组与可选的人体边界框返回包含网格顶点、3D 关键点、关节坐标与相机参数的字典import numpy as np from PIL import Image image np.array(Image.open(photo.jpg).convert(RGB)) result predictor.predict(image, bbox[100, 50, 400, 500]) # result[pred_vertices] - (18439, 3) mesh vertices # result[pred_keypoints_3d] - (70, 3) 3D keypoints # result[pred_camera] - (3,) weak-perspective camera # result[pred_joint_coords] - (127, 3) joint coordinates # result[bbox] - the bbox actually used从 generate.py 的实现可以看到predict的完整调用链bbox 处理未提供 bbox 时默认使用整幅图像[0, 0, w, h]相机内参未提供cam_int时以图像对角线sqrt(h²w²)作为焦距主点取图像中心构造 3×3 内参矩阵与 PyTorch 默认行为一致预处理通过 batch_prep.py 的prepare_image完成以 bbox 中心为基准、取 max(宽,高)×1.2 的方形裁剪 → 仿射变换缩放至 512×384 → ImageNet 归一化mean(0.485, 0.456, 0.406)std(0.229, 0.224, 0.225)CLIFF 条件get_cliff_condition按公式[(cx−W/2)/f, (cy−H/2)/f, bbox_scale/f]构造 3 维条件向量bbox_scale 宽度×1.25前向传播将图像、CLIFF 条件、bbox、原始图像尺寸与内参传入SAM3DBody显式求值由于 MLX 是惰性求值调用mx.eval强制计算网格、关键点、关节坐标与相机参数后转回 numpy。值得注意的是SAM3DBodyEstimatorestimator.py提供了更完整的变体当未指定 bbox 且auto_detectTrue时它会用 torchvision 的 Faster R-CNNMobileNet V3 backbone自动检测人COCO 类别 1并按面积降序选取最大的人此外还额外返回pred_pose前 136 维模型参数与pred_shape45 维形状参数并支持predict_batch批量处理多个裁剪。网格导出OBJ 写入预测得到的顶点可通过 estimator.py 中的write_obj导出为标准 OBJ 文件面片按 1 起始索引写出顶点保留 6 位小数from mlx_vlm.models.sam3d_body.estimator import write_obj write_obj(result[pred_vertices], faces, output.obj)面片索引faces需要从权重目录加载——overlay.py 中的load_faces会从 safetensors 中读取head_pose.faces张量(36874, 3) int32并在权重目录旁缓存一份faces.npy以加速重复使用。单图叠加渲染骨架与网格两种风格overlay.py 提供两种叠加方式骨架叠加纯 OpenCV零额外依赖与照片级网格渲染需要pyrendertrimeshimport cv2 from mlx_vlm.models.sam3d_body.overlay import ( draw_skeleton_overlay, render_mesh_overlay, load_faces, ) frame_bgr cv2.imread(photo.jpg) # Skeleton only (fast, no extra deps) skel_bgr draw_skeleton_overlay(result, frame_bgr) cv2.imwrite(photo_skeleton.jpg, skel_bgr) # Full mesh overlay (requires: pip install pyrender trimesh) faces load_faces(/path/to/sam3d-mlx-weights) mesh_bgr render_mesh_overlay(result, frame_bgr, faces) cv2.imwrite(photo_mesh.jpg, mesh_bgr)骨架叠加的核心逻辑在 video.py 中project_keypoints_perspective将弱透视相机参数 (s, tx, ty) 经符号翻转flip s 与 ty后结合 bbox、焦距转换为相机平移量再做透视投影把 3D 关键点映射回 2D随后draw_skeleton按 MHR70 关键点拓扑绘制连线注意并非 COCO-17 拓扑0–4 头部、5–6 肩、7–8 肘、9–10 髋、11–12 膝、13–14 踝、15–20 双脚、21–62 双手、69 颈并用红/绿/蓝/黄区分头面、躯干、手臂、腿部关节。网格渲染则通过compute_cam_t反投影相机平移、以 60° 默认 FOV 构造pyrender.IntrinsicsCamera对网格绕 X 轴旋转 180° 以对齐坐标系配合三点方向光完成照片级渲染最后用深度缓冲做前景/背景合成。视频管线逐帧人体姿态估计模块内置了基于 OpenCV 的视频处理脚本逐帧执行人体估计并以骨架叠加渲染输出python -m mlx_vlm.models.sam3d_body.video --input pitch.mp4 --output pitch_overlay.mp4process_videovideo.py的完整流程为读取视频元信息分辨率/FPS/总帧数→ 加载SAM3DBodyEstimator并预热人物检测器 → 逐帧读取若未指定固定 bbox 则每帧运行 Faster R-CNN 人物检测并通过track_person按 IoU阈值 0.3跨帧跟踪同一目标丢失跟踪时回退到最大检测框也可通过target_region指定首帧区域提示→ 推理 → 透视投影 → 绘制 bbox 与骨架 → 在帧上叠加 MLX 推理耗时与帧号 → 写入输出视频。帧间数据3D 关键点、相机、bbox会收集起来额外以输出名_keypoints.npy保存全部帧的 3D 关键点并打印吞吐量、中位耗时、检测失败率等汇总。CLI 还支持--weights、--bbox、--max-frames、--skip跳帧加速等参数。CLI单图到网格的一键命令generate.py 的main()提供了完整的命令行入口# Single image → OBJ mesh python -m mlx_vlm.models.sam3d_body.generate --image photo.jpg --output mesh.obj # With bounding box python -m mlx_vlm.models.sam3d_body.generate --image photo.jpg --bbox 100,50,400,500 --output mesh.obj # Custom weights directory python -m mlx_vlm.models.sam3d_body.generate --image photo.jpg --weights /path/to/weights/ --output mesh.obj # Save 3D keypoints alongside mesh python -m mlx_vlm.models.sam3d_body.generate --image photo.jpg --output mesh.obj --save-keypoints注意generate.py的 CLI 中--weights是必填参数--bbox以x1,y1,x2,y2字符串形式传入CLI 内部会 split 成浮点列表--output指向 JSON 结果文件而非 OBJ。CLI 运行后会在终端打印顶点形状、网格在 Y 轴身高方向的跨度单位米与相机参数并将顶点形状、网格跨度、相机与 bbox 写入 JSON。若想直接输出 OBJ 网格请使用上述 Python API 的write_objREADME 中的命令语义与estimator.write_obj对应--save-keypoints表示同时落盘 3D 关键点数据。架构全景从 DINOv3 骨干到 MHR 参数化人体README 给出了完整的数据流结合 model.py 的SAM3DBody.__call__可实现一一对应Image (512×384 RGB) │ ▼ DINOv3-H Backbone (32 layers, 1280d, 20 heads, 2D axial RoPE) │ ├── storage tokens (4) │ ▼ Prompt Encoder (70 keypoint embeddings hand box embedding) │ ▼ Transformer Decoder (6 layers, 1024d, 8 heads) │ cross-attention to backbone features │ ray conditioning (camera intrinsics → 1379-ch ray map → 1×1 conv) │ ├── pose token ──► MHR Head (FFN → 519 params) │ │ │ ├── body rotation: 33 joints × 6D cont. │ ├── shape: 45 betas │ ├── face: 72 expression coeffs │ └── hand: 54 PCA coeffs (27 per hand) │ │ │ ▼ │ MHR Body Model (pure MLX) │ ├── Parameter transform (889×249 matrix) │ ├── Forward kinematics (127 joints, quaternion prerotations) │ ├── Blend shapes (45 shape 72 face vectors) │ ├── Pose correctives (sparse predictor → dense) │ └── Linear blend skinning (51,337 sparse weights) │ │ │ ▼ │ Skinned vertices (18439, 3) skeleton (127, 4×4) │ ├── camera token ──► Camera Head (FFN → 3: scale, tx, ty) │ └── keypoint tokens ──► 3D Keypoint regression (70 joints)前向传播的关键步骤model.py依次是骨干编码DINOv3Backbone输出 (B, 32, 24, 1280) 的 patch 级特征光线条件Ray Conditioning当提供相机内参与 bbox 时compute_ray_map将裁剪区域像素映射回原图坐标按(pixel − principal_point) / focal_length构造 2D 光线方向apply_ray_conditioning用 AvgPool2d(16) 将光线降采样到 patch 分辨率补 z1 得 3D 方向经fourier_encode16 频段输出 99 维 3 原始 96 sin/cos编码后与图像特征拼接成 1379 通道再过 1×1 卷积model.py 的RayConditionEmbeddingconv 权重 (1280,1,1,1379) LayerNormToken 组装构建 145 个 token——initCLIFF 条件 3 维 初始姿态 519 维 初始相机 3 维 525 维经init_to_token_mhr投影、prev、promptdummy 无效点、2 个 hand 检测 token、70 个 2D 关键点 token、70 个 3D 关键点 token迭代解码6 层 transformer 解码器对骨干特征做 cross-attention每层通过token_to_pose_fn从首 token 预测中间姿态与相机kp_update_fn则用当前 3D 关键点做透视投影回 2D再以grid_sample_2d双线性采样图像特征更新 2D 关键点 token同时以骨盆左右髋均值归一化的 3D 坐标更新 3D 关键点位置编码——这是与 SAM 3D Body 原版一致的迭代细化机制最终输出取最后一层的 body_output 与相机预测返回。组件与权重前缀组件说明权重前缀Vision EncoderDINOv3-H ViT32 blocks2D axial RoPEbackbone.*Prompt Encoder关键点/框 prompt 嵌入prompt_encoder.*Decoder6 层 transformer cross-attentiondecoder.*Ray Conditioning相机光线 → embed_dim 的 1×1 卷积ray_condition_embed.*MHR Pose HeadFFN 将 decoder token 投影为 519 参数head_pose.proj.*MHR Body ModelFK blend shapes skinning纯 MLXhead_pose.body_model.*Camera HeadFFN 预测弱透视相机head_camera.*Keypoint MLPs2D/3D 关键点位置嵌入keypoint_posemb_linear.*、keypoint3d_posemb_linear.*MHR 人体模型详解MHR 身体模型是原版 TorchScript JIT 模型的纯 MLX 重实现mhr_body.py推理阶段不需要 PyTorch。各阶段职责与关键缓冲如下阶段作用关键缓冲参数变换 Parameter transform将 519 维网络输出经学习的线性变换映射为 889 维内部参数parameter_transform889×249正向运动学 Forward kinematics由局部旋转 四元数预旋转构建 127 关节骨架joint_translation_offsets、joint_prerotations、joint_parents混合变形 Blend shapes在静止网格上叠加形状45 PCA与面部72 表情形变shape_vectors45×18439×3、face_expressions.shape_vectors72×18439×3姿态校正 Pose correctives由关节旋转经稀疏预测器 → 稠密顶点修正稀疏索引53,136 项 稠密层55317×3000线性混合蒙皮 LBS每个顶点对关节变换的加权求和skin_weights_flattened51,337 项实现要点mhr_head.py 的MHRHead.__call__解码器输出的 519 维向量被切分为全局旋转 6D经rot6d_to_rotmat转旋转矩阵、再转 ZYX 欧拉角、260 维连续身体姿态经compact_cont_to_model_params_body映射为 133 维欧拉角、45 维形状、28 维尺度 PCA 系数、108 维手部参数左右手各 54经 PCA 解码hand_pose_mean coeffs hand_pose_comps后 scatter 回 136 维姿态向量与 72 维表情当前端口置零。姿态与形状拼接为 204 维模型参数后送入身体模型先按minmax_parameter_index做软钳制198 项再 pad 到 249 维与 (889,249) 参数变换矩阵相乘得到 889 维关节自由度每关节 7 DOF平移 3 欧拉旋转 3 尺度 1随后按joint_parents拓扑顺序逐关节做 FK根关节直接用局部变换子关节按parent_rot local累乘、位置parent_pos parent_scale * (parent_rot local_trans)旋转以四元数形式存入 skel_state (127, 8)蒙皮阶段用inverse_bind_pose与 51,337 项稀疏skin_indices/skin_weights/vert_indices对混合变形后的顶点做加权变换。最终顶点与关节坐标除以 100 从厘米换算为米经keypoint_mapping308×18566 18439 顶点 127 关节的 einsum 得到 308 个关键点取前 70并对 Y/Z 轴取反以对齐相机坐标系。Apple Silicon 性能基准README 给出的实测数据M3 Max 36GB、float16 精度Apple Silicon指标数值单图推理~490ms骨干DINOv3 32L~280ms解码器 各 head~60msMHR 人体模型FK skinning~150ms模型参数量~720M顶点精度 vs PyTorch 0.001mm视频处理在 M3 Max 上可持续约 2 FPS。这些数据是在特定硬件与精度条件下的实测结果不同机型与配置会有所差异。权重转换从 PyTorch checkpoint JIT 模型到 safetensorsSAM 3D Body 的原始权重由两部分组成PyTorch.ckpt主 checkpoint以及 MHR 人体模型的 TorchScript JIT.pt文件。转换器 convert_weights.py 一次性处理两者python -m mlx_vlm.models.sam3d_body.convert_weights \ --checkpoint /path/to/model.ckpt \ --mhr-model /path/to/assets/mhr_model.pt \ --output /path/to/sam3d-mlx-weights/关键转换步骤QKV 拆分融合的qkv.weight/qkv.bias正则匹配backbone.encoder.blocks.N.attn.qkv.*按 1/3 切分为独立的q_proj、k_proj、v_projbias_mask同理拆为 q/k/v 三份Conv2d 转置PyTorch 布局(O,I,H,W)→ MLX 布局(O,H,W,I)覆盖patch_embed.proj、mask_downscaling与ray_cond_emb.conv.weight另提供transpose_conv_transpose2d处理反卷积骨干前缀改写backbone.encoder.cls_token→backbone.cls_token、patch_embed.proj→patch_embed.projection、attn.proj→attention.o_proj、blocks.N.X→backbone.blocks.N.X等JIT 模型抽取torch.jit.load后遍历named_buffers()与named_parameters()前缀character_torch.→mhr.character.、face_expressions_model.→mhr.face_expressions.、pose_correctives_model.→mhr.pose_correctives.并跳过形状含 0 的空张量dtype 转换bfloat16 → float16safetensors 原生不支持 bfloat16、int64 → int32、bool → uint8输出model.safetensors或按 5GB 阈值分片为model-00001-of-0000N.safetensors多文件 model.safetensors.index.jsonconfig.json转换结束会打印各前缀的键数量统计与 dtype 分布。需要说明的是当前版本的关键键名规范已收拢到 model.py 的SAM3DBody.sanitize()静态方法中它以backbone.encoder.cls_token或character_torch.*是否存在为canary判定输入是否为原始 PyTorch 键若是则执行完整重映射含 QKV 拆分、Conv2d 转置、MHR 前缀映射并统一丢弃decoder_hand.*、head_pose_hand.*等手部专用模块与prompt_encoder.mask_downscaling.*、k_proj.biasK 偏置被掩码为零、bias_mask等推理无关键mlx-vlm 的mlx_vlm.utils.load()在加载时会自动调用sanitize()因此两条路径预转换键或原始键都能得到相同的加载结果。load_all_weights加载时同样会跳过hand_*前缀并通过head_pose.load_all_weights单独对 MHR 身体模型键做mhr.*→body_model.*的重映射未使用的缓冲如pmi、rest_vertices、texcoords等直接跳过。文件结构速览mlx_vlm/models/sam3d_body/ ├── __init__.py # Module exports (Model, ModelConfig, VisionModel, LanguageModel) ├── config.py # SAM3DConfig, VisionConfig, TextConfig dataclasses ├── model.py # SAM3DBody — top-level forward, weight loading, sanitize() ├── backbone.py # DINOv3Backbone — ViT-H (32 blocks, 1280d, 2D RoPE) ├── rope.py # DINOv3RoPE — 2D axial RoPE with learned periods ├── layers.py # LayerNorm32, SwiGLU, LayerScale ├── prompt_encoder.py # Keypoint/box prompt → embeddings ├── decoder.py # PromptableDecoder wrapper ├── transformer.py # DecoderFFN, transformer decoder layers ├── mhr_head.py # MHRHead — pose FFN → parameter extraction → body model ├── mhr_body.py # MHRBodyModel — FK, blend shapes, skinning (pure MLX) ├── mhr_utils.py # Rotation math (rot6d, euler, quaternion), parameter mapping ├── camera.py # Perspective projection ├── batch_prep.py # Crop, resize, ImageNet normalize, CLIFF conditioning ├── estimator.py # SAM3DBodyEstimator — preprocessing inference OBJ export ├── generate.py # SAM3DPredictor — mlx-vlm from_pretrained/predict API ├── video.py # Video pipeline with skeleton overlay rendering ├── overlay.py # Single-image skeleton pyrender mesh overlay helpers ├── convert_weights.py # PyTorch .ckpt JIT .pt → safetensors converter ├── vision.py # VisionModel stub (wraps backbone for mlx-vlm compat) └── language.py # LanguageModel stub (raises NotImplementedError)mlx-vlm 框架集成方式模块严格遵守 mlx-vlm 的模型约定见init.py导出Model SAM3DBody与ModelConfig SAM3DConfig别名供框架加载器使用VisionModel包装 DINOv3 骨干LanguageModel是占位桩SAM 3D Body 是纯视觉模型不涉及文本编码调用即抛NotImplementedErrorsanitize()静态方法接入 mlx-vlm 的权重加载路径配置中model_type: sam3d_body对应框架的MODEL_REMAPPING。已知限制与后续计划README 明确列出的当前移植版限制手部细化暂未实现上游模型自带第二阶段解码器用检测到的手部裁剪框对每只手单独细化checkpoint 中的decoder_hand.*、head_pose_hand.*等权重。当前移植版会过滤掉这些权重只运行身体主流程——对全身姿态足够准确但手指精度停留在身体头部较粗的预测水平。后续 PR 将接入手部细化解码器。手部框检测头已就绪但未启用身体解码器已经能输出预测的手部边界框hand_cls_embed、bbox_embed但在手部细化流程落地前不会被使用。许可证说明原始 SAM 3D Body 模型权重由 Meta 发布许可条款见上游仓库本 MLX 移植代码遵循与 mlx-vlm 相同的许可证。部署前请分别确认模型权重与移植代码的许可要求。【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考