관심쟁이 영호

[#12] OpenCV(With. c++)ㅣ 히스토그램! , calcHist(), cvtColor(), 본문

학교공부/OpenCV

[#12] OpenCV(With. c++)ㅣ 히스토그램! , calcHist(), cvtColor(),

관심쟁이 영호 2020. 11. 15. 01:30
반응형

안녕하세요!

관심쟁이 영호입니다.

 

오늘은 히스토그램에 관해서 공부를 해볼 예정인데요!

한번 같이 살펴보시죠!

 


히스토그램

히스토그램이란?

도수 분표를 그래프로 나타낸 것입니다!

 

저희가 해볼 것은 이미지의 화소 값을 히스토그램으로 나타내 볼 겁니다!

 

관련 함수를 먼저 보겠습니다!

 

이름 인수 인수 내용
calcHist(Mat images, int nimages, int** channels, Array mask,
Array hist, int dims, int* histSize, float** ranges, bool uniform,
bool accumulate)
images 원본 영상배열
nimages 원본 영상의 개수
channels 히스토그램 계산에 사용되는 차원 목록
mask 특정영역만 계산하기 위한 마스크 행렬
hist 계산된 히스토그램이 출력되는 배열
dims 히스토그램의 차원 수
histSize 각 차원의 히스토그램 배열 크기
ranges 각 차원의 히스토그램의 범위
uniform 히스토그램이 균일한지를 나타내는 플래그
accumulate 누적 플래그

 

이렇게 함수와 해당 함수의 인수에 대해서 공부를 해보았어요!

 

그럼 두 번째로,

히스토그램의 스트레칭에 관해서 공부를 해볼게요.

이 말이 무슨 뜻이냐면, 영상을 히스토그램화 시켜서 그래프로 나타내면

전반적으로 어두운 영상이나, 밝은 영상은 한쪽으로 그래프가 치우쳐서 잘 안 보이는 효과가 생겨요 ㅠ

그것을 없애기 위해서는 그래프가 있는 부분만 쭈욱 늘려서 표시를 해주면 됩니다!

 

세 번째로,

히스토그램을 스트레칭을 할 수 없는 경우인데요!

한쪽에만 치우쳐 저 있다면 늘리면 되는데,

한쪽에 있고 반대편에 조금씩 또 있다면?? 스트레칭이 곤란한 상황이 와버리죠..

 

그러면 히스토그램을 평활화해주어야 해요!

히스토그램 평활화란

"전체의 가능한 영역을 모두 채우기 위해 영상의 영상 소값들을 균일하게 분산시키는 기술을 말한다. 균일 함수(Uniform)와 가우시안 함수가 많이 사용된다."라고 합니다!

 

여기서 사용하는 평활화 함수가 있어요!

그것은 바로

equalizeHist(image, dst2)!

 

image는 평활화할 대상 이미지!

dst2는 평활화한 값을 저장하는 이미지!

 

그럼 코드를 보시죠!

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void calc_Histo(const Mat& image, Mat& hist, int bins, int range_max = 256) {
	int histSize[] = { bins };							//히스토그램 계급 개수
	float range[] = { 0, (float)range_max };			// 0번 채널 화소값 범위
	int channels[] = { 0 };								// 채널 목록 - 단일채널
	const float* ranges[] = { range };					// 모든 채널 화소범위

	calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges);
}

void draw_Histo(Mat hist, Mat& hist_img, Size size = Size(256, 200)) {

	hist_img = Mat(size, CV_8U, Scalar(255));				//그래프 해열ㄹ
	float bin = (float)hist_img.cols / hist.rows;			//한 계급 너비
	normalize(hist, hist, 0, hist_img.rows, NORM_MINMAX);	

	for (int i = 0; i < hist.rows; i++)
	{
		float start_x = i * bin;
		float end_x = (i + 1) * bin;
		Point2f pt1(start_x, 0);
		Point2f pt2(end_x, hist.at<float>(i));

		if (pt2.y > 0) {
			rectangle(hist_img, pt1, pt2, Scalar(0), -1); // 막대 사각형 그리기
		}
		flip(hist_img, hist_img, 0);					// x축 기준 영상 뒤집기
	}
}

void create_hist(Mat img, Mat& hist, Mat& hist_img) {
	int histsize = 256, range = 256;
	calc_Histo(img, hist, histsize, range);   //히스토그램 계산
	draw_Histo(hist, hist_img);				// 히스토그램 그래프 그리기
}
void main() {

	Mat image = imread("../image/sample_image2.jpg", 0);
	CV_Assert(!image.empty());
	Mat hist, dst1, dst2, hist_img, hist_img1, hist_img2;
	create_hist(image, hist, hist_img);		//히스토그램 및 그래프 그리기

	Mat accum_hist = Mat(hist.size(), hist.type(), Scalar(0));
	accum_hist.at<float>(0) = hist.at<float>(0);
	for (int i = 0; i < hist.rows; i++) {
		accum_hist.at<float>(i) = accum_hist.at<float>(i - 1) + hist.at<float>(i);
	}

	accum_hist /= sum(hist)[0];   //누적합의 정규화
	accum_hist *= 255;
	dst1 = Mat(image.size(), CV_8U);
	for (int i = 0; i < image.rows; i++)
	{
		for (int j = 0; j < image.cols; j++)
		{
			int idx = image.at<uchar>(i, j);
			dst1.at<uchar>(i, j) = (uchar)accum_hist.at<float>(idx);

		}

	}

	equalizeHist(image, dst2);				//OpenCV 히스토그램 평활화

	create_hist(dst1, hist, hist_img1);
	create_hist(dst2, hist, hist_img2);

	imshow("image", image), imshow("img_hist", hist_img);  //원본 히스토그램
	imshow("dst1-User", dst1), imshow("User_hist", hist_img1); // 사용자 평활화
	imshow("dst2-OpenCV", dst2), imshow("OpenCV_hist", hist_img2); // OpenCV 평활화
	
	
	waitKey(0);
}

해당 화면과 같이 볼 수 있어요!

 

결과는

음.. 그렸긴 했는데

어디가 잘못됐는지 잘 모르겠어요 ㅠㅠ

책에 있는 대로 했는데

대칭적으로 나왔네요...

flip과정에서 문제가 생긴 거 같긴 한데.. 음..

ㅋㅋㅋㅋㅋ

넘어갈게요!

 

어쨌든 평활화한 것이 보이네요!!

300x250
Comments