블러링

평균 값 필터

  • 각각의 좌표에서 주변 픽셀 값들의 산술 평균을 계산하고, 이를 출력 영상의 픽셀 값으로 설정
  • 영상에 평균 값 필터를 적용하면 인접한 픽셀간의 급격한 그레이스케일 값 변화가 줄어들어, 날카로운 에지가 무뎌지고 영상에 있는 잡음이 감소하는 효과
  • 아래의 예제 같은 경우
    • 현재 위치의 픽셀 값인 1과 자기 주변의 8개의 픽셀 값인 1을 그대로 더해서 9로 나눔
    • 또는 각각의 원소 값이 1/9로 채워져 있는 3 x 3 행렬로 생각할 수 있음

imageimage

  • 5 x 5, 7 x 7의 필터 마스크를 사용할 수도 있음
  • 필터 마스크의 들어있는 원소 합이 1이면 필터링 결과 영상인 평균 밝기가 입력영상의 평균 밝기와 동일하게 설정이 됨
    • 만약 1보다 크면 결과 영상의 형태가 좀 더 밝아지는 형태로, 1보다 작을 경우 결과 영상의 형태가 좀 더 어두워지는 형태로 나타남
  • 마스크 크기가 커질수록 평균 값 필터 결과가 더욱 부드러워짐

  • 곱셈의 횟수를 비교하면 3 X 3은 9번 5 X 5 는 25번의 연산을 하게 되므로 마스크 크기가 클수록 더 많은 연산량이 필요함

image

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main(void)
{
	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	/* float data[] = {
	1 / 9.f, 1 / 9.f, 1 / 9.f,
	1 / 9.f, 1 / 9.f, 1 / 9.f,
	1 / 9.f, 1 / 9.f, 1 / 9.f,
	}; 
		
	Mat kernel(3, 3, CV_32FC1, data);
	*/	
	Mat kernel = Mat::ones(3, 3, CV_32FC1) / 9.f;
	cout << kernel << endl;
	
	Mat dst;
	filter2D(src, dst, -1, kernel);

	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}

OpenCV 블러링 함수(평균 값 필터)

void blur(InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT);
  • src : 입력 영상
  • dst : 출력 영상
  • kszie : 평균값 필터 크기
  • anchor : 고정점
  • borderType : 가장자리 픽셀 확장 방식

image

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	Mat src = imread("rose.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	imshow("src", src);
	
	Mat dst;
	for (int ksize = 3; ksize <= 7; ksize += 2) { // 3부터 7까지 2씩 증가하므로 3, 5, 7
		blur(src, dst, Size(ksize, ksize)); // 각각의 for문에 대하여 ksize x ksize 만큼의 블러링 수행

		String desc = format("Mean: %dx%d", ksize, ksize); // 어떤 크기의 필터인지 영상 위에 문자열로 나타냄
		putText(dst, desc, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1.0, 
			Scalar(255), 1, LINE_AA);

		imshow("dst", dst);
		waitKey(); // 키보드 키를 기다림
	}
}

image

image

image

평균 값 필터에 의한 블러링의 단점

  • 필터링 대상 위치에서 가까이 있는 픽셀과 멀리 있는 픽셀이 모두 같은 가중치를 사용하여 평균을 계산함
    • 마스크의 크기가 커짐에 따라 현재 위치의 픽셀 값의 비중은 줄어들고, 상대적으로 멀리 떨어져 있는 픽셀들의 영향이 커짐
    • 아래 그림을 예시로 들면, 현재 위치의 픽셀 값은 1/7 밖에 되지 않고 나머지 위치의 비중이 6/7임 따라서 원본 영상의 같은 위치 보다 주변 위치의 픽셀 값의 영향을 더 많이 받는 단점이 있음
    • 평균값 필터의 결과 영상은 원본 값의 픽셀을 잘 보존하지 못하고 주변 픽셀의 영향을 너무 많이 받는 단점이 있음

image

  • 동일한 가중치가 아닌, 현재 위치와 가까이 있는 위치의 가중치는 크게, 멀리 떨어져 있는 위치의 가중치는 적게 주는 형태로 필터를 설계할 수 있음

image

정규 분포와 가우시안 함수

정규분포
  • 평균을 중심으로 좌우 대칭인 펴진 종 모양을 갖는 확률 분포
  • 가우시안 분포라고도 부름
  • 자연계에서 일어나는 수 많은 일들을 설명할 수 있음
    • 키, 몸무게, 시험 점수, 잡음, 측정 오차, 제품 수명 등

image

가우시안 필터
  • 가우시안 함수의 특징

image

  • 2차원 가우시안 함수

image

  • 2차원 가우시안 필터 마스크
    • x가 0이고 y가 0 일 때 2차원 가우시안 함수의 값은 0.1592
    • x가 1이고 y가 0 일 때 2차원 가우시안 함수의 값은 0.0965
    • 자세히 보면 값들이 상하좌우로 대칭인 것을 확인할 수 있음
    • x가 값이나 y의 값이 4에 가까울 수록 다 0으로 나타남
      • 소수점 5자리 이상으로 표현시 0이 아닌 0에 가까운 값인 것을 확인할 수 있지만 거의 0이라고 생각하면 됨
      • -3σ ~ 3σ 사이에 99.7%의 데이터가 들어가는 것과 어느 정도 연관이 있음
      • 보통 가우시안 함수의 필터 마스크를 생성할 때 6σ +1 형태의 마스크 크기를 생성하거나 8σ + 1 형태의 마스크 크기를 생성함

image

  • 가우시안 필터링
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT);
  • src : 입력 영상, 각 채널 별로 처리 됨. (CV_8U, CV_16U, CV_32F, CV_64F)
  • dst : 출력 영상, src와 같은 크기, 같은 타입
  • ksize : 가우시안 커널 크기, Size()를 지정하면 sigma 값에 의해 자동 결정됨
  • sigmaX : x 방향 sigma
  • sigmaY : y 방향 sigma. 0이면 sigma x와 같게 설정
  • borderType : 가장자리 픽셀 처리 방식
#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	Mat src = imread("rose.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	Mat dst;
	Mat dst2;

	GaussianBlur(src, dst, Size(), 2.0); // 시그마 값을 1.0을 줬으므로 자동으로 커널의 크기는 7 x 7 로 형성이 됨
	blur(src, dst2, Size(7, 7));

	imshow("src", src);
	imshow("dst", dst);
	imshow("dst2", dst2);
	waitKey(); // 키보드 키를 기다림

}
  • 같은 7 x7로 가우시안 필터와 평균값 필터를 이용해서 했을 때 평균값 필터의 결과가 더 많이 블러링이 되는 것은 사실이지만, 평균값 필터의 경우 필터 사이즈가 커지면, 결과의 품질이 점점 안 좋아짐
  • 평균값 필터의에서 진행한 블러링을 비슷하게 가우시안 블러링으로 하고 싶을 경우에는 가우시안 블러링의 시그마 값을 바꿔 주면 됨

  • 자연스러운 blur을 하고 싶을 때는 가급적이면 Gausian Blur을 사용
  • 간단한 형태로 blur을 하고 싶을 때는 평균 값 필터를 사용해도 문제 없음

image

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main(void)
{
	Mat src = imread("rose.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	imshow("src", src);

	Mat dst;
	for (int sigma = 1; sigma <= 5; sigma++) { // 시그마 값을 1부터 5까지 증가시키면서
		TickMeter tm;
		tm.start();

		GaussianBlur(src, dst, Size(0, 0), (double)sigma); // 가우시안 블러를 수행함
		
		tm.stop();
		cout << "sigma: " << sigma << ", time: " << tm.getTimeMilli() << " ms." << endl; // 가우시안 블러가 수행되는 시간을 측정해서 컨솔창에 출력함

		String desc = format("Sigma = %d", sigma);
		putText(dst, desc, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255), 1, LINE_AA); // 시그마의 값을 문자열로 출력해 영상에 보여줌

		imshow("dst", dst);
		waitKey();
	}
}
  • 시그마 값이 커짐에 따라 블러링이 더 심해지는 것을 확인할 수 있음
  • 시그마 값이 커질수록,내부에서 사용하는 커널 사이즈가 커지고, 그에 따라 연산 시간이 증가하는 것을 확인할 수 있음

image

image

image

image

image

샤프닝

언샤프 마스크 필터링

  • 날카롭지 않은(unsharp) 영상으로 블러링된 영상을 이용하여 날카로운 영상을 생성함
  • g(x)는 원본 영상 - 부드러운 영상(블러링 영상) 을 한 것이므로 g(x)는 날카로운 영상만 표현하고 있음을 말함
  • 따라서 원본 영상에 g(x)를 더하면 원본 영상이 날카로워지는 영상 h(x)를 생성할 수 있음

image

  • 원본 영상인 f(x)에서 변화의 폭 보다 날카로워진 영상 h(x) 에서의 변화의 폭이 더 커짐

    • 변화의 폭이 더 커졌다는 것은 사람 눈으로 보면 Contrast가 증가하여 좀 더 선명해 보이게 되고 날카로워진 느낌을 받게 됨

imageimage

  • g(x)의 식을 h(x)에 대입하면
    • 날카로운 영상을 만들기 위해서는 원본 영상의 2배에 블러링 영상을 빼면 됨

image

  • 평균값 필터를 사용한 언샤프 마스크 필터링 구현
Mat src = imread("rose.bmp", IMREAD_GRAYSCALE); // 입력 영상 src에 저장

Mat blr;
blur(src, blr, Size(3,3)); // blur 함수를 이용해 블러링 결과를 blr에 저장

Mat dst = 2 * src - blr; // 샤프닝 영상 dst
  • 언샤프 필터 만들기

image

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

void sharpen_mean();

int main(void)
{
	sharpen_mean();
}

void sharpen_mean()
{
	Mat src = imread("rose.bmp", IMREAD_GRAYSCALE); // src에 영상 저장

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}
	
	/*
	float sharpen[] = { // 주변에는 -1 / 9 로 채워져있고 가운데는 17 / 9 로 구성된 3 x 3 행렬의 float형 sharpen 생성
		-1 / 9.f, -1 / 9.f, -1 / 9.f,
		-1 / 9.f, 17 / 9.f, -1 / 9.f,
		-1 / 9.f, -1 / 9.f, -1 / 9.f
	};
	
	Mat kernel(3, 3, CV_32FC1, sharpen); # sharpen을 초기값으로 갖는 3 x 3 크기의 커널을 만듬

	Mat dst;
	filter2D(src, dst, -1, kernel); # 필터 마스크 행렬을 지정하면 샤프닝이 진행됨
	*/
	
	Mat blr;
	blur(src, blr, Size(3,3));
	
	Mat dst 2 & src - blr;
	
	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();
}
  • 입력 영상 src 보다 출력 영상 dst가 좀 더 선명해짐

image

언샤프 마스크 가우시안 필터

  • GaussianBlur 함수를 사용
    • 블러링 처럼 샤프닝ㅑ할 때도 가우시안 필터를 사용하면 더 자연스러운 결과를 기대할 수 있음
Mat src = imread("rose.bmp", IMREAD_GRAYSCALE);

Mat blr;
GaussianBlur(src, blr, Size(), 1.0);

Mat dst = 2 * src - blr;
  • 샤프니스(sharpness) 조절을 위한 가중치 : 알파
    • 알파 값이 1이면 그냥 더하기
    • 알파 값이 1보다 작으면 날카로운 성분을 조금만
    • 알파 값이 1보다 크면 날카로운 성분을 많이

image

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

void sharpen_gaussian();

int main(void)
{
	sharpen_gaussian();
}


void sharpen_gaussian()
{
	Mat src = imread("rose.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	imshow("src", src);

	Mat srcf;
	src.convertTo(srcf, CV_32FC1); // 입력 영상인 그레이스케일 src를 float 타입으로 변환해 srcf에 저장

	for (int sigma = 1; sigma <= 5; sigma++) {
		Mat blr;
		GaussianBlur(srcf, blr, Size(), sigma);  // 가우시안 블러 입력영상으로 srcf
		// 소수점 이하의 연산이 잘려나가지 않게 하기 위해 32F1의 src가 아닌 srcf로 진행

		float alpha = 1.0f;
		Mat dst = (1.f + alpha) * srcf - alpha * blr; // 샤프닝 된 영상 결과를 생성하고 dst는 float 타입의 실수형 행렬로 만들어짐

		dst.convertTo(dst, CV_8UC1); // float 타입으 ㅣ실수형 행렬인 dst를 화면에 보여주기 위해 다시 그레이스케일로 변환 

		String desc = format("sigma: %d", sigma); // 시그마 값 출력
		putText(dst, desc, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255), 1, LINE_AA);

		imshow("dst", dst);
		waitKey();
	}

	destroyAllWindows();
}
  • 시그마 값이 올라갈 때마다 선명해지면서 contrast가 높아지는 것을 확인 할 수 있음
  • 너무 많이 블러된 결과를 이용해서 샤프닝을 할때는 샤프닝의 결과가 과하다는 느낌을 받을 수 있으므로 적당한 시그마 값을 사용해야 함

  • 가중치 알파값을 1.5로 지정하게되면 아까보다 더 샤프닝된 결과를 얻을 수 있음

  • 적당한 수준에서 가우시아 표준 편차 값인 시그마 값과 알파 값을 조절해 가면서 샤프닝을 적용하면 됨

image image image image image

영상의 잡음

  • 영상의 픽셀 값에 추가되는 원치 않는 형태의 신호
  • 카메라에서 광학 신호를 전기적 신호로 변환하는 과정(센서)에서 잡음이 추가될 수 있음

image

  • 잡음의 종류
    • 소금 & 후추 잡음(Salt & Pepper noise)
      • 임의의 좌표 값들이 검정색(0) 또는 흰색(255)로 변경된 것을 볼 수 있음
    • 가우시안 잡음(Gaussian noise)
      • 윗 선반 밑에 색을 확인해보면 몇몇 픽셀은 좀 더 어둡고, 몇몇 픽셀은 좀 더 밝게 변경된 것을 볼 수 있음

image

  • 정상분포 난수 발생
void randn(InputOutputArray dst, InputArray mean, InputArray stddev);
  • dst : 정상 분포 난수 행렬. dst는 미리 생성 되어있어야 하며 1~4 채널을 가짐
    • 그레이스케일 영상의 경우 마이너스 값들이 포화연산되어서 0으로 변환 되므로 +, - 를 모두 갖는 난수를 발생시키고 싶을 때는 부호가 있는 정수나 실수형 행렬로 지정해야 함

image

  • mean : 평균
  • stddev : 표준편차

  • 균일분포 난수를 발생하려면 randu() 함수를 사용해야 함
int main(void)
{
	Mat src = imread("lenna.bmp", IMREADE_GRAYSCALE);
	
	Mat noise(src.size(), CV_32S); // 레나영상과 동일한 크기이면서 타입은 CV_32S인 부호가 있는 정수형(일반적인 c언어의 int타입) noise 변수를 생성
	randn(noise, 0, 10); // 평균이 0이고 표준편차가 10인 형태의 가우시안 분포를 따르는 랜덤 넘버를 생성해서 noise 행렬에 채움
	
	Mat dst;
	add(src, noise, dst, noArray(), CV_8U) // src의 입력영상과 noise를 더하여 결과 영상 dst를 생성
    // src와 noise의 타입이 다르므로 맨 마지막 타입 변수 없이는 add가 진행이 안됨
    // 최종적인 결과영상 dst는 CV_8U 타입으로 저장되게 코드를 작성
	
}

프로파일을 이용한 잡음 분석

  • 영사에서 특정 경로(라인 또는 곡선) 상에 있는 픽셀의 밝기 값을 그래프 형태로 나타냄
    • Line Profile, Intensity Profile
  • 하얀색 선에 대해 픽셀 값을 오른쪽 1차원 그래프 형태로 나타냄
  • 오른쪽 그래프는 아래가 0 위에는 255로 잠깐 내려왔을 때 하얀색 선이 어두운 부분인 지붕을 지나고 있기 때문

image

  • 표시한 검정색 직선에 대해 R, G, B 3개의 색상에 대한 프로파일을 나타냄

image

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

void on_trackbar(int, void*);

Mat src, dst, profile;
int row = 0;

int main(void)
{
	src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

//	GaussianBlur(src, src, Size(), 2);
	
	namedWindow("dst");
	namedWindow("profile");

	profile.create(256, src.cols, CV_8UC1);

	createTrackbar("Profile", "dst", &row, src.rows - 1, on_trackbar);
	on_trackbar(0, 0);

	waitKey();
}

void on_trackbar(int, void*)
{
	src.copyTo(dst);
	profile.setTo(255);

	uchar* pSrc = (uchar*)src.ptr<uchar>(row);
	uchar* pDst = (uchar*)dst.ptr<uchar>(row);

	for (int i = 1; i < src.cols; i++) {
		line(profile, Point(i - 1, 255 - pSrc[i - 1]), Point(i, 255 - pSrc[i]), 0);
		pDst[i] = saturate_cast<uchar>(pSrc[i] + 50);
	}

	imshow("dst", dst);
	imshow("profile", profile);
}

  • 가우시안 블러를 이용하니, 지글지글 했던 부분이 많이 완만해졌음 하지만 영상 자체가 상당히 흐려짐
  • 시그마 값을 키우면 키울 수록 노이즈가 감소하겠지만, 원본 영상의 윤곽이 너무 훼손이 됨

image

양방향 필터

  • 엣지 보전 잡음 제거 필터(edge-preserving noise removal filter): 엣지 성분은 그대로 냅두고 잡음만 제거함
  • 평균 값 필터 또는 가우시안 필터는 에지 부근에서도 픽셀 값을 평탄하게 만드는 단점이 있음
  • 기준 픽셀과 이웃 픽셀과의 거리, 그리고 픽셀 갓의 차이를 함께 고려하여 블러링 정도를 조절함

image

  • 일반적인 가우시안 필터링은 영상 전체에서 블러링

image

  • 양방향 필터는 에지가 아닌 부분에서만 블러링
    • 평탄한 영역과 엣지가 있는 영역 좀 복잡한 영역에 대해서 서로 다른 필터 마스크가 적용되도록 설계됨
      • 평탄한 영역에서는 일반적인 가우시안 필터를 적용하고 나머지 부분에 대해서는 기존 가우시안 필터를 활용해서 필터 마스크를 만듬
      • 픽셀 값의 배열에 따라 필터 마스크의 모양이 달라지므로, 각각의 픽셀마다 서로 다른 형태의 마스크가 적용되고 이러한 이유로 일반적인 가우시안 필터보다 연산 속도가 상당히 느려짐

  • 양방향 필터 수식

image

  • OpenCV 양방향 필터링 함수
void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT);
  • src : 입력 영상. 8비트 또는 실수형. 1채널 또는 3채널
  • dst : 출력 영상. src와 같은 크기, 타입
  • d : 필터링에서 사용될 이웃 픽셀의 거리. 음수(-1)를 입력할 경우 sigmaSpace 값에 의해 자동으로 결정됨
  • sigmaColor : 색 공간에서 필터의 표준 편차
  • sigmaSpace : 좌표 공간에서 필터의 표준 편차
  • borderType : 가장자리 픽셀 처리 방식
#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main(void)
{
	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	TickMeter tm;
	tm.start();

	Mat dst1;
	GaussianBlur(src, dst1, Size(), 5); // 가우시안 블러의 결과 영상은 dst1에 저장 
	// 가우시안 블러의 시그마 값은 5로 설정하고 필터의 사이즈는 자동으로 결정되게 함

	tm.stop(); 
	cout << "Gaussian: " << tm.getTimeMilli() << endl; // 가우시안 블러 연산 시간

	tm.reset();
	tm.start();

	Mat dst2;
	bilateralFilter(src, dst2, -1, 10, 5); // 양방향 필터의 결과 영상은 dst2에 저장
	// 시그마 space의 값을 5로 설정하여 가우시안 블러에서 사용되는 값과 동일하게 줌
	// d 값을 -1로 결정하면, 시그마 값을 이용하여 자동으로 필터 사이즈를 결정하므로 위 가우시안 블러의 필터 사이즈와 같음

	tm.stop();
	cout << "Bilateral: " << tm.getTimeMilli() << endl; // 가우시안 블러 연산 시간

	imshow("src", src);
	imshow("dst1", dst1);
	imshow("dst2", dst2);
	waitKey();
}
  • 양방향 필터의 연산시간이 더 오래걸림 (이미지에서 가우시안이 더 오래 걸린 이유는 영상을 키는데 걸리는 시간)
  • 가우시안 블러는
    • 전체적으로 노이즈는 많이 없어진 것 같은데 개체의 윤곽마저 블러링이 됨
  • 양방향 필터는
    • 전체적으로 엣지부분의 샤프니스가 살아있음
    • 평탄한 영역에서는 블러링 잘 적용 되서 원본 영상에서 지글지글 보이던 노이즈가 사라짐
    • 엣지 부분이나 픽셀 값이 급하게 변하는 머리카락 같은 부분에서는 블러링이 조금만 적용되거나 아예 적용되지 않아 원본 영상에서의 볼 수 있는 디테일이 그대로 살아 있음

image

bilateralFilter(src, dst2, -1, 10, 5);
  • -30 이나 +30을 벗어날 경우 일반적인 가우시안 값에 거의 0에 가까운 값을 곱한다고 생각하면 됨

    • 현재 픽셀과 주변 픽셀간의 차이가 2시그마 또는 3시그마 많게는 4시그마 (여기서는 10이라는 시그마 값을 줬으므로 대략적으로 30 픽셀 이상의 차이가 날 경우) 필터 마스크의 값이 거의 0에 가까운 값으로 변경되므로 그런 경우에는 거의 블러링이 발생하지 않음

    • 픽셀 값이 +1 ~ -1, +2 ~ -2 사이 일 경우 블러링이 적용되는 형태라고 생각할 수 있음

bilateralFilter(src, dst2, -1, 5, 5);
  • 주변 픽셀과의 픽셀 값의 차이가 좀 더 작을 경우에만 블러링이 적용 됨
  • 빨간 네모칸 부분의 맨들맨들한 부분이 적어짐

image

bilateralFilter(src, dst2, -1, 100, 5);
  • 픽셀 값의 차이가 300 이상이 되는 경우는 거의 발생하지 않으므로 이 경우에는 대부분의 위치에서 가우시안 블러가 실행됨

image

  • 아주 특출나게 픽셀 값의 차이가 큰 경우에만 엣지 성분이 조금 살아있는 것을 확인할 수 있음

image