Search
Duplicate

OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝/ 영상의 밝기와 명암비 조절

영상의 밝기 조절

그레이스케일 영상 다루기

과거에 개발된 영상 처리 알고리즘이 주로 그레이스케일 영상을 대상으로 개발되었기 때문에, 특별히 컬러 정보를 이용해야 하는 경우가 아니면 컬러 영상도 그레이스케일 영상으로 변환해서 사용하는 경우가 많다.
컬러 영상은 3개 채널을 갖고 있기 때문에 그레이스케일 영상보다 3배 더 수행 시간이 길다.

영상의 밝기 조절

영상의 밝기(brightness) 조절이란 영상의 전체적인 밝기를 조절하여 좀 더 밝거나 어두운 영상을 만드는 작업으로, 영상의 밝기를 조절하려면 입력 영상의 모든 픽셀에 일정 값을 더하거나 빼는 작업을 수행한다.
영상의 밝기 조절 수식은 다음과 같다.
dst(x,y)=src(x,y)+ndst(x, y) = src(x, y) + n
src는 입력 영상, dst는 출력 영상이다. n이 +면 밝기는 증가하고, -면 밝기는 감소한다.
픽셀 값은 0-255 사이의 값을 갖기 때문에 255보다 큰 값이 나오면 255로 맞춰지고, 0보다 작은 값이 나오면 0으로 맞춰야한다. 이런 연산을 OpenCV에서는 포화(saturate) 연산이라고 한다.
saturate(x)={0x<0255x>255xotherwisesaturate(x) = \begin{cases} 0 & x < 0 \\ 255 & x > 255 \\ x & otherwise \end{cases}
dst(x,y)=saturate(src(x,y)+n)dst(x, y) = saturate(src(x, y) + n)

영상의 명암비 조절

기본적인 명암비 조절 방법

명암비란 영상에서 밝은 영역과 어두운 영역 사이에 드러나는 밝기 차이의 강도를 의미하고, 명암 대비 또는 큰트라스트(contrast)라고 한다.
영상이 전반적으로 어둡거나 전반적으로 밝은 픽셀로만 구성된 경우 명암비가 낮다고 하고 밝은 영역과 어두운 영역이 골고루 섞인 경우 명암비가 높다고 한다.
밝기 조절이 영상의 모든 픽셀에 대해 덧셈 연산을 하는 것에 비해 명암비 조절은 곱셈 연산을 사용하여 구현한다.
dst(x,y)=saturate(ssrc(x,y))dst(x, y) = saturate(s \cdot src(x, y))
src는 입력 영상, dst는 출력 영상이다. s는 0보다 큰 양의 실수로 s가 1보다 작은 경우 명암비가 낮아지고 1보다 큰 경우 명암비가 높아지는 효과가 있다.

효과적인 명암비 조절 방법

명암비를 효과적으로 조절하려면 밝은 픽셀은 더욱 밝게, 어두운 픽셀은 더욱 어둡게 변경해야 한다.
이때 픽셀 값이 밝고 어둡다는 기준은 그레이스케일 범위의 중간값인 128을 기준으로 하거나, 입력 영상의 평균 밝기를 구하여 기준으로 삼을 수도 있다.
그레이스케일 범위의 중간값인 128을 기준으로 명암비를 조절하는 방식은 다음과 같다.
dst(x,y)=src(x,y)+(src(x,y)128)αdst(x, y) = src(x, y) + (src(x, y) - 128) \cdot \alpha
α\alpha는 -1보다 크거나 같은 실수

히스토그램 분석

히스토그램 구하기

영상의 히스토그램(histogram)이란 영사으이 픽셀값 분포를 그래프 형태로 표현한 것을 말한다.
그레이스케일 영상의 경우, 각 그레이스케일 값에 해당하는 픽셀의 개수를 구하고 이를 막대 그래프로 표현함으로써 히스토그램을 구할 수 있다.
컬러 영상일 경우 세 개의 색상 성분 조합에 따른 픽셀 개수를 계산하여 히스토그램을 구할 수 있다.
아래와 같이 영상의 픽셀 정보가 주어졌다면 히스토그램은 그 오른쪽의 그래프와 같다.
히스토그램 그래프에서 가로축을 히스토그램의 빈(bin)이라고 한다.
고로 위 그림의 히스토그램에서 빈의 개수는 8개가 된다.
히스토그램의 빈 개수가 항상 픽셀 값 범위와 같아야 하는 것은 아닌데, 경우에 따라 히스토그램의 빈 개수를 픽셀 값 범위보다 작게 설정할 수도 있다.
예컨대 위 이미지의 8개의 밝기 값을 가질 수 있는 영상에서 히스토그램의 빈의 개수를 아래와 같이 4로 설정할 수도 있다.
OpenCV에서 영상의 히스토그램을 구할 때는 calcHist() 함수를 이용한다.
calcHist() 함수는 한 장의 영상 뿐만 아니라 여러 장의 영상으로부터 히스토그램을 구할 수도 있고, 여러 채널로부터 히스토그램을 구할 수도 있고, 빈 개수를 조절할 수도 있다.
(calcHist() 함수에 대한 설명과 예제 코드 생략)

히스토그램 스트레칭

히스토그램 스트레칭(histogram stretching)은 영상의 히스토그램이 그레이스케일 전 구간에 걸쳐서 나타나도록 변경하는 선형 변환 기법이다.
보통 명암비가 낮은 영상은 시스토그램이 특정 구간에 집중되어 나타나게 되는데, 이러한 히스토그램을 고무줄 잡아 늘이듯이 펼쳐서 히스토그램 그래프가 그레이스케일 전 구간에서 나타나도록 변환하는 기법이다.
히스토그램 스트레칭을 수행한 영상은 명암비가 높아지기 때문에 대체로 보기 좋은 사진이 된다.
dst(x,y)=src(x,y)GminGmaxGmin×255dst(x, y) = { src(x, y) - G_{min} \over G_{max} - G_{min}} \times 255
위 식에서 src와 dst는 각각 입력, 출력 영상을 의미하고, Gmax,GminG_{max}, G_{min} 는 입력 영상의 픽셀 값 중에서 가장 큰 그레이스케일 값과 가장 작은 그레이스케일 값을 의미한다.
아래 이미지는 왼쪽의 이미지를 히스토그램 스트레칭 하여 오른쪽의 결과를 얻은 것이다.
히스토그램 스트레칭을 위한 함수는 OpenCV에서 제공하지 않는다.
그러나 minMaxLoc() 함수 등을 이용하여 쉽게 구현할 수 있다.
void histogram_stretching() { Mat src = imread("hawkes.bmp", IMREAD_GRAYSCALE); if (src.empty()) { cerr << "Image load failed!" << endl; return; } double gmin, gmax; minMaxLoc(src, &gmin, &gmax); Mat dst = (src - gmin) * 255 / (gmax - gmin); imshow("src", src); imshow("srcHist", getGrayHistImage(calcGrayHist(src))); imshow("dst", dst); imshow("dstHist", getGrayHistImage(calcGrayHist(dst))); waitKey(); destroyAllWindows(); }
C++

히스토그램 평활화

히스토그램 평활화(histogram equalization)은 히스토그램 스트레칭과 더불어 영상의 픽셀 값 분포가 그레이스케일 전체 영역에서 골고루 나타나도록 변경하는 알고리즘의 하나이다.
히스토그램 평활화는 히스토그램 그래프에서 특정 그레이스케일 값 근방에서 픽셀 분포가 너무 많이 뭉쳐 있는 경우 이를 넓게 펼쳐 주는 방식으로 픽셀 값 분포를 조절한다.
히스토그램 평활화는 히스토그램 균등화 또는 히스토그램 평탄화라고도 한다.
히스토그램 평활화를 구현하기 위해서는 먼저 히스토그램을 구해야 한다.
그레이스케일 영상의 히스토그램을 그레이스케일 값 g에 대한 함수 h(g)로 표현하면, h(g)는 영상에서 그레이스케일 값이 g인 픽셀 개수를 나타낸다.
히스토그램 평활화를 계산하기 위해서는 h(g)로부터 히스토그램 누적 함수 H(g)를 구해야 한다. 히스토그램 누적 함수 H(g)는 다음 수식으로 정의된다.
H(g)=0igh(i)H(g) = \sum_{0 \leq i \leq g} h(i)
히스토그램 평활화는 이 히스토그램 누적 함수 H(g)를 픽셀 값 변환 함수로 사용한다. 다만 H(g) 값의 범위가 보통 그레이스케일 값의 범위(0~255) 보다 훨씬 크기 때문에 H(g) 함수의 최댓값이 255가 되도록 정규화 과정을 거쳐야 한다.
만약 입력 영상의 픽셀 개수를 N이라고 표기하면 히스토그램 평활화는 다음과 같이 정의 된다.
dst(x,y)=round(H(src(x,y))×LmaxN)dst(x, y) = round(H(src(x,y)) \times {L_{max} \over N})
위 식에서 LmaxL_{max}는 영상이 가질 수 있는 최대 밝기 값을 의미하며 일반적인 그레이스케일 영상의 경우 Lmax=255L_{max} = 255 이다.
round()는 반올림 함수
아래 이미지의 (a)와 같이 4 x 4 크기의 영상이 0~7 사이의 밝기를 가졌다고 가정하고, 이 영상으로부터 구한 히스토그램 h(g)와 히스토그램 누적 함수 H(g)를 표로 나타내면 아래 이미지의 (b)가 된다.
영상의 픽셀 개수가 16개이므로 H(7)에 해당하는 값이 16인 것을 확인할 수 있다.
테스트로 사용한 영상은 최대 밝기 값이 7이고 전체 픽셀 개수가 16이므로 정규화를 위한 상수는 7/16을 사용해야 한다. 즉, H(g)에 7/16을 곱한 값을 결과 영상의 픽셀 값으로 설정한다.
다만 H(g)에 7/16을 곱한 결과가 실수로 계산되기 때문에 이를 반올림하여 결과 영상의 픽셀 값으로 설정한다.
이러한 연산 과정을 나타낸 것이 아래 이미지의 (c)이다.
최종 평활화 과정을 거친 결과는 (d)를 통해 확인할 수 있다.
OpenCV는 그레이스케일 영상의 히스토그램 평활화를 수행하는 equalizeHist() 함수를 제공한다.
equalizeHist() 함수는 CV_8UC1 타입을 사용하는 그레이스케일 영상만 입력으로 받는다.
void histogram_equalization() { Mat src = imread("hawkes.bmp", IMREAD_GRAYSCALE); if (src.empty()) { cerr << "Image load failed!" << endl; return; } Mat dst; equalizeHist(src, dst); imshow("src", src); imshow("srcHist", getGrayHistImage(calcGrayHist(src))); imshow("dst", dst); imshow("dstHist", getGrayHistImage(calcGrayHist(dst))); waitKey(); destroyAllWindows(); }
C++