shaheerawan3 commited on
Commit
eafa497
·
verified ·
1 Parent(s): 3e29b78

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +165 -203
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import sys
3
  import logging
 
4
  import numpy as np
5
  import cv2
6
  import trimesh
@@ -9,64 +10,49 @@ import streamlit as st
9
  from pathlib import Path
10
  from scipy.spatial.transform import Rotation as R
11
 
12
- # Explicitly set up rendering backend
13
- # Add these lines before any pyrender imports
14
- os.environ['PYOPENGL_PLATFORM'] = 'egl'
15
- import OpenGL.GL as gl
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO)
19
 
20
- class AdvancedModelProcessor:
21
- """Advanced processor for 3D models with enhanced animation capabilities"""
22
 
23
  @staticmethod
24
- def detect_model_type(file_path):
25
- """Detect the type of 3D model"""
26
- file_ext = Path(file_path).suffix.lower()
27
- rigged_extensions = ['.fbx', '.dae', '.blend']
28
- static_extensions = ['.obj', '.stl', '.ply', '.glb', '.gltf']
29
-
30
- if file_ext in rigged_extensions:
31
- return 'rigged'
32
- elif file_ext in static_extensions:
33
- return 'static'
34
- else:
35
- return 'unknown'
36
-
37
- @staticmethod
38
- def analyze_model(file_path):
39
- """Analyze the 3D model's characteristics"""
40
  try:
41
- # Load the mesh
42
- mesh = trimesh.load(file_path)
 
 
43
 
44
- # Ensure we have a trimesh object
45
- if isinstance(mesh, trimesh.Scene):
46
- # If it's a scene, combine meshes
47
- mesh = trimesh.util.concatenate([
48
- trimesh.Trimesh(vertices=geom.vertices, faces=geom.faces)
49
- for geom in mesh.geometry.values()
50
- ])
51
 
52
- # Basic model information
53
- info = {
54
- 'vertices': len(mesh.vertices),
55
- 'faces': len(mesh.faces),
56
- 'is_watertight': mesh.is_watertight,
57
- 'volume': mesh.volume,
58
- 'centroid': mesh.centroid.tolist() if hasattr(mesh, 'centroid') else None,
59
- 'bounding_box': mesh.bounding_box.extents.tolist() if hasattr(mesh, 'bounding_box') else None
60
- }
61
 
62
- return info
63
  except Exception as e:
64
- logging.error(f"Model analysis error: {str(e)}")
65
- return None
66
-
67
  @staticmethod
68
  def prepare_model(mesh):
69
- """Prepare model for rendering by centering and scaling"""
70
  # Ensure we have a trimesh object
71
  if isinstance(mesh, trimesh.Scene):
72
  # Combine meshes if it's a scene
@@ -84,150 +70,126 @@ class AdvancedModelProcessor:
84
 
85
  return mesh
86
 
87
- class AnimationPatterns:
88
- """Provides different animation patterns"""
89
 
90
  @staticmethod
91
- def rotate_y(frame_num, total_frames):
92
- """Rotate around Y-axis"""
93
- angle = (frame_num / total_frames) * 2 * np.pi
94
- rotation = R.from_rotvec(angle * np.array([0, 1, 0])).as_matrix()
95
- transform = np.eye(4)
96
- transform[:3, :3] = rotation
97
- return transform
98
-
99
- def create_pyrender_scene(mesh):
100
- """Create a pyrender scene with the model"""
101
- try:
102
- # Create scene with advanced lighting
103
- scene = pyrender.Scene(
104
- ambient_light=np.array([0.4, 0.4, 0.4, 1.0]),
105
- bg_color=np.array([0.1, 0.1, 0.1, 1.0])
106
- )
107
-
108
- # Convert trimesh to pyrender mesh
109
- py_mesh = pyrender.Mesh.from_trimesh(mesh, smooth=True)
110
-
111
- # Add mesh to scene
112
- scene.add(py_mesh)
113
-
114
- # Advanced camera setup
115
- camera = pyrender.PerspectiveCamera(
116
- yfov=np.pi / 3.0,
117
- aspectRatio=1.0,
118
- znear=0.1,
119
- zfar=10.0
120
- )
121
-
122
- # Camera positioning
123
- camera_pose = np.array([
124
- [1.0, 0.0, 0.0, 0.0],
125
- [0.0, 1.0, 0.0, 0.0],
126
- [0.0, 0.0, 1.0, 2.5], # Adjusted camera distance
127
- [0.0, 0.0, 0.0, 1.0]
128
- ])
129
- scene.add(camera, pose=camera_pose)
130
-
131
- # Enhanced lighting setup
132
- directional_light = pyrender.DirectionalLight(
133
- color=np.array([1.0, 1.0, 1.0]),
134
- intensity=2.0
135
- )
136
- scene.add(directional_light, pose=camera_pose)
137
-
138
- return scene
139
- except Exception as e:
140
- logging.error(f"Scene creation error: {str(e)}")
141
- raise
142
-
143
- def render_frame(scene, width=800, height=600):
144
- """Render a high-quality frame"""
145
- try:
146
- # Create renderer with EGL backend
147
- renderer = pyrender.OffscreenRenderer(
148
- viewport_width=width,
149
- viewport_height=height,
150
- point_size=1.0
151
- )
152
-
153
- # Render the scene
154
- color, _ = renderer.render(scene, flags=pyrender.RenderFlags.RGBA)
155
- renderer.delete()
156
- return color
157
- except Exception as e:
158
- logging.error(f"Frame rendering error: {str(e)}")
159
- # Print more detailed error information
160
- logging.error(f"Detailed error: {sys.exc_info()}")
161
- raise
162
 
163
- def generate_animation(model_path, settings):
164
- """Generate an animation video"""
165
- try:
166
- # Load and prepare the model
167
- model_processor = AdvancedModelProcessor()
168
-
169
- # Load the mesh
170
- mesh = trimesh.load(model_path)
171
-
172
- # Prepare the mesh
173
- prepared_mesh = model_processor.prepare_model(mesh)
174
-
175
- # Ensure output directory exists
176
- os.makedirs('output', exist_ok=True)
177
-
178
- # Prepare video writer
179
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
180
- out = cv2.VideoWriter('output/animation.mp4',
181
- fourcc,
182
- settings['fps'],
183
- (settings['width'], settings['height']))
184
-
185
- # Animation generation
186
- total_frames = settings['duration'] * settings['fps']
187
- progress_bar = st.progress(0)
188
- status_text = st.empty()
189
-
190
- for frame_num in range(total_frames):
191
- # Get transformation
192
- transform = AnimationPatterns.rotate_y(frame_num, total_frames)
193
 
194
- # Create scene
195
- scene = create_pyrender_scene(prepared_mesh)
196
 
197
- # Render frame
198
- frame = render_frame(scene, settings['width'], settings['height'])
 
 
 
 
 
199
 
200
- # Convert and write frame
201
- processed_frame = cv2.cvtColor(frame, cv2.COLOR_RGBA2BGR)
202
- out.write(processed_frame)
 
203
 
204
- # Update progress
205
- progress = (frame_num + 1) / total_frames
206
- progress_bar.progress(progress)
207
- status_text.text(f"Generating frame {frame_num + 1}/{total_frames}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
- out.release()
210
- status_text.text("Animation complete!")
211
- return 'output/animation.mp4'
212
-
213
- except Exception as e:
214
- logging.error(f"Animation generation error: {str(e)}")
215
- # Print more detailed error information
216
- logging.error(f"Detailed error: {sys.exc_info()}")
217
- raise
218
 
219
  def main():
220
  st.set_page_config(page_title="3D Model Animation Generator", layout="wide")
221
  st.title("3D Model Animation Generator")
222
 
223
- # Create necessary directories
224
- for dir_name in ['temp', 'output']:
225
- os.makedirs(dir_name, exist_ok=True)
226
-
227
  # File upload section
 
228
  uploaded_file = st.file_uploader(
229
- "Upload 3D Model",
230
- type=['obj', 'stl', 'ply', 'fbx', 'glb', 'gltf', 'dae']
 
231
  )
232
 
233
  # Animation settings
@@ -242,42 +204,42 @@ def main():
242
  # Generate animation button
243
  if st.sidebar.button("Generate Animation"):
244
  if uploaded_file is not None:
245
- # Save uploaded file
246
- file_extension = Path(uploaded_file.name).suffix
247
- model_path = f"temp/model{file_extension}"
248
-
249
- with open(model_path, "wb") as f:
250
- f.write(uploaded_file.getbuffer())
251
-
252
- # Generate animation
253
  try:
 
 
 
 
 
 
 
 
 
 
 
 
254
  with st.spinner("Generating Animation..."):
255
- video_path = generate_animation(model_path, settings)
256
-
257
- # Display and download video
258
- with open(video_path, 'rb') as f:
259
- video_bytes = f.read()
260
-
261
- st.video(video_bytes)
262
- st.download_button(
263
- label="Download Animation",
264
- data=video_bytes,
265
- file_name="3d_model_animation.mp4",
266
- mime="video/mp4"
267
- )
268
 
269
- # Show model analysis
270
- model_processor = AdvancedModelProcessor()
271
- model_info = model_processor.analyze_model(model_path)
272
 
273
- st.subheader("Model Analysis")
274
- st.json(model_info)
 
 
 
 
 
 
 
 
 
 
275
 
276
  except Exception as e:
277
- st.error(f"Animation generation error: {str(e)}")
278
- logging.error(f"Animation generation error: {str(e)}")
279
  else:
280
- st.error("Please upload a 3D model")
281
 
282
  if __name__ == "__main__":
283
  main()
 
1
  import os
2
  import sys
3
  import logging
4
+ import tempfile
5
  import numpy as np
6
  import cv2
7
  import trimesh
 
10
  from pathlib import Path
11
  from scipy.spatial.transform import Rotation as R
12
 
13
+ # Ensure temporary directories
14
+ os.makedirs('temp', exist_ok=True)
15
+ os.makedirs('output', exist_ok=True)
 
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO)
19
 
20
+ class ModelProcessor:
21
+ """Comprehensive model processing utilities"""
22
 
23
  @staticmethod
24
+ def validate_model(file_path):
25
+ """Validate the uploaded 3D model file"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  try:
27
+ # Check file size
28
+ file_size = os.path.getsize(file_path)
29
+ if file_size == 0:
30
+ raise ValueError("Empty file uploaded")
31
 
32
+ # Try loading the model
33
+ try:
34
+ mesh = trimesh.load(file_path)
35
+ except Exception as e:
36
+ raise ValueError(f"Invalid 3D model file: {str(e)}")
 
 
37
 
38
+ # Check mesh validity
39
+ if isinstance(mesh, trimesh.Scene):
40
+ # For scene, ensure it has geometry
41
+ if not mesh.geometry:
42
+ raise ValueError("Model scene contains no valid geometry")
43
+ else:
44
+ # For direct mesh, check vertices and faces
45
+ if len(mesh.vertices) == 0 or len(mesh.faces) == 0:
46
+ raise ValueError("Model has no vertices or faces")
47
 
48
+ return True
49
  except Exception as e:
50
+ st.error(f"Model validation error: {str(e)}")
51
+ return False
52
+
53
  @staticmethod
54
  def prepare_model(mesh):
55
+ """Prepare model for rendering"""
56
  # Ensure we have a trimesh object
57
  if isinstance(mesh, trimesh.Scene):
58
  # Combine meshes if it's a scene
 
70
 
71
  return mesh
72
 
73
+ class AnimationGenerator:
74
+ """Handles 3D model animation generation"""
75
 
76
  @staticmethod
77
+ def create_scene(mesh):
78
+ """Create a pyrender scene with the model"""
79
+ try:
80
+ # Create scene with advanced lighting
81
+ scene = pyrender.Scene(
82
+ ambient_light=np.array([0.4, 0.4, 0.4, 1.0]),
83
+ bg_color=np.array([0.1, 0.1, 0.1, 1.0])
84
+ )
85
+
86
+ # Convert trimesh to pyrender mesh
87
+ py_mesh = pyrender.Mesh.from_trimesh(mesh, smooth=True)
88
+
89
+ # Add mesh to scene
90
+ scene.add(py_mesh)
91
+
92
+ # Camera setup
93
+ camera = pyrender.PerspectiveCamera(
94
+ yfov=np.pi / 3.0,
95
+ aspectRatio=1.0,
96
+ znear=0.1,
97
+ zfar=10.0
98
+ )
99
+
100
+ # Camera positioning
101
+ camera_pose = np.array([
102
+ [1.0, 0.0, 0.0, 0.0],
103
+ [0.0, 1.0, 0.0, 0.0],
104
+ [0.0, 0.0, 1.0, 2.5],
105
+ [0.0, 0.0, 0.0, 1.0]
106
+ ])
107
+ scene.add(camera, pose=camera_pose)
108
+
109
+ # Lighting
110
+ directional_light = pyrender.DirectionalLight(
111
+ color=np.array([1.0, 1.0, 1.0]),
112
+ intensity=2.0
113
+ )
114
+ scene.add(directional_light, pose=camera_pose)
115
+
116
+ return scene
117
+ except Exception as e:
118
+ st.error(f"Scene creation error: {str(e)}")
119
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
+ @staticmethod
122
+ def generate_animation(model_path, settings):
123
+ """Generate animation from 3D model"""
124
+ try:
125
+ # Load the mesh
126
+ mesh = trimesh.load(model_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ # Prepare the mesh
129
+ prepared_mesh = ModelProcessor.prepare_model(mesh)
130
 
131
+ # Prepare video writer
132
+ output_path = 'output/3d_model_animation.mp4'
133
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
134
+ out = cv2.VideoWriter(output_path,
135
+ fourcc,
136
+ settings['fps'],
137
+ (settings['width'], settings['height']))
138
 
139
+ # Animation generation
140
+ total_frames = settings['duration'] * settings['fps']
141
+ progress_bar = st.progress(0)
142
+ status_text = st.empty()
143
 
144
+ for frame_num in range(total_frames):
145
+ # Rotation transformation
146
+ angle = (frame_num / total_frames) * 2 * np.pi
147
+ rotation = R.from_rotvec(angle * np.array([0, 1, 0])).as_matrix()
148
+ transform = np.eye(4)
149
+ transform[:3, :3] = rotation
150
+
151
+ # Create scene
152
+ scene = AnimationGenerator.create_scene(prepared_mesh)
153
+
154
+ if scene is None:
155
+ raise ValueError("Failed to create rendering scene")
156
+
157
+ # Render frame
158
+ renderer = pyrender.OffscreenRenderer(
159
+ viewport_width=settings['width'],
160
+ viewport_height=settings['height']
161
+ )
162
+ color, _ = renderer.render(scene)
163
+ renderer.delete()
164
+
165
+ # Convert and write frame
166
+ processed_frame = cv2.cvtColor(color, cv2.COLOR_RGBA2BGR)
167
+ out.write(processed_frame)
168
+
169
+ # Update progress
170
+ progress = (frame_num + 1) / total_frames
171
+ progress_bar.progress(progress)
172
+ status_text.text(f"Generating frame {frame_num + 1}/{total_frames}")
173
+
174
+ out.release()
175
+ status_text.text("Animation complete!")
176
+ return output_path
177
 
178
+ except Exception as e:
179
+ st.error(f"Animation generation error: {str(e)}")
180
+ logging.error(f"Detailed error: {traceback.format_exc()}")
181
+ return None
 
 
 
 
 
182
 
183
  def main():
184
  st.set_page_config(page_title="3D Model Animation Generator", layout="wide")
185
  st.title("3D Model Animation Generator")
186
 
 
 
 
 
187
  # File upload section
188
+ st.write("Upload a 3D Model (obj, stl, ply, fbx, glb, gltf, dae)")
189
  uploaded_file = st.file_uploader(
190
+ "Choose a file",
191
+ type=['obj', 'stl', 'ply', 'fbx', 'glb', 'gltf', 'dae'],
192
+ accept_multiple_files=False
193
  )
194
 
195
  # Animation settings
 
204
  # Generate animation button
205
  if st.sidebar.button("Generate Animation"):
206
  if uploaded_file is not None:
 
 
 
 
 
 
 
 
207
  try:
208
+ # Create a temporary file
209
+ with tempfile.NamedTemporaryFile(delete=False, suffix=Path(uploaded_file.name).suffix) as temp_file:
210
+ temp_file.write(uploaded_file.getvalue())
211
+ temp_file_path = temp_file.name
212
+
213
+ # Validate the model
214
+ if not ModelProcessor.validate_model(temp_file_path):
215
+ st.error("Invalid model file. Please check the file and try again.")
216
+ os.unlink(temp_file_path)
217
+ return
218
+
219
+ # Generate animation
220
  with st.spinner("Generating Animation..."):
221
+ video_path = AnimationGenerator.generate_animation(temp_file_path, settings)
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
+ # Clean up temporary file
224
+ os.unlink(temp_file_path)
 
225
 
226
+ if video_path and os.path.exists(video_path):
227
+ # Display and download video
228
+ with open(video_path, 'rb') as f:
229
+ video_bytes = f.read()
230
+
231
+ st.video(video_bytes)
232
+ st.download_button(
233
+ label="Download Animation",
234
+ data=video_bytes,
235
+ file_name="3d_model_animation.mp4",
236
+ mime="video/mp4"
237
+ )
238
 
239
  except Exception as e:
240
+ st.error(f"Error processing file: {str(e)}")
 
241
  else:
242
+ st.warning("Please upload a 3D model file")
243
 
244
  if __name__ == "__main__":
245
  main()