这次介绍下OpenCV中DFT的使用,对应的例程是(EXAMPLE) dft。在图像处理领域,通过DFT可以将图像转换到频域,实现高通和低通滤波;还可以利用矩阵的卷积运算等同于其在频域的乘法运算从而优化算法降低运算量, 即先将图像转换到频域,然后做完乘法运算后,再转换到图像域,opencv中的模板匹配就利用了这一特性降低运算量。
下面是dft例程的源码
- #include "opencv2/core/core.hpp"
- #include "opencv2/imgproc/imgproc.hpp"
- #include "opencv2/highgui/highgui.hpp"
-
- #include <stdio.h>
-
- using namespace cv;
- using namespace std;
-
- static void help()
- {
- printf("\nThis program demonstrated the use of the discrete Fourier transform (dft)\n"
- "The dft of an image is taken and it's power spectrum is displayed.\n"
- "Usage:\n"
- "./dft [image_name -- default lena.jpg]\n");
- }
-
- const char* keys =
- {
- "{1| |lena.jpg|input image file}"
- };
-
- int main(int argc, const char ** argv)
- {
- help();
- CommandLineParser parser(argc, argv, keys);
- string filename = parser.get<string>("1");
-
- Mat img = imread(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE);
- if( img.empty() )
- {
- help();
- printf("Cannot read image file: %s\n", filename.c_str());
- return -1;
- }
- int M = getOptimalDFTSize( img.rows );
- int N = getOptimalDFTSize( img.cols );
- Mat padded;
- copyMakeBorder(img, padded, 0, M - img.rows, 0, N - img.cols, BORDER_CONSTANT, Scalar::all(0));
-
- Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
- Mat complexImg;
- merge(planes, 2, complexImg);
-
- dft(complexImg, complexImg);
-
-
- split(complexImg, planes);
- magnitude(planes[0], planes[1], planes[0]);
- Mat mag = planes[0];
- mag += Scalar::all(1);
- log(mag, mag);
-
-
- mag = mag(Rect(0, 0, mag.cols & -2, mag.rows & -2));
-
- int cx = mag.cols/2;
- int cy = mag.rows/2;
-
-
-
- Mat tmp;
- Mat q0(mag, Rect(0, 0, cx, cy));
- Mat q1(mag, Rect(cx, 0, cx, cy));
- Mat q2(mag, Rect(0, cy, cx, cy));
- Mat q3(mag, Rect(cx, cy, cx, cy));
-
- q0.copyTo(tmp);
- q3.copyTo(q0);
- tmp.copyTo(q3);
-
- q1.copyTo(tmp);
- q2.copyTo(q1);
- tmp.copyTo(q2);
-
- normalize(mag, mag, 0, 1, CV_MINMAX);
-
- imshow("spectrum magnitude", mag);
- waitKey();
- return 0;
- }
下图为运行结果,
例程只介绍了将图像转换到频域,那如何将其逆变换转换成图像呢?opencv中提供逆变换的函数为idft,下面看下dft,idft这两个函数的原型
-
- CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags=0, int nonzeroRows=0);
-
- CV_EXPORTS_W void idft(InputArray src, OutputArray dst, int flags=0, int nonzeroRows=0);
从描述可以看出,dft,idft分别用于实现1维,2维的傅立叶变换和傅立叶逆变换。此时你或许可能认为opencv为dft,idft分别写了两个函数实现,但如果查看idft的源码,你会发现idft的实现也是在dft函数中,opencv中代码的重用率还是挺高的。
- void cv::idft( InputArray src, OutputArray dst, int flags, int nonzero_rows )
- {
- dft( src, dst, flags | DFT_INVERSE, nonzero_rows );
- }
通过一个flags来控制是进行dft变换还是idft变换,这种用法在opencv中十分的普遍。我们可以在程序中添加一些代码
- Mat ifft;
- idft(complexImg,ifft,DFT_REAL_OUTPUT);
- normalize(ifft,ifft,0,1,CV_MINMAX);
- imshow("idft",ifft);
实现DFT的逆变换
结果如下图所示

可以看出逆变换后的图像内容与原图是一样的,因为我们进行任何处理,但是idf的大小比原图要大一些,且有些黑色边界,这是由于dft需要保证边长长度为2的次方,在这种情况下,计算速度可以加速,因此在之前的处理过程中,将原图的大小调整到2的次方,黑色边界便是copyMakeBoder的0值填充所形成的,这个函数在插值,滤波,掩模操作中十分常用,被用来扩展边界以是图像边界上的像素也能得以处理,只不过在那些操作中,处理完的图片与原图一样大小。
傅立叶变换还有一个特性经常被利用到,就是它的相位,用来确定图像的选择角度,例如tutorial文档中就提到一个应用,用来确定文字的旋转角度,我们可以通过将一张图像旋转一定的角度来做一个实验,下面是结果。


可以看出沿着中心的两个轴随着图像的旋转而旋转,因此通过确定轴的旋转角度,便可以将图像位置调整过来,相应的,处理的如果是文字或者是车牌,也可以通过类似的处理进行对齐和角度调整。
对频域图像阈值处理后,可以看得更加清楚。

