MediaPipe Face Mesh_標記左右臉頰、人中ROI
參考paper
採用Python版本3.11.9
搭配
opencv-python 4.11.0.86
mediapipe 0.10.21 使用舊版Face Mesh 468個特徵點
測試資料集韓國的KoDF
測試程式
import cv2 import mediapipe as mp import numpy as np #初始化MediaPipe Face Mesh mp_face_mesh = mp.solutions.face_mesh mp_drawing = mp.solutions.drawing_utils # 開啟視訊鏡頭 # cap = cv2.VideoCapture(0) # 開啟指定目錄中的影片檔 #原始影片中目標人員:24c360f9f54e7cb0001d #F:\KoDF\downsized\original_videos\original_videos\24c360f9f54e7cb0001d #被偽造的影片目錄-fsgan偽造方式 #F:\KoDF\downsized\synthesized_videos\synthesized_videos\fsgan\20201005\24c360f9f54e7cb0001d video_path = r"F:\KoDF\downsized\synthesized_videos\synthesized_videos\fsgan\20201005\24c360f9f54e7cb0001d\24c360f9f54e7cb0001d_19ea0e3a2325e8edce70_3_0010.mp4" cap = cv2.VideoCapture(video_path) # 確認影片是否成功開啟 if not cap.isOpened(): print(f"無法開啟影片:{video_path}") raise SystemExit # 自訂 ROI 的 Face Mesh landmark 編號 # left / right 是以「影片中人物本身」的左右側為準,不是以觀看畫面的左右側。 # 人物的左臉頰,通常顯示在畫面右側 LEFT_CHEEK = [ 280, 330, 347, 352, 411, 425, 427, 436 ] # 人物的右臉頰,通常顯示在畫面左側 RIGHT_CHEEK = [ 50, 101, 118, 123, 187, 205, 207, 216 ] # 人中:鼻子下方至上嘴唇之間 PHILTRUM = [ 2, 164, 0, 37, 39, 40, 267, 269, 270 ] def get_roi_hull(face_landmarks, landmark_indices, frame_width, frame_height): """ 將 MediaPipe 正規化座標轉成 OpenCV 像素座標, 並計算 ROI 的凸包多邊形。 """ points = [] for index in landmark_indices: landmark = face_landmarks.landmark[index] x = int(landmark.x * frame_width) y = int(landmark.y * frame_height) # 避免座標超出影像邊界 x = max(0, min(x, frame_width - 1)) y = max(0, min(y, frame_height - 1)) points.append([x, y]) points = np.array(points, dtype=np.int32) # landmark 不一定按照多邊形邊界排列, # 使用 convexHull 自動建立外圍多邊形 hull = cv2.convexHull(points) return hull def draw_roi_label(frame, hull, text, color): """ 在 ROI 中央顯示英文標籤。 """ center = np.mean(hull.reshape(-1, 2),axis=0).astype(int) label_position = (center[0] - 40, center[1]) # 黑色外框,讓文字更清楚 cv2.putText(frame,text,label_position,cv2.FONT_HERSHEY_SIMPLEX,0.35,(0, 0, 0),3,cv2.LINE_AA) cv2.putText(frame,text,label_position,cv2.FONT_HERSHEY_SIMPLEX,0.35,color,1,cv2.LINE_AA) with mp_face_mesh.FaceMesh(min_detection_confidence=0.5,min_tracking_confidence=0.5) as face_mesh: while cap.isOpened(): ret, frame = cap.read() if not ret: print("影片播放完畢") break #frame = cv2.flip(frame, 1) #開視訊才需要->左右旋轉 frame_height, frame_width = frame.shape[:2] #BGR to RGB ->適配MediaPipe處理的顏色通道順序 rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) #處理 Face Detect & Face Mesh results = face_mesh.process(rgb_frame) if results.multi_face_landmarks: for face_landmarks in results.multi_face_landmarks: #Draw the face mesh landmarks on the frame mp_drawing.draw_landmarks(frame, face_landmarks, mp_face_mesh.FACEMESH_TESSELATION, landmark_drawing_spec=None, connection_drawing_spec=mp_drawing.DrawingSpec( color=(180, 180, 180), thickness=1, circle_radius=1 ) ) #取得三個 ROI 多邊形 left_cheek_hull = get_roi_hull(face_landmarks,LEFT_CHEEK,frame_width,frame_height) right_cheek_hull = get_roi_hull(face_landmarks,RIGHT_CHEEK,frame_width,frame_height) philtrum_hull = get_roi_hull(face_landmarks,PHILTRUM,frame_width,frame_height) # 建一個透明圖層 overlay = frame.copy() cv2.fillConvexPoly(overlay,left_cheek_hull,(0, 255, 0))# 左臉頰:綠色 cv2.fillConvexPoly(overlay,right_cheek_hull,(255, 0, 0))# 右臉頰:藍色 cv2.fillConvexPoly(overlay,philtrum_hull,(0, 0, 255))# 人中:紅色 alpha = 0.35 cv2.addWeighted(overlay,alpha,frame,1 - alpha,0,frame) #繪製 ROI 邊界 cv2.polylines(frame,[left_cheek_hull],True,(0, 255, 0),2) cv2.polylines(frame,[right_cheek_hull],True,(255, 0, 0),2) cv2.polylines(frame,[philtrum_hull],True,(0, 0, 255),2) #加入區域名稱 draw_roi_label(frame,left_cheek_hull,"LEFT CHEEK",(0, 255, 0)) draw_roi_label(frame,right_cheek_hull,"RIGHT CHEEK",(255, 0, 255)) draw_roi_label(frame,philtrum_hull,"PHILTRUM",(0, 0, 255)) cv2.imshow('Face Mesh', frame) if cv2.waitKey(5) & 0xFF == 27: break cap.release() cv2.destroyAllWindows()
本次採用的Dataset
KoDF: A Large-scale Korean DeepFake Detection Dataset
留言
張貼留言