Spaces:
Running
on
Zero
Running
on
Zero
Added 3D mesh cleanup to remove artifacts v1
Browse files
app.py
CHANGED
@@ -57,14 +57,6 @@ def resize_image(image_path, max_size=1024):
|
|
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 |
-
|
61 |
-
Args:
|
62 |
-
depth (np.ndarray): 2D array representing depth in meters.
|
63 |
-
image_path (str): Path to the RGB image.
|
64 |
-
focallength_px (float): Focal length in pixels.
|
65 |
-
|
66 |
-
Returns:
|
67 |
-
tuple: Paths to the exported 3D model files for viewing and downloading.
|
68 |
"""
|
69 |
# Load the RGB image and convert to a NumPy array
|
70 |
image = np.array(Image.open(image_path))
|
@@ -116,6 +108,28 @@ def generate_3d_model(depth, image_path, focallength_px):
|
|
116 |
# Create the mesh using Trimesh with vertex colors
|
117 |
mesh = trimesh.Trimesh(vertices=vertices, faces=faces, vertex_colors=colors)
|
118 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
# Export the mesh to OBJ files with unique filenames
|
120 |
timestamp = int(time.time())
|
121 |
view_model_path = f'view_model_{timestamp}.obj'
|
@@ -124,6 +138,30 @@ def generate_3d_model(depth, image_path, focallength_px):
|
|
124 |
mesh.export(download_model_path)
|
125 |
return view_model_path, download_model_path
|
126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
@spaces.GPU(duration=20)
|
128 |
def predict_depth(input_image):
|
129 |
temp_file = None
|
|
|
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 |
"""
|
61 |
# Load the RGB image and convert to a NumPy array
|
62 |
image = np.array(Image.open(image_path))
|
|
|
108 |
# Create the mesh using Trimesh with vertex colors
|
109 |
mesh = trimesh.Trimesh(vertices=vertices, faces=faces, vertex_colors=colors)
|
110 |
|
111 |
+
# Mesh cleaning and improvement steps
|
112 |
+
print("Original mesh - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
|
113 |
+
|
114 |
+
# 1. Mesh simplification
|
115 |
+
mesh = mesh.simplify_quadratic_decimation(target_percent=0.8)
|
116 |
+
print("After simplification - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
|
117 |
+
|
118 |
+
# 2. Remove small disconnected components
|
119 |
+
components = mesh.split(only_watertight=False)
|
120 |
+
if len(components) > 1:
|
121 |
+
areas = np.array([c.area for c in components])
|
122 |
+
mesh = components[np.argmax(areas)]
|
123 |
+
print("After removing small components - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
|
124 |
+
|
125 |
+
# 3. Smooth the mesh
|
126 |
+
mesh = mesh.smoothed()
|
127 |
+
print("After smoothing - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
|
128 |
+
|
129 |
+
# 4. Remove thin features
|
130 |
+
mesh = remove_thin_features(mesh)
|
131 |
+
print("After removing thin features - vertices: {}, faces: {}".format(len(mesh.vertices), len(mesh.faces)))
|
132 |
+
|
133 |
# Export the mesh to OBJ files with unique filenames
|
134 |
timestamp = int(time.time())
|
135 |
view_model_path = f'view_model_{timestamp}.obj'
|
|
|
138 |
mesh.export(download_model_path)
|
139 |
return view_model_path, download_model_path
|
140 |
|
141 |
+
def remove_thin_features(mesh, thickness_threshold=0.01):
|
142 |
+
"""
|
143 |
+
Remove thin features from the mesh.
|
144 |
+
"""
|
145 |
+
# Calculate edge lengths
|
146 |
+
edges = mesh.edges_unique
|
147 |
+
edge_points = mesh.vertices[edges]
|
148 |
+
edge_lengths = np.linalg.norm(edge_points[:, 0] - edge_points[:, 1], axis=1)
|
149 |
+
|
150 |
+
# Identify short edges
|
151 |
+
short_edges = edges[edge_lengths < thickness_threshold]
|
152 |
+
|
153 |
+
# Collapse short edges
|
154 |
+
for edge in short_edges:
|
155 |
+
try:
|
156 |
+
mesh.collapse_edge(edge)
|
157 |
+
except:
|
158 |
+
pass # Skip if edge collapse fails
|
159 |
+
|
160 |
+
# Remove any newly created degenerate faces
|
161 |
+
mesh.remove_degenerate_faces()
|
162 |
+
|
163 |
+
return mesh
|
164 |
+
|
165 |
@spaces.GPU(duration=20)
|
166 |
def predict_depth(input_image):
|
167 |
temp_file = None
|