[OpenCV] 04-10. Histograms in OpenCV (2)
🐍Python/OpenCV

[OpenCV] 04-10. Histograms in OpenCV (2)

728x90
반응형

< Histogram Equalization >

이번 장에서는

  • 히스토그램 균등화의 개념에 대해서 배우고 이미지의 대조를 향상시키는데 사용할 것이다.

Theory

픽셀 값이 특정 값 범위에만 국한된 이미지를 고려해보자. 예를 들면, 밝은 이미지는 모든 픽셀이 높은 값으로 범위를 이룰 것이다. 하지만 좋은 이미지는 이미지의 모든 지역에서 픽셀을 가지고 있어야한다. 그래서 히스토그램을 양쪽 끝까지 늘려야 하며, 그것이 히스토그램 균등화가 하는 일이다. 이는 일반적으로 이미지의 대조를 향상시켜준다.


위키피디아 Histogram Equalization에서 자세하게 읽는 것을 추천한다. 대신에, 여기ㅣ서는 Numpy 구현하는 것을 볼 것이다. 그 이후 OpenCV 함수에 대해 볼 것이다.

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

img = cv2.imread('./images/face2.jpg',0)

hist,bins = np.histogram(img.flatten(),256,[0,256])

plt.figure(figsize=(12,8))
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max() / cdf.max()
plt.subplot(1,2,1)
plt.imshow(img,'gray')
plt.subplot(1,2,2)
plt.plot(cdf_normalized,color='b')
plt.hist(img.flatten(),256,[0,256],color='r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc='upper left')
plt.show()


밝은 지역에 놓인 히스토그램을 볼 수 있다. 우리는 꽉 찬 스펙트럼이 필요하다. 이를 위해서 밝은 영역의 입력 픽셀을 전체 영역의 픽셀을 출력하도록 매핑하는 변환 기능이 필요하다. 그것이 히스토그램 균등화가 하는 것이다.

이제 최소 히스토그램 값을 찾아 위키 페이지에 주어진 히스토그램 균등 방정식을 적용한다. 하지만 Numpy의 마스크 배열 개념을 사용했다. 마스크 배열의 경우, 모든 작업은 마스크되지 않은 요소에 대해 수행된다. 마스크 배열에 있는 Numpy Docs에서 이에 대해 더 많이 읽어볼 수 있다.

cdf_m = np.ma.masked_equal(cdf,0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
cdf = np.ma.filled(cdf_m,0).astype('uint8')

이제 모든 입력 픽셀 값에 대한 출력 픽셀 값이 무엇인지에 대한 정보를 제공하는 표를 갖고 있다. 그래서 여기에 변형만 적용하면 된다.

img2 = cdf[img]

이제 히스토그램을 계산하고 cdf를 그려보자

hist,bins = np.histogram(img2.flatten(),256,[0,256])


plt.figure(figsize=(12,8))
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max() / cdf.max()
plt.subplot(1,2,1)
plt.imshow(img,'gray')
plt.subplot(1,2,2)
plt.plot(cdf_normalized,color='b')
plt.hist(img2.flatten(),256,[0,256],color='r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc='upper left')
plt.show()



다른 중요한 특성으로는, 비록 이미지가 우리가 사용한 밝은 이미지 대신에 어두운 이미지일지라도, 평준화 후에 우리는 우리가 받은 것과 거의 같은 이미지를 얻게 될 것이라는 것이다. 결과적으로, 이것은 “참조 도구”로 사용되어, 동일한 조명 조건을 가진 모든 이미지를 만든다. 예를 들어, 얼굴 인식에서 얼굴 데이터를 학습하기 전에, 얼굴 이미지는 동일한 조명 조건으로 모두 만들기 위해 히스토그램으로 동일화된다.

Histograms Equalization in OpenCV

OpenCV는 이를 하기 위한 cv2.equalizeHist() 함수가 존재한다. 입력은 흑백 이미지이고 출력은 균등화된 이미지 히스토그램이다.

아래의 간단한 코드 조각은 우리가 사용한 이미지의 용도를 보여주는 것이다.

img = cv2.imread('./images/face2.jpg',0)
equ = cv2.equalizeHist(img)
res = np.hstack((img,equ)) # 이미지를 사이드-바이-사이드로 쌓는다
cv2.imwrite('res.png',res)


그래서 다른 조명 조건의 다른 이미지를 가지고, 그것을 평등하게 하고 결과를 확인할 수 있다.

이미지의 히스토그램이 특정 영역에만 국한된 경우 히스토그램 균등화가 양호하다. 히스토그램이 큰 영역을 포함하는 큰 강도 편차가 있는 곳, 즉 밝은 픽셀과 어두운 픽셀이 모두 존재하는 곳에서는 잘 작동하지 않을 것이다. 추가 리소스의 SOF 링크를 확인해보자!

CLAHE (Contrast Limited Adaptive Histogram Equalization)

방금 본 첫 번째 히스토그램 균등화는 이미지의 전역 대조를 고려한다. 많은 경우에, 그것은 좋은 생각이 아니다. 예를 들어, 아래 이미지는 입력 이미지와 전역 히스토그램 균등화 후의 결과를 보여준다.


히스토그램 균등화 이후 배경 대비가 개선된 것은 사실이다. 하지만 두 가지 이미지에서 동상의 얼굴을 비교해보자. 지나치게 밝아져서 그곳의 대부분의 정보를 잃어버렸다. 이전 사례에서 본 것처럼 히스토그램이 특정 영역에만 국한되지 않기 때문이다.(입력 이미지의 히스토그램을 표시하려고 하면 더 많은 직관을 얻을 수 있다. )

그래서 이 문제를 해결하기 위해 “적응형 히스토그램 균등화”가 사용된다. 이 경우 이미지는 “tiles”(OpenCV에서 기본적으로 tileSize=8x8이다)라고 하는 작은 블록으로 나뉜다. 그런 다음 이 블록 각각은 평소와 동일하게 히스토그램이 된다. 따라서 좁은 영역에서 히스토그램은 작은 영역(노이즈만 없다면)에만 국한될 수 있다. 노이즈가 있으면 증폭이된다. 이를 방지하기 위해 대조도 제한을 적용한다. 히스토그램 bin이 지정된 대조도 한계(OpenCV는 40)를 초과하는 경우, 히스토그램 균등화를 적용하기 전에 해당 픽셀이 잘려 다른 bin에 균일하게 분포된다. 평준화 후 타일 테두리의 인위적인 것을 제거하기 위해 이선 보간법을 적용한다.

아래 코드는 CLAHE를 어떻게 적용하는지를 보여준다.

img = cv2.imread('./images/face2.jpg',0)

# CLAHE 객체를 생성한다. 
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)

cv2.imwrite('clahe_2.jpg',cl1)

아래 결과는 위의 결과와 비교했을 때, 정보들이 덜 뭉개지는 것을 볼 수 있다.


728x90
반응형