abdonkath commited on
Commit
dfafbb7
·
0 Parent(s):

uplauded utils & webcam.py

Browse files
Files changed (2) hide show
  1. utils.py +96 -0
  2. webcam.py +64 -0
utils.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import numpy as np
3
+ import mediapipe as mp
4
+ from mediapipe.tasks import python
5
+ from mediapipe.tasks.python import vision
6
+ import cv2
7
+
8
+
9
+ # landmarks of features from mediapipe
10
+ face_points={
11
+ "BLUSH_LEFT": [50],
12
+ "BLUSH_RIGHT": [280],
13
+ "LEFT_EYE": [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7, 33],
14
+ "RIGHT_EYE": [362, 298, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382, 362],
15
+ "EYELINER_LEFT": [243, 112, 26, 22, 23, 24, 110, 25, 226, 130, 33, 7, 163, 144, 145, 153, 154, 155, 133, 243],
16
+ "EYELINER_RIGHT": [463, 362, 382, 381, 380, 374, 373, 390, 249, 263, 359, 446, 255, 339, 254, 253, 252, 256, 341, 463],
17
+ "EYESHADOW_LEFT": [226, 247, 30, 29, 27, 28, 56, 190, 243, 173, 157, 158, 159, 160, 161, 246, 33, 130, 226],
18
+ "EYESHADOW_RIGHT": [463, 414, 286, 258, 257, 259, 260, 467, 446, 359, 263, 466, 388, 387, 386, 385, 384, 398, 362, 463],
19
+ "FACE": [152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109, 10, 338, 297, 332, 284, 251, 389, 454, 323, 401, 361, 435, 288, 397, 365, 379, 378, 400, 377, 152],
20
+ "LIP_UPPER": [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 308, 415, 310, 312, 13, 82, 81, 80, 191, 78],
21
+ "LIP_LOWER": [61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291, 308, 324, 402, 317, 14, 87, 178, 88, 95, 78, 61],
22
+ "EYEBROW_LEFT": [55, 107, 66, 105, 63, 70, 46, 53, 52, 65, 55],
23
+ "EYEBROW_RIGHT": [285, 336, 296, 334, 293, 300, 276, 283, 295, 285]
24
+ }
25
+
26
+ # initialize mediapipe face landmarker (new Tasks API)
27
+ # requires face_landmarker.task model file in the project directory
28
+ _base_options = python.BaseOptions(model_asset_path='face_landmarker.task')
29
+ _options = vision.FaceLandmarkerOptions(
30
+ base_options=_base_options,
31
+ num_faces=1,
32
+ )
33
+ _face_landmarker = vision.FaceLandmarker.create_from_options(_options)
34
+
35
+
36
+ def _normalized_to_pixel_coordinates(normalized_x, normalized_y, image_width, image_height):
37
+ x_px = min(math.floor(normalized_x * image_width), image_width - 1)
38
+ y_px = min(math.floor(normalized_y * image_height), image_height - 1)
39
+ return (x_px, y_px)
40
+
41
+
42
+ # to display image in cv2 window
43
+ def show_image(image: np.array, msg: str = "Loaded Image"):
44
+ """
45
+ image : image as np array
46
+ msg : cv2 window name
47
+ """
48
+ image_copy = image.copy()
49
+ cv2.imshow(msg, image_copy)
50
+ cv2.waitKey(0)
51
+ cv2.destroyAllWindows()
52
+
53
+
54
+ def read_landmarks(image: np.array):
55
+ """
56
+ image : image as np.array
57
+ """
58
+ landmark_cordinates = {}
59
+ # convert BGR to RGB, then wrap in mediapipe Image
60
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
61
+ mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_image)
62
+
63
+ # detect facial landmarks (returns normalized points 0 to 477)
64
+ results = _face_landmarker.detect(mp_image)
65
+ face_landmarks = results.face_landmarks[0]
66
+
67
+ # convert normalized points w.r.to image dimensions
68
+ for idx, landmark in enumerate(face_landmarks):
69
+ landmark_px = _normalized_to_pixel_coordinates(
70
+ landmark.x, landmark.y, image.shape[1], image.shape[0]
71
+ )
72
+ # create a map of facial landmarks to (x,y) coordinates
73
+ if landmark_px:
74
+ landmark_cordinates[idx] = landmark_px
75
+ return landmark_cordinates
76
+
77
+
78
+ # based on input facial features create make w.r.to colors
79
+ def add_mask(
80
+ mask: np.array, idx_to_coordinates: dict, face_connections: list, colors: list
81
+ ):
82
+ """
83
+ mask: image filled with 0's
84
+ idx_to_coordinates : dict with (x,y) cordinates for each face landmarks
85
+ face_connections : list of (x,y) cordinates for each facial features
86
+ colors : list of [B,G,R] color for each features
87
+ """
88
+ for i, connection in enumerate(face_connections):
89
+ # extract (x,y) w.r.to image for each cordinates
90
+ points = np.array([idx_to_coordinates[idx] for idx in connection])
91
+ # make a shape of feature in the mask and add color
92
+ cv2.fillPoly(mask, [points], colors[i])
93
+
94
+ # smoothening of image
95
+ mask = cv2.GaussianBlur(mask, (7, 7), 4)
96
+ return mask
webcam.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import pyvirtualcam
4
+ from utils import read_landmarks, add_mask, face_points
5
+
6
+ face_elements = ["LIP_LOWER", "LIP_UPPER", "EYEBROW_LEFT", "EYEBROW_RIGHT",
7
+ "EYELINER_LEFT", "EYELINER_RIGHT", "EYESHADOW_LEFT", "EYESHADOW_RIGHT"]
8
+ colors_map = {
9
+ # upper lip and lower lips
10
+ "LIP_UPPER": [0, 0, 255], # Red in BGR
11
+ "LIP_LOWER": [0, 0, 255], # Red in BGR
12
+ # eyeliner
13
+ "EYELINER_LEFT": [139, 0, 0], # Dark Blue in BGR
14
+ "EYELINER_RIGHT": [139, 0, 0], # Dark Blue in BGR
15
+ # eye shadow
16
+ "EYESHADOW_LEFT": [0, 100, 0], # Dark Green in BGR
17
+ "EYESHADOW_RIGHT": [0, 100, 0], # Dark Green in BGR
18
+ # eye brow
19
+ "EYEBROW_LEFT": [19, 69, 139], # Dark Brown in BGR
20
+ "EYEBROW_RIGHT": [19, 69, 139], # Dark Brown in BGR
21
+ }
22
+
23
+ face_connections = [face_points[idx] for idx in face_elements]
24
+ colors = [colors_map[idx] for idx in face_elements]
25
+
26
+ cap = cv2.VideoCapture(0)
27
+ if not cap.isOpened():
28
+ raise RuntimeError("Could not open webcam. Make sure Zoom is not already using it.")
29
+
30
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
31
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
32
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30
33
+
34
+ print(f"Webcam opened: {width}x{height} @ {fps}fps")
35
+
36
+ with pyvirtualcam.Camera(width=width, height=height, fps=fps) as cam:
37
+ print(f"Virtual camera running: {cam.device}")
38
+ print("Press Q in the preview window to quit.")
39
+ while True:
40
+ ret, frame = cap.read()
41
+ if not ret:
42
+ continue # skip bad frames, don't exit
43
+ # frame = cv2.flip(frame, 1) # mirror horizontally
44
+
45
+ try:
46
+ landmark_coordinates = read_landmarks(frame)
47
+ mask = np.zeros_like(frame)
48
+ mask = add_mask(mask, idx_to_coordinates=landmark_coordinates,
49
+ face_connections=face_connections, colors=colors)
50
+ output = cv2.addWeighted(frame, 1.0, mask, 0.2, 1.0)
51
+ except (IndexError, KeyError):
52
+ output = frame # no face detected, pass through plain video
53
+
54
+ # show local preview so you can confirm filter is working
55
+ cv2.imshow("Virtual Makeup Preview (Q to quit)", output)
56
+ if cv2.waitKey(1) & 0xFF == ord('q'):
57
+ break
58
+
59
+ # pyvirtualcam expects RGB
60
+ cam.send(cv2.cvtColor(output, cv2.COLOR_BGR2RGB))
61
+ cam.sleep_until_next_frame()
62
+
63
+ cap.release()
64
+ cv2.destroyAllWindows()