RedBottle13 commited on
Commit
5326e84
·
verified ·
1 Parent(s): b292667

Upload 65 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. app.py +690 -0
  3. constants.py +62 -0
  4. requirements.txt +5 -0
  5. stickers/.DS_Store +0 -0
  6. stickers/animal face/.DS_Store +0 -0
  7. stickers/animal face/None.png +0 -0
  8. stickers/animal face/blowing.png +0 -0
  9. stickers/animal face/cool.png +0 -0
  10. stickers/animal face/cry.png +0 -0
  11. stickers/animal face/evil.png +0 -0
  12. stickers/animal face/laughing.png +0 -0
  13. stickers/animal face/logy.png +0 -0
  14. stickers/animal face/love.png +0 -0
  15. stickers/animal face/mysterious.png +0 -0
  16. stickers/ears/.DS_Store +0 -0
  17. stickers/ears/None.png +0 -0
  18. stickers/ears/bent.png +0 -0
  19. stickers/ears/bunny.png +0 -0
  20. stickers/ears/bunny2.png +0 -0
  21. stickers/ears/carrot.png +0 -0
  22. stickers/ears/fluffy.png +0 -0
  23. stickers/ears/fox.png +0 -0
  24. stickers/ears/tiger.png +0 -0
  25. stickers/ears/white.png +0 -0
  26. stickers/glasses/.DS_Store +0 -0
  27. stickers/glasses/None.png +0 -0
  28. stickers/glasses/blue.png +0 -0
  29. stickers/glasses/cool.png +0 -0
  30. stickers/glasses/gray.png +0 -0
  31. stickers/glasses/heart.png +0 -0
  32. stickers/glasses/pink.png +0 -0
  33. stickers/glasses/rainbow.png +0 -0
  34. stickers/glasses/reading.png +0 -0
  35. stickers/glasses/round.png +0 -0
  36. stickers/hats/.DS_Store +0 -0
  37. stickers/hats/Christmas.png +0 -0
  38. stickers/hats/None.png +0 -0
  39. stickers/hats/chef.png +0 -0
  40. stickers/hats/green.png +0 -0
  41. stickers/hats/mexican.png +0 -0
  42. stickers/hats/party.png +0 -0
  43. stickers/hats/santa.png +0 -0
  44. stickers/hats/top.png +0 -0
  45. stickers/hats/witch.png +0 -0
  46. stickers/headbands/.DS_Store +0 -0
  47. stickers/headbands/None.png +0 -0
  48. stickers/headbands/blue.png +0 -0
  49. stickers/headbands/bow.png +0 -0
  50. stickers/headbands/cat.png +0 -0
.gitattributes CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  shape_predictor_81_face_landmarks.dat filter=lfs diff=lfs merge=lfs -text
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  shape_predictor_81_face_landmarks.dat filter=lfs diff=lfs merge=lfs -text
37
+ stickers/noses/clown.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,690 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ from PIL import Image
5
+ import dlib
6
+ import os
7
+ import math
8
+
9
+ from constants import *
10
+
11
+ MAX_EXPECTED_FACES=7
12
+ # get a list of faces in the image
13
+ def face_detecting(image):
14
+ detector = dlib.get_frontal_face_detector()
15
+ faces = detector(image, 1)
16
+ return faces
17
+
18
+ # show all the faces in rectangles in the image
19
+ def face_showing(image, faces):
20
+ for face in faces:
21
+ cv2.rectangle(image, (face.left(), face.top()), (face.right(), face.bottom()), (255, 255, 255), 2)
22
+ return image
23
+
24
+ # highlight the selected face in the image, using index to select the face
25
+ def face_selecting(image, faces, index):
26
+ face = faces[index]
27
+ cv2.rectangle(image, (face.left(), face.top()), (face.right(), face.bottom()), (255, 255, 255), 2)
28
+ return image
29
+
30
+ # get the landmarks of the face
31
+ def face_landmarking(image, face):
32
+ predictor = dlib.shape_predictor('shape_predictor_81_face_landmarks.dat')
33
+ landmarks = predictor(image, face)
34
+ return landmarks
35
+
36
+
37
+ # Function to overlay a transparent image onto another image
38
+ def overlay_transparent(background, overlay, x, y):
39
+ bg_height, bg_width = background.shape[:2]
40
+ if x >= bg_width or y >= bg_height:
41
+ return background
42
+
43
+ h, w = overlay.shape[:2]
44
+ if x + w > bg_width:
45
+ w = bg_width - x
46
+ overlay = overlay[:, :w]
47
+
48
+ if y + h > bg_height:
49
+ h = bg_height - y
50
+ overlay = overlay[:h]
51
+
52
+ if overlay.shape[2] < 4:
53
+ overlay = np.concatenate([overlay, np.ones((overlay.shape[0], overlay.shape[1], 1), dtype=overlay.dtype) * 255], axis=2)
54
+
55
+ overlay_img = overlay[..., :3]
56
+ mask = overlay[..., 3:] / 255.0
57
+
58
+ background[y:y+h, x:x+w] = (1.0 - mask) * background[y:y+h, x:x+w] + mask * overlay_img
59
+
60
+ return background
61
+
62
+
63
+ def calculate_eye_angle(landmarks, left_eye_indices, right_eye_indices):
64
+
65
+ # Calculate the center point of the left eye
66
+ left_eye_center = (
67
+ sum([landmarks.part(i).x for i in left_eye_indices]) // len(left_eye_indices),
68
+ sum([landmarks.part(i).y for i in left_eye_indices]) // len(left_eye_indices)
69
+ )
70
+
71
+ # Calculate the center point of the right eye
72
+ right_eye_center = (
73
+ sum([landmarks.part(i).x for i in right_eye_indices]) // len(right_eye_indices),
74
+ sum([landmarks.part(i).y for i in right_eye_indices]) // len(right_eye_indices)
75
+ )
76
+
77
+ # Calculate the differences in the x and y coordinates between the centers of the eyes
78
+ dx = right_eye_center[0] - left_eye_center[0]
79
+ dy = right_eye_center[1] - left_eye_center[1]
80
+
81
+ # Calculate the angle using the arctangent of the differences
82
+ angle = math.degrees(math.atan2(dy, dx))
83
+
84
+ return angle
85
+
86
+
87
+ # Function to add ear stickers
88
+ def add_ears_sticker(img_bgr, sticker_path, faces):
89
+ ears_pil = Image.open(sticker_path)
90
+
91
+ # Check the color mode and convert to RGBA
92
+ ears_rgba = ears_pil.convert('RGBA')
93
+
94
+ # Convert the ears_rgba to BGRA
95
+ r, g, b, a = ears_rgba.split()
96
+ ears_bgra = Image.merge("RGBA", (b, g, r, a))
97
+
98
+
99
+ # A copy of the original image
100
+ img_with_stickers = img_bgr.copy()
101
+
102
+ for face in faces:
103
+ landmarks = face_landmarking(img_bgr, face)
104
+
105
+ # the landmarks 68 to 80 are for the forehead
106
+ forehead = [landmarks.part(i) for i in range(68, 81)]
107
+
108
+ # The landmarks 36 to 41 are for the left eye, and 42 to 47 are for the right eye
109
+ left_eye = [landmarks.part(i) for i in range(36, 42)]
110
+ right_eye = [landmarks.part(i) for i in range(42, 48)]
111
+
112
+ # Calculate the center point between the eyes
113
+ left_eye_center = ((left_eye[0].x + left_eye[3].x) // 2, (left_eye[0].y + left_eye[3].y) // 2)
114
+ right_eye_center = ((right_eye[0].x + right_eye[3].x) // 2, (right_eye[0].y + right_eye[3].y) // 2)
115
+
116
+ # Calculate the angle of tilt
117
+ dx = right_eye_center[0] - left_eye_center[0]
118
+ dy = right_eye_center[1] - left_eye_center[1]
119
+ angle = math.degrees(math.atan2(dy, dx))
120
+
121
+
122
+ # Calculate the bounding box for the ears based on the eye landmarks
123
+ ears_width = int(abs(forehead[0].x - forehead[-1].x) * 2.1)
124
+ ears_height = int(ears_width * ears_bgra.height / ears_bgra.width)
125
+
126
+ # Resize the ears image
127
+ resized_ears_pil = ears_bgra.resize((ears_width, ears_height))
128
+ rotated_ears = resized_ears_pil.rotate(-angle, expand=True, resample=Image.BICUBIC)
129
+
130
+ # Calculate the position for the ears
131
+ y1 = min([point.y for point in forehead]) - int(0.7 * ears_height)
132
+ x1 = forehead[0].x - int(0.2 * ears_width)
133
+
134
+ # Convert PIL image to NumPy array
135
+ # ears_np = np.array(resized_ears_pil)
136
+ ears_np = np.array(rotated_ears)
137
+
138
+ # Overlay the ears on the image
139
+ img_with_stickers = overlay_transparent(img_with_stickers, ears_np, x1, y1)
140
+
141
+ return img_with_stickers
142
+
143
+ # Function to add hats stickers
144
+ def add_hats_sticker(img_bgr, sticker_path, faces):
145
+ hat_pil = Image.open(sticker_path)
146
+
147
+ # Check the color mode and convert to RGBA
148
+ hat_rgba = hat_pil.convert('RGBA')
149
+
150
+ # Convert the hat_rgba to BGRA
151
+ r, g, b, a = hat_rgba.split()
152
+ hat_bgra = Image.merge("RGBA", (b, g, r, a))
153
+
154
+ # A copy of the original image
155
+ img_with_stickers = img_bgr.copy()
156
+
157
+ for face in faces:
158
+ landmarks = face_landmarking(img_bgr, face)
159
+
160
+ # The landmarks 36 to 41 are for the left eye, and 42 to 47 are for the right eye
161
+ left_eye = [landmarks.part(i) for i in range(36, 42)]
162
+ right_eye = [landmarks.part(i) for i in range(42, 48)]
163
+
164
+ forehead = [landmarks.part(i) for i in range(68, 81)]
165
+
166
+ # Calculate the center point between the eyes
167
+ left_eye_center = ((left_eye[0].x + left_eye[3].x) // 2, (left_eye[0].y + left_eye[3].y) // 2)
168
+ right_eye_center = ((right_eye[0].x + right_eye[3].x) // 2, (right_eye[0].y + right_eye[3].y) // 2)
169
+ eye_center_x = (left_eye_center[0] + right_eye_center[0]) // 2
170
+ eye_center_y = (left_eye_center[1] + right_eye_center[1]) // 2
171
+
172
+ # Calculate the angle of tilt
173
+ dx = right_eye_center[0] - left_eye_center[0]
174
+ dy = right_eye_center[1] - left_eye_center[1]
175
+ angle = math.degrees(math.atan2(dy, dx))
176
+
177
+ # Calculate the size of the hat based on the width between the eyes
178
+ hat_width = int(abs(left_eye[0].x - right_eye[3].x) * 1.75)
179
+ hat_height = int(hat_width * hat_bgra.height / hat_bgra.width)
180
+
181
+ # Resize and rotate the hat image
182
+ resized_hat = hat_bgra.resize((hat_width, hat_height))
183
+ rotated_hat = resized_hat.rotate(-0.8*angle, expand=True, resample=Image.BICUBIC)
184
+
185
+
186
+ # Calculate the position for the hat
187
+ y1 = eye_center_y - hat_height - int(0.3 * hat_height)
188
+ # x1 = eye_center_x - (hat_width // 2) # Centering the hat on the midpoint between the eyes
189
+ # x1 = eye_center_x - (hat_width // 2) - int(0.03 * hat_width) # Moving the hat a bit further to the left
190
+ x1 = forehead[0].x - int(0.2 * hat_width)
191
+ # Convert PIL image to NumPy array
192
+ hat_np = np.array(rotated_hat)
193
+
194
+ # Overlay the hat on the image
195
+ img_with_stickers = overlay_transparent(img_with_stickers, hat_np, x1, y1)
196
+
197
+ return img_with_stickers
198
+
199
+ def add_headbands_sticker(img_bgr, sticker_path, faces):
200
+ headband_pil = Image.open(sticker_path)
201
+
202
+ # Check the color mode and convert to RGBA
203
+ headband_rgba = headband_pil.convert('RGBA')
204
+
205
+ # Convert the headband_rgba to BGRA
206
+ r, g, b, a = headband_rgba.split()
207
+ headband_bgra = Image.merge("RGBA", (b, g, r, a))
208
+
209
+ # A copy of the original image
210
+ img_with_stickers = img_bgr.copy()
211
+
212
+ for face in faces:
213
+ landmarks = face_landmarking(img_bgr, face)
214
+
215
+ # Determine the forehead region using landmarks
216
+ # Assuming the headband will be placed between the temples
217
+ left_temple = landmarks.part(0)
218
+ right_temple = landmarks.part(16)
219
+
220
+ # Calculate the width of the headband based on the temples
221
+ headband_width = int(abs(left_temple.x - right_temple.x) * 1.6)
222
+ headband_height = int(headband_width * headband_bgra.height / headband_bgra.width)
223
+
224
+ # Resize the headband image
225
+ resized_headband = headband_bgra.resize((headband_width, headband_height))
226
+
227
+ # Calculate the angle of tilt using the eyes as reference
228
+ left_eye_indices = range(36, 42)
229
+ right_eye_indices = range(42, 48)
230
+ angle = calculate_eye_angle(landmarks, left_eye_indices, right_eye_indices)
231
+
232
+ # Rotate the headband image
233
+ rotated_headband = resized_headband.rotate(-angle, expand=True, resample=Image.BICUBIC)
234
+
235
+ # Calculate the position for the headband
236
+ x1 = (left_temple.x + right_temple.x) // 2 - (headband_width // 2)
237
+ y1 = left_temple.y - (headband_height // 2)
238
+
239
+ # Convert PIL image to NumPy array
240
+ headband_np = np.array(rotated_headband)
241
+
242
+ # Overlay the headband on the image
243
+ img_with_stickers = overlay_transparent(img_with_stickers, headband_np, x1, y1)
244
+
245
+ return img_with_stickers
246
+
247
+
248
+ # Function to add glasses stickers
249
+ def add_glasses_sticker(img_bgr, sticker_path, faces):
250
+ glasses_pil = Image.open(sticker_path)
251
+
252
+ # Check the color mode and convert to RGBA
253
+ glasses_rgba = glasses_pil.convert('RGBA')
254
+
255
+ # Convert the glasses_rgba to BGRA
256
+ r, g, b, a = glasses_rgba.split()
257
+ glasses_bgra = Image.merge("RGBA", (b, g, r, a))
258
+
259
+ # A copy of the original image
260
+ img_with_stickers = img_bgr.copy()
261
+
262
+ for face in faces:
263
+ landmarks = face_landmarking(img_bgr, face)
264
+
265
+ # the landmarks 36 to 41 are for the left eye, and 42 to 47 are for the right eye
266
+ left_eye = [landmarks.part(i) for i in range(36, 42)]
267
+ right_eye = [landmarks.part(i) for i in range(42, 48)]
268
+
269
+ # Calculate the center points of the eyes
270
+ left_eye_center = (sum([p.x for p in left_eye]) // len(left_eye), sum([p.y for p in left_eye]) // len(left_eye))
271
+ right_eye_center = (sum([p.x for p in right_eye]) // len(right_eye), sum([p.y for p in right_eye]) // len(right_eye))
272
+
273
+ # Calculate the angle of tilt
274
+ dx = right_eye_center[0] - left_eye_center[0]
275
+ dy = right_eye_center[1] - left_eye_center[1]
276
+ angle = math.degrees(math.atan2(dy, dx)) # Angle in degrees
277
+
278
+ # Calculate the bounding box for the glasses based on the eye landmarks
279
+ glasses_width = int(abs(left_eye_center[0] - right_eye_center[0]) * 2)
280
+ glasses_height = int(glasses_width * glasses_bgra.height / glasses_bgra.width)
281
+
282
+ # Resize and rotate the glasses image
283
+ resized_glasses = glasses_bgra.resize((glasses_width, glasses_height))
284
+ rotated_glasses = resized_glasses.rotate(-0.8*angle, expand=True, resample=Image.BICUBIC) # Negative angle to correct orientation
285
+
286
+ # Calculate the position for the glasses, adjusting for the rotation
287
+ x1 = left_eye_center[0] - int(0.25 * glasses_width)
288
+ y1 = min(left_eye_center[1], right_eye_center[1]) - int(0.45 * glasses_height)
289
+
290
+ # Convert PIL image to NumPy array
291
+ glasses_np = np.array(rotated_glasses)
292
+
293
+ # Overlay the glasses on the image
294
+ img_with_stickers = overlay_transparent(img_with_stickers, glasses_np, x1, y1)
295
+
296
+ return img_with_stickers
297
+
298
+
299
+
300
+ def add_noses_sticker(img_bgr, sticker_path, faces):
301
+ nose_pil = Image.open(sticker_path)
302
+
303
+ # Check the color mode and convert to RGBA
304
+ nose_rgba = nose_pil.convert('RGBA')
305
+
306
+ # Convert the nose_rgba to BGRA
307
+ r, g, b, a = nose_rgba.split()
308
+ nose_bgra = Image.merge("RGBA", (b, g, r, a))
309
+
310
+ # A copy of the original image
311
+ img_with_stickers = img_bgr.copy()
312
+
313
+ for face in faces:
314
+ landmarks = face_landmarking(img_bgr, face)
315
+
316
+ # Assuming that the landmarks 27 to 35 are for the nose area
317
+ nose_area = [landmarks.part(i) for i in range(27, 36)]
318
+
319
+ # Calculate the bounding box for the nose based on the nose landmarks
320
+ nose_width = int(abs(nose_area[0].x - nose_area[-1].x) * 2.1)
321
+ nose_height = int(nose_width * nose_bgra.height / nose_bgra.width)
322
+
323
+ # the landmarks 31 and 35 are the leftmost and rightmost points of the nose area
324
+ nose_left = landmarks.part(31)
325
+ nose_right = landmarks.part(35)
326
+
327
+ # Calculate the center point of the nose
328
+ nose_center_x = (nose_left.x + nose_right.x) // 2
329
+
330
+ nose_top = landmarks.part(27) # Use 28 if it's more accurate
331
+ nose_bottom = landmarks.part(33)
332
+
333
+ # Calculate the midpoint of the vertical length of the nose
334
+ nose_center_y = (nose_top.y + nose_bottom.y) // 2
335
+
336
+ # Calculate the angle of tilt using the eyes as reference
337
+ left_eye_indices = range(36, 42)
338
+ right_eye_indices = range(42, 48)
339
+ angle = calculate_eye_angle(landmarks, left_eye_indices, right_eye_indices)
340
+
341
+ # Resize the nose image
342
+ resized_nose_pil = nose_bgra.resize((nose_width, nose_height))
343
+
344
+ rotated_nose = resized_nose_pil.rotate(-angle, expand=True, resample=Image.BICUBIC)
345
+
346
+
347
+ # the position for the nose
348
+ x1 = nose_center_x - (nose_width // 2)
349
+ y1 = nose_center_y - (nose_height // 2)+ int(0.1 * nose_height) # Adding a slight downward offset
350
+ # Convert PIL image to NumPy array
351
+ nose_np = np.array(rotated_nose)
352
+
353
+ # Overlay the nose on the image
354
+ img_with_stickers = overlay_transparent(img_with_stickers, nose_np, x1, y1)
355
+
356
+ return img_with_stickers
357
+
358
+ def add_animal_faces_sticker(img_bgr, sticker_path, faces):
359
+ animal_face_pil = Image.open(sticker_path)
360
+
361
+ # Check the color mode and convert to RGBA
362
+ animal_face_rgba = animal_face_pil.convert('RGBA')
363
+
364
+ # Convert the animal_face_rgba to BGRA
365
+ r, g, b, a = animal_face_rgba.split()
366
+ animal_face_bgra = Image.merge("RGBA", (b, g, r, a))
367
+
368
+ # A copy of the original image
369
+ img_with_stickers = img_bgr.copy()
370
+
371
+ for face in faces:
372
+ landmarks = face_landmarking(img_bgr, face)
373
+
374
+ # Find the top of the forehead using landmarks above the eyes
375
+ # Assuming landmarks 19 to 24 represent the eyebrows
376
+ forehead_top = min(landmarks.part(i).y for i in range(68, 81))
377
+
378
+ # Calculate the center point between the eyes as an anchor
379
+ left_eye = [landmarks.part(i) for i in range(36, 42)]
380
+ right_eye = [landmarks.part(i) for i in range(42, 48)]
381
+ eye_center_x = (left_eye[0].x + right_eye[3].x) // 2
382
+ eye_center_y = (left_eye[3].y + right_eye[0].y) // 2
383
+
384
+ # Calculate the size of the animal face sticker based on the width between the temples
385
+ head_width = int(abs(landmarks.part(0).x - landmarks.part(16).x)*1.4)
386
+ head_height = int(head_width * animal_face_bgra.height *1.2 / animal_face_bgra.width)
387
+
388
+
389
+ # Calculate the angle of tilt using the eyes as reference
390
+ left_eye_indices = range(36, 42)
391
+ right_eye_indices = range(42, 48)
392
+ angle = calculate_eye_angle(landmarks, left_eye_indices, right_eye_indices)
393
+
394
+ # Resize the animal face sticker
395
+ resized_animal_face = animal_face_bgra.resize((head_width, head_height))
396
+
397
+ rotated_animal_face = resized_animal_face.rotate(-angle, expand=True, resample=Image.BICUBIC)
398
+
399
+
400
+ # Calculate the position for the animal face sticker
401
+ x1 = eye_center_x - (head_width // 2)
402
+ y1 = forehead_top - int(0.18 * head_height)
403
+
404
+ # Convert PIL image to NumPy array
405
+ animal_face_np = np.array(rotated_animal_face)
406
+
407
+ # Overlay the animal face on the image
408
+ img_with_stickers = overlay_transparent(img_with_stickers, animal_face_np, x1, y1)
409
+
410
+ return img_with_stickers
411
+
412
+
413
+ # Function to process the image
414
+ def process_image(image, sticker_choice):
415
+ if sticker_choice:
416
+ # add .png to the sticker_choice
417
+ sticker_name = sticker_choice + '.png'
418
+ # find sticker's category
419
+ sticker_category = STICKER_TO_CATEGORY[sticker_name]
420
+ # Path to the single sticker
421
+ sticker_path = os.path.join('stickers',sticker_category, sticker_name)
422
+
423
+ # Convert PIL image to OpenCV format BGR
424
+ image_bgr = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
425
+
426
+ # Detect faces
427
+ faces = face_detecting(image_bgr)
428
+ # print(sticker_category)
429
+ if sticker_category == 'ears':
430
+ img_with_stickers = add_ears_sticker(image_bgr, sticker_path, faces)
431
+ elif sticker_category == 'glasses':
432
+ img_with_stickers = add_glasses_sticker(image_bgr, sticker_path, faces)
433
+ elif sticker_category == 'noses':
434
+ img_with_stickers = add_noses_sticker(image_bgr, sticker_path, faces)
435
+ elif sticker_category == 'headbands':
436
+ img_with_stickers = add_ears_sticker(image_bgr, sticker_path, faces)
437
+ else:
438
+ img_with_stickers = add_glasses_sticker(image_bgr, sticker_path, faces)
439
+ # Convert back to PIL image
440
+ img_with_stickers_pil = Image.fromarray(cv2.cvtColor(img_with_stickers, cv2.COLOR_BGR2RGB))
441
+ return img_with_stickers_pil
442
+ else:
443
+ return image
444
+
445
+ def process_image_with_selections(image_input):
446
+ # Convert PIL image to OpenCV format BGR
447
+ image_bgr = cv2.cvtColor(np.array(image_input), cv2.COLOR_RGB2BGR)
448
+
449
+ # Detect faces
450
+ faces = face_detecting(image_bgr)
451
+
452
+ # A copy of the original image to apply stickers on
453
+ img_with_stickers = image_bgr.copy()
454
+
455
+ for category, sticker_name in sticker_selections.items():
456
+ if sticker_name: # Check if a sticker was selected in this category
457
+ # the sticker file path
458
+ sticker_path = os.path.join('stickers', category, sticker_name + '.png')
459
+
460
+ # Apply the selected sticker based on its category
461
+ if category == 'ears':
462
+ img_with_stickers = add_ears_sticker(img_with_stickers, sticker_path, faces)
463
+ elif category == 'glasses':
464
+ img_with_stickers = add_glasses_sticker(img_with_stickers, sticker_path, faces)
465
+ elif category == 'noses':
466
+ img_with_stickers = add_noses_sticker(img_with_stickers, sticker_path, faces)
467
+ elif category == 'headbands':
468
+ img_with_stickers = add_hats_sticker(img_with_stickers, sticker_path, faces)
469
+ elif category == 'hats':
470
+ img_with_stickers = add_hats_sticker(img_with_stickers, sticker_path, faces)
471
+ elif category == 'animal face':
472
+ img_with_stickers = add_animal_faces_sticker(img_with_stickers, sticker_path, faces)
473
+ else:
474
+ img_with_stickers = img_with_stickers
475
+ # Convert back to PIL image
476
+ img_with_stickers_pil = Image.fromarray(cv2.cvtColor(img_with_stickers, cv2.COLOR_BGR2RGB))
477
+
478
+ print("Selected stickers:")
479
+ for category, selection in sticker_selections.items():
480
+ print(f"{category}: {selection}")
481
+
482
+ return img_with_stickers_pil
483
+
484
+ # This dictionary will hold the user's sticker selections
485
+ sticker_selections = {}
486
+
487
+ # Function to update sticker selections
488
+ def update_selections(category, selection):
489
+ # sticker_selections[category] = selection
490
+ sticker_selections[category] = None if selection == "None" else selection
491
+ return ""
492
+
493
+ # Function to load an example image
494
+ def load_example_image(image_path):
495
+ return gr.Image.from_file(image_path)
496
+
497
+ from PIL import Image
498
+
499
+ def resize_image(image, target_width, target_height):
500
+ # Maintain aspect ratio
501
+ original_width, original_height = image.size
502
+ ratio = min(target_width/original_width, target_height/original_height)
503
+ new_width = int(original_width * ratio)
504
+ new_height = int(original_height * ratio)
505
+ # Use Image.LANCZOS for high-quality downsampling
506
+ resized_image = image.resize((new_width, new_height), Image.LANCZOS)
507
+ return resized_image
508
+
509
+ def get_face_crops(image_bgr, faces, target_width=500, target_height=130):
510
+ face_crops = []
511
+ for face in faces:
512
+ x, y, w, h = face.left(), face.top(), face.width(), face.height()
513
+ face_crop = image_bgr[y:y+h, x:x+w]
514
+ face_pil = Image.fromarray(cv2.cvtColor(face_crop, cv2.COLOR_BGR2RGB))
515
+ # Resize image to fit the display while maintaining aspect ratio
516
+ resized_face = resize_image(face_pil, target_width, target_height)
517
+ face_crops.append(resized_face)
518
+ return face_crops
519
+
520
+
521
+ def get_face_crops2(image_bgr, faces):
522
+ # Convert color space from BGR to RGB since OpenCV uses BGR by default
523
+ image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
524
+ face_crops = []
525
+ for face in faces:
526
+ # Extract the region of interest (the face) from the original image
527
+ x, y, w, h = face.left(), face.top(), face.width(), face.height()
528
+ face_crop = image_rgb[y:y+h, x:x+w]
529
+ face_pil = Image.fromarray(face_crop)
530
+ face_crops.append(face_pil)
531
+ return face_crops
532
+
533
+ # Function to process uploaded images and display face crops
534
+ def process_and_show_faces(image_input):
535
+ # Convert PIL image to OpenCV format BGR
536
+ image_bgr = cv2.cvtColor(np.array(image_input), cv2.COLOR_RGB2BGR)
537
+ # Detect faces
538
+ faces = face_detecting(image_bgr)
539
+ # Get individual face crops
540
+ face_crops = get_face_crops(image_bgr, faces)
541
+ # Return face crops to display them in the interface
542
+ return face_crops
543
+
544
+
545
+ face_outputs = []
546
+ for i in range(MAX_EXPECTED_FACES):
547
+ face_output = gr.Image(label=f"Face {i+1}")
548
+ face_outputs.append(face_output)
549
+
550
+ # This list will hold the Checkbox components for each face
551
+ checkboxes = []
552
+
553
+ def process_selected_faces(image_input, selected_face_indices):
554
+ # Convert PIL image to OpenCV format BGR
555
+ image_bgr = cv2.cvtColor(np.array(image_input), cv2.COLOR_RGB2BGR)
556
+
557
+ # Detect all faces
558
+ all_faces = face_detecting(image_bgr)
559
+
560
+ # Filter faces to get only those selected
561
+ faces = [all_faces[i] for i in selected_face_indices]
562
+
563
+ img_with_stickers = image_bgr.copy()
564
+
565
+ for category, sticker_name in sticker_selections.items():
566
+ if sticker_name: # Check if a sticker was selected in this category
567
+ # the sticker file path
568
+ sticker_path = os.path.join('stickers', category, sticker_name + '.png')
569
+
570
+ # Apply the selected sticker based on its category
571
+ if category == 'ears':
572
+ img_with_stickers = add_ears_sticker(img_with_stickers, sticker_path, faces)
573
+ elif category == 'glasses':
574
+ img_with_stickers = add_glasses_sticker(img_with_stickers, sticker_path, faces)
575
+ elif category == 'noses':
576
+ img_with_stickers = add_noses_sticker(img_with_stickers, sticker_path, faces)
577
+ elif category == 'headbands':
578
+ img_with_stickers = add_hats_sticker(img_with_stickers, sticker_path, faces)
579
+ elif category == 'hats':
580
+ img_with_stickers = add_hats_sticker(img_with_stickers, sticker_path, faces)
581
+ elif category == 'animal face':
582
+ img_with_stickers = add_animal_faces_sticker(img_with_stickers, sticker_path, faces)
583
+ else:
584
+ img_with_stickers = img_with_stickers
585
+ # Convert back to PIL image
586
+ img_with_stickers_pil = Image.fromarray(cv2.cvtColor(img_with_stickers, cv2.COLOR_BGR2RGB))
587
+
588
+ print("Selected stickers:")
589
+ for category, selection in sticker_selections.items():
590
+ print(f"{category}: {selection}")
591
+
592
+ return img_with_stickers_pil
593
+
594
+ def handle_face_selection(image_input, *checkbox_states):
595
+ selected_face_indices = [i for i, checked in enumerate(checkbox_states) if checked]
596
+ print("selected_face_indices:",selected_face_indices)
597
+ return process_selected_faces(image_input, selected_face_indices)
598
+
599
+ def update_interface_with_faces(image_input):
600
+ image_bgr = cv2.cvtColor(np.array(image_input), cv2.COLOR_RGB2BGR)
601
+ faces = face_detecting(image_bgr)
602
+ face_crops = get_face_crops(image_bgr, faces)
603
+ return [(face, f"Face {i+1}") for i, face in enumerate(face_crops)]
604
+
605
+ def detect_and_display_faces(image_input):
606
+ image_bgr = cv2.cvtColor(np.array(image_input), cv2.COLOR_RGB2BGR)
607
+ faces = face_detecting(image_bgr)
608
+ face_crops = get_face_crops(image_bgr, faces)
609
+ if not face_crops:
610
+ # Return empty images and unchecked boxes if no faces are detected
611
+ return [None] * MAX_EXPECTED_FACES + [False] * MAX_EXPECTED_FACES
612
+ # Return face crops and True for each checkbox to indicate they should be checked
613
+ # Pad the list with None and False if fewer faces than MAX_EXPECTED_FACES are detected
614
+ output = face_crops + [None] * (MAX_EXPECTED_FACES - len(face_crops))
615
+ output += [True] * len(face_crops) + [False] * (MAX_EXPECTED_FACES - len(face_crops))
616
+ return output
617
+
618
+
619
+ css = """
620
+ #category {
621
+ padding-left: 100px;
622
+ font-size: 20px;
623
+ font-weight: bold;
624
+ margin-top: 20px;
625
+ }
626
+
627
+ #sticker {
628
+ height: 130px;
629
+ width: 30px;
630
+ padding: 10px;
631
+ }
632
+ .radio {
633
+ display: flex;
634
+ justify-content: space-around;
635
+ }
636
+ """
637
+
638
+ # Create the Gradio interface
639
+ with gr.Blocks(css=css) as demo:
640
+
641
+ with gr.Row():
642
+ with gr.Column():
643
+ image_input = gr.Image(type="pil", label="Original Image")
644
+ with gr.Column():
645
+ output_image = gr.Image(label="Image with Stickers")
646
+ # Prepare the checkboxes and image placeholders
647
+ detect_faces_btn = gr.Button("Detect Faces")
648
+
649
+ with gr.Row():
650
+ face_checkboxes = [gr.Checkbox(label=f"Face {i+1}") for i in range(7)]
651
+
652
+ with gr.Row():
653
+ face_images = [gr.Image(height=150, width=100, min_width=30, interactive=False, show_download_button=False) for i in range(7)]
654
+
655
+ detect_faces_btn.click(
656
+ detect_and_display_faces,
657
+ inputs=[image_input],
658
+ outputs=face_images + face_checkboxes
659
+ )
660
+
661
+ process_button = gr.Button("Apply Stickers To Selected Faces")
662
+
663
+ process_button.click(
664
+ handle_face_selection,
665
+ inputs=[image_input] + face_checkboxes,
666
+ outputs=output_image
667
+ )
668
+
669
+ # Iterate over each category to create a row for the category
670
+ for category, stickers in STICKER_PATHS.items():
671
+ with gr.Row():
672
+ with gr.Column(scale=1, elem_id="category_row"):
673
+ gr.Markdown(f"## {category}", elem_id="category")
674
+ with gr.Column(scale=10):
675
+ # Iterate over stickers in sets of 10
676
+ for i in range(0, len(stickers), 10):
677
+ with gr.Row():
678
+ for sticker_path in stickers[i:i+10]:
679
+ gr.Image(value=sticker_path, min_width=50, interactive=False, show_download_button=False, container=False, elem_id="sticker")
680
+ with gr.Row():
681
+ # radio = gr.Radio(label=' ', choices=[stickers[i].split('/')[-1].replace('.png', '') for i in range(len(stickers))], container=False, min_width=50)
682
+ choices = [sticker.split('/')[-1].replace('.png', '') for sticker in stickers]
683
+ radio = gr.Radio(label='', choices=choices, value="None", container=False, min_width=50, elem_classes="radio")
684
+ radio.change(lambda selection, cat=category: update_selections(cat, selection), inputs=[radio], outputs=[])
685
+
686
+
687
+
688
+ demo.launch()
689
+
690
+
constants.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sticker's paths for each category
2
+ STICKER_PATHS = {'hats': ['./stickers/hats/None.png', './stickers/hats/santa.png', './stickers/hats/top.png', './stickers/hats/party.png', './stickers/hats/witch.png', './stickers/hats/green.png', './stickers/hats/chef.png', './stickers/hats/Christmas.png', './stickers/hats/mexican.png'],
3
+ 'animal face': ['./stickers/animal face/None.png', './stickers/animal face/laughing.png', './stickers/animal face/cool.png', './stickers/animal face/love.png', './stickers/animal face/cry.png', './stickers/animal face/evil.png', './stickers/animal face/blowing.png', './stickers/animal face/logy.png', './stickers/animal face/mysterious.png'],
4
+ 'ears': ['./stickers/ears/None.png', './stickers/ears/bunny.png', './stickers/ears/fox.png', './stickers/ears/fluffy.png', './stickers/ears/carrot.png', './stickers/ears/tiger.png', './stickers/ears/white.png', './stickers/ears/bent.png', './stickers/ears/bunny2.png'],
5
+ 'glasses': ['./stickers/glasses/None.png', './stickers/glasses/heart.png', './stickers/glasses/round.png', './stickers/glasses/pink.png', './stickers/glasses/cool.png', './stickers/glasses/gray.png', './stickers/glasses/blue.png', './stickers/glasses/rainbow.png', './stickers/glasses/reading.png'],
6
+ 'noses': ['./stickers/noses/None.png', './stickers/noses/clown.png', './stickers/noses/pig.png', './stickers/noses/dog1.png', './stickers/noses/dog2.png', './stickers/noses/dog3.png', './stickers/noses/dog4.png', './stickers/noses/dog5.png', './stickers/noses/dog6.png'],
7
+ 'headbands': ['./stickers/headbands/None.png', './stickers/headbands/deer.png', './stickers/headbands/blue.png', './stickers/headbands/bow.png', './stickers/headbands/flower.png', './stickers/headbands/devil.png', './stickers/headbands/cat.png', './stickers/headbands/christmas.png', './stickers/headbands/green.png']}
8
+
9
+
10
+
11
+
12
+ # Creating a new dictionary with sticker name as key and category as value
13
+ STICKER_TO_CATEGORY = {
14
+ 'santa.png': 'hats',
15
+ 'top.png': 'hats',
16
+ 'party.png': 'hats',
17
+ 'mexican.png': 'hats',
18
+ 'witch.png': 'hats',
19
+ 'green.png': 'hats',
20
+ 'chef.png': 'hats',
21
+ 'Christmas.png': 'hats',
22
+ 'laughing.png': 'animal face',
23
+ 'cool.png': 'animal face',
24
+ 'love.png': 'animal face',
25
+ 'cry.png': 'animal face',
26
+ 'evil.png': 'animal face',
27
+ 'blowing.png': 'animal face',
28
+ 'logy.png': 'animal face',
29
+ 'mysterious.png': 'animal face',
30
+ 'bunny.png': 'ears',
31
+ 'fox.png': 'ears',
32
+ 'fluffy.png': 'ears',
33
+ 'carrot.png': 'ears',
34
+ 'tiger.png': 'ears',
35
+ 'white.png': 'ears',
36
+ 'bent.png': 'ears',
37
+ 'bunny2.png': 'ears',
38
+ 'heart.png': 'glasses',
39
+ 'round.png': 'glasses',
40
+ 'pink.png': 'glasses',
41
+ 'cool.png': 'glasses',
42
+ 'gray.png': 'glasses',
43
+ 'blue.png': 'glasses',
44
+ 'rainbow.png': 'glasses',
45
+ 'reading.png': 'glasses',
46
+ 'dog1.png': 'noses',
47
+ 'pig.png': 'noses',
48
+ 'clown.png': 'noses',
49
+ 'dog2.png': 'noses',
50
+ 'dog3.png': 'noses',
51
+ 'dog4.png': 'noses',
52
+ 'dog5.png': 'noses',
53
+ 'dog6.png': 'noses',
54
+ 'devil.png': 'headbands',
55
+ 'cat.png': 'headbands',
56
+ 'christmas.png': 'headbands',
57
+ 'blue.png': 'headbands',
58
+ 'bow.png': 'headbands',
59
+ 'deer.png': 'headbands',
60
+ 'flower.png': 'headbands',
61
+ 'green.png': 'headbands',
62
+ }
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ opencv-python
2
+ dlib
3
+ matplotlib
4
+ numpy
5
+ gradio
stickers/.DS_Store ADDED
Binary file (10.2 kB). View file
 
stickers/animal face/.DS_Store ADDED
Binary file (6.15 kB). View file
 
stickers/animal face/None.png ADDED
stickers/animal face/blowing.png ADDED
stickers/animal face/cool.png ADDED
stickers/animal face/cry.png ADDED
stickers/animal face/evil.png ADDED
stickers/animal face/laughing.png ADDED
stickers/animal face/logy.png ADDED
stickers/animal face/love.png ADDED
stickers/animal face/mysterious.png ADDED
stickers/ears/.DS_Store ADDED
Binary file (6.15 kB). View file
 
stickers/ears/None.png ADDED
stickers/ears/bent.png ADDED
stickers/ears/bunny.png ADDED
stickers/ears/bunny2.png ADDED
stickers/ears/carrot.png ADDED
stickers/ears/fluffy.png ADDED
stickers/ears/fox.png ADDED
stickers/ears/tiger.png ADDED
stickers/ears/white.png ADDED
stickers/glasses/.DS_Store ADDED
Binary file (6.15 kB). View file
 
stickers/glasses/None.png ADDED
stickers/glasses/blue.png ADDED
stickers/glasses/cool.png ADDED
stickers/glasses/gray.png ADDED
stickers/glasses/heart.png ADDED
stickers/glasses/pink.png ADDED
stickers/glasses/rainbow.png ADDED
stickers/glasses/reading.png ADDED
stickers/glasses/round.png ADDED
stickers/hats/.DS_Store ADDED
Binary file (6.15 kB). View file
 
stickers/hats/Christmas.png ADDED
stickers/hats/None.png ADDED
stickers/hats/chef.png ADDED
stickers/hats/green.png ADDED
stickers/hats/mexican.png ADDED
stickers/hats/party.png ADDED
stickers/hats/santa.png ADDED
stickers/hats/top.png ADDED
stickers/hats/witch.png ADDED
stickers/headbands/.DS_Store ADDED
Binary file (6.15 kB). View file
 
stickers/headbands/None.png ADDED
stickers/headbands/blue.png ADDED
stickers/headbands/bow.png ADDED
stickers/headbands/cat.png ADDED