Update app.py
Browse files
app.py
CHANGED
|
@@ -56,7 +56,7 @@ def auto_negative_prompt(positive_prompt):
|
|
| 56 |
if any(w in p for w in [
|
| 57 |
"person", "man", "woman", "face", "portrait", "team", "employee",
|
| 58 |
"people", "crowd", "character", "figure", "human", "child", "baby",
|
| 59 |
-
"girl", "boy", "lady", "gentleman", "fairy", "elf", "dwarf", "santa claus"
|
| 60 |
"mermaid", "angel", "demon", "witch", "wizard", "creature", "being",
|
| 61 |
"model", "actor", "actress", "celebrity", "avatar", "group"]):
|
| 62 |
negatives.append(
|
|
@@ -336,9 +336,10 @@ def load_txt2img(model_id):
|
|
| 336 |
raise
|
| 337 |
|
| 338 |
def load_img2img():
|
|
|
|
| 339 |
global pipe_img2img
|
| 340 |
if pipe_img2img is None:
|
| 341 |
-
print("
|
| 342 |
try:
|
| 343 |
pipe_img2img = StableDiffusionInpaintPipeline.from_pretrained(
|
| 344 |
"runwayml/stable-diffusion-inpainting",
|
|
@@ -346,22 +347,26 @@ def load_img2img():
|
|
| 346 |
allow_pickle=False,
|
| 347 |
safety_checker=None,
|
| 348 |
).to(device)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
except Exception as e:
|
| 350 |
-
print(f"Fehler beim Laden des Inpainting-Modells: {e}")
|
| 351 |
raise
|
| 352 |
|
| 353 |
-
from diffusers import DPMSolverMultistepScheduler
|
| 354 |
-
pipe_img2img.scheduler = DPMSolverMultistepScheduler.from_config(
|
| 355 |
-
pipe_img2img.scheduler.config,
|
| 356 |
-
algorithm_type="sde-dpmsolver++",
|
| 357 |
-
use_karras_sigmas=True,
|
| 358 |
-
timestep_spacing="trailing"
|
| 359 |
-
)
|
| 360 |
-
|
| 361 |
pipe_img2img.enable_attention_slicing()
|
| 362 |
pipe_img2img.enable_vae_tiling()
|
| 363 |
if hasattr(pipe_img2img, 'vae_slicing'):
|
| 364 |
pipe_img2img.vae_slicing = True
|
|
|
|
| 365 |
|
| 366 |
return pipe_img2img
|
| 367 |
|
|
@@ -767,47 +772,36 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 767 |
|
| 768 |
pipe = load_img2img()
|
| 769 |
|
| 770 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 771 |
|
| 772 |
adj_guidance = min(guidance_scale, 12.0)
|
| 773 |
seed = random.randint(0, 2**32 - 1)
|
| 774 |
generator = torch.Generator(device=device).manual_seed(seed)
|
| 775 |
print(f"🌱 Inpaint Seed: {seed}")
|
| 776 |
|
| 777 |
-
# =====
|
| 778 |
mask = None
|
| 779 |
if bbox_x1 is not None and bbox_y1 is not None and bbox_x2 is not None and bbox_y2 is not None:
|
| 780 |
-
#
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
# Skalierungsfaktoren berechnen
|
| 784 |
-
scale_x = IMG_SIZE / width
|
| 785 |
-
scale_y = IMG_SIZE / height
|
| 786 |
-
|
| 787 |
-
# Skaliere Bounding-Box-Koordinaten auf 512x512
|
| 788 |
-
pipeline_x1 = int(bbox_x1 * scale_x)
|
| 789 |
-
pipeline_y1 = int(bbox_y1 * scale_y)
|
| 790 |
-
pipeline_x2 = int(bbox_x2 * scale_x)
|
| 791 |
-
pipeline_y2 = int(bbox_y2 * scale_y)
|
| 792 |
-
|
| 793 |
-
# Sortiere und begrenze die Koordinaten
|
| 794 |
-
pipeline_bbox = sort_coordinates(
|
| 795 |
-
max(0, min(pipeline_x1, IMG_SIZE-1)),
|
| 796 |
-
max(0, min(pipeline_y1, IMG_SIZE-1)),
|
| 797 |
-
max(0, min(pipeline_x2, IMG_SIZE-1)),
|
| 798 |
-
max(0, min(pipeline_y2, IMG_SIZE-1))
|
| 799 |
-
)
|
| 800 |
|
| 801 |
-
|
|
|
|
|
|
|
| 802 |
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
print(f"✅ Maske erstellt für Modus: {mode}")
|
| 807 |
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
| 811 |
|
| 812 |
callback = ImageToImageProgressCallback(progress, int(steps), adj_strength)
|
| 813 |
|
|
@@ -830,6 +824,7 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 830 |
print(f"✅ Transformation abgeschlossen in {duration:.2f} Sekunden")
|
| 831 |
print(f"🎯 Verwendeter Modus: {mode}")
|
| 832 |
print(f"⚙️ ControlNet: {'Depth+Canny' if keep_environment else 'OpenPose+Canny'}")
|
|
|
|
| 833 |
|
| 834 |
generated_image = result.images[0]
|
| 835 |
return generated_image
|
|
@@ -856,7 +851,7 @@ def update_model_settings(model_id):
|
|
| 856 |
return (
|
| 857 |
config["recommended_steps"], # steps
|
| 858 |
config["recommended_cfg"], # guidance_scale
|
| 859 |
-
f"📊 Empfohlene Einstellungen: {config['
|
| 860 |
)
|
| 861 |
|
| 862 |
def main_ui():
|
|
|
|
| 56 |
if any(w in p for w in [
|
| 57 |
"person", "man", "woman", "face", "portrait", "team", "employee",
|
| 58 |
"people", "crowd", "character", "figure", "human", "child", "baby",
|
| 59 |
+
"girl", "boy", "lady", "gentleman", "fairy", "elf", "dwarf", "santa claus",
|
| 60 |
"mermaid", "angel", "demon", "witch", "wizard", "creature", "being",
|
| 61 |
"model", "actor", "actress", "celebrity", "avatar", "group"]):
|
| 62 |
negatives.append(
|
|
|
|
| 336 |
raise
|
| 337 |
|
| 338 |
def load_img2img():
|
| 339 |
+
"""Lädt das Inpainting-Modell mit DPMSolver++ Scheduler"""
|
| 340 |
global pipe_img2img
|
| 341 |
if pipe_img2img is None:
|
| 342 |
+
print("🔄 Lade Inpainting-Modell...")
|
| 343 |
try:
|
| 344 |
pipe_img2img = StableDiffusionInpaintPipeline.from_pretrained(
|
| 345 |
"runwayml/stable-diffusion-inpainting",
|
|
|
|
| 347 |
allow_pickle=False,
|
| 348 |
safety_checker=None,
|
| 349 |
).to(device)
|
| 350 |
+
|
| 351 |
+
# WICHTIG: Behalte DPMSolver++ Scheduler bei (beste Qualität für Inpainting)
|
| 352 |
+
pipe_img2img.scheduler = DPMSolverMultistepScheduler.from_config(
|
| 353 |
+
pipe_img2img.scheduler.config,
|
| 354 |
+
algorithm_type="sde-dpmsolver++",
|
| 355 |
+
use_karras_sigmas=True,
|
| 356 |
+
timestep_spacing="trailing"
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
print("✅ DPMSolver++ Multistep Scheduler für Inpainting konfiguriert")
|
| 360 |
+
|
| 361 |
except Exception as e:
|
| 362 |
+
print(f"❌ Fehler beim Laden des Inpainting-Modells: {e}")
|
| 363 |
raise
|
| 364 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
pipe_img2img.enable_attention_slicing()
|
| 366 |
pipe_img2img.enable_vae_tiling()
|
| 367 |
if hasattr(pipe_img2img, 'vae_slicing'):
|
| 368 |
pipe_img2img.vae_slicing = True
|
| 369 |
+
print("✅ Inpainting-Modell geladen und optimiert")
|
| 370 |
|
| 371 |
return pipe_img2img
|
| 372 |
|
|
|
|
| 772 |
|
| 773 |
pipe = load_img2img()
|
| 774 |
|
| 775 |
+
# ===== RICHTIGE BILD-SKALIERUNG =====
|
| 776 |
+
# Prüfe ob ControlNet schon 512×512 liefert
|
| 777 |
+
if inpaint_input.size != (512, 512):
|
| 778 |
+
img_resized = inpaint_input.convert("RGB").resize((512, 512), Image.Resampling.LANCZOS)
|
| 779 |
+
print("🔄 ControlNet Output von {} auf 512×512 skaliert".format(inpaint_input.size))
|
| 780 |
+
else:
|
| 781 |
+
img_resized = inpaint_input.convert("RGB")
|
| 782 |
+
print("✅ ControlNet Output ist bereits 512×512")
|
| 783 |
|
| 784 |
adj_guidance = min(guidance_scale, 12.0)
|
| 785 |
seed = random.randint(0, 2**32 - 1)
|
| 786 |
generator = torch.Generator(device=device).manual_seed(seed)
|
| 787 |
print(f"🌱 Inpaint Seed: {seed}")
|
| 788 |
|
| 789 |
+
# ===== RICHTIGE MASKEN-ERSTELLUNG (FIX: KEINE DOPPELTE SKALIERUNG) =====
|
| 790 |
mask = None
|
| 791 |
if bbox_x1 is not None and bbox_y1 is not None and bbox_x2 is not None and bbox_y2 is not None:
|
| 792 |
+
# 1. Maske AUF ORIGINAL-BILD erstellen (korrekte Proportionen)
|
| 793 |
+
original_mask = create_face_mask(image, (bbox_x1, bbox_y1, bbox_x2, bbox_y2), mode)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 794 |
|
| 795 |
+
# 2. Maske auf 512×512 skalieren (gleicher Algorithmus wie Bild)
|
| 796 |
+
# WICHTIG: NEAREST für scharfe Kanten, da Maske binär ist
|
| 797 |
+
mask = original_mask.resize((512, 512), Image.Resampling.NEAREST)
|
| 798 |
|
| 799 |
+
print(f"✅ Maske erstellt für Modus: {mode}")
|
| 800 |
+
print(f" Original BBox: [{bbox_x1}, {bbox_y1}, {bbox_x2}, {bbox_y2}]")
|
| 801 |
+
print(f" Maske skaliert von {original_mask.size} auf {mask.size}")
|
|
|
|
| 802 |
|
| 803 |
+
# WICHTIG: KEINE SCHEDULER-ÄNDERUNG MEHR - DPMSolver++ bleibt aktiv
|
| 804 |
+
print(f"✅ Verwende DPMSolver++ Scheduler: {type(pipe.scheduler).__name__}")
|
|
|
|
| 805 |
|
| 806 |
callback = ImageToImageProgressCallback(progress, int(steps), adj_strength)
|
| 807 |
|
|
|
|
| 824 |
print(f"✅ Transformation abgeschlossen in {duration:.2f} Sekunden")
|
| 825 |
print(f"🎯 Verwendeter Modus: {mode}")
|
| 826 |
print(f"⚙️ ControlNet: {'Depth+Canny' if keep_environment else 'OpenPose+Canny'}")
|
| 827 |
+
print(f"⚙️ Scheduler: DPMSolver++ (optimal für Inpainting)")
|
| 828 |
|
| 829 |
generated_image = result.images[0]
|
| 830 |
return generated_image
|
|
|
|
| 851 |
return (
|
| 852 |
config["recommended_steps"], # steps
|
| 853 |
config["recommended_cfg"], # guidance_scale
|
| 854 |
+
f"📊 Empfohlene Einstellungen: {config['recommended_steps']} Steps, CFG {config['recommended_cfg']}"
|
| 855 |
)
|
| 856 |
|
| 857 |
def main_ui():
|