Astridkraft commited on
Commit
47f75cd
·
verified ·
1 Parent(s): 9b14886

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -125
app.py CHANGED
@@ -3,6 +3,7 @@ from diffusers import StableDiffusionPipeline, StableDiffusionImg2ImgPipeline
3
  from diffusers import StableDiffusionInpaintPipeline, AutoencoderKL
4
  from diffusers import DPMSolverMultistepScheduler, PNDMScheduler
5
  from controlnet_module import controlnet_processor
 
6
  import torch
7
  from PIL import Image, ImageDraw
8
  import time
@@ -462,31 +463,24 @@ def load_txt2img(model_id):
462
  print(f"❌ Auch Fallback fehlgeschlagen: {fallback_error}")
463
  raise
464
 
 
465
  def load_img2img():
466
- """Lädt das Inpainting-Modell mit DPMSolver++ Scheduler"""
467
  global pipe_img2img
468
  if pipe_img2img is None:
469
- print("🔄 Lade Inpainting-Modell...")
470
- try:
471
- pipe_img2img = StableDiffusionInpaintPipeline.from_pretrained(
472
- "runwayml/stable-diffusion-inpainting",
473
- torch_dtype=torch_dtype,
474
- allow_pickle=False,
475
- safety_checker=None,
476
- ).to(device)
477
-
478
- pipe_img2img.scheduler = DPMSolverMultistepScheduler.from_config(
479
- pipe_img2img.scheduler.config,
480
- algorithm_type="sde-dpmsolver++",
481
- use_karras_sigmas=True,
482
- timestep_spacing="trailing"
483
- )
484
-
485
- print("✅ DPMSolver++ Multistep Scheduler für Inpainting konfiguriert")
486
-
487
- except Exception as e:
488
- print(f"❌ Fehler beim Laden des Inpainting-Modells: {e}")
489
- raise
490
 
491
  pipe_img2img.enable_attention_slicing()
492
  pipe_img2img.enable_vae_tiling()
@@ -774,12 +768,7 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
774
  mode, bbox_x1, bbox_y1, bbox_x2, bbox_y2,
775
  progress=gr.Progress()):
776
  """
777
- KORRIGIERTE HAUPTFUNKTION FÜR BILD-zu-BILD MIT RICHTIGEM COMPOSITING
778
-
779
- WICHTIG: Verwendet den korrekten Compositing-Workflow:
780
- 1. Skaliert Bild und Maske gemeinsam
781
- 2. Führt Inpainting auf 512×512 durch
782
- 3. Kompositiert nur den bearbeiteten Bereich zurück ins Originalbild
783
  """
784
  try:
785
  if image is None:
@@ -854,11 +843,8 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
854
 
855
  print(f"🎯 Finaler Prompt für {mode}: {enhanced_prompt}")
856
 
857
-
858
- #Zur Überbrückung bis von der Pipelines Infos kommen!
859
- progress(0, desc="Starte Generierung mit ControlNet...")
860
 
861
-
862
  # ===== MODUS-SPEZIFISCHE EINSTELLUNGEN =====
863
  adj_strength = min(0.85, strength * 1.25)
864
 
@@ -876,59 +862,14 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
876
  keep_environment = True
877
  controlnet_strength = adj_strength * 0.5
878
  print(f"🎯 MODUS: Ausschließlich Gesicht → Depth+Canny (keep_environment=True)")
879
-
880
- controlnet_steps = min(25, int(steps * 0.8))
881
- print(f"⚙️ ControlNet Settings: Strength={controlnet_strength:.3f}, Steps={controlnet_steps}")
882
-
883
-
884
-
885
 
886
- # ===== PUNKT 1: VAE-ENCODING & VERRAUSCHUNG =====
887
- print("🔧 Punkt 1: Bereite verrauschtes Latent vor...")
888
-
889
- # 1. Bild für Latent-Encoding vorbereiten (bereits skaliertes Bild verwenden)
890
- if scaled_image is not None:
891
- # img_for_latent ist das bereits herunterskalierte 512x512 Bild (mit Padding)
892
- img_for_latent = scaled_image
893
- else:
894
- # Fallback, falls keine Skalierung stattfand
895
- img_for_latent = image.convert("RGB").resize((IMG_SIZE, IMG_SIZE), Image.Resampling.LANCZOS)
896
-
897
- # 2. In den Latent Space encoden (VAE)
898
- with torch.no_grad():
899
- # Bild zu Tensor konvertieren
900
- img_tensor = pipe.feature_extractor(img_for_latent, return_tensors="pt").pixel_values.to(device)
901
- # Encoden
902
- init_latent_dist = pipe.vae.encode(img_tensor).latent_dist
903
- init_latents = init_latent_dist.sample() # Latent mit zufälliger Variation aus der Verteilung
904
- init_latents = init_latents * pipe.vae.config.scaling_factor # Skalierung anpassen
905
- print(f"✅ VAE-Encoding abgeschlossen. Latent Shape: {init_latents.shape}")
906
-
907
- # 3. Verrauschung basierend auf Strength (Scheduler)
908
- # Strength=0.8 bedeutet: Starte bei 80% des Rauschprozesses (stark verrauscht)
909
- strength = min(0.85, strength * 1.25) # Ihre existierende Stärke-Anpassung
910
- latent_timestep = int(strength * pipe.scheduler.config.num_train_timesteps)
911
-
912
- # Rauschen generieren
913
- noise = torch.randn_like(init_latents)
914
-
915
- # Verrauschte Latents erzeugen
916
- noised_latents = pipe.scheduler.add_noise(init_latents, noise, torch.tensor([latent_timestep]))
917
- print(f"✅ Verrauschung abgeschlossen (Strength: {strength}, Timestep: {latent_timestep})")
918
- print(f" Noised Latents Shape: {noised_latents.shape}")
919
-
920
- # Diese Variablen für später speichern:
921
- # - noised_latents: Das verrauschte Start-Latent für die Denoising-Schleife
922
- # - latent_timestep: Der Start-Timestep für die Denoising-Schleife
923
- # - init_latents: Das unverrauschte Latent (für spätere Referenz)
924
-
925
-
926
- progress(0.03, desc="ControlNet läuft...")
927
 
928
  # ===== WICHTIG: VARIABLEN FÜR KOMPLETTEN WORKFLOW =====
929
  original_mask = None
930
  padding_info = None
931
- controlnet_input = image.convert("RGB") # Standard: Originalbild
 
932
 
933
  if bbox_x1 is not None and bbox_y1 is not None and bbox_x2 is not None and bbox_y2 is not None:
934
  print(f"🎯 BBox Koordinaten erhalten: [{bbox_x1}, {bbox_y1}, {bbox_x2}, {bbox_y2}]")
@@ -943,75 +884,48 @@ print(f" Noised Latents Shape: {noised_latents.shape}")
943
  target_size=IMG_SIZE
944
  )
945
 
946
- #ControlNet ist ein paralleles Modell (CNN), das unveränderte Control-Maps (z. B. Tiefenkarten)
947
- #verarbeitet und konditionierende Signale an das frozen UNet weiterleitet, um die Gesamtgeneration zu steuern,
948
- #ohne pixelgenaue Manipulationen vorzunehmen. Es beeinflusst den Diffusionsprozess global/lokal durch Addition zu den Features.
949
- #ControlNet-Bildgröße und Inpaint-Bildgröße müssen übereinstimmen!
950
- controlnet_input = scaled_image # Verwende das skalierte Bild für ControlNet
951
  print(f"✅ Gemeinsame Skalierung abgeschlossen")
952
  print(f" Original: {image.size} → Skaliert: {scaled_image.size}")
953
  else:
954
  # Keine BBox: Normales Img2Img (ohne Maske)
955
  print(f"ℹ️ Keine BBox angegeben → normales Img2Img (ohne Maske)")
956
- controlnet_input = image.convert("RGB").resize((IMG_SIZE, IMG_SIZE), Image.Resampling.LANCZOS)
 
957
 
958
- # ===== CONTROLNET AUFRUF =====
959
- print(f"📊 ControlNet Input Größe: {controlnet_input.size}")
 
 
960
 
961
- controlnet_output, inpaint_input = controlnet_processor.generate_with_controlnet(
962
- image=controlnet_input,
963
- prompt=enhanced_prompt,
964
- negative_prompt=combined_negative_prompt,
965
- steps=controlnet_steps,
966
- guidance_scale=guidance_scale,
967
- controlnet_strength=controlnet_strength,
968
- progress=None,
969
  keep_environment=keep_environment
970
  )
971
 
972
- print(f"✅ ControlNet Output erhalten")
973
- print(f"✅ Inpaint Input Größe: {inpaint_input.size}")
974
 
975
  progress(0.3, desc="ControlNet abgeschlossen – starte Inpaint...")
976
 
977
- # ===== INPAINTING PIPELINE =====
978
- pipe = load_img2img()
979
 
980
- # Bild für Inpainting vorbereiten
981
- if inpaint_input.size != (IMG_SIZE, IMG_SIZE):
982
- print(f"⚠️ Inpaint Input hat unerwartete Größe {inpaint_input.size}, skaliere auf {IMG_SIZE}x{IMG_SIZE}")
983
- img_resized = inpaint_input.convert("RGB").resize((IMG_SIZE, IMG_SIZE), Image.Resampling.LANCZOS)
984
- else:
985
- img_resized = inpaint_input.convert("RGB")
986
- print(f"✅ Inpaint Input ist bereits {IMG_SIZE}x{IMG_SIZE}")
987
-
988
  # ===== SEED UND GENERATOR =====
989
  adj_guidance = min(guidance_scale, 12.0)
990
  seed = random.randint(0, 2**32 - 1)
991
  generator = torch.Generator(device=device).manual_seed(seed)
992
  print(f"🌱 Inpaint Seed: {seed}")
993
 
994
- # ===== MASKE FÜR INPAINTING VORBEREITEN =====
995
- inpaint_mask = None
996
- if original_mask is not None and padding_info is not None:
997
- # Verwende die skalierte Maske für Inpainting
998
- _, scaled_mask, _ = scale_image_and_mask_together(
999
- image.convert("RGB"),
1000
- original_mask,
1001
- target_size=IMG_SIZE
1002
- )
1003
- inpaint_mask = scaled_mask
1004
- print(f"✅ Maske für Inpainting vorbereitet: {inpaint_mask.size}")
1005
-
1006
  # ===== FORTSCHRITTS-CALLBACK =====
1007
  callback = ImageToImageProgressCallback(progress, int(steps), adj_strength)
1008
 
1009
- # ===== INPAINT DURCHFÜHREN =====
 
1010
  result = pipe(
1011
  prompt=enhanced_prompt,
1012
  negative_prompt=combined_negative_prompt,
1013
- image=img_resized,
1014
- mask_image=inpaint_mask,
 
1015
  strength=adj_strength,
1016
  num_inference_steps=int(steps),
1017
  guidance_scale=adj_guidance,
@@ -1020,6 +934,8 @@ print(f" Noised Latents Shape: {noised_latents.shape}")
1020
  callback_on_step_end_tensor_inputs=[],
1021
  )
1022
 
 
 
1023
  # ===== KORREKTES COMPOSITING =====
1024
  generated_image = result.images[0]
1025
 
@@ -1033,9 +949,9 @@ print(f" Noised Latents Shape: {noised_latents.shape}")
1033
  )
1034
  print(f"✅ Korrektes Compositing durchgeführt")
1035
  else:
1036
- # Keine Maske: Einfach das generierte Bild zurückgeben (bereits 512×512)
1037
  final_image = generated_image
1038
- print(f"ℹ️ Keine Maske → Direkte Rückgabe des 512×512 Bildes")
1039
 
1040
  end_time = time.time()
1041
  duration = end_time - start_time
@@ -1053,6 +969,7 @@ print(f" Noised Latents Shape: {noised_latents.shape}")
1053
  traceback.print_exc()
1054
  return None
1055
 
 
1056
  def update_bbox_from_image(image):
1057
  """Aktualisiert die Bounding-Box-Koordinaten wenn ein Bild hochgeladen wird"""
1058
  if image is None:
 
3
  from diffusers import StableDiffusionInpaintPipeline, AutoencoderKL
4
  from diffusers import DPMSolverMultistepScheduler, PNDMScheduler
5
  from controlnet_module import controlnet_processor
6
+ from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel
7
  import torch
8
  from PIL import Image, ImageDraw
9
  import time
 
463
  print(f"❌ Auch Fallback fehlgeschlagen: {fallback_error}")
464
  raise
465
 
466
+
467
  def load_img2img():
 
468
  global pipe_img2img
469
  if pipe_img2img is None:
470
+ print("🔄 Lade ControlNet-Inpainting-Modell...")
471
+ # Hier müssen die ControlNet-Modelle geladen werden
472
+ controlnet = ControlNetModel.from_pretrained("lllyasviel/sd-controlnet-canny", torch_dtype=torch_dtype)
473
+ # Oder für Multi-ControlNet: eine Liste von Modellen
474
+
475
+ pipe_img2img = StableDiffusionControlNetInpaintPipeline.from_pretrained(
476
+ "runwayml/stable-diffusion-v1-5",
477
+ controlnet=controlnet,
478
+ torch_dtype=torch_dtype,
479
+ safety_checker=None,
480
+ ).to(device)
481
+ # ... Rest Ihrer Konfiguration (Scheduler, etc.)
482
+ return pipe_img2img
483
+
 
 
 
 
 
 
 
484
 
485
  pipe_img2img.enable_attention_slicing()
486
  pipe_img2img.enable_vae_tiling()
 
768
  mode, bbox_x1, bbox_y1, bbox_x2, bbox_y2,
769
  progress=gr.Progress()):
770
  """
771
+ KORRIGIERTE HAUPTFUNKTION FÜR CONTROLNET-GESTEUERTES INPAINTING
 
 
 
 
 
772
  """
773
  try:
774
  if image is None:
 
843
 
844
  print(f"🎯 Finaler Prompt für {mode}: {enhanced_prompt}")
845
 
846
+ progress(0, desc="Starte Generierung...")
 
 
847
 
 
848
  # ===== MODUS-SPEZIFISCHE EINSTELLUNGEN =====
849
  adj_strength = min(0.85, strength * 1.25)
850
 
 
862
  keep_environment = True
863
  controlnet_strength = adj_strength * 0.5
864
  print(f"🎯 MODUS: Ausschließlich Gesicht → Depth+Canny (keep_environment=True)")
 
 
 
 
 
 
865
 
866
+ print(f"⚙️ ControlNet Settings: Strength={controlnet_strength:.3f}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
867
 
868
  # ===== WICHTIG: VARIABLEN FÜR KOMPLETTEN WORKFLOW =====
869
  original_mask = None
870
  padding_info = None
871
+ scaled_image = None
872
+ scaled_mask = None
873
 
874
  if bbox_x1 is not None and bbox_y1 is not None and bbox_x2 is not None and bbox_y2 is not None:
875
  print(f"🎯 BBox Koordinaten erhalten: [{bbox_x1}, {bbox_y1}, {bbox_x2}, {bbox_y2}]")
 
884
  target_size=IMG_SIZE
885
  )
886
 
 
 
 
 
 
887
  print(f"✅ Gemeinsame Skalierung abgeschlossen")
888
  print(f" Original: {image.size} → Skaliert: {scaled_image.size}")
889
  else:
890
  # Keine BBox: Normales Img2Img (ohne Maske)
891
  print(f"ℹ️ Keine BBox angegeben → normales Img2Img (ohne Maske)")
892
+ scaled_image = image.convert("RGB").resize((IMG_SIZE, IMG_SIZE), Image.Resampling.LANCZOS)
893
+ scaled_mask = Image.new("L", (IMG_SIZE, IMG_SIZE), 255) # Volle Maske
894
 
895
+ progress(0.1, desc="ControlNet läuft...")
896
+
897
+ # ===== CONTROLNET: MAPS ERSTELLEN =====
898
+ print(f"📊 ControlNet Input Größe: {scaled_image.size}")
899
 
900
+ controlnet_maps = controlnet_processor.prepare_controlnet_maps(
901
+ image=scaled_image,
 
 
 
 
 
 
902
  keep_environment=keep_environment
903
  )
904
 
905
+ print(f"✅ ControlNet Maps erstellt: {len(controlnet_maps)} Maps")
 
906
 
907
  progress(0.3, desc="ControlNet abgeschlossen – starte Inpaint...")
908
 
909
+ # ===== CONTROLNET-INPAINTING PIPELINE =====
910
+ pipe = load_img2img() # MUSS StableDiffusionControlNetInpaintPipeline sein!
911
 
 
 
 
 
 
 
 
 
912
  # ===== SEED UND GENERATOR =====
913
  adj_guidance = min(guidance_scale, 12.0)
914
  seed = random.randint(0, 2**32 - 1)
915
  generator = torch.Generator(device=device).manual_seed(seed)
916
  print(f"🌱 Inpaint Seed: {seed}")
917
 
 
 
 
 
 
 
 
 
 
 
 
 
918
  # ===== FORTSCHRITTS-CALLBACK =====
919
  callback = ImageToImageProgressCallback(progress, int(steps), adj_strength)
920
 
921
+ # ===== CONTROLNET-GESTEUERTES INPAINTING DURCHFÜHREN =====
922
+ print(f"🔄 Führe ControlNet-gesteuertes Inpainting durch...")
923
  result = pipe(
924
  prompt=enhanced_prompt,
925
  negative_prompt=combined_negative_prompt,
926
+ image=scaled_image, # Das skalierte Originalbild
927
+ mask_image=scaled_mask, # Die skalierte Maske
928
+ control_image=controlnet_maps, # Die ControlNet-Maps als Liste
929
  strength=adj_strength,
930
  num_inference_steps=int(steps),
931
  guidance_scale=adj_guidance,
 
934
  callback_on_step_end_tensor_inputs=[],
935
  )
936
 
937
+ print("✅ ControlNet-Inpainting abgeschlossen")
938
+
939
  # ===== KORREKTES COMPOSITING =====
940
  generated_image = result.images[0]
941
 
 
949
  )
950
  print(f"✅ Korrektes Compositing durchgeführt")
951
  else:
952
+ # Keine Maske: Einfach das generierte Bild zurückgeben
953
  final_image = generated_image
954
+ print(f"ℹ️ Keine Maske → Direkte Rückgabe des Bildes")
955
 
956
  end_time = time.time()
957
  duration = end_time - start_time
 
969
  traceback.print_exc()
970
  return None
971
 
972
+
973
  def update_bbox_from_image(image):
974
  """Aktualisiert die Bounding-Box-Koordinaten wenn ein Bild hochgeladen wird"""
975
  if image is None: