히스토그램 평활화(equalization) - equalizeHist

히스토그램 평활화(equalization) - equalizeHist

2018, Dec 29    
  • Reference : Python으로 배우는 OpenCV 프로그래밍
  • Code : https://github.com/gaussian37/Vision/tree/master/OpenCV/histogram

히스토그램 평활화 방법은 영상의 픽셀값들의 누적분포함수 를 이용하여 영상을 개선하는 방법입니다. 즉, 가장 간단한 image enhancement 방법 중 하나입니다.

  • 히스토그램 평활화는 화소값의 범위가 좁은 low contrast 입력 영상으로 화소값의 범위가 넓은 high contrast 출력 영상을 얻습니다.
    • 밝기 값이 몰려 있어서 어둡기만 한 영상 또는 밝기만 한 영상을 평활화하여 좀 더 선명한 영상을 얻습니다.
  • 그레이스케일 영상은 입력 영상에 바로 히스토그램 평활화를 적용하면 됩니다.
  • 컬러 영상은 HSV, YCrCb 등의 컬러 모델로 변환한 다음, 밝기값 채널 (V, Y)에 히스토그램 평활화를 적용 후 BGR 컬러로 변환합니다.
dst = cv2.equalizeHist(src)
  • src : 1채널 8비트 입력 영상. dst는 src와 같은 크기, 같은 종류의 히스토그램 평활화된 출력

  • 평활화 과정

Drawing


먼저 어떤 이미지의 히스토그램이 위와 같다고 생각해 보겠습니다. 상당히 한쪽으로 치우쳐 있습니다. 위와 같은 경우를 이미지에 대응시켜보면 상당히 밝은 이미지에 해당하게 됩니다. 너무 밝아서 선명하지가 않게 됩니다.

Drawing


차례 차례 계산하는 방법을 알아보겠습니다.

  • 1열 Gray Level 은 영상의 픽셀 값이라고 볼 수 있습니다.
  • 2열 \(n_{i}\) 는 각 gray level의 빈도 수 입니다.
  • 3열 \(\sum n_{i}\) 는 2열을 누적합 하였습니다. 누적합의 최종 결과는 360 입니다.
  • 4열 gray level의 총 갯수는 15개 (일반적 이미지에서는 8bit를 사용하므로 255) 이고 누적합이 360 이므로 각 gray level 별 누적합에 \(\frac{15}{360}\) 을 곱해줍니다.
    • 이 방법을 통해 정규화를 해주고 나온 결과값은 반올림을 해줍니다.
  • 5열의 값은 4열 값을 반올림 해준 것이고 2열의 값과 비교하면 균등하게 분포되었음을 볼 수 있습니다.

Drawing


히스토그램이 상당히 균등하게 분포되었음을 볼 수 있습니다.

  • cv2.equalizeHist() 를 사용하면 8bit 이미지를 대상으로 하기 때문에 \(\[0, 255 \]\) 사이로 균등하게 분포시킵니다.
import cv2
import numpy as np

src = np.array([ [0, 0, 0, 0],
                 [1, 1, 1, 1],
                 [200, 200, 200, 200],
                 [250, 250, 250, 250]], dtype = np.uint8)

dst = cv2.equalizeHist(src)

>> print(dst)
[[  0   0   0   0]
 [ 85  85  85  85]
 [170 170 170 170]
 [255 255 255 255]]


결과를 보면 위에서 손으로 계산해 본 것과 같이 0 ~ 255 사이의 범위에서 잘 분포된 것을 보실 수 있습니다. 이번에는 grayscale 이미지에 히스토그램을 적용해 보겠습니다.

코드는 다음과 같습니다.

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

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Image path to the directory")
args = vars(ap.parse_args())
path = args['image']
fname = os.path.splitext(path)[0]

# 입력 받은 이미지를 불러옵니다.
src = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
# 불러온 이미지에 histogram equalization을 적용합니다.
dst = cv2.equalizeHist(src)
srcHist = cv2.calcHist(images = [src],
                       channels = [0],
                       mask = None,
                       histSize = [256],
                       ranges = [0, 256])

dstHist = cv2.calcHist(images = [dst],
                       channels = [0],
                       mask = None,
                       histSize = [256],
                       ranges = [0, 256])

cv2.imshow('src', src)
cv2.imshow('dst', dst)
plt.title('Grayscale histogram of {} image'.format(fname), fontSize = 16)
plt.plot(srcHist, color = 'b', label = 'src hist')
plt.plot(dstHist, color = 'r', label = 'dst hist')
plt.legend(loc='best')
plt.show()


cv2.waitKey()
cv2.destroyAllWindows()


  • src 이미지

Drawing

  • dst 이미지

Drawing

  • 히스토그램 비교

Drawing

dst 이미지의 히스토그램이 전체적으로 잘 분포되어 있고 좀 더 선명한 것을 보실 수 있습니다.

이번에는 컬러 이미지에 equalizeHist 함수를 적용해 보겠습니다. 컬러영상에 히스토그램 평활화를 적용할 때에는 RGB 값에 바로 적용하면 색이 변할 수 있습니다. 먼저 RGB로 받은 이미지를 HSV 또는 YCrCb 형태의 이미지로 변경한 다음에 밝기값 채널을 변경해야 색을 변경하지 않고 선명하게 만들 수 있습니다. (그럼에도 불구하고 미세하게 나마 색이 변경될 수는 있습니다.)

코드는 다음과 같습니다.

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

# ap = argparse.ArgumentParser()
# ap.add_argument("-i", "--image", required=True, help="Image path to the directory")
# args = vars(ap.parse_args())
# path = args['image']

path = "lena.png"

# 입력 받은 이미지를 불러옵니다.
src = cv2.imread(path)

# hsv 컬러 형태로 변형합니다.
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
# h, s, v로 컬러 영상을 분리 합니다. 
h, s, v = cv2.split(hsv)
# v값을 히스토그램 평활화를 합니다.
equalizedV = cv2.equalizeHist(v)
# h,s,equalizedV를 합쳐서 새로운 hsv 이미지를 만듭니다.
hsv2 = cv2.merge([h,s,equalizedV])
# 마지막으로 hsv2를 다시 BGR 형태로 변경합니다.
hsvDst = cv2.cvtColor(hsv2, cv2.COLOR_HSV2BGR)

# YCrCb 컬러 형태로 변환합니다.
yCrCb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
# y, Cr, Cb로 컬러 영상을 분리 합니다.
y, Cr, Cb = cv2.split(yCrCb)
# y값을 히스토그램 평활화를 합니다.
equalizedY = cv2.equalizeHist(y)
# equalizedY, Cr, Cb를 합쳐서 새로운 yCrCb 이미지를 만듭니다.
yCrCb2 = cv2.merge([equalizedY, Cr, Cb])
# 마지막으로 yCrCb2를 다시 BGR 형태로 변경합니다.
yCrCbDst = cv2.cvtColor(yCrCb2, cv2.COLOR_YCrCb2BGR)

# src, hsv, YCrCb 각각을 출력합니다.
cv2.imshow('src', src)
cv2.imshow('hsv dst', hsvDst)
cv2.imshow('YCrCb dst', yCrCbDst)
cv2.waitKey()
cv2.destroyAllWindows()


  • 원본 영상입니다.

Drawing


  • hsv 형태에서 밝기값을 평활화 한 영상입니다.

Drawing