본문 바로가기
  • AI 개발자가 될래요
컴퓨터비전

[C++/OpenCV] 주파수 영역 필터링 / 저주파 및 고주파 통과 필터링

by 꿀개 2022. 7. 20.

  영상을 주파수 영역으로 변환하면 화소의 밝기가 서서히 변화하는 저주파 영역과 급격하게 변화하는 고주파 영역을 공간 영역에 비하여 쉽게 분리할 수 있다. 이렇게 분리된 주파수 영역에 대해서 각 주파수 영역을 강화하거나 약화하거나 혹은 제거하는 등의 처리를 할 수 있다.

 

주파수 영역 필터링

주파수 영역에서 필터링 과정은 푸리에 변환 계수에 필터 행렬을 원소 간(element-wise) 곱하여 수행된다. 여기서 푸리에 변환 계수는 복소수이기 때문에 필터의 곱셈도 실수부와 허수부의 두 채널에 수행해야 한다. 필터를 어떻게 구성하느냐에 따라 저주파 통과 필터링, 고주파 통과 필터링 등을 구현할 수 있다.

푸리에 변환에 대한 자세한 이야기는 아래 링크에서 다루었으니 참고 바란다.

https://hsyaloe.tistory.com/31?category=926804 

 

[C++/OpenCV] 공간 주파수, 이산 푸리에 변환, DFT

영상을 데이터로 표현하는 데에는 크게 두 가지 영역으로 나누어 설명한다. 화소값이 직접 표현된 공간 영역과 우주 공간과 같은 변환 영역이다. 변환 영역은 직교 변환에 의해 얻어진 영상 데

hsyaloe.tistory.com

 

저주파 및 고주파 통과 필터링

   ● 저주파 통과 필터링

저주파 통과 필터링은 DFT 변환 영역에서 저주파 영역의 계수들은 통과시키고 그 이외의 영역 즉, 고주파 영역의 계수는 차단하는 것을 말한다.

 

그림 1. 주파수 스펙트럼

 

그림 1. 의 주파수 스펙트럼 영상을 보면 중심 부분이 저주파 영역이며, 외곽으로 갈수록 고주파 영역이다.

필터링은 주파수 계수에 필터 행렬의 원소가 곱해져서 수행된다. 따라서 저주파 필터의 모양은 그림 2. 처럼 중심에서 지정된 반지름만큼 원형으로 1의 값을 갖게 하고, 외곽 부분을 0으로 지정하면 된다.

 

그림 2. 저주파 통과 필터

 

   ● 고주파 통과 필터링

고주파 통과 필터링은 저주파 통과 필터와는 반대로 고주파 영역의 계수들을 통과시키고 저주파 영역의 계수들을 차단하는 것이다. 그림 3. 처럼 지정된 반지름 크기의 원형으로 0의 값을 갖게 하고, 가장자리 부분을 1로 지정하면 된다.

 

그림 3. 고주파 통과 필터

 

주파수 계수와 필터의 원소가 곱해지기 때문에 0의 값을 갖는 부분은 주파수 계수가 제거되어 차단되고, 1의 값을 갖는 부분은 그대로 유지되어 통과된다.

 

다음 코드는 주파수 영역에서 저주파 및 고주파 통과 필터링을 구현한 것이다.

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

void log_mag(Mat complex, Mat& dst) { //푸리에 변환을 수행하면 복소수의 행렬이 결과로 생성
	Mat planes[2]; //복소수의 실수부와 허수부를 벡터로 간주
	split(complex, planes); //2채널 행렬 분리
	magnitude(planes[0], planes[1], dst); //벡터의 크기
	log(dst + 1, dst);
	normalize(dst, dst, 0, 255, NORM_MINMAX); //정규화(저주파 영역과 고주파 영역의 계수값을 정규화)
	dst.convertTo(dst, CV_8U);
}

void shuffling(Mat mag_img, Mat& dst) {
	int cx = mag_img.cols / 2;
	int cy = mag_img.rows / 2;
	Rect q1(cx, 0, cx, cy); //1사분면
	Rect q2(0, 0, cx, cy); //2
	Rect q3(0, cy, cx, cy); //3
	Rect q4(cx, cy, cx, cy); //4

	dst = Mat(mag_img.size(), mag_img.type());
	mag_img(q1).copyTo(dst(q3)); //사분면 맞바꿈
	mag_img(q3).copyTo(dst(q1));
	mag_img(q2).copyTo(dst(q4));
	mag_img(q4).copyTo(dst(q2));
}

Mat zeropadding(Mat img) {  // 입력 영상에 영삽입을 수행해서 반환하는 함수
	int m = 1 << (int)ceil(log2(img.rows));  // 2의 자승 계싼
	int n = 1 << (int)ceil(log2(img.cols));
	Mat dst(m, n, img.type(), Scalar(0));

	Rect rect(Point(0, 0), img.size());  // 원본 영상 크기 관심 영역
	img.copyTo(dst(rect));  // 원본 영상을 관심 영역에 복사
	dst.convertTo(dst, CV_32F);
	return dst;
}

Mat get_lowpassFilter(Size size, int radius) {  // 저주파 통과 필터 생성 함수
	Point center = size / 2;  // 중심점 개선
	Mat filter(size, CV_32FC2, Vec2f(0, 0));  // 2채널 행렬 선언
	circle(filter, center, radius, Vec2f(1, 1), -1);
	/*Mat filter2(size, CV_8UC1, Scalar(0));
	circle(filter2, center, radius, Scalar(255), -1);
	imwrite("lowpass_filter.jpg", filter2);*/
	return filter;
}

Mat get_highpassFilter(Size size, int radius) {  // 고주파 통과 필터 생성 함수
	Point center = size / 2;
	Mat filter(size, CV_32FC2, Vec2f(1, 1));  
	circle(filter, center, radius, Vec2f(0, 0), -1);  
	/*Mat filter2(size, CV_8UC1, Scalar(255));
	circle(filter2, center, radius, Scalar(0), -1);
	imwrite("highpass_filter.jpg", filter2); */
	return filter;
}

void FFT(Mat image, Mat& dft_coef, Mat& dft_img) {
	Mat complex_img;
	Mat pad_img = zeropadding(image);
	Mat tmp[] = { pad_img, Mat::zeros(pad_img.size(), pad_img.type()) };
	merge(tmp, 2, complex_img);  // 복소 행렬 구성
	dft(complex_img, dft_coef, 0);
	shuffling(dft_coef, dft_coef);
	log_mag(dft_coef, dft_img);  // 주파수 스펙트럼 영상
}

Mat IFFT(Mat dft_coef, Size size) { // 역DFT
	Mat idft_coef, idft_img[2];
	shuffling(dft_coef, dft_coef);
	dft(dft_coef, idft_coef, DFT_INVERSE+DFT_SCALE);
	split(idft_coef, idft_img);

	Rect img_rect(Point(0, 0), size);
	idft_img[0](img_rect).convertTo(idft_img[0], CV_8U);
	return idft_img[0];
}

int main() {
	Mat image = imread("Lenna.png", 0);
	Rect img_rect(Point(0, 0), image.size());
	Mat dft_coef, dft_img, low_dft, high_dft, filtered_mat1, filtered_mat2;

	FFT(image, dft_coef, dft_img);  //FFT 수행
	Mat low_filter = get_lowpassFilter(dft_coef.size(), 50);  // 저주파 필터 생성
	Mat high_filter = get_highpassFilter(dft_coef.size(), 20);  // 고주파 필터 생성

	multiply(dft_coef, low_filter, filtered_mat1);  //필터링
	multiply(dft_coef, high_filter, filtered_mat2);
	log_mag(filtered_mat1, low_dft);  // 주파수 스펙트럼 생성
	log_mag(filtered_mat2, high_dft);

	imshow("image", image);
	imshow("dft_img", dft_img);
	imshow("low_dft", low_dft);
	imshow("high_dft", high_dft);
	imshow("low_passed_img", IFFT(filtered_mat1, image.size()));
	imshow("high_passed_img", IFFT(filtered_mat2, image.size()));
	waitKey();
	return 0;
}

 

결과

- 입력 영상

 

- 저주파 통과 필터링 수행 후 주파수 스펙트럼

 

- 고주파 통과 필터링 수행 후 주파수 스펙트럼

 

- 저주파 통과 필터링 수행 후 DFT 역변환 영상

   : 저주파만 통과되고 고주파 부분이 차단되어 경계 부분이 둔화된 부드러운 영상이 되었음

 

- 고주파 통과 필터링 수행 후 DFT 역변환 영상

   : 배경 부분이 제거되고 고주파 성분으로 경계 부분만 남음

 

 

 

 

 

* 이 글은 <opencv로 배우는 영상 처리 및 응용> (정성환, 생능출판) 책으로 공부하며 작성한 것이다.