我是做Tracking 的,对于速度要求非常高。发现傅里叶变换能够使用。

于是学习之。

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

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

提出当中的低频分量做傅里叶逆变换得到的就是模糊的结果。

最不能理解的应该是:截取频域图中的不论什么一个区域相应的都是原来的整张图的区域。而不是相应的局部。

由于频域内的各个点都反映的是整张图的一个状态。

我们能够用时间和频率来理解:当你走完一段单位路程的时候。如果你花了100秒,那么你的频率就是0.01HZ。

这个0.01HZ显然体现的是一个总体的结果。而不是局部。

我们再由公式来看:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaWtlcnBlbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

能够非常明显的知道频域内的每个点的值都是由整个图像求出来的。当然以上得出的结果,我们一般仅仅关注幅值频谱图。

也就是说真正起作用的就是前面的那个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)的定义域相交的元素进行乘积而且求和,得出新的图像一点,就是被卷积后的图像. 模版又称为卷积核.卷积核做一个矩阵的形状.(当然边缘点可能须要特殊的处理,同一时候这个操作和滤波也非常像,或许就是一回事)。

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream> using namespace cv;
using namespace std; //http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#dft[2]
void convolveDFT(Mat A, Mat B, Mat& C)
{
// reallocate the output array if needed
C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type());
Size dftSize;
// calculate the size of DFT transform
dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);
dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1); // allocate temporary buffers and initialize them with 0's
Mat tempA(dftSize, A.type(), Scalar::all(0));//initial 0
Mat tempB(dftSize, B.type(), Scalar::all(0)); // copy A and B to the top-left corners of tempA and tempB, respectively
Mat roiA(tempA, Rect(0,0,A.cols,A.rows));
A.copyTo(roiA);
Mat roiB(tempB, Rect(0,0,B.cols,B.rows));
B.copyTo(roiB); // now transform the padded A & B in-place;
// use "nonzeroRows" hint for faster processing
dft(tempA, tempA, 0, A.rows);
dft(tempB, tempB, 0, B.rows); // multiply the spectrums;
// the function handles packed spectrum representations well
mulSpectrums(tempA, tempB, tempA, DFT_COMPLEX_OUTPUT);
//mulSpectrums(tempA, tempB, tempA, DFT_REAL_OUTPUT); // transform the product back from the frequency domain.
// Even though all the result rows will be non-zero,
// you need only the first C.rows of them, and thus you
// pass nonzeroRows == C.rows
dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows); // now copy the result back to C.
tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C); // all the temporary buffers will be deallocated automatically
} int main(int argc, char* argv[])
{
const char* filename = argc >=2 ? argv[1] : "Lenna.png"; Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
if( I.empty())
return -1; Mat kernel = (Mat_<float>(3,3) << 1, 1, 1, 1, 1, 1, 1, 1, 1);
cout << kernel; Mat floatI = Mat_<float>(I);// change image type into float
Mat filteredI;
convolveDFT(floatI, kernel, filteredI); normalize(filteredI, filteredI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
imshow("image", I);
imshow("filtered", filteredI);
waitKey(0); }

当中:

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. 灰度图像--频域滤波 傅里叶变换之离散傅里叶变换(DFT)

    学习DIP第23天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不 ...

  2. 【转】离散傅里叶变换-DFT(FFT)基础

    转:https://blog.csdn.net/zhangxz259/article/details/81627341 什么是离散傅里叶变换 matlab例子 本文是从最基础的知识开始讲解,力求用最通 ...

  3. 离散傅里叶变换DFT入门

    网上对于傅里叶变换相关的文章很多(足够多),有的是从物理相关角度入场,有的从数学分析角度入场.对于有志学习相关概念的同学还是能够很好的理解的. 数学包括三大块:代数学.几何.数学分析.前两块我们在中学 ...

  4. opencv 3 core组件进阶(3 离散傅里叶变换;输入输出XML和YAML文件)

    离散傅里叶变换 #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" ...

  5. OpenCV离散傅里叶变换

    离散傅里叶变换 作用:得到图像中几何结构信息 结论:傅里叶变换后的白色部分(即幅度较大的低频部分),表示的是图像中慢变化的特性,或者说是灰度变化缓慢的特性(低频部分). 傅里叶变换后的黑色部分(即幅度 ...

  6. 用matlab脚本语言写M文件函数时用三种方法简单实现实现DFT(离散傅里叶变换)

    %用二重循环实现DFT: function xk=dt_0(xn); %define a function N=length(xn); %caculate the length of the vari ...

  7. c语言数字图像处理(六):二维离散傅里叶变换

    基础知识 复数表示 C = R + jI 极坐标:C = |C|(cosθ + jsinθ) 欧拉公式:C = |C|ejθ 有关更多的时域与复频域的知识可以学习复变函数与积分变换,本篇文章只给出DF ...

  8. opencv3.2.0图像离散傅里叶变换

    源码: ##名称:离散傅里叶变换 ##平台:QT5.7.1+opencv3.2.0 ##日期:2017年12月13. /**** 新建QT控制台程序****/ #include <QCoreAp ...

  9. 【算法•日更•第四十二期】离散傅里叶变换(DFT)

    ▎前言 小编相当的菜,这篇博客难度稍高,所以有些可能不会带有证明,博客中更多的是定义. 我们将要学到的东西: 复数 暴力多项式乘法 DFT 当然,小编之前就已经写过一篇博客了,主要讲的就是基础多项式, ...

随机推荐

  1. Spring Quartz 持久化解决方案

    Quartz是实现了序列化接口的,包括接口,所以可以使用标准方式序列化到数据库. 而Spring2.5.6在集成Quartz时却未能考虑持久化问题. Spring对JobDetail进行了封装,却未实 ...

  2. POP数值动画

    POP数值动画 效果 源码 https://github.com/YouXianMing/Animations // // PopNumberController.m // Animations // ...

  3. java jxl excel 导入导出的 总结(建立超链接,以及目录sheet的索引)

    最近项目要一个批量导出功能,而且要生成一个单独的sheet页,最后后面所有sheet的索引,并且可以点击进入连接.网上搜索了一下,找到一个方法,同时把相关的excel导入导出操作记录一下!以便以后使用 ...

  4. HostMonitor监控主机状态

    HostMonitor 可以对windows和linux下的主机进行很多信息的监控,还提供web方式查看

  5. #line 的作用是改变当前行数和文件名称

    #line 的作用是改变当前行数和文件名称,它们是在编译程序中预先定义的标识符命令的基本形式如下:   #line number["filename"]其中[]内的文件名可以省略. ...

  6. Sqlserver存储过程生成日期维度

    话不多说,之前已经有一篇日志是利用oracle的存储过程生成日期维度表,接下来我们就用sqlserver来实现这个操作,如下面的步骤所示 1:创建日期维度表(Dim_time) USE [DW] GO ...

  7. Python标准库:内置函数abs(x)

    返回数字的绝对值. 參数能够是整数或浮点数.假设參数是复数,则返回复数的模. 因此abs()函数的注意点就是复数的不一样计算方式. 样例: #正整数 print('abs(1):', abs(1)) ...

  8. (算法)判断字符串中是否包含HelloWorld

    题目: 给定某字符串,判断该字符串中是否包含HelloWorld,出现HelloWorld不一定要连续,但顺序不变,如“HeByello,ByeWorByeld”就包含“HelloWorld”. 思路 ...

  9. Linux清理磁盘空间

    1.首先确定是否是磁盘满了 命令:   df -h 参数说明: -a:列出所有的文件系统,包括系统特有的/proc等文件系统 -k:以KB的容量显示各文件系统 -m:以MB的容量显示各文件系统 -h: ...

  10. UVa 10820 - Send a Table

    题目:找到整数区间[1.n]中全部的互质数对. 分析:数论,筛法,欧拉函数.在筛素数的的同一时候.直接更新每一个数字的欧拉函数. 每一个数字一定会被他前面的每一个素数筛到.而欧拉函数的计算是n*π(1 ...