Astridkraft commited on
Commit
e8ce19b
·
verified ·
1 Parent(s): 1992f31

Update controlnet_module.py

Browse files
Files changed (1) hide show
  1. controlnet_module.py +125 -156
controlnet_module.py CHANGED
@@ -6,7 +6,8 @@ import random
6
  import cv2
7
  import numpy as np
8
  import gradio as gr
9
- from segment_anything import sam_model_registry, SamPredictor
 
10
 
11
 
12
  class ControlNetProgressCallback:
@@ -33,100 +34,57 @@ class ControlNetProcessor:
33
  self.pose_detector = None
34
  self.midas_model = None
35
  self.midas_transform = None
36
- self.sam_predictor = None
 
 
37
  self.sam_initialized = False
38
 
39
-
40
- # In controlnet_module.py - Ersetze die _lazy_load_sam() Funktion
41
- from transformers import Sam2Model, Sam2Processor
42
-
43
-
44
- # In controlnet_module.py - Ersetze die _lazy_load_sam() Funktion
45
- from transformers import Sam2Model, Sam2Processor
46
-
47
- def _lazy_load_sam(self):
48
- if self.sam_initialized:
49
- return True
50
-
51
- try:
52
- print("🔄 Lade SAM 2 über 🤗 Transformers...")
53
-
54
- model_id = "facebook/sam2-hiera-tiny" # Dieser Pfad ist korrekt
55
- self.sam_processor = Sam2Processor.from_pretrained(model_id)
56
- self.sam_model = Sam2Model.from_pretrained(model_id).to(self.device)
57
-
58
- self.sam_initialized = True
59
- print("✅ SAM 2 erfolgreich geladen (via Transformers)")
60
- return True
61
-
62
- except Exception as e:
63
- print(f"❌ Fehler beim Laden von SAM 2: {e}")
64
- self.sam_initialized = True
65
- return False
66
-
67
-
68
- def _lazy_load_sam(self):
69
- if self.sam_initialized:
70
- return True
71
-
72
- try:
73
- print("🔄 Lade SAM 2 über 🤗 Transformers...")
74
-
75
- model_id = "facebook/sam2-hiera-tiny" # Dieser Pfad ist korrekt
76
- self.sam_processor = Sam2Processor.from_pretrained(model_id)
77
- self.sam_model = Sam2Model.from_pretrained(model_id).to(self.device)
78
-
79
- self.sam_initialized = True
80
- print("✅ SAM 2 erfolgreich geladen (via Transformers)")
81
- return True
82
-
83
- except Exception as e:
84
- print(f"❌ Fehler beim Laden von SAM 2: {e}")
85
- self.sam_initialized = True
86
- return False
87
-
88
  def _lazy_load_sam(self):
89
- """Lazy Loading von SAM 2 Tiny - Optimiert für Hugging Face Spaces"""
90
  if self.sam_initialized:
91
  return True
92
-
93
  try:
94
- print("🔄 Lade SAM 2 Tiny von Hugging Face Hub...")
95
-
96
- # KORRIGIERT: Nur der Hugging Face Model-ID Pfad
97
  model_id = "facebook/sam2-hiera-tiny"
98
-
99
- # SAM 2 Modell direkt von Hugging Face laden
100
- sam = sam_model_registry["sam2_hiera_tiny"](checkpoint=model_id)
101
- sam.to(self.device)
102
- self.sam_predictor = SamPredictor(sam)
103
-
104
  self.sam_initialized = True
105
- print(f"✅ SAM 2 ({model_id}) erfolgreich geladen")
106
  return True
107
-
108
  except Exception as e:
109
- print(f"❌ SAM 2 konnte nicht geladen werden: {str(e)[:100]}")
110
- print("ℹ️ Verwende rechteckige Masken als Fallback")
111
- self.sam_predictor = None
112
  self.sam_initialized = True # Verhindert weitere Ladeversuche
113
  return False
114
-
115
  def _validate_bbox(self, image, bbox_coords):
116
  """Validiert und korrigiert BBox-Koordinaten"""
117
  width, height = image.size
118
- x1, y1, x2, y2 = bbox_coords
119
-
 
 
 
 
 
 
120
  # Stelle sicher, dass x1 <= x2 und y1 <= y2
121
  x1, x2 = min(x1, x2), max(x1, x2)
122
  y1, y2 = min(y1, y2), max(y1, y2)
123
-
124
  # Begrenze auf Bildgrenzen
125
  x1 = max(0, min(x1, width - 1))
126
  y1 = max(0, min(y1, height - 1))
127
  x2 = max(0, min(x2, width - 1))
128
  y2 = max(0, min(y2, height - 1))
129
-
130
  # Stelle sicher, dass BBox gültig ist
131
  if x2 - x1 < 10 or y2 - y1 < 10:
132
  # Fallback auf sinnvolle Größe
@@ -135,110 +93,121 @@ def _lazy_load_sam(self):
135
  y1 = max(0, height/2 - size/2)
136
  x2 = min(width, width/2 + size/2)
137
  y2 = min(height, height/2 + size/2)
138
-
139
  return int(x1), int(y1), int(x2), int(y2)
140
-
141
  def _smooth_mask(self, mask_array, blur_radius=3):
142
- """Glättet die Maske für bessere Übergänge (5-Pixel Randbereich)"""
143
  try:
144
- # Gaussian Blur für weiche Kanten - nur der Randbereich wird beeinflusst
145
  if blur_radius > 0:
146
- mask_array = cv2.GaussianBlur(mask_array,
147
- (blur_radius*2+1, blur_radius*2+1),
148
- 0)
149
-
150
  return mask_array
151
- except:
 
152
  return mask_array
153
-
154
  def create_sam_mask(self, image, bbox_coords, mode):
155
  """
156
- Erstellt präzise Maske mit SAM 2 (transparent für Benutzer)
157
  Gibt PIL Image in L-Modus zurück (0=schwarz=erhalten, 255=weiß=verändern)
158
  """
159
  try:
160
- # Lade SAM bei Bedarf (automatisch für Hugging Face Spaces)
161
  if not self.sam_initialized:
162
  self._lazy_load_sam()
163
-
164
- # Fallback wenn SAM nicht verfügbar
165
- if self.sam_predictor is None:
166
  return self._create_rectangular_mask(image, bbox_coords, mode)
167
-
168
- # Validiere BBox
169
  x1, y1, x2, y2 = self._validate_bbox(image, bbox_coords)
170
-
171
- # Konvertiere zu numpy array (RGB)
 
172
  image_np = np.array(image.convert("RGB"))
173
-
174
- # SAM vorbereiten
175
- try:
176
- self.sam_predictor.set_image(image_np)
177
- except Exception as e:
178
- print(f"⚠️ SAM set_image Fehler: {e}")
179
- return self._create_rectangular_mask(image, bbox_coords, mode)
180
-
181
- # BBox für SAM formatieren
182
- input_box = np.array([x1, y1, x2, y2])
183
-
 
 
 
184
  print(f"🎯 SAM 2: Segmentiere Bereich {x1},{y1}-{x2},{y2}")
185
-
186
- # SAM Prediction
187
- masks, scores, _ = self.sam_predictor.predict(
188
- point_coords=None,
189
- point_labels=None,
190
- box=input_box[None, :],
191
- multimask_output=False,
192
- return_logits=False
193
- )
194
-
195
- # Beste Maske extrahieren und glätten (5-Pixel Übergang)
196
- mask_array = masks[0].astype(np.uint8) * 255
197
- mask_array = self._smooth_mask(mask_array, blur_radius=2) # ~5 Pixel Rand
198
-
199
- # Zu PIL Image konvertieren
 
 
 
 
 
 
 
 
200
  mask = Image.fromarray(mask_array).convert("L")
201
-
202
- # Modus-spezifische Anpassung
203
  if mode == "environment_change":
204
- # MODUS 1: Umgebung ändern
205
- # Objekt schwarz (0) = ERHALTEN, Umgebung weiß (255) = VERÄNDERN
206
  mask = Image.eval(mask, lambda x: 255 - x)
207
  print(" SAM-Modus: Umgebung ändern (Objekt erhalten)")
208
  else:
209
- # MODUS 2 & 3: Focus oder Gesicht ändern
210
- # Objekt weiß (255) = VERÄNDERN, Umgebung schwarz (0) = ERHALTEN
211
  print(" SAM-Modus: Focus/Gesicht ändern (Objekt verändern)")
212
-
213
  print(f"✅ SAM 2: Präzise Maske erstellt ({mask.size})")
214
  return mask
215
-
216
  except Exception as e:
217
- print(f"⚠️ SAM 2 Fehler: {str(e)[:100]}")
 
 
218
  print("ℹ️ Fallback auf rechteckige Maske")
219
  return self._create_rectangular_mask(image, bbox_coords, mode)
220
-
221
  def _create_rectangular_mask(self, image, bbox_coords, mode):
222
  """Fallback: Erstellt rechteckige Maske"""
223
  from PIL import ImageDraw
224
-
225
  mask = Image.new("L", image.size, 0)
226
-
227
  if bbox_coords and all(coord is not None for coord in bbox_coords):
228
  x1, y1, x2, y2 = self._validate_bbox(image, bbox_coords)
229
  draw = ImageDraw.Draw(mask)
230
-
231
  if mode == "environment_change":
232
  # MODUS 1: Alles außer Box verändern
233
  draw.rectangle([0, 0, image.size[0], image.size[1]], fill=255)
234
  draw.rectangle([x1, y1, x2, y2], fill=0)
 
235
  else:
236
  # MODUS 2 & 3: Nur Box verändern
237
  draw.rectangle([x1, y1, x2, y2], fill=255)
238
-
239
- print("ℹ️ Rechteckige Maske (SAM Fallback)")
240
  return mask
241
-
242
  def load_pose_detector(self):
243
  """Lädt nur den Pose-Detector"""
244
  if self.pose_detector is None:
@@ -249,35 +218,35 @@ def _lazy_load_sam(self):
249
  except Exception as e:
250
  print(f"⚠️ Pose-Detector konnte nicht geladen werden: {e}")
251
  return self.pose_detector
252
-
253
  def load_midas_model(self):
254
  """Lädt MiDaS Model für Depth Maps"""
255
  if self.midas_model is None:
256
  print("🔄 Lade MiDaS Modell für Depth Maps...")
257
  try:
258
  import torchvision.transforms as T
259
-
260
  self.midas_model = torch.hub.load(
261
- "intel-isl/MiDaS",
262
- "DPT_Hybrid",
263
  trust_repo=True
264
  )
265
-
266
  self.midas_model.to(self.device)
267
  self.midas_model.eval()
268
-
269
  self.midas_transform = T.Compose([
270
  T.Resize(384),
271
  T.ToTensor(),
272
  T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
273
  ])
274
-
275
  print("✅ MiDaS Modell erfolgreich geladen")
276
  except Exception as e:
277
  print(f"❌ MiDaS konnte nicht geladen werden: {e}")
278
  print("ℹ️ Verwende Fallback-Methode")
279
  self.midas_model = None
280
-
281
  return self.midas_model
282
 
283
  def extract_pose_simple(self, image):
@@ -309,13 +278,13 @@ def _lazy_load_sam(self):
309
  """Extrahiert Canny Edges für Umgebungserhaltung"""
310
  try:
311
  img_array = np.array(image.convert("RGB"))
312
-
313
  gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
314
  edges = cv2.Canny(gray, 100, 200)
315
-
316
  edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
317
  edges_image = Image.fromarray(edges_rgb)
318
-
319
  print("✅ Canny Edge Map erstellt")
320
  return edges_image
321
  except Exception as e:
@@ -330,11 +299,11 @@ def _lazy_load_sam(self):
330
  midas = self.load_midas_model()
331
  if midas is not None:
332
  print("🎯 Verwende MiDaS für Depth Map...")
333
-
334
  import torchvision.transforms as T
335
-
336
  img_transformed = self.midas_transform(image).unsqueeze(0).to(self.device)
337
-
338
  with torch.no_grad():
339
  prediction = midas(img_transformed)
340
  prediction = torch.nn.functional.interpolate(
@@ -343,22 +312,22 @@ def _lazy_load_sam(self):
343
  mode="bicubic",
344
  align_corners=False,
345
  ).squeeze()
346
-
347
  depth_np = prediction.cpu().numpy()
348
  depth_min, depth_max = depth_np.min(), depth_np.max()
349
-
350
  if depth_max > depth_min:
351
  depth_np = (depth_np - depth_min) / (depth_max - depth_min)
352
-
353
  depth_np = (depth_np * 255).astype(np.uint8)
354
  depth_image = Image.fromarray(depth_np).convert("RGB")
355
-
356
  print("✅ MiDaS Depth Map erfolgreich erstellt")
357
  return depth_image
358
-
359
  else:
360
  raise Exception("MiDaS nicht geladen")
361
-
362
  except Exception as e:
363
  print(f"⚠️ MiDaS Fehler: {e}. Verwende Fallback...")
364
  try:
@@ -368,7 +337,7 @@ def _lazy_load_sam(self):
368
  depth_map = cv2.GaussianBlur(gray, (5, 5), 0)
369
  depth_rgb = cv2.cvtColor(depth_map, cv2.COLOR_GRAY2RGB)
370
  depth_image = Image.fromarray(depth_rgb)
371
-
372
  print("✅ Fallback Depth Map erstellt")
373
  return depth_image
374
  except Exception as fallback_error:
@@ -380,7 +349,7 @@ def _lazy_load_sam(self):
380
  ERSTELLT NUR CONDITIONING-MAPS, generiert KEIN Bild.
381
  """
382
  print("🎯 ControlNet: Erstelle Conditioning-Maps...")
383
-
384
  if keep_environment:
385
  print(" Modus: Depth + Canny")
386
  conditioning_images = [
@@ -393,7 +362,7 @@ def _lazy_load_sam(self):
393
  self.extract_pose(image),
394
  self.extract_canny_edges(image)
395
  ]
396
-
397
  print(f"✅ {len(conditioning_images)} Conditioning-Maps erstellt.")
398
  return conditioning_images
399
 
 
6
  import cv2
7
  import numpy as np
8
  import gradio as gr
9
+ # WICHTIG: Importiere die neuen SAM2-Klassen aus Transformers
10
+ from transformers import Sam2Model, Sam2Processor
11
 
12
 
13
  class ControlNetProgressCallback:
 
34
  self.pose_detector = None
35
  self.midas_model = None
36
  self.midas_transform = None
37
+ # Ändere die Variablennamen für die neue API
38
+ self.sam_processor = None
39
+ self.sam_model = None
40
  self.sam_initialized = False
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def _lazy_load_sam(self):
43
+ """Lazy Loading von SAM 2 über 🤗 Transformers API"""
44
  if self.sam_initialized:
45
  return True
46
+
47
  try:
48
+ print("🔄 Lade SAM 2 über 🤗 Transformers...")
49
+
50
+ # Die korrekte Modell-ID für SAM 2 Tiny
51
  model_id = "facebook/sam2-hiera-tiny"
52
+
53
+ # Lade Processor und Modell mit der neuen API
54
+ self.sam_processor = Sam2Processor.from_pretrained(model_id)
55
+ self.sam_model = Sam2Model.from_pretrained(model_id).to(self.device)
56
+ self.sam_model.eval() # Setze Modell in Evaluierungsmodus
57
+
58
  self.sam_initialized = True
59
+ print("✅ SAM 2 erfolgreich geladen (via Transformers)")
60
  return True
61
+
62
  except Exception as e:
63
+ print(f"❌ Fehler beim Laden von SAM 2: {str(e)[:200]}")
 
 
64
  self.sam_initialized = True # Verhindert weitere Ladeversuche
65
  return False
66
+
67
  def _validate_bbox(self, image, bbox_coords):
68
  """Validiert und korrigiert BBox-Koordinaten"""
69
  width, height = image.size
70
+
71
+ # Extrahiere Koordinaten - unterstützt beide Formate
72
+ if isinstance(bbox_coords, (list, tuple)) and len(bbox_coords) == 4:
73
+ x1, y1, x2, y2 = bbox_coords
74
+ else:
75
+ # Für den Fall, dass Koordinaten einzeln übergeben werden
76
+ x1, y1, x2, y2 = bbox_coords
77
+
78
  # Stelle sicher, dass x1 <= x2 und y1 <= y2
79
  x1, x2 = min(x1, x2), max(x1, x2)
80
  y1, y2 = min(y1, y2), max(y1, y2)
81
+
82
  # Begrenze auf Bildgrenzen
83
  x1 = max(0, min(x1, width - 1))
84
  y1 = max(0, min(y1, height - 1))
85
  x2 = max(0, min(x2, width - 1))
86
  y2 = max(0, min(y2, height - 1))
87
+
88
  # Stelle sicher, dass BBox gültig ist
89
  if x2 - x1 < 10 or y2 - y1 < 10:
90
  # Fallback auf sinnvolle Größe
 
93
  y1 = max(0, height/2 - size/2)
94
  x2 = min(width, width/2 + size/2)
95
  y2 = min(height, height/2 + size/2)
96
+
97
  return int(x1), int(y1), int(x2), int(y2)
98
+
99
  def _smooth_mask(self, mask_array, blur_radius=3):
100
+ """Glättet die Maske für bessere Übergänge"""
101
  try:
 
102
  if blur_radius > 0:
103
+ # Verwende median blur für bessere Kantenerhaltung als Gaussian
104
+ mask_array = cv2.medianBlur(mask_array, blur_radius*2+1)
 
 
105
  return mask_array
106
+ except Exception as e:
107
+ print(f"⚠️ Fehler beim Glätten der Maske: {e}")
108
  return mask_array
109
+
110
  def create_sam_mask(self, image, bbox_coords, mode):
111
  """
112
+ Erstellt präzise Maske mit SAM 2 (via 🤗 Transformers API)
113
  Gibt PIL Image in L-Modus zurück (0=schwarz=erhalten, 255=weiß=verändern)
114
  """
115
  try:
116
+ # 1. SAM2 laden (falls noch nicht geschehen)
117
  if not self.sam_initialized:
118
  self._lazy_load_sam()
119
+
120
+ if self.sam_model is None or self.sam_processor is None:
121
+ print("⚠️ SAM 2 Model nicht verfügbar, verwende Fallback")
122
  return self._create_rectangular_mask(image, bbox_coords, mode)
123
+
124
+ # 2. Validiere BBox und konvertiere Bild
125
  x1, y1, x2, y2 = self._validate_bbox(image, bbox_coords)
126
+ width, height = image.size
127
+
128
+ # Konvertiere zu numpy array (RGB) - für SAM2 Processor
129
  image_np = np.array(image.convert("RGB"))
130
+
131
+ # 3. Vorbereiten der Eingabe für SAM2
132
+ # BBox im Format [x_min, y_min, x_max, y_max] erstellen
133
+ # ACHTUNG: SAM2 erwartet Boxen in diesem Format
134
+ input_boxes = [[x1, y1, x2, y2]]
135
+
136
+ # Bild mit dem Processor vorverarbeiten
137
+ inputs = self.sam_processor(
138
+ image_np,
139
+ input_boxes=[input_boxes], # WICHTIG: Liste von Box-Listen
140
+ return_tensors="pt"
141
+ ).to(self.device)
142
+
143
+ # 4. Vorhersage mit dem Modell
144
  print(f"🎯 SAM 2: Segmentiere Bereich {x1},{y1}-{x2},{y2}")
145
+ with torch.no_grad():
146
+ outputs = self.sam_model(**inputs)
147
+
148
+ # 5. Maske extrahieren und verarbeiten
149
+ # outputs.pred_masks enthält die Masken-Logits
150
+ # post_process_masks stellt die Originalgröße wieder her
151
+ mask = self.sam_processor.post_process_masks(
152
+ outputs.pred_masks,
153
+ inputs.original_sizes,
154
+ inputs.reshaped_input_sizes
155
+ )[0][0] # [batch_index][mask_index]
156
+
157
+ # Sigmoid für Wahrscheinlichkeiten, dann Schwellenwert
158
+ mask = mask.sigmoid().cpu().numpy()
159
+ mask_array = (mask > 0.5).astype(np.uint8) * 255
160
+
161
+ # 6. Zu PIL Image konvertieren und auf Originalgröße bringen
162
+ mask = Image.fromarray(mask_array.squeeze()).convert("L")
163
+ mask = mask.resize((width, height), Image.Resampling.NEAREST)
164
+
165
+ # 7. Kanten glätten für natürlichere Übergänge
166
+ mask_array = np.array(mask)
167
+ mask_array = self._smooth_mask(mask_array, blur_radius=2)
168
  mask = Image.fromarray(mask_array).convert("L")
169
+
170
+ # 8. Modus-spezifische Anpassung (Invertierung)
171
  if mode == "environment_change":
172
+ # MODUS 1: Umgebung ändern - Objekt schwarz (erhalten)
 
173
  mask = Image.eval(mask, lambda x: 255 - x)
174
  print(" SAM-Modus: Umgebung ändern (Objekt erhalten)")
175
  else:
176
+ # MODUS 2 & 3: Focus/Gesicht ändern - Objekt weiß (verändern)
 
177
  print(" SAM-Modus: Focus/Gesicht ändern (Objekt verändern)")
178
+
179
  print(f"✅ SAM 2: Präzise Maske erstellt ({mask.size})")
180
  return mask
181
+
182
  except Exception as e:
183
+ print(f"⚠️ SAM 2 Fehler (Transformers API): {str(e)[:200]}")
184
+ import traceback
185
+ traceback.print_exc()
186
  print("ℹ️ Fallback auf rechteckige Maske")
187
  return self._create_rectangular_mask(image, bbox_coords, mode)
188
+
189
  def _create_rectangular_mask(self, image, bbox_coords, mode):
190
  """Fallback: Erstellt rechteckige Maske"""
191
  from PIL import ImageDraw
192
+
193
  mask = Image.new("L", image.size, 0)
194
+
195
  if bbox_coords and all(coord is not None for coord in bbox_coords):
196
  x1, y1, x2, y2 = self._validate_bbox(image, bbox_coords)
197
  draw = ImageDraw.Draw(mask)
198
+
199
  if mode == "environment_change":
200
  # MODUS 1: Alles außer Box verändern
201
  draw.rectangle([0, 0, image.size[0], image.size[1]], fill=255)
202
  draw.rectangle([x1, y1, x2, y2], fill=0)
203
+ print("ℹ️ Rechteckige Maske: Umgebung ändern")
204
  else:
205
  # MODUS 2 & 3: Nur Box verändern
206
  draw.rectangle([x1, y1, x2, y2], fill=255)
207
+ print("ℹ️ Rechteckige Maske: Focus/Gesicht ändern")
208
+
209
  return mask
210
+
211
  def load_pose_detector(self):
212
  """Lädt nur den Pose-Detector"""
213
  if self.pose_detector is None:
 
218
  except Exception as e:
219
  print(f"⚠️ Pose-Detector konnte nicht geladen werden: {e}")
220
  return self.pose_detector
221
+
222
  def load_midas_model(self):
223
  """Lädt MiDaS Model für Depth Maps"""
224
  if self.midas_model is None:
225
  print("🔄 Lade MiDaS Modell für Depth Maps...")
226
  try:
227
  import torchvision.transforms as T
228
+
229
  self.midas_model = torch.hub.load(
230
+ "intel-isl/MiDaS",
231
+ "DPT_Hybrid",
232
  trust_repo=True
233
  )
234
+
235
  self.midas_model.to(self.device)
236
  self.midas_model.eval()
237
+
238
  self.midas_transform = T.Compose([
239
  T.Resize(384),
240
  T.ToTensor(),
241
  T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
242
  ])
243
+
244
  print("✅ MiDaS Modell erfolgreich geladen")
245
  except Exception as e:
246
  print(f"❌ MiDaS konnte nicht geladen werden: {e}")
247
  print("ℹ️ Verwende Fallback-Methode")
248
  self.midas_model = None
249
+
250
  return self.midas_model
251
 
252
  def extract_pose_simple(self, image):
 
278
  """Extrahiert Canny Edges für Umgebungserhaltung"""
279
  try:
280
  img_array = np.array(image.convert("RGB"))
281
+
282
  gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
283
  edges = cv2.Canny(gray, 100, 200)
284
+
285
  edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
286
  edges_image = Image.fromarray(edges_rgb)
287
+
288
  print("✅ Canny Edge Map erstellt")
289
  return edges_image
290
  except Exception as e:
 
299
  midas = self.load_midas_model()
300
  if midas is not None:
301
  print("🎯 Verwende MiDaS für Depth Map...")
302
+
303
  import torchvision.transforms as T
304
+
305
  img_transformed = self.midas_transform(image).unsqueeze(0).to(self.device)
306
+
307
  with torch.no_grad():
308
  prediction = midas(img_transformed)
309
  prediction = torch.nn.functional.interpolate(
 
312
  mode="bicubic",
313
  align_corners=False,
314
  ).squeeze()
315
+
316
  depth_np = prediction.cpu().numpy()
317
  depth_min, depth_max = depth_np.min(), depth_np.max()
318
+
319
  if depth_max > depth_min:
320
  depth_np = (depth_np - depth_min) / (depth_max - depth_min)
321
+
322
  depth_np = (depth_np * 255).astype(np.uint8)
323
  depth_image = Image.fromarray(depth_np).convert("RGB")
324
+
325
  print("✅ MiDaS Depth Map erfolgreich erstellt")
326
  return depth_image
327
+
328
  else:
329
  raise Exception("MiDaS nicht geladen")
330
+
331
  except Exception as e:
332
  print(f"⚠️ MiDaS Fehler: {e}. Verwende Fallback...")
333
  try:
 
337
  depth_map = cv2.GaussianBlur(gray, (5, 5), 0)
338
  depth_rgb = cv2.cvtColor(depth_map, cv2.COLOR_GRAY2RGB)
339
  depth_image = Image.fromarray(depth_rgb)
340
+
341
  print("✅ Fallback Depth Map erstellt")
342
  return depth_image
343
  except Exception as fallback_error:
 
349
  ERSTELLT NUR CONDITIONING-MAPS, generiert KEIN Bild.
350
  """
351
  print("🎯 ControlNet: Erstelle Conditioning-Maps...")
352
+
353
  if keep_environment:
354
  print(" Modus: Depth + Canny")
355
  conditioning_images = [
 
362
  self.extract_pose(image),
363
  self.extract_canny_edges(image)
364
  ]
365
+
366
  print(f"✅ {len(conditioning_images)} Conditioning-Maps erstellt.")
367
  return conditioning_images
368