Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -94,14 +94,7 @@ def allowed_file(filename):
|
|
94 |
# Enhanced image preprocessing with better detail preservation
|
95 |
def preprocess_image(image_path):
|
96 |
with Image.open(image_path) as img:
|
97 |
-
|
98 |
-
has_alpha = img.mode == 'RGBA'
|
99 |
-
|
100 |
-
# Convert to proper format while preserving alpha
|
101 |
-
if has_alpha:
|
102 |
-
img = img.convert("RGBA")
|
103 |
-
else:
|
104 |
-
img = img.convert("RGB")
|
105 |
|
106 |
# Resize if the image is too large
|
107 |
if img.width > MAX_DIMENSION or img.height > MAX_DIMENSION:
|
@@ -119,17 +112,11 @@ def preprocess_image(image_path):
|
|
119 |
# Convert to numpy array for additional preprocessing
|
120 |
img_array = np.array(img)
|
121 |
|
122 |
-
#
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
rgb = img_array
|
128 |
-
|
129 |
-
# Apply adaptive histogram equalization for better contrast on RGB channels only
|
130 |
-
if len(rgb.shape) == 3 and rgb.shape[2] == 3:
|
131 |
-
# Convert to LAB color space for better contrast enhancement
|
132 |
-
lab = cv2.cvtColor(rgb, cv2.COLOR_RGB2LAB)
|
133 |
l, a, b = cv2.split(lab)
|
134 |
|
135 |
# Apply CLAHE to L channel
|
@@ -140,17 +127,12 @@ def preprocess_image(image_path):
|
|
140 |
enhanced_lab = cv2.merge((cl, a, b))
|
141 |
|
142 |
# Convert back to RGB
|
143 |
-
|
144 |
|
145 |
-
#
|
146 |
-
|
147 |
-
result = np.dstack((rgb_enhanced, alpha))
|
148 |
-
img = Image.fromarray(result, 'RGBA')
|
149 |
-
else:
|
150 |
-
img = Image.fromarray(rgb_enhanced, 'RGB')
|
151 |
|
152 |
return img
|
153 |
-
|
154 |
|
155 |
def load_model():
|
156 |
global depth_estimator, model_loaded, model_loading
|
@@ -277,7 +259,7 @@ def enhance_depth_map(depth_map, detail_level='medium'):
|
|
277 |
|
278 |
# Convert depth map to 3D mesh with significantly enhanced detail
|
279 |
def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
280 |
-
"""Convert depth map to
|
281 |
# First, enhance the depth map for better details
|
282 |
enhanced_depth = enhance_depth_map(depth_map, detail_level)
|
283 |
|
@@ -289,94 +271,51 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
289 |
y = np.linspace(0, h-1, resolution)
|
290 |
x_grid, y_grid = np.meshgrid(x, y)
|
291 |
|
292 |
-
# Use bicubic interpolation for smoother surface
|
|
|
293 |
interp_func = interpolate.RectBivariateSpline(
|
294 |
np.arange(h), np.arange(w), enhanced_depth, kx=3, ky=3
|
295 |
)
|
296 |
|
297 |
-
# Sample depth at grid points
|
298 |
z_values = interp_func(y, x, grid=True)
|
299 |
|
300 |
-
#
|
301 |
if detail_level == 'high':
|
|
|
302 |
dx = np.gradient(z_values, axis=1)
|
303 |
dy = np.gradient(z_values, axis=0)
|
|
|
|
|
304 |
gradient_magnitude = np.sqrt(dx**2 + dy**2)
|
305 |
-
edge_mask = np.clip(gradient_magnitude * 5, 0, 0.2)
|
|
|
|
|
306 |
z_values = z_values + edge_mask * (z_values - gaussian_filter(z_values, sigma=1.0))
|
307 |
|
308 |
-
# Normalize z-values with advanced scaling
|
309 |
-
z_min, z_max = np.percentile(z_values, [2, 98])
|
310 |
z_values = (z_values - z_min) / (z_max - z_min) if z_max > z_min else z_values
|
311 |
|
312 |
-
# Apply depth scaling
|
313 |
if detail_level == 'high':
|
314 |
-
z_scaling = 2.5
|
315 |
elif detail_level == 'medium':
|
316 |
-
z_scaling = 2.0
|
317 |
else:
|
318 |
-
z_scaling = 1.5
|
319 |
|
320 |
z_values = z_values * z_scaling
|
321 |
|
322 |
-
# Normalize
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
# Create all vertices (front, back, and sides)
|
327 |
-
vertices = []
|
328 |
-
|
329 |
-
# Front face vertices
|
330 |
-
front_vertices = np.vstack([x_grid_front.flatten(), -y_grid_front.flatten(), -z_values.flatten()]).T
|
331 |
-
vertices.append(front_vertices)
|
332 |
-
|
333 |
-
# Back face vertices (mirrored from front face)
|
334 |
-
back_depth = 1.0 # Constant thickness for the model
|
335 |
-
back_vertices = np.vstack([x_grid_front.flatten(), -y_grid_front.flatten(), -z_values.flatten() - back_depth]).T
|
336 |
-
vertices.append(back_vertices)
|
337 |
-
|
338 |
-
# Create side vertices (top, bottom, left, right)
|
339 |
-
# For simplicity, we use a grid mapping for sides
|
340 |
-
top_vertices = []
|
341 |
-
bottom_vertices = []
|
342 |
-
left_vertices = []
|
343 |
-
right_vertices = []
|
344 |
-
|
345 |
-
# Create sides by connecting front and back faces
|
346 |
-
for i in range(resolution):
|
347 |
-
# Top edge
|
348 |
-
for j in range(resolution):
|
349 |
-
if i == 0:
|
350 |
-
top_vertices.append(front_vertices[i * resolution + j])
|
351 |
-
top_vertices.append(back_vertices[i * resolution + j])
|
352 |
-
# Bottom edge
|
353 |
-
if i == resolution - 1:
|
354 |
-
bottom_vertices.append(front_vertices[i * resolution + j])
|
355 |
-
bottom_vertices.append(back_vertices[i * resolution + j])
|
356 |
-
# Left edge
|
357 |
-
if j == 0:
|
358 |
-
left_vertices.append(front_vertices[i * resolution + j])
|
359 |
-
left_vertices.append(back_vertices[i * resolution + j])
|
360 |
-
# Right edge
|
361 |
-
if j == resolution - 1:
|
362 |
-
right_vertices.append(front_vertices[i * resolution + j])
|
363 |
-
right_vertices.append(back_vertices[i * resolution + j])
|
364 |
-
|
365 |
-
# Combine all vertices
|
366 |
-
all_vertices = np.vstack([
|
367 |
-
front_vertices,
|
368 |
-
back_vertices,
|
369 |
-
np.array(top_vertices),
|
370 |
-
np.array(bottom_vertices),
|
371 |
-
np.array(left_vertices),
|
372 |
-
np.array(right_vertices)
|
373 |
-
])
|
374 |
-
|
375 |
-
# Create faces (triangles)
|
376 |
-
faces = []
|
377 |
|
378 |
-
#
|
379 |
-
|
|
|
|
|
|
|
380 |
for i in range(resolution-1):
|
381 |
for j in range(resolution-1):
|
382 |
p1 = i * resolution + j
|
@@ -384,140 +323,97 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
384 |
p3 = (i + 1) * resolution + j
|
385 |
p4 = (i + 1) * resolution + (j + 1)
|
386 |
|
387 |
-
# Calculate normals
|
388 |
-
v1 =
|
389 |
-
v2 =
|
390 |
-
v3 =
|
391 |
-
v4 =
|
392 |
|
|
|
|
|
393 |
norm1 = np.cross(v2-v1, v4-v1)
|
394 |
norm2 = np.cross(v4-v3, v1-v3)
|
395 |
|
396 |
if np.dot(norm1, norm2) >= 0:
|
397 |
-
|
398 |
-
|
|
|
399 |
else:
|
400 |
-
|
401 |
-
|
|
|
402 |
|
403 |
-
|
404 |
-
back_offset = resolution * resolution # Offset for back face vertices
|
405 |
-
back_faces = []
|
406 |
-
for i in range(resolution-1):
|
407 |
-
for j in range(resolution-1):
|
408 |
-
p1 = back_offset + i * resolution + j
|
409 |
-
p2 = back_offset + i * resolution + (j + 1)
|
410 |
-
p3 = back_offset + (i + 1) * resolution + j
|
411 |
-
p4 = back_offset + (i + 1) * resolution + (j + 1)
|
412 |
-
|
413 |
-
# Reverse winding order compared to front face
|
414 |
-
back_faces.append([p1, p4, p2])
|
415 |
-
back_faces.append([p1, p3, p4])
|
416 |
-
|
417 |
-
# Side faces (connecting front and back)
|
418 |
-
side_faces = []
|
419 |
-
|
420 |
-
# Add faces for sides (top, bottom, left, right)
|
421 |
-
side_offset = 2 * resolution * resolution # Offset after front and back
|
422 |
-
|
423 |
-
# Top side
|
424 |
-
top_count = len(top_vertices)
|
425 |
-
for i in range(0, top_count - 2, 2):
|
426 |
-
side_faces.append([side_offset + i, side_offset + i + 1, side_offset + i + 3])
|
427 |
-
side_faces.append([side_offset + i, side_offset + i + 3, side_offset + i + 2])
|
428 |
-
|
429 |
-
# Bottom side
|
430 |
-
bottom_offset = side_offset + top_count
|
431 |
-
bottom_count = len(bottom_vertices)
|
432 |
-
for i in range(0, bottom_count - 2, 2):
|
433 |
-
side_faces.append([bottom_offset + i, bottom_offset + i + 3, bottom_offset + i + 1])
|
434 |
-
side_faces.append([bottom_offset + i, bottom_offset + i + 2, bottom_offset + i + 3])
|
435 |
-
|
436 |
-
# Left side
|
437 |
-
left_offset = bottom_offset + bottom_count
|
438 |
-
left_count = len(left_vertices)
|
439 |
-
for i in range(0, left_count - 2, 2):
|
440 |
-
side_faces.append([left_offset + i, left_offset + i + 1, left_offset + i + 3])
|
441 |
-
side_faces.append([left_offset + i, left_offset + i + 3, left_offset + i + 2])
|
442 |
-
|
443 |
-
# Right side
|
444 |
-
right_offset = left_offset + left_count
|
445 |
-
right_count = len(right_vertices)
|
446 |
-
for i in range(0, right_count - 2, 2):
|
447 |
-
side_faces.append([right_offset + i, right_offset + i + 3, right_offset + i + 1])
|
448 |
-
side_faces.append([right_offset + i, right_offset + i + 2, right_offset + i + 3])
|
449 |
-
|
450 |
-
# Combine all faces
|
451 |
-
faces = np.array(front_faces + back_faces + side_faces)
|
452 |
|
453 |
# Create mesh
|
454 |
-
mesh = trimesh.Trimesh(vertices=
|
455 |
|
456 |
-
# Apply texturing if image is provided
|
457 |
if image:
|
458 |
-
#
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
# Create vertex colors with transparency support
|
465 |
-
vertex_colors = np.zeros((all_vertices.shape[0], 4), dtype=np.uint8)
|
466 |
-
|
467 |
-
# Fill with default color (will be overridden for front face)
|
468 |
-
vertex_colors[:, :3] = [200, 200, 200] # Light gray default
|
469 |
-
vertex_colors[:, 3] = 255 # Fully opaque
|
470 |
-
|
471 |
-
# Front face texture (sample from image)
|
472 |
-
for i in range(resolution):
|
473 |
-
for j in range(resolution):
|
474 |
-
# Calculate image coordinates
|
475 |
-
img_x = j * (img_array.shape[1] - 1) / (resolution - 1)
|
476 |
-
img_y = i * (img_array.shape[0] - 1) / (resolution - 1)
|
477 |
-
|
478 |
-
# Bilinear interpolation setup
|
479 |
-
x0, y0 = int(img_x), int(img_y)
|
480 |
-
x1, y1 = min(x0 + 1, img_array.shape[1] - 1), min(y0 + 1, img_array.shape[0] - 1)
|
481 |
-
|
482 |
-
# Interpolation weights
|
483 |
-
wx = img_x - x0
|
484 |
-
wy = img_y - y0
|
485 |
-
|
486 |
-
vertex_idx = i * resolution + j
|
487 |
-
|
488 |
-
if has_alpha:
|
489 |
-
# Handle RGBA with bilinear interpolation
|
490 |
-
for c in range(4):
|
491 |
-
vertex_colors[vertex_idx, c] = int((1-wx)*(1-wy)*img_array[y0, x0, c] +
|
492 |
-
wx*(1-wy)*img_array[y0, x1, c] +
|
493 |
-
(1-wx)*wy*img_array[y1, x0, c] +
|
494 |
-
wx*wy*img_array[y1, x1, c])
|
495 |
-
else:
|
496 |
-
# Handle RGB (no alpha)
|
497 |
-
for c in range(3):
|
498 |
-
vertex_colors[vertex_idx, c] = int((1-wx)*(1-wy)*img_array[y0, x0, c] +
|
499 |
-
wx*(1-wy)*img_array[y0, x1, c] +
|
500 |
-
(1-wx)*wy*img_array[y1, x0, c] +
|
501 |
-
wx*wy*img_array[y1, x1, c])
|
502 |
-
vertex_colors[vertex_idx, 3] = 255 # Fully opaque
|
503 |
-
|
504 |
-
# Apply simpler texturing to back face
|
505 |
-
back_face_start = resolution * resolution
|
506 |
-
back_face_color = [180, 180, 180, 255] # Slightly darker gray
|
507 |
-
vertex_colors[back_face_start:back_face_start + (resolution * resolution)] = back_face_color
|
508 |
|
509 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
|
511 |
# Apply smoothing to get rid of staircase artifacts
|
512 |
if detail_level != 'high':
|
|
|
|
|
513 |
mesh = mesh.smoothed(method='laplacian', iterations=1)
|
514 |
|
515 |
# Calculate and fix normals for better rendering
|
516 |
mesh.fix_normals()
|
517 |
|
518 |
return mesh
|
519 |
-
|
520 |
-
|
521 |
|
522 |
@app.route('/health', methods=['GET'])
|
523 |
def health_check():
|
|
|
94 |
# Enhanced image preprocessing with better detail preservation
|
95 |
def preprocess_image(image_path):
|
96 |
with Image.open(image_path) as img:
|
97 |
+
img = img.convert("RGB")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
# Resize if the image is too large
|
100 |
if img.width > MAX_DIMENSION or img.height > MAX_DIMENSION:
|
|
|
112 |
# Convert to numpy array for additional preprocessing
|
113 |
img_array = np.array(img)
|
114 |
|
115 |
+
# Optional: Apply adaptive histogram equalization for better contrast
|
116 |
+
# This helps the depth model detect more details
|
117 |
+
if len(img_array.shape) == 3 and img_array.shape[2] == 3:
|
118 |
+
# Convert to LAB color space
|
119 |
+
lab = cv2.cvtColor(img_array, cv2.COLOR_RGB2LAB)
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
l, a, b = cv2.split(lab)
|
121 |
|
122 |
# Apply CLAHE to L channel
|
|
|
127 |
enhanced_lab = cv2.merge((cl, a, b))
|
128 |
|
129 |
# Convert back to RGB
|
130 |
+
img_array = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2RGB)
|
131 |
|
132 |
+
# Convert back to PIL Image
|
133 |
+
img = Image.fromarray(img_array)
|
|
|
|
|
|
|
|
|
134 |
|
135 |
return img
|
|
|
136 |
|
137 |
def load_model():
|
138 |
global depth_estimator, model_loaded, model_loading
|
|
|
259 |
|
260 |
# Convert depth map to 3D mesh with significantly enhanced detail
|
261 |
def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
262 |
+
"""Convert depth map to 3D mesh with highly improved detail preservation"""
|
263 |
# First, enhance the depth map for better details
|
264 |
enhanced_depth = enhance_depth_map(depth_map, detail_level)
|
265 |
|
|
|
271 |
y = np.linspace(0, h-1, resolution)
|
272 |
x_grid, y_grid = np.meshgrid(x, y)
|
273 |
|
274 |
+
# Use bicubic interpolation for smoother surface with better details
|
275 |
+
# Create interpolation function
|
276 |
interp_func = interpolate.RectBivariateSpline(
|
277 |
np.arange(h), np.arange(w), enhanced_depth, kx=3, ky=3
|
278 |
)
|
279 |
|
280 |
+
# Sample depth at grid points with the interpolation function
|
281 |
z_values = interp_func(y, x, grid=True)
|
282 |
|
283 |
+
# Apply a post-processing step to enhance small details even further
|
284 |
if detail_level == 'high':
|
285 |
+
# Calculate local gradients to detect edges
|
286 |
dx = np.gradient(z_values, axis=1)
|
287 |
dy = np.gradient(z_values, axis=0)
|
288 |
+
|
289 |
+
# Enhance edges by increasing depth differences at high gradient areas
|
290 |
gradient_magnitude = np.sqrt(dx**2 + dy**2)
|
291 |
+
edge_mask = np.clip(gradient_magnitude * 5, 0, 0.2) # Scale and limit effect
|
292 |
+
|
293 |
+
# Apply edge enhancement
|
294 |
z_values = z_values + edge_mask * (z_values - gaussian_filter(z_values, sigma=1.0))
|
295 |
|
296 |
+
# Normalize z-values with advanced scaling for better depth impression
|
297 |
+
z_min, z_max = np.percentile(z_values, [2, 98]) # Remove outliers
|
298 |
z_values = (z_values - z_min) / (z_max - z_min) if z_max > z_min else z_values
|
299 |
|
300 |
+
# Apply depth scaling appropriate to the detail level
|
301 |
if detail_level == 'high':
|
302 |
+
z_scaling = 2.5 # More pronounced depth variations
|
303 |
elif detail_level == 'medium':
|
304 |
+
z_scaling = 2.0 # Standard depth
|
305 |
else:
|
306 |
+
z_scaling = 1.5 # More subtle depth variations
|
307 |
|
308 |
z_values = z_values * z_scaling
|
309 |
|
310 |
+
# Normalize x and y coordinates
|
311 |
+
x_grid = (x_grid / w - 0.5) * 2.0 # Map to -1 to 1
|
312 |
+
y_grid = (y_grid / h - 0.5) * 2.0 # Map to -1 to 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
|
314 |
+
# Create vertices
|
315 |
+
vertices = np.vstack([x_grid.flatten(), -y_grid.flatten(), -z_values.flatten()]).T
|
316 |
+
|
317 |
+
# Create faces (triangles) with optimized winding for better normals
|
318 |
+
faces = []
|
319 |
for i in range(resolution-1):
|
320 |
for j in range(resolution-1):
|
321 |
p1 = i * resolution + j
|
|
|
323 |
p3 = (i + 1) * resolution + j
|
324 |
p4 = (i + 1) * resolution + (j + 1)
|
325 |
|
326 |
+
# Calculate normals to ensure consistent orientation
|
327 |
+
v1 = vertices[p1]
|
328 |
+
v2 = vertices[p2]
|
329 |
+
v3 = vertices[p3]
|
330 |
+
v4 = vertices[p4]
|
331 |
|
332 |
+
# Calculate normals for both possible triangulations
|
333 |
+
# and choose the one that's more consistent
|
334 |
norm1 = np.cross(v2-v1, v4-v1)
|
335 |
norm2 = np.cross(v4-v3, v1-v3)
|
336 |
|
337 |
if np.dot(norm1, norm2) >= 0:
|
338 |
+
# Standard triangulation
|
339 |
+
faces.append([p1, p2, p4])
|
340 |
+
faces.append([p1, p4, p3])
|
341 |
else:
|
342 |
+
# Alternative triangulation for smoother surface
|
343 |
+
faces.append([p1, p2, p3])
|
344 |
+
faces.append([p2, p4, p3])
|
345 |
|
346 |
+
faces = np.array(faces)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
347 |
|
348 |
# Create mesh
|
349 |
+
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
|
350 |
|
351 |
+
# Apply advanced texturing if image is provided
|
352 |
if image:
|
353 |
+
# Convert to numpy array if needed
|
354 |
+
if isinstance(image, Image.Image):
|
355 |
+
img_array = np.array(image)
|
356 |
+
else:
|
357 |
+
img_array = image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
358 |
|
359 |
+
# Create vertex colors with improved sampling
|
360 |
+
if resolution <= img_array.shape[0] and resolution <= img_array.shape[1]:
|
361 |
+
# Create vertex colors by sampling the image with bilinear interpolation
|
362 |
+
vertex_colors = np.zeros((vertices.shape[0], 4), dtype=np.uint8)
|
363 |
+
|
364 |
+
# Get normalized coordinates for sampling
|
365 |
+
for i in range(resolution):
|
366 |
+
for j in range(resolution):
|
367 |
+
# Calculate exact image coordinates with proper scaling
|
368 |
+
img_x = j * (img_array.shape[1] - 1) / (resolution - 1)
|
369 |
+
img_y = i * (img_array.shape[0] - 1) / (resolution - 1)
|
370 |
+
|
371 |
+
# Bilinear interpolation for smooth color transitions
|
372 |
+
x0, y0 = int(img_x), int(img_y)
|
373 |
+
x1, y1 = min(x0 + 1, img_array.shape[1] - 1), min(y0 + 1, img_array.shape[0] - 1)
|
374 |
+
|
375 |
+
# Calculate interpolation weights
|
376 |
+
wx = img_x - x0
|
377 |
+
wy = img_y - y0
|
378 |
+
|
379 |
+
vertex_idx = i * resolution + j
|
380 |
+
|
381 |
+
if len(img_array.shape) == 3 and img_array.shape[2] == 3: # RGB
|
382 |
+
# Perform bilinear interpolation for each color channel
|
383 |
+
r = int((1-wx)*(1-wy)*img_array[y0, x0, 0] + wx*(1-wy)*img_array[y0, x1, 0] +
|
384 |
+
(1-wx)*wy*img_array[y1, x0, 0] + wx*wy*img_array[y1, x1, 0])
|
385 |
+
g = int((1-wx)*(1-wy)*img_array[y0, x0, 1] + wx*(1-wy)*img_array[y0, x1, 1] +
|
386 |
+
(1-wx)*wy*img_array[y1, x0, 1] + wx*wy*img_array[y1, x1, 1])
|
387 |
+
b = int((1-wx)*(1-wy)*img_array[y0, x0, 2] + wx*(1-wy)*img_array[y0, x1, 2] +
|
388 |
+
(1-wx)*wy*img_array[y1, x0, 2] + wx*wy*img_array[y1, x1, 2])
|
389 |
+
|
390 |
+
vertex_colors[vertex_idx, :3] = [r, g, b]
|
391 |
+
vertex_colors[vertex_idx, 3] = 255 # Alpha
|
392 |
+
elif len(img_array.shape) == 3 and img_array.shape[2] == 4: # RGBA
|
393 |
+
for c in range(4): # For each RGBA channel
|
394 |
+
vertex_colors[vertex_idx, c] = int((1-wx)*(1-wy)*img_array[y0, x0, c] +
|
395 |
+
wx*(1-wy)*img_array[y0, x1, c] +
|
396 |
+
(1-wx)*wy*img_array[y1, x0, c] +
|
397 |
+
wx*wy*img_array[y1, x1, c])
|
398 |
+
else:
|
399 |
+
# Handle grayscale with bilinear interpolation
|
400 |
+
gray = int((1-wx)*(1-wy)*img_array[y0, x0] + wx*(1-wy)*img_array[y0, x1] +
|
401 |
+
(1-wx)*wy*img_array[y1, x0] + wx*wy*img_array[y1, x1])
|
402 |
+
vertex_colors[vertex_idx, :3] = [gray, gray, gray]
|
403 |
+
vertex_colors[vertex_idx, 3] = 255
|
404 |
+
|
405 |
+
mesh.visual.vertex_colors = vertex_colors
|
406 |
|
407 |
# Apply smoothing to get rid of staircase artifacts
|
408 |
if detail_level != 'high':
|
409 |
+
# For medium and low detail, apply Laplacian smoothing
|
410 |
+
# but preserve the overall shape
|
411 |
mesh = mesh.smoothed(method='laplacian', iterations=1)
|
412 |
|
413 |
# Calculate and fix normals for better rendering
|
414 |
mesh.fix_normals()
|
415 |
|
416 |
return mesh
|
|
|
|
|
417 |
|
418 |
@app.route('/health', methods=['GET'])
|
419 |
def health_check():
|