앞선글 [Python 영상처리 기초 7] 이미지의 화질개선 Contrast-Brightness-Gamma - part1
에서는 주로 이미지의 brightness 개선에 주목 했다면 본 글에서는 contrast 개선을 다루겠습니다.
앞서 언급했듯이 brightness와 contrast 는 긴밀한 관계이지만 선형적인 상관관계를 갖지 않습니다.
따라서 contrast가 조정됨과 동시에 brightness가 달라질 수 있고, 반대의 경우도 가능합니다.
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()
앞선글 [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()
이번에는 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()
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.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()
출처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
'Python 영상처리 기초 with OpenCV' 카테고리의 다른 글
[Python 영상처리 기초 8] Affine Transformations (0) | 2023.09.25 |
---|---|
[Python 영상처리 기초 7] 이미지의 화질개선 Contrast-Brightness-Gamma - part1 (2) | 2023.09.16 |
[Python 영상처리 기초 6] 선형 공간 필터링 - Average, Laplacian, Sobel filters (0) | 2023.08.08 |
[Python 영상처리 기초 5] 이미지의 화질개선 - Normalization, Standardization (1) | 2023.08.02 |
[Python 영상처리 기초 4] 이미지의 픽셀값 다루기 - Crop, 선형연산 (2) | 2023.07.31 |