snow나 snapchat같은 카메라 필터앱처럼 사진에서 얼굴을 인식하고 해당 위치에 스티커 사진을 삽입하는 프로그램을 만들어 보자. 기본 원리는 다음과 같다.
얼굴 검출 face detection
Object detection 기술을 이용해서 얼굴의 위치를 찾는다.
- dlib의 face detector는 HOG(Histogram of Oriented Gradients)와 SVM(Support Vector Machine) 사용
- HOG : 이미지에서 색상의 변화량을 나타낸 것
- Deep-learning 나오기 이전 사용 多
- 이미지로부터 물체의 특징을 잡아내는 능력 大
- 얼굴 인식해 카메라 초점잡는 기능에 사용
- SVM : 선형분류기, 한 이미지를 다차원 공간의 한 벡터로 보고, 벡터간 구분하는 방식
이미지의 색상만 가지고는 SVM이 큰 힘을 발휘 X. 하지만 이미지가 HOG를 통해 벡터로 만들어진다면 SVM이 잘 작동
얼굴 위치찾기 - sliding window 기법
- 작은 영역(window)을 이동해가며 확인하는 방법
- 큰 이미지의 작은 영역을 잘라 얼굴이 있는지 확인하고, 다시 작은 영역을 옆으로 옮겨 얼굴이 있는지 확인하는 방식.
- 이미지가 크면 클수록 오래걸리는 단점 - 딥러닝의 필요 이유
더 정확한 이미지 검출을 위해 - Image pyramid : Upsampling
upsampling이란,
- 간단하게는 데이터의 크기를 키우는 것. 일반적으로 CNN의 레이어를 통과하면서 이미지의 크기를 줄이는 것을 down sampling이라고 하는데, down sampling의 반대 개념이 upsampling
Image pyramid란?
- 이미지를 upsampling해서 크기를 키우는 것, 작게 촬영된 얼굴을 크게 보게 만드는것 - 정확한 검출 가능
얼굴 랜드마크 Face Landmark
- 스티커를 섬세하게 적용하기 위해서는 이목구비의 위치를 아는 것이 중요.
- 이목구비의 위치를 추론하는 것을 face landmark localization 기술
- face landmark는 detection 의 결과물인 bounding box 로 잘라낸(crop) 얼굴 이미지를 이용
- Object keypoint estimation 알고리즘 - 객체 내부의 점을 찾는 기술
- top-down : bounding box를 찾고 box 내부의 keypoint를 예측
- bottom-up : 이미지 전체의 keypoint를 먼저 찾고 point 관계를 이용해 군집화 해서 box 생성
- Dlib landmark localization
잘라진 얼굴 이미지에서 아래 68개의 이목구비 위치를 찾기
※ 점의 개수는 데이터셋이나 논문마다 다름
아래는 해당 프로그램을 파이썬으로 구현한 알고리즘이다.
바운딩 박스 정 중앙에 스티커를 위치시키는 코드다
# 고양이 스티커 부착하기
## 1. 필요 모듈 임포트
import os
import cv2
import matplotlib.pyplot as plt
import numpy as np
import dlib
## 2. 이미지 경로 설정 및 이미지 전처리
### 이미지를 가져와 사용할 준비를 한다. opencv 특징 상 BGR 색상계 사용하므로 사진 색이 변경될 수 있다. 따라서 cv2.cvtColor() 메서드를 통해 다시 RGB 색상계를 이용한 이미지로 변경해준다.
#이미지 경로설정
my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/image_3.png'
img_bgr = cv2.imread(my_image_path) # OpenCV로 이미지를 불러옵니다
img_show = img_bgr.copy() #출력용 이미지는 따로 보관함.
print("<Before preprocessing>")
plt.imshow(img_bgr)
plt.show()
##이미지 색상 보정처리
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
print("<After preprocessing>")
plt.imshow(img_rgb)
plt.show()
## 3. 얼굴 위치 검출기 생성
### 오브젝트 디텍션 기술을 이용해 얼굴의 위치를 찾는다.
### 이미지에서 각 위치마다의 색상 변화량을 HOG를 통해 파악하고 SVM 선형분류기를 이용해 벡터화 시킨다. 이후 sliding window 기법을 이용해 작은 영역을 조금씩 이동해겨며 얼굴을 파악해 정사각형을 그린다.
### 얼굴 탐지기는 dlib의 hog detector를 이용해 구현했다.
# HOG 얼굴 바운딩박스 탐지기 생성 및 할당
detector_hog = dlib.get_frontal_face_detector()
#얼굴 바운딩박스 추출 - 얼굴 검출기를 이미지에 적용
dlib_rects = detector_hog(img_rgb, 1) # (image, num of image pyramid)
# 이미지 피라미드? - 이미지를 upsampling해서 크기를 키우는 것
# 작게 촬영된 얼굴을 크게 보게 만드는것 - 정확한 검출 가능
# 찾은 얼굴 영역 좌표
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)
print("<Face detection rsult>")
plt.imshow(img_show_rgb)
plt.show()
## 4. 얼굴 랜드마크 데이터 불러오기
### Object keypoint estimation 알고리즘을 이용해 객체 내부의 점을 찾는다. 이번 프로젝트에서는 top-down형식으로, bounding box를 찾고 그 안에서 keypoint를 예측하는 형식으로 진행했다.
### 모델은 사전에 http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 에서
### 다운받아 ~/aiffel/camera_sticker/models 내에
### shape_predictor_68_face_landmarks.dat 의 이름으로 저장해 놓았다.
#얼굴 랜드마크 예측 모델 불러오기
#지정한 모델 불러오기
#landmark_predictor 는 RGB 이미지와 dlib.rectangle을 입력 받아 dlib.full_object_detection 를 반환
model_path = os.getenv('HOME')+'/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
## 5. Bounding Box에서 Face landmark 찾아내기
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]))
print(list_landmarks)
# 찾은 랜드마크 영상에 출력하기
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()
## 6. 코 위치 확인하기
### 해당 고양이 스티커는 코 부분에 위치할 것이므로 코의 좌표를 찾는다.
#코위치확인하기
for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
print (landmark[30]) # 데이터 셋 인덱스에서 코의 index는 30 입니다
x = landmark[33][0]
y = landmark[33][1] #- dlib_rect.height()//2
w = h = dlib_rect.width()
print ('(x,y) : (%d,%d)'%(x,y)) #바운딩 박스 내 코의 좌표
print ('(w,h) : (%d,%d)'%(w,h)) #바운딩 박스 크기
## 7. 스티커 적용하기
### 스티커 이미지를 읽어서 적용 해 보았다.
sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png'
img_sticker = cv2.imread(sticker_path) # 스티커 이미지를 불러옵니다
img_sticker = cv2.resize(img_sticker, (w,h))
print (img_sticker.shape)
### 원본 이미지에 스티커 추가하기 위해 x,y 좌표 수정
refined_x = x//2
refined_y = y//2
print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
### y축 좌표값에 발생하는 음수값 처리해주기
#### 여기서 음수는 바운딩 박스 바깥으로 나간 좌표값을 의미
#음수값 만큼 crop해주기
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))
## 8. 원본이미지에 스티커 적용
# 길어서 복잡해 보이지만 img_show[from:to] 형식입니다
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,img_sticker, sticker_area).astype(np.uint8)
## 9. 스티커 결과이미지 출력(img_show로 출력)
plt.imshow(cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB))
plt.show() #스티커 위치가 제대로 들어갔는지 확인
# 결과 이미지 출력(img_rgb로 출력)
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,img_sticker, sticker_area).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
'Computer Technology 기록부 > 코딩기록부 : Python' 카테고리의 다른 글
LSTM 모델을 이용한 인공지능 작사가 구축 (2) | 2022.07.05 |
---|---|
MNIST Dataset 이용한 CNN 모델 구축 (0) | 2022.07.05 |
[최종] 카메라 스티커 만들기 - 예외처리 포함 (0) | 2022.01.13 |
n면 주사위 클래스 함수 만들기 (0) | 2022.01.11 |
객체지향프로그래밍(OPP)이란? (0) | 2022.01.10 |
댓글