我是靠谱客的博主 义气摩托,这篇文章主要介绍OpenCV实现Matlab的fft2、ifft2函数,现在分享给大家,希望可以做个参考。

看标题就知道跟我的上一篇博客差不多了,本来没打算写这篇的,因为之前在网上搜过,有现成的OpenCV实现fft2和ifft2代码,而且我也试过fft2确实和Matlab计算的结果一致。但是项目后来又用到了ifft2,这次发现计算结果跟Matlab怎么都对不上,实在没办法,我只能自己摸索,后面也对网上的fft2代码进行了完善,现在一起发出来。

要用OpenCV实现傅里叶变换,首先要知道复数在OpenCV中是如何表示的,然后才能理解fft2和ifft2的实现。

一、复数在OpenCV中的表示方式

OpenCV的mat类没有专门的复数数据类型,可以将两个二维的Mat,一个存储实部,一个存储虚部,merge在一起,形成存储虚数的Mat。或者直接定义类似CV_64FC2格式的Mat,如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 第一种方式,定义实部和虚部 Mat real = Mat::zeros(3, 3, CV_64F); Mat imag = Mat::zeros(3, 3, CV_64F); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { real.at<double>(i, j) = i * 3 + j; imag.at<double>(i, j) = i * 3 + j; } } Mat planes[2] = { real, imag }; Mat complex; merge(planes, 2, complex); // 第二种方式,直接定义“复数”格式的Mat Mat complex2 = Mat::zeros(3, 3, CV_64FC2); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { complex2.at<Vec2d>(i, j)[0] = i * 3 + j; complex2.at<Vec2d>(i, j)[1] = i * 3 + j; } }

虽然第二种方式更简捷,但OpenCV的Mat矩阵运算基本上都不支持复数格式的数据,往往需要把复数拆分成实部和虚部,再分别进行计算,所以第二种还是需要转换成第一种的形式。另外再补充一下,输出复数的两种方法,这样就能方便地和Matlab比较结果了。

复制代码
1
2
3
4
5
6
7
8
9
// 第一种输出实部、虚部、复数的方式(点坐标) cout << complex2.at<Vec2d>(i, j)[0]; cout << complex2.at<Vec2d>(i, j)[1]; cout << complex2.at<Vec2d>(i, j); // 第二种输出实部、虚部、复数的方式(复数格式) cout << complex2.at<std::complex<double>>(i, j).real(); cout << complex2.at<std::complex<double>>(i, j).imag(); cout << complex2.at<std::complex<double>>(i, j);

注意at指针后面<>里面的内容,本文统一采用的是double格式数据。其实CV_64FC2的每个元素就是点坐标,点坐标的第一个值对应实部,第二个对应虚部。比较起来第二种方式更直观,但是写法稍复杂一点。另外第一种和第二种输出复数的表现形式有一点差别,如下图所示:
这里写图片描述

二、fft2的实现

OpenCV有自带的实现离散傅里叶函数:dft,但如果直接使用,往往得到的结果不太正确。此时我们可以参考官方给出的案例,具体就是,当输入的Mat数据格式是CV_64F时,需要将其转换为CV_64FC2格式。所以这里我们就要做个判断,根据输入的Mat数据格式,选择不同的处理方式。判断数据格式可以采用Mat自带的type函数,当格式为CV_8U~CV_64F时,该函数返回值为0~6,当格式为CV_8UC2~CV_64FC2时,返回值为8~14。至此,下面给出OpenCV实现fft2函数的代码。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void fft2(const Mat &src, Mat &Fourier) { int mat_type = src.type(); assert(mat_type<15); //不支持的数据格式 if (mat_type < 7) { Mat planes[] = { Mat_<double>(src), Mat::zeros(src.size(),CV_64F) }; merge(planes, 2, Fourier); dft(Fourier, Fourier); } else // 7<mat_type<15 { Mat tmp; dft(src, tmp); vector<Mat> planes; split(tmp, planes); magnitude(planes[0], planes[1], planes[0]); //将复数转化为幅值 Fourier = planes[0]; } }

三、ifft2的实现

ifft2和fft2十分类似,唯一的不同就是调用dft函数时使用的参数,一定要加上DFT_SCALE,对结果进行缩放,这样才能达到与Matlab的ifft2函数相同的效果。下面是具体代码。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void ifft2(const Mat &src, Mat &Fourier) { int mat_type = src.type(); assert(mat_type<15); //不支持的数据格式 if (mat_type < 7) { Mat planes[] = { Mat_<double>(src), Mat::zeros(src.size(),CV_64F) }; merge(planes, 2, Fourier); dft(Fourier, Fourier, DFT_INVERSE + DFT_SCALE, 0); } else // 7<mat_type<15 { Mat tmp; dft(src, tmp, DFT_INVERSE + DFT_SCALE, 0); vector<Mat> planes; split(tmp, planes); magnitude(planes[0], planes[1], planes[0]); //将复数转化为幅值 Fourier = planes[0]; } }

PS

需要注意的是,fft2和ifft2返回的结果都是CV_64F(double)类型的,自己可以根据需要改成float型。同样地,相关的代码已经更新到了我的Github上,欢迎提意见。另外再多说几句,目前这已经是第四篇个人原创技术博客了,可是四篇的浏览量加起来才100出头,简直是在自言自语,略尴尬。自我感觉写的东西还是有点用的,特别是Matlab代码转OpenCV的时候,希望等百度能搜到我的博客后,情况会好吧。

最后

以上就是义气摩托最近收集整理的关于OpenCV实现Matlab的fft2、ifft2函数的全部内容,更多相关OpenCV实现Matlab内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(92)

评论列表共有 0 条评论

立即
投稿
返回
顶部