Schoolwork/컴퓨터그래픽스 및 비젼

[Python] 03주차 과제 - 히스토그램 역투영과 오츄의 이진화를 이용한 얼굴 검출

FATKITTY 2020. 9. 5. 11:38
반응형

● 목적:

- 이 과제에서는 영상에 히스토그램 역투영 방법을 적용하여 얼굴 영역을 검출한다.

 

● 참고 자료 - 아래 예제 및 소스코드 참조 바랍니다.

1. 오츄 자동 threshold
https://opencv-python.readthedocs.io/en/latest/doc/09.imageThresholding/imageThresholding.html

2. 2d histogram
https://opencv-python.readthedocs.io/en/latest/doc/21.image2DHistogram/image2DHistogram.html

3. 히스토그램 역투영 
https://webnautes.tistory.com/1275

 

● 주의할 점

- numpy package 사용시 배열의 가감승제등의 연산에서 data type 에 유의 할 것. 예상치 못한 결과를 가져오는 경우가 있습니다.

- OpenCV의 함수에서 출력 영상에 주의하자. 출력 영상을 저장할 배열을 함수에서 새로 생성하는 경우도 있고 함수 호출전에 미리 생성해 놓아야 하는 경우도 있다.

- Googling 하면 많은 예제와 참고 자료가 나옵니다. 활용합시다. 특히 error 는 stackoverflow 와 같은 사이트를 이용하면 해결할 수 있는 경우가 많습니다.

- Colab 은 예제를 실행해 보기에는 좋지만 키보드, 마우스, 디스플레이 등의 입출력 장치를 사용할 때는 별도의 코딩이 필요합니다. 이런 경우 Pycharm 등이 더 쉬울 수 있습니다. 각자 선호하는 개발 환경 사용하세요.

- 주요한 과정에 대하여 opencv, numpy 등, package 함수를 사용해도 무방합니다. 단, 모델 히스토그램을 생성 과정, 역투영하는 과정을 직접 코딩할 경우 소정의 보너스 포인트 부여합니다.

- 필요한 경우 각 단계에서 *적절한* 최적화 가 필요합니다. 여러분의 최적화 노력을 PPT 에 간략하게 설명하기 바랍니다.

- 기타 세부적인 사항은 적절히 가정하시기 바랍니다.

 

1. 모델 영상을 준비한다.

     A. 사람의 얼굴부분만을 수집하여 모델영상을 만든다. 다양한 인종을 포함해도 됩니다.

     B. HSV 포맷으로 변환

     C. 프로그램이 아닌 사진 편집도구를 사용할 것.  GIMP는 포토샵의 모든 기능을 가진 무료 사진 편집 도구이다. → Googling

 

2. 모델 영상으로부터 모델 히스토그램을 작성한다.

     A. H, S 요소에 대한 히스토그램입니다. 최종적인 모델 히스토그램은 정규화된 히스토그램입니다.

     B. 검출 성능을 높이기 위해서 적절한 양자화-quantization 이 필요합니다. 양자화 단계수에 대하여 최적화가 필요합니다.

모든 단계수를 다 써볼 필요는 없지만 “적절한 노력으로” 실험에 의하여 결정합시다. 

양자화 단계수 란 bin 의 숫자를 말합니다.
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
함수 에서
histSize
bin 의 숫자 입니다.  Default 값은 256 이므로 0, 255 256 단계(bin)에 대하여 빈도수를 계산합니다. 그러나 bin 128 이라면 화소값을 두개씩 묶어 0, 1 의 빈도수, 2, 3의 빈도수, …. 등으로 128 단계에 대하여 빈도수를 계산합니다. bin 64 라면 화소값을 네 개씩 묶어서 64 단계에 대하여 빈도수를 계산 합니다.
이렇게 화소의 밝기 단계수를 조절하는 것을 양자화 - quantization이라고 합니다.

     C. 히스토그램을 plot-그래프 해 봅시다. Plot 할 때는 [0, 255] 범위로 정규화-normalize 해야 합니다.

 

3. *적절한* 크기의 사람 얼굴이 포함된 입력영상에 대하여 모델 히스토그램을 이용하여 역투영 과정을 실행합니다.

     A. 입력영상을 역투영한 결과 영상-신뢰도 맵을 디스플레이 해 봅시다.
마찬가지로 디스플레이 하기 위해서는 신뢰도 맵의 화소값이 [0, 255] 가 되도록 정규화(normalize)가 필요합니다.

     B. 디스플레이 결과를 보고 사람의 피부에 대하여 높은 신뢰도 값을 보여 주는지 확인 합시다.

 

4. 신뢰도 맵에 대하여 이진화를 시도합시다.  

     A. 신뢰도 맵에 대하여 히스토그램을 계산하고 plot해 보세요.

     B. 여러분이 육안으로 히스토그램에서 적절한 임계값을 결정하세요.  

     C. 임계값으로 이진화를 시도하고 이진화된 영상을 디스플레이하여 확인합시다.

 

5.     신뢰도 맵에 대하여 오츄의 알고리즘을 적용하여 이진화하고 디스플레이해 봅시다.

     A. 알고리즘 수행 결과 임계값은 얼마인가요?

 

6. 결과고찰

     A. 모델 히스토그램 생성할 때 다양한 크기의 bin (셀, 구간)에 대하여 최적화한 결과
          즉, bin 크기 와 결과영상을 대비하여 최적인 bin 크기는 얼마인가?

     B. 여러가지 임계 값에 대한 최적화한 결과
         임계값과 결과영상을 대비하여 최적인 임계값은 얼마인가?

     C. Otsu의 자동 임계값과 여러분이 선택한 임계값의 비교
          결과 영상으로 비교

     D. 세 장 이상의 입력 영상으로 실험해 보세요.

과제내용

 

Code

import numpy as np
import cv2
from matplotlib import pyplot as plt

hsv_map = np.zeros((180, 256, 3), dtype=np.uint8)
# zeros()는 shape와 dtype으로 정의된 형태의 zero로 채워진 배열을 리턴함. 여기서는 shape가 3차원 배열

h, s = np.indices(hsv_map.shape[:2])
hsv_map[:,:,0] = h
hsv_map[:,:,1] = s
hsv_map[:,:,2] = 255
hsv_map = cv2.cvtColor(hsv_map, cv2.COLOR_HSV2BGR)
cv2.imshow('hsv_map', hsv_map)
cv2.namedWindow('hist', 0)
hist_scale = 10
# hist_scale 변수는 트랙바를 사용하여 조절하도록 되어 있습니다.
def set_scale(val):
    global hist_scale
    hist_scale = val
cv2.createTrackbar('scale', 'hist', hist_scale, 32, set_scale)

# 모델 이미지 준비, 히스토그램 작성
skin = cv2.imread('face.jpg')
hsv_skin = cv2.cvtColor(skin,cv2.COLOR_BGR2HSV)
img = cv2.imread('pictures.jpg')
hsv_img = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

while True:
    dark = hsv_img[..., 2] < 32
    hsv_img[dark] = 0
    # 이미지에서 Hue와 Saturation에 대한 히스토그램
    h = cv2.calcHist([hsv_img], [0, 1], None, [180, 256], [0, 180, 0, 256])
    h = np.clip(h * 0.005 * hist_scale, 0, 1)
    vis = hsv_map * h[:, :, np.newaxis]
    scale = np.uint8(vis)
    cv2.normalize(scale, scale, 0, 255, cv2.NORM_MINMAX)
    cv2.imshow('hist', scale)
    ch = cv2.waitKey(1)
    if ch == 32 or ch == 27:
        break

M = cv2.calcHist([hsv_skin],[0, 1], None, [180, 256], [0, 180, 0, 256] )  # 오브젝트 영역 이미지(M)
I = cv2.calcHist([hsv_img],[0, 1], None, [180, 256], [0, 180, 0, 256] )  # 오브젝트 영역을 찾을 이미지(I)
cv2.imshow('i', I)  # 흑백 히스토그램
R = M/(I+1)  # 히스토그램 M과 I의 비율을 계산. 분모에 1 안 더하면 0 때문에 nan 값 생김. (어차피 '비율'을 구하는거니까 1을 더하는건 상관x)

h,s,v = cv2.split(hsv_img)  # HSV 색공간의 타겟 이미지를 h,s,v 채널로 분리
B = R[h.ravel(), s.ravel()]
B = np.minimum(B, 1)  # 배열 B에서 1 이상 값을 1로.
B = B.reshape(hsv_img.shape[:2])  # B의 shape 를 1차원에서 이미지 크기의 shape 로 다시 변경

disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
cv2.filter2D(B, -1, disc, B)
B = np.uint8(B)
cv2.normalize(B, B, 0, 255, cv2.NORM_MINMAX)  # 배열 B의 타입을 uint8로 변환하고 0 ~ 255 사이의 값으로 정규화
hist_2 = cv2.calcHist([B], [0], None, [256], [0,256])  # 역투영 결과에 대한 히스토그램

plt.plot(I, color ='r')
plt.title('Confidence Map Histogram')
plt.xlim([0, 256])
plt.show()
plt.waitforbuttonpress()
plt.close()
cv2.imshow('exam', B)  # 역투영 결과 신뢰도맵
cv2.waitKey(0)
th = cv2.adaptiveThreshold(B, 121, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,15,2)  # 임계점 설정 후 신뢰도 맵 이진화
cv2.imshow('bn', th)  # 이진화 결과
cv2.waitKey(0)
ret,thresh = cv2.threshold(B,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

thresh = cv2.merge((thresh,thresh,thresh))   # 1채널 바이너리 이미지를 3채널 이미지(BGR)로 변환한 후,
res = cv2.bitwise_and(img,thresh)   # 원본 이미지와 and 연산하여 겹치는 부분만 얻습니다.

cv2.imshow("result1", thresh)
cv2.imshow("result2", res)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

점수 9/10

반응형