作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
函数原型
void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
void idft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
参数说明
- InputArray类型的src,输入图像,如Mat类型。
- OutputArray类型的dst,输出图像。
- int类型的flags,转换的标识符,默认为0,也就是使用标准的正向变换。
- int类型的nonzeroRows,默认值为0。如果该值设为非零值,dft函数会将该值作为非零行的有效区间长度,只对非零行进行处理,提高计算效率。
针对第三个参数flags,有如下几种常见的标识符,对此展开简单介绍:
- DFT_INVERSE:一维或二维逆变换。
- DFT_SCALE:缩放比例标识符,输出的结果会以1/N进行缩放。
- DFT_ROWS:对输入矩阵的每行进行正向或反向的变换,适用于处理多种矢量的场景,可减小资源开销。
- DFT_COMPLEX_OUTPUT:一维或二维实数数组正变换。
- DFT_REAL_OUTPUT:一维或二维复数数组反变换。
测试代码
#include<iostream>
#include<opencv2/opencv.hpp>
#include<ctime>
using namespace std;
using namespace cv;void fftshift(cv::Mat &plane0, cv::Mat &plane1);int main(void)
{Mat test = imread("test.jpg", 0);test.convertTo(test, CV_32FC1);//创建通道,存储dft后的实部与虚部(CV_32F,必须为单通道数)cv::Mat plane[] = { test.clone(), cv::Mat::zeros(test.size() , CV_32FC1) };cv::Mat complexIm;cv::merge(plane, 2, complexIm); // 合并通道 (把两个矩阵合并为一个2通道的Mat类容器)cv::dft(complexIm, complexIm, 0); // 进行傅立叶变换,结果保存在自身// 分离通道(数组分离)cv::split(complexIm, plane);// 以下的操作是频域迁移fftshift(plane[0], plane[1]);// 计算幅值cv::Mat mag,mag_log,mag_nor;cv::magnitude(plane[0], plane[1], mag);// 幅值对数化:log(1+m),便于观察频谱信息mag += Scalar::all(1);cv::log(mag, mag_log);cv::normalize(mag_log, mag_nor, 1,0, NORM_MINMAX);cv::Mat BLUR;// 再次搬移回来进行逆变换fftshift(plane[0], plane[1]);cv::merge(plane, 2, BLUR); // 实部与虚部合并cv::idft(BLUR, BLUR); // idft结果也为复数BLUR = BLUR / BLUR.rows / BLUR.cols;cv::split(BLUR, plane);//分离通道,主要获取通道imshow("original", test / 255);imshow("result", plane[0] / 255);waitKey(0);system("pause");return 0;
}// fft变换后进行频谱搬移
void fftshift(cv::Mat &plane0, cv::Mat &plane1)
{// 以下的操作是移动图像 (零频移到中心)int cx = plane0.cols / 2;int cy = plane0.rows / 2;cv::Mat part1_r(plane0, cv::Rect(0, 0, cx, cy)); // 元素坐标表示为(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); //左上与右下交换位置(实部)part4_r.copyTo(part1_r);temp.copyTo(part4_r);part2_r.copyTo(temp); //右上与左下交换位置(实部)part3_r.copyTo(part2_r);temp.copyTo(part3_r);cv::Mat part1_i(plane1, cv::Rect(0, 0, cx, cy)); //元素坐标(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); //左上与右下交换位置(虚部)part4_i.copyTo(part1_i);temp.copyTo(part4_i);part2_i.copyTo(temp); //右上与左下交换位置(虚部)part3_i.copyTo(part2_i);temp.copyTo(part3_i);
}
测试效果


我采用了最基础的dft函数(够用了,几乎很少有场景非要用其他模式的dft)进行傅里叶变换,傅里叶变换后将频谱的低频迁移至中心,再对数化进行观察,因为在频域没有进行任何额外的操作,所以反傅里叶变换后的结果同原图一致。
注意反傅里叶变换后数值要除以图像的宽*高~
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!