Siyun He commited on
Commit
7aff7cc
·
1 Parent(s): e9eb9ec

add face shape detection

Browse files
Files changed (2) hide show
  1. app.py +39 -21
  2. requirements.txt +2 -1
app.py CHANGED
@@ -3,12 +3,17 @@ import cvzone
3
  import numpy as np
4
  import os
5
  import gradio as gr
 
6
  from datetime import datetime
7
 
8
  # Load the YuNet model
9
  model_path = 'face_detection_yunet_2023mar.onnx'
10
  face_detector = cv2.FaceDetectorYN.create(model_path, "", (320, 320))
11
 
 
 
 
 
12
  # Initialize the glass number
13
  num = 1
14
  overlay = cv2.imread(f'glasses/glass{num}.png', cv2.IMREAD_UNCHANGED)
@@ -20,6 +25,19 @@ def count_files_in_directory(directory):
20
  file_count += len(files)
21
  return file_count
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  directory_path = 'glasses'
24
  total_glass_num = count_files_in_directory(directory_path)
25
 
@@ -32,62 +50,59 @@ def change_glasses():
32
  overlay = cv2.imread(f'glasses/glass{num}.png', cv2.IMREAD_UNCHANGED)
33
  return overlay
34
 
35
- # Process frame for overlay
36
  def process_frame(frame):
37
  global overlay
38
- # Ensure the frame is writable
39
  frame = np.array(frame, copy=True)
40
-
41
  height, width = frame.shape[:2]
42
  face_detector.setInputSize((width, height))
43
  _, faces = face_detector.detect(frame)
44
 
 
45
  if faces is not None:
46
  for face in faces:
47
  x, y, w, h = face[:4].astype(int)
48
- face_landmarks = face[4:14].reshape(5, 2).astype(int) # Facial landmarks
 
 
 
 
 
 
 
 
49
 
50
  # Get the nose position
51
  nose_x, nose_y = face_landmarks[2].astype(int)
52
- # Left and right eye positions
53
  left_eye_x, left_eye_y = face_landmarks[0].astype(int)
54
  right_eye_x, right_eye_y = face_landmarks[1].astype(int)
55
 
56
- # Calculate the midpoint between the eyes
57
  eye_center_x = (left_eye_x + right_eye_x) // 2
58
  eye_center_y = (left_eye_y + right_eye_y) // 2
59
 
60
- # Calculate the angle of rotation
61
  delta_x = right_eye_x - left_eye_x
62
  delta_y = right_eye_y - left_eye_y
63
  angle = np.degrees(np.arctan2(delta_y, delta_x))
64
-
65
- # Negate the angle to rotate in the opposite direction
66
  angle = -angle
67
 
68
- # Resize the overlay
69
  overlay_resize = cv2.resize(overlay, (int(w * 1.15), int(h * 0.8)))
70
-
71
- # Rotate the overlay
72
  overlay_center = (overlay_resize.shape[1] // 2, overlay_resize.shape[0] // 2)
73
  rotation_matrix = cv2.getRotationMatrix2D(overlay_center, angle, 1.0)
74
  overlay_rotated = cv2.warpAffine(
75
- overlay_resize, rotation_matrix,
76
  (overlay_resize.shape[1], overlay_resize.shape[0]),
77
  flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0)
78
  )
79
 
80
- # Calculate the position to center the glasses on the eyes
81
  overlay_x = eye_center_x - overlay_rotated.shape[1] // 2
82
  overlay_y = eye_center_y - overlay_rotated.shape[0] // 2
83
 
84
- # Overlay the glasses
85
  try:
86
  frame = cvzone.overlayPNG(frame, overlay_rotated, [overlay_x, overlay_y])
87
  except Exception as e:
88
  print(f"Error overlaying glasses: {e}")
89
-
90
- return frame
91
 
92
  # Transform function
93
  def transform_cv2(frame, transform):
@@ -148,9 +163,9 @@ def save_frame(frame):
148
 
149
  # Gradio webcam input
150
  def webcam_input(frame, transform):
151
- frame = process_frame(frame)
152
  frame = transform_cv2(frame, transform)
153
- return frame
154
 
155
  # Gradio Interface
156
  with gr.Blocks() as demo:
@@ -159,13 +174,16 @@ with gr.Blocks() as demo:
159
  transform = gr.Dropdown(choices=["cartoon", "edges", "none"],
160
  value="none", label="Transformation")
161
  input_img = gr.Image(sources=["webcam"], type="numpy", streaming=True)
 
162
  next_button = gr.Button("Next Glasses")
163
- save_button = gr.Button("Save as a Picture")
164
- input_img.stream(webcam_input, [input_img, transform], [input_img], time_limit=30, stream_every=0.1)
 
165
  with gr.Row():
166
  next_button.click(change_glasses, [], [])
167
  with gr.Row():
168
  save_button.click(save_frame, [input_img], [])
 
169
  if __name__ == "__main__":
170
  demo.launch(share=True)
171
 
 
3
  import numpy as np
4
  import os
5
  import gradio as gr
6
+ import dlib
7
  from datetime import datetime
8
 
9
  # Load the YuNet model
10
  model_path = 'face_detection_yunet_2023mar.onnx'
11
  face_detector = cv2.FaceDetectorYN.create(model_path, "", (320, 320))
12
 
13
+ # Load dlib's shape predictor
14
+ shape_predictor_path = 'shape_predictor_68_face_landmarks.dat'
15
+ shape_predictor = dlib.shape_predictor(shape_predictor_path)
16
+
17
  # Initialize the glass number
18
  num = 1
19
  overlay = cv2.imread(f'glasses/glass{num}.png', cv2.IMREAD_UNCHANGED)
 
25
  file_count += len(files)
26
  return file_count
27
 
28
+ # Determine face shape
29
+ def determine_face_shape(landmarks):
30
+ # Example logic to determine face shape based on landmarks
31
+ # This is a simplified version and may need adjustments
32
+ jaw_width = np.linalg.norm(landmarks[0] - landmarks[16])
33
+ face_height = np.linalg.norm(landmarks[8] - landmarks[27])
34
+ if jaw_width / face_height > 1.5:
35
+ return "Round"
36
+ elif jaw_width / face_height < 1.2:
37
+ return "Oval"
38
+ else:
39
+ return "Square"
40
+
41
  directory_path = 'glasses'
42
  total_glass_num = count_files_in_directory(directory_path)
43
 
 
50
  overlay = cv2.imread(f'glasses/glass{num}.png', cv2.IMREAD_UNCHANGED)
51
  return overlay
52
 
53
+ # Process frame for overlay and face shape detection
54
  def process_frame(frame):
55
  global overlay
 
56
  frame = np.array(frame, copy=True)
 
57
  height, width = frame.shape[:2]
58
  face_detector.setInputSize((width, height))
59
  _, faces = face_detector.detect(frame)
60
 
61
+ face_shape = "Unknown"
62
  if faces is not None:
63
  for face in faces:
64
  x, y, w, h = face[:4].astype(int)
65
+ face_landmarks = face[4:14].reshape(5, 2).astype(int)
66
+
67
+ # Convert to dlib rectangle
68
+ dlib_rect = dlib.rectangle(x, y, x + w, y + h)
69
+ landmarks = shape_predictor(frame, dlib_rect)
70
+ landmarks = np.array([(p.x, p.y) for p in landmarks.parts()])
71
+
72
+ # Determine face shape
73
+ face_shape = determine_face_shape(landmarks)
74
 
75
  # Get the nose position
76
  nose_x, nose_y = face_landmarks[2].astype(int)
 
77
  left_eye_x, left_eye_y = face_landmarks[0].astype(int)
78
  right_eye_x, right_eye_y = face_landmarks[1].astype(int)
79
 
 
80
  eye_center_x = (left_eye_x + right_eye_x) // 2
81
  eye_center_y = (left_eye_y + right_eye_y) // 2
82
 
 
83
  delta_x = right_eye_x - left_eye_x
84
  delta_y = right_eye_y - left_eye_y
85
  angle = np.degrees(np.arctan2(delta_y, delta_x))
 
 
86
  angle = -angle
87
 
 
88
  overlay_resize = cv2.resize(overlay, (int(w * 1.15), int(h * 0.8)))
 
 
89
  overlay_center = (overlay_resize.shape[1] // 2, overlay_resize.shape[0] // 2)
90
  rotation_matrix = cv2.getRotationMatrix2D(overlay_center, angle, 1.0)
91
  overlay_rotated = cv2.warpAffine(
92
+ overlay_resize, rotation_matrix,
93
  (overlay_resize.shape[1], overlay_resize.shape[0]),
94
  flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0)
95
  )
96
 
 
97
  overlay_x = eye_center_x - overlay_rotated.shape[1] // 2
98
  overlay_y = eye_center_y - overlay_rotated.shape[0] // 2
99
 
 
100
  try:
101
  frame = cvzone.overlayPNG(frame, overlay_rotated, [overlay_x, overlay_y])
102
  except Exception as e:
103
  print(f"Error overlaying glasses: {e}")
104
+
105
+ return frame, face_shape
106
 
107
  # Transform function
108
  def transform_cv2(frame, transform):
 
163
 
164
  # Gradio webcam input
165
  def webcam_input(frame, transform):
166
+ frame, face_shape = process_frame(frame)
167
  frame = transform_cv2(frame, transform)
168
+ return frame, face_shape
169
 
170
  # Gradio Interface
171
  with gr.Blocks() as demo:
 
174
  transform = gr.Dropdown(choices=["cartoon", "edges", "none"],
175
  value="none", label="Transformation")
176
  input_img = gr.Image(sources=["webcam"], type="numpy", streaming=True)
177
+ face_shape_output = gr.Textbox(label="Detected Face Shape")
178
  next_button = gr.Button("Next Glasses")
179
+ save_button = gr.Button("Save as a Picture")
180
+
181
+ input_img.stream(webcam_input, [input_img, transform], [input_img, face_shape_output], time_limit=30, stream_every=0.1)
182
  with gr.Row():
183
  next_button.click(change_glasses, [], [])
184
  with gr.Row():
185
  save_button.click(save_frame, [input_img], [])
186
+
187
  if __name__ == "__main__":
188
  demo.launch(share=True)
189
 
requirements.txt CHANGED
@@ -1,4 +1,5 @@
1
  gradio
2
  cvzone
3
  opencv-python
4
- numpy
 
 
1
  gradio
2
  cvzone
3
  opencv-python
4
+ numpy
5
+ dlib