Update app.py
Browse files
app.py
CHANGED
|
@@ -4,40 +4,59 @@ import numpy as np
|
|
| 4 |
from PIL import Image
|
| 5 |
import noise # Requires `pynoise` package: `pip install noise`
|
| 6 |
|
| 7 |
-
# Advanced Normal Map
|
| 8 |
-
def generate_normal_map(image, strength=1.0, blur_size=5, use_bilateral=False):
|
| 9 |
-
img =
|
| 10 |
-
|
|
|
|
| 11 |
if use_bilateral:
|
| 12 |
-
|
| 13 |
else:
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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 |
-
#
|
| 37 |
-
|
|
|
|
| 38 |
# Adjust contrast
|
| 39 |
img = cv2.convertScaleAbs(img, alpha=contrast, beta=0)
|
| 40 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
if add_noise:
|
| 42 |
height, width = img.shape
|
| 43 |
noise_map = np.zeros((height, width), dtype=np.float32)
|
|
@@ -47,28 +66,33 @@ def generate_displacement_map(image, contrast=1.0, add_noise=False, noise_scale=
|
|
| 47 |
img = cv2.add(img, noise_map.astype(np.uint8))
|
| 48 |
return Image.fromarray(img)
|
| 49 |
|
| 50 |
-
# Advanced Roughness Map
|
| 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 |
-
#
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
| 55 |
# Optional inversion
|
| 56 |
if invert:
|
| 57 |
img = 255 - img
|
| 58 |
-
#
|
| 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
|
| 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,
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
| 72 |
return normal_map, displacement_map, roughness_map
|
| 73 |
|
| 74 |
# Gradio Interface with advanced controls
|
|
@@ -78,16 +102,19 @@ interface = gr.Interface(
|
|
| 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 (
|
| 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"),
|
|
@@ -95,7 +122,7 @@ interface = gr.Interface(
|
|
| 95 |
gr.Image(type="pil", label="Roughness Map")
|
| 96 |
],
|
| 97 |
title="Advanced Material Map Generator",
|
| 98 |
-
description="Upload an image to generate highly
|
| 99 |
)
|
| 100 |
|
| 101 |
interface.launch()
|
|
|
|
| 4 |
from PIL import Image
|
| 5 |
import noise # Requires `pynoise` package: `pip install noise`
|
| 6 |
|
| 7 |
+
# Advanced Normal Map with multi-scale gradients and color influence
|
| 8 |
+
def generate_normal_map(image, strength=1.0, blur_size=5, use_bilateral=False, color_influence=0.3):
|
| 9 |
+
img = np.array(image)
|
| 10 |
+
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
| 11 |
+
# Noise reduction
|
| 12 |
if use_bilateral:
|
| 13 |
+
gray = cv2.bilateralFilter(gray, 9, 75, 75)
|
| 14 |
else:
|
| 15 |
+
gray = cv2.GaussianBlur(gray, (blur_size, blur_size), 0)
|
| 16 |
+
|
| 17 |
+
# Multi-scale gradient computation using Laplacian pyramid
|
| 18 |
+
levels = 3
|
| 19 |
+
normal_map = np.zeros((gray.shape[0], gray.shape[1], 3), dtype=np.float32)
|
| 20 |
+
for i in range(levels):
|
| 21 |
+
scale = 1 / (2 ** i)
|
| 22 |
+
resized = cv2.resize(gray, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
|
| 23 |
+
sobel_x = cv2.Scharr(resized, cv2.CV_64F, 1, 0)
|
| 24 |
+
sobel_y = cv2.Scharr(resized, cv2.CV_64F, 0, 1)
|
| 25 |
+
sobel_x = cv2.resize(sobel_x, (gray.shape[1], gray.shape[0]), interpolation=cv2.INTER_LINEAR)
|
| 26 |
+
sobel_y = cv2.resize(sobel_y, (gray.shape[1], gray.shape[0]), interpolation=cv2.INTER_LINEAR)
|
| 27 |
+
normal_map[..., 0] += sobel_x * (1.0 / levels)
|
| 28 |
+
normal_map[..., 1] += sobel_y * (1.0 / levels)
|
| 29 |
+
|
| 30 |
+
# Normalize with strength
|
| 31 |
+
normal_map[..., 0] = cv2.normalize(normal_map[..., 0], None, -strength, strength, cv2.NORM_MINMAX)
|
| 32 |
+
normal_map[..., 1] = cv2.normalize(normal_map[..., 1], None, -strength, strength, cv2.NORM_MINMAX)
|
| 33 |
+
normal_map[..., 2] = 1.0
|
| 34 |
+
|
| 35 |
+
# Add color influence (e.g., red affects X, green affects Y)
|
| 36 |
+
color_factor = color_influence * strength
|
| 37 |
+
normal_map[..., 0] += (img[..., 0] / 255.0 - 0.5) * color_factor
|
| 38 |
+
normal_map[..., 1] += (img[..., 1] / 255.0 - 0.5) * color_factor
|
| 39 |
+
|
| 40 |
+
# Normalize to unit vectors
|
| 41 |
norm = np.linalg.norm(normal_map, axis=2, keepdims=True)
|
| 42 |
normal_map = np.divide(normal_map, norm, out=np.zeros_like(normal_map), where=norm != 0)
|
|
|
|
| 43 |
normal_map = (normal_map + 1) * 127.5
|
| 44 |
normal_map = np.clip(normal_map, 0, 255).astype(np.uint8)
|
| 45 |
return Image.fromarray(normal_map)
|
| 46 |
|
| 47 |
+
# Advanced Displacement Map with adaptive thresholding and edge enhancement
|
| 48 |
+
def generate_displacement_map(image, contrast=1.0, add_noise=False, noise_scale=0.1, edge_boost=1.0):
|
| 49 |
img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
|
| 50 |
+
# Adaptive histogram equalization for better contrast
|
| 51 |
+
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
|
| 52 |
+
img = clahe.apply(img)
|
| 53 |
# Adjust contrast
|
| 54 |
img = cv2.convertScaleAbs(img, alpha=contrast, beta=0)
|
| 55 |
+
# Edge enhancement with Laplacian
|
| 56 |
+
laplacian = cv2.Laplacian(img, cv2.CV_64F)
|
| 57 |
+
img = cv2.addWeighted(img, 1.0, laplacian, edge_boost, 0)
|
| 58 |
+
img = np.clip(img, 0, 255).astype(np.uint8)
|
| 59 |
+
# Optional Perlin noise
|
| 60 |
if add_noise:
|
| 61 |
height, width = img.shape
|
| 62 |
noise_map = np.zeros((height, width), dtype=np.float32)
|
|
|
|
| 66 |
img = cv2.add(img, noise_map.astype(np.uint8))
|
| 67 |
return Image.fromarray(img)
|
| 68 |
|
| 69 |
+
# Advanced Roughness Map with frequency separation and texture analysis
|
| 70 |
+
def generate_roughness_map(image, invert=True, sharpness=1.0, detail_boost=0.5, frequency_weight=0.5):
|
| 71 |
img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
|
| 72 |
+
# Frequency separation: Low-frequency (smooth) and high-frequency (detail)
|
| 73 |
+
low_freq = cv2.bilateralFilter(img, 9, 75, 75)
|
| 74 |
+
high_freq = cv2.subtract(img, low_freq)
|
| 75 |
+
# Combine with weight
|
| 76 |
+
img = cv2.addWeighted(low_freq, 1.0 - frequency_weight, high_freq, frequency_weight, 0)
|
| 77 |
# Optional inversion
|
| 78 |
if invert:
|
| 79 |
img = 255 - img
|
| 80 |
+
# Sharpening and detail boost
|
| 81 |
blurred = cv2.GaussianBlur(img, (5, 5), 0)
|
| 82 |
img = cv2.addWeighted(img, 1.0 + sharpness, blurred, -sharpness, 0)
|
|
|
|
| 83 |
img = cv2.addWeighted(img, 1.0 + detail_boost, blurred, -detail_boost, 0)
|
| 84 |
return Image.fromarray(img)
|
| 85 |
|
| 86 |
+
# Process function with advanced parameters
|
| 87 |
+
def process_image(input_image, normal_strength=1.0, normal_blur=5, normal_bilateral=False, normal_color=0.3,
|
| 88 |
+
disp_contrast=1.0, disp_noise=False, disp_noise_scale=0.1, disp_edge=1.0,
|
| 89 |
+
rough_invert=True, rough_sharpness=1.0, rough_detail=0.5, rough_freq=0.5):
|
| 90 |
+
normal_map = generate_normal_map(input_image, strength=normal_strength, blur_size=normal_blur,
|
| 91 |
+
use_bilateral=normal_bilateral, color_influence=normal_color)
|
| 92 |
+
displacement_map = generate_displacement_map(input_image, contrast=disp_contrast, add_noise=disp_noise,
|
| 93 |
+
noise_scale=disp_noise_scale, edge_boost=disp_edge)
|
| 94 |
+
roughness_map = generate_roughness_map(input_image, invert=rough_invert, sharpness=rough_sharpness,
|
| 95 |
+
detail_boost=rough_detail, frequency_weight=rough_freq)
|
| 96 |
return normal_map, displacement_map, roughness_map
|
| 97 |
|
| 98 |
# Gradio Interface with advanced controls
|
|
|
|
| 102 |
gr.Image(type="pil", label="Upload Image"),
|
| 103 |
# Normal Map Controls
|
| 104 |
gr.Slider(minimum=0.1, maximum=5.0, step=0.1, value=1.0, label="Normal Map Strength"),
|
| 105 |
+
gr.Slider(minimum=3, maximum=15, step=2, value=5, label="Normal Map Blur Size (odd numbers)"),
|
| 106 |
gr.Checkbox(value=False, label="Use Bilateral Filter for Normal Map"),
|
| 107 |
+
gr.Slider(minimum=0.0, maximum=1.0, step=0.1, value=0.3, label="Normal Map Color Influence"),
|
| 108 |
# Displacement Map Controls
|
| 109 |
gr.Slider(minimum=0.5, maximum=2.0, step=0.1, value=1.0, label="Displacement Map Contrast"),
|
| 110 |
gr.Checkbox(value=False, label="Add Noise to Displacement Map"),
|
| 111 |
gr.Slider(minimum=0.05, maximum=0.5, step=0.05, value=0.1, label="Displacement Noise Scale"),
|
| 112 |
+
gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1.0, label="Displacement Edge Boost"),
|
| 113 |
# Roughness Map Controls
|
| 114 |
gr.Checkbox(value=True, label="Invert Roughness Map"),
|
| 115 |
gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1.0, label="Roughness Sharpness"),
|
| 116 |
+
gr.Slider(minimum=0.0, maximum=1.0, step=0.1, value=0.5, label="Roughness Detail Boost"),
|
| 117 |
+
gr.Slider(minimum=0.0, maximum=1.0, step=0.1, value=0.5, label="Roughness Frequency Weight")
|
| 118 |
],
|
| 119 |
outputs=[
|
| 120 |
gr.Image(type="pil", label="Normal Map"),
|
|
|
|
| 122 |
gr.Image(type="pil", label="Roughness Map")
|
| 123 |
],
|
| 124 |
title="Advanced Material Map Generator",
|
| 125 |
+
description="Upload an image to generate highly accurate Normal, Displacement, and Roughness maps with advanced controls."
|
| 126 |
)
|
| 127 |
|
| 128 |
interface.launch()
|