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