Spaces:
Sleeping
Sleeping
File size: 6,136 Bytes
7dcd378 ef2c483 7dcd378 5dbb4c9 7dcd378 5dbb4c9 7dcd378 ef2c483 7dcd378 ef2c483 5dbb4c9 ef2c483 5dbb4c9 7dcd378 ef2c483 7dcd378 5dbb4c9 7dcd378 5dbb4c9 7dcd378 ef2c483 7dcd378 ef2c483 7dcd378 5dbb4c9 7dcd378 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | import os
import cv2
import numpy as np
import torch
import torchvision.transforms as transforms
from PIL import Image
from insightface.app import FaceAnalysis
from huggingface_hub import hf_hub_download
from utils.bisenet import BiSeNet
class FacePreprocessor:
def __init__(self, device='cuda'):
self.device = device if torch.cuda.is_available() else 'cpu'
print(f"π§ Initializing Face Preprocessor on: {self.device}")
# Initialize InsightFace with better error handling
print(" - Loading InsightFace...")
try:
self.app = FaceAnalysis(
name='antelopev2',
root='./third_party_files',
providers=['CPUExecutionProvider']
)
self.app.prepare(ctx_id=0, det_size=(640, 640))
print(" β
InsightFace loaded successfully")
except AssertionError as e:
print(" β οΈ InsightFace antelopev2 failed, trying buffalo_l...")
try:
# Fallback to buffalo_l model
self.app = FaceAnalysis(
name='buffalo_l',
root='./third_party_files',
providers=['CPUExecutionProvider']
)
self.app.prepare(ctx_id=0, det_size=(640, 640))
print(" β
InsightFace buffalo_l loaded successfully")
except Exception as e2:
print(f" β Both InsightFace models failed: {str(e2)}")
raise RuntimeError("Could not load InsightFace models") from e2
# Initialize BiSeNet for segmentation
print(" - Loading BiSeNet for background extraction...")
self.bisenet = BiSeNet(n_classes=19).to(self.device)
# Download BiSeNet weights from YOUR affecto-faceparsing repo
print(" - Downloading BiSeNet weights from HuggingFace...")
try:
bisenet_path = hf_hub_download(
repo_id="gauravvjhaa/affecto-faceparsing", # β YOUR REPO
filename="79999_iter.pth",
cache_dir="./models"
)
print(f" - Loading BiSeNet from: {bisenet_path}")
self.bisenet.load_state_dict(
torch.load(bisenet_path, map_location=self.device)
)
print(" β
BiSeNet loaded successfully!")
except Exception as e:
print(f" β οΈ BiSeNet download error: {str(e)}")
print(f" β οΈ Error details: {repr(e)}")
print(" β οΈ Using random initialization (background extraction will be poor)")
self.bisenet.eval()
# Transforms
self.to_tensor = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
print("β
Preprocessor ready!")
def detect_and_crop_face(self, image):
"""Detect face and crop to 512x512"""
print(" πΈ Detecting face...")
img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
try:
faces = self.app.get(img_cv)
except Exception as e:
print(f" β οΈ Face detection error: {str(e)}")
raise ValueError(f"Face detection failed: {str(e)}")
if len(faces) == 0:
raise ValueError("β No face detected!")
if len(faces) > 1:
print(" β οΈ Multiple faces, using largest")
face = sorted(faces, key=lambda x: (x['bbox'][2] - x['bbox'][0]) * (x['bbox'][3] - x['bbox'][1]))[-1]
bbox = face['bbox']
x1, y1, x2, y2 = bbox
w = x2 - x1
h = y2 - y1
center_x = (x1 + x2) / 2
center_y = (y1 + y2) / 2
size = max(w, h) * 1.5
x1_new = int(center_x - size / 2)
y1_new = int(center_y - size / 2)
x2_new = int(center_x + size / 2)
y2_new = int(center_y + size / 2)
# Handle boundaries
h_img, w_img = img_cv.shape[:2]
x1_new = max(0, x1_new)
y1_new = max(0, y1_new)
x2_new = min(w_img, x2_new)
y2_new = min(h_img, y2_new)
img_crop = img_cv[y1_new:y2_new, x1_new:x2_new]
# Check if crop is valid
if img_crop.size == 0:
raise ValueError("Invalid crop region")
img_crop_512 = cv2.resize(img_crop, (512, 512), interpolation=cv2.INTER_LINEAR)
img_pil = Image.fromarray(cv2.cvtColor(img_crop_512, cv2.COLOR_BGR2RGB))
print(" β
Face cropped to 512x512")
return img_pil
def extract_background(self, image):
"""Extract background using BiSeNet"""
print(" π¨ Extracting background...")
img_tensor = self.to_tensor(image).unsqueeze(0).to(self.device)
with torch.no_grad():
out = self.bisenet(img_tensor)[0]
parsing = out.squeeze(0).cpu().numpy().argmax(0)
img_np = np.array(image)
mask = np.zeros_like(img_np)
# Set face regions to black (classes 1-18 are face parts)
for i in range(1, 19):
mask[parsing == i] = [0, 0, 0]
# Background is class 0
mask[parsing == 0] = img_np[parsing == 0]
bg_pil = Image.fromarray(mask)
print(" β
Background extracted")
return bg_pil
def preprocess(self, image):
"""Full preprocessing pipeline"""
try:
cropped = self.detect_and_crop_face(image)
bg = self.extract_background(cropped)
return cropped, bg
except Exception as e:
print(f"β Preprocessing error: {str(e)}")
print(" β οΈ Using fallback (simple resize)")
img_resized = image.resize((512, 512), Image.LANCZOS)
from PIL import ImageFilter
bg = img_resized.filter(ImageFilter.GaussianBlur(radius=10))
return img_resized, bg |