Update app.py
Browse files
app.py
CHANGED
|
@@ -15,6 +15,7 @@ import re
|
|
| 15 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 16 |
torch_dtype = torch.float16 if device == "cuda" else torch.float32
|
| 17 |
IMG_SIZE = 512
|
|
|
|
| 18 |
|
| 19 |
print(f"Running on: {device}")
|
| 20 |
|
|
@@ -104,6 +105,15 @@ def auto_negative_prompt(positive_prompt):
|
|
| 104 |
else:
|
| 105 |
return base_negatives
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
# === GESICHTSMASKEN-FUNKTIONEN (ERWEITERT FÜR 3 MODI) ===
|
| 108 |
def create_face_mask(image, bbox_coords, mode):
|
| 109 |
"""
|
|
@@ -121,44 +131,54 @@ def create_face_mask(image, bbox_coords, mode):
|
|
| 121 |
mask = Image.new("L", image.size, 0) # Start mit komplett schwarzer Maske (alles geschützt)
|
| 122 |
|
| 123 |
if bbox_coords and all(coord is not None for coord in bbox_coords):
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
draw = ImageDraw.Draw(mask)
|
| 126 |
|
| 127 |
if mode == "environment_change":
|
| 128 |
# MODUS 1: Umgebung ändern (Depth + Canny)
|
| 129 |
-
# Maske: Alles weiß AUSSER
|
| 130 |
draw.rectangle([0, 0, image.size[0], image.size[1]], fill=255) # Alles weiß = verändern
|
| 131 |
-
draw.rectangle([x1, y1, x2, y2], fill=0) #
|
| 132 |
-
print("🎯 MODUS: Umgebung ändern - Alles außer
|
| 133 |
|
| 134 |
elif mode == "focus_change":
|
| 135 |
# MODUS 2: Focus verändern (OpenPose + Canny)
|
| 136 |
# Maske: Nur innerhalb der Box weiß (Rest schwarz)
|
| 137 |
draw.rectangle([x1, y1, x2, y2], fill=255) # Nur Box weiß = verändern
|
| 138 |
-
print("🎯 MODUS: Focus verändern - Nur innerhalb der
|
| 139 |
|
| 140 |
elif mode == "face_only_change":
|
| 141 |
# MODUS 3: Ausschließlich Gesicht (Depth + Canny)
|
| 142 |
# Maske: Nur innerhalb der Box weiß (Rest schwarz) - wie focus_change
|
| 143 |
draw.rectangle([x1, y1, x2, y2], fill=255) # Nur Box weiß = verändern
|
| 144 |
-
print("🎯 MODUS: Ausschließlich Gesicht - Nur
|
| 145 |
|
| 146 |
return mask
|
| 147 |
|
| 148 |
def auto_detect_face_area(image):
|
| 149 |
"""Optimierten Vorschlag für Gesichtsbereich ohne externe Bibliotheken"""
|
| 150 |
width, height = image.size
|
| 151 |
-
# Größere Bounding Box für bessere Abdeckung (50% statt 40%)
|
| 152 |
face_size = min(width, height) * 0.4
|
| 153 |
-
# Verschiebe y1 nach oben, um Stirn und Kinn besser abzudecken
|
| 154 |
x1 = (width - face_size) / 2
|
| 155 |
-
y1 = (height - face_size) / 4
|
| 156 |
x2 = x1 + face_size
|
| 157 |
-
y2 = y1 + face_size * 1.2
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
return [x1, y1, x2, y2]
|
| 163 |
|
| 164 |
# === PIPELINES ===
|
|
@@ -403,19 +423,19 @@ def create_preview_image(image, bbox_coords, mode):
|
|
| 403 |
# Farben basierend auf Modus
|
| 404 |
if mode == "environment_change":
|
| 405 |
border_color = (0, 255, 0, 180) # Grün für Umgebung
|
| 406 |
-
mode_text = "UMGEBUNG ÄNDERN (
|
| 407 |
-
box_color = (255, 255, 0, 200) # Gelb für
|
| 408 |
text_bg_color = (0, 128, 0, 160) # Dunkelgrün
|
| 409 |
|
| 410 |
elif mode == "focus_change":
|
| 411 |
border_color = (255, 165, 0, 180) # Orange für Focus
|
| 412 |
-
mode_text = "FOCUS VERÄNDERN (
|
| 413 |
box_color = (255, 0, 0, 200) # Rot für Veränderungsbereich
|
| 414 |
text_bg_color = (255, 140, 0, 160) # Dunkelorange
|
| 415 |
|
| 416 |
elif mode == "face_only_change":
|
| 417 |
border_color = (255, 0, 0, 180) # Rot für nur Gesicht
|
| 418 |
-
mode_text = "NUR
|
| 419 |
box_color = (255, 0, 0, 200) # Rot für Veränderungsbereich
|
| 420 |
text_bg_color = (128, 0, 0, 160) # Dunkelrot
|
| 421 |
else:
|
|
@@ -425,27 +445,36 @@ def create_preview_image(image, bbox_coords, mode):
|
|
| 425 |
box_color = (128, 128, 128, 200)
|
| 426 |
text_bg_color = (64, 64, 64, 160)
|
| 427 |
|
| 428 |
-
#
|
| 429 |
-
border_width = 8
|
| 430 |
draw.rectangle([0, 0, preview.width-1, preview.height-1],
|
| 431 |
outline=border_color, width=border_width)
|
| 432 |
|
| 433 |
if bbox_coords and all(coord is not None for coord in bbox_coords):
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
# Bounding Box zeichnen
|
| 437 |
-
draw.rectangle([x1, y1, x2, y2], outline=box_color, width=3)
|
| 438 |
|
| 439 |
-
#
|
| 440 |
-
|
|
|
|
|
|
|
|
|
|
| 441 |
|
| 442 |
-
#
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
|
| 450 |
return preview
|
| 451 |
|
|
@@ -457,7 +486,8 @@ def update_live_preview(image, bbox_x1, bbox_y1, bbox_x2, bbox_y2, mode):
|
|
| 457 |
if image is None:
|
| 458 |
return None
|
| 459 |
|
| 460 |
-
|
|
|
|
| 461 |
|
| 462 |
return create_preview_image(image, bbox_coords, mode)
|
| 463 |
|
|
@@ -466,18 +496,48 @@ def process_image_upload(image):
|
|
| 466 |
if image is None:
|
| 467 |
return None, None, None, None, None
|
| 468 |
|
| 469 |
-
|
| 470 |
-
image = image.resize((512, 512), Image.LANCZOS)
|
| 471 |
-
print(f"Bild auf 512x512 skaliert")
|
| 472 |
|
|
|
|
| 473 |
bbox = auto_detect_face_area(image)
|
| 474 |
-
bbox_x1, bbox_y1, bbox_x2, bbox_y2 = bbox
|
| 475 |
|
| 476 |
-
#
|
| 477 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 478 |
|
| 479 |
return preview, bbox_x1, bbox_y1, bbox_x2, bbox_y2
|
| 480 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
# === HAUPTFUNKTIONEN (ANGEPASST FÜR 3 MODI) ===
|
| 482 |
def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress()):
|
| 483 |
try:
|
|
@@ -693,23 +753,34 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 693 |
|
| 694 |
# ===== MASKE ERSTELLEN (BASIEREND AUF MODUS) =====
|
| 695 |
mask = None
|
| 696 |
-
if bbox_x1 and bbox_y1 and bbox_x2 and bbox_y2:
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
bbox_coords = [
|
| 700 |
-
int(bbox_x1 * scale_x),
|
| 701 |
-
int(bbox_y1 * scale_y),
|
| 702 |
-
int(bbox_x2 * scale_x),
|
| 703 |
-
int(bbox_y2 * scale_y)
|
| 704 |
-
]
|
| 705 |
-
print(f"📐 Skalierte Koordinaten: {bbox_coords}")
|
| 706 |
|
| 707 |
-
#
|
| 708 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 709 |
if mask:
|
| 710 |
print(f"✅ Maske erstellt für Modus: {mode}")
|
| 711 |
-
else:
|
| 712 |
-
print("⚠️ Keine gültigen Koordinaten – keine Maske")
|
| 713 |
|
| 714 |
from diffusers import EulerAncestralDiscreteScheduler
|
| 715 |
if not isinstance(pipe.scheduler, EulerAncestralDiscreteScheduler):
|
|
@@ -1022,39 +1093,40 @@ def main_ui():
|
|
| 1022 |
gr.Markdown("""
|
| 1023 |
<div style="font-size: 12px; color: #666; margin-top: 10px;">
|
| 1024 |
<strong>Modus-Erklärungen:</strong><br>
|
| 1025 |
-
• <strong>🌳 Umgebung ändern:</strong> Ändert alles AUSSER dem
|
| 1026 |
-
• <strong>🎯 Focus verändern:</strong> Ändert
|
| 1027 |
-
• <strong>👤 Ausschließlich Gesicht:</strong> Ändert NUR
|
| 1028 |
</div>
|
| 1029 |
""")
|
| 1030 |
|
| 1031 |
with gr.Row():
|
| 1032 |
gr.Markdown("### 📐 Bildelementbereich anpassen")
|
| 1033 |
|
|
|
|
| 1034 |
with gr.Row():
|
| 1035 |
with gr.Column():
|
| 1036 |
bbox_x1 = gr.Slider(
|
| 1037 |
label="← Links (x1)",
|
| 1038 |
-
minimum=0, maximum=
|
| 1039 |
info="Linke Kante des Bildelementbereichs"
|
| 1040 |
)
|
| 1041 |
with gr.Column():
|
| 1042 |
bbox_y1 = gr.Slider(
|
| 1043 |
label="↑ Oben (y1)",
|
| 1044 |
-
minimum=0, maximum=
|
| 1045 |
info="Obere Kante des Bildelementbereichs"
|
| 1046 |
)
|
| 1047 |
with gr.Row():
|
| 1048 |
with gr.Column():
|
| 1049 |
bbox_x2 = gr.Slider(
|
| 1050 |
label="→ Rechts (x2)",
|
| 1051 |
-
minimum=0, maximum=
|
| 1052 |
info="Rechte Kante des Bildelementbereichs"
|
| 1053 |
)
|
| 1054 |
with gr.Column():
|
| 1055 |
bbox_y2 = gr.Slider(
|
| 1056 |
label="↓ Unten (y2)",
|
| 1057 |
-
minimum=0, maximum=
|
| 1058 |
info="Untere Kante des Bildelementbereichs"
|
| 1059 |
)
|
| 1060 |
|
|
@@ -1098,27 +1170,34 @@ def main_ui():
|
|
| 1098 |
gr.Markdown(
|
| 1099 |
"### 📋 Hinweise:\n"
|
| 1100 |
"• **🆕 3 Transformations-Modi** für präzise Kontrolle\n"
|
|
|
|
| 1101 |
"• **🆕 Automatische Bildelementerkennung** setzt Koordinaten beim Upload\n"
|
| 1102 |
"• **🆕 Live-Vorschau** zeigt farbige Rahmen je nach Modus\n"
|
| 1103 |
-
"• **🆕 Koordinaten-Schieberegler**
|
| 1104 |
"• **ControlNet-Technologie** für konsistente Ergebnisse\n"
|
| 1105 |
-
"• **Automatische Negative Prompts** für bessere Qualität"
|
|
|
|
| 1106 |
)
|
| 1107 |
|
| 1108 |
transform_btn = gr.Button("🔄 Bild transformieren", variant="primary")
|
| 1109 |
|
| 1110 |
with gr.Row():
|
| 1111 |
img_output = gr.Image(
|
| 1112 |
-
label="✨ Transformiertes Bild",
|
| 1113 |
show_download_button=True,
|
| 1114 |
type="pil",
|
| 1115 |
height=400
|
| 1116 |
)
|
| 1117 |
|
|
|
|
| 1118 |
img_input.change(
|
| 1119 |
fn=process_image_upload,
|
| 1120 |
inputs=[img_input],
|
| 1121 |
outputs=[preview_output, bbox_x1, bbox_y1, bbox_x2, bbox_y2]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1122 |
)
|
| 1123 |
|
| 1124 |
# NEUE Input-Liste mit mode_radio statt face_preserve
|
|
@@ -1159,7 +1238,7 @@ if __name__ == "__main__":
|
|
| 1159 |
demo.launch(
|
| 1160 |
server_name="0.0.0.0",
|
| 1161 |
server_port=7860,
|
| 1162 |
-
max_file_size="
|
| 1163 |
show_error=True,
|
| 1164 |
share=False,
|
| 1165 |
ssr_mode=False # SSR deaktivieren für Stabilität
|
|
|
|
| 15 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 16 |
torch_dtype = torch.float16 if device == "cuda" else torch.float32
|
| 17 |
IMG_SIZE = 512
|
| 18 |
+
MAX_IMAGE_SIZE = 4096 # Maximale Bildgröße für Verarbeitung
|
| 19 |
|
| 20 |
print(f"Running on: {device}")
|
| 21 |
|
|
|
|
| 105 |
else:
|
| 106 |
return base_negatives
|
| 107 |
|
| 108 |
+
# === HILFSFUNKTION: KOORDINATEN SORTIEREN ===
|
| 109 |
+
def sort_coordinates(x1, y1, x2, y2):
|
| 110 |
+
"""Sortiert Koordinaten, so dass x1 <= x2 und y1 <= y2"""
|
| 111 |
+
sorted_x1 = min(x1, x2)
|
| 112 |
+
sorted_x2 = max(x1, x2)
|
| 113 |
+
sorted_y1 = min(y1, y2)
|
| 114 |
+
sorted_y2 = max(y1, y2)
|
| 115 |
+
return sorted_x1, sorted_y1, sorted_x2, sorted_y2
|
| 116 |
+
|
| 117 |
# === GESICHTSMASKEN-FUNKTIONEN (ERWEITERT FÜR 3 MODI) ===
|
| 118 |
def create_face_mask(image, bbox_coords, mode):
|
| 119 |
"""
|
|
|
|
| 131 |
mask = Image.new("L", image.size, 0) # Start mit komplett schwarzer Maske (alles geschützt)
|
| 132 |
|
| 133 |
if bbox_coords and all(coord is not None for coord in bbox_coords):
|
| 134 |
+
# Sortiere Koordinaten
|
| 135 |
+
x1, y1, x2, y2 = sort_coordinates(*bbox_coords)
|
| 136 |
+
|
| 137 |
+
# Stelle sicher, dass Koordinaten innerhalb des Bildes liegen
|
| 138 |
+
x1 = max(0, min(x1, image.width-1))
|
| 139 |
+
y1 = max(0, min(y1, image.height-1))
|
| 140 |
+
x2 = max(0, min(x2, image.width-1))
|
| 141 |
+
y2 = max(0, min(y2, image.height-1))
|
| 142 |
+
|
| 143 |
draw = ImageDraw.Draw(mask)
|
| 144 |
|
| 145 |
if mode == "environment_change":
|
| 146 |
# MODUS 1: Umgebung ändern (Depth + Canny)
|
| 147 |
+
# Maske: Alles weiß AUSSER Bereich (schwarz)
|
| 148 |
draw.rectangle([0, 0, image.size[0], image.size[1]], fill=255) # Alles weiß = verändern
|
| 149 |
+
draw.rectangle([x1, y1, x2, y2], fill=0) # Bereich schwarz = geschützt (rechteckig)
|
| 150 |
+
print(f"🎯 MODUS: Umgebung ändern - Alles außer BBox wird verändert (BBox: {x1},{y1},{x2},{y2})")
|
| 151 |
|
| 152 |
elif mode == "focus_change":
|
| 153 |
# MODUS 2: Focus verändern (OpenPose + Canny)
|
| 154 |
# Maske: Nur innerhalb der Box weiß (Rest schwarz)
|
| 155 |
draw.rectangle([x1, y1, x2, y2], fill=255) # Nur Box weiß = verändern
|
| 156 |
+
print(f"🎯 MODUS: Focus verändern - Nur innerhalb der BBox wird verändert (BBox: {x1},{y1},{x2},{y2})")
|
| 157 |
|
| 158 |
elif mode == "face_only_change":
|
| 159 |
# MODUS 3: Ausschließlich Gesicht (Depth + Canny)
|
| 160 |
# Maske: Nur innerhalb der Box weiß (Rest schwarz) - wie focus_change
|
| 161 |
draw.rectangle([x1, y1, x2, y2], fill=255) # Nur Box weiß = verändern
|
| 162 |
+
print(f"🎯 MODUS: Ausschließlich Gesicht - Nur innerhalb der BBox wird verändert (BBox: {x1},{y1},{x2},{y2})")
|
| 163 |
|
| 164 |
return mask
|
| 165 |
|
| 166 |
def auto_detect_face_area(image):
|
| 167 |
"""Optimierten Vorschlag für Gesichtsbereich ohne externe Bibliotheken"""
|
| 168 |
width, height = image.size
|
|
|
|
| 169 |
face_size = min(width, height) * 0.4
|
|
|
|
| 170 |
x1 = (width - face_size) / 2
|
| 171 |
+
y1 = (height - face_size) / 4
|
| 172 |
x2 = x1 + face_size
|
| 173 |
+
y2 = y1 + face_size * 1.2
|
| 174 |
+
|
| 175 |
+
# Sortiere Koordinaten und stelle sicher, dass sie innerhalb des Bildes liegen
|
| 176 |
+
x1 = max(0, int(min(x1, x2)))
|
| 177 |
+
y1 = max(0, int(min(y1, y2)))
|
| 178 |
+
x2 = min(width, int(max(x1, x2)))
|
| 179 |
+
y2 = min(height, int(max(y1, y2)))
|
| 180 |
+
|
| 181 |
+
print(f"Geschätzte Gesichtskoordinaten: [{x1}, {y1}, {x2}, {y2}] (Bild: {width}x{height})")
|
| 182 |
return [x1, y1, x2, y2]
|
| 183 |
|
| 184 |
# === PIPELINES ===
|
|
|
|
| 423 |
# Farben basierend auf Modus
|
| 424 |
if mode == "environment_change":
|
| 425 |
border_color = (0, 255, 0, 180) # Grün für Umgebung
|
| 426 |
+
mode_text = "UMGEBUNG ÄNDERN (Bereich geschützt)"
|
| 427 |
+
box_color = (255, 255, 0, 200) # Gelb für geschützten Bereich
|
| 428 |
text_bg_color = (0, 128, 0, 160) # Dunkelgrün
|
| 429 |
|
| 430 |
elif mode == "focus_change":
|
| 431 |
border_color = (255, 165, 0, 180) # Orange für Focus
|
| 432 |
+
mode_text = "FOCUS VERÄNDERN (Bereich+Körper)"
|
| 433 |
box_color = (255, 0, 0, 200) # Rot für Veränderungsbereich
|
| 434 |
text_bg_color = (255, 140, 0, 160) # Dunkelorange
|
| 435 |
|
| 436 |
elif mode == "face_only_change":
|
| 437 |
border_color = (255, 0, 0, 180) # Rot für nur Gesicht
|
| 438 |
+
mode_text = "NUR BEREICH VERÄNDERN"
|
| 439 |
box_color = (255, 0, 0, 200) # Rot für Veränderungsbereich
|
| 440 |
text_bg_color = (128, 0, 0, 160) # Dunkelrot
|
| 441 |
else:
|
|
|
|
| 445 |
box_color = (128, 128, 128, 200)
|
| 446 |
text_bg_color = (64, 64, 64, 160)
|
| 447 |
|
| 448 |
+
# Skaliere Rahmendicke basierend auf Bildgröße
|
| 449 |
+
border_width = max(8, image.width // 200) # Mindestens 8px, bei großen Bildern dicker
|
| 450 |
draw.rectangle([0, 0, preview.width-1, preview.height-1],
|
| 451 |
outline=border_color, width=border_width)
|
| 452 |
|
| 453 |
if bbox_coords and all(coord is not None for coord in bbox_coords):
|
| 454 |
+
# Sortiere Koordinaten
|
| 455 |
+
x1, y1, x2, y2 = sort_coordinates(*bbox_coords)
|
|
|
|
|
|
|
| 456 |
|
| 457 |
+
# Stelle sicher, dass die Koordinaten innerhalb des Bildes liegen
|
| 458 |
+
x1 = max(0, min(x1, preview.width-1))
|
| 459 |
+
y1 = max(0, min(y1, preview.height-1))
|
| 460 |
+
x2 = max(0, min(x2, preview.width-1))
|
| 461 |
+
y2 = max(0, min(y2, preview.height-1))
|
| 462 |
|
| 463 |
+
# Nur zeichnen, wenn die Bounding Box gültig ist
|
| 464 |
+
if x2 > x1 and y2 > y1:
|
| 465 |
+
# Skaliere Box-Rahmen basierend auf Bildgröße
|
| 466 |
+
box_width = max(3, image.width // 400)
|
| 467 |
+
draw.rectangle([x1, y1, x2, y2], outline=box_color, width=box_width)
|
| 468 |
+
|
| 469 |
+
text_color = (255, 255, 255)
|
| 470 |
+
|
| 471 |
+
# Text über der Bounding Box platzieren
|
| 472 |
+
text_y = max(0, y1 - 25)
|
| 473 |
+
text_bbox = draw.textbbox((x1, text_y), mode_text)
|
| 474 |
+
draw.rectangle([text_bbox[0]-5, text_bbox[1]-2, text_bbox[2]+5, text_bbox[3]+2],
|
| 475 |
+
fill=text_bg_color)
|
| 476 |
+
|
| 477 |
+
draw.text((x1, text_y), mode_text, fill=text_color)
|
| 478 |
|
| 479 |
return preview
|
| 480 |
|
|
|
|
| 486 |
if image is None:
|
| 487 |
return None
|
| 488 |
|
| 489 |
+
# Sortiere die Koordinaten (Slider zeigen Originalkoordinaten)
|
| 490 |
+
bbox_coords = sort_coordinates(bbox_x1, bbox_y1, bbox_x2, bbox_y2)
|
| 491 |
|
| 492 |
return create_preview_image(image, bbox_coords, mode)
|
| 493 |
|
|
|
|
| 496 |
if image is None:
|
| 497 |
return None, None, None, None, None
|
| 498 |
|
| 499 |
+
width, height = image.size
|
|
|
|
|
|
|
| 500 |
|
| 501 |
+
# Berechne Bounding-Box basierend auf der tatsächlichen Bildgröße
|
| 502 |
bbox = auto_detect_face_area(image)
|
|
|
|
| 503 |
|
| 504 |
+
# Sortiere die Koordinaten
|
| 505 |
+
bbox_x1, bbox_y1, bbox_x2, bbox_y2 = sort_coordinates(*bbox)
|
| 506 |
+
|
| 507 |
+
# Für die Vorschau verwende die Originalkoordinaten
|
| 508 |
+
preview = create_preview_image(image, [bbox_x1, bbox_y1, bbox_x2, bbox_y2], "environment_change")
|
| 509 |
+
|
| 510 |
+
# Slider-Werte SIND JETZT ORIGINALKOORDINATEN (keine 512-Skalierung!)
|
| 511 |
+
print(f"Bild {width}x{height} -> Slider-Originalwerte: [{bbox_x1}, {bbox_y1}, {bbox_x2}, {bbox_y2}]")
|
| 512 |
|
| 513 |
return preview, bbox_x1, bbox_y1, bbox_x2, bbox_y2
|
| 514 |
|
| 515 |
+
# === FUNKTION FÜR SLIDER-UPDATE ===
|
| 516 |
+
def update_slider_for_image(image):
|
| 517 |
+
"""Aktualisiert Slider-Maxima basierend auf Bildgröße bis 4096x4096"""
|
| 518 |
+
if image is None:
|
| 519 |
+
return (
|
| 520 |
+
gr.update(maximum=MAX_IMAGE_SIZE),
|
| 521 |
+
gr.update(maximum=MAX_IMAGE_SIZE),
|
| 522 |
+
gr.update(maximum=MAX_IMAGE_SIZE),
|
| 523 |
+
gr.update(maximum=MAX_IMAGE_SIZE)
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
width, height = image.size
|
| 527 |
+
|
| 528 |
+
# Setze Slider-Maxima auf Bildgröße (begrenzt auf MAX_IMAGE_SIZE für Stabilität)
|
| 529 |
+
max_width = min(width, MAX_IMAGE_SIZE)
|
| 530 |
+
max_height = min(height, MAX_IMAGE_SIZE)
|
| 531 |
+
|
| 532 |
+
print(f"Slider-Maxima gesetzt auf: {max_width}x{max_height}")
|
| 533 |
+
|
| 534 |
+
return (
|
| 535 |
+
gr.update(maximum=max_width),
|
| 536 |
+
gr.update(maximum=max_height),
|
| 537 |
+
gr.update(maximum=max_width),
|
| 538 |
+
gr.update(maximum=max_height)
|
| 539 |
+
)
|
| 540 |
+
|
| 541 |
# === HAUPTFUNKTIONEN (ANGEPASST FÜR 3 MODI) ===
|
| 542 |
def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress()):
|
| 543 |
try:
|
|
|
|
| 753 |
|
| 754 |
# ===== MASKE ERSTELLEN (BASIEREND AUF MODUS) =====
|
| 755 |
mask = None
|
| 756 |
+
if bbox_x1 is not None and bbox_y1 is not None and bbox_x2 is not None and bbox_y2 is not None:
|
| 757 |
+
# Skaliere Slider-Werte (Original-Bildgröße) auf 512x512 für die Pipeline
|
| 758 |
+
width, height = image.size
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 759 |
|
| 760 |
+
# Skalierungsfaktoren berechnen
|
| 761 |
+
scale_x = IMG_SIZE / width
|
| 762 |
+
scale_y = IMG_SIZE / height
|
| 763 |
+
|
| 764 |
+
# Skaliere Bounding-Box-Koordinaten auf 512x512
|
| 765 |
+
pipeline_x1 = int(bbox_x1 * scale_x)
|
| 766 |
+
pipeline_y1 = int(bbox_y1 * scale_y)
|
| 767 |
+
pipeline_x2 = int(bbox_x2 * scale_x)
|
| 768 |
+
pipeline_y2 = int(bbox_y2 * scale_y)
|
| 769 |
+
|
| 770 |
+
# Sortiere und begrenze die Koordinaten
|
| 771 |
+
pipeline_bbox = sort_coordinates(
|
| 772 |
+
max(0, min(pipeline_x1, IMG_SIZE-1)),
|
| 773 |
+
max(0, min(pipeline_y1, IMG_SIZE-1)),
|
| 774 |
+
max(0, min(pipeline_x2, IMG_SIZE-1)),
|
| 775 |
+
max(0, min(pipeline_y2, IMG_SIZE-1))
|
| 776 |
+
)
|
| 777 |
+
|
| 778 |
+
print(f"Original BBox: [{bbox_x1}, {bbox_y1}, {bbox_x2}, {bbox_y2}] -> Pipeline BBox: {pipeline_bbox}")
|
| 779 |
+
|
| 780 |
+
# Erstelle Maske basierend auf skalierten Koordinaten
|
| 781 |
+
mask = create_face_mask(img_resized, pipeline_bbox, mode)
|
| 782 |
if mask:
|
| 783 |
print(f"✅ Maske erstellt für Modus: {mode}")
|
|
|
|
|
|
|
| 784 |
|
| 785 |
from diffusers import EulerAncestralDiscreteScheduler
|
| 786 |
if not isinstance(pipe.scheduler, EulerAncestralDiscreteScheduler):
|
|
|
|
| 1093 |
gr.Markdown("""
|
| 1094 |
<div style="font-size: 12px; color: #666; margin-top: 10px;">
|
| 1095 |
<strong>Modus-Erklärungen:</strong><br>
|
| 1096 |
+
• <strong>🌳 Umgebung ändern:</strong> Ändert alles AUSSER dem markierten Bereich (Depth+Canny)<br>
|
| 1097 |
+
• <strong>🎯 Focus verändern:</strong> Ändert markierten Bereich+Körper (OpenPose+Canny)<br>
|
| 1098 |
+
• <strong>👤 Ausschließlich Gesicht:</strong> Ändert NUR den markierten Bereich (Depth+Canny)
|
| 1099 |
</div>
|
| 1100 |
""")
|
| 1101 |
|
| 1102 |
with gr.Row():
|
| 1103 |
gr.Markdown("### 📐 Bildelementbereich anpassen")
|
| 1104 |
|
| 1105 |
+
# SLIDER MIT DYNAMISCHEM MAXIMUM (4096 für große Bilder)
|
| 1106 |
with gr.Row():
|
| 1107 |
with gr.Column():
|
| 1108 |
bbox_x1 = gr.Slider(
|
| 1109 |
label="← Links (x1)",
|
| 1110 |
+
minimum=0, maximum=MAX_IMAGE_SIZE, value=100, step=1,
|
| 1111 |
info="Linke Kante des Bildelementbereichs"
|
| 1112 |
)
|
| 1113 |
with gr.Column():
|
| 1114 |
bbox_y1 = gr.Slider(
|
| 1115 |
label="↑ Oben (y1)",
|
| 1116 |
+
minimum=0, maximum=MAX_IMAGE_SIZE, value=100, step=1,
|
| 1117 |
info="Obere Kante des Bildelementbereichs"
|
| 1118 |
)
|
| 1119 |
with gr.Row():
|
| 1120 |
with gr.Column():
|
| 1121 |
bbox_x2 = gr.Slider(
|
| 1122 |
label="→ Rechts (x2)",
|
| 1123 |
+
minimum=0, maximum=MAX_IMAGE_SIZE, value=300, step=1,
|
| 1124 |
info="Rechte Kante des Bildelementbereichs"
|
| 1125 |
)
|
| 1126 |
with gr.Column():
|
| 1127 |
bbox_y2 = gr.Slider(
|
| 1128 |
label="↓ Unten (y2)",
|
| 1129 |
+
minimum=0, maximum=MAX_IMAGE_SIZE, value=300, step=1,
|
| 1130 |
info="Untere Kante des Bildelementbereichs"
|
| 1131 |
)
|
| 1132 |
|
|
|
|
| 1170 |
gr.Markdown(
|
| 1171 |
"### 📋 Hinweise:\n"
|
| 1172 |
"• **🆕 3 Transformations-Modi** für präzise Kontrolle\n"
|
| 1173 |
+
"• **🆕 Unterstützt Bilder bis 4096×4096 Pixel**\n"
|
| 1174 |
"• **🆕 Automatische Bildelementerkennung** setzt Koordinaten beim Upload\n"
|
| 1175 |
"• **🆕 Live-Vorschau** zeigt farbige Rahmen je nach Modus\n"
|
| 1176 |
+
"• **🆕 Dynamische Koordinaten-Schieberegler** passen sich an Bildgröße an\n"
|
| 1177 |
"• **ControlNet-Technologie** für konsistente Ergebnisse\n"
|
| 1178 |
+
"• **Automatische Negative Prompts** für bessere Qualität\n"
|
| 1179 |
+
"• **Ausgabe immer 512×512 Pixel** für beste Kontrolle"
|
| 1180 |
)
|
| 1181 |
|
| 1182 |
transform_btn = gr.Button("🔄 Bild transformieren", variant="primary")
|
| 1183 |
|
| 1184 |
with gr.Row():
|
| 1185 |
img_output = gr.Image(
|
| 1186 |
+
label="✨ Transformiertes Bild (512×512 - SD-Technologie-Limit)",
|
| 1187 |
show_download_button=True,
|
| 1188 |
type="pil",
|
| 1189 |
height=400
|
| 1190 |
)
|
| 1191 |
|
| 1192 |
+
# EVENT-HANDLER FÜR DYNAMISCHE BILDGRÖßEN
|
| 1193 |
img_input.change(
|
| 1194 |
fn=process_image_upload,
|
| 1195 |
inputs=[img_input],
|
| 1196 |
outputs=[preview_output, bbox_x1, bbox_y1, bbox_x2, bbox_y2]
|
| 1197 |
+
).then(
|
| 1198 |
+
fn=update_slider_for_image,
|
| 1199 |
+
inputs=[img_input],
|
| 1200 |
+
outputs=[bbox_x1, bbox_y1, bbox_x2, bbox_y2]
|
| 1201 |
)
|
| 1202 |
|
| 1203 |
# NEUE Input-Liste mit mode_radio statt face_preserve
|
|
|
|
| 1238 |
demo.launch(
|
| 1239 |
server_name="0.0.0.0",
|
| 1240 |
server_port=7860,
|
| 1241 |
+
max_file_size="15MB",
|
| 1242 |
show_error=True,
|
| 1243 |
share=False,
|
| 1244 |
ssr_mode=False # SSR deaktivieren für Stabilität
|