[OpenCV] 3. 히스토그램 스트레칭(Histogram Stretching)

저번 글에서는 히스토그램의 개념에 대해 간단히 알아보았습니다.
이번 글에서는 히스토그램 개념에서 더 나아가 히스토그램 스트레칭(Histogram Stretching)에 대해 알아보겠습니다.

우선 영상의 명암의 변화가 히스토그램 모양에 어떤 변화가 생기는지 알아보겠습니다.
그레이스케일의 레나 이미지가 전반적으로 어두워질 경우 히스토그램의 위치가 좌측으로 많이 치우쳐집니다.

어두운 영상

어두운 영상

만약 영상이 전반적으로 밝아질 경우 히스토그램의 위치가 우측으로 많이 치우쳐 집니다.

밝은 영상

밝은 영상

이처럼 그레이스케일 영상에서 픽셀값이 전반적으로 밝아지거나 어두워질 경우 히스토그램의 위치가 한쪽으로 치우쳐지게 됩니다.
명암비가 높아지는 경우엔 어떻게 될까요? 명암비가 높은 영상에서는 어두운 값의 픽셀과, 밝은 값의 픽셀이 골고루 존재하게 됩니다.

히스토그램 스트레칭

위와 같이 그래프가 좌우로 늘려진 것처럼 명암비가 높아지는 것을 히스토그램 스트레칭이라고 합니다.
히스토그램 스트레칭은 그래프의 모양을 유지하면서 픽셀의 밝기값이 골고루 존재하므로 적절한 명암비를 가지게 됩니다.
수식을 코드로 표현하면 아래와 같습니다.

// dbMin: 영상에 존재하는 픽셀값 중 최소값
// dbMax: 영상에 존재하는 픽셀값 중 최대값
dst = ((src - dbMin) / (dbMax - dbMin)) * 255;

히스토그램 스트레칭을 적용하는 전체 코드는 아래와 같습니다.

// 히스토그램 구하기
Mat src = imread("lenna.bmp", CV_8UC1);
if (src.empty())
{
	cerr << "image open error" << endl;
	return -1;
}

int iImages = 1;
int iChannels[] ={0};
Mat histogram;
int iDims = 1;
const int iHistogramSize[] ={256};
float graylevel[] ={0, 256};
const float *pRanges[] ={graylevel};

double dbMin, dbMax;
minMaxLoc(src, &dbMin, &dbMax);
Mat dst;

// dbMin: 영상에 존재하는 픽셀값 중 최소값
// dbMax: 영상에 존재하는 픽셀값 중 최대값
dst = ((src - dbMin) / (dbMax - dbMin)) * 255;

calcHist(&dst, iImages, iChannels, noArray(), histogram, iDims, iHistogramSize, pRanges);

// histogram 그리기
double dbHistMax; // 히스토그램에서 제일 큰 값
minMaxLoc(histogram, 0, &dbHistMax);

Mat histogram_img(100, 256, CV_8UC1, Scalar(255, 255, 255));
for (int iRow = 0 ; iRow < 256 ; iRow++)
{
	float val_hist = histogram.at<float>(iRow, 0);
	int iPos = cvRound(val_hist * 100 / dbHistMax);
	line(histogram_img, Point(iRow, 100), Point(iRow, 100 - iPos), Scalar(0, 0, 0));
}

imshow("src", src);
imshow("dst", dst);
imshow("histogram", histogram_img);

waitKey();
destroyAllWindows();

실행 결과는 아래와 같습니다.

스트레칭 적용 전 이미지와 히스토그램

스트레칭 적용 전 이미지와 히스토그램

스트레칭 적용 후 이미지와 히스토그램

스트레칭 적용 후 이미지와 히스토그램