dev-bjoern commited on
Commit
30aba9f
Β·
0 Parent(s):

Initial SAM 3D Objects MCP server

Browse files
Files changed (3) hide show
  1. README.md +42 -0
  2. app.py +148 -0
  3. requirements.txt +16 -0
README.md ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: SAM 3D Objects MCP
3
+ emoji: πŸ“¦
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 6.0.2
8
+ python_version: "3.10"
9
+ app_file: app.py
10
+ pinned: false
11
+ license: other
12
+ hardware: zero-a10g
13
+ tags:
14
+ - mcp-server-track
15
+ - building-mcp-track-consumer
16
+ - agents
17
+ - 3d
18
+ - object-reconstruction
19
+ - sam3d
20
+ short_description: "Image + Mask β†’ 3D Object (PLY) - MCP Server"
21
+ ---
22
+
23
+ # πŸ“¦ SAM 3D Objects MCP Server
24
+
25
+ **Image + Mask β†’ 3D Object** powered by [Meta's SAM 3D Objects](https://github.com/facebookresearch/sam-3d-objects)
26
+
27
+ ## MCP Integration
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "sam3d-objects": {
33
+ "command": "npx",
34
+ "args": ["mcp-remote", "https://dev-bjoern-sam3d-objects-mcp.hf.space/gradio_api/mcp/sse"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Credits
41
+ - [facebook/sam-3d-objects](https://huggingface.co/facebook/sam-3d-objects)
42
+ - [facebookresearch/sam-3d-objects](https://github.com/facebookresearch/sam-3d-objects)
app.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SAM 3D Objects MCP Server
3
+ Image + Mask β†’ 3D Object (PLY)
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
+ from huggingface_hub import snapshot_download, login
16
+ from PIL import Image
17
+
18
+ # Login with HF_TOKEN if available
19
+ if os.environ.get("HF_TOKEN"):
20
+ login(token=os.environ.get("HF_TOKEN"))
21
+
22
+ # Clone sam-3d-objects repo if not exists
23
+ SAM3D_PATH = Path("/home/user/app/sam-3d-objects")
24
+ if not SAM3D_PATH.exists():
25
+ print("Cloning sam-3d-objects repository...")
26
+ subprocess.run([
27
+ "git", "clone",
28
+ "https://github.com/facebookresearch/sam-3d-objects.git",
29
+ str(SAM3D_PATH)
30
+ ], check=True)
31
+ sys.path.insert(0, str(SAM3D_PATH))
32
+
33
+ # Add to path
34
+ sys.path.insert(0, str(SAM3D_PATH))
35
+
36
+ # Global model
37
+ MODEL = None
38
+
39
+
40
+ def load_model():
41
+ """Load SAM 3D Objects model"""
42
+ global MODEL
43
+
44
+ if MODEL is not None:
45
+ return MODEL
46
+
47
+ import torch
48
+ print("Loading SAM 3D Objects model...")
49
+
50
+ # Download checkpoint
51
+ checkpoint_dir = snapshot_download(
52
+ repo_id="facebook/sam-3d-objects",
53
+ token=os.environ.get("HF_TOKEN")
54
+ )
55
+
56
+ from sam_3d_objects import Sam3dObjects
57
+
58
+ device = "cuda" if torch.cuda.is_available() else "cpu"
59
+
60
+ MODEL = Sam3dObjects.from_pretrained(checkpoint_dir, device=device)
61
+
62
+ print("βœ“ Model loaded")
63
+ return MODEL
64
+
65
+
66
+ @spaces.GPU(duration=120)
67
+ def reconstruct_object(image: np.ndarray, mask: np.ndarray) -> tuple:
68
+ """
69
+ Reconstruct 3D object from image and mask.
70
+
71
+ Args:
72
+ image: Input RGB image
73
+ mask: Binary mask indicating object region
74
+
75
+ Returns:
76
+ tuple: (ply_path, status)
77
+ """
78
+ if image is None:
79
+ return None, "❌ No image provided"
80
+ if mask is None:
81
+ return None, "❌ No mask provided"
82
+
83
+ try:
84
+ import torch
85
+ import trimesh
86
+ model = load_model()
87
+
88
+ # Process image
89
+ if isinstance(image, Image.Image):
90
+ image = np.array(image)
91
+
92
+ # Process mask
93
+ if isinstance(mask, Image.Image):
94
+ mask = np.array(mask)
95
+
96
+ # Convert mask to binary if needed
97
+ if len(mask.shape) == 3:
98
+ mask = mask[:, :, 0]
99
+ mask = (mask > 127).astype(np.uint8)
100
+
101
+ # Run inference
102
+ outputs = model.predict(image, mask)
103
+
104
+ if outputs is None:
105
+ return None, "⚠️ Reconstruction failed"
106
+
107
+ # Export as PLY
108
+ output_dir = tempfile.mkdtemp()
109
+ ply_path = f"{output_dir}/object_{uuid.uuid4().hex[:8]}.ply"
110
+
111
+ # Save gaussian splat as PLY
112
+ outputs.save_ply(ply_path)
113
+
114
+ return ply_path, "βœ“ Object reconstructed"
115
+
116
+ except Exception as e:
117
+ import traceback
118
+ traceback.print_exc()
119
+ return None, f"❌ Error: {e}"
120
+
121
+
122
+ # Gradio Interface
123
+ with gr.Blocks(title="SAM 3D Objects MCP") as demo:
124
+ gr.Markdown("# πŸ“¦ SAM 3D Objects MCP Server\n**Image + Mask β†’ 3D Object (PLY)**")
125
+
126
+ with gr.Row():
127
+ with gr.Column():
128
+ input_image = gr.Image(label="Input Image", type="numpy")
129
+ input_mask = gr.Image(label="Object Mask", type="numpy")
130
+ btn = gr.Button("🎯 Reconstruct", variant="primary")
131
+
132
+ with gr.Column():
133
+ output_file = gr.File(label="3D Object (PLY)")
134
+ status = gr.Textbox(label="Status")
135
+
136
+ btn.click(reconstruct_object, inputs=[input_image, input_mask], outputs=[output_file, status])
137
+
138
+ gr.Markdown("""
139
+ ---
140
+ ### MCP Server
141
+ ```json
142
+ {"mcpServers": {"sam3d-objects": {"command": "npx", "args": ["mcp-remote", "URL/gradio_api/mcp/sse"]}}}
143
+ ```
144
+ """)
145
+
146
+
147
+ if __name__ == "__main__":
148
+ demo.launch(mcp_server=True)
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch>=2.5.0
2
+ torchvision>=0.20.0
3
+ gradio>=6.0.2
4
+ huggingface_hub>=0.26.0
5
+ spaces>=0.30.0
6
+ kaolin==0.17.0
7
+ gsplat @ git+https://github.com/nerfstudio-project/gsplat.git@2323de5905d5e90e035f792fe65bad0fedd413e7
8
+ seaborn==0.13.2
9
+ numpy>=1.26.0
10
+ opencv-python>=4.8.0
11
+ Pillow>=10.0.0
12
+ trimesh>=4.0.0
13
+ hydra-core>=1.3.0
14
+ omegaconf>=2.3.0
15
+ einops>=0.7.0
16
+ loguru>=0.7.0