1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 傅里叶变换及逆变换

傅里叶变换及逆变换

时间:2023-09-07 05:01:08

相关推荐

傅里叶变换及逆变换

关于FFT,书上已经给出了实现方法;曾在研2时也使用迭代法实现了自己的FFT,速度上要慢一些,但是理解起来要容易一些;

最近看书,发现了一些以前没有注意到的问题;比如,FFT产生是到底是什么呢?是频率的信息吗?完整吗?程序表现出来的结果到底正确吗?等等一些问题;以前没有考虑过。

今天来给出答案,当然是本人的一些个人理解,不一定正确!

一,FFT产生的到底是什么?

书上曾经把FFT后的信息用一幅图像来表示,其实这很勉强;目的只是让大家了解频域到底反应了一些什么东西,比如,边缘上在频域上就可以很直观的反应出来;别说一幅图像了,就是多幅图像也不一定能表示得了,因为频域变换出来的数据不是整型,如果强制为整型,则会造成数据的丢失;除此之外,更重要的一点,一幅图像的频域只反应了一个频谱,也就是相位并没有考虑进去,这样,通过FFT产生的频域来还原时域图像是不可能的。

二,这些频率信息完整吗?

如上所述,这些信息是不完整的,我曾试图使用一幅24位的bmp来表示频率的信息,但是最终因为图像的整数表示而放弃,当然也是可以做的,只是效果要差很多;以后有机会我会试一试的。频域应该是一个复数;在我看来,频域的的完整信息应该有两部分表示,一部分是实部,一部分是虚部;

三,以前图像处理程序中给出的实现正确吗?

首先,我应该说是正确的,因为频率上确实是这样的;但是,以前程序在为了更加细节描述频域的信息,并没有除M*N,取代除了个100;这样更能突出细节;

关于反向FFT的算法其实很简单,使用前向变换来计算就很方便!我试着用频域信息还原时域图像,有很少的地方失真,失真的原因是由于计算机在计算时浮点数的省略;

我还原时域图像的方法是这样的:使用FFT对时域图像进行变换,得到频域的信息,保存在两个txt文件当中,这两个文件分别是频域的实部和虚部!然后使用前向变换,用txt文件来构建时域图像。

代码如下:

.h文件

//FFT.h:interfacefortheCFFTclass.

//

//

#if!defined(AFX_FFT_H__C3BE732F_8D82_4870_B422_25580A5BABC9__INCLUDED_)

#defineAFX_FFT_H__C3BE732F_8D82_4870_B422_25580A5BABC9__INCLUDED_

#if_MSC_VER>1000

#pragmaonce

#endif//_MSC_VER>1000

/**********************************************

*类名:FFT及反向FFT

*目的:时域到频域,频域到时域的变换

*作者:无忧狂澜

*日期:-02-19

**********************************************/

classCFFT

{

public:

BOOLComputeConverseFFT(LONGlWidth,

LONGlHeight,

double*fFReal,

double*fFImag,

double*fTReal,

double*fTImag,

boolbCenter);

BOOLConverseFourier(LPSTRlpDIBBits,

LONGlWidth,

LONGlHeight,

double*fFReal,

double*fFImag);

BOOLFourier(LPSTRlpDIBBits,LONGlWidth,LONGlHeight,double*fReal,double*fImag,boolbCenter);

CFFT();

virtual~CFFT();

};

#endif//!defined(AFX_FFT_H__C3BE732F_8D82_4870_B422_25580A5BABC9__INCLUDED_)

.cpp文件

[cpp] view plaincopyprint?

//FFT.cpp:implementationoftheCFFTclass.

//

//

#include"stdafx.h"

#include"mydip.h"

#include"FFT.h"

#include"math.h"

#include<complex>

usingnamespacestd;

#definePI3.1415926

#defineTWOPI6.2831852

#ifdef_DEBUG

#undefTHIS_FILE

staticcharTHIS_FILE[]=__FILE__;

#definenewDEBUG_NEW

#endif

//

//Construction/Destruction

//

/*************************************************************************

*

*函数名称:

*FFT()

*

*参数:

*complex<double>*TD-指向时域数组的指针

*complex<double>*FD-指向频域数组的指针

*r-2的幂数,即迭代次数

*

*返回值:

*无。

*

*说明:

*用迭代法实现的快速付立叶变换。

*速度比蝶形算法要慢一些,但理解起来容易

************************************************************************/

/*

voidinit(intr)

{

f=newcomplex<double>[1<<r];

inttempIndex,i,j;

doubleangle;

W=newcomplex<double>*[1<<r];

for(tempIndex=1;tempIndex<=r;tempIndex++)

{

W[1<<tempIndex]=newcomplex<double>[(1<<(r-1))-1];

}

for(i=2;i<=(1<<r);i=i<<1)

for(j=0;j<=(i>>1)-1;j++)

{

angle=-j*TWOPI/i;

W[i][j]=complex<double>(cos(angle),sin(angle));

}

}

complex<double>*WINAPIFFTMySelf(intn,intxr,intxi)

{

intu;//临时变量,用于循环计数

complex<double>*FMeven,*FModd;//用于指向上次FFT的运算结果

complex<double>*F=newcomplex<double>[n];//用于保存此次FFT运算结果

if(n/2>=1)

{

FMeven=FFTMySelf(n/2,2*xr,xi);//迭代FFT

FModd=FFTMySelf(n/2,2*xr,xr+xi);

for(u=0;u<n/2;u++)

{

F[u]=(1/(double)2)*(FMeven[u]+FModd[u]*W[n][u]);//根据公式计算FFT

F[u+n/2]=(1/(double)2)*(FMeven[u]-FModd[u]*W[n][u]);

}

free(FMeven);//释放所占用的堆内存

free(FModd);

}

elseif(n==1)

F[0]=f[xi];//如果是一点变换,FFT就是本身

returnF;

}

VOIDWINAPIFFT(complex<double>*TD,complex<double>*FD,intr)

{

inttempCount=1<<r;

init(r);

//将时域点写入f

complex<double>*tempTD=newcomplex<double>[tempCount];

memcpy(f,TD,sizeof(complex<double>)*(tempCount));

tempTD=FFTMySelf(tempCount,1,0);

inti;

for(i=0;i<tempCount;i++)

{

FD[i]=tempTD[i]*complex<double>(tempCount,0);

}

}

*/

/*************************************************************************

*

*函数名称:

*FFT()

*

*参数:

*complex<double>*TD-指向时域数组的指针

*complex<double>*FD-指向频域数组的指针

*r-2的幂数,即迭代次数

*

*返回值:

*无。

*

*说明:

*该函数用来实现快速付立叶变换。

*

************************************************************************/

VOIDWINAPIFFT(complex<double>*TD,complex<double>*FD,intr)

{

//付立叶变换点数

LONGcount;

//循环变量

inti,j,k;

//中间变量

intbfsize,p;

//角度

doubleangle;

complex<double>*W,*X1,*X2,*X;

//计算付立叶变换点数

count=1<<r;

//分配运算所需存储器

W=newcomplex<double>[count/2];

X1=newcomplex<double>[count];

X2=newcomplex<double>[count];

//计算加权系数

for(i=0;i<count/2;i++)

{

angle=-i*PI*2/count;

W[i]=complex<double>(cos(angle),sin(angle));

}

//将时域点写入X1

memcpy(X1,TD,sizeof(complex<double>)*count);

//采用蝶形算法进行快速付立叶变换

for(k=0;k<r;k++)

{

for(j=0;j<1<<k;j++)

{

bfsize=1<<(r-k);

for(i=0;i<bfsize/2;i++)

{

p=j*bfsize;

X2[i+p]=X1[i+p]+X1[i+p+bfsize/2];

X2[i+p+bfsize/2]=(X1[i+p]-X1[i+p+bfsize/2])*W[i*(1<<k)];

}

}

X=X1;

X1=X2;

X2=X;

}

//重新排序

for(j=0;j<count;j++)

{

p=0;

for(i=0;i<r;i++)

{

if(j&(1<<i))

{

p+=1<<(r-i-1);

}

}

FD[j]=X1[p];

}

//释放内存

deleteW;

deleteX1;

deleteX2;

}

CFFT::CFFT()

{

}

CFFT::~CFFT()

{

}

/*************************************************************************

*功能:正向傅里叶变换

*最后两个参数表示变换后频域的实部和虚部

*注意:频域的实部和虚部我没有进行中心变换

*因此:在逆向傅里叶变换时不需要进行中心逆变换

*bCenter表示是否中心化

*************************************************************************/

BOOLCFFT::Fourier(LPSTRlpDIBBits,LONGlWidth,LONGlHeight,double*fReal,double*fImag,boolbCenter)

{

//指向源图像的指针

unsignedchar*lpSrc;

//中间变量

doubledTemp;

//循环变量

LONGi;

LONGj;

//进行付立叶变换的宽度和高度(2的整数次方)

LONGw;

LONGh;

intwp;

inthp;

//图像每行的字节数

LONGlLineBytes;

//计算图像每行的字节数

lLineBytes=WIDTHBYTES(lWidth*8);

//赋初值

w=1;

h=1;

wp=0;

hp=0;

//计算进行付立叶变换的宽度和高度(2的整数次方)

while(w*2<=lWidth)

{

w*=2;

wp++;

}

while(h*2<=lHeight)

{

h*=2;

hp++;

}

//分配内存

complex<double>*TD=newcomplex<double>[w*h];

complex<double>*FD=newcomplex<double>[w*h];

//行

for(i=0;i<h;i++)

{

//列

for(j=0;j<w;j++)

{

//指向DIB第i行,第j个象素的指针

lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;

//给时域赋值

if(bCenter)

{//如果中心化

if((i+j)%2==0)

dTemp=*(lpSrc);

else

dTemp=*(lpSrc)*-1;

}

else

{

dTemp=*(lpSrc);

}

TD[j+w*i]=complex<double>(dTemp,0);

}

}

for(i=0;i<h;i++)

{

//对y方向进行快速付立叶变换

FFT(&TD[w*i],&FD[w*i],wp);

}

//保存变换结果

for(i=0;i<h;i++)

{

for(j=0;j<w;j++)

{

TD[i+h*j]=FD[j+w*i];

}

}

for(i=0;i<w;i++)

{

//对x方向进行快速付立叶变换

FFT(&TD[i*h],&FD[i*h],hp);

}

for(i=0;i<h;i++)

{

//列

for(j=0;j<w;j++)

{

//计算频谱

dTemp=sqrt(FD[j*h+i].real()*FD[j*h+i].real()+

FD[j*h+i].imag()*FD[j*h+i].imag())/100;

//注意:此处没有进行中心变换

fReal[j*h+i]=FD[j*h+i].real()/(lWidth*lHeight);

fImag[j*h+i]=FD[j*h+i].imag()/(lWidth*lHeight);

//判断是否超过255

if(dTemp>255)

{

//对于超过的,直接设置为255

dTemp=255;

}

//指向DIB第(i<h/2?i+h/2:i-h/2)行,第(j<w/2?j+w/2:j-w/2)个象素的指针

//此处不直接取i和j,是为了将变换后的原点移到中心

lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;

//lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*

//(lHeight-1-(i<h/2?i+h/2:i-h/2))+(j<w/2?j+w/2:j-w/2);

//更新源图像

*(lpSrc)=(BYTE)(dTemp);

}

}

//删除临时变量

deleteTD;

deleteFD;

//返回

returnTRUE;

}

/*************************************************************************

*功能:逆向向傅里叶变换(使用前向变换算法)

*最后两个参数表示变换后频域的实部和虚部

*注意:频域的实部和虚部我没有进行中心变换

*因此:在逆向傅里叶变换时不需要进行中心逆变换

*fFReal,fFImag,表示频域

*fTReal,fTImag,表示时域

*************************************************************************/

BOOLCFFT::ConverseFourier(LPSTRlpDIBBits,

LONGlWidth,

LONGlHeight,

double*fFReal,

double*fFImag)

{

//指向源图像的指针

unsignedchar*lpSrc;

//中间变量

doubledTemp;

//循环变量

LONGi;

LONGj;

//进行付立叶变换的宽度和高度(2的整数次方)

LONGw;

LONGh;

intwp;

inthp;

//图像每行的字节数

LONGlLineBytes;

//计算图像每行的字节数

lLineBytes=WIDTHBYTES(lWidth*8);

//赋初值

w=1;

h=1;

wp=0;

hp=0;

//计算进行付立叶变换的宽度和高度(2的整数次方)

while(w*2<=lWidth)

{

w*=2;

wp++;

}

while(h*2<=lHeight)

{

h*=2;

hp++;

}

//分配内存

complex<double>*TD=newcomplex<double>[w*h];

complex<double>*FD=newcomplex<double>[w*h];

//行

for(i=0;i<h;i++)

{

//列

for(j=0;j<w;j++)

{

//指向DIB第i行,第j个象素的指针

//lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;

//给频域赋值

TD[j+w*i]=complex<double>(fFReal[j*h+i],fFImag[j*h+i]*-1);

}

}

for(i=0;i<h;i++)

{

//对y方向进行快速付立叶变换

FFT(&TD[w*i],&FD[w*i],wp);

}

//保存变换结果

for(i=0;i<h;i++)

{

for(j=0;j<w;j++)

{

TD[i+h*j]=FD[j+w*i];

}

}

for(i=0;i<w;i++)

{

//对x方向进行快速付立叶变换

FFT(&TD[i*h],&FD[i*h],hp);

}

for(i=0;i<h;i++)

{

//列

for(j=0;j<w;j++)

{

//计算频谱

dTemp=sqrt(FD[j*h+i].real()*FD[j*h+i].real()+

FD[j*h+i].imag()*FD[j*h+i].imag());

//判断是否超过255

if(dTemp>255)

{

//对于超过的,直接设置为255

dTemp=255;

}

//指向DIB第(i<h/2?i+h/2:i-h/2)行,第(j<w/2?j+w/2:j-w/2)个象素的指针

//此处不直接取i和j,是为了将变换后的原点移到中心

//lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;

lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*

(lHeight-1-i)+j;

//更新源图像

*(lpSrc)=(BYTE)(dTemp);

}

}

//删除临时变量

deleteTD;

deleteFD;

//返回

returnTRUE;

}

//根据频域计算时域

//bCenter表示是否中心化

BOOLCFFT::ComputeConverseFFT(LONGlWidth,

LONGlHeight,

double*fFReal,

double*fFImag,

double*fTReal,

double*fTImag,

boolbCenter)

{

//循环变量

LONGi;

LONGj;

//进行付立叶变换的宽度和高度(2的整数次方)

LONGw;

LONGh;

intwp;

inthp;

doublefTemp,fTemp1,fTemp2;

//图像每行的字节数

LONGlLineBytes;

//计算图像每行的字节数

lLineBytes=WIDTHBYTES(lWidth*8);

//赋初值

w=1;

h=1;

wp=0;

hp=0;

//计算进行付立叶变换的宽度和高度(2的整数次方)

while(w*2<=lWidth)

{

w*=2;

wp++;

}

while(h*2<=lHeight)

{

h*=2;

hp++;

}

//分配内存

complex<double>*TD=newcomplex<double>[w*h];

complex<double>*FD=newcomplex<double>[w*h];

//行

for(i=0;i<h;i++)

{

//列

for(j=0;j<w;j++)

{

//指向DIB第i行,第j个象素的指针

//lpSrc=(unsignedchar*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;

//给频域赋值

if(fFImag==NULL)

fTemp=0;

else

fTemp=fFImag[j*h+i]*-1;

if(bCenter)

{//如果中心化

if((i+j)%2==0)

{

fTemp1=fFReal[j*h+i];

fTemp2=fTemp;

}

else

{

fTemp1=fFReal[j*h+i]*-1;

fTemp2=fTemp*-1;

}

}

else

{

fTemp1=fFReal[j*h+i];

fTemp2=fTemp;

}

TD[j+w*i]=complex<double>(fTemp1,fTemp2);

}

}

for(i=0;i<h;i++)

{

//对y方向进行快速付立叶变换

FFT(&TD[w*i],&FD[w*i],wp);

}

//保存变换结果

for(i=0;i<h;i++)

{

for(j=0;j<w;j++)

{

TD[i+h*j]=FD[j+w*i];

}

}

for(i=0;i<w;i++)

{

//对x方向进行快速付立叶变换

FFT(&TD[i*h],&FD[i*h],hp);

}

for(i=0;i<h;i++)

{

//列

for(j=0;j<w;j++)

{

//计算频谱

//FFT-1是不用除采样周期的哦

fTReal[j*h+i]=FD[j*h+i].real();

fTImag[j*h+i]=FD[j*h+i].imag()*-1;

}

}

//删除临时变量

deleteTD;

deleteFD;

//返回

returnTRUE;

}

为了方便大家了解,现在把附加代码也粘上来

[cpp] view plaincopyprint?

//正向FFT

voidCMyDIPView::OnFft()

{

//TODO:Addyourcommandhandlercodehere

//获取文档

CMyDIPDoc*pDoc=GetDocument();

//指向DIB的指针

LPSTRlpDIB;

//指向DIB象素指针

LPSTRlpDIBBits;

double*fReal,*fImag;

intnWidth,nHeight,i,j;

CFFTfft;

//锁定DIB

lpDIB=(LPSTR)::GlobalLock((HGLOBAL)pDoc->GetHDIB());

//找到DIB图像象素起始位置

lpDIBBits=::FindDIBBits(lpDIB);

//判断是否是8-bpp位图(这里为了方便,只处理8-bpp位图的付立叶变换,其它的可以类推)

if(::DIBNumColors(lpDIB)!=256)

{

//提示用户

MessageBox("目前只支持256色位图的付立叶变换!","系统提示",

MB_ICONINFORMATION|MB_OK);

//解除锁定

::GlobalUnlock((HGLOBAL)pDoc->GetHDIB());

//返回

return;

}

//更改光标形状

BeginWaitCursor();

nWidth=::DIBWidth(lpDIB);

nHeight=::DIBHeight(lpDIB);

fReal=newdouble[nWidth*nHeight];

fImag=newdouble[nWidth*nHeight];

//调用Fourier()函数进行付立叶变换

if(fft.Fourier(lpDIBBits,nWidth,nHeight,fReal,fImag))

{

//设置脏标记

pDoc->SetModifiedFlag(TRUE);

//更新视图

pDoc->UpdateAllViews(NULL);

}

else

{

//提示用户

MessageBox("分配内存失败!","系统提示",MB_ICONINFORMATION|MB_OK);

}

//解除锁定

::GlobalUnlock((HGLOBAL)pDoc->GetHDIB());

//把数据写入文件中

CStringstrTemp;

//写频域实部数据

m_file=fopen("频域实部.txt","w+");

//行

for(i=0;i<nHeight;i++)

{

//列

for(j=0;j<nWidth;j++)

{

strTemp.Format("%f",fReal[j*nHeight+i]);

fwrite(strTemp,strTemp.GetLength(),1,m_file);

strTemp="";

fwrite(strTemp,strTemp.GetLength(),1,m_file);

}

}

fclose(m_file);

//写频域虚部数据

m_file=fopen("频域虚部.txt","w+");

//行

for(i=0;i<nHeight;i++)

{

//列

for(j=0;j<nWidth;j++)

{

strTemp.Format("%f",fImag[j*nHeight+i]);

fwrite(strTemp,strTemp.GetLength(),1,m_file);

strTemp="";

fwrite(strTemp,strTemp.GetLength(),1,m_file);

}

}

fclose(m_file);

if(fReal)

{

delete[]fReal;

fReal=NULL;

}

if(fImag)

{

delete[]fImag;

fImag=NULL;

}

//恢复光标

EndWaitCursor();

}

//逆向傅里叶变换

//通过频域信息还原时域图像

voidCMyDIPView::OnConversefft()

{

//TODO:Addyourcommandhandlercodehere

//TODO:Addyourcommandhandlercodehere

//获取文档

CMyDIPDoc*pDoc=GetDocument();

//指向DIB的指针

LPSTRlpDIB;

//指向DIB象素指针

LPSTRlpDIBBits;

double*fReal,*fImag;

intnWidth,nHeight,i,j;

CFFTfft;

//锁定DIB

lpDIB=(LPSTR)::GlobalLock((HGLOBAL)pDoc->GetHDIB());

//找到DIB图像象素起始位置

lpDIBBits=::FindDIBBits(lpDIB);

//判断是否是8-bpp位图(这里为了方便,只处理8-bpp位图的付立叶变换,其它的可以类推)

if(::DIBNumColors(lpDIB)!=256)

{

//提示用户

MessageBox("目前只支持256色位图的付立叶变换!","系统提示",

MB_ICONINFORMATION|MB_OK);

//解除锁定

::GlobalUnlock((HGLOBAL)pDoc->GetHDIB());

//返回

return;

}

//更改光标形状

BeginWaitCursor();

nWidth=::DIBWidth(lpDIB);

nHeight=::DIBHeight(lpDIB);

fReal=newdouble[nWidth*nHeight];

fImag=newdouble[nWidth*nHeight];

//从文件读取频域数据

CStringstrTemp;

//写频域实部数据

m_file=fopen("频域实部.txt","r");

//行

for(i=0;i<nHeight;i++)

{

//列

for(j=0;j<nWidth;j++)

{

fscanf(m_file,"%lf",&fReal[j*nHeight+i]);

}

}

fclose(m_file);

//写频域虚部数据

m_file=fopen("频域虚部.txt","r");

//行

for(i=0;i<nHeight;i++)

{

//列

for(j=0;j<nWidth;j++)

{

fscanf(m_file,"%lf",&fImag[j*nHeight+i]);

}

}

//调用Fourier()函数进行付立叶变换

if(fft.ConverseFourier(lpDIBBits,nWidth,nHeight,fReal,fImag))

{

//设置脏标记

pDoc->SetModifiedFlag(TRUE);

//更新视图

pDoc->UpdateAllViews(NULL);

}

else

{

//提示用户

MessageBox("分配内存失败!","系统提示",MB_ICONINFORMATION|MB_OK);

}

//解除锁定

::GlobalUnlock((HGLOBAL)pDoc->GetHDIB());

if(fReal)

{

delete[]fReal;

fReal=NULL;

}

if(fImag)

{

delete[]fImag;

fImag=NULL;

}

//恢复光标

EndWaitCursor();

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。