[캐글스터디] 내 얼굴로 어노잉 오렌지 만들기

뚜비·2022년 1월 10일
0

캐글스터디

목록 보기
1/5
post-thumbnail


🍊내 얼굴로 어노잉 오렌지 만들기

✅ 우리가 사용할 라이브러리

  • openCV(cv2) : 이미지 처리 라이브러리, 이미지 합성을 위한 것
  • Dlib : 이미지 처리 라이브러리, 얼굴인식을 위한 것, 즉 얼굴영역과 랜드마크 탐지
  • imutils : resize 함
  • numpy : 행렬 연산 라이브러리


STEP 1. 오렌지 이미지 다운 받기

오렌지
해당 링크로 들어가서 orange 이미지를 다운 받는다.

아주 싱그러운 오렌지군?^^

무료 영상 다운 참고로 이곳에서 영상 다운 받을 수 있다는 점!



STEP 2. 환경 설정

먼저 코딩을 하기에 앞서 위와 같은 에러가 떠서 스택오버플로우에 찾아보니

  • cv2 : 현재 working environment에 opencv-python을 설치하면 된다고 한다! (혹은 python을 3.8 이후 버전을 사용하면 된다고 한다.)

💢 문제 발생
cv2.error: OpenCV(4.5.5) cpp:1268: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvShowImage
-> 해결방법 : pip uninstall opencv-python을 입력해서 삭제한 후 pip install opencv-python을 입력해서 재설치..


  • dlib : camke 라이브러리를 설치하고 dlib 라이브러리를 설치하면 된다고 한다!

💢 문제 발생
위와 같이 vsCode에서 설치를 했음에도 dlib에 에러가 떴다...
-> 해결방법 : Anaconda prompt 키고 해당 conda 환경(나는 pybook)에서 똑같이 명령어를 입력했더니 잘 된다^^


  • imutils : pip install imutils 라고 터미널에 입력하면 된다.


STEP 3. 코딩하기

import cv2, dlib
from imutils import face_utils, resize
import numpy as np

orange_img = cv2.imread('orange.jpg') # 오렌지 이미지 준비한 것을 opencv의 imread로 읽어서 orange_img에 저장 
orange_img = cv2.resize(orange_img, dsize=(512,512)) # 가로 512 세로 512로 resize

detector = dlib.get_frontal_face_detector() # dlib 안에 있는 얼굴 영역 탐지 관련 메소드를 초기화 해주고 
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat') # ** 68개 점의 랜드 마크를 탐지 **

cap = cv2.VideoCapture('video.mp4') # 0이면 본인의 웹캠이 켜지고 영상 파일 이름을 넣으면 영상 파일이 켜진다. 

while cap.isOpened():
    ret, img = cap.read() # read를 통해 img를 읽어오고 

    if not ret: # 보낼 프레임이 없다면 종료 
        break

    faces = detector(img) # 얼굴 영역을 인식해줌, 이때 faces에 얼굴 영역 좌표가 담김

    result = orange_img.copy() #Orange 이미지를 복사한 것 

    if len(faces) > 0: # 얼굴이 1개 이상이면, 즉 프레임이 여러개이므로 detect된 얼굴도 여러 개이겠지
        face = faces[0] # 그중에서 1개(첫 번째)의 얼굴만을 원한다. 

        x1, y1, x2, y2 = face.left(), face.top(), face.right(), face.bottom()
        face_img = img[y1:y2, x1:x2].copy() # img에서 해당 face만 crop(잘라내기)해서 face_img에 저장

        # 랜드마크의 68점을 찾는다
        shape = predictor(img, face) 
        shape = face_utils.shape_to_np(shape) # 원래 shape는 dlib형태인데 이를 numpy 형태로 바꿔준다. 

        for p in shape:
            cv2.circle(face_img, center=(p[0]-x1, p[1]-y1), radius=2, color=255, thickness=-1)

        # ** 눈과 입을 잘라서 오렌지 파일에 붙이는 코드 **
        # eyes
        le_x1 = shape[36, 0] # **왼쪽 눈을 자르기 위해 x좌표 36번 39번, y좌표 37번 41번 인덱스가 필요**
        le_y1 = shape[37, 1]
        le_x2 = shape[39, 0]
        le_y2 = shape[41, 1]
        le_margin = int((le_x2 - le_x1) * 0.18) # 너무 가깝게 자르면 안 되니까 margin을 준다 

        re_x1 = shape[42, 0] # 오른쪽 눈
        re_y1 = shape[43, 1]
        re_x2 = shape[45, 0]
        re_y2 = shape[47, 1]
        re_margin = int((re_x2 - re_x1) * 0.18)

        # 여기서 왼쪽 눈과 오른쪽 눈에 margin을 준 값을 crop한다. 
        left_eye_img = img[le_y1-le_margin:le_y2+le_margin, le_x1-le_margin:le_x2+le_margin].copy() 
        right_eye_img = img[re_y1-re_margin:re_y2+re_margin, re_x1-re_margin:re_x2+re_margin].copy()

        left_eye_img = resize(left_eye_img, width=100) # 가로를 100으로 resize
        right_eye_img = resize(right_eye_img, width=100) 

        # ** opencv의 seamlessClone **
        result = cv2.seamlessClone( # 왼쪽 눈 합성  
            left_eye_img,
            result, #result(오렌지 이미지를 복사한 것)에 합성 하는 것 
            np.full(left_eye_img.shape[:2], 255, left_eye_img.dtype),
            (100, 200),
            cv2.MIXED_CLONE
        )

        result = cv2.seamlessClone( # 오른쪽 눈 합성 
            right_eye_img,
            result,
            np.full(right_eye_img.shape[:2], 255, right_eye_img.dtype),
            (250, 200),
            cv2.MIXED_CLONE
        )

        # mouth
        mouth_x1 = shape[48, 0]
        mouth_y1 = shape[50, 1]
        mouth_x2 = shape[54, 0]
        mouth_y2 = shape[57, 1]
        mouth_margin = int((mouth_x2 - mouth_x1) * 0.1)

        mouth_img = img[mouth_y1-mouth_margin:mouth_y2+mouth_margin, mouth_x1-mouth_margin:mouth_x2+mouth_margin].copy()

        mouth_img = resize(mouth_img, width=250)

        result = cv2.seamlessClone( # 입 합성 
            mouth_img,
            result,
            np.full(mouth_img.shape[:2], 255, mouth_img.dtype),
            (180, 320),
            cv2.MIXED_CLONE
        )

        cv2.imshow('left', left_eye_img)
        cv2.imshow('right', right_eye_img)
        cv2.imshow('mouth', mouth_img)
        cv2.imshow('face', face_img)

        cv2.imshow('result', result)

    # cv2.imshow('img', img)
    if cv2.waitKey(1) == ord('q'):
        break
  • 68개 점의 랜드 마크를 탐지
    다운받기 를 클릭해서 해당 파일을 다운받도록 하자

  • 랜드마크의 68점을 구한다

    0번부터 67번 (총 68개)의 좌표가 얼굴의 형태를 잡아주는데 이때 해당 img에 맞는 얼굴 형태의 랜드 마크 좌표가 shape에 들어간다는 것!

import cv2, dlib
from imutils import face_utils, resize
import numpy as np

orange_img = cv2.imread('orange.jpg') # 오렌지 이미지 준비한 것을 opencv의 imread로 읽어서 orange_img에 저장 
orange_img = cv2.resize(orange_img, dsize=(512,512)) # 가로 512 세로 512로 resize

detector = dlib.get_frontal_face_detector() # dlib 안에 있는 얼굴 영역 탐지 관련 메소드를 초기화 해주고 
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat') # ** 68개 점의 랜드 마크를 탐지 **

cap = cv2.VideoCapture(0) # 0이면 본인의 웹캠이 켜지고 영상 파일 이름을 넣으면 영상 파일이 켜진다. 

while cap.isOpened():
    ret, img = cap.read() # read를 통해 img를 읽어오고 

    if not ret: # 보낼 프레임이 없다면 종료 
        break

    faces = detector(img) # 얼굴 영역을 인식해줌, 이때 faces에 얼굴 영역 좌표가 담김

    result = orange_img.copy()

    if len(faces) > 0: # 얼굴이 1개 이상이면, 즉 프레임이 여러개이므로 detect된 얼굴도 여러 개이겠지
        face = faces[0] # 그중에서 1개(첫 번째)의 얼굴만을 원한다. 

        x1, y1, x2, y2 = face.left(), face.top(). face.right(), face.bottom()
        face_img = img[y1:y2, x1:x2].copy() # img에서 해당 face만 crop(잘라내기)해서 face_img에 저장

        # 랜드마크의 68점을 찾는다
        shape = predictor(img, face) 
        shape = face_utils.shape_to_np(shape) # 원래 shape는 dlib형태인데 이를 numpy 형태로 바꿔준다. 

        for p in shape:
            cv2.circle(face_img, center=(p[0]-x1, p[1]-y1), radius=2, color=255, thickness=-1)

        cv2.imshow('face', face_img)

        cv2.imshow('result', result)

    # cv2.imshow('img', img)
    if cv2.waitKey(1) == ord('q'):
        break

위의 코드를 실행시켜보면 다음과 같이 영상 속 사람 얼굴에 68개의 랜드마크가 찍혀 있는 것을 볼 수 있다.

  • 눈과 입을 잘라서 오렌지 파일에 붙이는 코드

    즉 사람의 눈과 입 부분을 잘라서 오렌지 이미지에 붙여 보겠다.

  • 왼쪽 눈을 자르기 위해 x좌표 36번 39번, y좌표 37번 41번 인덱스가 필요

  • opencv의 seamlessClone
    : Poisson Blending (푸아성 블렌딩)
    : 티가 안나게 합성을 해주는 것
    : option 중 cv2.MIXED_CLONE을 주면 알아서 합성을 한다



❗ 결과 ❗

앜ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 만들고 보니 너무 재밌다!!
영상과 관련 코드는 추후에 깃허브에 올리도록 하겠다!!

profile
SW Engineer 꿈나무 / 자의식이 있는 컴퓨터

0개의 댓글