Astridkraft commited on
Commit
105bc86
·
verified ·
1 Parent(s): f749287

Update controlnet_module.py

Browse files
Files changed (1) hide show
  1. controlnet_module.py +101 -14
controlnet_module.py CHANGED
@@ -104,8 +104,8 @@ class ControlNetProcessor:
104
 
105
  def create_sam_mask(self, image, bbox_coords, mode):
106
  """
107
- Erstellt präzise Maske mit SAM 2 und Nachbearbeitung
108
- Gibt PIL Image in L-Modus zurück (0=schwarz=erhalten, 255=weiß=verändern)
109
  """
110
  try:
111
  print("#" * 80)
@@ -127,10 +127,62 @@ class ControlNetProcessor:
127
 
128
  # 2. Validiere BBox
129
  x1, y1, x2, y2 = self._validate_bbox(image, bbox_coords)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  print("-" * 60)
131
- print(f"📦 BOUNDING BOX DETAILS:")
132
- print(f" Ursprüngliche Koordinaten: {bbox_coords}")
133
- print(f" Validierte Koordinaten: [{x1}, {y1}, {x2}, {y2}]")
134
  print(f" BBox Dimensionen: {x2-x1}px × {y2-y1}px")
135
 
136
  # 3. Vorbereitung für SAM2
@@ -174,7 +226,8 @@ class ControlNetProcessor:
174
  print(f" Nach Sigmoid und CPU: {mask_np.shape}, Wertebereich: [{mask_np.min():.3f}, {mask_np.max():.3f}]")
175
 
176
  mask_array = (mask_np > 0.5).astype(np.uint8) * 255
177
- print(f" Nach Threshold (0.5): {mask_array.shape}, Unique Werte: {np.unique(mask_array)}")
 
178
 
179
  # 7. BEIDE MASKEN ERSTELLEN (vor Nachbearbeitung)
180
  original_mask_array = mask_array.copy() # Person weiß (255), Hintergrund schwarz (0)
@@ -253,21 +306,28 @@ class ControlNetProcessor:
253
  print(" ✅ Focus-Modus: Person verändert, Hintergrund geschützt")
254
 
255
  elif mode == "face_only_change":
256
- print("👤 MODUS: NUR GESICHT ÄNDERN")
257
  # Arbeite auf der ORIGINAL-Maske (Person weiß, Hintergrund schwarz)
258
  mask_array = original_mask_array.copy()
259
  print(" Arbeite auf originaler Maske (Person weiß, Hintergrund schwarz)")
260
 
261
  # Größte weiße Komponente behalten (Person)
262
  labeled_array, num_features = ndimage.label(mask_array)
263
- print(f" Gefundene weiße Komponenten (Person): {num_features}")
264
 
265
- if num_features > 1:
266
  sizes = ndimage.sum(mask_array, labeled_array, range(1, num_features + 1))
267
- print(f" Größen der weißen Komponenten: {sizes}")
268
- largest_component = np.argmax(sizes) + 1
269
- mask_array = np.where(labeled_array == largest_component, mask_array, 0)
270
- print(f" ✅ Behalte größte Person-Komponente ({num_features} Komponenten)")
 
 
 
 
 
 
 
271
 
272
  # Starke Erosion für präzises Gesicht
273
  kernel = np.ones((3,3), np.uint8)
@@ -282,7 +342,33 @@ class ControlNetProcessor:
282
  print(f" Wende GaussianBlur an (Kernel 3x3) für glatte Kanten...")
283
  mask_array = cv2.GaussianBlur(mask_array, (3, 3), 0)
284
 
285
- print(" ✅ Gesichts-Modus: Nur Gesicht verändert")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  # 9. Qualitätskontrolle und Statistik
288
  white_pixels = np.sum(mask_array > 127)
@@ -303,6 +389,7 @@ class ControlNetProcessor:
303
  print("#" * 80)
304
  print(f"✅ SAM 2 SEGMENTIERUNG ABGESCHLOSSEN")
305
  print(f"📐 Finale Maskengröße: {mask.size}")
 
306
  print("#" * 80)
307
  return mask
308
 
 
104
 
105
  def create_sam_mask(self, image, bbox_coords, mode):
106
  """
107
+ ERWEITERTE Funktion: Erstellt präzise Maske mit SAM 2
108
+ Sonderbehandlung für face_only_change: Arbeitet auf Bildausschnitt
109
  """
110
  try:
111
  print("#" * 80)
 
127
 
128
  # 2. Validiere BBox
129
  x1, y1, x2, y2 = self._validate_bbox(image, bbox_coords)
130
+
131
+ # ============================================================
132
+ # SPEZIALBEHANDLUNG NUR FÜR face_only_change
133
+ # ============================================================
134
+ if mode == "face_only_change":
135
+ print("-" * 60)
136
+ print("👤 SPEZIALMODUS: NUR GESICHT - ERSTELLE FOKUSIERTEN AUSSCHNITT")
137
+ print("-" * 60)
138
+
139
+ # Originalbild und Koordinaten sichern
140
+ original_image = image
141
+ original_bbox = (x1, y1, x2, y2)
142
+
143
+ # Puffer um die BBox berechnen (20% der BBox-Größe, mindestens 50px)
144
+ padding_x = max(50, int((x2 - x1) * 0.2))
145
+ padding_y = max(50, int((y2 - y1) * 0.2))
146
+
147
+ # Ausschnitt-Koordinaten berechnen (innerhalb der Bildgrenzen)
148
+ crop_x1 = max(0, x1 - padding_x)
149
+ crop_y1 = max(0, y1 - padding_y)
150
+ crop_x2 = min(image.width, x2 + padding_x)
151
+ crop_y2 = min(image.height, y2 + padding_y)
152
+
153
+ print(f" 📐 Original-BBox: [{x1}, {y1}, {x2}, {y2}]")
154
+ print(f" 📏 Original-BBox Größe: {x2-x1} × {y2-y1} px")
155
+ print(f" 🔲 Ausschnitt-Bereich: [{crop_x1}, {crop_y1}, {crop_x2}, {crop_y2}]")
156
+ print(f" 📏 Ausschnitt-Größe: {crop_x2-crop_x1} × {crop_y2-crop_y1} px")
157
+ print(f" 📊 Puffer: {padding_x} × {padding_y} px")
158
+
159
+ # Bild ausschneiden
160
+ cropped_image = image.crop((crop_x1, crop_y1, crop_x2, crop_y2))
161
+ print(f" ✅ Ausschnitt erstellt: {cropped_image.size}")
162
+
163
+ # BBox-Koordinaten relativ zum Ausschnitt neu berechnen
164
+ rel_x1 = x1 - crop_x1
165
+ rel_y1 = y1 - crop_y1
166
+ rel_x2 = x2 - crop_x1
167
+ rel_y2 = y2 - crop_y1
168
+
169
+ print(f" 🎯 Relative BBox im Ausschnitt: [{rel_x1}, {rel_y1}, {rel_x2}, {rel_y2}]")
170
+ print(f" 📏 Relative BBox Größe: {rel_x2-rel_x1} × {rel_y2-rel_y1} px")
171
+
172
+ # Für SAM: Verwende Ausschnitt und relative Koordinaten
173
+ image = cropped_image
174
+ x1, y1, x2, y2 = rel_x1, rel_y1, rel_x2, rel_y2
175
+
176
+ print(" 🔄 SAM wird auf Ausschnitt (nicht Vollbild) ausgeführt")
177
+
178
+ # ============================================================
179
+ # GEMEINSAME SAM-LOGIK FÜR ALLE MODI
180
+ # (arbeitet auf `image` - bei face_only_change ist das der Ausschnitt)
181
+ # ============================================================
182
  print("-" * 60)
183
+ print(f"📦 BOUNDING BOX DETAILS FÜR SAM:")
184
+ print(f" Bild-Größe für SAM: {image.size}")
185
+ print(f" BBox Koordinaten: [{x1}, {y1}, {x2}, {y2}]")
186
  print(f" BBox Dimensionen: {x2-x1}px × {y2-y1}px")
187
 
188
  # 3. Vorbereitung für SAM2
 
226
  print(f" Nach Sigmoid und CPU: {mask_np.shape}, Wertebereich: [{mask_np.min():.3f}, {mask_np.max():.3f}]")
227
 
228
  mask_array = (mask_np > 0.5).astype(np.uint8) * 255
229
+ unique_vals = np.unique(mask_array)
230
+ print(f" Nach Threshold (0.5): {mask_array.shape}, Unique Werte: {unique_vals}")
231
 
232
  # 7. BEIDE MASKEN ERSTELLEN (vor Nachbearbeitung)
233
  original_mask_array = mask_array.copy() # Person weiß (255), Hintergrund schwarz (0)
 
306
  print(" ✅ Focus-Modus: Person verändert, Hintergrund geschützt")
307
 
308
  elif mode == "face_only_change":
309
+ print("👤 MODUS: NUR GESICHT ÄNDERN (AUF AUSSCHNITT)")
310
  # Arbeite auf der ORIGINAL-Maske (Person weiß, Hintergrund schwarz)
311
  mask_array = original_mask_array.copy()
312
  print(" Arbeite auf originaler Maske (Person weiß, Hintergrund schwarz)")
313
 
314
  # Größte weiße Komponente behalten (Person)
315
  labeled_array, num_features = ndimage.label(mask_array)
316
+ print(f" Gefundene weiße Komponenten auf AUSSCHNITT: {num_features}")
317
 
318
+ if num_features > 0:
319
  sizes = ndimage.sum(mask_array, labeled_array, range(1, num_features + 1))
320
+ print(f" Größen der weißen Komponenten auf AUSSCHNITT: {sizes}")
321
+
322
+ if num_features > 1:
323
+ # WICHTIG: Für Gesicht nehmen wir die GRÖSSTE Komponente im AUSSCHNITT
324
+ # (Im Ausschnitt sollte das das Gesicht sein, nicht der Hintergrund)
325
+ largest_component = np.argmax(sizes) + 1
326
+ mask_array = np.where(labeled_array == largest_component, mask_array, 0)
327
+ print(f" ✅ Behalte größte Komponente im Ausschnitt ({num_features} Komponenten)")
328
+ print(f" 📊 Größe der behaltenen Komponente: {sizes[largest_component-1]:,} Pixel")
329
+ else:
330
+ print(f" ℹ️ Nur eine Komponente gefunden, behalte diese")
331
 
332
  # Starke Erosion für präzises Gesicht
333
  kernel = np.ones((3,3), np.uint8)
 
342
  print(f" Wende GaussianBlur an (Kernel 3x3) für glatte Kanten...")
343
  mask_array = cv2.GaussianBlur(mask_array, (3, 3), 0)
344
 
345
+ print(" ✅ Gesichts-Modus: Nachbearbeitung auf Ausschnitt abgeschlossen")
346
+
347
+ # ============================================================
348
+ # SPEZIALSCHRITT NUR FÜR face_only_change: MASKE ZURÜCKSKALIEREN
349
+ # ============================================================
350
+ print("-" * 60)
351
+ print("🔄 SPEZIALSCHRITT: MASKE VOM AUSSCHNITT ZURÜCK AUF ORIGINALGRÖSSE")
352
+
353
+ # Temporäre Maske aus dem Array erstellen
354
+ temp_mask = Image.fromarray(mask_array).convert("L")
355
+ print(f" Maskengröße auf Ausschnitt: {temp_mask.size}")
356
+
357
+ # Leere Maske in Originalbild-Größe erstellen
358
+ final_mask = Image.new("L", original_image.size, 0)
359
+ print(f" Leere Maske in Originalgröße: {final_mask.size}")
360
+
361
+ # Die segmentierte Maske an der richtigen Position im Originalbild platzieren
362
+ final_mask.paste(temp_mask, (crop_x1, crop_y1))
363
+ print(f" Maskenposition im Original: ({crop_x1}, {crop_y1})")
364
+
365
+ # Zurück zum mask_array konvertieren
366
+ mask_array = np.array(final_mask)
367
+ print(f" ✅ Maske zurück auf Originalgröße skaliert: {mask_array.shape}")
368
+
369
+ # Originalbild wiederherstellen für eventuelle spätere Verwendung
370
+ image = original_image
371
+ print(f" 🔄 Bild-Referenz wieder auf Original gesetzt: {image.size}")
372
 
373
  # 9. Qualitätskontrolle und Statistik
374
  white_pixels = np.sum(mask_array > 127)
 
389
  print("#" * 80)
390
  print(f"✅ SAM 2 SEGMENTIERUNG ABGESCHLOSSEN")
391
  print(f"📐 Finale Maskengröße: {mask.size}")
392
+ print(f"🎛️ Verwendeter Modus: {mode}")
393
  print("#" * 80)
394
  return mask
395