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




留言

這個網誌中的熱門文章

SAP物料主數據(Material Master Data)

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

外貿Payment Term 付款條件(方式)常見的英文縮寫與定義