mac9087 commited on
Commit
c0d1170
·
verified ·
1 Parent(s): fa62b8d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -208
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
- # Keep alpha channel if present
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
- # Extract alpha channel if present
123
- if has_alpha:
124
- alpha = img_array[:, :, 3]
125
- rgb = img_array[:, :, :3]
126
- else:
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
- rgb_enhanced = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2RGB)
144
 
145
- # Recombine with alpha if needed
146
- if has_alpha:
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 complete 3D model with all sides"""
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
- # Process enhancement as in original code
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 coordinates for front face
323
- x_grid_front = (x_grid / w - 0.5) * 2.0
324
- y_grid_front = (y_grid / h - 0.5) * 2.0
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
- # Front face triangles
379
- front_faces = []
 
 
 
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 for consistent orientation
388
- v1 = front_vertices[p1]
389
- v2 = front_vertices[p2]
390
- v3 = front_vertices[p3]
391
- v4 = front_vertices[p4]
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
- front_faces.append([p1, p2, p4])
398
- front_faces.append([p1, p4, p3])
 
399
  else:
400
- front_faces.append([p1, p2, p3])
401
- front_faces.append([p2, p4, p3])
 
402
 
403
- # Back face triangles (note: reversed winding order for correct normals)
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=all_vertices, faces=faces)
455
 
456
- # Apply texturing if image is provided
457
  if image:
458
- # Handle RGBA properly to ensure transparency is maintained
459
- img_array = np.array(image)
460
-
461
- # Check if image has alpha channel
462
- has_alpha = len(img_array.shape) == 3 and img_array.shape[2] == 4
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
- mesh.visual.vertex_colors = vertex_colors
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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():