Astridkraft commited on
Commit
336c145
·
verified ·
1 Parent(s): 9e6af7a

Update controlnet_module.py

Browse files
Files changed (1) hide show
  1. controlnet_module.py +144 -75
controlnet_module.py CHANGED
@@ -33,9 +33,12 @@ class ControlNetProcessor:
33
  self.pose_detector = None
34
  self.controlnet_openpose = None
35
  self.controlnet_canny = None
 
36
  self.pipe_openpose = None
37
  self.pipe_canny = None
38
- self.pipe_multi = None
 
 
39
 
40
  def load_pose_detector(self):
41
  """Lädt nur den Pose-Detector"""
@@ -85,12 +88,31 @@ class ControlNetProcessor:
85
  edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
86
  edges_image = Image.fromarray(edges_rgb)
87
 
88
- print("✅ Canny Edge für Umgebungserhaltung erstellt")
89
  return edges_image
90
  except Exception as e:
91
  print(f"Fehler bei Canny Edge Extraction: {e}")
92
  return image.convert("RGB").resize((512, 512))
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  def load_controlnet_pipeline(self, controlnet_type="openpose"):
95
  """Lädt die passende ControlNet Pipeline"""
96
  if controlnet_type == "openpose":
@@ -143,11 +165,35 @@ class ControlNetProcessor:
143
  raise
144
  return self.pipe_canny
145
 
146
- elif controlnet_type == "multi":
147
- if self.pipe_multi is None:
148
- print("Loading Multi-ControlNet pipeline...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  try:
150
- # Beide ControlNet-Modelle laden
151
  if self.controlnet_openpose is None:
152
  self.controlnet_openpose = ControlNetModel.from_pretrained(
153
  "lllyasviel/sd-controlnet-openpose",
@@ -159,8 +205,7 @@ class ControlNetProcessor:
159
  torch_dtype=self.torch_dtype
160
  )
161
 
162
- # Multi-ControlNet Pipeline
163
- self.pipe_multi = StableDiffusionControlNetPipeline.from_pretrained(
164
  "runwayml/stable-diffusion-v1-5",
165
  controlnet=[self.controlnet_openpose, self.controlnet_canny],
166
  torch_dtype=self.torch_dtype,
@@ -169,13 +214,45 @@ class ControlNetProcessor:
169
  ).to(self.device)
170
 
171
  from diffusers import EulerAncestralDiscreteScheduler
172
- self.pipe_multi.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe_multi.scheduler.config)
173
- self.pipe_multi.enable_attention_slicing()
174
- print("✅ Multi-ControlNet pipeline loaded successfully!")
175
  except Exception as e:
176
- print(f"Fehler beim Laden von Multi-ControlNet: {e}")
177
  raise
178
- return self.pipe_multi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  def generate_with_controlnet(
181
  self, image, prompt, negative_prompt,
@@ -184,40 +261,45 @@ class ControlNetProcessor:
184
  ):
185
  """Generiert Bild mit ControlNet und Fortschrittsanzeige"""
186
  try:
187
- # --- KORRIGIERTE LOGIK ---
188
  if keep_environment:
189
- # UMGEBUNG BEIBEHALTEN, PERSON ÄNDERNMULTI-CONTROLNET
190
- print("🎯 ControlNet Modus: Umgebung beibehalten (Multi-ControlNet: OpenPose + Canny)")
191
 
192
  # Beide Conditioning Maps erstellen
193
- pose_image = self.extract_pose(image)
194
  canny_image = self.extract_canny_edges(image)
195
- print("✅ OpenPose + Canny Maps erstellt")
196
-
197
- # Multi-ControlNet verwenden
198
- conditioning_images = [pose_image, canny_image]
199
- controlnet_type = "multi"
200
 
201
- # Unterschiedliche Strengths für Pose und Canny
202
- controlnet_conditioning_scale = [controlnet_strength * 0.6, # OpenPose: 60% für Person
203
- controlnet_strength * 0.4] # Canny: 40% für Umgebung
204
 
205
- # Zufälliger Seed
206
- seed = random.randint(0, 2**32 - 1)
207
- generator = torch.Generator(device=self.device).manual_seed(seed)
208
- print(f"ControlNet Seed: {seed}")
209
 
210
  else:
211
- # PERSON BEIBEHALTEN, UMGEBUNG ÄNDERNNUR OPENPOSE
212
- controlnet_type = "openpose"
213
- print("🎯 ControlNet Modus: Person beibehalten (OpenPose)")
214
- conditioning_images = self.extract_pose(image)
215
- controlnet_conditioning_scale = controlnet_strength
 
 
 
 
 
 
 
 
 
 
216
 
217
- # Zufälliger Seed
218
- seed = random.randint(0, 2**32 - 1)
219
- generator = torch.Generator(device=self.device).manual_seed(seed)
220
- print(f"ControlNet Seed: {seed}")
221
 
222
  pipe = self.load_controlnet_pipeline(controlnet_type)
223
 
@@ -227,36 +309,20 @@ class ControlNetProcessor:
227
  print("🔄 ControlNet: Starte Pipeline...")
228
 
229
  # ControlNet Generierung
230
- if controlnet_type == "multi":
231
- result = pipe(
232
- prompt=prompt,
233
- image=conditioning_images,
234
- negative_prompt=negative_prompt,
235
- num_inference_steps=int(steps),
236
- guidance_scale=guidance_scale,
237
- generator=generator,
238
- controlnet_conditioning_scale=controlnet_conditioning_scale,
239
- height=512,
240
- width=512,
241
- output_type="pil",
242
- callback_on_step_end=callback,
243
- callback_on_step_end_tensor_inputs=[],
244
- )
245
- else:
246
- result = pipe(
247
- prompt=prompt,
248
- image=conditioning_images,
249
- negative_prompt=negative_prompt,
250
- num_inference_steps=int(steps),
251
- guidance_scale=guidance_scale,
252
- generator=generator,
253
- controlnet_conditioning_scale=controlnet_conditioning_scale,
254
- height=512,
255
- width=512,
256
- output_type="pil",
257
- callback_on_step_end=callback,
258
- callback_on_step_end_tensor_inputs=[],
259
- )
260
 
261
  print("✅ ControlNet abgeschlossen!")
262
 
@@ -275,14 +341,17 @@ class ControlNetProcessor:
275
  Rückgabe: (image_für_inpaint, conditioning_info)
276
  """
277
  if keep_environment:
278
- # PERSON ÄNDERN: Originalbild an Inpaint übergeben
279
- print("🎯 Inpaint: Übergebe Originalbild (Person ändern)")
280
- return image, {"type": "original", "image": image}
 
 
 
 
281
  else:
282
- # UMGEBUNG ÄNDERN: Pose-Map an Inpaint übergeben
283
- print("🎯 Inpaint: Übergebe Pose-Map (Umgebung ändern)")
284
- pose_image = self.extract_pose(image)
285
- return pose_image, {"type": "pose", "image": pose_image}
286
 
287
 
288
  # Globale Instanz
 
33
  self.pose_detector = None
34
  self.controlnet_openpose = None
35
  self.controlnet_canny = None
36
+ self.controlnet_depth = None
37
  self.pipe_openpose = None
38
  self.pipe_canny = None
39
+ self.pipe_depth = None
40
+ self.pipe_multi_inside = None # OpenPose + Canny für Inside-Box
41
+ self.pipe_multi_outside = None # Depth + Canny für Outside-Box
42
 
43
  def load_pose_detector(self):
44
  """Lädt nur den Pose-Detector"""
 
88
  edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
89
  edges_image = Image.fromarray(edges_rgb)
90
 
91
+ print("✅ Canny Edge Map erstellt")
92
  return edges_image
93
  except Exception as e:
94
  print(f"Fehler bei Canny Edge Extraction: {e}")
95
  return image.convert("RGB").resize((512, 512))
96
 
97
+ def extract_depth_map(self, image):
98
+ """Extrahiert Depth Map für räumliche Konsistenz"""
99
+ try:
100
+ # Für echte Depth-Maps würde man ein Depth-Estimation-Modell verwenden
101
+ # Hier als Fallback: Konvertierung zu Grayscale als Depth-Approximation
102
+ img_array = np.array(image.convert("RGB"))
103
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
104
+
105
+ # Depth-ähnliche Map erstellen (helle Bereiche = nah, dunkle = fern)
106
+ depth_map = cv2.GaussianBlur(gray, (5, 5), 0)
107
+ depth_rgb = cv2.cvtColor(depth_map, cv2.COLOR_GRAY2RGB)
108
+ depth_image = Image.fromarray(depth_rgb)
109
+
110
+ print("✅ Depth Map erstellt (Grayscale Approximation)")
111
+ return depth_image
112
+ except Exception as e:
113
+ print(f"Fehler bei Depth Map Extraction: {e}")
114
+ return image.convert("RGB").resize((512, 512))
115
+
116
  def load_controlnet_pipeline(self, controlnet_type="openpose"):
117
  """Lädt die passende ControlNet Pipeline"""
118
  if controlnet_type == "openpose":
 
165
  raise
166
  return self.pipe_canny
167
 
168
+ elif controlnet_type == "depth":
169
+ if self.pipe_depth is None:
170
+ print("Loading Depth ControlNet pipeline...")
171
+ try:
172
+ self.controlnet_depth = ControlNetModel.from_pretrained(
173
+ "lllyasviel/sd-controlnet-depth",
174
+ torch_dtype=self.torch_dtype
175
+ )
176
+ self.pipe_depth = StableDiffusionControlNetPipeline.from_pretrained(
177
+ "runwayml/stable-diffusion-v1-5",
178
+ controlnet=self.controlnet_depth,
179
+ torch_dtype=self.torch_dtype,
180
+ safety_checker=None,
181
+ requires_safety_checker=False
182
+ ).to(self.device)
183
+
184
+ from diffusers import EulerAncestralDiscreteScheduler
185
+ self.pipe_depth.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe_depth.scheduler.config)
186
+ self.pipe_depth.enable_attention_slicing()
187
+ print("✅ Depth ControlNet pipeline loaded successfully!")
188
+ except Exception as e:
189
+ print(f"Fehler beim Laden von Depth ControlNet: {e}")
190
+ raise
191
+ return self.pipe_depth
192
+
193
+ elif controlnet_type == "multi_inside": # OpenPose + Canny für Inside-Box
194
+ if self.pipe_multi_inside is None:
195
+ print("Loading Multi-ControlNet pipeline für Inside-Box...")
196
  try:
 
197
  if self.controlnet_openpose is None:
198
  self.controlnet_openpose = ControlNetModel.from_pretrained(
199
  "lllyasviel/sd-controlnet-openpose",
 
205
  torch_dtype=self.torch_dtype
206
  )
207
 
208
+ self.pipe_multi_inside = StableDiffusionControlNetPipeline.from_pretrained(
 
209
  "runwayml/stable-diffusion-v1-5",
210
  controlnet=[self.controlnet_openpose, self.controlnet_canny],
211
  torch_dtype=self.torch_dtype,
 
214
  ).to(self.device)
215
 
216
  from diffusers import EulerAncestralDiscreteScheduler
217
+ self.pipe_multi_inside.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe_multi_inside.scheduler.config)
218
+ self.pipe_multi_inside.enable_attention_slicing()
219
+ print("✅ Multi-ControlNet (Inside) pipeline loaded successfully!")
220
  except Exception as e:
221
+ print(f"Fehler beim Laden von Multi-ControlNet Inside: {e}")
222
  raise
223
+ return self.pipe_multi_inside
224
+
225
+ elif controlnet_type == "multi_outside": # Depth + Canny für Outside-Box
226
+ if self.pipe_multi_outside is None:
227
+ print("Loading Multi-ControlNet pipeline für Outside-Box...")
228
+ try:
229
+ if self.controlnet_depth is None:
230
+ self.controlnet_depth = ControlNetModel.from_pretrained(
231
+ "lllyasviel/sd-controlnet-depth",
232
+ torch_dtype=self.torch_dtype
233
+ )
234
+ if self.controlnet_canny is None:
235
+ self.controlnet_canny = ControlNetModel.from_pretrained(
236
+ "lllyasviel/sd-controlnet-canny",
237
+ torch_dtype=self.torch_dtype
238
+ )
239
+
240
+ self.pipe_multi_outside = StableDiffusionControlNetPipeline.from_pretrained(
241
+ "runwayml/stable-diffusion-v1-5",
242
+ controlnet=[self.controlnet_depth, self.controlnet_canny],
243
+ torch_dtype=self.torch_dtype,
244
+ safety_checker=None,
245
+ requires_safety_checker=False
246
+ ).to(self.device)
247
+
248
+ from diffusers import EulerAncestralDiscreteScheduler
249
+ self.pipe_multi_outside.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe_multi_outside.scheduler.config)
250
+ self.pipe_multi_outside.enable_attention_slicing()
251
+ print("✅ Multi-ControlNet (Outside) pipeline loaded successfully!")
252
+ except Exception as e:
253
+ print(f"Fehler beim Laden von Multi-ControlNet Outside: {e}")
254
+ raise
255
+ return self.pipe_multi_outside
256
 
257
  def generate_with_controlnet(
258
  self, image, prompt, negative_prompt,
 
261
  ):
262
  """Generiert Bild mit ControlNet und Fortschrittsanzeige"""
263
  try:
264
+ # --- KORRIGIERTE LOGIK FÜR KONSISTENZ MIT APP.PY ---
265
  if keep_environment:
266
+ # OUTSIDE-BOX ÄNDERN (Umgebung bleibt erhalten) Depth + Canny
267
+ print("🎯 ControlNet Modus: Outside-Box ändern (Depth + Canny)")
268
 
269
  # Beide Conditioning Maps erstellen
270
+ depth_image = self.extract_depth_map(image)
271
  canny_image = self.extract_canny_edges(image)
272
+ print("✅ Depth + Canny Maps für Outside-Box erstellt")
 
 
 
 
273
 
274
+ # Multi-ControlNet für Outside verwenden
275
+ conditioning_images = [depth_image, canny_image]
276
+ controlnet_type = "multi_outside"
277
 
278
+ # Gewichtung: Depth 60%, Canny 40%
279
+ controlnet_conditioning_scale = [controlnet_strength * 0.6, # Depth: 60% für räumliche Tiefe
280
+ controlnet_strength * 0.4] # Canny: 40% für Strukturen
 
281
 
282
  else:
283
+ # INSIDE-BOX ÄNDERN (Person bleibt erhalten) OpenPose + Canny
284
+ print("🎯 ControlNet Modus: Inside-Box ändern (OpenPose + Canny)")
285
+
286
+ # Beide Conditioning Maps erstellen
287
+ pose_image = self.extract_pose(image)
288
+ canny_image = self.extract_canny_edges(image)
289
+ print("✅ OpenPose + Canny Maps für Inside-Box erstellt")
290
+
291
+ # Multi-ControlNet für Inside verwenden
292
+ conditioning_images = [pose_image, canny_image]
293
+ controlnet_type = "multi_inside"
294
+
295
+ # Gewichtung: OpenPose 70%, Canny 30%
296
+ controlnet_conditioning_scale = [controlnet_strength * 0.7, # OpenPose: 70% für Person
297
+ controlnet_strength * 0.3] # Canny: 30% für Konturen
298
 
299
+ # Zufälliger Seed
300
+ seed = random.randint(0, 2**32 - 1)
301
+ generator = torch.Generator(device=self.device).manual_seed(seed)
302
+ print(f"ControlNet Seed: {seed}")
303
 
304
  pipe = self.load_controlnet_pipeline(controlnet_type)
305
 
 
309
  print("🔄 ControlNet: Starte Pipeline...")
310
 
311
  # ControlNet Generierung
312
+ result = pipe(
313
+ prompt=prompt,
314
+ image=conditioning_images,
315
+ negative_prompt=negative_prompt,
316
+ num_inference_steps=int(steps),
317
+ guidance_scale=guidance_scale,
318
+ generator=generator,
319
+ controlnet_conditioning_scale=controlnet_conditioning_scale,
320
+ height=512,
321
+ width=512,
322
+ output_type="pil",
323
+ callback_on_step_end=callback,
324
+ callback_on_step_end_tensor_inputs=[],
325
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
  print("✅ ControlNet abgeschlossen!")
328
 
 
341
  Rückgabe: (image_für_inpaint, conditioning_info)
342
  """
343
  if keep_environment:
344
+ # OUTSIDE-BOX ÄNDERN: Depth+Canny Info für Umgebung
345
+ print("🎯 Inpaint: Übergebe Depth+Canny Info (Outside-Box ändern)")
346
+ depth_image = self.extract_depth_map(image)
347
+ canny_image = self.extract_canny_edges(image)
348
+ # Für Inpaint kann eine kombinierte Map verwendet werden
349
+ combined_map = Image.blend(depth_image.convert("RGB"), canny_image.convert("RGB"), alpha=0.5)
350
+ return combined_map, {"type": "depth_canny", "image": combined_map}
351
  else:
352
+ # INSIDE-BOX ÄNDERN: Originalbild an Inpaint übergeben
353
+ print("🎯 Inpaint: Übergebe Originalbild (Inside-Box ändern)")
354
+ return image, {"type": "original", "image": image}
 
355
 
356
 
357
  # Globale Instanz