파이썬 강의/openCV

파이썬 openCV 7. 히스토그램 평탄화(Histogram equalization)

마리사라 2020. 11. 19. 20:34
반응형

파이썬 openCV 7번째 강의는 히스토그램 평탄화(Histogram equalization)입니다. 히스토그램 평탄화는 히스토그램 평활화, 히스토그램 균등화라고도 부릅니다. 평탄화는 이름처럼 히스토그램을 평평하게 만들어 주는 작업이라고 할 수 있습니다.


0. 히스토그램 평탄화?

 

일반적인 이미지는 괜찮지만, 이미지 중에 히스토그램이 특정 영역에 집중되어 있을 수 있습니다. 그럴 때 히스토그램 평탄화를 통해 히스토그램이 집중되어있는(히스토그램 그래프에서 한쪽이 높은) 부분을 평평하게 만들어줄 수 있는 거죠.

 

설명만 들으면 명암대비 스트레칭과 비슷한 부분이 있다고 느껴지실 겁니다.(아니면 어쩔 수 없고요) 실제로 히스토그램 스트레칭과 비슷한 효과를 줄 수 있지만 결과는 조금 다릅니다.


1. openCV에서의 히스토그램 평탄화

이번 히스토그램 평탄화는 앞서의 강의들과는 달리 LUT(Look Up Table, 룩업 테이블)을 사용해볼까 합니다. 룩업 테이블은 쉽게 말해서 A = B의 집합입니다. 룩업 테이블에 일정한 값(A)을 넣으면 룩업 테이블에 기록된 값에 따라서 또 다른 값(B)이 나오는 형식이죠. 룩업 테이블의 장점은 평탄화의 계산 공식에 따라 나올 값들을 미리 계산해두었기에, 연산 속도가 빨라진다는 것입니다. 매 화소마다 곱하기 나누기를 하는 것보다는 A = B를 계속 반복하는 게 빠르니까요.

 

이제 코드 설명 들어가겠습니다.

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

이번에 쓰는 모듈들입니다. 이제부터는 거의 매번 쓰는 모듈들이라, 앞으로는 미리 import 해두는 걸 전제로 가도록 하겠습니다.

 

img = cv2.imread('lenna.png')

우선 이미지를 불러와줍니다.

 

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

그레이 스케일로 바꾸어 줍니다.

 

histogram, bin = np.histogram(img.ravel(), 256, [0, 256])

이번에는 np.histogram이라는 함수를 사용해보려고 합니다. np가 numpy니까 numpy모듈에 있는 histogram이라는 함수인 겁니다. 이것 역시 plt.hist와 거의 비슷하게 히스토그램을 만들 때 사용하는 함수입니다. 이때 bin에는 도수분포 구간이 들어갑니다.(쓰지는 않습니다)

 

cumsum = histogram.cumsum()

이제 cumsum이라는 변수에 위의 histogram의 누적합(cumsum())을 담아줍니다.

 

LUT = np.uint8((cumsum - cumsum.min()) * 255 / (cumsum.max() - cumsum.min())

이제 룩업 테이블을 만들어줍시다. 결괏값으로 나올 값들은 정수형이 되어야 하니 np.uint8로 미리 정수형으로 선언해줍니다. 그리고 룩업 테이블을 누적합을 통해서 미리 계산해줍니다.

 

equ - LUT[gray]

이제 룩업 테이블에 그레이 스케일 이미지를 넣어주어 평탄화된 이미지를 만들어줍니다.

 

 

자, histogram부터 equ까지가 히스토그램 평탄화 작업입니다. 하지만 평탄화 작업을 할 때마다 이렇게 하면 귀찮겠죠? 그래서 openCV에는 평탄화를 해주는 함수가 따로 있는데요

hist = cv2.equalizeHist(gray)

openCV에 있는 equalizeHist함수를 이용하면 바로 평탄화된 이미지를 얻을 수 있습니다. 그렇다면 왜 이렇게 안 하고 위와 같은 작업이 필요하냐고요? 이 함수는 그레이 스케일에만 적용이 가능합니다. 하지만 위의 계산과정을 응용하면 컬러 이미지에도 적용이 가능합니다.

 

cv2.imshow("original", gray)
cv2.imshow('result1', equ)
cv2.imshow('result2', hist)

마무리로 3가지 이미지를 출력해주고,

 

plt.figure()
plt.subplot(1, 3, 1)
plt.hist(img.ravel(), 256, [0, 256])
plt.subplot(1, 3, 2)
plt.hist(equ.ravel(), 256, [0, 256])
plt.subplot(1, 3, 3)
plt.hist(hist.ravel(), 256, [0, 256])
plt.show()

각각의 히스토그램도 그려주면 끝입니다.

 

 

원본 이미지
직접 계산한 평탄화
함수로 계산한 평탄화
왼쪽 : 원본 / 중앙 : 직접 계산한 평탄화 / 오른쪽 : 함수로 계산한 평탄화

 

 

둘 다 평탄화 작업이 되었습니다. 직접 계산한 건 어두운 부분이 조금 많지만 전체적으로 평평하게 된 모습이고, 함수로 사용한 건 모든 화소가 골고루 평평하게 되었습니다.


2. 마치며

히스토그램 평탄화를 먼저 강의로 만들지, 스트레칭과 end-in기법을 먼저 만들지 생각했었는데, 이번에도 그냥 제가 배운 대로 만들었습니다. ^^

 

이다음은 히스토그램 명세화(specification)를 주제로 만들어보겠습니다. 감사합니다~

 

 

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


img = cv2.imread('lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
histogram, bin = np.histogram(img.ravel(), 256, [0, 256])
cumsum = histogram.cumsum()
LUT = np.uint8((cumsum - cumsum.min()) * 255 / (cumsum.max() - cumsum.min()))
equ = LUT[gray]
hist = cv2.equalizeHist(gray)

cv2.imshow("original", gray)
cv2.imshow('result1', equ)
cv2.imshow('result2', hist)

plt.figure()
plt.subplot(1, 3, 1)
plt.hist(img.ravel(), 256, [0, 256])
plt.subplot(1, 3, 2)
plt.hist(equ.ravel(), 256, [0, 256])
plt.subplot(1, 3, 3)
plt.hist(hist.ravel(), 256, [0, 256])
plt.show()

cv2.waitKey()

 

반응형