基于MFC和opencv的FFT

在网上折腾了一阵子,终于把这个程序写好了,程序是基于MFC的,图像显示的部分和获取图像的像素点是用到了opencv的一些函数,不过FFT算法没有用opencv的(呵呵,老师不让),网上的二维的FFT程序一般都是把图像分别进行行变换后进行列变换的,在编程过程中遇到了一些问题,是这样的,FFT算法算完后得到的复数矩阵怎么imshow?问题就出现在这,我原来的程序因为归一化到0-255时,程序运行特别慢(用了个CArray,找出array里的最大值和最小值,然后(每一个复数矩阵求模后-最小值)/(最大值-最小值),不满才怪呵呵,得出FFT结果是全黑的)。参考了别人的归一化

 1     /*-----------------------------------------------------------------------------
 2      *  计算功率谱
 3      *    和归一化
 4      *
 5      *-----------------------------------------------------------------------------*/
 6     //
 7     for(i = 0; i < h; i++)
 8     {
 9         //
10         for(j = 0; j < w; j++)
11         {
12             // 计算频谱
13             dTemp = sqrt(FD[j * h + i].real() * FD[j * h + i].real() + 
14                 FD[j * h + i].imag() * FD[j * h + i].imag()) / 100;
15 
16             // 判断是否超过255
17             if (dTemp > 255)
18             {
19                 // 对于超过的,直接设置为255
20                 dTemp = 255;
21             }
22 
23             image.at<uchar>(i,j)=(uchar)dTemp;
24             // 更新源图像
25 
26         }
27     }

主要代码:

  1 /*
  2  * =====================================================================================
  3  *
  4  *       Filename:  fft_dlgDlg.cpp
  5  *      Environment:opencv2.4.4 vs2010
  6  *            
  7  *    Description:  基于MFC的FFT程序
  8  *
  9  *
 10  *
 11  *        Version:  1.0
 12  *        Created:  2013/10/19 19:24:06
 13  *         Author:  yuliyang
 14 I*
 15  *             Mail:  wzyuliyang911@gmail.com
 16  *             Blog:  http://www.cnblogs.com/yuliyang
 17  *
 18  * =====================================================================================
 19  */
 20 
 21 
 22 // fft_dlgDlg.cpp : 实现文件
 23 //
 24 
 25 #include "stdafx.h"
 26 #include "fft_dlg.h"
 27 #include "fft_dlgDlg.h"
 28 #include "afxdialogex.h"
 29 
 30 #include <complex>
 31 #include <math.h>
 32 #include <stdio.h>
 33 #include <Windows.h>
 34 #include "opencvhighgui.h"
 35 #include "opencv2/core/core.hpp"  
 36 #include "opencv2/highgui/highgui.hpp"  
 37 #include "opencv2/imgproc/imgproc.hpp"  
 38 using namespace cv;
 39 using namespace std;
 40 #define PI 3.1415936;
 41 
 42 #ifdef _DEBUG
 43 #define new DEBUG_NEW
 44 #endif
 45 
 46 
 47 // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
 48 
 49 class CAboutDlg : public CDialogEx
 50 {
 51 public:
 52     CAboutDlg();
 53 
 54 // 对话框数据
 55     enum { IDD = IDD_ABOUTBOX };
 56 
 57     protected:
 58     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
 59 
 60 // 实现
 61 protected:
 62     DECLARE_MESSAGE_MAP()
 63 };
 64 
 65 CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
 66 {
 67 }
 68 
 69 void CAboutDlg::DoDataExchange(CDataExchange* pDX)
 70 {
 71     CDialogEx::DoDataExchange(pDX);
 72 }
 73 
 74 BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
 75 END_MESSAGE_MAP()
 76 
 77 
 78 // Cfft_dlgDlg 对话框
 79 
 80 
 81 
 82 
 83 Cfft_dlgDlg::Cfft_dlgDlg(CWnd* pParent /*=NULL*/)
 84     : CDialogEx(Cfft_dlgDlg::IDD, pParent)
 85 {
 86     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 87 }
 88 
 89 void Cfft_dlgDlg::DoDataExchange(CDataExchange* pDX)
 90 {
 91     CDialogEx::DoDataExchange(pDX);
 92 }
 93 
 94 BEGIN_MESSAGE_MAP(Cfft_dlgDlg, CDialogEx)
 95     ON_WM_SYSCOMMAND()
 96     ON_WM_PAINT()
 97     ON_WM_QUERYDRAGICON()
 98     ON_BN_CLICKED(IDC_OPEN_FILE, &Cfft_dlgDlg::OnBnClickedOpenFile)
 99     
100 END_MESSAGE_MAP()
101 
102 
103 // Cfft_dlgDlg 消息处理程序
104 
105 BOOL Cfft_dlgDlg::OnInitDialog()
106 {
107     CDialogEx::OnInitDialog();
108 
109     // 将“关于...”菜单项添加到系统菜单中。
110 
111     // IDM_ABOUTBOX 必须在系统命令范围内。
112     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
113     ASSERT(IDM_ABOUTBOX < 0xF000);
114 
115     CMenu* pSysMenu = GetSystemMenu(FALSE);
116     if (pSysMenu != NULL)
117     {
118         BOOL bNameValid;
119         CString strAboutMenu;
120         bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
121         ASSERT(bNameValid);
122         if (!strAboutMenu.IsEmpty())
123         {
124             pSysMenu->AppendMenu(MF_SEPARATOR);
125             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
126         }
127     }
128 
129     // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
130     //  执行此操作
131     SetIcon(m_hIcon, TRUE);            // 设置大图标
132     SetIcon(m_hIcon, FALSE);        // 设置小图标
133 
134     // TODO: 在此添加额外的初始化代码
135 
136     return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
137 }
138 
139 void Cfft_dlgDlg::OnSysCommand(UINT nID, LPARAM lParam)
140 {
141     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
142     {
143         CAboutDlg dlgAbout;
144         dlgAbout.DoModal();
145     }
146     else
147     {
148         CDialogEx::OnSysCommand(nID, lParam);
149     }
150 }
151 
152 // 如果向对话框添加最小化按钮,则需要下面的代码
153 //  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
154 //  这将由框架自动完成。
155 
156 void Cfft_dlgDlg::OnPaint()
157 {
158     if (IsIconic())
159     {
160         CPaintDC dc(this); // 用于绘制的设备上下文
161 
162         SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
163 
164         // 使图标在工作区矩形中居中
165         int cxIcon = GetSystemMetrics(SM_CXICON);
166         int cyIcon = GetSystemMetrics(SM_CYICON);
167         CRect rect;
168         GetClientRect(&rect);
169         int x = (rect.Width() - cxIcon + 1) / 2;
170         int y = (rect.Height() - cyIcon + 1) / 2;
171 
172         // 绘制图标
173         dc.DrawIcon(x, y, m_hIcon);
174     }
175     else
176     {
177         CDialogEx::OnPaint();
178     }
179 }
180 
181 //当用户拖动最小化窗口时系统调用此函数取得光标
182 //显示。
183 HCURSOR Cfft_dlgDlg::OnQueryDragIcon()
184 {
185     return static_cast<HCURSOR>(m_hIcon);
186 }
187 
188 
189 
190 void Cfft_dlgDlg::OnBnClickedOpenFile()
191 {
192     // TODO: 在此添加控件通知处理程序代码
193     
194     /*-----------------------------------------------------------------------------
195      *  选择文件名
196      *-----------------------------------------------------------------------------*/
197     
198     CString strFileName,strszFilter,strtitle,strext;
199     strszFilter="位图文件(*.bmp)|*.bmp|位图文件(*.jpg)|*.jpg|全部文件(*.*)|*.*||";
200     CFileDialog bmpdlg(TRUE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,strszFilter,NULL);
201     if(IDOK == bmpdlg.DoModal())
202     {
203         strFileName = bmpdlg.GetPathName();
204         strtitle=bmpdlg.GetFileTitle();
205         strext=bmpdlg.GetFileExt();
206 
207     }
208     if (strFileName.IsEmpty())
209     {
210         //MessageBox((LPCTSTR)strFileName);
211         MessageBox("请选择一副图像");
212 
213         return ;
214     }
215 
216     /*-----------------------------------------------------------------------------
217      *  opencv读入图像
218      *
219      *
220      *-----------------------------------------------------------------------------*/
221     Mat image=imread(strFileName.GetBuffer(0),0);
222 
223     //unsigned char*    lpSrc;
224     // 中间变量
225     double    dTemp;
226     // 循环变量
227     LONG    i;
228     LONG    j;
229     // 进行付立叶变换的宽度和高度(2的整数次方)
230     LONG    w;
231     LONG    h;
232     int        wp;
233     int        hp;
234     // 赋初值
235     w = 1;
236     h = 1;
237     wp = 0;
238     hp = 0;
239 
240     /*-----------------------------------------------------------------------------
241      *  填充到2的幂次方
242      *
243      *
244      *-----------------------------------------------------------------------------*/
245     // 计算进行付立叶变换的宽度和高度(2的整数次方)
246     while(w * 2 <= image.cols)
247     {
248         w *= 2;
249         wp++;
250     }
251 
252     while(h * 2 <= image.rows)
253     {
254         h *= 2;
255         hp++;
256     }
257     // 分配内存
258     complex<double> *TD = new complex<double>[w * h];
259     complex<double> *FD = new complex<double>[w * h];
260     //
261     for(i = 0; i < h; i++)
262     {
263         //
264         for(j = 0; j < w; j++)
265         {
266             // 给时域赋值
267             TD[j + w * i] = complex<double>(image.at<uchar>(i,j), 0); /* opencv函数读取图像像素到复数矩阵里 */
268         }
269     }
270     /*-----------------------------------------------------------------------------
271      *  把二维的FFT换成分别对行方向和列方向进行一维的FFT
272      *
273      *
274      *-----------------------------------------------------------------------------*/
275     for(i = 0; i < h; i++)
276     {
277         // 对y方向进行快速付立叶变换
278         FFT(&TD[w * i], &FD[w * i], wp);
279     }
280     // 保存变换结果
281     for(i = 0; i < h; i++)
282     {
283         for(j = 0; j < w; j++)
284         {
285             TD[i + h * j] = FD[j + w * i];
286         }
287     }
288 
289     for(i = 0; i < w; i++)
290     {
291         // 对x方向进行快速付立叶变换
292         FFT(&TD[i * h], &FD[i * h], hp);
293     }
294 
295     /*-----------------------------------------------------------------------------
296      *  计算功率谱
297      *    和归一化
298      *
299      *-----------------------------------------------------------------------------*/
300     //
301     for(i = 0; i < h; i++)
302     {
303         //
304         for(j = 0; j < w; j++)
305         {
306             // 计算频谱
307             dTemp = sqrt(FD[j * h + i].real() * FD[j * h + i].real() + 
308                 FD[j * h + i].imag() * FD[j * h + i].imag()) / 100;
309 
310             // 判断是否超过255
311             if (dTemp > 255)
312             {
313                 // 对于超过的,直接设置为255
314                 dTemp = 255;
315             }
316 
317             image.at<uchar>(i,j)=(uchar)dTemp;
318             // 更新源图像
319 
320         }
321     }
322 
323     /*-----------------------------------------------------------------------------
324      *  释放内存
325      *
326      *
327      *-----------------------------------------------------------------------------*/
328     // 删除临时变量
329     delete TD;
330     delete FD;
331     
332     /*-----------------------------------------------------------------------------
333      *  进行图像的中心化
334      *
335      *
336      *-----------------------------------------------------------------------------*/
337     int cy = image.rows/2;                      /* 中心点位置 cx ,cy */
338     int cx = image.cols/2;
339     uchar tmp13,tmp24;
340 
341     //imshow("未中心化的功率谱",image);
342 
343     IplImage  center_image=IplImage(image);
344     for( j = 0; j < cy; j++ ){  
345         for( i = 0; i < cx; i++ ){  
346             //中心化,将整体份成四块进行对角交换  
347             tmp13 = CV_IMAGE_ELEM( &center_image, uchar, j, i);  
348             CV_IMAGE_ELEM( &center_image, uchar, j, i) = CV_IMAGE_ELEM(  
349                 &center_image, uchar, j+cy, i+cx);  
350             CV_IMAGE_ELEM( &center_image, uchar, j+cy, i+cx) = tmp13;  
351 
352             tmp24 = CV_IMAGE_ELEM( &center_image, uchar, j, i+cx);  
353             CV_IMAGE_ELEM( &center_image, uchar, j, i+cx) =  
354                 CV_IMAGE_ELEM( &center_image, uchar, j+cy, i);  
355             CV_IMAGE_ELEM( &center_image, uchar, j+cy, i) = tmp24;  
356         }  
357     }  
358 
359 
360     /*-----------------------------------------------------------------------------
361      *  用于保存FFT图像
362      *
363      *
364      *-----------------------------------------------------------------------------*/
365     Mat img(&center_image,0);
366     if (BST_CHECKED == ::IsDlgButtonChecked(m_hWnd,IDC_CHECK_SAVE))
367     {
368 
369         strtitle="FFT_"+strtitle+"."+strext;
370         
371         //MessageBox(strtitle.GetBuffer(0));
372         imwrite(strtitle.GetBuffer(0),img);
373 
374     }
375     imshow("中心化后的功率谱",img);
376 
377     waitKey();
378     // 返回
379     //return TRUE;
380     
381 
382 
383 }
384 
385 /* 
386  * ===  FUNCTION  ======================================================================
387  *         Name:  FFT
388  *  Description:  计算一维FFT
389  * =====================================================================================
390  */
391 void Cfft_dlgDlg::FFT(complex<double> * TD, complex<double> * FD, int r)
392 {
393 
394     LONG    count;
395     // 循环变量
396     int        i,j,k;
397     // 中间变量
398     int        bfsize,p;
399     // 角度
400     double    angle;
401     complex<double> *W,*X1,*X2,*X;
402     // 计算付立叶变换点数
403     count = 1 << r;
404     // 分配运算所需存储器
405     W  = new complex<double>[count / 2];
406     X1 = new complex<double>[count];
407     X2 = new complex<double>[count];
408     // 计算加权系数
409     for(i = 0; i < count / 2; i++)
410     {
411         angle =-2*i*PI;
412         angle =angle/(double)count;
413         W[i] = complex<double> (cos(angle), sin(angle));
414     }
415     // 将时域点写入X1
416     memcpy(X1, TD, sizeof(complex<double>) * count);
417 
418     /*-----------------------------------------------------------------------------
419      *  
420      *                   采用蝶形算法进行快速付立叶变换
421      *
422      *-----------------------------------------------------------------------------*/
423      
424     for(k = 0; k < r; k++)
425     {
426         for(j = 0; j < 1 << k; j++)
427         {
428             bfsize = 1 << (r-k);
429             for(i = 0; i < bfsize / 2; i++)
430             {
431                 p = j * bfsize;
432                 X2[i + p] = X1[i + p] + X1[i + p + bfsize / 2];
433                 X2[i + p + bfsize / 2] = (X1[i + p] - X1[i + p + bfsize / 2]) * W[i * (1<<k)];
434             }
435         }
436         X  = X1;
437         X1 = X2;
438         X2 = X;
439     }
440     // 重新排序
441     for(j = 0; j < count; j++)
442     {
443         p = 0;
444         for(i = 0; i < r; i++)
445         {
446             if (j&(1<<i))
447             {
448                 p+=1<<(r-i-1);
449             }
450         }
451         FD[j]=X1[p];
452     }
453     // 释放内存
454     delete W;
455     delete X1;
456     delete X2;
457 }

运行效果如下:

提供程序一份:

http://pan.baidu.com/s/1ehvwy

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/yuliyang/p/3378257.html