C++|OpenCV 离散傅里叶变换在图像处理中的应用学习笔记

傅里叶变换的物理意义

  • 傅里叶原理:
    任何连续测量的时序或信号,都可以表示为不同频率的正弦波信号的无限叠加
  • 图像傅里叶变换的物理意义
    对一张图像使用傅里叶变换就是将它分解成正弦和余弦两部分,将图像从空间域转换到频域
    在图像处理中,频域反应图像在空域灰度变化剧烈程度,也就是图像灰度的变化速度,也就是图像梯度大小
    图像的频率是表征图像中灰度变化剧烈程度的指标,是灰度在平面空间上的梯度
    设 f 是一个能量有限的模拟信号,其傅里叶变换表示的就是 f 谱,从数学意义上是将一个函数转换为一系列周期函数来处理, 从物理意义上是将图像的灰度分布函数变换为图像的频率分布函数
  • 举例:
    大面积的沙漠在图像中是一片灰度变化缓慢的区域,对应频率值很低
    对于地表属性变换剧烈的边缘区域在图像中是一片灰度变化剧烈的区域,对应频率值很高
  • 对图像进行二维傅里叶变换得到的频谱图就是图像梯度的分布图, 频谱图上的各点与图像上的各点并不存在一一对应关系。傅里叶频谱图上所看到的明亮不一的亮点实际上是图像上某一点与邻域点差异的强弱,即梯度的大小,也即该点频率的大小
  • 梯度大则该点的亮度强,反之则弱。 通过观察傅里叶变换后的频谱图(功率图)可以看出能量分布。如果频谱图中暗点多,则实际图像比较柔和(因为各点与邻域差异都不大,梯度相对较小),反之图像尖锐、边界分明两边像素差异较大
  • 在频域里,对于一幅图像,高频部分代表图像的细节、纹理信息;低频部分代表图像的轮廓信息。 图像边缘部分是突变部分,变化较快,反映在频域上是高频分量,图像的噪声大部分情况下是高频部分;图像平缓变化部分则为低频分量。如果对一幅精细的图像使用低通滤波器,那么滤波后的结果就只剩下轮廓。如果图像受到的噪声恰好定位于某个特定的“频率”范围内,则可以通过滤波器来恢复原来的图像
应用
  1. 图像增强与图像去噪
    绝大部分噪音都是图像的高频分量,通过低通滤波器来滤除高频噪声;边缘也是图像的高频分量,可以通过添加图像的高频分量来增强原始图像的边缘
  2. 图像分割之边缘检测
    提取图像的高频分量
  3. 图像特征提取
    形状特征:傅里叶描述
    纹理特征:直接通过傅里叶系数来计算纹理特征
    其他特征:将提取的特征值进行傅里叶变换来使特征具有平移、伸缩、旋转不变性
  4. 图像压缩
    直接通过傅里叶系数来压缩数据,常用的离散余弦变换是傅里叶变换的实变换
类比理解 冈萨雷斯版《图像处理》解释:将傅里叶变换比作一个玻璃棱镜,棱镜是可以将光分解为不同颜色的物理仪器,每个成分的颜色由波长(或频率)来决定。傅里叶变换可以看作是数学上的棱镜,将函数基于频率分解为不同的成分。当我们考虑光时,讨论它的光谱或频率。同样,傅里叶变换能使我们通过频率成分来分析一个函数。
傅里叶变换性质
  • 线性
  • 对称性
  • 时移性:函数在时域中的时移,对应于其在频率域中附加产生的相移,而幅度频谱则保持不变
  • 频移性:函数在时域中乘以e^(jwt),可以使整个频谱搬移w
  • 卷积定理:时域卷积等于频域乘积;时域乘积等于频域卷积(附加一个系数)
信号在频率域的表现 在频域中,频率越大说明原始信号变化速度越快;频率越小说明原始信号越平缓
当频率为0时,表示直流信号,没有变化
高频分量解释信号的突变部分;低频分量决定决定信号的整体形象
dft() 函数详解 dft 函数的作用是对一维或者二维浮点数数组进行正向或反向离散傅里叶变换
void dft(InputArray src,OutputArray dst,int flags =0,int nonzeroRows=0)

【C++|OpenCV 离散傅里叶变换在图像处理中的应用学习笔记】第一个参数:InputArray 类型的 src ,输入矩阵,可以为实数或者虚数
第二个参数:OutputArray 类型的 dst,函数调用后的运算结果存在这里,其尺寸和类型取决于标识符,也就是第三个参数 flags
第三个参数:int 类型的 flags,转换的标识符,有默认值 0,取值可以为为下表:
C++|OpenCV 离散傅里叶变换在图像处理中的应用学习笔记
文章图片

第四个参数:int 类型的 nonzeroRows,默认值为 0,当此参数设为非零时,函数会假设只有输入矩阵的第一个非零行包含非零元素,或只有输出矩阵的一个非零行包含非零元素
#include "opencv2\core\core.hpp" #include "opencv2\imgproc\imgproc.hpp" #include "opencv2\highgui\highgui.hpp" #include using namespace std; using namespace cv; int main() { //以灰度模式读取原始图像并显示 Mat srcImage = imread("girl.jpg", 0); imshow("原始图像", srcImage); //将输入图像延扩到最佳尺寸,边界用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)); //为傅里叶变换的结果(实部和虚部)分配存储空间 //将planes数组组合合并成一个多通道的数组complexI Mat planes[] = { Mat_(padded), Mat::zeros(padded.size(),CV_32F) }; Mat complexI; merge(planes, 2, complexI); //进行就地离散傅里叶变换 dft(complexI, complexI); //将复数转换为幅值 split(complexI, planes); //将多通道数组complexI分离成几个单通道数组 magnitude(planes[0], planes[1], planes[0]); Mat magnitudeImage = planes[0]; //进行对数尺度缩放 magnitudeImage += Scalar::all(1); log(magnitudeImage, magnitudeImage); //剪切和重分布幅度图象限 //若有奇数行或奇数列,进行频谱裁剪 magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols& -2, magnitudeImage.rows& -2)); //重新排列傅里叶图像中的象限,使得原点位于图像中心 int cx = magnitudeImage.cols / 2; int cy = magnitudeImage.rows / 2; Mat q0(magnitudeImage, Rect(0, 0, cx, cy)); //ROI区域的左上 Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); //ROI区域的右上 Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); //ROI区域的左下 Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); //ROI区域的右下 //交换象限(左上与右下进行交换) Mat tmp; q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3); //交换象限(右上与左下进行交换) q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2); //归一化 normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX); //显示效果图 imshow("频谱幅值", magnitudeImage); waitKey(); return 0; }

    推荐阅读