我用fftw库对一维数组double array[ ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }进行FFT变换,但是得到的结果与matlab的结果不一样,两种fft结果前六个数据是一样的,后三个数据就很奇怪。原代码如下:
//****************************ifft********************************
double array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
out_cpx = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * size);
out = (double*)malloc(size * sizeof(double));
err = (double*)malloc(size * sizeof(double));
fft = fftw_plan_dft_r2c_1d(size, array, out_cpx, FFTW_ESTIMATE); //Setup fftw plan for fft
cout << "fft输出结果" << endl;
for (int k = 0; k < size; k++)
cout << "(" << out_cpx[k][0] << "," << out_cpx[k][1] << ") ";
ifft = fftw_plan_dft_c2r_1d(size, out_cpx, out, FFTW_ESTIMATE); //Setup fftw plan for ifft
for (i = 0; i < size; i++)
err[i] = (array[i] - out[i]);
printf("%f\t%f\n", (array[i]), out[i] / size);//需要做归一化处理
//***************************************ifft*********************
c++运行结果
matlab运行结果
matlab代码:
因为fftw将实数fft具有对称性,fftw库为了效率只保存了前半部分,后半部分与前半部分对称。故可以通过对称性求出另一部分。
项目场景:
C++中,对含有较多数据的二维实数矩阵Eigen::MatrixXd进行傅里叶变换时,ftw_plan_dft_r2c_2d()会比fftw_plan_dft_2d()快一些,但在使用ftw_plan_dft_r2c_2d()时,发现要输出与输入同等大小的矩阵时会缺失一些值(共轭值)。
问题描述
C++中,对含有较多数据的二维实数矩阵Eigen::MatrixXd进行傅里叶变换的时候,ftw_plan_dft_r2c_2d()如果输出与输入同等大小的矩阵时会缺失一些值(共轭值)。
1、C++使用fftw_plan_dft_2d()对实数矩阵进行傅里叶变换:先将MatrixXd赋值给MatrixXcd,然后再使用fftw_plan_dft_2d()。其输出结果和Matlab的结果相同。
#include <opencv2/core/eigen.hpp>
a << 11, 18, 20, 16, 10, 14,
MatrixXcd FTa(a.rows(), a.cols());
P = fftw_plan_dft_2d(a.cols(), a.rows(), (fftw_complex*)A.data(), (fftw_complex*)FTa.data(), FFTW_FORWARD, FFTW_ESTIMATE);
2、Matlab的fft2输出结果:
3、但是,在C++使用fftw_plan_dft_r2c_2d()对实数矩阵进行傅里叶变换的时候,其输出结果其实只有Matlab/fftw_plan_dft_2d()结果的前 int(实矩阵行数 / 2) + 1 行的数据。
#include <opencv2/core/eigen.hpp>
MatrixXd a(5, 6); //奇数行实矩阵
a << 11, 18, 20, 16, 10, 14,
MatrixXcd FTa(a.rows(), a.cols());
P = fftw_plan_dft_r2c_2d(a.cols(), a.rows(), a.data(), (fftw_complex*)FTa.data(), FFTW_ESTIMATE);
cout << FTa << endl; //输出只有matlab/fftw_plan_dft_2d()输出的前3行数据
上图C++输出的红框数据对应下图Matlab输出的红框数据。
4、补充:偶数行实矩阵的情况
MatrixXd aa(4, 6); //偶数行实矩阵
aa << 11, 18, 20, 16, 10, 14,
MatrixXcd FTaa(aa.rows(), aa.cols());
PP = fftw_plan_dft_r2c_2d(aa.cols(), aa.rows(), aa.data(), (fftw_complex*)FTaa.data(), FFTW_ESTIMATE);
解决方案:
实际上,fftw_plan_dft_r2c_2d()输出缺少的 ceil(double(实矩阵行数) / 2) - 1 行的数据对应的共轭部分已经全部包含在它的输出里。
以Matlab的输出展示,黄框为fftw_plan_dft_r2c_2d()输出的缺失行,红框为缺失行对应的共轭区域。 因此,只需要对红框的数据取共轭并排列得当获得黄框数据,就能补全fftw_plan_dft_r2c_2d()的输出。
偶数行实矩阵的傅里叶变换的共轭区域与上述奇数行实矩阵的傅里叶变换的共轭区域略有不同,如图所示:
观察可以发现,互为共轭的数据分为两部分,如蓝框所示:第1列部分与其余列部分。
第一部分的元素在列方向上共轭对称;第二部分中红框的元素取共轭,并使红框区域翻转180°即为黄框区域。
#include <opencv2/core/eigen.hpp>
MatrixXd a(5, 6); //奇数行实矩阵
a << 11, 18, 20, 16, 10, 14,
MatrixXd a(4, 6); //偶数行实矩阵
a << 11, 18, 20, 16, 10, 14,
MatrixXcd temp(a.rows()/2 + 1, a.cols());
P = fftw_plan_dft_r2c_2d(a.cols(), a.rows(), a.data(), (fftw_complex*)temp.data(), FFTW_ESTIMATE);
int ConjRows = ceil(double(a.rows()) / 2) - 1;
MatrixXcd FTa(a.rows(), a.cols());
FTa.topRows(temp.rows()) = temp;
FTa.bottomLeftCorner(ConjRows, 1) = ((temp.block(1, 0, ConjRows, 1)).conjugate()).colwise().reverse();
FTa.bottomRightCorner(ConjRows, a.cols() - 1) = (((temp.block(1, 1, ConjRows, a.cols() - 1)).conjugate()).rowwise().reverse()).colwise().reverse();