Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -19,10 +19,11 @@ def remove_background(img: Image.Image) -> Image.Image:
|
|
| 19 |
r, g, b, _ = rgba.split()
|
| 20 |
return Image.merge("RGBA", (r, g, b, alpha))
|
| 21 |
|
| 22 |
-
def
|
| 23 |
"""
|
| 24 |
-
|
| 25 |
-
|
|
|
|
| 26 |
"""
|
| 27 |
rgba = image.convert("RGBA")
|
| 28 |
width, height = rgba.size
|
|
@@ -34,8 +35,6 @@ def blur_face_region(image: Image.Image) -> Image.Image:
|
|
| 34 |
|
| 35 |
# Load frontal face cascade
|
| 36 |
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
|
| 37 |
-
|
| 38 |
-
# Detect faces
|
| 39 |
faces = face_cascade.detectMultiScale(
|
| 40 |
gray,
|
| 41 |
scaleFactor=1.1,
|
|
@@ -43,31 +42,44 @@ def blur_face_region(image: Image.Image) -> Image.Image:
|
|
| 43 |
minSize=(25, 25)
|
| 44 |
)
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
region = result.crop((0, top, width, bottom))
|
| 67 |
-
blurred_region = region.filter(ImageFilter.GaussianBlur(radius=10))
|
| 68 |
-
result.paste(blurred_region, (0, top))
|
| 69 |
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
def extract_fine_details_cv2(img: Image.Image, detail_profile="balanced") -> Image.Image:
|
| 73 |
"""
|
|
@@ -87,9 +99,9 @@ def extract_fine_details_cv2(img: Image.Image, detail_profile="balanced") -> Ima
|
|
| 87 |
if detail_profile == "soft":
|
| 88 |
t1, t2 = 50, 150
|
| 89 |
elif detail_profile == "balanced":
|
| 90 |
-
t1, t2 =
|
| 91 |
else: # raw / detailed
|
| 92 |
-
t1, t2 = 12,
|
| 93 |
|
| 94 |
# Run Canny edge detector
|
| 95 |
edges = cv2.Canny(smoothed, t1, t2)
|
|
@@ -139,8 +151,8 @@ def process_faceless(input_image, prompt="", negative_prompt=""):
|
|
| 139 |
# 1. Remove background
|
| 140 |
subject = remove_background(input_image)
|
| 141 |
|
| 142 |
-
# 2.
|
| 143 |
-
preprocessed =
|
| 144 |
|
| 145 |
# 3. Extract line details using OpenCV Canny (highly superior thin lines)
|
| 146 |
details_alpha = extract_fine_details_cv2(preprocessed, detail_profile="balanced")
|
|
|
|
| 19 |
r, g, b, _ = rgba.split()
|
| 20 |
return Image.merge("RGBA", (r, g, b, alpha))
|
| 21 |
|
| 22 |
+
def blur_faces_smooth(image: Image.Image) -> tuple[Image.Image, list]:
|
| 23 |
"""
|
| 24 |
+
Detects faces using Haar Cascades and applies a smooth, feathered Gaussian blur
|
| 25 |
+
only to the face interiors. This avoids any box/crop lines in Canny and provides
|
| 26 |
+
a premium faceless look.
|
| 27 |
"""
|
| 28 |
rgba = image.convert("RGBA")
|
| 29 |
width, height = rgba.size
|
|
|
|
| 35 |
|
| 36 |
# Load frontal face cascade
|
| 37 |
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
|
|
|
|
|
|
|
| 38 |
faces = face_cascade.detectMultiScale(
|
| 39 |
gray,
|
| 40 |
scaleFactor=1.1,
|
|
|
|
| 42 |
minSize=(25, 25)
|
| 43 |
)
|
| 44 |
|
| 45 |
+
if len(faces) == 0:
|
| 46 |
+
# Fallback: soft feathered blur on upper center if no faces detected
|
| 47 |
+
mask = Image.new("L", (width, height), 0)
|
| 48 |
+
mask_np = np.array(mask)
|
| 49 |
+
# generic head ellipse at top center
|
| 50 |
+
cx, cy = width // 2, int(height * 0.25)
|
| 51 |
+
rx, ry = int(width * 0.25), int(height * 0.15)
|
| 52 |
+
cv2.ellipse(mask_np, (cx, cy), (rx, ry), 0, 0, 360, 255, -1)
|
| 53 |
+
mask = Image.fromarray(mask_np).filter(ImageFilter.GaussianBlur(radius=20))
|
| 54 |
+
|
| 55 |
+
blurred_img = rgba.filter(ImageFilter.GaussianBlur(radius=14))
|
| 56 |
+
return Image.composite(blurred_img, rgba, mask), []
|
| 57 |
+
|
| 58 |
+
# Create a smooth, feathered mask (0 = original, 255 = blurred)
|
| 59 |
+
mask = Image.new("L", (width, height), 0)
|
| 60 |
+
mask_np = np.array(mask)
|
| 61 |
+
|
| 62 |
+
for (x, y, w, h) in faces:
|
| 63 |
+
cx = x + w // 2
|
| 64 |
+
cy = y + h // 2
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
+
# Erase eyes, nose, mouth but keep eyebrows & ears by shaping the ellipse
|
| 67 |
+
# Height radius (ry) covers center of face. Shift center down slightly.
|
| 68 |
+
rx = int(w * 0.36)
|
| 69 |
+
ry = int(h * 0.38)
|
| 70 |
+
cy_shifted = cy + int(h * 0.06) # shift down to preserve eyebrows
|
| 71 |
+
|
| 72 |
+
cv2.ellipse(mask_np, (cx, cy_shifted), (rx, ry), 0, 0, 360, 255, -1)
|
| 73 |
+
|
| 74 |
+
mask = Image.fromarray(mask_np)
|
| 75 |
+
# Heavy blur on mask to feather edges completely smoothly
|
| 76 |
+
mask = mask.filter(ImageFilter.GaussianBlur(radius=12))
|
| 77 |
+
|
| 78 |
+
# Generate the blurred version of the face areas
|
| 79 |
+
blurred_img = rgba.filter(ImageFilter.GaussianBlur(radius=18))
|
| 80 |
+
|
| 81 |
+
# Composite the blurred face areas onto the original image
|
| 82 |
+
return Image.composite(blurred_img, rgba, mask), list(faces)
|
| 83 |
|
| 84 |
def extract_fine_details_cv2(img: Image.Image, detail_profile="balanced") -> Image.Image:
|
| 85 |
"""
|
|
|
|
| 99 |
if detail_profile == "soft":
|
| 100 |
t1, t2 = 50, 150
|
| 101 |
elif detail_profile == "balanced":
|
| 102 |
+
t1, t2 = 22, 85
|
| 103 |
else: # raw / detailed
|
| 104 |
+
t1, t2 = 12, 55
|
| 105 |
|
| 106 |
# Run Canny edge detector
|
| 107 |
edges = cv2.Canny(smoothed, t1, t2)
|
|
|
|
| 151 |
# 1. Remove background
|
| 152 |
subject = remove_background(input_image)
|
| 153 |
|
| 154 |
+
# 2. Smoothly blur face regions (removes eyes/mouth organically, keeps eyebrows/ears/jawline)
|
| 155 |
+
preprocessed, faces = blur_faces_smooth(subject)
|
| 156 |
|
| 157 |
# 3. Extract line details using OpenCV Canny (highly superior thin lines)
|
| 158 |
details_alpha = extract_fine_details_cv2(preprocessed, detail_profile="balanced")
|