얼굴 인식 - Face Swapping

김성빈·2024년 5월 6일
0

Modern Computer Vision

목록 보기
31/117

저번 포스팅의 Facial Landmark Detection with Dlib 을 이용해서 얼굴의 특징을 랜드마크 했다면

두 이미지의 랜드마크를 서로 바꾼다.

결과

결론부터 확인하면 서로 얼굴이 바뀌어 있는것을 확인할 수 있다.


코드

  1. 각 이미지에서 얼굴 랜드마크를 감지하고 얼굴 특징을 추출하는 데 사용되는 라이브러리 및 상수를 가져온다.
import sys

PREDICTOR_PATH = "shape_predictor_68_face_landmarks.dat"
SCALE_FACTOR = 1
FEATHER_AMOUNT = 11

FACE_POINTS = list(range(17, 68))
MOUTH_POINTS = list(range(48, 61))
RIGHT_BROW_POINTS = list(range(17, 22))
LEFT_BROW_POINTS = list(range(22, 27))
RIGHT_EYE_POINTS = list(range(36, 42))
LEFT_EYE_POINTS = list(range(42, 48))
NOSE_POINTS = list(range(27, 35))
JAW_POINTS = list(range(0, 17))

range 안의 범위가 의미하는것은 전체 이미지 얼굴을 랜드마크 했을때 해당 범위안에 얼굴의 그 부위가 있다는 것을 나타내며,

Mouth_points 는 입 주변의 점들을 나타내는데 그 점들이 48~61 번 점들이다.

  1. 이미지에서 얼굴을 감지하고 각 얼굴에 대한 랜드마크를 반환
def get_landmarks(im):
    # Returns facial landmarks as (x,y) coordinates
    rects = detector(im, 1)

    if len(rects) > 1:
        raise TooManyFaces
    if len(rects) == 0:
        raise NoFaces

    return np.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])

dlib 라이브러리를 사용하여 얼굴을 감지하고 랜드마크를 추출하여 반환

  1. 이미지에 얼굴 랜드마크를 그리고 해당 랜드마크의 인덱스를 표시
def annotate_landmarks(im, landmarks):
    #Overlays the landmark points on the image itself

    im = im.copy()
    for idx, point in enumerate(landmarks):
        pos = (point[0, 0], point[0, 1])
        cv2.putText(im, str(idx), pos,
                    fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
                    fontScale=0.4,
                    color=(0, 0, 255))
        cv2.circle(im, pos, 3, color=(0, 255, 255))
    return im
  1. 주어진 점들을 기반으로 볼록 다각형 표시
def draw_convex_hull(im, points, color):
    points = cv2.convexHull(points)
    cv2.fillConvexPoly(im, points, color=color)
  1. 얼굴 특징을 기반으로 얼굴의 마스크 생성
def get_face_mask(im, landmarks):
    im = np.zeros(im.shape[:2], dtype=np.float64)

    for group in OVERLAY_POINTS:
        draw_convex_hull(im,
                         landmarks[group],
                         color=1)

    im = np.array([im, im, im]).transpose((1, 2, 0))

    im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0
    im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)

    return im
  1. 두 이미지 간의 얼굴 특징의 변환 행렬을 계산

각 얼굴 특징의 행렬을 비슷하게 만들고 Swapping 을 진행을 해야 이상한 부분이 없음


def transformation_from_points(points1, points2):
    """
    Return an affine transformation [s * R | T] such that:
        sum ||s*R*p1,i + T - p2,i||^2
    is minimized.
    """
    # Solve the procrustes problem by subtracting centroids, scaling by the
    # standard deviation, and then using the SVD to calculate the rotation. See
    # the following for more details:
    #   https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem

    points1 = points1.astype(np.float64)
    points2 = points2.astype(np.float64)

    c1 = np.mean(points1, axis=0)
    c2 = np.mean(points2, axis=0)
    points1 -= c1
    points2 -= c2

    s1 = np.std(points1)
    s2 = np.std(points2)
    points1 /= s1
    points2 /= s2

    U, S, Vt = np.linalg.svd(points1.T * points2)

    # The R we seek is in fact the transpose of the one given by U * Vt. This
    # is because the above formulation assumes the matrix goes on the right
    # (with row vectors) where as our solution requires the matrix to be on the
    # left (with column vectors).
    R = (U * Vt).T

    return np.vstack([np.hstack(((s2 / s1) * R,
                                       c2.T - (s2 / s1) * R * c1.T)),
                         np.matrix([0., 0., 1.])])
  1. 두 이미지의 색상을 보정

이미지를 덮어 씌울때 사용할 얼굴 특징의 끝을 Blur 처리를 해주면 자연스러워진다.

def correct_colours(im1, im2, landmarks1):
    blur_amount = COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm(
                              np.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -
                              np.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))
    blur_amount = int(blur_amount)
    if blur_amount % 2 == 0:
        blur_amount += 1
    im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)
    im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)

    # Avoid divide-by-zero errors.
    im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)

    return (im2.astype(np.float64) * im1_blur.astype(np.float64) /
                                                im2_blur.astype(np.float64))

  1. 두 이미지에서 얼굴을 교환
swappy(image1, image2)

두 이미지에서 얼굴을 교환 하는 함수로

두 이미지의 특징을 추출하고, 변환 행렬을 사용하여 두 번째 이미지의 얼굴을 첫 번째 이미지의 얼굴에 맞게 변환

그런 다음 색상 보정 및 합성을 통해 두 이미지를 교환하고 결과를 반환

profile
감사합니다. https://www.youtube.com/channel/UCxlkiu9_aWijoD7BannNM7w

0개의 댓글