Update controlnet_module.py
Browse files- controlnet_module.py +79 -75
controlnet_module.py
CHANGED
|
@@ -293,91 +293,95 @@ class ControlNetProcessor:
|
|
| 293 |
cv2.rectangle(mask_array, (fb_x1, fb_y1), (fb_x2, fb_y2), 0, -1)
|
| 294 |
|
| 295 |
# Damit wird die Rohmaske für die UI-Anzeige gespeichert
|
| 296 |
-
raw_mask_array = mask_array.copy()
|
| 297 |
|
|
|
|
|
|
|
|
|
|
| 298 |
print("🌳 ENVIRONMENT-CHANGE POSTPROCESSING")
|
| 299 |
|
| 300 |
# Konvertierung zu PIL, hochskalieren auf Originalgröße (korrekte Überlagerung mit O-Bild),
|
| 301 |
# Konvertierung NumPy für weitere Verarbeitung da mathematisch korrekter als PIL.
|
| 302 |
-
if image.size != original_image.size:
|
| 303 |
print(f" ⚠️ Bildgröße angepasst: {image.size} → {original_image.size}")
|
| 304 |
-
temp_mask = Image.fromarray(mask_array).convert("L")
|
| 305 |
-
temp_mask = temp_mask.resize(original_image.size, Image.Resampling.NEAREST)
|
| 306 |
-
mask_array = np.array(temp_mask)
|
| 307 |
print(f" ✅ Maske auf Originalgröße skaliert: {mask_array.shape}")
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
print(f"
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
print(" ✅ MORPH_CLOSE für zusammenhängende Umgebung")
|
| 327 |
-
|
| 328 |
-
# DEBUG nach MORPH_CLOSE
|
| 329 |
-
print(f" Nach MORPH_CLOSE - Weiße Pixel: {np.sum(mask_array > 127)}")
|
| 330 |
-
|
| 331 |
-
# Weiche Ränder für bessere Integration der Person
|
| 332 |
-
print("🌈 Erstelle weiche Übergänge...")
|
| 333 |
-
mask_array = cv2.GaussianBlur(mask_array, (9, 9), 2.0) #2.0 bestimmt wie stark die Unschärfe ist
|
| 334 |
-
print(" ✅ Gaussian Blur für weiche Übergänge")
|
| 335 |
-
|
| 336 |
-
# DEBUG nach Gaussian Blur
|
| 337 |
-
print(f" Nach Gaussian Blur - Min/Max: {mask_array.min()}/{mask_array.max()}")
|
| 338 |
-
print(f" Nach Gaussian Blur - dtype: {mask_array.dtype}")
|
| 339 |
-
|
| 340 |
-
# Gamma-Korrektur für präzisere Ränder
|
| 341 |
-
print("🎛️ Wende Gamma-Korrektur an...")
|
| 342 |
-
mask_array = mask_array.astype(np.float32) / 255.0
|
| 343 |
-
print(f" Konvertiert zu Float32: Min={mask_array.min():.3f}, Max={mask_array.max():.3f}")
|
| 344 |
-
|
| 345 |
-
mask_array = np.clip(mask_array, 0.0, 1.0) #begrenzt alle Werte auf 0 und 1
|
| 346 |
-
mask_array = mask_array ** 0.85 # Gamma-Korrektur Werte > 0.5 werden abgedunkelt, <0.5 aufgehellt-erzeugt natürliche Maskenübergänge
|
| 347 |
-
print(f" Nach Gamma 0.85: Min={mask_array.min():.3f}, Max={mask_array.max():.3f}")
|
| 348 |
-
|
| 349 |
-
mask_array = (mask_array * 255).astype(np.uint8)
|
| 350 |
-
print(" ✅ Gamma-Korrektur (0.85) gegen milchige Ränder")
|
| 351 |
-
|
| 352 |
-
# FINALE QUALITÄTSKONTROLLE
|
| 353 |
-
print("-" * 60)
|
| 354 |
-
print("📊 FINALE MASKEN-STATISTIK (ENVIRONMENT_CHANGE)")
|
| 355 |
-
|
| 356 |
-
white_pixels = np.sum(mask_array > 127)
|
| 357 |
-
black_pixels = np.sum(mask_array <= 127)
|
| 358 |
-
total_pixels = mask_array.size
|
| 359 |
-
|
| 360 |
-
white_ratio = white_pixels / total_pixels * 100
|
| 361 |
-
black_ratio = black_pixels / total_pixels * 100
|
| 362 |
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
|
| 367 |
-
#
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
|
| 379 |
# Zurück zu PIL Image
|
| 380 |
-
mask = Image.fromarray(
|
| 381 |
raw_mask = Image.fromarray(raw_mask_array).convert("L")
|
| 382 |
|
| 383 |
print("#" * 80)
|
|
@@ -386,7 +390,7 @@ class ControlNetProcessor:
|
|
| 386 |
print(f"🎛️ Verwendeter Modus: {mode}")
|
| 387 |
print("#" * 80)
|
| 388 |
|
| 389 |
-
return mask, raw_mask # in mask steht die invertierte nachbearbeitete Maske, in raw_mask die Rohmaske. In app.py wird mask immer auf 512 skaliert
|
| 390 |
|
| 391 |
# ============================================================
|
| 392 |
# BLOCK 2: FOCUS_CHANGE
|
|
|
|
| 293 |
cv2.rectangle(mask_array, (fb_x1, fb_y1), (fb_x2, fb_y2), 0, -1)
|
| 294 |
|
| 295 |
# Damit wird die Rohmaske für die UI-Anzeige gespeichert
|
| 296 |
+
raw_mask_array = mask_array.copy()
|
| 297 |
|
| 298 |
+
##########################################################
|
| 299 |
+
# POSTPROCESSING
|
| 300 |
+
##########################################################
|
| 301 |
print("🌳 ENVIRONMENT-CHANGE POSTPROCESSING")
|
| 302 |
|
| 303 |
# Konvertierung zu PIL, hochskalieren auf Originalgröße (korrekte Überlagerung mit O-Bild),
|
| 304 |
# Konvertierung NumPy für weitere Verarbeitung da mathematisch korrekter als PIL.
|
| 305 |
+
if image.size != original_image.size: #Vergleich SAM-Maskengröße und Original-Bildgröße
|
| 306 |
print(f" ⚠️ Bildgröße angepasst: {image.size} → {original_image.size}")
|
| 307 |
+
temp_mask = Image.fromarray(mask_array).convert("L") #wandelt NumPy-Array in PIL-Bild
|
| 308 |
+
temp_mask = temp_mask.resize(original_image.size, Image.Resampling.NEAREST) #skaliert auf Originalgröße
|
| 309 |
+
mask_array = np.array(temp_mask) #np. heißt mache aus PIL-Image wieder numPy-Array
|
| 310 |
print(f" ✅ Maske auf Originalgröße skaliert: {mask_array.shape}")
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
# DILATE auf der weißen Person - daduch wird Person etwas vergrößert
|
| 314 |
+
kernel_dilate = np.ones((5, 5), np.uint8)
|
| 315 |
+
working_mask = cv2.dilate(working_mask, kernel_dilate, iterations=1)
|
| 316 |
+
print(f" ✅ Dilate (5x5) - Person leicht erweitert")
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
# MORPH_CLOSE auf dem schwarzen Hintergrund (feine Löcher)- kleiner Kernel filigrane Heranarbeitung an Person,
|
| 320 |
+
# es werden aber nur kleine Löcher in Umgebung von weiß nach schwarz geändert!
|
| 321 |
+
kernel_close_small = np.ones((3, 3), np.uint8)
|
| 322 |
+
working_mask = cv2.morphologyEx(working_mask, cv2.MORPH_CLOSE, kernel_close_small, iterations=1)
|
| 323 |
+
print(f" ✅ MORPH_CLOSE (3x3) - Feine Löcher im Hintergrund geschlossen")
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
# KONTURENFILTER auf der weißen Person - arbeitet filigraner als MORPH-CLOSE
|
| 327 |
+
# Finde Konturen (nur äußere)
|
| 328 |
+
contours, _ = cv2.findContours(working_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
|
| 330 |
+
if len(contours) > 0:
|
| 331 |
+
# Finde die größte Kontur (sollte die Person sein)
|
| 332 |
+
largest_contour = max(contours, key=cv2.contourArea)
|
| 333 |
+
|
| 334 |
+
# Erstelle eine saubere Maske mit nur der größten Kontur
|
| 335 |
+
clean_mask = np.zeros_like(working_mask)
|
| 336 |
+
cv2.drawContours(clean_mask, [largest_contour], -1, 255, -1)
|
| 337 |
+
|
| 338 |
+
# Optional: Kleine weiße Punkte IN der Person entfernen
|
| 339 |
+
# Dazu invertieren wir temporär, um "Löcher" (schwarze Pixel) in der Person zu finden
|
| 340 |
+
temp_inverted = 255 - clean_mask
|
| 341 |
+
hole_contours, _ = cv2.findContours(temp_inverted, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
|
| 342 |
+
|
| 343 |
+
for hole in hole_contours:
|
| 344 |
+
area = cv2.contourArea(hole)
|
| 345 |
+
if area < 100: # Sehr kleine Löcher füllen
|
| 346 |
+
cv2.drawContours(clean_mask, [hole], -1, 255, -1)
|
| 347 |
+
|
| 348 |
+
working_mask = clean_mask
|
| 349 |
+
print(f" ✅ Konturenfilter - Größte Kontur behalten, {len(contours)-1} kleine entfernt")
|
| 350 |
+
|
| 351 |
+
|
| 352 |
|
| 353 |
+
# Gaussian-BLUR für weiche Kanten
|
| 354 |
+
working_mask = cv2.GaussianBlur(working_mask, (5, 5), 1.2)
|
| 355 |
+
print(f" ✅ Gaussian Blur (5x5, sigma=1.2) für weiche Kanten")
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
# GAMMA-Korrektur für präzisere Ränder
|
| 360 |
+
working_mask_float = working_mask.astype(np.float32) / 255.0
|
| 361 |
+
working_mask_float = np.clip(working_mask_float, 0.0, 1.0)
|
| 362 |
+
working_mask_float = working_mask_float ** 0.85 # Gamma 0.85
|
| 363 |
+
working_mask = (working_mask_float * 255).astype(np.uint8)
|
| 364 |
+
print(f" ✅ Gamma-Korrektur (0.85) gegen milchige Ränder")
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
# Für environment_change: JETZT invertieren
|
| 369 |
+
final_mask = 255 - working_mask
|
| 370 |
+
print(f" ✅ Finale Invertierung für environment_change")
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
# Qualitätskontrolle - Debug
|
| 374 |
+
white_pixels = np.sum(final_mask > 127)
|
| 375 |
+
black_pixels = np.sum(final_mask <= 127)
|
| 376 |
+
total_pixels = final_mask.size
|
| 377 |
+
|
| 378 |
+
print(f" 📊 FINALE MASKE:")
|
| 379 |
+
print(f" • Weiße Pixel (Hintergrund): {white_pixels:,} ({white_pixels/total_pixels*100:.1f}%)")
|
| 380 |
+
print(f" • Schwarze Pixel (Person): {black_pixels:,} ({black_pixels/total_pixels*100:.1f}%)")
|
| 381 |
+
|
| 382 |
|
| 383 |
# Zurück zu PIL Image
|
| 384 |
+
mask = Image.fromarray(final_mask).convert("L")
|
| 385 |
raw_mask = Image.fromarray(raw_mask_array).convert("L")
|
| 386 |
|
| 387 |
print("#" * 80)
|
|
|
|
| 390 |
print(f"🎛️ Verwendeter Modus: {mode}")
|
| 391 |
print("#" * 80)
|
| 392 |
|
| 393 |
+
return mask, raw_mask # in mask steht die invertierte nachbearbeitete Maske, in raw_mask die Rohmaske. In app.py wird mask immer auf 512 skaliert
|
| 394 |
|
| 395 |
# ============================================================
|
| 396 |
# BLOCK 2: FOCUS_CHANGE
|