# controlnet_facefix.py - PURE QUALITY ENHANCEMENT WITH MINIMAL CHANGE import torch from diffusers import StableDiffusionControlNetPipeline, ControlNetModel, AutoencoderKL from PIL import Image, ImageFilter, ImageEnhance import time import cv2 import numpy as np from torchvision import transforms print("="*60) print("FACE-FIX: REINE QUALITÄTSVERBESSERUNG - MINIMALE ÄNDERUNG") print("="*60) _components_loaded = False _controlnet_depth = None _controlnet_pose = None _pipeline = None def _initialize_components(): """Lade nur notwendige Komponenten""" global _components_loaded, _controlnet_depth, _controlnet_pose if _components_loaded: return True print("⚠️ Lade nur OpenPose (Depth wird deaktiviert)...") try: # NUR OPENPOSE - Depth verändert zu viel _controlnet_pose = ControlNetModel.from_pretrained( "lllyasviel/sd-controlnet-openpose", torch_dtype=torch.float16 ) print("✅ OpenPose geladen") # Depth wird NICHT geladen - es verändert den Hintergrund zu stark _controlnet_depth = None _components_loaded = True return True except Exception as e: print(f"❌ Fehler: {e}") return False def _extract_precise_pose(image): """SEHR PRÄZISE Pose-Extraktion nur für Gesicht""" try: img_array = np.array(image.convert("RGB")) gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) # EXTREM NIEDRIGE Thresholds für minimale Kanten edges = cv2.Canny(gray, 15, 45) # Nur feinste Kanten # Face detection für Fokus face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' ) faces = face_cascade.detectMultiScale(gray, 1.1, 4) # Erstelle leere Pose Map pose_map = np.zeros_like(img_array) # Nur Gesichtskanten einfügen if len(faces) > 0: for (x, y, w, h) in faces: # Extrahiere Gesichtsregion face_region = edges[y:y+h, x:x+w] # Nur 10% der stärksten Kanten behalten threshold = np.percentile(face_region[face_region > 0], 90) face_region[face_region < threshold] = 0 pose_map[y:y+h, x:x+w, 0] = face_region else: # Falls kein Gesicht erkannt, minimale Kanten pose_map[..., 0] = edges * 0.3 # Noch schwächer return Image.fromarray(pose_map) except: # Fallback: minimale Kanten gray = cv2.cvtColor(np.array(image.convert("RGB")), cv2.COLOR_RGB2GRAY) edges = cv2.Canny(gray, 10, 30) * 0.2 # Sehr schwach return Image.fromarray(edges).convert("RGB") def _apply_face_enhancement(image): """EINFACHE Face Enhancement ohne AI""" try: img_array = np.array(image.convert("RGB")) # 1. Scharfe Kanten (nur leicht) sharpened = cv2.filter2D(img_array, -1, np.array([[-0.5, -0.5, -0.5], [-0.5, 5.0, -0.5], [-0.5, -0.5, -0.5]]) / 3.0) # 2. Leichter De-Noise denoised = cv2.fastNlMeansDenoisingColored(sharpened, None, 3, 3, 7, 21) # 3. Kontrast leicht erhöhen lab = cv2.cvtColor(denoised, cv2.COLOR_RGB2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=1.0, tileGridSize=(8,8)) l = clahe.apply(l) enhanced = cv2.merge([l, a, b]) enhanced = cv2.cvtColor(enhanced, cv2.COLOR_LAB2RGB) return Image.fromarray(enhanced) except: return image def apply_facefix(image: Image.Image, prompt: str, negative_prompt: str, seed: int, model_id: str): """ SUPER-SUBTILE QUALITÄTSVERBESSERUNG Strategie: 1. NUR OpenPose (kein Depth - das verändert zu viel) 2. SEHR niedrige ControlNet-Stärke 3. Fast kein CFG Scale 4. Identischer Prompt """ print("\n" + "🎯"*50) print("SUBTILE QUALITÄTSVERBESSERUNG") print(f" Größe: {image.size}") print("🎯"*50) start_time = time.time() # OPTION 1: Einfache non-AI Verbesserung (empfohlen) print("\n⚡ OPTION 1: Einfache non-AI Verbesserung...") enhanced = _apply_face_enhancement(image) # Optional: AI-Verbesserung nur wenn nötig use_ai_enhancement = False # Auf False setzen für minimale Änderung if not use_ai_enhancement: duration = time.time() - start_time print(f"✅ Non-AI Verbesserung in {duration:.1f}s") return enhanced # OPTION 2: Minimale AI-Verbesserung (falls gewünscht) print("⚠️ Starte MINIMALE AI-Verbesserung...") if not _initialize_components(): return enhanced # Control Map vorbereiten original_size = image.size control_size = (512, 512) resized_image = image.resize(control_size, Image.Resampling.LANCZOS) # MINIMALE Pose Map pose_img = _extract_precise_pose(resized_image) pose_img.save("debug_minimal_pose.png") # Pipeline (nur falls nicht geladen) global _pipeline if _pipeline is None: try: print("🔄 Lade Pipeline...") _pipeline = StableDiffusionControlNetPipeline.from_pretrained( model_id, controlnet=[_controlnet_pose], # NUR OpenPose! torch_dtype=torch.float16, safety_checker=None, requires_safety_checker=False, ) _pipeline.enable_attention_slicing() _pipeline.enable_vae_slicing() print("✅ Pipeline geladen") except Exception as e: print(f"❌ Pipeline Fehler: {e}") return enhanced try: device = "cuda" if torch.cuda.is_available() else "cpu" print(f" Device: {device}") pipeline = _pipeline.to(device) # KRITISCHE ÄNDERUNGEN: # 1. GLEICHER PROMPT wie ursprünglich # 2. SEHR niedrige ControlNet-Stärke # 3. FAST KEIN CFG print("\n⚙️ EXTREM SUBTILE PARAMETER:") print(" • OpenPose Strength: 0.3 (SEHR NIEDRIG)") print(" • Steps: 15 (wenig)") print(" • CFG: 2.0 (fast kein Guidance)") print(" • Gleicher Seed") result = pipeline( prompt=prompt, # WICHTIG: GLEICHER PROMPT! negative_prompt=f"{negative_prompt}, deformed, blurry", image=[pose_img], # Nur Pose controlnet_conditioning_scale=[0.3], # EXTREM NIEDRIG num_inference_steps=15, # WENIG Steps guidance_scale=2.0, # FAST KEIN CFG generator=torch.Generator(device).manual_seed(seed + 100), # Leicht anderer Seed height=512, width=512, ).images[0] # Zurück auf Originalgröße if original_size != (512, 512): result = result.resize(original_size, Image.Resampling.LANCZOS) # 50/50 Blend mit Original für noch weniger Änderung result_array = np.array(result).astype(float) original_array = np.array(image).astype(float) # 70% Original, 30% AI-result blended = (original_array * 0.7 + result_array * 0.3).astype(np.uint8) final_result = Image.fromarray(blended) duration = time.time() - start_time print(f"\n✅ SUBTILE VERBESSERUNG in {duration:.1f}s") print(f" • 70% Original, 30% AI") print(f" • OpenPose: 0.3") print(f" • CFG: 2.0") return final_result except Exception as e: print(f"\n❌ AI-Verbesserung fehlgeschlagen: {e}") return enhanced print("="*60) print("FACE-FIX: REINE QUALITÄTSVERBESSERUNG") print("="*60)