OpenCV实现傅里叶变换:从原理到代码详解
本文详解OpenCV中傅里叶变换的实现原理与步骤,包括图像尺寸优化、复数存储、幅值计算、对数缩放及象限重分布。提供完整的C++代码示例,展示如何将空域图像转换为频域频谱图,适用于图像分析与特征提取场景。
傅里叶变换的核心作用是将图像从空间域转换至频率域,通过将其分解为正弦和余弦分量来重构图像。基于数学原理,任何函数均可近似表示为无穷多个正弦与余弦函数的叠加,而二维图像的傅里叶变换正是实现这一转换的数学工具。

在上述公式中,f代表空域中的图像像素值,F则代表频域中的对应值。尽管变换结果为复数,无法直接通过单一真实图像或复数图像直观展示,但我们通常关注幅值图像,因为它包含了重建图像几何结构所需的全部关键信息。
为了可视化傅里叶变换的结果,需遵循特定的处理流程。
第一步是对图像进行尺寸扩展以优化性能。离散傅里叶变换(DFT)的计算效率高度依赖图像尺寸,当像素数为2、3或5的倍数时运算最快。因此,通常采用边界填充(Padding)技术,将图像扩展至最佳尺寸以提升处理速度。
第二步是为变换结果的实部和虚部分配存储空间。由于傅里叶变换输出为复数,且频域数据的动态范围远大于空间域,直接使用整型存储会导致精度丢失。因此,需将输入图像转换为浮点类型,并通过添加通道的方式存储复数值,确保数据完整性。
第三步是执行实际的傅里叶变换操作,利用OpenCV库函数完成从空域到频域的数学转换。
第四步是将复数结果转换为幅值。根据公式
计算DFT的幅值,该公式反映了频域中各频率成分的强度分布。
第五步是切换到对数刻度进行显示。由于傅里叶系数矩阵的动态范围极大,直接线性缩放会导致高频细节被掩盖(高值变白,低值变黑)。通过引入对数变换,可以将线性刻度转换为对数刻度,从而在屏幕上清晰展示从极小到极大的数值变化。

第六步是剪切与重分布幅度图像。首先去除之前扩展的边界像素,恢复原始尺寸。其次,为了可视化方便,需重新排列频谱图的四个象限,使原点(0,0)移动至图像中心,以便更直观地观察低频和高频成分。
第七步是归一化处理。由于计算得到的幅值图像数值范围可能超出显示器的显示能力,需通过归一化将数值映射到0到1的范围内,从而生成适合人类视觉观察的灰度图像。
以下是基于OpenCV C++接口的具体实现程序。
void _DFT(){
//1以灰度模式读取原图像并显示
Mat srcImage = imread("miFan.jpg",0);
if (!srcImage.data){ cout << "Error\n"; }
imshow("原图像", srcImage);
//2将输入图像扩展到最佳尺寸,边界用0补充
int m = getOptimalDFTSize(srcImage.rows);
int n = getOptimalDFTSize(srcImage.cols);
//将添加的像素初始化为0
Mat padded;
copyMakeBorder(srcImage, padded, 0, m - srcImage.rows,
0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));
//3为傅里叶变换的结果(实部和虚部)分配存储空间
//将数组组合合并为一个多通道数组
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//4进行傅里叶变换
dft(complexI, complexI);
//5将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
//将多通道数组分离为几个单通道数组
split(complexI, planes);//planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);
Mat magImage = planes[0];
//6进行对数尺度缩放
magImage += Scalar::all(1);
log(magImage, magImage);//求自然对数
//7剪切和重分布幅度图象限
//若有奇数行或奇数列,进行频谱剪裁
magImage = magImage(Rect(0, 0, magImage.cols&-2, magImage.rows&-2));
//重新排列傅立叶图像中的象限,使得原点位于图像中心
int cx = magImage.cols / 2;
int cy = magImage.rows / 2;
Mat q0(magImage, Rect(0, 0, cx, cy));
Mat q1(magImage, Rect(cx, 0, cx, cy));
Mat q2(magImage, Rect(0,cy,cx,cy));
Mat q3(magImage, 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);
//8归一化,用0到1的浮点值将矩阵变换为可视的图像格式
normalize(magImage, magImage, 0, 1, CV_MINMAX);
//9显示
imshow("频谱增幅", magImage);
waitKey();
}

经过上述处理后,得到的傅里叶变换频谱图像。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













