본문 바로가기

3학년/OpenCV

[OpenCV] 7장 필터

 

 

 

7.1 영상 필터링

 

7.1.1 필터링 연산 방법

 

필터 (filter): 무언가를 걸러 내고 일부만 통과시키는 장치

 

더보기

void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor = Point(-1, -1), double delta = 0, int borderType = BORDER_DEFAULT);

  • src : 입력 영상
  • dst: 출력 영상,  src와 같은 크기, 같은 채널 수를 갖는다
  • ddepth : 결과 영상의 깊이 
  • kernel: 필터링 커널, 1채널 실수형 행렬
  • anchor: 고정점 좌표. Point(-1, -1)을 지정하면 커널 중심을 고정점으로 사용합니다.
  • delta: 필터링 연산 후 추가적으로 더할 값
  • borderType: 가장자리 픽셀 확장 방식

 

 

7.1.2 엠보싱 필터링

 

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

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

	float data[] = { -1, -1, 0, -1, 0, 1, 0, 1, 1 };
	Mat emboss(3, 3, CV_32FC1, data);

	Mat dst;
	filter2D(src, dst, -1, emboss, Point(-1, -1), 128);

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

	waitKey();
	destroyAllWindows();
}
  • 3행: rose.bmp 영상을 그레이스케일 형식으로 불러온다
  • 10~11행: data 배열을 이용하여 3x3 크기의 엠보싱 필터 마스크 행렬 emboss를 생성한다
  • 14행: filter2D() 함수를 이용하여 엠보싱 필터링을 수행한다. 이때 filter2D() 함수 여섯 번쨰 인자에 128을 지정하여 필터링 결과 영상에 128을 더한다.

 

 

 

7.2 블러링: 영상 부드럽게 하기

 

7.2.1 평균값 필터

 

블러링: 초점이 맞지 않는 사진처럼 영상을 부드럽게 만드는 필터링 기법. 스무딩이라고도 한다.

 

더보기

void blur(InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1, -1), int borderType = BORDER_DEFAULT);

  • src: 입력 영상. 다채널 영상은 각 채널별로 블러링을 수행한다. 입력 영상의 깊이는 CV_8U, CV_16U, CV_16S, CV_32F, CV_64F 중 하나여야 한다.
  • dst: 출력 영상. src와 같은 크기, 같은 채널 수를 갖는다
  • ksize:  블러링 커널 크기
  • anchor: 고정점 좌표. Point(-1, -1)을 지정하면 커널 중심을 고정점으로 사용한다.
  • borderType: 가장자리 픽셀 확장 방식
void blurring_mean()
{
	Mat src = imread("rose.bmp", IMREAD_GRAYSCALE);

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

	imshow("src", src);

	Mat dst;
	for (int ksize = 3; ksize <= 7; ksize += 2) {
		blur(src, dst, Size(ksize, 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();
	}	

	destroyAllWindows();
}
  • 13행: ksize 값이 3,5, 7이 되도록 for 반복문을 설정한다
  • 14행: ksize x ksize 크기의 평균값 필터 마스크를 이용하여 블러링을 수행한다
  • 16~18행:  사용된 평균값 필터의 크기를 무낮열 형태로 결과 영상 dst 위에 출력한다.

 

 

7.2.2 가우시안 필터

 

가우시안 필터(Gaussian filter): 평균값 필터보다 자연스러운 블러링 결과를 생성. 가우시안 분포 함수를 근사하여 생성한 필터 마스크를 사용하는 필터링 기법

 

가우시안 필터 마스크를 이용하여 마스크 연산을 수행한다는 것은 필터링 대상 픽셀 근처에는 가중치를 크게 주고, 필터링 대상 픽셀과 멀리 떨어져있는 주변부에는 가중치를 조금만 주어서 가중 평균 (weighted average)를 구하는 것과 같다. 즉, 가우시안 필터 마스크가 가중 평균을 구하기 위한 가중치 행렬 역할을 하는 것.

 

 

더보기

void GaussianBlur (InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT);

  • src: 입력 영상, 다채널 영상은 각 채널별로 블러링을 수행한다
  • dst: 출력 영상. src와 같은 크기, 같은 타입을 갖습니다.
  • ksize: 가우시안 커널 크기. ksize,width와 kszie.height는 0보다 큰 홀수이어야 합니다. ksize에 Size()를 지정하면 표준 편차로부터 커널 크기를 자동으로 결정합니다.
  • sigmaX: x 방향으로의 가우시안 커널 표준 편차
  • sigmaY: y 방향으로의 가우시안 커널 표준 편차. 만약 sigmaY = 0 이면 sigmaX와 같은 값을 사용한다. 만약 sigmaX와 sigmaY 가 모두 0이면 ksize의 width와 height 값으로부터 표준 편차를 계산하여 사용한다
  • borderType: 가장자리 픽셀 확장 방식

 

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

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

	imshow("src", src);

	Mat dst;
	for (int sigma = 1; sigma <= 5; sigma++) {
		GaussianBlur(src, dst, Size(0, 0), (double)sigma);

		String desc = format("Gaussian: sigma = %d", sigma);
		putText(dst, desc, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1.0, 
				Scalar(255), 1, LINE_AA);

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

	destroyAllWindows();
}
  • 13~20행: sigma 값을 1부터 5까지 증가시키면서 가우시안 블러링을 수행하고 그 결과를 화면에 나타냅니다.
  • 14행: src영상에 가우시안 표준 편차가 sigma인 가우시안 블러링을 수행하고 그 결과를 dst에 저장합니다
  • 16~18행: 사용한 가우시안 표준 편차 (sigma)값을 결과 영상 dst 위에 출력합니다.

 

 

 

더보기

Mat getGaussianKernel(int kszie, double sigma, int ktype = CV_64F);

 

  • ksize: 커널 크기. ksize는 0보다 큰 홀수여야한다
  • sigma: 가우시안 표준 편차. 만약 0 또는 음수를 지정하면 sigma = 0.4*((ksize-1)*0.5 - 1) + 0.8의 형태로 sigma를 계산
  • ktype: 필터의 타입. CV_32F 또는 CV_64F
  • 반환값: ksize x 1 크기의 가우시안 필터 커널

 

7.3 샤프닝: 영상 날카롭게 하기

 

7.3.1 언샤프 마스크 필터

 

샤프닝: 영상을 날카로운 느낌이 나도록 변경하는 필터링 기법. 날카로운 느낌: 초점이 잘 맞은 사진처럼 객체의 윤곽이 뚜렷하게 구분되는 영상을 의미한다. 

 

이를 위해서는 영상 에지 근방에서 픽셀 값의 명암비가 커지도록 수정해야한다.

 

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

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

	imshow("src", src);

	for (int sigma = 1; sigma <= 5; sigma++) {
		Mat blurred;
		GaussianBlur(src, blurred, Size(), sigma);

		float alpha = 1.f;
		Mat dst = (1 + alpha) * src - alpha * blurred;

		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();
}
  • 12~24행: 가우시안 필터의 표준 편차 sigma 값을 1부터 5까지 증가시키면서 언샤프 마스크 필터링을 수행한다
  • 13~14행: 가우시안 필터를 이용한 블러링 영상을 blurred에 저장한다
  • 16~17행: 언샤프 마스크 필터링을 수행한다
  • 19~21행: 샤프닝 결과 영상 dst에 사용된 sigma 값을 출력한다.

 

 

7.4 잡음 제거 필터링

 

7.4.1 영상과 잡음 모델

 

잡음: 원본 신호에 추가된 원치 않은 신호. 디지털 카메라에서 사진을 촬영하는 경우에는 광학적 신호를 전기적 신호로 변화하는 센서에서 주로 잡음이 추가된다. 

 

 

더보기

void randn(InputOutputArray dst, InputArray mean, InputArray stddev);

  • dst: 가우시안 난수로 채워질 행렬. dst 행렬은 미리 할당되어 있어야 한다
  • mean: 가우시안 분포 평균
  • stddev: 가우시안 분포 표준 편차

 

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

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

	imshow("src", src);

	for (int stddev = 10; stddev <= 30; stddev += 10) {
		Mat noise(src.size(), CV_32SC1);
		randn(noise, 0, stddev);

		Mat dst;
		add(src, noise, dst, Mat(), CV_8U);

		String desc = format("stddev = %d", stddev);
		putText(dst, desc, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255), 1, LINE_AA);
		imshow("dst", dst);
		waitKey();
	}

	destroyAllWindows();
}
  • 3행: lenna.bmp  파일을 그레이스케일 형식으로 불러와 src에 저장
  • 12행: 표준 편차 stddev 값이 10, 20, 30이 되도록 for 반복문을 수행
  • 13~14행: 평균이 0이고 표준 편차가  stddev인 가우시안 잡음을 생성하여 noise 행렬에 저장. 이때 noise 행렬은 부호 있는 정수형 (CV_32SC1)을 사용하도록 미리 생성하여 randn() 함수에 전달
  •  17행: 입력 영상 src에 가우시안 잡음 noise 를 더하여 결과 영상 dst 를 생성. dst 영상의 깊이는 CV_8U로 설정

 

 

7.4.2 양방향 필터

 

양방향 필터 수식의 경우 픽셀 값의 차이에 의존적이기 때문에 양방향 필터 마스크는 영상의 모든 픽셀에서 서로 다른 형태를 갖게 된다. 즉 모든 픽셀 위치에서 주변 픽셀과의 밝기 차이에 의한 고유의 필터 마스크 행렬을 만들어서 마스크 연산을 수행해야 한다. 이는 일반적인 가우시안 블러링이 모든 위치에서 일정한 가우시안 마스크 행렬을 사용하는 것과 차이가 있다. 그러므로 양방향 필터는 가우시안 블러링보다 훨씬 많은 연산량을 필요로 한다.

 

 

더보기

void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT);

  • src: 입력 영상. 8비트 또는 실수형. 1채널 또는 3채널 영상
  • dst: 출력 영상. src와 같은 크기, 같은 타입을 갖는다
  • d: 필터링에 사용할 이웃 픽셀과의 거리, 양수가 아닌 값을 지정하면 sigmaSpace로부터 자동 계산된다
  • sigmaColor: 색 공간에서의 가우시안 필터 표준 편차
  • sigmaSpace: 좌표 공간에서의 가우시안 필터 표준 편차
  • borderType: 가장자리 픽셀 확장 방식

 

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

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

	Mat noise(src.size(), CV_32SC1);
	randn(noise, 0, 5);
	add(src, noise, src, Mat(), CV_8U);

	Mat dst1;
	GaussianBlur(src, dst1, Size(), 5);

	Mat dst2;
	bilateralFilter(src, dst2, -1, 10, 5);

	imshow("src", src);
	imshow("dst1", dst1);
	imshow("dst2", dst2);

	waitKey();
	destroyAllWindows();
}
  • 10~12행: 그레이스케일 레나 영상 src에 평균이 0이고, 표준 편차가 5인 가우시안 잡음을 추가
  • 14~15행: 표준 편차가 5인 가우시안 필터링을 수행하여 dst1에 저장
  • 17~18행: 색 공간의 표준 편차는 10. 좌표 공간의 표준 편차는 5를 사용하는 양방향 필터링을 수행하여 dst2에 저장
  • 20~22행: src, dst1, dst2 영상을 모두 화면에 출력

 

 

 

 

7.4.3 미디언 필터

 

미디언 필터: 입력 영상에서 자기 자신 픽셀과 주변 픽셀 값 중에서 중간값을 선택하여 결과 영상 픽셀 값으로 설정하는 필터링 기법

 

void medianBlur(InputArray src, OutputArray dst, int ksize);
  • src: 입력 영상. 1, 3, 4채널 영상. ksize가 3 또는 5이면 CV_8U, CV_16U, CV_32F의 깊이를 가질 수 있고, 그 이상의 필터 크기에서는 CV_8U 깊이만 사용할 수 있다.
  • dst: 출력 영상. src와 같은 크기, 같은 타입을 갖는다
  • ksize: 필터 크기. 3과 같거나 큰 홀수를 지정

 

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

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

	int num = (int)(src.total() * 0.1);
	for (int i = 0; i < num; i++) {
		int x = rand() % src.cols;
		int y = rand() % src.rows;
		src.at<uchar>(y, x) = (i % 2) * 255;
	}

	Mat dst1;
	GaussianBlur(src, dst1, Size(), 1);

	Mat dst2;
	medianBlur(src, dst2, 3);

	imshow("src", src);
	imshow("dst1", dst1);
	imshow("dst2", dst2);

	waitKey();
	destroyAllWindows();
}
  • 10~15행: src영상에서 10%에 해당하는 픽셀 값을 0 또는 255로 설정
  • 17~18행: 표준 편차가 1인 가우시안 필터링을 수행하여 dst1에 저장
  • 20~21행: 크기가 3인 미디언 필터를 실행하여 dst2에 저장
  • 23~25행: src, dst1, dst2 영상을 모두 화면에 출력