exp3. 카메라 스티커앱 만들기

조경모·2022년 1월 11일
0

exploration

목록 보기
1/1

카메라 스티커앱 만들기 노드는 CV분야의 첫번째 노드이다.
우리가 흔히 snow, soda 카메라 등으로 사진을 찍을때 카메라는 우리의 사진을 보고, 사진으로부터 얼굴 영역(face landmark)를 찾고, 찾아진 영역으로부터 올바른 위치에 스티커를 붙여넣는다.
한번 이 순서대로 진행해보자.

1. 사진 준비하기

import os
import cv2
import matplotlib.pyplot as plt
import numpy as np
import dlib

image_path = '이미지 경로'
img_bgr = cv2.imread(image_path)    # OpenCV로 이미지를 불러옵니다
img_show = img_bgr.copy()      # 출력용 이미지를 따로 보관합니다
plt.imshow(img_bgr)
plt.show()

위 코드를 실행하면 사진이 보이게 된다. 하지만 opencv를 사용하였기 때문에 사진의 색상이 RGB가 아니라 BRG가 사용되어 뭔가 푸르스름하게 출력된다.
따라서 우리는 색 보정 과정을 거쳐야 한다.

img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.show()

이렇게 사진 준비단계는 완료되었다.

2. 얼굴 검출

우리는 dlib의 face detector을 사용하는데, 이것은 이미지에서 색상의 변화량을 나타내는 HOG와 SVM을 사용해 얼굴을 찾는다.
이미지의 색상 정보만 있다면 SVM으로 얼굴을 구별하기가 힘들지만, HOG를 이용하면 SVM으로 얼굴을 구분할 수 있다.

  • detector 선언
detector_hog = dlib.get_frontal_face_detector()
  • detector을 이용해 얼굴의 bounding box 추출
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  

dlib은 RGB이미지를 입력으로 받는다. 따라서 이전처럼 BRG이미지를 RGB로 바꿔주었다.
그럼, detector_hog의 두번째 인자인 '1'은 무엇일까?
이미지 upsamplinf을 통해 크기를 키우는 것을 이미지 피라미드라고 하는데, 1은 이미지 피라미드의 수이다. 이는 작은 얼굴을 크게 볼 수 있어 정확한 검출을 돕는다.

  • 찾은 얼굴 출력
print(dlib_rects)   

for dlib_rect in dlib_rects:
    l = dlib_rect.left()
    t = dlib_rect.top()
    r = dlib_rect.right()
    b = dlib_rect.bottom()

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA)

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

3. 얼굴 랜드마크

이제는 얼굴의 이목구비를 정확하게 찾아야 한다. 이때, face landmark기술이 사용된다.
이번 실습에서는 공개되어있는 weight file을 사용하였다.

landmark_predictor = dlib.shape_predictor(model_path)
#랜드마크 모델 선언

list_landmarks = []

# 얼굴 영역 박스 마다 face landmark를 찾아낸다.
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    # face landmark 좌표를 저장.
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0])) >>68
  • 랜드마크를 이미지에 출력
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 2, (0, 255, 255), -1)

img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

4. 스티커 적용

    1. 스티커 위치
      스티커 위치는 (x=x_nose, y=y_nose-height/2)
    1. 스티커 크기
      스티커 크기는 width=height=width_bbox

      			for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
        print (landmark[30]) # 코의 index는 30 
        x = landmark[30][0]
        y = landmark[30][1] - dlib_rect.height()//2
        w = h = dlib_rect.width()
        print ('(x,y) : (%d,%d)'%(x,y))
        print ('(w,h) : (%d,%d)'%(w,h))
      			```
      
      스티커 이미지를 읽고, 위에서 계산한 크기로 resize
      ```python
      			img_sticker = cv2.imread(sticker_path) 
      			img_sticker = cv2.resize(img_sticker, (w,h))
      			print (img_sticker.shape)
      			```
  • 원본 이미지에 스티커 이미지를 추가하기 위해 x,y좌표 조정

refined_x = x - w // 2
refined_y = y - h
print ('(x,y) : (%d,%d)'%(refined_x, refined_y))

y축(x축) 좌표의 값이 음수가 나왔다면?

우리는 현재 이마 자리에 왕관을 두고 싶은건데, 이마위치 - 왕관 높이를 했더니 이미지의 범위를 초과하여 음수가 나오는 것이다! opencv는 ndarray데이터를 사용하는데 , ndarrau는 음수인덱스에 접근 불가하므로 스티커 이미지를 잘라 줘야 한다.

if refined_x < 0: 
    img_sticker = img_sticker[:, -refined_x:]
    refined_x = 0
if refined_y < 0:
    img_sticker = img_sticker[-refined_y:, :]
    refined_y = 0

print ('(x,y) : (%d,%d)'%(refined_x, refined_y))

위 코드를 이용해 스티커를 잘라주고 좌표를 수정해 주었다.

  • 원본 이미지에 스티커 적용

sticker_area = img_show[refined_y:refined_y+img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
img_show[refined_y:refined_y+img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,sticker_area,img_sticker).astype(np.uint8)
print("슝~")

sticker_area는 스티커를 적용할 위치를 crop한 이미지이다.

우리는 (344,0)부터 (344+187, 0+89)범위이다.

  • 랜드마크와 바운딩 박스 없이 사진 출력
sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,sticker_area,img_sticker).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()

다른 사진으로 고양이 수염 스티커 붙혀보기

위에서 배운 노드를 활용하여 손흥민 선수 사진에 고양이 수염 스티커를 붙혀보았다.
https://github.com/chogyeongmo98/aiffel_exp/blob/main/%5BEx-3%5D%EA%B3%A0%EC%96%91%EC%9D%B4%20%EC%88%98%EC%97%BC%20%EC%8A%A4%ED%8B%B0%EC%BB%A4%20%EB%A7%8C%EB%93%A4%EA%B8%B0.ipynb

0개의 댓글