Astridkraft commited on
Commit
41351c1
·
verified ·
1 Parent(s): fbba1b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -134
app.py CHANGED
@@ -247,12 +247,13 @@ def scale_image_and_mask_together(image, mask, target_size=512, bbox_coords=None
247
  return padded_image, padded_mask, padding_info
248
 
249
 
 
250
  # Composition Workflow nach Ausgabe ControlnetInpaint-Pipeline
251
  def enhanced_composite_with_sam(original_image, inpaint_result, original_mask,
252
  padding_info, bbox_coords, mode):
253
  """
254
- COMPOSITING MIT SAM-MASKEN (MASKE-BASIERT)
255
- Berücksichtigt die präzisen/erweiterten Kanten der SAM-Maske
256
  """
257
  print(f"🎨 Verbessertes Compositing für Modus: {mode}")
258
 
@@ -269,178 +270,118 @@ def enhanced_composite_with_sam(original_image, inpaint_result, original_mask,
269
  # FALL 1: Bild war bereits 512×512 (keine Skalierung)
270
  # ==============================================
271
  if scale_factor == 1.0 and x_offset == 0 and y_offset == 0:
272
- print(f"✅ FALL 1: Bild 512×512 - einfaches Compositing")
273
-
274
- if mode == "environment_change":
275
- # Umgebung ändern: SAM-Maske invertieren (Objekt schützen)
276
- mask_inverted = Image.eval(original_mask, lambda x: 255 - x)
277
- soft_mask = mask_inverted.filter(ImageFilter.GaussianBlur(5))
278
-
279
- original_with_alpha = original_image.copy().convert("RGBA")
280
- original_with_alpha.putalpha(soft_mask)
281
-
282
- final_image = inpaint_result.copy().convert("RGBA")
283
- final_image.paste(original_with_alpha, (0, 0), original_with_alpha)
284
- return final_image.convert("RGB")
285
-
286
- else:
287
- # Focus/Face: Direktes Alpha-Compositing
288
- soft_mask = original_mask.filter(ImageFilter.GaussianBlur(5))
289
-
290
- inpaint_rgba = inpaint_result.convert("RGBA")
291
- mask_alpha = soft_mask.convert("L")
292
- inpaint_rgba.putalpha(mask_alpha)
293
-
294
- original_rgba = original_image.convert("RGBA")
295
-
296
- # Composite
297
- final_image = Image.new("RGBA", original_image.size, (0, 0, 0, 0))
298
- final_image.paste(original_rgba, (0, 0))
299
- final_image.paste(inpaint_rgba, (0, 0), inpaint_rgba)
300
- return final_image.convert("RGB")
301
 
302
  # ==============================================
303
- # FALL 2 & 3: Bild wurde skaliert - MASKE-BASIERTES COMPOSITING
304
  # ==============================================
305
- print(f"🔄 FALL 2/3: Bild skaliert - MASKE-BASIERTES COMPOSITING")
306
 
307
  # 1. PADDING ENTFERNEN von 512×512 Ergebnis
308
  downscaled_result = inpaint_result.crop(
309
  (x_offset, y_offset, x_offset + scaled_width, y_offset + scaled_height)
310
  )
311
 
312
- # 2. AUF ORIGINALGRÖßE SKALIEREN (Bearbeitetes Bild)
313
- new_background = downscaled_result.resize(
314
- (original_width, original_height),
315
- Image.Resampling.LANCZOS
316
- )
317
-
318
- # 3. SAM-MASKE FÜR KOMPOSITING VORBEREITEN
319
- print(f"📐 SAM-Maske Größe: {original_mask.size}")
320
 
321
  if mode == "environment_change":
322
  # ==============================================
323
  # MODUS: UMWELT ÄNDERN (Objekt bleibt original)
 
 
324
  # ==============================================
325
  print("🌳 Modus: Umwelt ändern mit SAM-Maske")
326
 
 
 
 
 
 
 
 
 
 
327
  # Invertierte Maske für Objekterhalt
328
  mask_inverted = Image.eval(original_mask, lambda x: 255 - x)
329
 
330
  # Weiche Kanten für natürlichen Übergang
331
- soft_mask = mask_inverted.filter(ImageFilter.GaussianBlur(5))
332
-
333
- # Originalbild mit Alpha-Kanal
334
- original_with_alpha = original_image.copy().convert("RGBA")
335
  original_with_alpha.putalpha(soft_mask)
336
 
337
  # Compositing
338
  final_image = new_background.copy().convert("RGBA")
339
  final_image.paste(original_with_alpha, (0, 0), original_with_alpha)
340
 
341
- print(f"✅ Umwelt-Compositing abgeschlossen")
342
-
343
  else:
344
  # ==============================================
345
- # MODUS: FOCUS oder GESICHT ÄNDERN (MASKE-BASIERT)
 
 
346
  # ==============================================
347
  mode_name = "Focus" if mode == "focus_change" else "Gesicht"
348
- print(f"👤 Modus: {mode_name} ändern - MASKE-BASIERT")
349
-
350
- # WICHTIG: MASKE-BASIERTES AUSSCHNEIDEN
351
- # 3a. SAM-Maske auf 512px skalieren
352
- mask_on_512 = original_mask.resize((512, 512), Image.Resampling.LANCZOS)
353
 
354
- # 3b. Bounding Box der Maske auf 512px finden
355
- mask_array = np.array(mask_on_512)
356
- white_pixels = np.where(mask_array > 128)
357
-
358
- if len(white_pixels[0]) == 0:
359
- print("⚠️ Keine weißen Pixel in Maske → Fallback auf BBox")
360
- if bbox_coords and all(c is not None for c in bbox_coords):
361
- # Fallback: User-BBox verwenden
362
- mask_bbox_512 = (
363
- int(bbox_coords[0] * scale_factor) + x_offset,
364
- int(bbox_coords[1] * scale_factor) + y_offset,
365
- int(bbox_coords[2] * scale_factor) + x_offset,
366
- int(bbox_coords[3] * scale_factor) + y_offset
367
- )
368
- else:
369
- # Keine BBox → gesamtes Bild
370
- final_image = new_background
371
- return final_image.convert("RGB")
372
- else:
373
- # MASKE-BASIERTE BBOX berechnen
374
- y_min, x_min = white_pixels[0].min(), white_pixels[1].min()
375
- y_max, x_max = white_pixels[0].max(), white_pixels[1].max()
 
 
 
 
 
 
 
 
 
 
376
 
377
- # Puffer für bessere Ergebnisse
378
- buffer = 15
379
- x_min = max(0, x_min - buffer)
380
- y_min = max(0, y_min - buffer)
381
- x_max = min(512, x_max + buffer)
382
- y_max = min(512, y_max + buffer)
 
383
 
384
- mask_bbox_512 = (x_min, y_min, x_max, y_max)
 
 
385
 
386
- print(f" 🎯 Maske-basierte BBox auf 512px: {mask_bbox_512}")
387
- print(f" 📏 Größe: {x_max-x_min}×{y_max-y_min} Pixel")
388
-
389
- # 3c. Bearbeiteten Bereich aus 512×512 ausschneiden (MASKE-BASIERT)
390
- edited_region_512 = inpaint_result.crop(mask_bbox_512)
391
-
392
- # 3d. Auf Original-Maskengröße skalieren
393
- mask_width = mask_bbox_512[2] - mask_bbox_512[0]
394
- mask_height = mask_bbox_512[3] - mask_bbox_512[1]
395
-
396
- # Maske auf Ausschnitt-Größe zuschneiden
397
- mask_cropped = mask_on_512.crop(mask_bbox_512)
398
-
399
- # Auf Originalgröße skalieren (mit Maske als Alpha)
400
- mask_original_cropped = mask_cropped.resize(
401
- (original_width, original_height),
402
- Image.Resampling.LANCZOS
403
- )
404
-
405
- edited_region_fullsize = edited_region_512.resize(
406
- (original_width, original_height),
407
- Image.Resampling.LANCZOS
408
- )
409
-
410
- # 3e. Weiche Kanten für natürliche Übergänge
411
- soft_mask = mask_original_cropped.filter(ImageFilter.GaussianBlur(5))
412
-
413
- # 3f. Alpha-Compositing
414
- edited_rgba = edited_region_fullsize.convert("RGBA")
415
- edited_rgba.putalpha(soft_mask)
416
-
417
- # 3g. MASKE-BASIERTE POSITION bestimmen
418
- # BBox der Maske im Originalbild finden
419
- mask_original_array = np.array(original_mask)
420
- white_original = np.where(mask_original_array > 128)
421
-
422
- if len(white_original[0]) > 0:
423
- paste_x = white_original[1].min()
424
- paste_y = white_original[0].min()
425
- print(f" 📍 Einfüge-Position (maske-basiert): ({paste_x}, {paste_y})")
426
- else:
427
- # Fallback: Zentriert
428
- paste_x = (original_width - edited_rgba.width) // 2
429
- paste_y = (original_height - edited_rgba.height) // 2
430
- print(f" 📍 Einfüge-Position (zentriert): ({paste_x}, {paste_y})")
431
-
432
- # 3h. Finales Compositing
433
- final_image = original_image.copy().convert("RGBA")
434
- final_image.paste(edited_rgba, (paste_x, paste_y), edited_rgba)
435
-
436
- print(f" ✅ {mode_name}-Compositing maskenbasiert abgeschlossen")
437
 
438
  print(f"✅ Korrektes Compositing abgeschlossen. Finale Größe: {final_image.size}")
439
 
440
  return final_image.convert("RGB")
441
 
442
 
443
-
444
  def auto_detect_face_area(image):
445
  """Optimierten Vorschlag für Gesichtsbereich ohne externe Bibliotheken"""
446
  width, height = image.size
 
247
  return padded_image, padded_mask, padding_info
248
 
249
 
250
+
251
  # Composition Workflow nach Ausgabe ControlnetInpaint-Pipeline
252
  def enhanced_composite_with_sam(original_image, inpaint_result, original_mask,
253
  padding_info, bbox_coords, mode):
254
  """
255
+ COMPOSITING MIT SAM-MASKEN UND BBox-KOORDINATEN
256
+ Berücksichtigt die präzisen Kanten der SAM-Maske
257
  """
258
  print(f"🎨 Verbessertes Compositing für Modus: {mode}")
259
 
 
270
  # FALL 1: Bild war bereits 512×512 (keine Skalierung)
271
  # ==============================================
272
  if scale_factor == 1.0 and x_offset == 0 and y_offset == 0:
273
+ print(f"✅ FALL 1: Bild 512×512 - kein Compositing nötig")
274
+ return inpaint_result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
  # ==============================================
277
+ # FALL 2 & 3: Bild wurde skaliert
278
  # ==============================================
279
+ print(f"🔄 FALL 2/3: Bild skaliert - Compositing mit SAM-Maske")
280
 
281
  # 1. PADDING ENTFERNEN von 512×512 Ergebnis
282
  downscaled_result = inpaint_result.crop(
283
  (x_offset, y_offset, x_offset + scaled_width, y_offset + scaled_height)
284
  )
285
 
286
+ # 2. AUF ORIGINALGRÖßE SKALIEREN
287
+ final_image = original_image.copy()
 
 
 
 
 
 
288
 
289
  if mode == "environment_change":
290
  # ==============================================
291
  # MODUS: UMWELT ÄNDERN (Objekt bleibt original)
292
+ # In dem Fall muß die BBox nicht berücksichtigt werden da Originalbild ausgeschnitten wird
293
+ # anhand der SAM-Maske
294
  # ==============================================
295
  print("🌳 Modus: Umwelt ändern mit SAM-Maske")
296
 
297
+ # Gesamtes bearbeitetes Bild hochskalieren
298
+ new_background = downscaled_result.resize(
299
+ (original_width, original_height),
300
+ Image.Resampling.LANCZOS
301
+ )
302
+
303
+ # Originalbild mit SAM-Maske einfügen
304
+ original_with_alpha = original_image.copy().convert("RGBA")
305
+
306
  # Invertierte Maske für Objekterhalt
307
  mask_inverted = Image.eval(original_mask, lambda x: 255 - x)
308
 
309
  # Weiche Kanten für natürlichen Übergang
310
+ soft_mask = mask_inverted.filter(ImageFilter.GaussianBlur(3))
 
 
 
311
  original_with_alpha.putalpha(soft_mask)
312
 
313
  # Compositing
314
  final_image = new_background.copy().convert("RGBA")
315
  final_image.paste(original_with_alpha, (0, 0), original_with_alpha)
316
 
 
 
317
  else:
318
  # ==============================================
319
+ # MODUS: FOCUS oder GESICHT ÄNDERN
320
+ # Hier muß die BBox berücksichtigt werden da generiertes Bild ausgeschnitten wird
321
+ # ohne die BBox wird entlang der SAM-Maske geschnitten -> ungenau!
322
  # ==============================================
323
  mode_name = "Focus" if mode == "focus_change" else "Gesicht"
324
+ print(f"👤 Modus: {mode_name} ändern mit SAM-Maske")
 
 
 
 
325
 
326
+ if not bbox_coords or not all(c is not None for c in bbox_coords):
327
+ # Keine BBox: gesamtes Bild zurückgeben
328
+ final_image = downscaled_result.resize(
329
+ (original_width, original_height),
330
+ Image.Resampling.LANCZOS
331
+ )
332
+ return final_image.convert("RGB")
333
+
334
+
335
+ #BBox-Koordinaten korrekt transformieren
336
+ #Die BBox-Koordinaten müssen vom Originalbild nach 512x512 transformiert werden
337
+ bbox_scaled = (
338
+ int(bbox_coords[0] * scale_factor),
339
+ int(bbox_coords[1] * scale_factor),
340
+ int(bbox_coords[2] * scale_factor),
341
+ int(bbox_coords[3] * scale_factor)
342
+ )
343
+
344
+
345
+ #Mit den Padding-Offsets wird bei nicht quadratischen 512x512 Bildern
346
+ #das Padding hinzugefügt
347
+ bbox_in_512 = (
348
+ bbox_scaled[0] + x_offset,
349
+ bbox_scaled[1] + y_offset,
350
+ bbox_scaled[2] + x_offset,
351
+ bbox_scaled[3] + y_offset
352
+ )
353
+
354
+ if bbox_in_512[2] > bbox_in_512[0] and bbox_in_512[3] > bbox_in_512[1]:
355
+ # Bearbeiteten Bereich aus dem 512×512-Ergebnis ausschneiden
356
+ edited_region = inpaint_result.crop(bbox_in_512)
357
+
358
 
359
+ # Auf ORIGINAL-BBox-Größe skalieren
360
+ original_bbox_size = (bbox_coords[2] - bbox_coords[0],
361
+ bbox_coords[3] - bbox_coords[1])
362
+ edited_region_fullsize = edited_region.resize(
363
+ original_bbox_size,
364
+ Image.Resampling.LANCZOS
365
+ )
366
 
367
+ # SAM-Maske für den Bereich zuschneiden und weichzeichnen
368
+ mask_cropped = original_mask.crop(bbox_coords)
369
+ soft_mask = mask_cropped.filter(ImageFilter.GaussianBlur(3))
370
 
371
+ # Alpha-Compositing mit präziser SAM-Maske
372
+ edited_rgba = edited_region_fullsize.convert("RGBA")
373
+ mask_rgba = soft_mask.convert("L") # SAM-Maske als Alpha-Kanal
374
+
375
+ temp_image = Image.new("RGBA", original_bbox_size, (0, 0, 0, 0))
376
+ temp_image.paste(edited_rgba, (0, 0), mask_rgba)
377
+
378
+ final_image.paste(temp_image, (bbox_coords[0], bbox_coords[1]), temp_image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
  print(f"✅ Korrektes Compositing abgeschlossen. Finale Größe: {final_image.size}")
381
 
382
  return final_image.convert("RGB")
383
 
384
 
 
385
  def auto_detect_face_area(image):
386
  """Optimierten Vorschlag für Gesichtsbereich ohne externe Bibliotheken"""
387
  width, height = image.size