dev-bjoern Claude commited on
Commit
bdcd5f4
Β·
1 Parent(s): db0d80e

Add SAM 3D Body MCP server implementation

Browse files

- Gradio app with GPU support for body mesh reconstruction
- Image to 3D GLB export via Blender
- MCP server integration for AI agents

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (3) hide show
  1. README.md +35 -6
  2. app.py +167 -0
  3. requirements.txt +20 -0
README.md CHANGED
@@ -1,12 +1,41 @@
1
  ---
2
- title: Sam3d Body Mcp
3
- emoji: πŸ‘€
4
- colorFrom: yellow
5
- colorTo: blue
6
  sdk: gradio
7
- sdk_version: 6.0.2
8
  app_file: app.py
9
  pinned: false
 
 
 
 
 
 
 
 
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: SAM 3D Body MCP
3
+ emoji: 🧍
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 5.9.1
8
  app_file: app.py
9
  pinned: false
10
+ license: other
11
+ hardware: zero-a10g
12
+ tags:
13
+ - mcp-server-track
14
+ - building-mcp-track-consumer
15
+ - agents
16
+ - 3d
17
+ - mesh-recovery
18
+ - sam3d
19
+ short_description: "Image β†’ 3D Human Mesh (GLB) - MCP Server"
20
  ---
21
 
22
+ # 🧍 SAM 3D Body MCP Server
23
+
24
+ **Image β†’ 3D Human Mesh** powered by [Meta's SAM 3D Body](https://github.com/facebookresearch/sam-3d-body)
25
+
26
+ ## MCP Integration
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "sam3d": {
32
+ "command": "npx",
33
+ "args": ["mcp-remote", "https://dev-bjoern-sam3d-body-mcp.hf.space/gradio_api/mcp/sse"]
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ## Credits
40
+ - [facebook/sam-3d-body-dinov3](https://huggingface.co/facebook/sam-3d-body-dinov3)
41
+ - [rerun-io/sam3d-body-rerun](https://github.com/rerun-io/sam3d-body-rerun)
app.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SAM 3D Body MCP Server
3
+ Image β†’ 3D Human Mesh (GLB)
4
+ """
5
+ import os
6
+ import sys
7
+ import subprocess
8
+ import tempfile
9
+ import uuid
10
+ from pathlib import Path
11
+
12
+ import gradio as gr
13
+ import numpy as np
14
+ import spaces
15
+ import torch
16
+ import bpy
17
+ from huggingface_hub import snapshot_download
18
+ from PIL import Image
19
+
20
+ # Clone sam-3d-body repo if not exists
21
+ SAM3D_PATH = Path("/home/user/app/sam-3d-body")
22
+ if not SAM3D_PATH.exists():
23
+ print("Cloning sam-3d-body repository...")
24
+ subprocess.run([
25
+ "git", "clone",
26
+ "https://github.com/facebookresearch/sam-3d-body.git",
27
+ str(SAM3D_PATH)
28
+ ], check=True)
29
+ sys.path.insert(0, str(SAM3D_PATH))
30
+
31
+ # Add to path
32
+ sys.path.insert(0, str(SAM3D_PATH))
33
+
34
+ # Global model
35
+ MODEL = None
36
+ FACES = None
37
+
38
+ def load_model():
39
+ """Load SAM 3D Body model"""
40
+ global MODEL, FACES
41
+
42
+ if MODEL is not None:
43
+ return MODEL, FACES
44
+
45
+ print("Loading SAM 3D Body model...")
46
+
47
+ # Download checkpoint
48
+ checkpoint_dir = snapshot_download(
49
+ repo_id="facebook/sam-3d-body-dinov3",
50
+ token=os.environ.get("HF_TOKEN")
51
+ )
52
+
53
+ from sam_3d_body import load_sam_3d_body, SAM3DBodyEstimator
54
+
55
+ device = "cuda" if torch.cuda.is_available() else "cpu"
56
+
57
+ model, model_cfg = load_sam_3d_body(
58
+ checkpoint_path=f"{checkpoint_dir}/model.ckpt",
59
+ device=device,
60
+ mhr_path=f"{checkpoint_dir}/assets/mhr_model.pt"
61
+ )
62
+
63
+ MODEL = SAM3DBodyEstimator(
64
+ sam_3d_body_model=model,
65
+ model_cfg=model_cfg,
66
+ )
67
+ FACES = MODEL.faces
68
+
69
+ print("βœ“ Model loaded")
70
+ return MODEL, FACES
71
+
72
+
73
+ @spaces.GPU(duration=60)
74
+ def reconstruct_body(image: np.ndarray) -> tuple:
75
+ """
76
+ Reconstruct 3D body mesh from image.
77
+
78
+ Args:
79
+ image: Input RGB image
80
+
81
+ Returns:
82
+ tuple: (glb_path, status)
83
+ """
84
+ if image is None:
85
+ return None, "❌ No image provided"
86
+
87
+ try:
88
+ estimator, faces = load_model()
89
+
90
+ # Process image
91
+ if isinstance(image, Image.Image):
92
+ image = np.array(image)
93
+
94
+ # BGR for OpenCV
95
+ import cv2
96
+ img_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
97
+
98
+ outputs = estimator.process_one_image(img_bgr, bbox_thr=0.5)
99
+
100
+ if not outputs:
101
+ return None, "⚠️ No humans detected"
102
+
103
+ # Export first person as GLB via Blender
104
+ person = outputs[0]
105
+ vertices = person["pred_vertices"]
106
+
107
+ # Reset Blender scene
108
+ bpy.ops.wm.read_factory_settings(use_empty=True)
109
+
110
+ # Create mesh
111
+ mesh = bpy.data.meshes.new("body_mesh")
112
+ mesh.from_pydata(vertices.tolist(), [], faces.tolist())
113
+ mesh.update()
114
+
115
+ # Create object
116
+ obj = bpy.data.objects.new("body", mesh)
117
+ bpy.context.collection.objects.link(obj)
118
+ bpy.context.view_layer.objects.active = obj
119
+ obj.select_set(True)
120
+
121
+ # Smooth shading
122
+ for poly in mesh.polygons:
123
+ poly.use_smooth = True
124
+
125
+ # Save GLB
126
+ output_dir = tempfile.mkdtemp()
127
+ glb_path = f"{output_dir}/body_{uuid.uuid4().hex[:8]}.glb"
128
+ bpy.ops.export_scene.gltf(
129
+ filepath=glb_path,
130
+ export_format='GLB',
131
+ use_selection=True
132
+ )
133
+
134
+ return glb_path, f"βœ“ Reconstructed {len(outputs)} person(s)"
135
+
136
+ except Exception as e:
137
+ import traceback
138
+ traceback.print_exc()
139
+ return None, f"❌ Error: {e}"
140
+
141
+
142
+ # Gradio Interface
143
+ with gr.Blocks(title="SAM 3D Body MCP") as demo:
144
+ gr.Markdown("# 🧍 SAM 3D Body MCP Server\n**Image β†’ 3D Human Mesh (GLB)**")
145
+
146
+ with gr.Row():
147
+ with gr.Column():
148
+ input_image = gr.Image(label="Input Image", type="numpy")
149
+ btn = gr.Button("🎯 Reconstruct", variant="primary")
150
+
151
+ with gr.Column():
152
+ output_file = gr.File(label="3D Mesh (GLB)")
153
+ status = gr.Textbox(label="Status")
154
+
155
+ btn.click(reconstruct_body, inputs=[input_image], outputs=[output_file, status])
156
+
157
+ gr.Markdown("""
158
+ ---
159
+ ### MCP Server
160
+ ```json
161
+ {"mcpServers": {"sam3d": {"command": "npx", "args": ["mcp-remote", "URL/gradio_api/mcp/sse"]}}}
162
+ ```
163
+ """)
164
+
165
+
166
+ if __name__ == "__main__":
167
+ demo.launch(mcp_server=True)
requirements.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch>=2.2.0
2
+ torchvision>=0.17.0
3
+ gradio>=5.9.0
4
+ huggingface_hub>=0.26.0
5
+ spaces>=0.30.0
6
+ bpy>=4.2.0
7
+ numpy>=1.26.0
8
+ opencv-python>=4.8.0
9
+ Pillow>=10.0.0
10
+ einops>=0.7.0
11
+ timm>=0.9.0
12
+ pytorch-lightning>=2.0.0
13
+ roma>=1.5.0
14
+ yacs>=0.1.8
15
+ hydra-core>=1.3.0
16
+ pyrootutils>=1.0.4
17
+ networkx==3.2.1
18
+ loguru>=0.7.0
19
+ fvcore>=0.1.5
20
+ rich>=13.0.0