opencv C++ 频域滤波

1、频域滤波的步骤小结

1、给定一副大小为M*N的输入图像,首先得到填充参数P和Q,通常填充(满足)。

2、对添加必要数量的0,形成大小P*Q的填充图像。

3、添加的虚部,全部为0即可,合并成新的Mat对象

4、计算步骤3的图像的DFT,得到,这里要将进行变换。

5、根据相应的算法生成P*Q大小的滤波函数,用阵列相乘.

6、将进行变换,然后对进行IDFT变换。

2、填充图像

填充图像主要是将图像由MN大小填充到PN大小,这里要满足
填充常数0。这样做的主要原因是避免循环卷积中的缠绕错误。常见的填充大小为P=2M,Q=2N。

2.1、填充代码

void paddingPQ(Mat& src, Mat& dst)
{
	//要求输入一张单通道图片
	//输入图像f(x,y) 通常P=2M Q=2N 这里填充为常量0
	int M = src.rows;//高
	int N = src.cols;//宽
	cout << "M N" << M << " " << N << endl;
	dst = Mat::zeros(Size(2 * N, 2 * M), CV_8UC1);

	for (int i = 0; i < M; i++)
	{
		for (int j = 0; j < N; j++)
		{
			dst.at(i, j) = src.at(i, j);
		}
	}
}

2.2、填充结果

opencv C++ 频域滤波_第1张图片

3 灰度图像的DFT和IDFT变换

3.1 DFT

1、对于输入一张灰度图片如果是CV_8UC1的,先转换成CV_32FC1。
2、构建输入图片的虚部,这也是一个CV_32F的Mat对象,数据全0就可以了。
3、将实部和虚部合并成一个Mat对象,调用dft()函数。
4、对步骤3得到的乘以,主要是将四个角的低频部分转换到中间。
5、计算实部和虚部的幅度值,并用,转换灰度值的范围(这步主要是为了方便观察)。
6、展示处理结果。

3.2 DFT代码

void takeDFT(Mat& source, Mat& destination)
{
	// CV_8UC1 to CV_32FC1
	Mat originalFloat;
	source.convertTo(originalFloat, CV_32FC1, 1.0 / 255.0);
	// ready dft data complex;
	Mat originalComplex[2] = { originalFloat,Mat::zeros(originalFloat.size(),CV_32F) };
	Mat dftOriginal;
	merge(originalComplex, 2, dftOriginal);
	dft(dftOriginal, destination, DFT_COMPLEX_OUTPUT);
}
void showDFT(Mat& source)
{
	Mat sourceComplex[2];
	split(source, sourceComplex);
	Mat logReady;
	magnitude(sourceComplex[0], sourceComplex[1], logReady);
	logReady += Scalar::all(1);
	log(logReady, logReady);
	changeQuadrant(logReady);
	normalize(logReady, logReady, 0, 1, NORM_MINMAX);
	namedWindow("spectrum", WINDOW_FREERATIO);
	imshow("spectrum", logReady);
}
void changeQuadrant(Mat& source)
{
	//draw spectrum
	int cx = source.cols / 2;
	int cy = source.rows / 2;

	Mat q0(source, Rect(0, 0, cx, cy));
	Mat q1(source, Rect(cx, 0, cx, cy));
	Mat q2(source, Rect(0, cy, cx, cy));
	Mat q3(source, Rect(cx, cy, cx, cy));

	Mat tmp;
	q0.copyTo(tmp);
	q3.copyTo(q0);
	tmp.copyTo(q3);

	q1.copyTo(tmp);
	q2.copyTo(q1);
	tmp.copyTo(q2);
}

3.3、DFT效果

opencv C++ 频域滤波_第2张图片

4 IDFT

IDFT基本步骤
1、将包含实部和虚部的数据信息的Mat对象,调用下idft()函数就可以,后面实验都有我就不做单独演示。
注意:如果前面做了象限的转换(将低频转到中间)这个步骤,现在做一下逆过程。

5 理想低通滤波器

对于做了DFT变换的图像,高频主要是图像中的细节信息,如果做低通滤波,保留的就是直流分量。大致的讲就是让图片变得模糊。其实这也告诉我们空域的卷积和频域的点乘可以达到同样的效果。
在原点为圆心,以为半径的圆内,无衰减的通过所有的频率,而圆外阻断所有的频率的低通滤波器称为理想低通滤波器(ILPF),它的的定义为

是一个正常值,表示中心点到频域中心的距离。

具体步骤:
1、创建P*Q的理想低通滤波过滤器
2、频域滤波,然后IDFT变换到空域。

5.1 理想低通滤波器实现代码

void make_ILPE_filter(int width, int height, double D0,Mat& dst) 
{
	dst = Mat::zeros(Size(width, height), CV_32F);
	double D0_2 = pow(D0, 2);
	double half_h = height * (1.0) / 2;
	double half_w = width * (1.0) / 2;
	//创建理想低通滤波器
	for (int i = 0; i < height; i++) 
	{
		for (int j = 0; j < width; j++) 
		{
			double distance = pow((half_h - (double)i), 2) + pow((half_w - (double)j), 2);
			if (less()(distance,D0_2)) {
				dst.at(i, j) = 1;
			}
		}
	}
}

void iILPF(Mat& src, Mat& dst, double D0)
{
	//这里输入的src是刚通过DFT处理后的Mat 并没有进行其他的处理
	//先将低频高亮部分移到中间
	changeQuadrant(src);

	//Mat sourceComplex[2];
	//split(src, sourceComplex);

	//创建低通滤波器
	Mat ifilter;
	make_ILPE_filter(src.cols, src.rows, D0, ifilter);


	Mat sourceComplex[2];
	split(src, sourceComplex);

	Mat temp[2];
	multiply(sourceComplex[0], ifilter, temp[0]);
	multiply(sourceComplex[1], ifilter, temp[1]);

	Mat idft;
	merge(temp, 2, idft);
	//转换回相应的象限
	changeQuadrant(idft);

	dft(idft, dst, DFT_INVERSE | DFT_REAL_OUTPUT | DFT_SCALE);
	imshow("理想低通滤波", dst);
}

5.3理想低通滤波器实验效果

这里,很明显过滤掉了很多细节。

opencv C++ 频域滤波_第3张图片

6 理想高通滤波器

理想高通滤波器和理想低通滤波器功能相反,主要是保留图片的细节。
在原点为圆心,以为半径的圆内,阻断通过所有的频率,而圆外通过所有的频率的高通通滤波器称为理想高通滤波器(IHPF),它的的定义为

是一个正常值,截止频率,表示中心点到频域中心的距离

6.1、理想高通滤波器实现代码

void make_IHPE_filter(int width, int height, double D0, Mat& dst)
{
	dst = Mat::ones(Size(width, height), CV_32F);
	double D0_2 = pow(D0, 2);
	double half_h = height * (1.0) / 2;
	double half_w = width * (1.0) / 2;
	//创建理想高通滤波器
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			double distance = pow((half_h - (double)i), 2) + pow((half_w - (double)j), 2);
			if (less()(distance, D0_2)) {
				dst.at(i, j) = 0;
			}
		}
	}
}

void iHLPF(Mat& src, Mat& dst, double D1)
{
	//这里输入的src是刚通过DFT处理后的Mat 并没有进行其他的处理
	//先将低频高亮部分移到中间
	changeQuadrant(src);
	Mat ifilter;
	make_IHPE_filter(src.cols, src.rows, D1, ifilter);


	Mat sourceComplex[2];
	split(src, sourceComplex);

	Mat temp[2];
	multiply(sourceComplex[0], ifilter, temp[0]);
	multiply(sourceComplex[1], ifilter, temp[1]);

	Mat idft;
	merge(temp, 2, idft);
	//转换回相应的象限
	changeQuadrant(idft);

	dft(idft, dst, DFT_INVERSE | DFT_REAL_OUTPUT | DFT_SCALE);
	imshow("理想高通滤波", dst);
}

6.2、理想高通滤波器效果

这里截止频率

opencv C++ 频域滤波_第4张图片

7 布特沃斯高通滤波器

布特沃斯高通滤波器的定义:

布特沃斯高通滤波器比理想高通滤波器更平滑,这里的n是阶数,是截止频率。

7.1布特沃斯高通滤波器实现

void make_BHPF_filter(int width, int height, double D0,double n, Mat& dst)
{
	dst = Mat::ones(Size(width, height), CV_32F);
	double D0_2 = pow(D0, 2);
	double half_h = height * (1.0) / 2;
	double half_w = width * (1.0) / 2;
	//创建理想高通滤波器
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			double distance = pow((half_h - (double)i), 2) + pow((half_w - (double)j), 2);
			double temp = pow(D0 / sqrt(distance), 2 * n);
			dst.at(i, j) = 1.0 / (1.0 + temp);
		}
	}
}

void iBHPF(Mat& src, Mat& dst, double D1,double n) 
{
	//这里输入的src是刚通过DFT处理后的Mat 并没有进行其他的处理
	//先将低频高亮部分移到中间
	changeQuadrant(src);
	Mat ifilter;
	make_BHPF_filter(src.cols, src.rows, D1,n, ifilter);

	Mat sourceComplex[2];
	split(src, sourceComplex);

	Mat temp[2];
	multiply(sourceComplex[0], ifilter, temp[0]);
	multiply(sourceComplex[1], ifilter, temp[1]);

	Mat idft;
	merge(temp, 2, idft);
	//转换回相应的象限
	changeQuadrant(idft);

	dft(idft, dst, DFT_INVERSE | DFT_REAL_OUTPUT | DFT_SCALE);
	imshow("布特沃斯高通滤波", dst);
}

7.2 布特沃斯高通滤波器实验效果

n=2

opencv C++ 频域滤波_第5张图片

n=2

opencv C++ 频域滤波_第6张图片

n=2
opencv C++ 频域滤波_第7张图片

8 布特沃斯低通滤波器

布特沃斯低通滤波器的定义:

这里的n是阶数,是截止频率。BLPF传递函数并没有通过频率和滤除频率之间给出明显截止的急剧不连续性,基本上没有振铃现象。

8.1布特沃斯低通滤波器代码实现

void make_BIPF_filter(int width, int height, double D0, double n, Mat& dst)
{
	dst = Mat::ones(Size(width, height), CV_32F);
	double D0_2 = pow(D0, 2);
	double half_h = height * (1.0) / 2;
	double half_w = width * (1.0) / 2;
	//布特沃斯低通滤波器
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			double distance = pow((half_h - (double)i), 2) + pow((half_w - (double)j), 2);
			double temp = pow(sqrt(distance)/D0, 2 * n);
			dst.at(i, j) = 1.0 / (1.0 + temp);
		}
	}
}

void iBIPF(Mat& src, Mat& dst, double D1, double n)
{
	//这里输入的src是刚通过DFT处理后的Mat 并没有进行其他的处理
	//先将低频高亮部分移到中间
	changeQuadrant(src);
	Mat ifilter;
	make_BIPF_filter(src.cols, src.rows, D1, n, ifilter);

	Mat sourceComplex[2];
	split(src, sourceComplex);

	Mat temp[2];
	multiply(sourceComplex[0], ifilter, temp[0]);
	multiply(sourceComplex[1], ifilter, temp[1]);

	Mat idft;
	merge(temp, 2, idft);
	//转换回相应的象限
	changeQuadrant(idft);

	dft(idft, dst, DFT_INVERSE | DFT_REAL_OUTPUT | DFT_SCALE);
	imshow("布特沃斯低通滤波", dst);
}

8.2布特沃斯低通滤波器实验效果

n=2

opencv C++ 频域滤波_第8张图片

n=2

opencv C++ 频域滤波_第9张图片

n=2

opencv C++ 频域滤波_第10张图片

9、实验过程遇到的问题

1、虽然实验是自己做的也完成了,但是傅里叶变换的推导公式哪里还是有些懵懵的,之前学的时候搞懂了,现在又忘了。
2、实验中观察振铃现象,具体的频谱图我没记录下来,后面改进的时候将这部分代码单独拎出来。

完整代码地址

你可能感兴趣的:(opencv C++ 频域滤波)