mediapipe face 랜드마크 위치 & 얼굴 특성 다루기

목록으로 돌아가기

mediapipe

이미지 설명

MediaPipe는 Google에서 개발한 오픈 소스 라이브러리로, 컴퓨터 비전 및 머신 러닝 기반 애플리케이션을 개발하는 데 사용됩니다. 여러 신체 부위의 특성을 손 쉽게 뽑아주기 때문에 아주 유용합니다!
저는 이번에 face 인식을 할 예정입니다.

Face landmark

이미지 설명

다른 신체부위는 랜드마크(포인트 위치좌표)의 갯수가 적고 정확한 정보도 많지만 얼굴의 경우 각 포인트 별 좌표 번호가 없어 먼저 해당 좌표를 출력했습니다. 우클릭해서 새탭에서 열거나 다운로드 받아 보시면 됩니다! 고화질로 했어요!

아래는 좌표 추출코드 입니다

import cv2
import mediapipe as mp
from IPython.display import Image, display

# MediaPipe FaceMesh 초기화
mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh()

# OpenCV 이미지 불러오기
image_path = 'face.png'  # 얼굴 이미지 파일 경로 설정
image = cv2.imread(image_path)
image = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)  # 이미지를 2배로 확대
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)


# 얼굴 특성 추출
results = face_mesh.process(image_rgb)

# 랜드마크 그리기 및 번호 표시
if results.multi_face_landmarks:
    for face_landmarks in results.multi_face_landmarks:
        for idx, landmark in enumerate(face_landmarks.landmark):
            x = int(landmark.x * image.shape[1])
            y = int(landmark.y * image.shape[0])
            cv2.circle(image, (x, y), 2, (0, 255, 0), -1)  # 랜드마크를 초록색 점으로 표시
            cv2.putText(image, str(idx), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 1)  # 랜드마크 번호 표시

# 이미지 저장
output_image_path = 'output_face.png'
cv2.imwrite(output_image_path, image)

# 이미지 출력
display(Image(filename=output_image_path))

Face Capture

이제 추출된 랜드마크를 사용하여 얼굴의 특성을 뽑아보자

    def capture(self):
        # 웹캠에서 프레임 읽기
        ret, frame = self.cap.read()
        if not ret:
            return

        # 좌우반전
        frame = cv2.flip(frame, 1)

        # 이미지 크기 얻기
        image_height, image_width, _ = frame.shape

        # 이미지를 RGB 형식으로 변환
        image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 얼굴 특성 추출
        results = self.face_mesh.process(image_rgb)

        # 추출된 얼굴 랜드마크를 리스트로 변환합니다.
        landmarks = []
        for face_landmarks in results.multi_face_landmarks:
            for landmark in face_landmarks.landmark:
                landmarks.append((landmark.x, landmark.y, landmark.z))

        # landmarks 리스트를 NumPy 배열로 변환합니다.
        landmarks_array = np.array(landmarks, dtype=np.float32)

        # 필요한 랜드마크 인덱스를 정의합니다.
        # 눈
        left_eye = [
            133,
            173,
            157,
            158,
            159,
            160,
            161,
            246,
            33,
            130,
            7,
            163,
            144,
            145,
            153,
            154,
            155,
        ]
        right_eye = [
            362,
            382,
            381,
            380,
            374,
            373,
            390,
            249,
            359,
            263,
            466,
            388,
            387,
            386,
            385,
            384,
            398,
        ]

        # 코
        nose_tip = [
            168,
            245,
            128,
            114,
            217,
            198,
            209,
            49,
            64,
            98,
            97,
            2,
            326,
            327,
            278,
            360,
            420,
            399,
            351,
        ]

        # 입
        top_lip = [61, 185, 40, 39, 37, 0, 267, 270]
        bottom_lip = [146, 91, 181, 84, 17, 314, 405, 321]

        # 눈썹
        left_eyebrow = [336, 296, 334, 293, 276, 283, 282, 295, 285]
        right_eyebrow = [107, 66, 105, 63, 70, 53, 52, 65, 66]

        # 얼굴 윤곽
        face_contour = [
            10,
            338,
            297,
            332,
            284,
            251,
            389,
            356,
            454,
            323,
            361,
            288,
            397,
            365,
            379,
            378,
            400,
            377,
            152,
            148,
            176,
            149,
            150,
            136,
            172,
            58,
            132,
            93,
            234,
            127,
            162,
            21,
            54,
            103,
            67,
            109,
            10,
        ]

        # 눈 간의 거리와 눈 높이의 비율 계산
        left_eye_x = landmarks_array[left_eye, 0]
        left_eye_y = landmarks_array[left_eye, 1]
        right_eye_x = landmarks_array[right_eye, 0]
        right_eye_y = landmarks_array[right_eye, 1]

        eye_distance = np.linalg.norm(left_eye_x - right_eye_x)
        eye_height = np.linalg.norm(left_eye_y - right_eye_y)
        eye_ratio = eye_distance / eye_height

        # 코의 좌우 길이 계산
        leftmost_nose_x = landmarks_array[nose_tip[0], 0]
        rightmost_nose_x = landmarks_array[nose_tip[8], 0]
        nose_width = np.linalg.norm(leftmost_nose_x - rightmost_nose_x)

        # 코의 위아래 길이 계산
        top_nose_y = landmarks_array[nose_tip[12], 1]
        bottom_nose_y = landmarks_array[nose_tip[0], 1]
        nose_height = np.linalg.norm(top_nose_y - bottom_nose_y)

        # 얼굴 폭과 얼굴 높이의 비율 계산
        face_width = np.linalg.norm(landmarks_array[16] - landmarks_array[0])
        face_height = np.linalg.norm(landmarks_array[8] - landmarks_array[27])
        face_ratio = face_width / face_height

        # 눈썹 간의 거리와 얼굴 높이의 비율 계산
        left_eyebrow_x = landmarks_array[left_eyebrow, 0]
        left_eyebrow_y = landmarks_array[left_eyebrow, 1]
        right_eyebrow_x = landmarks_array[right_eyebrow, 0]
        right_eyebrow_y = landmarks_array[right_eyebrow, 1]

        eyebrow_distance = np.linalg.norm(left_eyebrow_x - right_eyebrow_x)
        eyebrow_height = np.linalg.norm(left_eyebrow_y - right_eyebrow_y)
        eyebrow_ratio = eyebrow_distance / eyebrow_height

        # 입술 좌우 길이 계산 (하나의 길이로 표현)
        lip_width = np.linalg.norm(
            landmarks_array[top_lip[0]] - landmarks_array[top_lip[6]]
        )

        caption = ""

        if eye_ratio < 6.096545581817628:
            caption += "대눈,"
        elif eye_ratio > 9.090017929077149:
            caption += "소눈,"
        else:
            caption += "중눈,"

        if face_ratio < 0.32334879755973817:
            caption += "대얼굴,"
        elif face_ratio > 0.3679878675937653:
            caption += "소얼굴,"
        else:
            caption += "중얼굴,"

        if eyebrow_ratio < 17.789510231018067:
            caption += "대눈썹,"
        elif eyebrow_ratio > 21.317358169555668:
            caption += "소눈썹,"
        else:
            caption += "중눈썹,"

        if lip_width < 0.04647548146545887:
            caption += "대입,"
        elif lip_width > 0.0533718939870596:
            caption += "소입,"
        else:
            caption += "중입,"

        if nose_width < 0.021084710955619812:
            caption += "대코,"
        elif nose_width > 0.025440793633461:
            caption += "소코,"
        else:
            caption += "중코,"

        # 캡션을 텍스트 박스에 표시
        self.text_box.setPlainText(caption)

완성된 UI

qt5로 결과를 만들었다. 이제 스테이블 티퓨전 모델을 학습시켜 닮은 캐릭터 생성을 할 계획이다.

이미지 설명

Etc

author-profile
Written by 유찬영

댓글