개념 1. Haar Cascade
Haar Feature
Viola와 Jones가 제안한 개념으로 간단한 특징의 Boosted Cascade로 빠른 오브젝트 검출이 가능하다.
원리
1. Feature 선택: 중요한 Features 선택
2. Attention: 잠재적 영역에 집중한다.
3. 빠른 feature 평가를 위해 적분(Integral) 영상을 사용한다.
특정 Feature는 명암 차이가 있다는 이론을 바탕으로 한다.
이미지에서 영역과 영역의 밝기차를 이용하여 특징을 찾아낸다.
사람을 얼굴, 눈, 코, 입 등 특징적인 밝기 차가 있어서 이를 활용하는 것이다.
OpenCV에서 ML로 훈련시킨 Feature에 따른 Harrdata를 xml 파일 형태로 제공한다.
즉, Haar Cascade는 머신러닝을 기반으로 한 오브젝트(얼굴) 검출 알고리즘 이다.
개념2. Bitwise Operation 비트 연산
Bitwise를 사용하여 영상이나 이미지를 비트 연산 할 수 있다.
연산 함수는 cv2.Bitwise함수(이미지1, 이미지2, 결과, 마스크) 형식이다.
And 연산
A | B | A and B |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Or 연산
A | B | A or B |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Xor 연산
A | B | A xor B |
0 | 0 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Not 연산
A | not A |
0 | 1 |
1 | 0 |
참고로, 컬러 이미지는 한 픽셀당 0~255의 값을 가진다.
검정색은 픽셀 값이 0, 흰색은 픽셀 값이 255로 숫자가 커질수록 밝은 색이 나온다.
위 연산식에서 1을 0이 아닌 모든 수로 간주하면 된다.
즉, 픽셀 값이 0인 곳과 픽셀 값이 100인 것을
and 연산 하면 0이 나올 것이고,
or 연산 하면 100이 나올 것이다.
예시. 두 이미지의 bitwise 연산
import cv2
imgFile1='../img/green.jpg'
imgFile2='../img/tree.png'
img1=cv2.imread(imgFile1)
img2=cv2.imread(imgFile2)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
img1=cv2.resize(img1,(300,300))
img2=cv2.resize(img2,(300,300))
bit_and=cv2.bitwise_and(img1,img2)
bit_not=cv2.bitwise_not(img2)
cv2.imshow('bit_and',bit_and)
cv2.imshow('bit_not',bit_not)
cv2.waitKey()
cv2.destroyAllWindows()
not 연산을 했을 때, 왼쪽 사진처럼 흑과 백이 바뀐 것을 볼 수 있다.
and 연산을 했을 때, bit_and 연산을 한 것은 검정색(픽셀 값이 0)이 아닌 부분의 색깔(초록)이 도출되는 것을 볼 수 있다.
개념3. Threshold 임계값
임계값이란 경계값을 의미한다.
한 기준점을 중심으로 영역이 나뉘게 된다.
영상에서는 cv2.threshold 함수를 사용하는데 그레이 스케일 (흑백) 이미지만 적용이 가능하다.
함수는 cv2.threshold(이미지, 임계값, 최대값, 임계값 적용 스타일) 형식으로 쓰여진다.
(자세한 설명은 공식 문서를 참고해주세요.)
다음 이미지를 그레이스케일로 읽어와서 네번째 매개변수를 변화시키며 결과를 관찰해 보자.
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('baboon.jpg', 0)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
결과
개념4. 디지털영상 표현과 Zero-based 좌표계
이미지 좌표는 다음과 같이 이해하면 된다.
- 이미지의 왼쪽 위가 (0,0)
- 가로가 x축, 세로가 y축
- 오른쪽으로 갈수록 x의 값이 커지고 아래쪽으로 갈수록 y값이 커진다.
그래서 얼굴 영역만 슬라이싱 하고싶다면 다음과 같이 하면 될 것이다.
face_img = img[y : y + h , x : x + w]
개념 5. 마스크 적용 원리
아래 실습 코드에서 적용된 변수의 이름을 적어두었다.
img_roi
하르 분류기로 찾아낸 얼굴 영역이다.
얼굴만 슬라이싱 하여 넣어준 것이다.
face_mask_small
마스크 이미지를 resize 함수로 크기 조절을 해준것이다.
gray_mask
위 사이즈 조절된 이미지를 흑백 이미지로 바꾼 것이다.
mask
threshold 함수로 임계값을 기준으로 이진화한 것이다.
THRESH_BINARY_INV 매개변수로 임계값 이하는 255로, 임계값 이상은 0적용된 것이다.
mask_inv
bitwise_not을 사용하여 흑과 백을 바꾸어준다.
masked_face
2번째 이미지(face_mask_small)와 4번째 이미지(mask)를 and 연산 한 것이다.
마스크를 제외한 영역을 검정색으로 바꾸어주는 작업이다.
masked_frame
1번째 이미지와 5번째 이미지를 and연산한 것이다.
마스크를 씌울 부분에 구멍을 뚫어준 것이다.
최종적으로 아래쪽의 두 이미지(masked_face, masked_frame)를 더하여 이미지에 붙여 넣어주면 완성된다!
(궁극적으로 이 두 이미지를 추출하기 위하여 앞선 복잡한 작업들을 해준 것이다.)
실습 코드
import cv2
import numpy as np
# face 분류기 로드
face_cascade = cv2.CascadeClassifier('cascade_files/haarcascade_frontalface_alt.xml')
# 가면 영상
face_mask = cv2.imread('../../img/bb.jpg')
#cv2.imshow('test',face_mask)
h_mask, w_mask = face_mask.shape[:2]
if face_cascade.empty():
raise IOError('Unable to load the face cascade classifier xml file')
cap = cv2.VideoCapture("../../img/faces.mp4")
scaling_factor = 0.1
while True:
ret, frame = cap.read()
#cv2.imshow('Original', frame)
if not ret:
break
frame = cv2.resize(frame, None,fx=scaling_factor,fy=scaling_factor, interpolation=cv2.INTER_AREA)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
face_rects = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in face_rects:
if h > 0 and w > 0:
x = int(x-w*0.1 )
y = int(y -h*0.05)
w = int(1.2* w)
h = int(1.2 * h)
frame_roi = frame[y:y + h, x:x + w]
face_mask_small = cv2.resize(face_mask, (w, h), interpolation=cv2.INTER_AREA)
gray_mask = cv2.cvtColor(face_mask_small, cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(gray_mask, 240, 255, cv2.THRESH_BINARY_INV)
#cv2.imshow('gray_mask', gray_mask)
#cv2.imshow('mask', mask)
mask_inv = cv2.bitwise_not(mask)
masked_face = cv2.bitwise_and(face_mask_small, face_mask_small, mask=mask)
masked_frame = cv2.bitwise_and(frame_roi, frame_roi, mask=mask_inv)
#cv2.imshow('masked_face', masked_face)
#cv2.imshow('masked_frame', masked_frame)
frame[y:y + h, x:x + w] = cv2.add(masked_face, masked_frame)
cv2.imshow('Face Detector', frame)
c = cv2.waitKey(1)
if c == 27:
break
cap.release()
cv2.destroyAllWindows()
결과
[합성 전]
[합성 후]
[합성 전]
[합성 후]
참고로, 얼굴이 기울어지거나 정면이 아닐 때, 얼굴을 잘 검출하지 못할 수 있다.
Github: https://github.com/uyeonH/HelloEngineering
'인공지능 > OpenCV' 카테고리의 다른 글
[OpenCV] 동영상에서 특정 색을 다른 색으로 바꾸어 출력하기 (0) | 2020.04.09 |
---|---|
[OpenCV] Cartoon Effect 이미지 카툰화 (0) | 2020.04.09 |
[OpenCV] 동영상에서 특정 색상의 물체를 찾기 (0) | 2020.04.09 |
[OpenCV] 트랙바를 이용하여 이진화 (0) | 2020.04.09 |
[OpenCV] 유튜브영상 윈도우창에 출력하기 (0) | 2019.08.15 |