영상의 밝기 조절을 수식으로 표현하면 다음과 같다.
이 수식에서 src는 입력 영상, dst는 출력 영상, n은 조절할 밝기 값을 나타낸다.
n이 양수이면 출력 영상 dst의 전체적인 밝기가 증가하고, n이 음수이면 밝기가 감소하여 어두워진다.
이 그래프에서 가로축은 입력 영상 src의 그레이스케일 값을 나타낸다.
세로축은 출력 영상 dst의 그레이스케일 이다.
그래프 (a)는 n이 음수인 전체적으로 밝기가 어두워진 결과 영상이 생성된다.
그래프 (b)는 n이 양수인 경우이며 결과 영상의 밝기가 밝아지는 그래프이다.
행렬의 원소값을 설정할 때, 원소 자료형이 가질 수 있는 값의 범위를 벗어나는 경우 해당 자료형의 최솟값 또는 최댓값으로 원소 값을 설정하는 연산을 OpenCV에서 포화연산이라고 부른다.
uchar 자료형을 사용하는 그레이스케일 영상에 대해 포화 연산을 수식으로 나타내면 다음과 같다.
실제로 영상의 밝기 조절을 구현할 때에는 다음과 같이 포화 연산을 함께 고려한 수식을 사용해야 한다.
포화연산을 고려하여 밝기를 높이면 다음과 같이 전체적으로 밝아지지만,
포화연산을 고려하지 않으면 다음과 같이 이미지가 깨져서 나온다.
다음과 같은 코드로 밝기를 조절해야한다.
영상의 밝기를 보기좋게 바꾸려면 명암비를 조절해야한다.
def saturated(value):
if value > 255:
value = 255
elif value < 0:
value = 0
return value
dst = np.empty(src.shape, dtype=src.dtype)
for y in range(src.shape[0]):
for x in range(src.shape[1]):
dst[y, x] = src[y, x] -50
#dst[y, x] = saturated(src[y, x] - 50 )
plt.subplot(1,2,1)
imshow("", src)
plt.subplot(1,2,2)
imshow("", dst)
src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)
# dst(x,y) = src(x,y) + (src(x,y) - 128)*alpha
alpha = -0.1
dst = np.clip(src + (src - 128.)*alpha, 0, 255).astype(np.uint8)
plt.subplot(1,2,1)
imshow("", src)
plt.subplot(1,2,2)
imshow("", dst)
src = cv2.imread("gamma.jpg")
imshow("", src)
gamma = 0.5
lut = np.zeros((256,1),dtype = 'uint8')
for i in range(256): lut[i][0] = 255 * (float(i)/255) ** (gamma)
dst = cv2.LUT(src, lut)
gamma = 1.5
lut = np.zeros((256,1),dtype = 'uint8')
for i in range(256): lut[i][0] = 255 * (float(i)/255) ** (gamma)
dst2 = cv2.LUT(src, lut)
plt.figure(figsize=(12, 3))
plt.subplot(1,3,1)
imshow("src", src)
plt.subplot(1,3,2)
imshow("0.5", dst)
plt.subplot(1,3,3)
imshow("1.5", dst2)
다음과 같이 명암비가 달라지는 모습을 볼 수 있다.
import cv2
import numpy as np
import matplotlib.pylab as plt
def imshow(title, image) :
plt.title(title)
if len(image.shape) == 3 :
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
else :
plt.imshow(image, cmap="gray")
def CtoG(image):
image[:,:,1] = image[:,:,0]
image[:,:,2] = image[:,:,0]
bright = cv2.imread('bright.jpg')
dark = cv2.imread('dark.jpg')
cv2.cvtColor(bright, cv2.COLOR_BGR2RGB)
cv2.cvtColor(dark, cv2.COLOR_BGR2RGB)
print(bright.shape, dark.shape)
(3024, 3024, 3) (3024, 3024, 3)
bright = cv2.imread('bright.jpg', cv2.IMREAD_GRAYSCALE)
dark = cv2.imread('dark.jpg', cv2.IMREAD_GRAYSCALE)
plt.subplot(1,2,1)
imshow('bright', bright)
plt.subplot(1,2,2)
imshow('dark', dark)
histSize = [256]
histRange = [0, 256]
bright = cv2.calcHist([bright], [0], None, histSize, histRange)
dark = cv2.calcHist([dark], [0], None, histSize, histRange)
plt.plot(bright)
plt.plot(dark)
위 그래프를 보면, 밝은 사진(하늘색 그래프)은 250 쪽에 픽셀이 가장 많은 것을 볼 수 있고, 어두운 사진(노란색 그래프)은 픽셀이 0 ~ 50 사이에 몰려 있는 것을 알 수 있다.
src_bright = cv2.imread('bright.jpg', cv2.IMREAD_GRAYSCALE)
dst_bright = cv2.equalizeHist(src_bright)
histSrc_bright = cv2.calcHist([src_bright], [0], None, histSize, histRange)
histDst_bright = cv2.calcHist([dst_bright], [0], None, histSize, histRange)
src_dark = cv2.imread('dark.jpg', cv2.IMREAD_GRAYSCALE)
dst_dark = cv2.equalizeHist(src_dark)
histSrc_dark = cv2.calcHist([src_dark], [0], None, histSize, histRange)
histDst_dark = cv2.calcHist([dst_dark], [0], None, histSize, histRange)
plt.subplot(2,2,1)
imshow("", src_bright)
plt.subplot(2,2,2)
imshow("", dst_bright)
plt.subplot(2,2,3)
imshow("", src_dark)
plt.subplot(2,2,4)
imshow("", dst_dark)
이미지를 보면 밝은 사진은 차이가 확실하진 않지만 밝은사진, 어두운사진 둘 다 명암비가 높아진 것을 알 수 있다.
plt.plot(histSrc_bright, 'k')
plt.plot(histDst_bright, 'r')
plt.plot(histSrc_dark, 'k')
plt.plot(histDst_dark, 'r')
결과 히스토그램 그래프를 보면 원본 사진의 픽셀들이 균등하게 퍼져있는 것을 볼 수 있다.