原文:http://blog.csdn.net/ikerpeng/article/details/41845545?utm_source=tuicool&utm_medium=referral

我是做Tracking 的,对于速度要求很高。发现傅里叶变换可以使用。于是学习之!

核心: 最根本的一点就是将时域内的信号转移到频域里面。这样时域里的卷积可以转换为频域内的乘积!

在分析图像信号的频率特性时,对于一幅图像,直流分量表示预想的平均灰度,低频分量代表了大面积背景区域和缓慢变化部分,高频部分代表了它的边缘,细节,跳跃部分以及颗粒噪声.  因此,我们可以做相应的锐化和模糊的处理:提出其中的高频分量做傅里叶逆变换得到的就是锐化的结果。提出其中的低频分量做傅里叶逆变换得到的就是模糊的结果。

最不能理解的应该是:截取频域图中的任何一个区域对应的都是原来的整张图的区域,而不是对应的局部。

因为频域内的各个点都反映的是整张图的一个状态。我们可以用时间和频率来理解:当你走完一段单位路程的时候,假设你花了100秒,那么你的频率就是0.01HZ。这个0.01HZ显然体现的是一个整体的结果。而不是局部。我们再由公式来看:

可以很明显的知道频域内的每一个点的值都是由整个图像求出来的。当然以上得出的结果,我们一般只关注幅值频谱图。也就是说真正起作用的就是前面的那个cos x而已. 于是我们可以知道,在整个范围内(0<k <N, 0<l <N),低频分量集中于四个角。且其他地方的值只可能比这个小。在原点的傅里叶变换即等于图像的平均灰度级。因为 在原点处常常为零,F(0,0)有时称做 频率谱的直流成分。

使用:

当图像的尺寸是2,3,5的整数倍时,计算速度最快。因此OpenCV里面有一个函数:

int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // 在边缘添加0

它可以使得图片的尺寸可以满足这个要求。

但是这样就需要对原来的图像进行大小的处理,因此使用函数:CopyMakeBorder复制图像并且制作边界。(处理边界卷积)

Mat padded; 
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));

将原始的图像I 扩充为理想的大小放在padded里面。

接下来我们需要给计算出来的结果分配空间:

Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI);         // 为延扩后的图像增添一个初始化为0的通道

然后便可以进行傅里叶变换了:

dft(complexI, complexI);            // 变换结果很好的保存在原始矩阵中

得到的结果有两部分,实数部分和虚数部分,你可以分别对这两部分进行操作:

split(complexI, planes);                   // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];

当然还可以进行:归一化:

normalize(magI, magI, 0, 1, CV_MINMAX); // 将float类型的矩阵转换到可显示图像范围// (float [0, 1]).

另外重要的一个应用是: convolveDFT。

 其中的 *代表的是 卷积。我觉得这也是我们进行离散傅里叶变换的目的。使得计算的速度大大的增加。

先来说一下卷积在图像中的意义:

假设图像f(x),模板是g(x),然后将模版g(x)在模版中移动,每到一个位置,就把f(x)与g(x)的定义域相交的元素进行乘积并且求和,得出新的图像一点,就是被卷积后的图像. 模版又称为卷积核.卷积核做一个矩阵的形状.(当然边缘点可能需要特殊的处理,同时这个操作和滤波也很像,也许就是一回事)。

[cpp] view plaincopyprint?
  1. #include "opencv2/core/core.hpp"
  2. #include "opencv2/imgproc/imgproc.hpp"
  3. #include "opencv2/highgui/highgui.hpp"
  4. #include <iostream>
  5. using namespace cv;
  6. using namespace std;
  7. //http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#dft[2]
  8. void convolveDFT(Mat A, Mat B, Mat& C)
  9. {
  10. // reallocate the output array if needed
  11. C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type());
  12. Size dftSize;
  13. // calculate the size of DFT transform
  14. dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);
  15. dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);
  16. // allocate temporary buffers and initialize them with 0's
  17. Mat tempA(dftSize, A.type(), Scalar::all(0));//initial 0
  18. Mat tempB(dftSize, B.type(), Scalar::all(0));
  19. // copy A and B to the top-left corners of tempA and tempB, respectively
  20. Mat roiA(tempA, Rect(0,0,A.cols,A.rows));
  21. A.copyTo(roiA);
  22. Mat roiB(tempB, Rect(0,0,B.cols,B.rows));
  23. B.copyTo(roiB);
  24. // now transform the padded A & B in-place;
  25. // use "nonzeroRows" hint for faster processing
  26. dft(tempA, tempA, 0, A.rows);
  27. dft(tempB, tempB, 0, B.rows);
  28. // multiply the spectrums;
  29. // the function handles packed spectrum representations well
  30. mulSpectrums(tempA, tempB, tempA, DFT_COMPLEX_OUTPUT);
  31. //mulSpectrums(tempA, tempB, tempA, DFT_REAL_OUTPUT);
  32. // transform the product back from the frequency domain.
  33. // Even though all the result rows will be non-zero,
  34. // you need only the first C.rows of them, and thus you
  35. // pass nonzeroRows == C.rows
  36. dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);
  37. // now copy the result back to C.
  38. tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);
  39. // all the temporary buffers will be deallocated automatically
  40. }
  41. int main(int argc, char* argv[])
  42. {
  43. const char* filename = argc >=2 ? argv[1] : "Lenna.png";
  44. Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
  45. if( I.empty())
  46. return -1;
  47. Mat kernel = (Mat_<float>(3,3) << 1, 1, 1, 1, 1, 1, 1, 1, 1);
  48. cout << kernel;
  49. Mat floatI = Mat_<float>(I);// change image type into float
  50. Mat filteredI;
  51. convolveDFT(floatI, kernel, filteredI);
  52. normalize(filteredI, filteredI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
  53. // viewable image form (float between values 0 and 1).
  54. imshow("image", I);
  55. imshow("filtered", filteredI);
  56. waitKey(0);
  57. }

其中:

[cpp] view plaincopyprint?
  1. C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type());

C 为什么是这样的勒?想想一个特殊的例子就知道了: 当A,B尺寸相等的时候,这个时候的高斯滤波得到的也就是中心点的那一个值(卷积核滤波的差别在于需要绕中心180度旋转)。

MulSpectrums 是对于两张频谱图中每一个元素的乘法。
void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags );
src1
第一输入数组
src2
第二输入数组
dst
输出数组,和输入数组有相同的类型和大小。
flags
下面列举的值的组合:
CV_DXT_ROWS - 把数组的每一行视为一个单独的频谱 (参见 cvDFT 的参数讨论).
CV_DXT_MUL_CONJ - 在做乘法之前取第二个输入数组的共轭. 

第四个参数flag值没有指定,应指定为DFT_COMPLEX_OUTPUT或是DFT_REAL_OUTPUT.

参考资料:

http://blog.sina.com.cn/s/blog_4bdb170b01019atv.html

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.html

http://www.cnblogs.com/xianglan/archive/2010/12/30/1922386.html

http://www.cnblogs.com/tornadomeet/archive/2012/07/26/2610414.html

http://blog.csdn.net/ubunfans/article/details/24787569

http://blog.csdn.net/lichengyu/article/details/18848281

Opencv 实现图像的离散傅里叶变换(DFT)、卷积运算(相关滤波)相关推荐

  1. 傅里叶级数FS,连续时间傅里叶变换CTFT,离散时间傅里叶变换DTFT,离散傅里叶变换DFT,推导与联系(一)

    本文主要从傅里叶级数 FS,连续时间傅里叶变换 CTFT,离散时间傅里叶变换 DTFT,以及离散傅里叶变换 DFT 之间的区别与联系进行了比较详细的讨论,主要注重于公式形式上的推导,略去了相关的图像示 ...

  2. 傅里叶变换、离散傅里叶变换(DFT)、快速傅里叶变换(FFT)详解

    前置知识 以下内容参考<复变函数与积分变换>,如果对积分变换有所了解,完全可以跳过忽略 复数的三角表达式如下 Z=r(cosθ+isinθ)Z=r(cos\theta+isin\theta ...

  3. 离散傅里叶变换 (DFT)、快速傅里叶变换 (FFT)

    目录 离散傅里叶变换 (DFT) 离散傅里叶变换的基 离散傅里叶变换 快速傅里叶变换 (FFT) 卷积 线性时不变系统 傅里叶级数 参考文献 离散傅里叶变换 (DFT) 离散傅里叶变换的基 对于周期为 ...

  4. 傅里叶级数FS,连续时间傅里叶变换CTFT,离散时间傅里叶变换DTFT,离散傅里叶变换DFT,推导与联系(二)

    由于本文公式所占用的字符比较多,无法在一篇博客中完整发布,所以将其分为两篇博客.本篇主要介绍了离散傅里叶变换 DFT 的内容,以及相关的总结.对于前置内容,包括傅里叶级数 FS,连续时间傅里叶变换 C ...

  5. Matlab如何进行利用离散傅里叶变换DFT (快速傅里叶变换FFT)进行频谱分析

    文章目录 1. 定义 2. 变换和处理 3. 函数 4. 实例演示 例1:单频正弦信号(整数周期采样) 例2:单频正弦信号(非整数周期采样) 例3:含有直流分量的单频正弦信号 例4:正弦复合信号 例5 ...

  6. 【算法】离散傅里叶变换(DFT)

    真实的系统是会离散的,时变的.理想者将瞬时态看成时线性的系统,将时变系统分成了不同阶段.离散在围观层面是连续的,但从表层感受时,变化是迅猛的,可以忽略不计变化的过程,因而成为了离散. 一.离散系统 离 ...

  7. 离散傅里叶变换DFT

    离散傅里叶变换DFT 离散傅里叶变换的定义 实部和虚部的计算 振幅的计算 说明与注意 三角函数公式 特殊角的三角函数值 举例说明 全图汇总 离散傅里叶变换的定义 设x(n)是一个长度为M的有限长序列, ...

  8. OPenCV:傅里叶变换、时域和频域、频谱和相位谱、傅里叶级数、离散傅里叶变换(DFT)、频域滤波、高通和低通滤波器、带通和带阻滤波器

    日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) 快速了解傅立叶变换(播放PPT即能动态地显示gif图)    ...

  9. 离散傅里叶变换DFT与FFT,MATLAB的FFT函数使用(原创)——如何使用fft()绘制出真正的频谱图像

    以前一直对MATLAB中fft()函数的使用一直存在疑惑,为什么要加一 些参数,并且如何确定这些参数,也查了许多资料,但很多都感觉只是 表面一说根本没有讲清其本质.但随着学习的推进,慢慢有所领悟,所 ...

最新文章

  1. Python3学习笔记01-环境安装和运行环境
  2. 【驱动】linux下I2C驱动架构全面分析
  3. oracle数据库升级失败,Oracle 11.2.0.1 rac 升级失败后,数据库降级方案(flashback database)...
  4. 经验分享:Web前端入门要怎么开始学?
  5. 关于MJ刷新的报Too many arguments to function call, expected 0, have *问题
  6. Oracle19c下载安装和配置教程
  7. JS实现自定义右键菜单
  8. 【毕业设计】43-基于单片机的红外无线防盗报警系统设计与实现(原理图工程源文件+源代码+实物图+答辩论文)
  9. CAD版本转换怎么转?简单几步帮你解决
  10. 电子签名工具 SignatureTool
  11. 内核编译出错 [arch/arm/boot/compressed/piggy.lzo] Error 1
  12. 微型计算机技术陈慈发,微型计算机技术
  13. SOD(显著性目标检测)数据集
  14. linux ftp搭建及多端口监听
  15. js判断网页是否在微信打开
  16. 简述Tomcat的作用
  17. Octopii:一款AI驱动的个人身份信息(PII)扫描工具
  18. python爬取大众点评数据_利用Node.js制作爬取大众点评的爬虫
  19. java jdbc.idbdriver_Servlet开发中JDBC的高级应用
  20. windows电脑快捷键操作总结

热门文章

  1. MYSQL中如何查看表结构命令_mysql查看表结构命令
  2. mysql 转成树_mysql整形转换的坑
  3. 删除logs mysql_关于删除MySQL Logs的问题记录_MySQL
  4. ik分词器 分词原理_Solr7.7.2中文分词器
  5. java字符串逐个分解_改进JAVA字符串分解的方法
  6. 使用 Spring Boot Operator 部署 Spring Boot 到 Kubernetes
  7. 面试官:为什么HTTPS是安全的?
  8. 深度剖析RabbitMQ可靠性消息投递以及实践方案
  9. MySQL能够运行于多种操作系统平台_快速的掌握可以运行MySQL的操作系统
  10. preg_relace_callback不起作用匿名函数不启作用替换字符串中的所有图片