dev-bjoern commited on
Commit
8906f9a
Β·
1 Parent(s): db71401

Fix: use generate_bpy_script and execute_bpy_script, remove PIL dependency

Browse files
Files changed (1) hide show
  1. app.py +47 -51
app.py CHANGED
@@ -59,16 +59,21 @@ def analyze_image(image: np.ndarray, prompt: str = "Describe this image for 3D s
59
  try:
60
  pipe = load_smolvlm()
61
 
62
- # Convert to PIL
63
- if isinstance(image, np.ndarray):
64
- pil_image = Image.fromarray(image)
65
- else:
66
- pil_image = image
 
 
 
 
 
67
 
68
  # Generate description
69
  result = pipe.generate(
70
  prompt,
71
- image=pil_image,
72
  max_new_tokens=256
73
  )
74
 
@@ -77,21 +82,31 @@ def analyze_image(image: np.ndarray, prompt: str = "Describe this image for 3D s
77
  return f"Error: {e}"
78
 
79
 
80
- def generate_scene_description(scene_type: str, style: str = "realistic") -> str:
81
- """Generate scene description with SmolLM3"""
82
  try:
83
  pipe = load_smollm3()
84
 
85
- prompt = f"Create a detailed description for a {style} {scene_type} 3D scene. Include objects, materials, lighting, and camera position."
 
 
 
 
 
 
 
 
 
 
86
 
87
  result = pipe.generate(
88
  prompt,
89
- max_new_tokens=512
90
  )
91
 
92
  return result
93
  except Exception as e:
94
- return f"Error: {e}"
95
 
96
 
97
  def create_primitive(primitive_type: str, name: str = "Object", location: tuple = (0, 0, 0)) -> str:
@@ -137,44 +152,25 @@ def create_primitive(primitive_type: str, name: str = "Object", location: tuple
137
  return f"Error: {e}"
138
 
139
 
140
- def create_scene_from_description(description: str) -> str:
141
- """Create a simple scene based on AI description"""
142
  try:
143
- # Clear scene
144
- bpy.ops.object.select_all(action='SELECT')
145
- bpy.ops.object.delete()
 
 
 
 
 
146
 
147
- # Parse description for keywords and create objects
148
- desc_lower = description.lower()
149
 
150
- # Add ground plane
151
- bpy.ops.mesh.primitive_plane_add(size=10, location=(0, 0, 0))
152
- ground = bpy.context.active_object
153
- ground.name = "Ground"
154
 
155
- # Add objects based on keywords
156
- if "cube" in desc_lower or "box" in desc_lower:
157
- bpy.ops.mesh.primitive_cube_add(location=(0, 0, 1))
158
- bpy.context.active_object.name = "Cube"
159
-
160
- if "sphere" in desc_lower or "ball" in desc_lower:
161
- bpy.ops.mesh.primitive_uv_sphere_add(location=(2, 0, 1))
162
- bpy.context.active_object.name = "Sphere"
163
-
164
- if "cylinder" in desc_lower or "pillar" in desc_lower:
165
- bpy.ops.mesh.primitive_cylinder_add(location=(-2, 0, 1))
166
- bpy.context.active_object.name = "Cylinder"
167
-
168
- # Add camera
169
- bpy.ops.object.camera_add(location=(7, -7, 5))
170
- camera = bpy.context.active_object
171
- camera.rotation_euler = (1.1, 0, 0.8)
172
- bpy.context.scene.camera = camera
173
-
174
- # Add light
175
- bpy.ops.object.light_add(type='SUN', location=(5, 5, 10))
176
-
177
- # Export
178
  output_dir = tempfile.mkdtemp()
179
  glb_path = f"{output_dir}/scene_{uuid.uuid4().hex[:8]}.glb"
180
 
@@ -185,7 +181,7 @@ def create_scene_from_description(description: str) -> str:
185
 
186
  return glb_path
187
  except Exception as e:
188
- return f"Error: {e}"
189
 
190
 
191
  # Gradio Interface
@@ -224,17 +220,17 @@ with gr.Blocks(title="BPY MCP") as demo:
224
  label="Style",
225
  value="realistic"
226
  )
227
- scene_btn = gr.Button("πŸ“ Generate Description", variant="primary")
228
  with gr.Column():
229
- scene_desc = gr.Textbox(label="Scene Description", lines=10)
230
 
231
- scene_btn.click(generate_scene_description, [scene_type, scene_style], scene_desc)
232
 
233
  with gr.Row():
234
- create_btn = gr.Button("🎬 Create 3D Scene from Description", variant="secondary")
235
  scene_model = gr.Model3D(label="3D Scene")
236
 
237
- create_btn.click(create_scene_from_description, scene_desc, scene_model)
238
 
239
  with gr.Tab("Primitives"):
240
  with gr.Row():
 
59
  try:
60
  pipe = load_smolvlm()
61
 
62
+ # Save image temporarily using bpy
63
+ temp_path = f"{tempfile.mkdtemp()}/input_{uuid.uuid4().hex[:8]}.png"
64
+
65
+ # Use bpy to save image
66
+ img = bpy.data.images.new("temp_input", width=image.shape[1], height=image.shape[0])
67
+ img.pixels = (image / 255.0).flatten().tolist()
68
+ img.filepath_raw = temp_path
69
+ img.file_format = 'PNG'
70
+ img.save()
71
+ bpy.data.images.remove(img)
72
 
73
  # Generate description
74
  result = pipe.generate(
75
  prompt,
76
+ image=temp_path,
77
  max_new_tokens=256
78
  )
79
 
 
82
  return f"Error: {e}"
83
 
84
 
85
+ def generate_bpy_script(scene_type: str, style: str = "realistic") -> str:
86
+ """Generate a bpy Python script with SmolLM3"""
87
  try:
88
  pipe = load_smollm3()
89
 
90
+ prompt = f"""Write a Python script using bpy (Blender Python API) to create a {style} {scene_type} 3D scene.
91
+
92
+ The script must:
93
+ 1. Clear existing objects with bpy.ops.object.select_all(action='SELECT') and bpy.ops.object.delete()
94
+ 2. Create mesh objects using bpy.ops.mesh.primitive_* functions
95
+ 3. Position objects with location parameter
96
+ 4. Add a camera with bpy.ops.object.camera_add()
97
+ 5. Add lighting with bpy.ops.object.light_add()
98
+ 6. Set materials using bpy.data.materials.new()
99
+
100
+ Only output valid Python code, no explanations. Start with import bpy."""
101
 
102
  result = pipe.generate(
103
  prompt,
104
+ max_new_tokens=1024
105
  )
106
 
107
  return result
108
  except Exception as e:
109
+ return f"# Error: {e}"
110
 
111
 
112
  def create_primitive(primitive_type: str, name: str = "Object", location: tuple = (0, 0, 0)) -> str:
 
152
  return f"Error: {e}"
153
 
154
 
155
+ def execute_bpy_script(script: str) -> str:
156
+ """Execute a bpy script and export to GLB"""
157
  try:
158
+ # Extract just the Python code (remove markdown if present)
159
+ code = script
160
+ if "```python" in code:
161
+ code = code.split("```python")[1].split("```")[0]
162
+ elif "```" in code:
163
+ parts = code.split("```")
164
+ if len(parts) > 1:
165
+ code = parts[1]
166
 
167
+ # Remove import bpy if present (already imported)
168
+ code = code.replace("import bpy", "# import bpy (already loaded)")
169
 
170
+ # Execute the script
171
+ exec(code, {"bpy": bpy, "math": __import__("math")})
 
 
172
 
173
+ # Export to GLB
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  output_dir = tempfile.mkdtemp()
175
  glb_path = f"{output_dir}/scene_{uuid.uuid4().hex[:8]}.glb"
176
 
 
181
 
182
  return glb_path
183
  except Exception as e:
184
+ return f"Error executing script: {e}"
185
 
186
 
187
  # Gradio Interface
 
220
  label="Style",
221
  value="realistic"
222
  )
223
+ scene_btn = gr.Button("πŸ“ Generate BPY Script", variant="primary")
224
  with gr.Column():
225
+ scene_desc = gr.Textbox(label="Generated BPY Script", lines=10)
226
 
227
+ scene_btn.click(generate_bpy_script, [scene_type, scene_style], scene_desc)
228
 
229
  with gr.Row():
230
+ create_btn = gr.Button("🎬 Execute Script & Create 3D", variant="secondary")
231
  scene_model = gr.Model3D(label="3D Scene")
232
 
233
+ create_btn.click(execute_bpy_script, scene_desc, scene_model)
234
 
235
  with gr.Tab("Primitives"):
236
  with gr.Row():