[OpenCV] 카메라 스티커앱 만들기
1. 사진 준비
$ mkdir -p ~/aiffel/camera_sticker/models #디렉토리 생성
$ mkdir -p ~/aiffel/camera_sticker/images #디렉토리 생성
$ ln -s ~/data/* ~/aiffel/camera_sticker/images #이미지 연결
import os
import cv2 #이미지 처리를 위한 OpenCV
import matplotlib.pyplot as plt #이미지 출력을 위한 matplotlib
import numpy as np
import dlib
print("🌫🛸")
#이미지 읽기
my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/image.png'
img_bgr = cv2.imread(my_image_path) # OpenCV로 이미지 불러오기
img_show = img_bgr.copy() # 출력용 이미지 따로 보관
plt.imshow(img_bgr)
plt.show()
>>출력 결과, 얼굴에 푸른빛이 감돈다.
OpenCV는 BGR(파랑, 녹색, 빨강)을 사용. 사진에서 붉은색<->푸른색으로 바뀌어 출력됨
# RGB로 이미지 보정
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.show()
OpenCV 이미지 읽기 flag
- cv2.imread('img.png',1) : IMREAD_COLOR - 이미지 파일을 Color로 읽어들입니다. 투명한 부분은 무시되며, Default값입니다.
- cv2.imread('img.png',0) : IMREAD_GRAYSCALE - 이미지를 Grayscale로 읽어 들입니다. 실제 이미지 처리시 중간단계로 많이 사용합니다.
- cv2.imread('img.png',-1) IMREAD_UNCHANGED - 이미지파일을 alpha channel까지 포함하여 읽어 들입니다.
2. face detection
dlib의 face detector
- HOG(Histogram of Oriented Gradients) : 이미지에서 색상의 변화량을 나타낸 것. 딥러닝 이전에 사용되던 방식
- SVM(Support Vector Machine) : 선형분류기.
- sliding window : 큰 이미지의 작은 영역을 잘라 얼굴이 있는지 확인하고, 다시 작은 영역을 옆으로 옮겨 얼굴이 있는지 확인하며 얼굴 위치 찾기.
# dlib를 활용해 hog detector 선언
detector_hog = dlib.get_frontal_face_detector()
print("🌫🛸")
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) #cvtColor()을 이용해 OpenCV bgr이미지 rgb로 변환
dlib_rects = detector_hog(img_rgb, 1) #두번째 파라미터인 1은 이미지 피라미드의 수
print("🌫🛸")
찾은 얼굴 화면에 출력
# 찾은 얼굴 영역 박스 리스트
# 여러 얼굴이 있을 수 있습니다
print(dlib_rects)
for dlib_rect in dlib_rects: #dlib detector은 dlib.rectangels 타입 객체 반환, left(), top(), right(), bottom(), height(), width() 등 멤버 함수 포함
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
face landmark localization 기술 : 이목구비의 위치 추론. detection 의 결과물인 bounding box 로 잘라낸(crop) 얼굴 이미지를 이용
Object keypoint estimation 알고리즘 : Face landmark와 같이 객체 내부의 점을 찾는 기술
- keypoint를 찾는 알고리즘
1) top-down : bounding box를 찾고 box 내부의 keypoint를 예측
2) bottom-up : 이미지 전체의 keypoint를 먼저 찾고 point 관계를 이용해 군집화 해서 box 생성
Dlib landmark localization
잘라진 얼굴 이미지에서 68개 이목구비 위치 찾기
$ wget http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 #weight file 다운로드
$ mv shape_predictor_68_face_landmarks.dat.bz2 ~/aiffel/camera_sticker/models #압축 풀어주기
$ cd ~/aiffel/camera_sticker && bzip2 -d ./models/shape_predictor_68_face_landmarks.dat.bz2
#저장한 landmark 모델 불러오기
model_path = os.getenv('HOME')+'/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
print("🌫🛸")
list_landmarks = []
# 얼굴 영역 박스 마다 face landmark 찾아내기
for dlib_rect in dlib_rects:
points = landmark_predictor(img_rgb, dlib_rect)#rgb이미지, dlib.rectangle을 입력받아 dlib.full_object_detection를 반환
# face landmark 좌표 저장
list_points = list(map(lambda p: (p.x, p.y), points.parts())) #point는 dlib.full_object_detection 객체로 parts() 함수로 개별 위치에 접근 가능.
list_landmarks.append(list_points)
print(len(list_landmarks[0]))
#랜드마크 영상에 출력
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. 스티커 적용하기
랜드마크를 기준으로 눈썹 위 얼굴 중앙에 스티커를 씌우기
- 스티커 위치
- 스티커 크기
#좌표확인
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))
>> (437, 182) #얼굴이미지에서 코의 중심점이 (437,182)
(x,y) : (437,89)
(w,h) : (187,187)
#스티커 이미지를 읽어서 적용
sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/king.png'
img_sticker = cv2.imread(sticker_path) # 스티커 이미지 읽기
img_sticker = cv2.resize(img_sticker, (w,h)) #윗 단계에서 계산한 크기로 리사이즈
print (img_sticker.shape)
>> (187, 187, 3)
#스티커 이미지 추가를 위해 x,y좌표 조정
refined_x = x - w // 2
refined_y = y - h
print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
>> (x,y) : (344,-98) #y축좌표의 값이 음수로 계산됨 문제 발생
->음수로 계산된 이유 : 스티커의 시작점이 얼굴 사진의 영역을 벗어났기 때문에.
스티커의 시작점 + 스티커 사진의 크기가 원본 이미지의 크기보다 큰 경우 문제 해결 방법
#-y 크기만큼 스티커를 crop, top 의 x좌표와 y 좌표를 각각의 경우에 맞춰 원본 이미지의 경계 값으로 수정
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))
# 길어서 복잡해 보이지만 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,sticker_area,img_sticker).astype(np.uint8) #sticker_area:원본이미지에서 스티커를 적용할 위치를 crop한 이미지
print("슝~")
#결과 수행
plt.imshow(cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB))
plt.show()
#bounding box, landmark 제거
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()
📆 2022-01-17
📚 EXPLORATION NODE3
Author And Source
이 문제에 관하여([OpenCV] 카메라 스티커앱 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yenaryu/머신러닝-카메라-스티커앱-만들기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)