Bài này tương tự bài Phát hiện cử động khuôn mặt bằng Facemesh. Có đôi chút khác ở công thức tính nên tách riêng ra 1 bài để giải thích.
Bước 1: tìm landmark
Tìm 468 điểm trên khuôn mặt, các điểm này có tọa độ xyz trong không gian 3 chiều
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import mediapipe as mp import math face_mesh = mp.solutions.face_mesh.FaceMesh( static_image_mode=True, max_num_faces=1, #refine_landmarks=True, min_detection_confidence=0.5) results = face_mesh.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) if results.multi_face_landmarks: landmarks = results.multi_face_landmarks[0] landmark = landmarks.landmark |
Bước 2: tìm giá trị z1, z2 và height để tính kết quả
Ta cần tìm z1 và z2 theo công thức hình bên dưới do khuôn mặt có thể xa – gần camera. z1 và z2 là khoảng cách near-far với camera, được tính bằng hàm CalcNearFar() ở source code cuối bài.
Còn h là khoảng cách giữa 2 điểm nên tính bằng khoảng cách Euclid bằng hàm CalcDistance().
Khuôn mặt phải ngước lên – cúi xuống quá 1 ngưỡng thì mới tính, hiện tại là 0.4. Tùy bài toán/yêu cầu mà điều chỉnh cho phù hợp.
Hình ảnh dùng để test:
Full source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import math import time import cv2 import mediapipe as mp class AngleDetector(): face_mesh = mp.solutions.face_mesh.FaceMesh( static_image_mode=True, max_num_faces=1, #refine_landmarks=True, min_detection_confidence=0.5) #################################################################################################### def ConvertToPoint(self, landmark): return [landmark.x, landmark.y, landmark.z] #################################################################################################### def CalcDistance(self, point1, point2): x1, y1, z1 = self.ConvertToPoint(point1) x2, y2, z2 = self.ConvertToPoint(point2) distance = math.sqrt((x1 - x2)**2 + (y1 - y2)**2) return distance #################################################################################################### def CalcNearFar(self, point1, point2): x1, y1, z1 = self.ConvertToPoint(point1) x2, y2, z2 = self.ConvertToPoint(point2) distance = z1 - z2 return distance #################################################################################################### def Detect(self, frame): results = self.face_mesh.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) if not results.multi_face_landmarks: return "no face", 1 landmarks = results.multi_face_landmarks[0] landmark = landmarks.landmark ratio = 1 up = self.CalcNearFar(landmark[5], landmark[10]) down = self.CalcNearFar(landmark[5], landmark[152]) faceHeight = self.CalcDistance(landmark[10], landmark[152]) ratio = (abs(up) + abs(down)) / faceHeight result = "center" if(up < down): if(abs(ratio) > 0.4): ratio = abs(ratio) #to easy to view result = "up" elif(down < up): if(abs(ratio) > 0.4): result = "down" return result, ratio angleDetector = AngleDetector() frame = cv2.imread("2023-01-23_08-20-28_M0yg3XqbPa.jpg") print(angleDetector.Detect(frame)) |