File size: 4,658 Bytes
fdae55a 65a3cf4 fdae55a e6cea74 fdae55a e6cea74 fdae55a e6cea74 fdae55a 65a3cf4 fdae55a 65a3cf4 fdae55a 65a3cf4 fdae55a 65a3cf4 fdae55a 65a3cf4 fdae55a 6254392 fdae55a 6254392 fdae55a 65a3cf4 fdae55a 65a3cf4 fdae55a 0475f02 fdae55a 0475f02 fdae55a 0475f02 fdae55a 0475f02 65a3cf4 0475f02 fdae55a 0475f02 fdae55a 65a3cf4 fdae55a 0475f02 fdae55a 0475f02 65a3cf4 0475f02 fdae55a 0475f02 fdae55a 65a3cf4 fdae55a | 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 | import gradio as gr
import cv2
import numpy as np
from PIL import Image
import os
import zipfile
import tempfile
import shutil
def process_image(img, strength, upscale=False):
if upscale:
new_size = (img.width * 2, img.height * 2)
img = img.resize(new_size, resample=Image.LANCZOS)
img_cv = np.array(img.convert('L'))
img_cv = img_cv.astype(np.float32) / 255.0
sobelx = cv2.Sobel(img_cv, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img_cv, cv2.CV_64F, 0, 1, ksize=3)
nx = -sobelx * strength
ny = -sobely * strength
nz = np.ones_like(img_cv)
length = np.sqrt(nx**2 + ny**2 + nz**2)
nx /= length
ny /= length
nz /= length
nx = (nx + 1) / 2 * 255
ny = (ny + 1) / 2 * 255
nz = (nz + 1) / 2 * 255
normal_map = np.stack((nx, ny, nz), axis=-1).astype(np.uint8)
return Image.fromarray(normal_map)
def process_single(image, strength, upscale):
if image is None:
return None
return process_image(image, strength, upscale)
def process_files(file_objs, strength, upscale):
if not file_objs:
return None
# Create temp directory and ZIP file
temp_dir = tempfile.mkdtemp()
zip_filename = "normal_maps.zip"
zip_path = os.path.join(tempfile.gettempdir(), zip_filename)
with zipfile.ZipFile(zip_path, 'w') as zipf:
for file_obj in file_objs:
file_path = file_obj.name if hasattr(file_obj, 'name') else file_obj
file_name = os.path.basename(file_path)
if file_name.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.webp')):
try:
img = Image.open(file_path)
normal_map = process_image(img, strength, upscale)
base_name, ext = os.path.splitext(file_name)
out_name = f"{base_name}_normalmap{ext}"
out_path = os.path.join(temp_dir, out_name)
try:
normal_map.save(out_path)
except:
out_name = f"{base_name}_normalmap.png"
out_path = os.path.join(temp_dir, out_name)
normal_map.save(out_path, format="PNG")
zipf.write(out_path, arcname=out_name)
except Exception as e:
print(f"Error ({file_path}): {e}")
# Cleanup temp directory
shutil.rmtree(temp_dir)
return zip_path
# Gradio Interface
with gr.Blocks(title="Normal Map Generator", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🗺️ Normal Map Generator")
gr.Markdown("Create normal maps from standard images for 3D modeling and texturing.")
with gr.Tabs():
# SINGLE IMAGE TAB
with gr.Tab("Single Image"):
with gr.Row():
with gr.Column():
img_input = gr.Image(type="pil", label="Input Image")
strength_slider_1 = gr.Slider(minimum=0.1, maximum=10.0, value=2.0, step=0.1, label="Normal Map Strength")
upscale_check_1 = gr.Checkbox(label="Upscale 2x (High Quality Smoothing)", value=False)
btn_single = gr.Button("Generate", variant="primary")
with gr.Column():
img_output = gr.Image(type="pil", label="Normal Map Result", format="png")
btn_single.click(fn=process_single, inputs=[img_input, strength_slider_1, upscale_check_1], outputs=img_output)
# FOLDER/BATCH TAB
with gr.Tab("Folder (Batch Processing)"):
gr.Markdown("Select a folder or drag and drop multiple images. All images will be processed and provided as a ZIP file.")
with gr.Row():
with gr.Column():
# file_count="directory" allows folder upload
folder_input = gr.File(file_count="directory", label="Upload Folder / Files")
strength_slider_2 = gr.Slider(minimum=0.1, maximum=10.0, value=2.0, step=0.1, label="Normal Map Strength")
upscale_check_2 = gr.Checkbox(label="Upscale 2x (High Quality Smoothing)", value=False)
btn_folder = gr.Button("Generate Batch and Download", variant="primary")
with gr.Column():
file_output = gr.File(label="Download ZIP File")
btn_folder.click(fn=process_files, inputs=[folder_input, strength_slider_2, upscale_check_2], outputs=file_output)
if __name__ == "__main__":
demo.launch() |