Update app.py
Browse files
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 |
-
#
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 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 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
interface = gr.Interface(
|
| 65 |
-
fn=
|
| 66 |
-
inputs=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
outputs=[
|
| 68 |
-
gr.Image(type="pil", label="
|
| 69 |
-
gr.Image(type="pil", label="
|
| 70 |
-
gr.Image(type="pil", label="
|
| 71 |
],
|
| 72 |
-
title="Material Map Generator",
|
| 73 |
-
description="Upload an image to generate
|
| 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()
|