open-cv 영상처리

opencv c++ 가우시안 필터

hojung 2022. 3. 23.
728x90
반응형

opencv 라이브러리를 사용하여 c++언어로 가우시안 필터를 구현해보았다. 

int myKernelCon9x9(uchar* arr, int kernel[][9], int x, int y, int width, int height) //convolution
{
	int sum = 0;
	int sumKernel = 0;

	for (int j = -1; j <= 7; j++)
	{
		for (int i = -1; i <= 7; i++)
		{
			if ((y + j) >= 0 && (y + j) < height && (x + i) >= 0 && (x + i) < width)
			{
				sum += arr[(y + j) * width + (x + i)] * kernel[i + 1][j + 1];
				sumKernel += kernel[i + 1][j + 1];
			}
		}
	}
	if (sumKernel != 0) { return sum / sumKernel; } //합이 1로 정규화되도록 해 영상의 밝기 변화 방지
	else { return sum; }
}

가우시안 필터에서는 convolusion이라는 연산 과정이 필요하다. 따라서 다음과 같이 9x9 행렬에서 convolution연산을 수행해주는 함수를 생성하였다. 

Mat myGaussianfilter(Mat srcImg)
{
	int width = srcImg.cols;
	int height = srcImg.rows;

	int kernel[9][9] = { 0,1,1,2,2,2,1,1,0,
						 1,2,4,5,5,5,4,2,1,
						 1,4,5,3,0,3,5,4,1,
						 2,5,3,12,24,12,3,5,2,
						 2,5,0,24,40,24,0,5,2,
						 2,5,3,12,24,12,3,5,2,
						 1,4,5,3,0,3,5,4,1,
						 1,2,4,5,5,5,4,2,1,
						 0,1,1,2,2,2,1,1,0
};
	Mat dstImg(srcImg.size(), CV_8UC1);
	uchar* srcData = srcImg.data;
	uchar* dstData = dstImg.data;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			dstData[y * width + x] = myKernelCon9x9(srcData, kernel, x, y, width, height);
		}
	}

	return dstImg;
}

다음 행렬은 각 행렬마다 가중치를 주는 정규분포를 따르는 가우시안 분포에 해당하는 행렬이다. 저 행렬과 내가 가우시안 필터링을 하고 싶은 이미지를 convolution해주면 가우시안 필터가 씌워진 이미지가 만들어진다. 가우시안 필터는 고주파 영역을 뭉개뜨리는 역할을 하기 때문에 이미지를 처리하면 좀 흐려진 이미지가 생성될 것이다. 가우시안 매트릭스는 가운데가 볼록하고 가장자리로 갈수록 퍼지는 형태로 좀 더 극적인 가우시안 필터링을 수행하고 싶다면 최고점과 최하점의 차이를 크게 하면 된다. 

 

Mat GetHistogram(Mat& src)
{
	Mat histogram;
	const int* channel_numbers = { 0 };
	float channel_range[] = { 0.0, 255.0 };
	const float* channel_ranges = channel_range;
	int number_bins = 255;

	//히스토그램 계산
	calcHist(&src, 1, channel_numbers, Mat(), histogram, 1, &number_bins, &channel_ranges);

	//히스토그램 plot
	int hist_w = 500;
	int hist_h = 1000;
	int bin_w = cvRound((double)hist_w / number_bins);

	Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(0, 0, 0));
	normalize(histogram, histogram, 0, histImage.rows, NORM_MINMAX, -1, Mat());//정규화 

	for (int i = 1; i < number_bins; i++)
	{
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(histogram.at<float>(i - 1))),
			Point(bin_w * (i - 1), hist_h - cvRound(histogram.at<float>(i))),
			Scalar(255, 0, 0), 2, 8, 0); // 값과 값을 잇는 선을 그리는 방식으로 plot
	}
	return histImage;
}

가우시안 필터링을 통해 생성된 이미지와 원본을 비교하기 위해 생성한 히스토그램 생성 함수이다. 밝기에 따른 픽셀 값을 그래프로 보여준다.

int main()
{
	Mat gear = imread("gear.jpg", 0); // 원본 이미지
	Mat colorgear = imread("gear.jpg", 1); // 컬러
	imshow("gear", gear); // 원본 이미지

	Mat bgear = GetHistogram(gear); // 가우시안 전 히스토그램
	imshow("before gaussian", bgear); // 가우시안 전 히스토그램

	Mat blurgear = myGaussianfilter(gear); // 가우시안 처리

	imshow("gear1", blurgear); // 가우시안 후 이미지
	Mat agear = GetHistogram(blurgear); // 가우시안 후 이미지
	imshow("after gaussian", agear); // 가우시안 후 이미지 보여주기

	SpreadSalts_blue(gear, 100); // 점 찍기
	imshow("salt gear", gear); // 점찍은 이미지 보여주기
	Mat saltblurgear = myGaussianfilter(gear); // 점 찍은 거 블러 처리하기 (가우시안)
	imshow("salt after gaussian", saltblurgear); // 보여주기

	Mat sobelgear = mySobelFilter(gear); // 45 135도 소벨 필터
	imshow("sobel", sobelgear); // 처리 값

	vector <Mat> gau = myGaussianPyramid(gear);
	imshow("2nd", gau[2]);
	imshow("1st", gau[1]);
	imshow("0st", gau[0]);
	imshow("colorgear", colorgear);


	waitKey(0);
}

메인 함수는 다음과 같다. 다른 sobel filter와 같은 것은 다음 포스팅에서 설명하겠다. 

히스토그램을 출력해 비교하면 다음과 같다. 

 

1. 원본

2. 가우시안 필터 처리 후

원본에 비해 고주파 성분이 많이 뭉개져 전체적으로 좀 흐린 이미지를 얻을 수 있다. 

1. 원본 이미지의 히스토그램

2. 가우시안 필터를 적용한 이미지의 히스토그램

이것은 가우시안 필터를 적용한 이미지의 히스토그램이다. 히스토그램의 차이를 확인해보면 가우 시안 블러를 처리한 후의 히스토그램 또한 부드럽게 처리가 되어 있는 것을 볼 수 있다. 블러 처 리를 했기 때문에 픽셀 값이 discrete하지 않고 연속적으로 된 것이다.


자 그럼 가우시안 필터는 왜 사용하는 것일까?

영상과 이미지에서 노이즈란 아주 극적인 고주파에 해당하는 경우가 많다. 원본 이미지와 동떨어져 생뚱맞은 값을 가지기 때문이다. 따라서 가우시안 필터를 적용하면 이런 outlier들을 제거하여 노이즈를 줄일 수 있다. 

앞선 포스팅에서 적용했던 salt pepper noise를 이미지에 적용하면 다음과 같다. 

1. salt pepper noise를 적용한 이미지

중간 중간 하얀점이 보이는 것을 확인할 수 있다. 

 

2. noise가 존재하는 이미지에 가우시안 필터를 씌운다. 

 

가우시안 필터를 씌우자 salt pepper noise가 보이지 않는 것을 확인할 수 있었다. 따라서 가우시안 블러는 노이즈 제거에 탁월하다는 것을 알 수 있다. 

728x90
반응형

'open-cv 영상처리' 카테고리의 다른 글

opencv C++ BandPass Filter  (2) 2022.03.24
opencv c++ sobel filter  (0) 2022.03.23
opencv 사진 합성  (0) 2021.06.18
opencv spread salt  (0) 2021.06.18

댓글