[OpenCV 4] 5. 영상의 밝기와 명암비 조절
과거에 개발되었던 많은 영상 처리 알고리즘이 주로 그레이스케일 영상을 대상으로 개발되었기 때문에 대부분의 영상 처리 알고리즘은 컬러 영상이 아니라 그레이스케일 영상을 대상으로 한다.
간단히 그레이스케일 영상을 불러오는 방법은 다음과 같다.
Mat img1 = imread(“lenna.bmp”, IMREAD_GRAYSCALE);
만약 새로운 그레이스케일 영상을 다룰 때는 생성을 해줘야 하는데 다음과 같다.
Mat img2(480, 640, CV_8UC1, Scalar(0));
CV_8UC1 은 unsigned char 8bit 해서 0~255까지를 의미한다.
컬러 -> 흑백은 다음과 같은 코드를 사용한다.
Mat img3 = imread(“lenna.bmp”, IMREAD_COLOR);
Mat img4;
cvtColor(img3, img4, COLOR_BGR2GRAY);
cvtColor 함수를 사용한다.
영상의 밝기 조절
영상의 밝기를 조절하려면 입력 영상의 모든 픽셀에 일정 값을 더하거나 빼는 작업을 수행한다. 입력 영상의 모든 픽셀에 양수 값을 더하면 영상이 밝아지고, 반대로 양수 값을 빼면 영상이 어두워진다.
영상의 밝기 조절을 수식으로 표현한 그림이다.
src는 입력 영상, dst는 출력 영상, n은 조절할 밝기 값을 나타낸다.
n의 값에 따라 밝기 변화를 알 수 있다.
원소 자료형이 가질 수 있는 값의 범위를 벗어나는 경우 해당 자료형의 최솟값 또는 최댓값으로 원소 값을 설정하는 연산을 OpenCV에서는 포화(saturate) 연산이라고 부른다.
uchar 자료형을 사용하는 그레이스케일 영상에 대해 포화 연산을 수식으로 나타내면 다음과 같다.
코드를 이용해 영상의 밝기를 조절할때 주의해야 할 내용이 영상의 픽셀값에 접근 하는 것이다.
dst.at<uchar>(j, i) = src.at<uchar>(j, i) + 100;
이런식으로 <데이터형>을 해주고 Mat 행렬이므로 (j,i)를 해준다.
위 코드는 이중포문을 통해 행렬의 원소(행,열 = 픽셀값)에 접근하는 방식이다. 몰랐던 부분이라 알고 있자.
영상의 명암비 조절
영상의 밝기 조절이 기본적으로 덧셈 연산을 사용하는 방식이라면, 영상의 명암비 조절은 전체 픽셀에 적절한 실수를 곱하는 곱셈 연산을 사용한다. 다만 명암비 조절을 위한 곱셈 수식을 어떻게 적용하느냐에 따라 결과 영상의 품질에서 차이가 발생할 수 있다.
이 수식에서 src는 입력 영상, dst는 출력 영상, 그리고 상수 n는 0보다 큰 양의 실수다. 입력 영상 픽셀 값에 상수 s를 곱한 결과가 255보다 커지는 경우가 발생할 수 있으므로 포화 연산도 함께 사용해야 한다. 상수 s가 1보다 작은 경우에는 명암비가 낮아지는 효과가 있고, s가 1보다 큰 경우에는 명암비가 높아지는 효과가 있다.
s 값에 따라 명암비가 변하는 것을 확인 할 수 있다.
나머지는 위와 같아서 생략하겠다.
히스토그램
영상의 히스토그램(histogram)이란 영상의 픽셀 값 분포를 그래프 형태로 표현한 것을 의미한다.
OpenCV에서 영상의 히스토그램을 구하려면 calcHist() 함수를 사용한다. calcHist() 함수는 한 장의 영상뿐만 아니라 여러 장의 영상으로부터 히스토그램을 구할 수 있고, 여러 채널로부터 히스토그램을 구할 수도 있다. 또한 히스토그램 빈 개수도 조절할 수 있다.
void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate = false); |
|
• images |
입력 영상의 배열 또는 입력 영상의 주소. 영상의 배열인 경우, 모든 영상의 크기와 깊이는 같아야 합니다. |
• nimages |
입력 영상 개수 |
• channels |
히스토그램을 구할 채널을 나타내는 정수형 배열 |
• mask |
마스크 영상. 입력 영상과 크기가 같은 8비트 배열이어야 합니다. 마스크 행렬의 원소 값이 0이 아닌 좌표의 픽셀만 히스토그램 계산에 사용됩니다. mask 인자에 Mat() 또는 noArray()를 지정하면 입력 영상 전체에 대해 히스토그램을 구합니다. |
• hist |
출력 히스토그램. CV_32F 깊이를 사용하는 dims-차원의 행렬입니다. |
• dims |
출력 히스토그램의 차원 수 |
• histSize |
각 차원의 히스토그램 배열 크기를 나타내는 배열(즉, 각 차원의 히스토그램 빈 개수를 나타내는 배열) |
• ranges |
각 차원의 히스토그램 범위. 등간격 히스토그램이면(uniform = true), ranges[i]는 각 차원의 최솟값과 최댓값으로 구성된 배열이고 [최솟값, 최댓값)2 범위를 나타냅니다. 비등간격 히스토그램이면(uniform = false), ranges[i]는 각각의 구역을 나타내는 histSize[i]+1개의 원소로 구성된 배열입니다. |
• uniform |
히스토그램 빈의 간격이 균등한지를 나타내는 플래그 |
• accumulate |
누적 플래그. 이 값이 true이면 hist 배열을 초기화하지 않고 누적하여 히스토그램을 계산합니다. |
히스토그램 스트레칭(histogram stretching)은 영상의 히스토그램이 그레이스케일 전 구간에 걸쳐서 나타나도록 변경하는 선형 변환 기법이다. 보통 명암비가 낮은 영상은 히스토그램이 특정 구간에 집중되어 나타나게 되는데, 이러한 히스토그램을 마치 고무줄을 잡아 늘이듯이 펼쳐서 히스토그램 그래프가 그레이스케일 전 구간에서 나타나도록 변환하는 기법이다.
수식으로 표현하면 다음과 같다.
Gmin과 Gmax는 입력 영상의 픽셀 값 중에서 가장 큰 그레이스케일 값과 가장 작은 그레이스케일 값을 나타낸다.
void histgoram_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();
}
원본 영상 src의 히스토그램을 스트레칭해서 dst 영상을 얻었다.
Mat dst = ( src - gmin ) * 255 / ( gmax - gmin );
직선의 방정식을 위 코드로 표현한다.
minMaxLoc 함수로 영상의 그레이스케일 최대값과 최소값을 구한다.
히스토그램 평활화(histogram equalization)는 히스토그램 스트레칭과 더불어 영상의 픽셀 값 분포가 그레이스케일 전체 영역에서 골고루 나타나도록 변경하는 알고리즘이다. 히스토그램 평활화는 히스토그램 그래프에서 특정 그레이스케일 값 근방에서 픽셀 분포가 너무 많이 뭉쳐 있는 경우 이를 넓게 펼쳐 주는 방식으로 픽셀 값 분포를 조절한다.
수식으로 표현하면 다음과 같다. Lmax는 영상이 가질 수 있는 최대 밝기 값을 의미하며 일반적인 그레이스케일 영상의 경우 Lmax=255이다. round()는 반올림 함수를 나타낸다.
4*4 영상을 예를 들었다. 최대 픽셀이 7이고 개수가 16이니까 H(g) * 7 / 16 을 해주고 반올림하여 결과 영상의 픽셀 값으로 설정한다.
OpenCV에서는 그레이스케일 영상의 히스토그램 평활화를 수행하는 equalizeHist() 함수를 제공한다.
void equalizeHist( InputArray src, OutputArray dst ); |
|
• src |
입력 영상. 8비트 1채널 |
• dst |
출력 영상. src와 크기와 타입이 같습니다. |
위 함수는 CV_8UC1 타입을 사용하는 그레이스케일 영상만 입력으로 받는다.
참고자료 : OpenCV 4로 배우는 컴퓨터 비전과 머신러닝