Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -259,7 +259,7 @@ def enhance_depth_map(depth_map, detail_level='medium'):
|
|
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 |
|
@@ -314,7 +314,38 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
314 |
# Create vertices
|
315 |
vertices = np.vstack([x_grid.flatten(), -y_grid.flatten(), -z_values.flatten()]).T
|
316 |
|
317 |
-
# Create
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
318 |
faces = []
|
319 |
for i in range(resolution-1):
|
320 |
for j in range(resolution-1):
|
@@ -323,25 +354,31 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
323 |
p3 = (i + 1) * resolution + j
|
324 |
p4 = (i + 1) * resolution + (j + 1)
|
325 |
|
326 |
-
#
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
345 |
|
346 |
faces = np.array(faces)
|
347 |
|
@@ -356,10 +393,11 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
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):
|
@@ -378,23 +416,26 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
378 |
|
379 |
vertex_idx = i * resolution + j
|
380 |
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
(1-wx)*wy*img_array[
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
|
|
|
|
|
|
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] +
|
@@ -415,6 +456,60 @@ def depth_to_mesh(depth_map, image, resolution=100, detail_level='medium'):
|
|
415 |
|
416 |
return mesh
|
417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
@app.route('/health', methods=['GET'])
|
419 |
def health_check():
|
420 |
return jsonify({
|
|
|
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 and transparency support"""
|
263 |
# First, enhance the depth map for better details
|
264 |
enhanced_depth = enhance_depth_map(depth_map, detail_level)
|
265 |
|
|
|
314 |
# Create vertices
|
315 |
vertices = np.vstack([x_grid.flatten(), -y_grid.flatten(), -z_values.flatten()]).T
|
316 |
|
317 |
+
# Create transparency mask for the image if it has an alpha channel
|
318 |
+
# This will be used to filter out faces that contain transparent pixels
|
319 |
+
has_alpha = False
|
320 |
+
alpha_mask = np.ones((resolution, resolution), dtype=bool)
|
321 |
+
|
322 |
+
if image is not None:
|
323 |
+
if isinstance(image, Image.Image):
|
324 |
+
if image.mode == 'RGBA':
|
325 |
+
has_alpha = True
|
326 |
+
# Convert image to numpy array with alpha channel
|
327 |
+
img_array = np.array(image)
|
328 |
+
# Extract alpha channel
|
329 |
+
alpha_channel = img_array[:, :, 3]
|
330 |
+
|
331 |
+
# Create alpha mask by sampling alpha channel at grid positions
|
332 |
+
for i in range(resolution):
|
333 |
+
for j in range(resolution):
|
334 |
+
img_y = int(i * (img_array.shape[0] - 1) / (resolution - 1))
|
335 |
+
img_x = int(j * (img_array.shape[1] - 1) / (resolution - 1))
|
336 |
+
alpha_mask[i, j] = alpha_channel[img_y, img_x] > 10 # Threshold for transparency
|
337 |
+
elif isinstance(image, np.ndarray) and image.shape[2] == 4: # RGBA numpy array
|
338 |
+
has_alpha = True
|
339 |
+
alpha_channel = image[:, :, 3]
|
340 |
+
|
341 |
+
# Sample alpha channel at grid positions
|
342 |
+
for i in range(resolution):
|
343 |
+
for j in range(resolution):
|
344 |
+
img_y = int(i * (image.shape[0] - 1) / (resolution - 1))
|
345 |
+
img_x = int(j * (image.shape[1] - 1) / (resolution - 1))
|
346 |
+
alpha_mask[i, j] = alpha_channel[img_y, img_x] > 10 # Threshold for transparency
|
347 |
+
|
348 |
+
# Create faces (triangles) with transparency handling
|
349 |
faces = []
|
350 |
for i in range(resolution-1):
|
351 |
for j in range(resolution-1):
|
|
|
354 |
p3 = (i + 1) * resolution + j
|
355 |
p4 = (i + 1) * resolution + (j + 1)
|
356 |
|
357 |
+
# Only create faces if all vertices are visible (non-transparent)
|
358 |
+
if not has_alpha or (alpha_mask[i, j] and alpha_mask[i, j+1] and
|
359 |
+
alpha_mask[i+1, j] and alpha_mask[i+1, j+1]):
|
360 |
+
# Calculate normals to ensure consistent orientation
|
361 |
+
v1 = vertices[p1]
|
362 |
+
v2 = vertices[p2]
|
363 |
+
v3 = vertices[p3]
|
364 |
+
v4 = vertices[p4]
|
365 |
+
|
366 |
+
# Calculate normals for both possible triangulations
|
367 |
+
# and choose the one that's more consistent
|
368 |
+
norm1 = np.cross(v2-v1, v4-v1)
|
369 |
+
norm2 = np.cross(v4-v3, v1-v3)
|
370 |
+
|
371 |
+
if np.dot(norm1, norm2) >= 0:
|
372 |
+
# Standard triangulation
|
373 |
+
faces.append([p1, p2, p4])
|
374 |
+
faces.append([p1, p4, p3])
|
375 |
+
else:
|
376 |
+
# Alternative triangulation for smoother surface
|
377 |
+
faces.append([p1, p2, p3])
|
378 |
+
faces.append([p2, p4, p3])
|
379 |
+
|
380 |
+
if len(faces) == 0:
|
381 |
+
raise ValueError("No faces generated - image may be completely transparent")
|
382 |
|
383 |
faces = np.array(faces)
|
384 |
|
|
|
393 |
else:
|
394 |
img_array = image
|
395 |
|
396 |
+
# Create vertex colors with improved sampling and transparency support
|
397 |
if resolution <= img_array.shape[0] and resolution <= img_array.shape[1]:
|
398 |
# Create vertex colors by sampling the image with bilinear interpolation
|
399 |
vertex_colors = np.zeros((vertices.shape[0], 4), dtype=np.uint8)
|
400 |
+
vertex_colors[:, 3] = 255 # Default alpha to opaque
|
401 |
|
402 |
# Get normalized coordinates for sampling
|
403 |
for i in range(resolution):
|
|
|
416 |
|
417 |
vertex_idx = i * resolution + j
|
418 |
|
419 |
+
# Apply vertex colors based on image format
|
420 |
+
if len(img_array.shape) == 3:
|
421 |
+
if img_array.shape[2] == 4: # RGBA
|
422 |
+
# Set colors with alpha channel
|
423 |
+
for c in range(4): # For each RGBA channel
|
424 |
+
vertex_colors[vertex_idx, c] = int((1-wx)*(1-wy)*img_array[y0, x0, c] +
|
425 |
+
wx*(1-wy)*img_array[y0, x1, c] +
|
426 |
+
(1-wx)*wy*img_array[y1, x0, c] +
|
427 |
+
wx*wy*img_array[y1, x1, c])
|
428 |
+
elif img_array.shape[2] == 3: # RGB
|
429 |
+
# Apply bilinear interpolation for each color channel
|
430 |
+
r = int((1-wx)*(1-wy)*img_array[y0, x0, 0] + wx*(1-wy)*img_array[y0, x1, 0] +
|
431 |
+
(1-wx)*wy*img_array[y1, x0, 0] + wx*wy*img_array[y1, x1, 0])
|
432 |
+
g = int((1-wx)*(1-wy)*img_array[y0, x0, 1] + wx*(1-wy)*img_array[y0, x1, 1] +
|
433 |
+
(1-wx)*wy*img_array[y1, x0, 1] + wx*wy*img_array[y1, x1, 1])
|
434 |
+
b = int((1-wx)*(1-wy)*img_array[y0, x0, 2] + wx*(1-wy)*img_array[y0, x1, 2] +
|
435 |
+
(1-wx)*wy*img_array[y1, x0, 2] + wx*wy*img_array[y1, x1, 2])
|
436 |
+
|
437 |
+
vertex_colors[vertex_idx, :3] = [r, g, b]
|
438 |
+
vertex_colors[vertex_idx, 3] = 255 # Full opacity for RGB images
|
439 |
else:
|
440 |
# Handle grayscale with bilinear interpolation
|
441 |
gray = int((1-wx)*(1-wy)*img_array[y0, x0] + wx*(1-wy)*img_array[y0, x1] +
|
|
|
456 |
|
457 |
return mesh
|
458 |
|
459 |
+
# Enhanced image preprocessing to properly handle PNGs with transparency
|
460 |
+
def preprocess_image(image_path):
|
461 |
+
with Image.open(image_path) as img:
|
462 |
+
# Get original mode to check if it has transparency
|
463 |
+
original_mode = img.mode
|
464 |
+
|
465 |
+
# Convert to RGB or RGBA as needed
|
466 |
+
if original_mode == 'RGBA':
|
467 |
+
# Keep alpha channel for transparency
|
468 |
+
img = img.convert("RGBA")
|
469 |
+
else:
|
470 |
+
# Otherwise use RGB
|
471 |
+
img = img.convert("RGB")
|
472 |
+
|
473 |
+
# Resize if the image is too large
|
474 |
+
if img.width > MAX_DIMENSION or img.height > MAX_DIMENSION:
|
475 |
+
# Calculate new dimensions while preserving aspect ratio
|
476 |
+
if img.width > img.height:
|
477 |
+
new_width = MAX_DIMENSION
|
478 |
+
new_height = int(img.height * (MAX_DIMENSION / img.width))
|
479 |
+
else:
|
480 |
+
new_height = MAX_DIMENSION
|
481 |
+
new_width = int(img.width * (MAX_DIMENSION / img.height))
|
482 |
+
|
483 |
+
# Use high-quality Lanczos resampling for better detail preservation
|
484 |
+
img = img.resize((new_width, new_height), Image.LANCZOS)
|
485 |
+
|
486 |
+
# Handle enhancement only for RGB (non-transparent) parts
|
487 |
+
if original_mode != 'RGBA':
|
488 |
+
# Convert to numpy array for additional preprocessing
|
489 |
+
img_array = np.array(img)
|
490 |
+
|
491 |
+
# Optional: Apply adaptive histogram equalization for better contrast
|
492 |
+
# This helps the depth model detect more details
|
493 |
+
if len(img_array.shape) == 3 and img_array.shape[2] == 3:
|
494 |
+
# Convert to LAB color space
|
495 |
+
lab = cv2.cvtColor(img_array, cv2.COLOR_RGB2LAB)
|
496 |
+
l, a, b = cv2.split(lab)
|
497 |
+
|
498 |
+
# Apply CLAHE to L channel
|
499 |
+
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
|
500 |
+
cl = clahe.apply(l)
|
501 |
+
|
502 |
+
# Merge channels back
|
503 |
+
enhanced_lab = cv2.merge((cl, a, b))
|
504 |
+
|
505 |
+
# Convert back to RGB
|
506 |
+
img_array = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2RGB)
|
507 |
+
|
508 |
+
# Convert back to PIL Image
|
509 |
+
img = Image.fromarray(img_array)
|
510 |
+
|
511 |
+
return img
|
512 |
+
|
513 |
@app.route('/health', methods=['GET'])
|
514 |
def health_check():
|
515 |
return jsonify({
|