iBetter Books
수정

dlib의 68점이 얼굴의 골격을 짚는다면, MediaPipe의 FaceLandmarker는 얼굴 전체를 468개 점의 촘촘한 그물로 덮습니다. 실시간으로 동작하고, 표정 강도를 나타내는 블렌드셰이프와 머리 방향을 담은 변환 행렬까지 함께 줍니다. 이 장에서는 현재 권장 방식인 Tasks API로 이 고밀도 메시를 다룹니다.

옛 face_mesh가 아니라 FaceLandmarker

PART 02의 MediaPipe 검출에서 보았듯, 오래된 예제는 mp.solutions.face_mesh를 씁니다. 하지만 이 레거시 방식은 지원이 종료되어 최신 mediapipe에는 모듈 자체가 없습니다. 지금은 Tasks API의 FaceLandmarker가 그 자리를 대신합니다. 사용법은 PART 02에서 본 FaceDetector와 똑같은 꼴이라, 한 번 익히면 그대로 응용됩니다.

모델 파일 face_landmarker.task를 MediaPipe 모델 페이지에서 내려받아 작업 폴더에 둡니다.

468점 메시 검출 코드

다음 내용을 mesh.py로 저장합니다.

# 파일: mesh.py"""MediaPipe FaceLandmarker로 468점 메시·블렌드셰이프를 검출한다."""import cv2import mediapipe as mpfrom mediapipe.tasks import pythonfrom mediapipe.tasks.python import visionbase_options = python.BaseOptions(model_asset_path="face_landmarker.task")options = vision.FaceLandmarkerOptions(    base_options=base_options,    output_face_blendshapes=True,            # 표정 강도(블렌드셰이프)도 받기    output_facial_transformation_matrixes=True,  # 머리 방향 행렬도 받기    num_faces=1,)landmarker = vision.FaceLandmarker.create_from_options(options)img = cv2.imread("sample.jpg")h, w = img.shape[:2]mp_image = mp.Image.create_from_file("sample.jpg")result = landmarker.detect(mp_image)# result.face_landmarks: 얼굴별 [NormalizedLandmark, ...] (478점, 좌표 0~1)for landmarks in result.face_landmarks:    for lm in landmarks:        x, y = int(lm.x * w), int(lm.y * h)   # 비율 → 픽셀 환산 필수        cv2.circle(img, (x, y), 1, (0, 255, 0), -1)cv2.imwrite("mesh_result.jpg", img)

MediaPipe 468점 메시

dlib과 가장 다른 점은 좌표가 0과 1 사이의 비율로 나온다는 것입니다. 그래서 lm.x * w, lm.y * h로 이미지 크기를 곱해 픽셀로 바꿔야 점이 제자리에 찍힙니다. 1장에서 강조한 "픽셀인가 비율인가"가 바로 여기서 갈립니다. 참고로 점은 468개에 홍채 10개를 더해 실제로는 478개가 나옵니다.

함께 따라오는 두 가지 선물

FaceLandmarker는 점만 주지 않습니다. 옵션을 켜면 두 가지를 덤으로 줍니다.

  • 블렌드셰이프(blendshapes): "왼쪽 눈을 얼마나 감았는가", "입꼬리를 얼마나 올렸는가" 같은 표정 요소를 0~1 점수로 줍니다. 이름과 점수로 표정을 읽을 수 있어, PART 08의 감정 분석에서 다시 활용합니다.
  • 변환 행렬(transformation matrix): 얼굴이 카메라에 대해 어떻게 회전·이동했는지를 담은 4×4 행렬로, 머리 방향 추정이나 3D AR에 쓰입니다.
# 블렌드셰이프 상위 항목 출력 (위 코드에 이어서)for blendshapes in result.face_blendshapes:    top = sorted(blendshapes, key=lambda b: b.score, reverse=True)[:3]    for b in top:        print(f"  {b.category_name}: {b.score:.2f}")

result.face_blendshapes는 얼굴별 항목 리스트이고, 각 항목은 이름(category_name)과 점수(score)를 가집니다. 점수가 높은 순으로 정렬하면 지금 가장 두드러진 표정 요소를 알 수 있습니다.

실무 팁. 468점을 전부 화면에 찍으면 얼굴이 점으로 뒤덮여 오히려 보기 어렵습니다. 실제로는 MediaPipe가 제공하는 연결 정보(테셀레이션·윤곽·눈·입술)를 선으로 그려 메시 형태로 보여 주는 경우가 많습니다. 점만으로 충분한 분석이라면 필요한 인덱스(예: 눈·입 주변)만 골라 찍는 편이 가볍고 깔끔합니다.

이 장에서 기억할 것

MediaPipe FaceLandmarker(Tasks API)는 옛 face_mesh를 대신하는 현재 표준으로, FaceDetector와 같은 방식(.task 모델 → create_from_options → detect)으로 478점 메시를 줍니다. 좌표는 비율이라 픽셀 환산이 필수이고, 옵션을 켜면 표정 강도(블렌드셰이프)와 머리 방향(변환 행렬)까지 함께 얻습니다. 다음 장에서는 지금까지 뽑은 랜드마크의 핵심 쓰임새인 정렬이, 인식 정확도를 실제로 얼마나 끌어올리는지 확인합니다.