1. ホーム
  2. OpenCV

OpenCV - 理想的なハイパスフィルタとローパスフィルタ (C++)

2022-02-09 13:13:16

著者 スティーブン・ザイ ティエンバオ
著作権について 著作権は著者に帰属します。商業的な複製は著者に、非商業的な複製は出典を明記してください。

シナリオの要件

       画像処理を行う上で、フィルタリングはお家芸です。今日は、理想的なフィルタの最も基本的な実装を紹介します。

       ご存知のように、スペクトルにおいて、低周波は主に滑らかな領域の画像全体の濃淡分布に対応し、高周波はエッジやノイズなど画像の細かい部分に対応します。理想的なフィルタの原理は、カットオフ点として遮断周波数Dを使用することです、ハイパスは、スペクトル成分の遮断周波数が0になるよりも小さい、ローパスは、スペクトル成分の遮断周波数が0になるよりも大きく、式と特定の原理が表示されません、Baiduはすべて持って、次は難しいことです - C + +& OpenCVコードの実装です。

該当する関数のC++実装コード

// Ideal low-pass filter
cv::Mat ideal_low_pass_filter(cv::Mat &src, float sigma)
{
	cv::Mat padded = image_make_border(src);
	cv::Mat ideal_kernel = ideal_low_kernel(padded, sigma);
	cv::Mat result = frequency_filter(padded, ideal_kernel);
	return result;
}

// ideal low-pass filter kernel function
cv::Mat ideal_low_kernel(cv::Mat &scr, float sigma)
{
	cv::Mat ideal_low_pass(scr.size(), CV_32FC1); //, CV_32FC1
	float d0 = sigma;// the smaller the radius D0, the larger the blur; the larger the radius D0, the smaller the blur
	for (int i = 0; i < scr.rows; i++) {
		for (int j = 0; j < scr.cols; j++) {
			float d = sqrt(pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2));//numerator, compute pow must be float type
			if (d <= d0) {
				ideal_low_pass.at<float>(i, j) = 1;
			}
			else {
				ideal_low_pass.at<float>(i, j) = 0;
			}
		}
	}
	return ideal_low_pass;
}

// ideal_high_pass_filter
cv::Mat ideal_high_pass_filter(cv::Mat &src, float sigma)
{
	cv::Mat padded = image_make_border(src);
	cv::Mat ideal_kernel = ideal_high_kernel(padded, sigma);
	cv::Mat result = frequency_filter(padded, ideal_kernel);
	return result;
}

// Ideal high-pass filter kernel function
cv::Mat ideal_high_kernel(cv::Mat &scr, float sigma)
{
	cv::Mat ideal_high_pass(scr.size(), CV_32FC1); //, CV_32FC1
	float d0 = sigma;// the smaller the radius D0, the larger the blur; the larger the radius D0, the smaller the blur
	for (int i = 0; i < scr.rows; i++) {
		for (int j = 0; j < scr.cols; j++) {
			float d = sqrt(pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2));//numerator, compute pow must be float type
			if (d <= d0) {
				ideal_high_pass.at<float>(i, j) = 0;
			}
			else {
				ideal_high_pass.at<float>(i, j) = 1;
			}
		}
	}
	return ideal_high_pass;
}

// Frequency filtering
cv::Mat frequency_filter(cv::Mat &scr, cv::Mat &blur)
{
	cv::Mat mask = scr == scr;
	scr.setTo(0.0f, ~mask);

	//create channel, store real and imaginary parts after dft (CV_32F, must be a single channel number)
	cv::Mat plane[] = { scr.clone(), cv::Mat::zeros(scr.size() , CV_32FC1) };

	cv::Mat complexIm;
	cv::merge(plane, 2, complexIm); // merge channels (merge two matrices into a 2-channel Mat-like container)
	cv::dft(complexIm, complexIm); // perform Fourier transform and save the result in itself

	// separate channels (array separation)
	cv::split(complexIm, plane);

	// The following operation is frequency domain migration
	fftshift(plane[0], plane[1]);

	// product of ***************** filter function and DFT result ****************
	cv::Mat blur_r, blur_i, BLUR;
	cv::multiply(plane[0], blur, blur_r); // filter (real part multiplied by the corresponding element of the filter template)
	cv::multiply(plane[1], blur, blur_i); // filter (the imaginary part is multiplied by the corresponding element of the filter template)
	cv::Mat plane1[] = { blur_r, blur_i };

	// move back again for inversion
	fftshift(plane1[0], plane1[1]);
	cv::merge(plane1, 2, BLUR); // merge the real part with the imaginary part

	cv::idft(BLUR, BLUR); // idft also results in a complex number
	BLUR = BLUR / BLUR.rows / BLUR.cols;

	cv::split(BLUR, plane); // separate channels, mainly to get channels

	return plane[0];
}

// Image boundary processing
cv::Mat image_make_border(cv::Mat &src)
{
	int w = cv::getOptimalDFTSize(src.cols); // Get the optimal width of the DFT transform
	int h = cv::getOptimalDFTSize(src.rows); // Get the optimal height of the DFT transformation

	cv::Mat padded;
	// constant method to expand image boundaries, constant = 0
	cv::copyMakeBorder(src, padded, 0, h - src.rows, 0, w - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	padded.convertTo(padded, CV_32FC1);

	return padded;
}

// fft transform followed by spectrum shift
void fftshift(cv::Mat &plane0, cv::Mat &plane1)
{
	// The following operation moves the image (zero frequency shift to the center)
	int cx = plane0.cols / 2;
	int cy = plane0.rows / 2;
	cv::Mat part1_r(plane0, cv::Rect(0, 0, cx, cy)); // element coordinates are represented as (cx, cy)
	cv::Mat part2_r(plane0, cv::Rect(cx, 0, cx, cy));
	cv::Mat part3_r(plane0, cv::Rect(0, cy, cx, cy));
	cv::Mat part4_r(plane0, cv::Rect(cx, cy, cx, cy));

	cv::Mat temp;
	part1_r.copyTo(temp); //swap top-left and bottom-right positions (real part)
	part4_r.copyTo(part1_r);
	temp.copyTo(part4_r);

	part2_r.copyTo(temp); //exchange position between top-right and bottom-left (real part)
	part3_r.copyTo(part2_r);
	temp.copyTo(part3_r);

	cv::Mat part1_i(plane1, cv::Rect(0, 0, cx, cy)); //element coordinates (cx,cy)
	cv::Mat part2_i(plane1, cv::Rect(cx, 0, cx, cy));
	cv::Mat part3_i(plane1, cv::Rect(0, cy, cx, cy));
	cv::Mat part4_i(plane1, cv::Rect(cx, cy, cx, cy));

	part1_i.copyTo(temp); //swap top-left and bottom-right positions (imaginary part)
	part4_i.copyTo(part1_i);
	temp.copyTo(part4_i);

	part2_i.copyTo(temp); //exchange position between upper right and lower left (imaginary part)
	part3_i.copyTo(part2_i);
	temp.copyTo(part3_i);
}


テストコード

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

cv::Mat ideal_low_kernel(cv::Mat &scr, float sigma);
cv::Mat ideal_low_pass_filter(cv::Mat &src, float sigma);
cv::Mat ideal_high_kernel(cv::Mat &scr, float sigma);
cv::Mat ideal_high_pass_filter(cv::Mat &src, float sigma);
cv::Mat frequency_filter(cv::Mat &scr, cv::Mat &blur);
cv::Mat image_make_border(cv::Mat &src);
void fftshift(cv::Mat &plane0, cv::Mat &plane1);

int main(void)
{
	Mat test = imread("tangsan.jpg", 0);
	float D0 = 50.0f;
	float D1 = 5.0f;
	Mat lowpass = ideal_low_pass_filter(test, D0);
	Mat highpass = ideal_high_pass_filter(test, D1);

	imshow("original", test);
	imshow("low pass", lowpass / 255);
	imshow("high pass", highpass/255);
	waitKey(0);

	system("pause");
	return 0;
}

// ideal low-pass filter
cv::Mat ideal_low_pass_filter(cv::Mat &src, float sigma)
{
	cv::Mat padded = image_make_border(src);
	cv::Mat ideal_kernel = ideal_low_kernel(padded, sigma);
	cv::Mat result = frequency_filter(padded, ideal_kernel);
	return result;
}

// ideal low-pass filter kernel function
cv::Mat ideal_low_kernel(cv::Mat &scr, float sigma)
{
	cv::Mat ideal_low_pass(scr.size(), CV_32FC1); //, CV_32FC1
	float d0 = sigma;// the smaller the radius D0, the larger the blur; the larger the radius D0, the smaller the blur
	for (int i = 0; i < scr.rows; i++) {
		for (int j = 0; j < scr.cols; j++) {
			float d = sqrt(pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2));//numerator, compute pow must be float type
			if (d <= d0) {
				ideal_low_pass.at<float>(i, j) = 1;
			}
			else {
				ideal_low_pass.at<float>(i, j) = 0;
			}
		}
	}
	return ideal_low_pass;
}

// Ideal high pass filter kernel function
cv::Mat ideal_high_kernel(cv::Mat &scr, float sigma)
{
	cv::Mat ideal_high_pass(scr.size(), CV_32FC1); //, CV_32FC1
	float d0 = sigma;// the smaller the radius D0, the larger the blur; the larger the radius D0, the smaller the blur
	for (int i = 0; i < scr.rows; i++) {
		for (int j = 0; j < scr.cols; j++) {
			float d = sqrt(pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2));//numerator, compute pow must be float type
			if (d <= d0) {
				ideal_high_pass.at<float>(i, j) = 0;
			}
			else {
				ideal_high_pass.at<float>(i, j) = 1;
			}
		}
	}
	return ideal_high_pass;
}

// ideal high pass filter
cv::Mat ideal_high_pass_filter(cv::Mat &src, float sigma)
{
	cv::Mat padded = image_make_border(src);
	cv::Mat ideal_kernel = ideal_high_kernel(padded, sigma);
	cv::Mat result = frequency_filter(padded, ideal_kernel);
	return result;
}

// Frequency filtering
cv::Mat frequency_filter(cv::Mat &scr, cv::Mat &blur)
{
	cv::Mat mask = scr == scr;
	scr.setTo(0.0f, ~mask);

	//create channel, store real and imaginary parts after dft (CV_32F, must be a single channel number)
	cv::Mat plane[] = { scr.clone(), cv::Mat::zeros(scr.size() , CV_32FC1) };

	cv::Mat complexIm;
	cv::merge(plane, 2, complexIm); // merge channels (merge two matrices into a 2-channel Mat-like container)
	cv::dft(complexIm, complexIm); // perform Fourier transform and save the result in itself

	// separate channels (array separation)
	cv::split(complexIm, plane);

	// The following operation is frequency domain migration
	fftshift(plane[0], plane[1]);

	// product of ***************** filter function and DFT result ****************
	cv::Mat blur_r, blur_i, BLUR;
	cv::multiply(plane[0], blur, blur_r); // filter (real part multiplied by the corresponding element of the filter template)
	cv::multiply(plane[1], blur, blur_i); // filter (the imaginary part is multiplied by the corresponding element of the filter template)
	cv::Mat plane1[] = { blur_r, blur_i };

	// move back again for inversion
	fftshift(plane1[0], plane1[1]);
	cv::merge(plane1, 2, BLUR); // merge the real part with the imaginary part

	cv::idft(BLUR, BLUR); // idft also results in a complex number
	BLUR = BLUR / BLUR.rows / BLUR.cols;

	cv::split(BLUR, plane); // separate channels, mainly to get channels

	return plane[0];
}

// Image boundary processing
cv::Mat image_make_border(cv::Mat &src)
{
	int w = cv::getOptimalDFTSize(src.cols); // Get the optimal width of the DFT transform
	int h = cv::getOptimalDFTSize(src.rows); // get the optimal height of the DFT transformation

	cv::Mat padded;
	// constant method to expand image boundaries, constant = 0
	cv::copyMakeBorder(src, padded, 0, h - src.rows, 0, w - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	padded.convertTo(padded, CV_32FC1);

	return padded;
}

// fft transform followed by spectrum shift
void fftshift(cv::Mat &plane0, cv::Mat &plane1)
{
	// The following operation moves the image (zero frequency shift to the center)
	int cx = plane0.cols / 2;
	int cy = plane0.rows / 2;
	cv::Mat part1_r(plane0, cv::Rect(0, 0, cx, cy)); // element coordinates are represented as (cx, cy)
	cv::Mat part2_r(plane0, cv::Rect(cx, 0, cx, cy));
	cv::Mat part3_r(plane0, cv::Rect(0, cy, cx, cy));
	cv::Mat part4_r(plane0, cv::Rect(cx, cy, cx, cy));

	cv::Mat temp;
	part1_r.copyTo(temp); //swap top-left and bottom-right positions (real part)
	part4_r.copyTo(part1_r);
	temp.copyTo(part4_r);

	part2_r.copyTo(temp); //exchange position between top-right and bottom-left (real part)
	part3_r.copyTo(part2_r);
	temp.copyTo(part3_r);

	cv::Mat part1_i(plane1, cv::Rect(0, 0, cx, cy)); //element coordinates (cx,cy)
	cv::Mat part2_i(plane1, cv::Rect(cx, 0, cx, cy));
	cv::Mat part3_i(plane1, cv::Rect(0, cy, cx, cy));
	cv::Mat part4_i(plane1, cv::Rect(cx, cy, cx, cy));

	part1_i.copyTo(temp); //swap top-left and bottom-right positions (imaginary part)
	part4_i.copyTo(part1_i);
	temp.copyTo(part4_i);

	part2_i.copyTo(temp); //exchange position between upper right and lower left (imaginary part)
	part3_i.copyTo(part2_i);
	temp.copyTo(part3_i);
}


テスト結果

図1 レンダリング

       ああ、私は以前に例外の結果の少ないこの部分のコードを書いた周波数ドメインスペクトルの移行は、修正されていることに注意してください〜場合は、上記の最新のコードの変更に従って、以前の参照エラーの学生、トラブルが発生します。

      また、私のコードに問題がある場合、我々は一緒に進歩することができますので、反対し、それを批判すること自由に感じるしてください。

       もしこの記事が役に立ったなら、「いいね!」で教えてくれたら嬉しいです~!乾杯