tejani commited on
Commit
be0aad2
·
verified ·
1 Parent(s): 4ec840a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -64
app.py CHANGED
@@ -1,77 +1,101 @@
1
- import torch
2
- from transformers import AutoModelForDepthEstimation, AutoImageProcessor
3
  import gradio as gr
 
4
  import numpy as np
5
  from PIL import Image
 
6
 
7
- # Load the pretrained depth estimation model from Hugging Face
8
- model_name = "Intel/dpt-large"
9
- processor = AutoImageProcessor.from_pretrained(model_name)
10
- model = AutoModelForDepthEstimation.from_pretrained(model_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- def generate_material_maps(image):
13
- """
14
- Generate diffuse, height, and normal maps from an input image.
15
-
16
- Args:
17
- image: PIL Image object (input image)
18
-
19
- Returns:
20
- tuple: Diffuse map, height map, and normal map as numpy arrays
21
- """
22
- # Preprocess the image for the model
23
- inputs = processor(images=image, return_tensors="pt")
24
-
25
- # Run depth estimation
26
- with torch.no_grad():
27
- outputs = model(**inputs)
28
- depth_map = outputs.predicted_depth
29
-
30
- # Convert depth map to numpy and normalize to [0, 1]
31
- depth_map = depth_map.squeeze().cpu().numpy()
32
- height_map = (depth_map - depth_map.min()) / (depth_map.max() - depth_map.min())
33
-
34
- # Compute normal map from height map using gradients
35
- gy, gx = np.gradient(height_map)
36
- normal_map = np.dstack((gx, gy, np.ones_like(height_map))) # RGB for x, y, z normals
37
- normal_map = (normal_map + 1) / 2 # Normalize to [0, 1] for visualization
38
-
39
- # Use the original image as the diffuse map
40
- diffuse_map = np.array(image) / 255.0 # Normalize to [0, 1]
41
-
42
- return diffuse_map, height_map, normal_map
43
 
44
- def gradio_interface(image):
45
- """
46
- Interface function for Gradio to process the input and return images.
47
-
48
- Args:
49
- image: PIL Image object
50
-
51
- Returns:
52
- tuple: PIL Images for diffuse, height, and normal maps
53
- """
54
- diffuse, height, normal = generate_material_maps(image)
55
-
56
- # Convert to PIL Images and scale to [0, 255] for display
57
- diffuse_img = Image.fromarray((diffuse * 255).astype(np.uint8))
58
- height_img = Image.fromarray((height * 255).astype(np.uint8), mode="L") # Grayscale
59
- normal_img = Image.fromarray((normal * 255).astype(np.uint8))
60
-
61
- return diffuse_img, height_img, normal_img
62
 
63
- # Create Gradio interface
 
 
 
 
 
 
 
 
 
64
  interface = gr.Interface(
65
- fn=gradio_interface,
66
- inputs=gr.Image(type="pil", label="Upload an Image"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  outputs=[
68
- gr.Image(type="pil", label="Diffuse Map"),
69
- gr.Image(type="pil", label="Height Map"),
70
- gr.Image(type="pil", label="Normal Map")
71
  ],
72
- title="Material Map Generator",
73
- description="Upload an image to generate diffuse, height, and normal maps using a pretrained Hugging Face model."
74
  )
75
 
76
- # Launch the interface locally (for testing)
77
  interface.launch()
 
 
 
1
  import gradio as gr
2
+ import cv2
3
  import numpy as np
4
  from PIL import Image
5
+ import noise # Requires `pynoise` package: `pip install noise`
6
 
7
+ # Advanced Normal Map generation
8
+ def generate_normal_map(image, strength=1.0, blur_size=5, use_bilateral=False):
9
+ img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
10
+ # Noise reduction: Bilateral filter (edge-preserving) or Gaussian blur
11
+ if use_bilateral:
12
+ img = cv2.bilateralFilter(img, 9, 75, 75)
13
+ else:
14
+ img = cv2.GaussianBlur(img, (blur_size, blur_size), 0)
15
+ # Compute gradients with Scharr (better than Sobel for fine details)
16
+ sobel_x = cv2.Scharr(img, cv2.CV_64F, 1, 0)
17
+ sobel_y = cv2.Scharr(img, cv2.CV_64F, 0, 1)
18
+ # Normalize gradients with strength adjustment
19
+ sobel_x = cv2.normalize(sobel_x, None, -strength, strength, cv2.NORM_MINMAX)
20
+ sobel_y = cv2.normalize(sobel_y, None, -strength, strength, cv2.NORM_MINMAX)
21
+ # Compute normal vectors
22
+ normal_map = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.float32)
23
+ normal_map[..., 0] = sobel_x # X (Red)
24
+ normal_map[..., 1] = sobel_y # Y (Green)
25
+ normal_map[..., 2] = 1.0 # Z (Blue)
26
+ norm = np.linalg.norm(normal_map, axis=2, keepdims=True)
27
+ normal_map = np.divide(normal_map, norm, out=np.zeros_like(normal_map), where=norm != 0)
28
+ # Convert to RGB
29
+ normal_map = (normal_map + 1) * 127.5
30
+ normal_map = np.clip(normal_map, 0, 255).astype(np.uint8)
31
+ return Image.fromarray(normal_map)
32
 
33
+ # Advanced Displacement Map generation
34
+ def generate_displacement_map(image, contrast=1.0, add_noise=False, noise_scale=0.1):
35
+ img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
36
+ # Enhance contrast with histogram equalization
37
+ img = cv2.equalizeHist(img)
38
+ # Adjust contrast
39
+ img = cv2.convertScaleAbs(img, alpha=contrast, beta=0)
40
+ # Optional: Add Perlin noise for realism
41
+ if add_noise:
42
+ height, width = img.shape
43
+ noise_map = np.zeros((height, width), dtype=np.float32)
44
+ for y in range(height):
45
+ for x in range(width):
46
+ noise_map[y, x] = noise.pnoise2(x / 50.0, y / 50.0, octaves=6) * noise_scale * 255
47
+ img = cv2.add(img, noise_map.astype(np.uint8))
48
+ return Image.fromarray(img)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ # Advanced Roughness Map generation
51
+ def generate_roughness_map(image, invert=True, sharpness=1.0, detail_boost=0.5):
52
+ img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
53
+ # Edge-preserving smoothing
54
+ img = cv2.bilateralFilter(img, 9, 75, 75)
55
+ # Optional inversion
56
+ if invert:
57
+ img = 255 - img
58
+ # Sharpen the image
59
+ blurred = cv2.GaussianBlur(img, (5, 5), 0)
60
+ img = cv2.addWeighted(img, 1.0 + sharpness, blurred, -sharpness, 0)
61
+ # Boost fine details with unsharp masking
62
+ img = cv2.addWeighted(img, 1.0 + detail_boost, blurred, -detail_boost, 0)
63
+ return Image.fromarray(img)
 
 
 
 
64
 
65
+ # Process function with all parameters
66
+ def process_image(input_image, normal_strength=1.0, normal_blur=5, normal_bilateral=False,
67
+ disp_contrast=1.0, disp_noise=False, disp_noise_scale=0.1,
68
+ rough_invert=True, rough_sharpness=1.0, rough_detail=0.5):
69
+ normal_map = generate_normal_map(input_image, strength=normal_strength, blur_size=normal_blur, use_bilateral=normal_bilateral)
70
+ displacement_map = generate_displacement_map(input_image, contrast=disp_contrast, add_noise=disp_noise, noise_scale=disp_noise_scale)
71
+ roughness_map = generate_roughness_map(input_image, invert=rough_invert, sharpness=rough_sharpness, detail_boost=rough_detail)
72
+ return normal_map, displacement_map, roughness_map
73
+
74
+ # Gradio Interface with advanced controls
75
  interface = gr.Interface(
76
+ fn=process_image,
77
+ inputs=[
78
+ gr.Image(type="pil", label="Upload Image"),
79
+ # Normal Map Controls
80
+ gr.Slider(minimum=0.1, maximum=5.0, step=0.1, value=1.0, label="Normal Map Strength"),
81
+ gr.Slider(minimum=3, maximum=15, step=2, value=5, label="Normal Map Blur Size (if not bilateral)"),
82
+ gr.Checkbox(value=False, label="Use Bilateral Filter for Normal Map"),
83
+ # Displacement Map Controls
84
+ gr.Slider(minimum=0.5, maximum=2.0, step=0.1, value=1.0, label="Displacement Map Contrast"),
85
+ gr.Checkbox(value=False, label="Add Noise to Displacement Map"),
86
+ gr.Slider(minimum=0.05, maximum=0.5, step=0.05, value=0.1, label="Displacement Noise Scale"),
87
+ # Roughness Map Controls
88
+ gr.Checkbox(value=True, label="Invert Roughness Map"),
89
+ gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1.0, label="Roughness Sharpness"),
90
+ gr.Slider(minimum=0.0, maximum=1.0, step=0.1, value=0.5, label="Roughness Detail Boost")
91
+ ],
92
  outputs=[
93
+ gr.Image(type="pil", label="Normal Map"),
94
+ gr.Image(type="pil", label="Displacement Map"),
95
+ gr.Image(type="pil", label="Roughness Map")
96
  ],
97
+ title="Advanced Material Map Generator",
98
+ description="Upload an image to generate highly customizable Normal, Displacement, and Roughness maps."
99
  )
100
 
 
101
  interface.launch()