Programming/OpenCV

OpenCV 히스토그램 그리기 in C++

꾸준희 2016. 1. 5. 12:56
728x90
반응형

영상처리에서 히스토그램은 필수적이다.


히스토그램은 영상의 중요한 정보를 담은 자료이며, 영상의 명암값 프로필을 보여주기 위해 사용되는 도구이다. 히스토그램은 영상의 명암도 분포 상태를 나타내는 좋은 지표이다.


히스토그램은 0부터 255까지 명암값을 가로축 인덱스로 하고, 영상을 구성하고 있는 각 화소의 명암값에 해당하는 개수를 빈도수로 간주하여 세로축에 표시하는 것이다.


다음은 C++ 로 구현된 히스토그램의 예제 코드이다.






 
#include<iostream>
#include<opencv2\core.hpp>
#include<opencv\cv.h>
#include<opencv2\highgui.hpp>
#include<opencv2\imgproc.hpp>

using namespace cv;

class Histogram1D{
private:
	int histSize[1]; // 히스토그램 빈도수
	float hranges[2]; // 히스토그램 최소/최대 화소값
	const float* ranges[1];
	int channels[1]; // 1채널만 사용

public:
	Histogram1D(){ // 1차원 히스토그램을 위한 인자 준비
		histSize[0] = 256;
		hranges[0] = 0.0;
		hranges[1] = 255.0;
		ranges[0] = hranges;
		channels[0] = 0;
	}

	// 1차원 히스토그램 계산 
	MatND getHistogram(const Mat &image){
		MatND hist;
		// 이미지의 히스토그램 계산
		calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges);
		// 인자 값 : 이미지, 단일영상, 대상채널, 마스크 사용안함, 결과히스토그램,
		// 1차원 히스토그램, 빈도수, 화소값 범위
		return hist;
	}

	// 히스토그램을 위한 바 그래프 사용 

	Mat getHistogramImage(const Mat &image){
		MatND hist = getHistogram(image); // 히스토그램 계산
		
		double maxVal = 0; // 최대 빈도수
		double minVal = 0; // 최소 빈도수
		minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		// 히스토그램을 출력하기 위한 영상
		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));

		// nbins의 90%를 최대점으로 설정
		int hpt = static_cast<int>(0.9*histSize[0]);

		for (int h = 0; h < histSize[0]; h++){
			float binVal = hist.at<float>(h);
			int intensity = static_cast<int> ( binVal*hpt / maxVal);

			// 두 점간의 거리를 그림
			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0)); 
		}
		return histImg;
	}
};

int main(){
	
	// 입력 영상을 이진 영상으로 간주해 열기
	Mat image = imread("lena.png", 0);
	if (!image.data)
	{
		std::cout << "이미지를 불러오지 못했습니다 ㅠㅠ" << std::endl;
		return 0;
	}

	namedWindow("Image");
	imshow("Image", image);

	Histogram1D h; // 히스토그램을 위한 객체
	MatND histo = h.getHistogram(image); // 히스토그램 계산

	for (int i = 0; i < 256; i++) // 히스토그램의 빈도를 조회
		std::cout << "Value" << i << "=" << histo.at<float>(i) << std::endl;

	// 히스토그램을 영상으로 띄우기
	namedWindow("Histogram");
	imshow("Histogram", h.getHistogramImage(image));
	// 왼쪽이 검정색값, 오른쪽이 흰색값
	// 가운데 봉우리 부분은 중간 명암도 값
	// 왼쪽이 영상의 전경, 오른쪽이 배경

	//영상을 두 그룹으로 나누는 부분을 경계값으로 처리해 확인
	Mat thresholded; // 경계값으로 이진 영상 생성
	threshold(image, thresholded, 60, 255, THRESH_BINARY);
	// 영상을 경계화 하기 위해 히스토그램의 높은 봉우리(60) 방향으로 증가하기 직전인 최소값으로 정함
	namedWindow("Binary Image");
	imshow("Binary Image", thresholded);

	waitKey(0);

	return 0;
}



위의 예제코드에 주석을 달아놓은 것을 보면 쉽게 이해 할 수 있지만, 부연설명을 덧붙이자면




calcHist()는 영상의 히스토그램 값을 계산하는 함수이다.


이 함수는 히스토그램의 값을 계산해준다고 해서 RGB 채널 모두의 히스토그램을 구해주지 않는다. 이 RGB 채널, 즉 3채널의 히스토그램을 모두 그려주지 않기 때문에 채널 각각의 히스토그램을 따로따로 계산해주어야한다. 여섯번째 인자 자리는 히스토그램의 차원수를 결정한다. 히스토그램 계산 시 고려해야 할 채널은 설정한 차원수를 갖는 배열 중 하나이다. 또한, 대부분의 히스토그램은 단일 1채널이나 3채널 영상중 하나에 속하게 된다.





728x90
반응형