vitordigitizing commited on
Commit
7f85ca5
·
verified ·
1 Parent(s): ea9073d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -33
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 blur_face_region(image: Image.Image) -> Image.Image:
23
  """
24
- Uses OpenCV Haar Cascade to automatically detect all faces in the image
25
- and blurs only those bounding boxes, keeping the rest of the image details intact.
 
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
- result = rgba.copy()
47
-
48
- if len(faces) > 0:
49
- for (x, y, w, h) in faces:
50
- # Expand box slightly to cover ears, eyebrows, forehead and chin
51
- pad_x = int(w * 0.1)
52
- pad_y = int(h * 0.15)
53
- x1 = max(0, x - pad_x)
54
- y1 = max(0, y - pad_y * 2) # extend upwards for forehead
55
- x2 = min(width, x + w + pad_x)
56
- y2 = min(height, y + h + pad_y)
57
-
58
- # Crop, blur, and paste back
59
- region = result.crop((x1, y1, x2, y2))
60
- blurred_region = region.filter(ImageFilter.GaussianBlur(radius=16))
61
- result.paste(blurred_region, (x1, y1))
62
- else:
63
- # Fallback: soft general blur only on upper 30% center if no faces detected
64
- top = max(0, int(height * 0.1))
65
- bottom = min(height, int(height * 0.4))
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
- return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 = 25, 90
91
  else: # raw / detailed
92
- t1, t2 = 12, 60
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. Blur face region to keep it faceless (no eyes, eyebrows, mouth)
143
- preprocessed = blur_face_region(subject)
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")