快速傅里叶变换
零、序言
0.1主要参考资料
[1]timothy sauer,裴玉茹.《数值分析(第二版)》[M]. 北京: 机械工业出版社, 2014.10 : P467~P415.
[2]冈萨雷斯.《数字图像处理(第四版)》[M]. 北京: 电子工业出版社, 2020.5: P138~P212.
[3]丘维声. 《高等代数》[M]. 北京: , : .
吐槽一句,简书的公示显示很不稳定,每次刷新都可能是不同的公式不能正常显示。
0.2目录
零、序言
一、复数
二、一维离散傅里叶变换
三、二维离散傅里叶变换
四、快速傅里叶变换
五、现有代码实验
六、cpu实现
七、gpu实现
八、总结
一、复数
复数表示:
模:
共轭复数:
欧拉公式:
在复平面上的单位圆上。
任一复数都可以用极坐标形式表示成,其中。
先复习下三角函数的公式:
复数乘法:
本原单位根:是次本原单位根。
二、一维离散傅里叶变换
离散傅里叶变换定义:的离散傅里叶变换(DFT)为维向量,其中
用矩阵表示如下
上式中的矩阵称为傅里叶矩阵,傅里叶矩阵是一个范德蒙矩阵。
傅里叶变换的矩阵为:
傅里叶逆变换的矩阵为
若方阵满足,则称是酉阵。傅里叶矩阵是酉阵。
例题就不搬了。
复数共轭和乘积运算时可交换的,即先共轭再相乘等于先相乘再共轭。
三、二维离散傅里叶变换
正式开始之前先吐槽一句,冈萨雷斯是真的难,太难了,看一个小时推导就头疼得要死。
顺便说一句,看推导的话,还请移步冈萨雷斯。
二维离散傅里叶变换:
二维离散傅里叶逆变换:
以上写法和冈萨雷斯有所出入,是为了和《数值分析》的表达形式统一。
而且这儿暂时不太好表示成的次方的形式,也不是,不好表示,而是若是要表示的话需要特地说明两个和分别是几次本原单位根,这步还是放到后面说。
运算可分性:
二维傅里叶变换:
其中,,下同。
同理,二维傅里叶逆变换:
这一部分理论来源于
矩阵运算形式:千万要注意转置:
二维离散傅里叶变换矩阵形式:
二维离散傅里叶逆变换矩阵形式:
也就是说,,二维傅里叶变换可以拆分成两步一维傅里叶变换进行运算。
写对也不容易啊,东西基本都忘得差不多了。
四、快速傅里叶变换
先看一个一维傅里叶变换的例子,计算.
这儿本原单位根。
把矩阵乘积算出来,但是偶数项写在前面,并且提取公因式,还要切记,有时候不要迷糊。
由于,可以得到
后面部分,冈萨雷斯和《数值分析》又不一样了,两个方法由差异,以冈萨雷斯为准好像又更好懂一些。
即,令以上部分又可以写成
如此迭代分治下去,就是cpu版本的快速傅里叶变换了。
五、现有代码实验
5.1 使用单位阵测试
输入矩阵为
5.1.1 MATLAB
Matlab
代码如下,其中,fft
是一维傅里叶变换,fft2
是二维傅里叶变换。
A = eye(4,4)
A_fft = fft(A)
A_fft2 = fft2(A)
得到结果如下:
A =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
A_fft =
1.0000 + 0.0000i 1.0000 + 0.0000i 1.0000 + 0.0000i 1.0000 + 0.0000i
1.0000 + 0.0000i 0.0000 - 1.0000i -1.0000 + 0.0000i 0.0000 + 1.0000i
1.0000 + 0.0000i -1.0000 + 0.0000i 1.0000 + 0.0000i -1.0000 + 0.0000i
1.0000 + 0.0000i 0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i
A_fft2 =
4 0 0 0
0 0 0 4
0 0 4 0
0 4 0 0
5.1.2 opencv的cpu版本
代码如下,放到函数里面就行,
{
Mat src = Mat::eye(Size(4,4),CV_32FC1);
Mat FFT;
dft(src , FFT, 0/*DFT_REAL_OUTPUT*/);
}
捕获fft.PNG结果
捕获cpufftcode.PNG结果是这样的原因是opencv输入实数矩阵的时候的输出时经过特殊编码的,编码方式如下,文档说明中说是按
ipp
的格式来编码的。可以参考opencv dft文档。
5.1.3 opencv的gpu版本
经过查看
cv::cuda::dft
的源码了解到,opencv
的cuda
加速的版本的fft
就是直接调用了Nvdia
的cuFFT
接口实现的。可以参考opencv cv:cuda::dft文档和cuFFT输入输出格式。
主要实现语句诸如以下几句
cufftPlan1d(&plan, dft_size_opt.width, dft_type, dft_size_opt.height);
cufftPlan2d(&plan, dft_size_opt.height, dft_size_opt.width, dft_type);
cufftExecC2C(plan, src_cont.ptr<cufftComplex>(), dst.ptr<cufftComplex>(),
is_inverse ? CUFFT_INVERSE : CUFFT_FORWARD);
cufftExecC2R(plan, src_cont.ptr<cufftComplex>(), dst.ptr<cufftReal>());
cufftExecR2C(plan, src_cont.ptr<cufftReal>(), dst.ptr<cufftComplex>());
调用也很简单
{
Mat src = Mat::eye(Size(4,4),CV_32FC1);
Mat FFT;
cuda::GpuMat gP, gFFT;
gP.upload(src);
cuda::dft(gP, gFFT, padded2.size(), 0);
gFFT.download(FFT);
}
捕获cufft.PNG得到的结果如下,列数的解释在cuFFT输入输出格式。
5.2 使用一般矩阵测试
输入矩阵
5.2.1 Matalb结果
A_fft =
2.0000 + 0.0000i 1.0000 + 0.0000i 1.0000 + 0.0000i 1.0000 + 0.0000i
1.0000 + 1.0000i 0.0000 - 1.0000i -1.0000 + 0.0000i 0.0000 + 1.0000i
0.0000 + 0.0000i -1.0000 + 0.0000i 1.0000 + 0.0000i -1.0000 + 0.0000i
1.0000 - 1.0000i 0.0000 + 1.0000i -1.0000 + 0.0000i 0.0000 - 1.0000i
A_fft2 =
5.0000 + 0.0000i 1.0000 + 0.0000i 1.0000 + 0.0000i 1.0000 + 0.0000i
0.0000 + 1.0000i 0.0000 + 1.0000i 0.0000 + 1.0000i 4.0000 + 1.0000i
-1.0000 + 0.0000i -1.0000 + 0.0000i 3.0000 + 0.0000i -1.0000 + 0.0000i
0.0000 - 1.0000i 4.0000 - 1.0000i 0.0000 - 1.0000i 0.0000 - 1.0000i
5.2.2 opencv cpu版本
捕获fft1.PNG5.2.3 opencv gpu版本
捕获cufft1.PNG六、cpu实现
应该会有一个别人的和一个自己改的的版本,至于opencv的版本写得过于复杂,引用依赖过多,就不拷贝过来做参考了。
6.1 蝴蝶操作
搁置
6.2
搁置
七、gpu实现
7.1 cuFFT头文件
首先说一下,据说
cuda
提供了cuFFT
头文件,可以测试下。也可以不用测试,毕竟opencv
的gpu
版本已经就是了。另外也有很好的参考资料 CUDA学习笔记3:CUFFT(CUDA提供了封装好的CUFFT库)的使用例子。
7.2 自行实现
搁置
可参考CUDA实现FFT快速傅里叶变换;
八、结论
各家提供的的结果编码方式差异好大,好坑。