在前面所讲述的图像处理的方法中,我们都是在空间域对图像进行处理。但是,在实际过程中,我们经常需要从空间域转换到频域上对图像进行处理。而最常用的变化方法即是傅里叶变换。下面我们就简单的讲一下,如何使用opencv为我们提供的函数来进行傅里叶变化。
我们知道,任意函数都可以表示成无数个正弦和余弦函数和的形式。图像的傅里叶变换就是将图像分解成正弦和余弦两部分。二维图像的傅里叶变换公式如下:
其中对e的指数项利用欧拉公式即可达到目标。
上述公式中,f(x,y)是空间域的值,F(u, v)则频域的值。转换之后的频域值是负数。故傅里叶变换之后的结果有两个,即实数图像和虚数图像。即:
其中R(u,v)即为实数图像,I(u,v)为虚数图像。但我们一般使用极坐标的形式,即:
其中
称为幅度。
称为相角。
在实际的图像处理过程中,我们仅仅使用幅度图像,因为幅度图像几乎包含了所有原图像中我们想要的几何信息。
那么如何获得一副图像的幅度图像呢?一般来讲有一下几步。
为了离散傅里叶变换的运行速度更快。我们一般需要对原图像进行添凑新的边缘像素的方法来获取最佳的图像尺寸。
我们可以使用getOptimalDFTSize()函数来返回最佳的尺寸,利用copyMarkBorder函数来进行填充。如下所示:
Mat padded;
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols );
copyMakeBorder( I, padded, 0, m-I.rows, 0, n-I.cols, BORDER_CONSTANT, Scalar::all(0) );
我们知道傅里叶变换的结果是复数,而且数值范围一般超过空间值范围,所以我们需要分配一个32位浮点类型的双通道图像,以用来存储实数图像和虚数图像
Mat planes[] = { Mat_(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge( planes, 2, complexI );
dft( complexI, complexI );
第一个参数为输入图像,第二个参数为变换后的输出图像。(支持原地变换)。
即根据幅度的计算公式来计算出幅度图像。如下所示:
split( complexI, planes );
magnitude( planes[0], planes[1], planes[0] );
Mat magI = planes[0];
这一步的目的是为了使傅里叶变换的幅度值范围适合在屏幕上显示。变换的公式为:
magI += Scalar::all(1);
log( magI, magI );
为了更好地显示图像,我们需要对图像进行重新分布,我们通过划分和交换幅度图像的位置来实现。同时,我们还需要将幅度的图像进行归一化到显示范围[0,1]上去,在32位浮点数类型的图像中,它的取值范围是[0,1],在使用imshow函数来显示图像时,将为我们自动乘以255,以扩展到[0,255]。
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
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);
normalize(magI, magI, 0, 1, CV_MINMAX);
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
using namespace cv;
using namespace std;
int main( int argc, char ** argv )
{
if( argc != 2 )
{
cerr << "参数个数错误!" << endl;
return -1;
}
const char * filename = argv[1];
Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
if( I.empty())
return -1;
Mat padded;
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols );
copyMakeBorder( I, padded, 0, m-I.rows, 0, n-I.cols, BORDER_CONSTANT, Scalar::all(0) );
Mat planes[] = { Mat_(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge( planes, 2, complexI );
dft( complexI, complexI );
split( complexI, planes );
magnitude( planes[0], planes[1], planes[0] );
Mat magI = planes[0];
magI += Scalar::all(1);
log( magI, magI );
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
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);
normalize(magI, magI, 0, 1, CV_MINMAX);
imshow("Input Image" , I );
imshow("spectrum magnitude", magI);
waitKey();
return 0;
}