学习OpenCV范例(八)——离散傅立叶变换

离散傅里叶变换到目前为止还是没有十分的了解,也从信号处理的资料中了解一些,但是还是有点朦胧,在这里我也不多谈论,我觉得OpenCV教程中介绍得虽然简单,但是大概意思还是有涵盖到,对于初学者还是比较容易理解的,大家也可以看看这个范例的介绍,写得也比较详细,我这里只是贴出来,然后归纳一些函数和类的用法,现在就介绍一下范例吧。

1、原理

对一张图像使用傅立叶变换就是将它分解成正弦和余弦两部分。也就是将图像从空间域(spatial domain)转换到频域(frequency domain)。这一转换的理论基础来自于以下事实:任一函数都可以表示成无数个正弦和余弦函数的和的形式。傅立叶变换就是一个用来将函数分解的工具。 2维图像的傅立叶变换可以用以下数学公式表达:

式中 f 是空间域(spatial domain)值, F 则是频域(frequency domain)值。 转换之后的频域值是复数, 因此,显示傅立叶变换之后的结果需要使用实数图像(real image) 加虚数图像(complex image), 或者幅度图像(magitude image)加相位图像(phase image)。 在实际的图像处理过程中,仅仅使用了幅度图像,因为幅度图像包含了原图像的几乎所有我们需要的几何信息。然而,如果你想通过修改幅度图像或者相位图像的方法来间接修改原空间图像,你需要使用逆傅立叶变换得到修改后的空间图像,这样你就必须同时保留幅度图像和相位图像了。

在此示例中,我将展示如何计算以及显示傅立叶变换后的幅度图像。由于数字图像的离散性,像素值的取值范围也是有限的。比如在一张灰度图像中,像素灰度值一般在0到255之间。 因此,我们这里讨论的也仅仅是离散傅立叶变换(DFT)。 如果你需要得到图像中的几何结构信息,那你就要用到它了。

在频域里面,对于一幅图像,高频部分代表了图像的细节、纹理信息;低频部分代表了图像的轮廓信息。如果对一幅精细的图像使用低通滤波器,那么滤波后的结果就剩下了轮廓了。这与信号处理的基本思想是相通的。如果图像受到的噪声恰好位于某个特定的“频率”范围内,则可以通过滤波器来恢复原来的图像。傅里叶变换在图像处理中可以做到:图像增强与图像去噪,图像分割之边缘检测,图像特征提取,图像压缩等等。

2、代码实现

#include"stdafx.h"
 
#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()
{
        cout << endl
               <<  "This program demonstrated the use ofthe discrete Fourier transform (DFT). " << endl
               <<  "The dft of an image is taken and it'spower spectrum is displayed."         << endl
               <<  "Usage:"                                                                     << endl
               <<  " [image_name -- default lena.jpg]"                       <<endl << endl;
}
 
int main(int argc,char ** argv)
{
        help();
 
        const char* filename =  "Lena.jpg";
 
        Mat I = imread(filename,CV_LOAD_IMAGE_GRAYSCALE);
        if( I.empty())
               return -1;
 
        Mat padded;                            //expand inputimage 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 planewith zeros
 
        dft(complexI, complexI);            // this way the result may fit inthe source matrix
 
        // compute the magnitude and switch tologarithmic 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 logarithmicscale
        log(magI, magI);
 
        // crop the spectrum, if it has an oddnumber of rows or columns
        magI = magI(Rect(0, 0, magI.cols & -2,magI.rows & -2));
 
        // rearrange the quadrants of Fourierimage  so that the origin is at the imagecenter
        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-Rightwith 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 betweenvalues 0 and 1).
 
        imshow("Input Image"       , I  );    // Show the result
        imshow("spectrum magnitude",magI);
        waitKey();
 
        return 0;
}

3、运行结果

  学习OpenCV范例(八)——离散傅立叶变换_第1张图片学习OpenCV范例(八)——离散傅立叶变换_第2张图片

            图1、原图片                                 图2、幅度图像

学习OpenCV范例(八)——离散傅立叶变换_第3张图片

                         图3、文本1

学习OpenCV范例(八)——离散傅立叶变换_第4张图片

                        图4、幅度图像1

学习OpenCV范例(八)——离散傅立叶变换_第5张图片

                          图5、文本2

学习OpenCV范例(八)——离散傅立叶变换_第6张图片

                         图6、幅度图像2

4、总结

离散傅立叶变换的一个应用是决定图片中物体的几何方向.比如,在文字识别中首先要搞清楚文字是不是水平排列的? 看一些文字,你就会注意到文本行一般是水平的而字母则有些垂直分布。文本段的这两个主要方向也是可以从傅立叶变换之后的图像看出来。我们使用这个 水平文本图像 以及 旋转文本图像 来展示离散傅立叶变换的结果 。观察观察上面的图片你会发现频域的主要内容(幅度图中的亮点)是和空间图像中物体的几何方向相关的。通过这点我们可以计算旋转角度并修正偏差。有关频率域的应用在开篇就已经介绍了一些,本人了解的也不是很多,希望之后的学习能再补充,也希望高手们能指点指点。

5、用到的类和函数

copyMakeBorder:

功能:扩充图像边界

结构:

void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom,int left, int right, int borderType, const Scalar& value=Scalar() )

src :原图像.
dst :输出图像,和src有同样的类型,并且size应该是(src.cols+left+right,src.rows+top+bottom)
top 、bottom、left 、right : 这四个参数风别表示在源图像的四个方向上分别扩充多少像素,例如top=1, bottom=1, left=1, right=1 意味着在源图像的上下左右各扩充一个像素宽度的边界。
borderType – 边界类型
value – 边界值,如果borderType==BORDER_CONSTANT .

getOptimalDFTSize:

功能:返回给定向量尺寸的傅里叶最优尺寸大小,意思就是为了提高离散傅立叶变换的运行速度,需要扩充图像,需要扩充多少,就由这个函数计算得到。

结构:

int getOptimalDFTSize(int vecsize)

vecsize:向量尺寸,即图片的rows,cols

merge:

功能:合并多个单通道图像到一个多通道图像

结构:

void merge(const Mat*mv, size_t count, OutputArray dst)

mv:被合并的图像指针,所有的图像必须要相同的size和depth

count:被合并图像的数目

dst:输出图像,图像的size和depth和mv[0]相同,通道数为被合并图像通道数的总和

dft:

功能:进行傅里叶变换

结构:

void dft(InputArraysrc, OutputArray dst, int flags=0, int nonzeroRows=0)

src:输入图像

dst:输出图像

关于dft的介绍在帮助文档中有很详细的介绍

magnitude:

功能:计算幅度

结构:

void magnitude(InputArray x, InputArray y, OutputArray magnitude)

x:实部

y:虚部,size和x一样

magnitude:输出图像,和x有同样的size和type

原理如下:


log:

功能:对数组的每一个元素取自然对数

结构:

void log(InputArraysrc, OutputArray dst)

src:输入图像

dst:输出图像

原理如下:

C是一个很大的负数

normalize:

功能:矩阵归一化

结构:

void normalize(InputArray src, OutputArraydst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1,InputArray mask=noArray() )

src:输入矩阵

dst:输出矩阵

alpha:归一化后的最大值

beta:归一化后的最小值

norm_type:归一化类型,有NORM_INFNORM_L1,  NORM_L2和NORM_MINMAX

当norm_type类型为NORM_INFNORM_L1,  NORM_L2时,原理如下:

当为NORM_MINMAX时,原理如下:

问题:1、在文档中介绍alpha为下界,而beta为上届,但是在程序中两个数字颠倒(即alpha=0beta=1),得到的结果都是一样的,这是为什么呢?

2、经过转换后的矩阵数值应该都在0-1之间,但是图片显示为什么不是全黑,而且有些亮度还挺亮的,做了一个对比,定义一个Mat矩阵,赋值为0.5,显示一片乌黑,这又是为什么呢?

希望大神们可以给予指点,谢谢

 

 


你可能感兴趣的:(merge,傅里叶变换,dft,copyMakeBorder)