Astridkraft commited on
Commit
7408fc3
·
verified ·
1 Parent(s): 4f1f9a7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -65
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
- x1, y1, x2, y2 = bbox_coords
 
 
 
 
 
 
 
 
125
  draw = ImageDraw.Draw(mask)
126
 
127
  if mode == "environment_change":
128
  # MODUS 1: Umgebung ändern (Depth + Canny)
129
- # Maske: Alles weiß AUSSER Gesicht (schwarz)
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) # Gesicht schwarz = geschützt (rechteckig)
132
- print("🎯 MODUS: Umgebung ändern - Alles außer Gesicht wird verändert")
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 Box wird verändert")
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 Gesicht wird verändert")
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 # Höher positioniert (25% statt 33%)
156
  x2 = x1 + face_size
157
- y2 = y1 + face_size * 1.2 # Leicht länglicher für ovale Gesichter
158
- # Stelle sicher, dass Koordinaten innerhalb des Bildes liegen
159
- x1, y1 = max(0, int(x1)), max(0, int(y1))
160
- x2, y2 = min(width, int(x2)), min(height, int(y2))
161
- print(f"Geschätzte Gesichtskoordinaten: [{x1}, {y1}, {x2}, {y2}]")
 
 
 
 
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 (Gesicht geschützt)"
407
- box_color = (255, 255, 0, 200) # Gelb für geschütztes Gesicht
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 (Gesicht+Körper)"
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 GESICHT VERÄNDERN"
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
- # Rahmen um das gesamte Bild
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
- x1, y1, x2, y2 = bbox_coords
435
-
436
- # Bounding Box zeichnen
437
- draw.rectangle([x1, y1, x2, y2], outline=box_color, width=3)
438
 
439
- # Modus-Text anzeigen
440
- text_color = (255, 255, 255)
 
 
 
441
 
442
- # Text-Hintergrund zeichnen
443
- text_bbox = draw.textbbox((x1, y1 - 25), mode_text)
444
- draw.rectangle([text_bbox[0]-5, text_bbox[1]-2, text_bbox[2]+5, text_bbox[3]+2],
445
- fill=text_bg_color)
446
-
447
- # Text zeichnen
448
- draw.text((x1, y1 - 25), mode_text, fill=text_color)
 
 
 
 
 
 
 
 
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
- bbox_coords = [bbox_x1, bbox_y1, bbox_x2, bbox_y2]
 
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
- if image.size != (512, 512):
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
- # Standardmodus: "environment_change" (Umgebung ändern)
477
- preview = create_preview_image(image, bbox, "environment_change")
 
 
 
 
 
 
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
- orig_w, orig_h = image.size
698
- scale_x, scale_y = 512 / orig_w, 512 / orig_h
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
- # NEU: Modus-spezifische Maskenerstellung
708
- mask = create_face_mask(img_resized, bbox_coords, mode)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 Gesicht (Depth+Canny)<br>
1026
- • <strong>🎯 Focus verändern:</strong> Ändert Gesicht+Körper (OpenPose+Canny)<br>
1027
- • <strong>👤 Ausschließlich Gesicht:</strong> Ändert NUR das Gesicht (Depth+Canny)
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=512, value=100, step=1,
1039
  info="Linke Kante des Bildelementbereichs"
1040
  )
1041
  with gr.Column():
1042
  bbox_y1 = gr.Slider(
1043
  label="↑ Oben (y1)",
1044
- minimum=0, maximum=512, value=100, step=1,
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=512, value=300, step=1,
1052
  info="Rechte Kante des Bildelementbereichs"
1053
  )
1054
  with gr.Column():
1055
  bbox_y2 = gr.Slider(
1056
  label="↓ Unten (y2)",
1057
- minimum=0, maximum=512, value=300, step=1,
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** für präzise Anpassung mit Live-Update\n"
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="10MB",
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