mediapipe에서 제공하는 Face Mesh는 얼굴의 랜드마크를 검출해준다.
Face Mesh는 모바일 GPU 환경 또는 CPU 환경에서 잘 작동하도록 제작되었기 때문에 고성능 컴퓨팅 자원을 요구하지 않고 한 대의 RGB 카메라만으로도 잘 동작한다는 점이 큰 장점이다. 또한 데이터를 학습하는 과정에서 여러 조명(lighting) 환경에서 촬영된 데이터를 사용했기 때문에 빛의 영향을 적게 받는다는 것을 경험적으로 확인할 수 있었다. 게다가 기존의 방식은 영상의 모든 프레임에서 detector를 거쳐 얼굴을 검출하는 반면, Face Mesh는 (트래킹 모드에서) 이전 프레임의 정보를 활용해 얼굴을 검출하고 얼굴을 인식하기 힘든 특별한 상황이 발생했을 때 detector를 거쳐 얼굴을 재검출한다.
관련 정보는 "Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs"에서 확인할 수 있다.
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를 통해 값을 가져온다.
- x와 y는 정규화된 값으로 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이다.