mediapipe - Face Mesh

Mediapipe FaceMesh 공식 문서

 

Face Mesh

Cross-platform, customizable ML solutions for live and streaming media.

google.github.io

mediapipe에서 제공하는 Face Mesh는 얼굴의 랜드마크를 검출해준다. 

Face Mesh는 모바일 GPU 환경 또는 CPU 환경에서 잘 작동하도록 제작되었기 때문에 고성능 컴퓨팅 자원을 요구하지 않고 한 대의 RGB 카메라만으로도 잘 동작한다는 점이 큰 장점이다. 또한 데이터를 학습하는 과정에서 여러 조명(lighting) 환경에서 촬영된 데이터를 사용했기 때문에 빛의 영향을 적게 받는다는 것을 경험적으로 확인할 수 있었다. 게다가 기존의 방식은 영상의 모든 프레임에서 detector를 거쳐 얼굴을 검출하는 반면, Face Mesh는 (트래킹 모드에서) 이전 프레임의 정보를 활용해 얼굴을 검출하고 얼굴을 인식하기 힘든 특별한 상황이 발생했을 때 detector를 거쳐 얼굴을 재검출한다. 

관련 정보는 "Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs"에서 확인할 수 있다.

 

Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs

We present an end-to-end neural network-based model for inferring an approximate 3D mesh representation of a human face from single camera input for AR applications. The relatively dense mesh model of 468 vertices is well-suited for face-based AR effects.

arxiv.org

Face Mesh는 Android, iOS, C++, Python Js에 대하여 API를 제공한다. 


설치

$ pip install mediapipe
$ pip install protobuf==3.20.*

pip를 통해 mediapipe를 설치한 후, protobuf를 다운그레이드 해준다. 


예제 - 랜드마크 그리기

import cv2
import mediapipe as mp

# 얼굴 검출을 위한 객체
mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(
    refine_landmarks=True,
    static_image_mode=True,
    max_num_faces=3,
)
# Face Mesh를 그리기 위한 객체
mp_drawing = mp.solutions.drawing_utils
drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)

# 이미지 읽기
image = cv2.imread("face.jpg")

# 얼굴 검출
results = face_mesh.process(image)

# Face Mesh 그리기
for single_face_landmarks in results.multi_face_landmarks:
    mp_drawing.draw_landmarks(
        image=image,
        landmark_list=single_face_landmarks,
        connections=mp_face_mesh.FACEMESH_CONTOURS,
        landmark_drawing_spec=drawing_spec,
        connection_drawing_spec=drawing_spec,
    )

# 이미지로 저장
cv2.imwrite("face-mesh.jpg", image)

 

1. mp.solutions.face_mesh.FaceMesh는 얼굴의 랜드마크 검출을 위한 객체이다. 

  • refine_landmarks: True일 때, 눈과 입술 주변의 랜드마크를 더욱 정교하게 검출한다.
  • static_image_mode: True일 경우, 모든 프레임에 대하여 얼굴 검출을 진행한다. 영상 검출의 경우, False로 설정해 얼굴을 추적(tracking)해 랜드마크를 검출한다. (모든 프레임에 대하여 얼굴 검출을 진행하지 않고 첫 프레임에서 얼굴 검출을 진행한 후 이후 프레임은 tracking 방식을 사용하여 랜드마크를 추출한다. 만약 tracking을 통해 얼굴 검출이 되지 않을 경우 다시 얼굴 검출을 진행한다.)
  • max_num_faces: 최대로 검출할 얼굴의 갯수를 설정한다. 

공식 문서를 통해 다른 파라미터 정보를 확인할 수 있다.

2. process를 통해 객체 검출을 진행하고, multi_face_landmarks를 통해 그 정보를 확인할 수 있다.

3. mp.solutions.face_mesh.DrawingSpec은 랜드마크 출력을 위한 객체이다. draw_landmarks를 이용하면 아래와 같이 이미지에 Face Mesh가 출력된다. 


랜드마크 좌표

Face Mesh는 468개의 랜드마크를 제공한다. 랜드마크 인덱스 정보는 mediapipe Github에서 확인할 수 있다. 

for single_face_landmarks in results.multi_face_landmarks:
    coordinates = single_face_landmarks.landmark[랜드마크 인덱스]
    coordinates.x, coordinates.y, coordinates.z

landmark에서 원하는 랜드마크 인덱스를 통해 좌표를 가져오고 x, y, z를 통해 값을 가져온다.

  • xy는 정규화된 값으로 0 ~ 1사이의 값을 가진다.
  • z는 Mesh 중앙을 지나는 평면을 기준으로 하는 상대적인 깊이를 나타낸다. 

 

예제: 코끝 랜드마크 

import cv2
import mediapipe as mp

# 코끝 인덱스 번호
NOSE_INDEX = 1

mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(
    static_image_mode=False,
    max_num_faces=1,
)

# 카메라 실행
cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, frame = cap.read()

    if ret:
        frame = cv2.flip(frame, 1)
        image_height, image_width, _ = frame.shape

        # 얼굴 검출
        results = face_mesh.process(frame)

        if results.multi_face_landmarks:
            for single_face_landmarks in results.multi_face_landmarks:
                # 코끝의 좌표값 구하기
                coordinates = single_face_landmarks.landmark[NOSE_INDEX]
                x = coordinates.x * image_width
                y = coordinates.y * image_height
                z = coordinates.z

                # x, y 좌표 화면에 그리기
                cv2.circle(frame, (int(x), int(y)), 5, (255, 0, 0), -1)               

        cv2.imshow("Frame", frame)
        if cv2.waitKey(3) & 0xFF == ord("q"):
            break
            
    else:
        break

cv2.destroyAllWindows()
cap.release()

* 코끝의 인덱스는 1이다.