히스토그램 균일화

Goal

  • 히스토그램 균일화(Histogram Equalization)에 대해서 알 수 있고, 이것을 이용하여 이미지의 contrast를 향상시킬 수 있다.

Theory

이미지의 히스토그램이 특정영역에 너무 집중되어 있으면 contrast가 낮아 좋은 이미지라고 할 수 없습니다. 전체 영역에 골고루 분포가 되어 있을 때 좋은 이미지라고 할 수 있습니다. 아래 히스토그램을 보면 좌측 처럼 특정 영역에 집중되어 있는 분포를 오른쪽 처럼 골고루 분포하도록 하는 작업을 Histogram Equalization 이라고 합니다.

../../_images/image011.png

이론적인 방법은 이미지의 각 픽셀의 cumulative distribution function(cdf)값을 구하고 Histogram Equalization 공식에 대입하여 0 ~ 255 사이의 값으로 변환을 하게 됩니다. 이렇게 새롭게 구해진 값으로 이미지를 표현하면 균일화된 이미지를 얻을 수 있습니다.

자세한 내용은 Wikipedia 를 참고하시기 바랍니다.

그럼 우선 Numpy를 이용하여 균일화 작업을 하는 예제입니다.

 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
#-*-coding:utf-8-*-
import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('images/hist_unequ.jpg');

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

cdf = hist.cumsum()

# cdf의 값이 0인 경우는 mask처리를 하여 계산에서 제외
# mask처리가 되면 Numpy 계산에서 제외가 됨
# 아래는 cdf array에서 값이 0인 부분을 mask처리함
cdf_m = np.ma.masked_equal(cdf,0)

#History Equalization 공식
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())

# Mask처리를 했던 부분을 다시 0으로 변환
cdf = np.ma.filled(cdf_m,0).astype('uint8')

img2 = cdf[img]
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.subplot(122),plt.imshow(img2),plt.title('Equalization')
plt.show()

Result

../../_images/result01.png

Note

Numpy의 masked array 는 대상에서 비정상적인 대상을 제거할 때 사용되는 모듈입니다. 자세한 사항은 Numpy document를 참고해주시기 바랍니다.

Histogram Equalization의 결과는 밝은 이미지나 어두운 이미지 어떤 것을 사용해도 동일한 결과가 나옵니다. 이것은 이미지의 인식을 할 때 유용합니다. 예를 들면 얼굴인식을 할때 대상 이미지를 Equalization을 하고 나면 동일한 밝기가 되기 때문에 동일한 환경에서 작업을 할 수가 있습니다.

OpenCV에서는 아래의 함수로 간단하게 Equalization을 처리할 수 있습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#-*-coding:utf-8-*-
import cv2
import numpy as np
from matplotlib import pyplot as plt


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

# OpenCV의 Equaliztion함수
img2 = cv2.equalizeHist(img)
img = cv2.resize(img,(400,400))
img2 = cv2.resize(img2,(400,400))

dst = np.hstack((img, img2))
cv2.imshow('img',dst)
cv2.waitKey()
cv2.destroyAllWindows()

Result

../../_images/result02.png

CLAHE (Contrast Limited Adaptive Histogram Equalization)

지금까지의 처리는 이미지의 전체적인 부분에 균일화를 적용하였습니다. 하지만 일반적인 이미지는 밝은 부분과 어두운 부분이 섞여 있기 때문에 전체에 적용하는 것은 그렇게 유용하지 않습니다. 아래 결과를 보시면 이해가 될겁니다.

../../_images/result03.png

위 결과에서 주변의 어두운 부분은 균일화가 적용되어 밝아졌지만, 가운데 이미지는 너무 밝아져 경계선을 알아볼 수 없게 되었습니다. 이 문제를 해결하기 위해서 adaptive histogram equalization을 적용하게 됩니다. 즉, 이미지를 작은 title형태로 나누어 그 title안에서 Equalization을 적용하는 방식입니다. 그런데 여기서도 한가지 문제가 있습니다. 작은 영역이다 보니 작은 노이즈(극단적으로 어둡거나, 밝은 영역)가 있으면 이것이 반영이 되어 원하는 결과를 얻을 수 없게 됩니다. 이 문제를 피하기 위해서 contrast limit라는 값을 적용하여 이 값을 넘어가는 경우는 그 영역은 다른 영역에 균일하게 배분하여 적용을 합니다.

그러면 CLAHE를 적용한 결과를 다시 보겠습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#-*-coding:utf-8-*-
import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('images/clahe.png',0);

# contrast limit가 2이고 title의 size는 8X8
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
img2 = clahe.apply(img)

img = cv2.resize(img,(400,400))
img2 = cv2.resize(img2,(400,400))

dst = np.hstack((img, img2))
cv2.imshow('img',dst)
cv2.waitKey()
cv2.destroyAllWindows()

Result

../../_images/result04.png

결과를 보면 가운데 이미지의 윤곽선도 유지가 되면서 전체적인 contrast가 높아진 것을 볼 수 있습니다.