Python 영상처리 기초 with OpenCV

[Python 영상처리 기초 7] 이미지의 화질개선 Contrast-Brightness-Gamma - part2

toyprojects 2023. 9. 17. 03:23

 

 

앞선글 [Python 영상처리 기초 7] 이미지의 화질개선 Contrast-Brightness-Gamma - part1

에서는 주로 이미지의 brightness 개선에 주목 했다면 본 글에서는 contrast 개선을 다루겠습니다. 

앞서 언급했듯이 brightness와 contrast 는 긴밀한 관계이지만 선형적인 상관관계를 갖지 않습니다.

따라서 contrast가 조정됨과 동시에 brightness가 달라질 수 있고, 반대의 경우도 가능합니다.

 

 

 

Cells from cervical cancer - National Cancer Institute (출처1)

 

 

 

Contrast 개선

 

이번에도 위의 예제 이미지의 gray scale 이미지를 바탕으로 contrast 개선에 주목하겠습니다.

위의 이미지는 본 블로그 맨밑 출처 1에 가셔서 다운로드 받을 수 있습니다. 

 

첫 번째로 이미지 픽셀값의 normalization을 통한 contrast stretching 입니다. 

" [Python 영상처리 기초 5] 이미지의 화질개선 - Normalization, Standardization " 에서 다룬

Min-Max Normalization의 식은 다음과 같고

 

 

min-max scaling = (X - Xmin) / (Xmax - Xmin)

(여기서 X: 픽셀값, Xmax: 픽셀의 최대Xmin: 픽셀의 최소값)

 

 

밑의 결과 이미지(우측)에서 볼 수 있듯이 왼측의 입력 이미지에 비해 contrast 개선이 이루어 졌음을

알 수 있고, 왼편의 입력 이미지의 histogram은 낮은 픽셀값 영역(픽셀값 0에서 0.4 사이)에 몰려 있는 반면

오른편의 histogram은 픽셀값 0.6 까지 확대되어 있음을 볼 수 있습니다.

 

 

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

img_color = cv2.imread('national-cancer-institute-jdfn7Z03Qa4-unsplash.jpg',cv2.IMREAD_COLOR)
img_gray = cv2.cvtColor(img_color,cv2.COLOR_BGR2GRAY)

# data type conversion and rescaling
img_gray_copy = img_gray.copy().astype(np.float32) # convert from uint8 to float32 type data
img_gray_copy /= 255.                              # rescaling

max_value = np.max(img_gray_copy)
min_value = np.min(img_gray_copy)

# pixel normalization: Scales values of the pixels in 0-1 range.
# min-max scaling = (X - Xmin) / (Xmax - Xmin), where X is a pixel of an image data 
img_gray_normal = (img_gray_copy - min_value) / (max_value - min_value)

img_gray_hist = cv2.calcHist([img_gray_copy],[0],None,[256],[0,1.0])
img_gray_normal_hist = cv2.calcHist([img_gray_normal],[0],None,[256],[0,1.0])

fig = plt.figure( figsize=(12, 10) )
fig.add_subplot(2,2,1)
plt.axis("off")
plt.imshow(img_gray_copy, cmap='gray') 
plt.title('Original')

fig.add_subplot(2,2,2)
plt.axis("off")
plt.imshow(img_gray_normal, cmap='gray') 
plt.title('Normalization result')

fig.add_subplot(2,2,3)
plt.plot(img_gray_hist)
plt.xticks(np.arange(0, 256, step=50),['0','0.2','0.4','0.6','0.8','1.0'])

fig.add_subplot(2,2,4)
plt.plot(img_gray_normal_hist)
plt.xticks(np.arange(0, 256, step=50),['0','0.2','0.4','0.6','0.8','1.0'])

plt.show()

 

 

Contrast 개선한 결과 image와 histogram (좌: 입력 이미지, 우: normalization 을 이용한 contrast stretching 결과)

 

 

 

앞선글 [Python 영상처리 기초 7] 이미지의 화질개선 Contrast-Brightness-Gamma - part1

에서 소개한 다음의 변환수식 (출처 2)중 alpha를 제어함으로써 contrast를 개선할 수 있습니다.

 

 

g(i, j) = alpha · f(i, j) + beta

 

 

입력 이미지 f( i 번째 행, j 번째 열 ) 는 alpha와 beta - 각각 gain과 bias로 불리며 contrast와 brightness

를 제어하는 파라메터 - 값에 의한 연산으로 결과 이미지 g(i, j) 를 얻습니다. 

 

 

다음은 contrast 변환의 결과를 보여줍니다. 입력 이미지는 uint8 타입으로써 최소 0, 최대 255의 픽셀값

범위를 가지며, 변환수식중 임의값 alpha = 0.6 으로 대상 이미지의 contrast를 축소시켰고, 밑의

contrast 변환 전후의 histogram를 비교해보면 그래프의 픽셀값 범위가 크게 압축되었음을 알 수 있습니다.

 

 

alpha = 0.6   # contrast control
beta = 0      # brightness control
img_alpha_DOWN = cv2.convertScaleAbs(img_gray, alpha=alpha, beta=beta)

img_gray_hist = cv2.calcHist([img_gray], [0], None, [256], [0, 256])
img_alpha_DOWN_hist = cv2.calcHist([img_alpha_DOWN], [0], None, [256], [0, 256])

fig = plt.figure(figsize=(12,10))    
fig.add_subplot(2,2,1)
plt.axis("off")
plt.title("Original")
plt.imshow(img_gray,cmap='gray')

fig.add_subplot(2,2,2)
plt.axis("off")
plt.title("Contrast reduced")
plt.imshow(img_alpha_DOWN,cmap='gray')

fig.add_subplot(2,2,3)
plt.plot(img_gray_hist)
plt.xlim([0, 256])

fig.add_subplot(2,2,4)
plt.plot(img_alpha_DOWN_hist)
plt.xlim([0, 256])

plt.show()

 

Contrast가 축소된 결과 image와 histogram (좌: 입력 이미지, 우: alpha=0.6)

 

 

 

 

이번에는 alpha = 1.6 으로 대상 이미지의 contrast를 확대시켰고, contrast 변환 전후의 histogram를

비교해보면 그래프의 픽셀값 범위가 넓게 확장되었음을 알 수 있습니다.

 

 

alpha = 1.6   # contrast control
beta = 0      # brightness control
img_alpha_UP = cv2.convertScaleAbs(img_gray, alpha=alpha, beta=beta)

img_gray_hist = cv2.calcHist([img_gray], [0], None, [256], [0, 256])
img_alpha_UP_hist = cv2.calcHist([img_alpha_UP], [0], None, [256], [0, 256])

fig = plt.figure(figsize=(12,10))    
fig.add_subplot(2,2,1)
plt.axis("off")
plt.title("Original")
plt.imshow(img_gray,cmap='gray')

fig.add_subplot(2,2,2)
plt.axis("off")
plt.title("Contrast raised")
plt.imshow(img_alpha_UP,cmap='gray')

fig.add_subplot(2,2,3)
plt.plot(img_gray_hist)
plt.xlim([0, 256])

fig.add_subplot(2,2,4)
plt.plot(img_alpha_UP_hist)
plt.xlim([0, 256])

plt.show()

 

 

Contrast가 확대된 결과 image와 histogram (좌: 입력 이미지, 우: alpha=1.6)

 

 

 

 

Gamma correction

 

 

Gamma 보정은 대상 이미지의 brightness를 변환하기 위한 비선형적인 보정방식 입니다.

밑의 식(출처 2)이 그것이며 uint8 형식의 입력값 I 에 대해 gamma값을 조정하며 (같은 형식의)

출력값 O를 제어하는 방식 입니다.

 

 

 

 

Gamma 값에 따른 입력-출력값의 상관관계는 밑의 그래프(출처 2)에 묘사되어 있습니다.

그래프에 따르면, gamma < 1 인 경우 입력 이미지의 어두운 부분(= 낮은 픽셀값 영역)은

출력 이미지에서 밝은영역으로 변환될 것인반면, gamma > 1 에서는 반대로 입력의 밝은 부분

(= 높은 픽셀값 영역)이 어두운 영역으로 변환되어 출력될 것입니다.

 

 

 

 

 

다음은 gamma 보정의 결과를 보여줍니다. 입력 이미지는 uint8 타입으로써 최소 0, 최대 255의 픽셀값

범위를 가지며, 여기서 gamma = 0.4 (gamma < 1 인 경우) 으로 입력 이미지의 brightness를 밝게하고,

밑의 gamma값 변환 전후의 histogram를 비교해보면 그래프가 우측으로 이동 되었음을 알 수 있습니다.

 

코드에서 보면 uint8 형식의 입력값 i (0 부터 255)와 하이퍼 파라메터 - 사용자가 직접 지정하는 파라메터 -

인 gamma값에 따른 위의 수식 연산결과를 "lookUpTable"에 저장합니다. 이때 연산결과가 0보다 작으면

0으로, 255보다 크다면 255로 'clip' 하여 저장합니다.

연산후 입력 이미지 img_gray의 각 픽셀에 대응하는 값을 "lookUpTable"에서 찾아 새로운 출력 변수

"img_gamma_DOWN"을 생성 합니다. 이로써 효율적인 gamma 보정 연산을 마쳤습니다.

 

 

gamma = 0.4    # contrast control
lookUpTable = np.empty((1,256), np.uint8)
for i in range(256):
    lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
img_gamma_DOWN = cv2.LUT(img_gray, lookUpTable)

img_gray_hist = cv2.calcHist([img_gray], [0], None, [256], [0, 256])
img_gamma_DOWN_hist = cv2.calcHist([img_gamma_DOWN], [0], None, [256], [0, 256])

fig = plt.figure(figsize=(12,10))    
fig.add_subplot(2,2,1)
plt.axis("off")
plt.title("Original")
plt.imshow(img_gray,cmap='gray')

fig.add_subplot(2,2,2)
plt.axis("off")
plt.title("Gamma reduced")
plt.imshow(img_gamma_DOWN,cmap='gray')

fig.add_subplot(2,2,3)
plt.plot(img_gray_hist)
plt.xlim([0, 256])

fig.add_subplot(2,2,4)
plt.plot(img_gamma_DOWN_hist)
plt.xlim([0, 256])

plt.show()

 

 

Gamma < 1 경우의 결과 image와 histogram (좌: 입력 이미지, 우: gamma=0.4)

 

 

 

이번에는 gamma = 1.4 (gamma > 1 인 경우)으로 입력 이미지의 brightness를 어둡게

변환시켰고, gamma값 변환 전후의 histogram를 비교해보면 그래프가 왼쪽으로 이동 되었음을 알 수

있습니다.

 

 

gamma = 1.4    # contrast control
lookUpTable = np.empty((1,256), np.uint8)
for i in range(256):
    lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
img_gamma_UP = cv2.LUT(img_gray, lookUpTable)

img_gray_hist = cv2.calcHist([img_gray], [0], None, [256], [0, 256])
img_gamma_UP_hist = cv2.calcHist([img_gamma_UP], [0], None, [256], [0, 256])

fig = plt.figure(figsize=(12,10))    
fig.add_subplot(2,2,1)
plt.axis("off")
plt.title("Original")
plt.imshow(img_gray,cmap='gray')

fig.add_subplot(2,2,2)
plt.axis("off")
plt.title("Gamma raised")
plt.imshow(img_gamma_UP,cmap='gray')

fig.add_subplot(2,2,3)
plt.plot(img_gray_hist)
plt.xlim([0, 256])

fig.add_subplot(2,2,4)
plt.plot(img_gamma_UP_hist)
plt.xlim([0, 256])

plt.show()

 

 

Gamma > 1 경우의 결과 image와 histogram (좌: 입력 이미지, 우: gamma=1.4)

 

 

 

 

 

 

 

출처1: https://unsplash.com/photos/jdfn7Z03Qa4

 

사진 작가: National Cancer Institute, Unsplash

Cells from cervical cancer – Splash에서 National Cancer Institute의 이 사진 다운로드

unsplash.com

 

 

 

 

출처2: https://docs.opencv.org/3.4/d3/dc1/tutorial_basic_linear_transform.html

 

OpenCV: Changing the contrast and brightness of an image!

Prev Tutorial: Adding (blending) two images using OpenCV Next Tutorial: Discrete Fourier Transform Goal In this tutorial you will learn how to: Access pixel values Initialize a matrix with zeros Learn what cv::saturate_cast does and why it is useful Get so

docs.opencv.org