OpenCV中的DFT和iDFT的详细代码及注释

来源:互联网 发布:淘宝微淘怎么做 编辑:程序博客网 时间:2024/05/19 15:44

这次介绍下OpenCV中DFT的使用,对应的例程是(EXAMPLE) dft。在图像处理领域,通过DFT可以将图像转换到频域,实现高通和低通滤波;还可以利用矩阵的卷积运算等同于其在频域的乘法运算从而优化算法降低运算量, 即先将图像转换到频域,然后做完乘法运算后,再转换到图像域,opencv中的模板匹配就利用了这一特性降低运算量。

下面是dft例程的源码

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include "opencv2/core/core.hpp"  
  2. #include "opencv2/imgproc/imgproc.hpp"  
  3. #include "opencv2/highgui/highgui.hpp"  
  4.   
  5. #include <stdio.h>  
  6.   
  7. using namespace cv;  
  8. using namespace std;  
  9.   
  10. static void help()  
  11. {  
  12.     printf("\nThis program demonstrated the use of the discrete Fourier transform (dft)\n"  
  13.            "The dft of an image is taken and it's power spectrum is displayed.\n"  
  14.            "Usage:\n"  
  15.             "./dft [image_name -- default lena.jpg]\n");  
  16. }  
  17.   
  18. const char* keys =  
  19. {  
  20.     "{1| |lena.jpg|input image file}"  
  21. };  
  22.   
  23. int main(int argc, const char ** argv)  
  24. {  
  25.     help();  
  26.     CommandLineParser parser(argc, argv, keys);        // opencv中用来处理命令行参数的类  
  27.     string filename = parser.get<string>("1");  
  28.   
  29.     Mat img = imread(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE);    //以灰度图像读入  
  30.     if( img.empty() )  
  31.     {  
  32.         help();  
  33.         printf("Cannot read image file: %s\n", filename.c_str());  
  34.         return -1;  
  35.     }  
  36.     int M = getOptimalDFTSize( img.rows );                               // 获得最佳DFT尺寸,为2的次方  
  37.     int N = getOptimalDFTSize( img.cols );                                 //同上  
  38.     Mat padded;  
  39.     copyMakeBorder(img, padded, 0, M - img.rows, 0, N - img.cols, BORDER_CONSTANT, Scalar::all(0));   // opencv中的边界扩展函数,提供多种方式扩展  
  40.   
  41.     Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};          // Mat 数组,第一个为扩展后的图像,一个为空图像,  
  42.     Mat complexImg;                                                                                                      
  43.     merge(planes, 2, complexImg);                                                                              // 合并成一个Mat  
  44.   
  45.     dft(complexImg, complexImg);                                                                              // FFT变换, dft需要一个2通道的Mat  
  46.   
  47.     // compute log(1 + sqrt(Re(DFT(img))**2 + Im(DFT(img))**2))  
  48.     split(complexImg, planes);                                                                                     //分离通道, planes[0] 为实数部分,planes[1]为虚数部分  
  49.     magnitude(planes[0], planes[1], planes[0]);                                                          // 求模  
  50.     Mat mag = planes[0];  
  51.     mag += Scalar::all(1);                                                                                              
  52.     log(mag, mag);                                                                                                      // 模的对数  
  53.   
  54.     // crop the spectrum, if it has an odd number of rows or columns  
  55.     mag = mag(Rect(0, 0, mag.cols & -2, mag.rows & -2));                                        //保证偶数的边长  
  56.   
  57.     int cx = mag.cols/2;  
  58.     int cy = mag.rows/2;  
  59.   
  60.     // rearrange the quadrants of Fourier image                                                        //对傅立叶变换的图像进行重排,4个区块,从左到右,从上到下 分别为q0, q1, q2, q3  
  61.     // so that the origin is at the image center                                                          //  对调q0和q3, q1和q2  
  62.     Mat tmp;  
  63.     Mat q0(mag, Rect(0, 0, cx, cy));  
  64.     Mat q1(mag, Rect(cx, 0, cx, cy));  
  65.     Mat q2(mag, Rect(0, cy, cx, cy));  
  66.     Mat q3(mag, Rect(cx, cy, cx, cy));  
  67.   
  68.     q0.copyTo(tmp);  
  69.     q3.copyTo(q0);  
  70.     tmp.copyTo(q3);  
  71.   
  72.     q1.copyTo(tmp);  
  73.     q2.copyTo(q1);  
  74.     tmp.copyTo(q2);  
  75.   
  76.     normalize(mag, mag, 0, 1, CV_MINMAX);                                                           // 规范化值到 0~1 显示图片的需要  
  77.   
  78.     imshow("spectrum magnitude", mag);  
  79.     waitKey();  
  80.     return 0;  
  81. }  
下图为运行结果,

例程只介绍了将图像转换到频域,那如何将其逆变换转换成图像呢?opencv中提供逆变换的函数为idft,下面看下dft,idft这两个函数的原型

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //! performs forward or inverse 1D or 2D Discrete Fourier Transformation  
  2. CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags=0, int nonzeroRows=0);  
  3. //! performs inverse 1D or 2D Discrete Fourier Transformation  
  4. 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中代码的重用率还是挺高的。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void cv::idft( InputArray src, OutputArray dst, int flags, int nonzero_rows )  
  2. {  
  3.     dft( src, dst, flags | DFT_INVERSE, nonzero_rows );  
  4. }  

通过一个flags来控制是进行dft变换还是idft变换,这种用法在opencv中十分的普遍。我们可以在程序中添加一些代码

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1.     Mat ifft;  
  2.     idft(complexImg,ifft,DFT_REAL_OUTPUT);  
  3.     normalize(ifft,ifft,0,1,CV_MINMAX);  
  4.     imshow("idft",ifft);  
 实现DFT的逆变换

结果如下图所示


可以看出逆变换后的图像内容与原图是一样的,因为我们进行任何处理,但是idf的大小比原图要大一些,且有些黑色边界,这是由于dft需要保证边长长度为2的次方,在这种情况下,计算速度可以加速,因此在之前的处理过程中,将原图的大小调整到2的次方,黑色边界便是copyMakeBoder的0值填充所形成的,这个函数在插值,滤波,掩模操作中十分常用,被用来扩展边界以是图像边界上的像素也能得以处理,只不过在那些操作中,处理完的图片与原图一样大小。

傅立叶变换还有一个特性经常被利用到,就是它的相位,用来确定图像的选择角度,例如tutorial文档中就提到一个应用,用来确定文字的旋转角度,我们可以通过将一张图像旋转一定的角度来做一个实验,下面是结果。


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

对频域图像阈值处理后,可以看得更加清楚。

阅读全文
0 0
原创粉丝点击