컴퓨터 비전 - OpenCV(4주차)

YoungJin Cho·2021년 3월 28일
1

영상의 밝기와 명암비 조절

영상의 밝기 조절

  • 영상의 밝기(brightness) 조절이란 영상의 전체적인 밝기를 조절하여 좀 더 밝거나 어두운 영상을 만드는 작업이다.
  • 영상의 밝기를 조절하려면 입력 영상의 모든 픽셀에 일정 값을 더하거나 빼는 작업을 수행해야한다.
  • 입력 영상의 모든 픽셀에 양수 값을 더하면 영상이 밝아지고, 반대로 양수 값을 빼면 영상이 어두워진다.

영상의 밝기 조절

  • 영상의 밝기 조절을 수식으로 표현하면 다음과 같다.

  • 이 수식에서 src는 입력 영상, dst는 출력 영상, n은 조절할 밝기 값을 나타낸다.

  • n이 양수이면 출력 영상 dst의 전체적인 밝기가 증가하고, n이 음수이면 밝기가 감소하여 어두워진다.

  • 이 그래프에서 가로축은 입력 영상 src의 그레이스케일 값을 나타낸다.

  • 세로축은 출력 영상 dst의 그레이스케일 이다.

  • 그래프 (a)는 n이 음수인 전체적으로 밝기가 어두워진 결과 영상이 생성된다.

  • 그래프 (b)는 n이 양수인 경우이며 결과 영상의 밝기가 밝아지는 그래프이다.

  • 행렬의 원소값을 설정할 때, 원소 자료형이 가질 수 있는 값의 범위를 벗어나는 경우 해당 자료형의 최솟값 또는 최댓값으로 원소 값을 설정하는 연산을 OpenCV에서 포화연산이라고 부른다.

  • uchar 자료형을 사용하는 그레이스케일 영상에 대해 포화 연산을 수식으로 나타내면 다음과 같다.

  • 실제로 영상의 밝기 조절을 구현할 때에는 다음과 같이 포화 연산을 함께 고려한 수식을 사용해야 한다.

  • 포화연산을 고려하여 밝기를 높이면 다음과 같이 전체적으로 밝아지지만,

  • 포화연산을 고려하지 않으면 다음과 같이 이미지가 깨져서 나온다.

  • 다음과 같은 코드로 밝기를 조절해야한다.

영상의 밝기를 보기좋게 바꾸려면 명암비를 조절해야한다.

기본적인 명암비 조절 방법

  • 명암비란 영상에서 밝은 영역과 어두운 영역 사이에 드러나는 밝기 차이의 강도를 의미한다.
  • 영상이 전반적으로 어둡거나 또는 전반적으로 밝은 픽셀로만 구성된 경우, 명암비가 낮다고 표현한다.
  • 일반적으로 명암비가 낮은 영상은 객체 간의 구분이 잘 되지 않아서 전반적으로 흐릿하게 느껴진다.

    (b)는 (a)에 비해 뚜렷하고 선명하게 보인다.
  • dst 영상을 보면 전체적으로 픽셀 값이 포화되어 흰색으로 나타나는 영역이 너무 많고, 이로 인해 사물의 윤곽 구분이 더 어려워진다.
  • 픽셀 값에 일정 상수를 단순히 곱하여 명암비를 조절하는 방식은 실전에서는 잘 사용되지 않는다.
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) 

효과적인 명암비 조절 방법

  • 명암을 효과적으로 높이기 위해서는 밝은 픽셀은 더욱 밝게, 어두운 픽셀은 더욱 어두워지게 변경해야 한다.
  • 픽셀 값이 밝고 어둡다는 기준을 어떻게 설정할 것인지가 명암비 조절 결과 영상의 품질차이를 가져올 수 있다ㅏ.
  • 그레이스케일 범위 중간값인 128을 기준으로 픽셀 값이 128보다 크면 더욱 밝게 만들고, 작으면 더욱 어둡게 만든다.
  • 명암비를 감소시키려면 위와 반대로 수행한다.
  • 수식
  • 위 수식에 의해 계산되는 결과는 영상의 픽셀 값이 0보다 작거나 255보다 커지는 경우가 발생할 수 있으므로 포화 연산도 함께 수행해야 한다.
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)    

다음과 같이 명암비가 달라지는 모습을 볼 수 있다.

히스토그램 분석

  • 영상의 히스토그램이란 영상의 픽셀 값 분포를 그래프 형태로 표현한 것이다.
  • 그레이스케일 영상의 경우, 각 그레이스케일 값에 해당하는 픽셀의 개수를 구하고 이를 막대 그래프 형태로 표현함으로써 히스토그램을 구할 수 있다.
  • 컬러 영상에 대해서도 세 개의(RGB) 성분 조합에 다른 픽셀 개수를 계산하여 히스토그램을 구할 수 있다.
  • 히스토그램 그래프에서 가로축을 히스토그램의 빈(bin)이라고 한다.
  • 경우에 따라서는 히스토그램의 빈 개수를 픽셀 값 범위보다 작게 설정할 수 있다.
  • 다음은 밝은 배경의 사진과 어두운 배경의 사진을 찍어 히스토그램 분석을 해 본 결과이다.
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 사이에 몰려 있는 것을 알 수 있다.

히스토그램 스트레칭

  • 히스토그램 스트레칭은 영상의 히스토그램이 그레이스케일 전 구간에 걸쳐서 나타나도록 변경하는 선형 변환 기법이다.
  • 보통 명암비가 낮은 영상의 경우 히스토그램이 특정 구간에 집중되어 나타난다.
  • 히스토그램 스트레칭을 수행한 영상은 명암비가 높아지기 때문에 대체로 보기 좋은 사진으로 바뀌게 된다.
  • 히스토그램 스트레칭을 수식으로 표현하면 다음과 같다.
  • Gmin값을 0이되게, Gmax값을 255가 되도록 변환하면 히스토그램이 그레이스케일 전체 구간에 대해 나타나게 된다.

히스토그램 평활화(histogram equalization)

  • 히스토그램 평활화는 히스토그램 스트레칭과 더불어 영상의 픽셀값 분포가 그레이스케일 전체 영역에서 골고루 나타나도록 변경하는 알고리즘 중 하나이다.
  • 히스토그램 그래프에서 특정 그레이스케일 값 근방에서 픽셀 분포가 너무 많이 뭉쳐있는 경우 이를 넓게 펼쳐 주는 방식으로 픽셀 값 분포를 조절한다.
  • 히스토그램 스트레칭은 전체 적인 분포를 넓히는 반면 히스토그램 평화로하는 특정 그레이스케일 값 근방을 넓게 펼쳐 주는 방식이다.
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')


결과 히스토그램 그래프를 보면 원본 사진의 픽셀들이 균등하게 퍼져있는 것을 볼 수 있다.

profile
자율주행 개발자가 되고싶은 대학생입니다.

0개의 댓글