OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY—— Discrete Fourier Transform

2.8 离散的傅立叶变换

目标

我们要寻找以下问题的答案:

1、什么是傅立叶变换,为什么我们要用这个?

2、在OpenCV中如何做到?

3、例如copyMakeBorder(),merge(),dft(),getOptimalDFGSize(),log()以及normalize()函数的用法。

源代码

你可以从这里下载或者从samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete找到代码。

#include "opencv2/core/core.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/highgui/highgui.hpp"

#include <iostream>

using namespace cv;

using namespace std;

static void help(char* progName)

{

cout << endl

<<  "This program demonstrated the use of the discrete Fourier transform (DFT). " << endl

<<  "The dft of an image is taken and it‘s power spectrum is displayed."          << endl

<<  "Usage:"                                                                      << endl

<< progName << " [image_name -- default lena.jpg] "                       << endl << endl;

}

int main(int argc, char ** argv)

{

help(argv[0]);

const char* filename = argc >=2 ? argv[1] : "lena.jpg";

Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);

if( I.empty())

return -1;

Mat padded;                            //expand input image to optimal size

int m = getOptimalDFTSize( I.rows );

int n = getOptimalDFTSize( I.cols ); // on the border add zero values

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

Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};

Mat complexI;

merge(planes, 2, complexI);         // Add to the expanded another plane with zeros

dft(complexI, complexI);            // this way the result may fit in the source matrix

// compute the magnitude and switch to logarithmic scale

// => 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]);// planes[0] = magnitude

Mat magI = planes[0];

magI += Scalar::all(1);                    // switch to logarithmic scale

log(magI, magI);

// crop the spectrum, if it has an odd number of rows or columns

magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));

// rearrange the quadrants of Fourier image  so that the origin is at the image center

int cx = magI.cols/2;

int cy = magI.rows/2;

Mat q0(magI, Rect(0, 0, cx, cy));   // Top-Left - Create a ROI per quadrant

Mat q1(magI, Rect(cx, 0, cx, cy));  // Top-Right

Mat q2(magI, Rect(0, cy, cx, cy));  // Bottom-Left

Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right

Mat tmp;                           // swap quadrants (Top-Left with Bottom-Right)

q0.copyTo(tmp);

q3.copyTo(q0);

tmp.copyTo(q3);

q1.copyTo(tmp);                    // swap quadrant (Top-Right with Bottom-Left)

q2.copyTo(q1);

tmp.copyTo(q2);

normalize(magI, magI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a

// viewable image form (float between values 0 and 1).

imshow("Input Image"       , I   );    // Show the result

imshow("spectrum magnitude", magI);

waitKey();

return 0;

}

解释

傅里叶变换将图像拆分为组成它的正弦和余弦部分。换句话说,他将一幅图像从它的空间与转换为他的频度域。这个思想来源于任何函数可以无限接近于正弦和余弦函数之和。傅立叶变换就是这样的一个方法。二维图像在数学上的傅立叶变换就是:

f是图像在空间域的值,F是频率域的值。变换的结果是一个复数。显示这些可能要通过real格式的图像和一个复数图像或者是通过幅值图像以及相位图像。然而,整个图像处理算法只有在幅值图像中才有意思,因为这个图像包含所有的我们需要的图像几何结构的信息。

然而,如果你想要在图像中做一些类似于这种形式的修改并且你需要重新转换它,你就需要保留他们两个。

在这个示例中,我会给你展示如何计算和显示傅里叶变换产生的幅值图像。数字图像本身就是离散的。这就意味着他们可能从给定的域值中取值。例如在基本的灰度图像中,数值一般在0-255之间。因此傅里叶变换同样需要最终转换为离散的形式,也就是离散的傅里叶变换(DFT)。当你在需要从一个图像的几何点中决定图像的类型时,你会用到它。下面就是步骤(输入图像I是灰度的情况):

1、拉伸图像到好的(optimal)尺寸。DFT的性能是依据图像的尺寸。通过将图像的尺寸乘以数字2,3以及5来达到最快的效果。因此,为了达到最大性能,将边缘值垫衬到这样的一个数值也不是为一种好的办法。getOptimalDFTSize()函数返回最有尺寸并且我们可以而使用copyMakeBorder()函数来拉伸一个图像的边缘:

Mat padded;

int  m=getOptimalDFRSize(I.rows);

Int n=getOptimalDFTSize(I.cols);

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

附加的像素的数值被初始化为0。

2、为复数和真值腾出空间。傅立叶变换的结果是复数。这就表明对于每一个ie图像的结果的数值也就是两个图像的值(一个组成部分就是一个)。此外频率域的范围远远大于对应的空间(域)。因此我们经常最起码使用float类型来存储它们。因此,我们将我们的输入图像转换为这个类型的并且使用另外的通道来存储复数。

Mat planes[]={Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F};

Mat complexI;

Merge(planes,2,complesI);

3、进行离散傅立叶变换。原地(in-place 输入和输出是一样的)执行计算是可能的:

dft(complexI,complexI);

4、将真值和复数部分转换为幅值图像。一个复数由真值(Re)和复数(imaginary-Im)部分组成。DFT的结果是复数。幅值图像的DFT:

使用OpenCV代码表示:

split(complexI,planes);

magnitude(planes[0],planes[1],planes[0]);

Mat magI=planes[0];

5、选择一个对数范围。傅里叶变换得出来的动态范围的系数大到不能够在屏幕中显示。我们无法在这样的数值中观察到小的以及一些高的变化值。因此高的数值将会被转化为白点,低的数值就是黑点。为了使用灰度数值来可视化,我们可以说将线性范围转换为对数范围:

转变为OpenCV代码:

magI+=Scalar::all(1);

log(magI,magI);

6、修剪和重排列。还记得我们在第一步重的图像延伸吗?现在就到了要把那些新引入的数值扔掉的时候了。为了可视化的目的,我们可以重排列结果的象限,因此origin(zero,zero)最应于图像的中心点。

magI=magI(Rect(0,0,magI.cols&-2,magI.rows&02));

int cx=magI.cols/2;

Int cy=magI.rows/2;

Mat q0(magI,Rect(0,0,cx,cy));

Mat q1(magI,Rect(cx,0,cx,cy));

Mat q2(magI,Rect(0,cy,cx,cy));

Mat q3(magI,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);

7、正常化。这也是为了可视化的目的。我们现在拥有了幅值图像,然而这个还是在我们的0-1的显示范围之外。我们使用normalize()函数将我们的数值正常化到这个范围中。

normalize(magI,magI,0,1,CV_MINMAX);

结果

一个应用的观点可能是决定图像中几何的存在的方位。例如,让我们发现文字是否水平。看一些文字,你会发现文字行安排为水平,书信形式的被安排为竖直行。这两个主要的文字片段组成可能同样被看作傅立叶转换。让我们做一下水平的和有文字的图形被旋转。

在水平文字情况:

旋转文字情况:

你可以看到频率域的最有影响的组成部分(在幅值图像重的最亮的点)在图像中跟随对象进行几何旋转。从这里我们就可以计算出偏移量以及一张图像旋转去更正最后的缺失的偏移量。

时间: 2024-12-19 08:08:36

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY—— Discrete Fourier Transform的相关文章

OpenCV Tutorials &mdash;&mdash; Discrete Fourier Transform

The Fourier Transform will decompose an image into its sinus and cosines components. In other words, it will transform an image from its spatial domain to its frequency domain. 将图像从空域转换到频域,使其由 sin 和 cos 成分构成 The idea is that any function may be appro

离散傅里叶变换(Discrete Fourier Transform,缩写为DFT)

核心函数: cvDFT 程序: 代码: #include "cv.h" #include "cxcore.h" #include "highgui.h" #include <iostream> int DFT(int argc,char** argv)  //离散傅里叶变换(Discrete Fourier Transform,缩写为DFT) { IplImage* src=cvLoadImage("e:\\picture\

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY—— File Input and Output using XML and YAML files

2.9XML和YAML格式作为文件输入输出 目标 你会从文中找到下面问题的答案: 1.如何从OpenCV使用的YAML或者XML文件中读取和打印文字条目.? 2.对于OpenCV数据结构如何做到相同的事情? 3.对你的数据结构如何做到? 4.OpenCV的数据结构,例如FileStorage,FileNode或者FileNodeIterator的使用方法. 源代码 你可以从这里下载代码或者从OpenCV的源代码库的samples/cpp/tutorial_code/core/file_input

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY—— Interoperability with OpenCV 1

2.10 和OpenCV的互用性 目标 对于OpenCV的开发团队来说,不断提升OpenCV库是非常重要的.我们不断考虑那些可以减轻你工作过程的方法,同时还要保障库的灵活性.新的C++接口就是我们为了这个而开发出来的东西.然而,向后兼容性仍然是很重要的.我们不想打碎那些你使用更早的OpenCV库写下的代码.因此,我们为了保障这个事情从而加上了一些函数.在下面的教程中,你会学习到: 1.相比于同样使用的第一版本的OpenCV库,第二版本中有了什么变化. 2.如何在图像中添加一些高斯噪点. 3.什么

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY——Random genenrator and text with OpenCV

2.7 随机产生器和OpenCV当中的文字 目标 在教程中,你会学习到: 1.使用随机数字产生类(RNG)并且如何从均匀分布中获得随机数字. 2.使用OpenCV的putText函数在窗口中显示文字 代码 1.在前一个的教程(Basic Drawing)中,我们画了不同的几何图形,给出了例如坐标(使用Points形式的)的输入参数,颜色,线条粗细,等等.你可能已经注意到我们对于那些参数都是给出了特殊的值. 2.在本教程中,我们打算使用为绘图参数使用随机值.同样,我们打算使用大量的几何图形来填满我

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY—— Adding(blending) two images using OpenCV

2.5 改变图像的对比度和明暗 目标 在教程中,你会学到如何: 1.读取像素值 2.用0初始化一个矩阵 3.学习staurate_cast是做什么的 4.获取有关像素变换的一些更酷的信息(Get some cool info about pixel transformations) 理论 注意,下面的解释来自于Richard Szeliski所写的<Computer Vision:Algorithms and Applications> 图像处理 1.一般的图像处理机是一个接受一个或多个输入图

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY——Adding (blending) two images using OpenCV

目标 在教程中你会学到: 1.什么事线性混合,它有什么用. 2.如何使用addWeighted将两个图像相加 理论 注意:下面的解释来自Richard Szeliski写的<Computer Vision:Algorithms and Application>. 在前面的教程中,我们已经学习了一些像素的运算.一个有意思的二元(两个输入)运算符就是线性混合运算符: 通过α从0到1变化这个运算符可以被用做两个图像或者录像之间的时间交融(cross-dissolve,这里tutorial里面用的是c

OpenCV_Tutorials——CORE MODULE.THE CORE FUNCTIONALITY—— Mat - The Basic Image Container

在家这段时间内,发现了这样的OpenCV库自带的教程,感觉不错,尝试翻译并且添加一些tips,帮助自己学习,同时也与各位交流一下. 核心模块.核心功能 这里这两部分说的是核心模块以及核心功能的简介,其中蓝字部分可以链接到相关部分,这里我采用顺序方法,从第一部分,即 “Mat:The Basic Image Container”开始叙述. 2.1 基本的图像容器—矩阵 目标 我们可以通过许多途径从真实世界获取数字图像,例如:数码相机.扫描仪.计算机断层扫描以及磁成像共振等等.不管怎样,那些都只是我

Personal reminder (or CheetSheet) about Fourier Transform

Recently, I'm studying Fourier Transform by watching the lectures from Stanford University. I felt that I already forget the math basics that I've learnt in college. So, to set up a quick lookup table for myself, I decide to write something to memori