A19grey commited on
Commit
f9c3dad
1 Parent(s): 8eda443

add open3d and let user set parameters for 3D model

Browse files
Files changed (2) hide show
  1. app.py +61 -22
  2. requirements.txt +2 -1
app.py CHANGED
@@ -54,7 +54,7 @@ def resize_image(image_path, max_size=1024):
54
  img.save(temp_file, format="PNG")
55
  return temp_file.name
56
 
57
- def generate_3d_model(depth, image_path, focallength_px):
58
  """
59
  Generate a textured 3D mesh from the depth map and the original image.
60
  """
@@ -112,7 +112,7 @@ def generate_3d_model(depth, image_path, focallength_px):
112
  print("Original mesh - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
113
 
114
  # 1. Mesh simplification
115
- target_faces = int(len(mesh.faces) * 0.8) # Simplify to 80% of original faces
116
  mesh = mesh.simplify_quadric_decimation(face_count=target_faces)
117
  print("After simplification - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
118
 
@@ -124,11 +124,12 @@ def generate_3d_model(depth, image_path, focallength_px):
124
  print("After removing small components - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
125
 
126
  # 3. Smooth the mesh
127
- mesh = mesh.smoothed()
 
128
  print("After smoothing - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
129
 
130
  # 4. Remove thin features
131
- mesh = remove_thin_features(mesh)
132
  print("After removing thin features - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
133
 
134
  # Export the mesh to OBJ files with unique filenames
@@ -163,6 +164,18 @@ def remove_thin_features(mesh, thickness_threshold=0.01):
163
 
164
  return mesh
165
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  @spaces.GPU(duration=20)
167
  def predict_depth(input_image):
168
  temp_file = None
@@ -222,13 +235,13 @@ def predict_depth(input_image):
222
  # Generate the 3D model from the depth map and resized image
223
  view_model_path, download_model_path = generate_3d_model(depth, temp_file, focallength_px)
224
 
225
- return output_path, f"Focal length: {focallength_px:.2f} pixels", raw_depth_path, view_model_path, download_model_path
226
  except Exception as e:
227
  # Return error messages in case of failures
228
  import traceback
229
  error_message = f"An error occurred: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"
230
  print(error_message) # Print the full error message to the console
231
- return None, error_message, None, None, None
232
  finally:
233
  # Clean up by removing the temporary resized image file
234
  if temp_file and os.path.exists(temp_file):
@@ -245,18 +258,9 @@ def get_last_commit_timestamp():
245
  # Create the Gradio interface with appropriate input and output components.
246
  last_updated = get_last_commit_timestamp()
247
 
248
- iface = gr.Interface(
249
- fn=predict_depth,
250
- inputs=gr.Image(type="filepath"),
251
- outputs=[
252
- gr.Image(type="filepath", label="Depth Map"),
253
- gr.Textbox(label="Focal Length or Error Message"),
254
- gr.File(label="Download Raw Depth Map (CSV)"),
255
- gr.Model3D(label="View 3D Model"),
256
- gr.File(label="Download 3D Model (OBJ)")
257
- ],
258
- title="DepthPro Demo with 3D Visualization",
259
- description=(
260
  "An enhanced demo that creates a textured 3D model from the input image and depth map.\n\n"
261
  "Forked from https://huggingface.co/spaces/akhaliq/depth-pro and model from https://huggingface.co/apple/DepthPro\n"
262
  "**Instructions:**\n"
@@ -264,10 +268,45 @@ iface = gr.Interface(
264
  "2. The app will predict the depth map, display it, and provide the focal length.\n"
265
  "3. Download the raw depth data as a CSV file.\n"
266
  "4. View the generated 3D model textured with the original image.\n"
267
- "5. Download the 3D model as an OBJ file if desired.\n\n"
 
268
  f"Last updated: {last_updated}"
269
- ),
270
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
  # Launch the Gradio interface with sharing enabled
273
- iface.launch(share=True) # share=True allows you to share the interface with others.
 
54
  img.save(temp_file, format="PNG")
55
  return temp_file.name
56
 
57
+ def generate_3d_model(depth, image_path, focallength_px, simplification_factor=0.8, smoothing_iterations=1, thin_threshold=0.01):
58
  """
59
  Generate a textured 3D mesh from the depth map and the original image.
60
  """
 
112
  print("Original mesh - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
113
 
114
  # 1. Mesh simplification
115
+ target_faces = int(len(mesh.faces) * simplification_factor)
116
  mesh = mesh.simplify_quadric_decimation(face_count=target_faces)
117
  print("After simplification - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
118
 
 
124
  print("After removing small components - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
125
 
126
  # 3. Smooth the mesh
127
+ for _ in range(smoothing_iterations):
128
+ mesh = mesh.smoothed()
129
  print("After smoothing - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
130
 
131
  # 4. Remove thin features
132
+ mesh = remove_thin_features(mesh, thickness_threshold=thin_threshold)
133
  print("After removing thin features - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
134
 
135
  # Export the mesh to OBJ files with unique filenames
 
164
 
165
  return mesh
166
 
167
+ def regenerate_3d_model(depth_csv, image_path, focallength_px, simplification_factor, smoothing_iterations, thin_threshold):
168
+ # Load depth from CSV
169
+ depth = np.loadtxt(depth_csv, delimiter=',')
170
+
171
+ # Generate new 3D model with updated parameters
172
+ view_model_path, download_model_path = generate_3d_model(
173
+ depth, image_path, focallength_px,
174
+ simplification_factor, smoothing_iterations, thin_threshold
175
+ )
176
+
177
+ return view_model_path, download_model_path
178
+
179
  @spaces.GPU(duration=20)
180
  def predict_depth(input_image):
181
  temp_file = None
 
235
  # Generate the 3D model from the depth map and resized image
236
  view_model_path, download_model_path = generate_3d_model(depth, temp_file, focallength_px)
237
 
238
+ return output_path, f"Focal length: {focallength_px:.2f} pixels", raw_depth_path, view_model_path, download_model_path, temp_file, focallength_px
239
  except Exception as e:
240
  # Return error messages in case of failures
241
  import traceback
242
  error_message = f"An error occurred: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"
243
  print(error_message) # Print the full error message to the console
244
+ return None, error_message, None, None, None, None, None
245
  finally:
246
  # Clean up by removing the temporary resized image file
247
  if temp_file and os.path.exists(temp_file):
 
258
  # Create the Gradio interface with appropriate input and output components.
259
  last_updated = get_last_commit_timestamp()
260
 
261
+ with gr.Blocks() as iface:
262
+ gr.Markdown("# DepthPro Demo with 3D Visualization")
263
+ gr.Markdown(
 
 
 
 
 
 
 
 
 
264
  "An enhanced demo that creates a textured 3D model from the input image and depth map.\n\n"
265
  "Forked from https://huggingface.co/spaces/akhaliq/depth-pro and model from https://huggingface.co/apple/DepthPro\n"
266
  "**Instructions:**\n"
 
268
  "2. The app will predict the depth map, display it, and provide the focal length.\n"
269
  "3. Download the raw depth data as a CSV file.\n"
270
  "4. View the generated 3D model textured with the original image.\n"
271
+ "5. Adjust parameters and click 'Regenerate 3D Model' to update the model.\n"
272
+ "6. Download the 3D model as an OBJ file if desired.\n\n"
273
  f"Last updated: {last_updated}"
274
+ )
275
+
276
+ with gr.Row():
277
+ input_image = gr.Image(type="filepath", label="Input Image")
278
+ depth_map = gr.Image(type="filepath", label="Depth Map")
279
+
280
+ focal_length = gr.Textbox(label="Focal Length")
281
+ raw_depth_csv = gr.File(label="Download Raw Depth Map (CSV)")
282
+
283
+ with gr.Row():
284
+ view_3d_model = gr.Model3D(label="View 3D Model")
285
+ download_3d_model = gr.File(label="Download 3D Model (OBJ)")
286
+
287
+ with gr.Row():
288
+ simplification_factor = gr.Slider(minimum=0.1, maximum=1.0, value=0.8, step=0.1, label="Simplification Factor")
289
+ smoothing_iterations = gr.Slider(minimum=0, maximum=5, value=1, step=1, label="Smoothing Iterations")
290
+ thin_threshold = gr.Slider(minimum=0.001, maximum=0.1, value=0.01, step=0.001, label="Thin Feature Threshold")
291
+
292
+ regenerate_button = gr.Button("Regenerate 3D Model")
293
+
294
+ # Hidden components to store intermediate results
295
+ hidden_depth_csv = gr.State()
296
+ hidden_image_path = gr.State()
297
+ hidden_focal_length = gr.State()
298
+
299
+ input_image.change(
300
+ predict_depth,
301
+ inputs=[input_image],
302
+ outputs=[depth_map, focal_length, raw_depth_csv, view_3d_model, download_3d_model, hidden_image_path, hidden_focal_length]
303
+ )
304
+
305
+ regenerate_button.click(
306
+ regenerate_3d_model,
307
+ inputs=[raw_depth_csv, hidden_image_path, hidden_focal_length, simplification_factor, smoothing_iterations, thin_threshold],
308
+ outputs=[view_3d_model, download_3d_model]
309
+ )
310
 
311
  # Launch the Gradio interface with sharing enabled
312
+ iface.launch(share=True)
requirements.txt CHANGED
@@ -7,4 +7,5 @@ pillow_heif==0.8.0
7
  timm
8
  trimesh==3.22.1
9
  gradio
10
- opencv-python==4.5.5.64
 
 
7
  timm
8
  trimesh==3.22.1
9
  gradio
10
+ opencv-python==4.5.5.64
11
+ open3d