Yankkee's picture
Upload 3 files
58b2866 verified
Raw
History Blame Contribute Delete
6.7 kB
"""
Depth ControlNet Logo Generator
--------------------------------
- Nimmt ein hochgeladenes Logo/Referenzbild
- Berechnet daraus eine Depth Map (Midas)
- Generiert per Stable Diffusion 1.5 + ControlNet-Depth ein neues,
strukturell ähnliches Bild nach Text-Prompt
- Läuft auf HuggingFace ZeroGPU (dynamisch zugewiesene GPU pro Request)
"""
import spaces
import gradio as gr
import numpy as np
import torch
from PIL import Image
from diffusers import (
StableDiffusionControlNetPipeline,
ControlNetModel,
UniPCMultistepScheduler,
)
from transformers import pipeline as hf_pipeline
# ---------------------------------------------------------------------------
# Konfiguration
# ---------------------------------------------------------------------------
SD_MODEL_ID = "runwayml/stable-diffusion-v1-5"
CONTROLNET_ID = "lllyasviel/sd-controlnet-depth"
DEPTH_MODEL_ID = "Intel/dpt-hybrid-midas" # gleicher Preprocessor wie beim ControlNet-Training
RESOLUTION = 512 # native SD1.5 Auflösung
device = "cuda"
dtype = torch.float16
# ---------------------------------------------------------------------------
# Modelle laden (einmalig beim Space-Start)
# Bei ZeroGPU: .to("cuda") hier ist ok, tatsächliche GPU wird erst bei
# @spaces.GPU-Aufrufen zugewiesen.
# ---------------------------------------------------------------------------
print("Lade Depth-Estimator ...")
depth_estimator = hf_pipeline("depth-estimation", model=DEPTH_MODEL_ID)
print("Lade ControlNet + Stable Diffusion Pipeline ...")
controlnet = ControlNetModel.from_pretrained(CONTROLNET_ID, torch_dtype=dtype)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
SD_MODEL_ID,
controlnet=controlnet,
torch_dtype=dtype,
safety_checker=None,
)
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
pipe = pipe.to(device)
try:
pipe.enable_xformers_memory_efficient_attention()
except Exception:
pass
# ---------------------------------------------------------------------------
# Hilfsfunktionen
# ---------------------------------------------------------------------------
def preprocess_image(image: Image.Image, resolution: int = RESOLUTION) -> Image.Image:
"""Logo auf quadratische Zielauflösung bringen."""
image = image.convert("RGB")
image = image.resize((resolution, resolution), Image.LANCZOS)
return image
def get_depth_map(image: Image.Image) -> Image.Image:
"""Depth Map aus dem Logo berechnen (das ist das ControlNet-Kontrollbild)."""
depth = depth_estimator(image)["depth"]
depth = np.array(depth)
depth = depth[:, :, None]
depth = np.concatenate([depth, depth, depth], axis=2)
return Image.fromarray(depth)
# ---------------------------------------------------------------------------
# Haupt-Generierungsfunktion (läuft auf der ZeroGPU-Instanz)
# ---------------------------------------------------------------------------
@spaces.GPU(duration=60)
def generate(
logo_image,
prompt,
negative_prompt,
controlnet_scale,
num_steps,
guidance_scale,
seed,
progress=gr.Progress(track_tqdm=True),
):
if logo_image is None:
raise gr.Error("Bitte zuerst ein Logo-/Referenzbild hochladen.")
if not prompt or prompt.strip() == "":
raise gr.Error("Bitte einen Prompt eingeben.")
logo_image = preprocess_image(logo_image)
depth_image = get_depth_map(logo_image)
seed = int(seed)
if seed < 0:
generator = None # zufälliger Seed
else:
generator = torch.Generator(device=device).manual_seed(seed)
result = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=depth_image,
num_inference_steps=int(num_steps),
guidance_scale=float(guidance_scale),
controlnet_conditioning_scale=float(controlnet_scale),
generator=generator,
).images[0]
return result, depth_image
# ---------------------------------------------------------------------------
# Gradio UI
# ---------------------------------------------------------------------------
with gr.Blocks(title="Depth ControlNet Logo Generator") as demo:
gr.Markdown(
"""
# 🎨 Depth ControlNet — Logo Generator
Lade ein bestehendes Logo hoch, gib einen neuen Stil-Prompt ein.
Die **Tiefenstruktur / Silhouette** des Logos bleibt erhalten,
während Stil, Farben und Textur komplett neu generiert werden.
"""
)
with gr.Row():
with gr.Column():
logo_input = gr.Image(
label="1️⃣ Logo / Referenzbild hochladen",
type="pil",
height=300,
)
prompt = gr.Textbox(
label="2️⃣ Prompt",
placeholder="z.B. vintage japanese emblem logo, ink brush style, minimal, black and red, flat vector, white background",
lines=3,
)
negative_prompt = gr.Textbox(
label="Negative Prompt",
value="blurry, low quality, watermark, text, extra elements, photo, 3d render",
lines=2,
)
with gr.Accordion("⚙️ Erweiterte Einstellungen", open=False):
controlnet_scale = gr.Slider(
0.0, 2.0, value=1.0, step=0.05,
label="ControlNet Conditioning Scale (Struktur-Treue)",
)
num_steps = gr.Slider(10, 50, value=25, step=1, label="Inference Steps")
guidance_scale = gr.Slider(1.0, 20.0, value=7.5, step=0.5, label="Guidance Scale")
seed = gr.Slider(-1, 999999, value=-1, step=1, label="Seed (-1 = zufällig)")
run_button = gr.Button("🚀 Generieren", variant="primary")
with gr.Column():
output_image = gr.Image(label="Ergebnis", height=400)
depth_preview = gr.Image(label="Erkannte Depth Map (Kontrollbild)", height=200)
run_button.click(
fn=generate,
inputs=[
logo_input,
prompt,
negative_prompt,
controlnet_scale,
num_steps,
guidance_scale,
seed,
],
outputs=[output_image, depth_preview],
)
gr.Markdown(
"""
---
💡 **Tipps:**
- Höherer *ControlNet Conditioning Scale* = Form/Struktur des Original-Logos wird strenger befolgt.
- Niedrigerer Wert = mehr kreative Freiheit, weniger Ähnlichkeit zur Vorlage.
- Für saubere Vektor-Optik: Begriffe wie "flat vector", "clean lines", "white background" im Prompt verwenden.
"""
)
demo.queue(max_size=20)
demo.launch()