본문 바로가기

Computer Vision by OpenCV

OpenCV 잡음(noise) 제거하기 - Local Averaging, Gaussian smoothing

반응형

이미지에서 Noise 제거하기


Noise 픽셀들만 정확히 찾아내서 원래 색깔로 돌려놓는 방법이 있으면 좋으련만...

이런 방법을 구현하기는 거의 불가능하다. 왜냐면 어떤 픽셀이 noise인지 알기 쉽지 않을 뿐더러, 설령 알 수 있다하더라도 원래 색깔이 어떤 것인지를 알기는 더더욱 어렵기 때문이다.


Noise를 완전히 없애기는 어려우니, 눈에 덜 띄도록 하는 것이 한 가지 방법이 될 수 있다.

Noise 픽셀의 특징은 주변 픽셀들과 다른 색깔값을 갖는다는 것이다.

그렇다면 주변 픽셀들과 비슷한 색깔을 갖도록 하면 될 것이다.

그런데, 어떤 픽셀이 noise인지 모르기 때문에, 모든 픽셀에 대해서 주변 픽셀들과 비슷한 색을 갖도록 하는 것이 noise를 완화시키는 한 가지 방법이다.


Local Averaging

픽셀이 주변 픽셀들과 유사한 색깔값을 갖도록 하는 한 가지 방법은 자신과 주변픽셀들의 색깔값의 평균값을 구해서 그것으로 자신의 색깔값으로 하는 것이다.

예를들어, 아래 그림과 같이 E픽셀의 값은 주변 8개 픽셀 (A ~ I)들과 자신의 색깔값의 평균으로 하는 것이다.

혹은 이웃의 범위를 조금 더 넓혀서 '파'픽셀의 경우 주변 24개 픽셀 (가 ~ 캬)들과의 평균값으로 한다.


Gaussian Smoothing


Local Averaging은 모든 픽셀들의 값을 같은 비율로 반영한다. 반면에, Gaussian smoothing은 픽셀별로 반영비율을 달리 한 것이다. 동기는 이렇다. 어느 픽셀의 값을 결정하는데, 자기자신의 픽셀값이 가장 많이 반영되는 것이 더 합리적이기 때문이다. 그리고 주변 이웃픽셀들도 멀리 있는 것보다는 가까이 있는 것의 반영비율을 높이는 것이 원래 색깔값을 유지하는 적절한 방법이 될 것이다.


예를 들면 아래 그림은 중앙픽셀값을 계산하기 위한 각 픽셀들의 반영비율을 보여준다. 중앙에 있는 자기자신이 4/16으로 가장 많이 반영되고, 대각선으로 멀이 있는 픽셀들은 1/16로 덜 반영한다. 여기서는 3x3 경우만 보이지만 5x5도 가능하며, 반영비율의 차이도 조절할 수 있다. 


실험결과

Salt and Pepper Noise가 들어간 이미지에 대해 잡음제거 작업을 시도한 결과를 살펴보자.

그림 1과 2는 각각 원본과 인위적으로 noise를 추가한 이미지이다.

그리고 그림 3은 5x5 방식의 local averaging을 noise이미지에 시도한 결과이고, 그림 4는 5x5 방식의 Gaussian smoothing을 시도한 결과이다.

똑같은 5x5 방식일지라도 Gaussain smoothing이 local averaging보다 좀 더 선명한 것을 알 수 있다. 왜냐하면 Gaussian smoothing은 자기자신의 색깔이 좀 더 많이 반영되기 때문에, 어느 정도 윤곽이 남기 때문이다.


<그림1> 원본 이미지


<그림 2> Salt and pepper 잡음이 들어간 이미지


<그림 3> Local averaging (5x5)을 적용한 결과


<그림 4> Gaussian smoothing (5x5)을 적용한 결과


소스코드 1은 그림 <1,2,3,4> 생성에 사용된 것으로, local averaging을 위해 OpenCV가 제공하는 함수 blur와 Gaussain smoothing을 위해서도 OpenCV 함수 GaussianBlur( )를 이용하였다.


<소스코드 1> 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/*
img의 전체 픽셀중 noise_ratio 퍼센트 만큼의
픽셀을 salt & pepper noise로 바꾼다.
*/
void addSaltAndPepperNoise(Mat& img, double noise_ratio)
{
	int rows = img.rows;
	int cols = img.cols;
	int ch = img.channels();
	int num_of_noise_pixels = (int)((double)(rows * cols * ch)*noise_ratio);

	for (int i = 0; i < num_of_noise_pixels; i++)
	{
		int r = rand() % rows;  // noise로 바꿀 행을 임의로 선택
		int c = rand() % cols;  // noise로 바꿀 열을 임의로 선택
		int _ch = rand() % ch;  // noise로 바꿀 채널의 임의로 선택

		// img.ptr<uchar>(r)은 r번째 행의 첫번째 픽셀, 첫번째 채널에 대한 주소값을 반환한다.
		uchar* pixel = img.ptr<uchar>(r) +(c*ch) + _ch; // noise로 바꿀 정확한 위치를 계산

		*pixel = (rand() % 2 == 1) ? 255 : 0; // black(0) 혹은 white(255)로 교체
	}
}

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat spImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), 0.25, 0.25, CV_INTER_AREA);

	spImg = inputImg.clone();
	addSaltAndPepperNoise(spImg, 0.05);

	Mat localAvgImg;
	// Noise 제거를 위해 3 by 3 매트릭스를 이용하여
	// local averaing을 시행.
	blur(spImg, localAvgImg, Size(5, 5));  

	Mat gaussianSmoothedImg;
	GaussianBlur(spImg, gaussianSmoothedImg, Size(5, 5), 1.5);

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("SaltAndPepper", CV_WINDOW_AUTOSIZE);
	namedWindow("LocalAveraging", CV_WINDOW_AUTOSIZE);
	namedWindow("GaussianSmoothing", CV_WINDOW_AUTOSIZE);

	moveWindow("Original", 100, 100);
	moveWindow("SaltAndPepper", 120, 120);
	moveWindow("LocalAveraging", 140, 140);
	moveWindow("GaussianSmoothing", 150, 150);

	imshow("Original", inputImg);
	imshow("SaltAndPepper", spImg);
	imshow("LocalAveraging", localAvgImg);
	imshow("GaussianSmoothing", gaussianSmoothedImg);

	waitKey(0);
	return 0;
}


반응형