티스토리 뷰
2021.1.12 23:19
안녕하세요
프로그래밍을 배우는 빛나는 샤트입니다.
AIFFEL 대전 1기 Exploration 3장 카메라 스티커앱 만들기
*2021.01.12 (화)
0. 카메라 스티커앱 소개 및 프로젝트 목표
아래와 같이 카메라가 얼굴을 인식해 다양한 효과의 스티커 등을 이목구비의 적절한 위치에 표현해주는 것이다!
몇 번 사용은 해봤지만 실제로 이걸 만들어 볼 줄이야😄
⭐프로젝트 목표⭐
'셀카 사진을 이용해 고양이 수염 이미지를 코에 알맞게 위치시키기'
🐍프로젝트 결과🐍
샤트은(는) 고양이 수염을 코에 잘 장착했다!
효과는...대단했다!!
1. 프로젝트 진행 순서
- 이미지 불러오기
- 경계박스, 랜드마크 찾기 (dlib 이용)
- 코 끝 위치, 스티커 사이즈 조절
- 스티커 이미지 영역 좌표 설정
- np.where를 활용해 스티커 이미지 + 원본 이미지
2. 이미지 불러오기
# 필요한 모듈 불러오기
import cv2
import matplotlib.pyplot as plt
import numpy as np
import os
my_image_path = os.getenv('HOME')+'/1.AIFFEL_Study/Exploration/E03_sticker_camera/image2.jpg'
print('이미지 경로는: ', my_image_path)
img_bgr = cv2.imread(my_image_path) #- OpenCV로 이미지를 읽어서
img_bgr = cv2.resize(img_bgr, (640, 360)) # 640x360의 크기로 Resize
img_show = img_bgr.copy() #- 출력용 이미지 별도 보관
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # BGR -> RGB 변환
plt.imshow(img_bgr)
plt.show()
3. 경계박스, 랜드마크 찾기 (dlib 이용)
1) dlib를 이용해 hog detector 선언
2) .get_frontal_face_detector() 메소드 이용해 얼굴의 경계 박스(bounding box) 검출
※ 메소드 두번째 인자 = 이미지 피라미드: 이미지를 upsampling 방법을 통해 크기를 키우는 것
> 이미지 피라미드에서 얼굴을 다시 검출하면 작게 촬영된 얼굴을 크게 볼 수 있기 때문에 더 정확한 검출이 가능!
import dlib
detector_hog = dlib.get_frontal_face_detector() #- detector 선언
dlib_rects = detector_hog(img_rgb, 1) #- (image, num of img pyramid)
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) 랜드마크 추출 및 출력
# 랜드마크 불러오기
model_path = os.getenv('HOME')+'/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
'''
RGB이미지, dlib.rectangle을 입력받아 dlib.full_object_detection 반환
map함수를 이용해 (a,b) -> points.part()에서 x와 y를 추출해 list_points = [(x1, y1), (x2, y2), ... , (x68, y68)] 만들고
append를 이용해 list_landmarks라는 리스트에 추가. 즉 list_landmarks = [ [ (x1, y1), (x2, y2), ... , (x68, y68) ] ]
'''
points = landmark_predictor(img_rgb, dlib_rect)
list_points = list(map(lambda p: (p.x, p.y), points.parts()))
list_landmarks.append(list_points)
print(len(list_landmarks[0]))
2)에서 진행한 코드에서 dlib_rects란 변수를 보면 get_frontal_face_detector라는 메소드와 연결되어 있다.
바로 위 코드를 하나씩 살펴보면,
points라는 변수를 선언하고 거기에 원본이미지(RGB)와 dilb.rectangle를 입력받아 dlib.full_object_detection 변환해 저장
list_points라는 변수는 일단 list 타입이다. 그 안에 map함수가 있는데 map은 아래와 같다.
>>> a = [1.2, 2.5, 3.7, 4.6]
>>> a = list(map(int, a))
>>> a
[1, 2, 3, 4]
# 이렇게 map(형식, 적용대상) 으로 사용할 수 있다.
파이썬 map: dojang.io/mod/page/view.php?id=2286
다음은 lambda! 자주 나오기 때문에 익혀두는 것이 좋다!
lambda 변수 : 변수를 어떻게 표현할 지, 배열
# lambda : 표현식
>>> (lambda x,y: x + y)(10, 20)
30
# map과 lambda 함께 활용
>>> list(map(lambda x: x ** 2, range(5)))
[0, 1, 4, 9, 16]
파이썬 lambda: wikidocs.net/64
다시 list_points 변수를 보면 위에서 정의한 points 변수에서 parts()메소드를 사용해 x,y좌표값들을 받아와서 lambda, map를 활용해 좌표값들을 쭉 저장하는데, 저장하는 곳이 list이다!
마지막 append는 리스트에 새로운 인자를 추가하는 내용이다.
즉, 여기서는 랜드마크의 좌표값들의 리스트를 만들기 위한 작업이었다고 보면 될 것이다.
아래는 경계박스와 랜드마크를 출력해 확인해보는 것이다.
# 랜드마크 68개가 노란 점으로 잘 표현된 것으로 확인했다.
for landmark in list_landmarks:
for idx, point in enumerate(list_points):
cv2.circle(img_show, point, 2, (0, 255, 255), -1) # yellow
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
4. 코 끝 위치, 스티커 사이즈 조절
고양이 수염은의 중앙은 어디로 가야할까?
바로 코끝이다!
그럼 랜드마크를 보고 몇번에 집중해야할 지 살펴보자
우리는 33번 점이 코 끝인것을 알 수 있다.
고양이 수염 이미지에서 코 부분을 33번에 매칭하면 뭔가 될 것 같다.
1) 코끝 좌표와 고양이 수염 이미지 사이즈 설정
for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
print ('코끝의 위치: ', landmark[33]) # nose edge index : 33, 코끝의 위치: (387, 240)
x = landmark[33][0] # 수염의 중앙 x좌표값
y = landmark[33][1] # 수염의 중앙 y좌표값
w = dlib_rect.width() # 수염 스티커 resize를 위한 경계박스 너비
h = dlib_rect.width() # 수염 스티커 resize를 위한 경계박스 높이
print ('(x,y) : (%d,%d)'%(x,y)) # (x,y) : (387,240)
print ('(w,h) : (%d,%d)'%(w,h)) # (w,h) : (130,130)
여기서 landmark[33]을 이용해 코 끝의 좌표값을 얻을 수 있다. 이것을 x,y 변수에 저장!
그리고 w,h에는 경계박스 width, height를 지정해준다. (고양이 이미지 사이즈 조정용, resize 메소드 이용 예정)
2) 수염 이미지 불러오기 & resize
import os
sticker_path = os.getenv('HOME')+'/1.AIFFEL_Study/Exploration/E03_sticker_camera/cat-whiskers.png'
print('수염 이미지 경로: ', sticker_path)
img_sticker = cv2.imread(sticker_path)
img_sticker = cv2.resize(img_sticker, (w,h))
print (img_sticker.shape) # (130, 130, 3)
plt.imshow(img_sticker)
5. 스티커 이미지 영역 좌표 설정
🔥사실 가장 중요한 부분이 아닐까 싶다🔥
이 파트의 목표를 잘 잡고 가야한다.
"스티커가 원본 이미지에서 차지할 영역(두 개의 좌표값)을 알아내야 한다"
sticker_area를 알아내야 한다.
잠깐!
⚡원본 이미지에 스티커 이미지가 입혀지는 원리⚡
원본 이미지에 스티커 이미지가 들어갈 영역(여기서는 코 주위)의 이미지 픽셀값을 스티커 이미지의 픽셀값으로 바꿔치기 하는 것!
포토샵 프로그램과 같이 원본 이미지 위에 그냥 스티커 이미지를 붙여넣는것이 아니다.
원본 이미지의 픽셀값을 바꿔줘서 마치 '원본 이미지와 스티커 이미지가 합쳐진 것'처럼 보이는 것이다.
아래 코드를 보면 img_bgr(원본 이미지)에 고양이 수염이 들어갈 영역을 []안에 지정해줘야 한다.
영역지정은 고양이 수염의 영역 좌측 상단과 우측 하단의 좌표값의 차이로 구할 수 있다.
아래 그림을 통해 좀 더 알아보자
img_bgr[좌측 상단 y 좌표 : 우측 하단 y 좌표, 좌측 상단 x 좌표 : 우측 하단 x 좌표] = \
np.where(img_sticker==~~~, ~~~~생략~~~~)
# 아래는 이미지 출력하는 부분
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()
1) 원본 이미지에서 코 끝의 좌표값 (landmark[33])을 구한다. 이것이 수염의 중앙이 될 것이다.
2) 고양이 수염의 중앙이 코 끝과 일치하게 된다면 아래 그림처럼 된다.
- 파란색 박스가 고양이 수염 이미지의 영역이다. 우리는 위에서 수염 중앙을 구했으므로 고양이 수염 영역도 간단한 수식을 통해 구할 수 있다!
(수식을 코딩으로 구현한 부분은 과제가 종료된 후 포스팅하겠습니다.)
이상 AIFFEL 탐험 노드 3번째 '카메라 스티커앱 만들기'였습니다.
아 각도와 조명, 촬영거리 등의 변수가 미치는 영향은 테스트 후에 포스팅 예정😎
'교육 > AIFFEL(대전)1기' 카테고리의 다른 글
AIFFEL(대전) [Exploration]2장 머신러닝 데이터 분류하기 (0) | 2022.02.17 |
---|---|
[Exploration]np.where 활용 (0) | 2022.02.17 |
AIFFEL(대전) [Fundamental] 2장 필수 교양 Part.1 (0) | 2022.02.17 |
AIFFEL(대전) [Fundamental] 1장 인공지능 토지 가꾸기 (0) | 2022.02.16 |
AIFFEL(대전) [Fundamental] 0장 환경설정 (0) | 2022.02.16 |
- Total
- Today
- Yesterday
- 모두의 연구소
- 인공지능
- 광주AI
- 서빙로봇
- AIFFEL인공지능과정
- 인공지능교육
- 해커톤
- 모두의연구소
- SLAM공부
- AIFFEL
- 자율주행기술
- 도전
- 광주인공지능사관학교
- 멘탈관리
- 아이펠
- 자율주행로봇
- Python
- AIFFEL교육
- 양정연SLAM
- 인공지능 교육
- Slam
- 배달로봇
- 대전 인공지능
- IT
- 광주
- 멋쟁이사자처럼
- 실내자율주행
- SLAM강의
- ros
- AIFFEL후기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |