用AS3实现一维傅里叶变换,一维快速傅里叶变换,二维快速傅里叶变换。以及反变换。
参考:[精通Visual.Cpp数字图像处理典型算法及实现(第2版)].张宏林.扫描版
下载地址链接: http://pan.baidu.com/s/1bnCtVjD 密码: kbhu
这本书写的不错,讲解的详细,代码也看的懂。我只是把里边的c代码人工翻译成了AS3代码。里边还有好多算法,有时间都用AS3实现一遍。
Fourier.as代码如下:
package { /** * @date 2015/7/27 15:50 * @author hanyeah */ public class Fourier { /** * */ public function Fourier() { //构造函数 throw(new Error("静态类,直接通过类名来调用类中的方法,如:Fourier.FFT(fre,fim,r)")); } /** * 一维离散傅里叶变换,直接由定义计算。 * @param fre 输入信号的实部。 * @param fim 输入信号的虚部。 * @param dir 1/-1:正变换/反变换,默认是1。 * @return Object { "re":gre, "im":gim }。 */ public static function DFT(fre:Vector.<Number>, fim:Vector.<Number>, dir:int = 1):Object { var n = fre.length; var gre:Vector.<Number> = new Vector.<Number>(n); var gim:Vector.<Number> = new Vector.<Number>(n); var i:int, j:int; var sqrtN:Number = Math.sqrt(n); for (i = 0; i < n; i++) { gre[i] = 0; gim[i] = 0; var arg = -dir * 2 * Math.PI * i / n; for (j = 0; j < n; j = j++) { var cos:Number = Math.cos(j * arg); var sin:Number = Math.sin(j * arg); gre[i] += fre[j] * cos - fim[j] * sin; gim[i] += fre[j] * sin + fim[j] * cos; } if (dir == 1) { //do nothing; } else { gre[i] = gre[i] / sqrtN; gim[i] = gim[i] / sqrtN; } } return {"re": gre, "im": gim}; } /** * 一维快速傅里叶变换。 * @param fre 输入信号的实部。 * @param fim 输入信号的虚部。 * @param r 信号长度应该是2^r * @return Object { "re":gre, "im":gim }。 */ public static function FFT(fre:Vector.<Number>, fim:Vector.<Number>, r:int):Object { var n:int = 1 << r; var hn:int = n >> 1; var wre:Vector.<Number> = new Vector.<Number>(hn); var wim:Vector.<Number> = new Vector.<Number>(hn); var gre:Vector.<Number> = new Vector.<Number>(n); var gim:Vector.<Number> = new Vector.<Number>(n); var lre:Vector.<Number>; var lim:Vector.<Number>; var i:int, j:int, k:int, p:int; var sqrtN:Number = Math.sqrt(n); //计算加权系数 for (i = 0; i < hn; i++) { var angle:Number = -2 * Math.PI * i / n; wre[i] = Math.cos(angle); wim[i] = Math.sin(angle); } //蝶形运算 for (k = 0; k < r; k++) { for (j = 0; j < (1 << k); j++) { var bfsize:int = 1 << (r - k); var hbfsize:int = bfsize >> 1; for (i = 0; i < hbfsize; i++) { p = j * bfsize; var i1:int = i + p; var i2:int = i + p + hbfsize; gre[i1] = fre[i1] + fre[i2]; gim[i1] = fim[i1] + fim[i2]; var kk:int = i << k; var xre:Number = fre[i1] - fre[i2]; var xim:Number = fim[i1] - fim[i2]; gre[i2] = xre * wre[kk] - xim * wim[kk]; gim[i2] = xre * wim[kk] + xim * wre[kk]; } } //交换 lre = fre; lim = fim; fre = gre; fim = gim; gre = lre; gim = lim; } //重新排序 for (j = 0; j < n; j++) { p = 0; for (i = 0; i < r; i++) { if (j & (1 << i)) { p += 1 << (r - i - 1); } } gre[j] = fre[p]/sqrtN; gim[j] = fim[p]/sqrtN; } return {"re": gre, "im": gim}; } /** * 一维快速傅里叶变换,反变换。 * @param fre 输入信号的实部。 * @param fim 输入信号的虚部。 * @param r 信号长度应该是2^r * @return Object { "re":gre, "im":gim }。 */ public static function IFFT(fre:Vector.<Number>, fim:Vector.<Number>, r:int):Object { var n:int = 1 << r; var i:int; for (i = 0; i < n; i++) { fim[i] = -fim[i]; } var g:Object = FFT(fre, fim, r); for (i = 0; i < n; i++) { g.re[i] = g.re[i]; g.im[i] = -g.im[i]; } return g; } /** * 二维快速傅里叶变换。 * @param fre 输入信号的实部。 * @param fim 输入信号的虚部。 * @param wr 宽=2^wr。 * @param hr 高=2^hr。 * @return Object { "re":gre, "im":gim }。 */ public static function FFT2(fre:Vector.<Number>, fim:Vector.<Number>, wr:int, hr:int):Object { var n:int = 1 << wr; var m:int = 1 << hr; var gre:Vector.<Number> = new Vector.<Number>(m * n); var gim:Vector.<Number> = new Vector.<Number>(m * n); var i:int, j:int, index:int; var lre:Vector.<Number>; var lim:Vector.<Number>; var g:Object; lre = new Vector.<Number>(n); lim = new Vector.<Number>(n); for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { index = i * n + j; lre[j] = fre[index]; lim[j] = fim[index]; } g = FFT(lre, lim, wr); for (j = 0; j < n; j++) { index = i * n + j; gre[index] = g.re[j]; gim[index] = g.im[j]; } } lre = new Vector.<Number>(m); lim = new Vector.<Number>(m); for (j = 0; j < n; j++) { for (i = 0; i < m; i++) { index = i * n + j; lre[i] = gre[index]; lim[i] = gim[index]; } g = FFT(lre, lim, hr); for (i = 0; i < m; i++) { index = i * n + j; gre[index] = g.re[i]; gim[index] = g.im[i]; } } /* var sqrtMul:Number = Math.sqrt( m * n); for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { index = i * n + j; gre[index] = gre[index] / sqrtMul; gre[index] = -gre[index] / sqrtMul; } } */ return {"re": gre, "im": gim}; } /** * 二维快速傅里叶变换,反变换。 * @param fre 输入信号的实部。 * @param fim 输入信号的虚部。 * @param wr 宽=2^wr。 * @param hr 高=2^hr。 * @return Object { "re":gre, "im":gim }。 */ public static function IFFT2(fre:Vector.<Number>, fim:Vector.<Number>, wr:int, hr:int):Object { var n:int = 1 << wr; var m:int = 1 << hr; var i:int, j:int, index:int; for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { index = i * n + j; fim[index] = -fim[index]; } } var g:Object = FFT2(fre, fim, wr, hr); for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { index = i * n + j; g.re[index] = g.re[index]; g.im[index] = -g.im[index]; } } return g; } //end function } }
我们知道,离散傅里叶变换,F(i)=(1/N)*求和项,反变换是f(x)=求和项;物理上,通常是F(i)=(根号(1/N))*求和项,f(x)=(根号(1/N))*求和项。
参考书中采用前者,上面的代码采用后者。
用上面的代码,两次傅里叶变换,会的到图像的等大的倒立的实像。听着很耳熟吧,没错,就是透镜成像。不难推出,4次傅里叶变化,图像就会还原。(这就是为什么正负变换都要乘以(根号(1/N))的原因了,符合物理规律,功率保持不变)
同样,也用js实现了一下,js实现代码如下:
function DFT(fre,fim,dir){ var n=fre.length; var gre=[]; var gim=[]; for(var i=0;i<n;i++){ gre[i]=0; gim[i]=0; var arg=-dir*2*Math.PI*i/n; for(var j=0;j<n;j++){ var cos=Math.cos(j*arg); var sin=Math.sin(j*arg); gre[i]+=fre[j]*cos-fim[j]*sin; gim[i]+=fre[j]*sin+fim[j]*cos; } if(dir==1){ //do nothing } else{ gre[i]=gre[i]/n; gim[i]=gim[i]/n; } } return {re:gre,im:gim}; } function FFT(fre,fim,r){ var n=1<<r; var wre=[]; var wim=[]; var hn=n>>1; var gre=[]; var gim=[]; var lre=[]; var lim=[]; //计算加权系数 for(var i=0;i<hn;i++){ var angle=-2*Math.PI*i/n; wre[i]=Math.cos(angle); wim[i]=Math.sin(angle); } //蝶形运算 for(var k=0;k<r;k++){ for(var j=0;j<(1<<k);j++){ var bfsize=1<<(r-k); var hbfsize=bfsize>>1; for(var i=0;i<hbfsize;i++){ var p=j*bfsize; var i1=i+p; var i2=i+p+hbfsize; gre[i1]=fre[i1]+fre[i2]; gim[i1]=fim[i1]+fim[i2]; var kk=i<<k; var xre=fre[i1]-fre[i2]; var xim=fim[i1]-fim[i2]; gre[i2]=xre*wre[kk]-xim*wim[kk]; gim[i2]=xre*wim[kk]+xim*wre[kk]; } } //交换 lre=fre; lim=fim; fre=gre; fim=gim; gre=lre; gim=lim; } //重新排序 for(var j=0;j<n;j++){ var p=0; for(var i=0;i<r;i++){ if(j&(1<<i)){ p+=1<<(r-i-1); } } gre[j]=fre[p]; gim[j]=fim[p]; } return {re:gre,im:gim}; } function IFFT(fre,fim,r){ var n=1<<r; for(var i=0;i<n;i++){ fim[i]=-fim[i]; } var g=FFT(fre,fim,r); for(var i=0;i<n;i++){ g.re[i]=g.re[i]/n; g.im[i]=-g.im[i]/n; } return g; } function FFT2(fre,fim,wr,hr){ var n=1<<wr; var m=1<<hr; var gre=[]; var gim=[]; for(var i=0;i<m;i++){ var lre=[]; var lim=[]; var index; for(var j=0;j<n;j++){ index=i*n+j; lre[j]=fre[index]; lim[j]=fim[index]; } var g=FFT(lre,lim,wr); for(j=0;j<n;j++){ index=i*n+j; gre[index]=g.re[j]; gim[index]=g.im[j]; } } for(var j=0;j<n;j++){ var lre=[]; var lim=[]; var index; for(var i=0;i<m;i++){ index=i*n+j; lre[i]=gre[index]; lim[i]=gim[index]; } var g=FFT(lre,lim,hr); for(i=0;i<m;i++){ index=i*n+j; gre[index]=g.re[i]; gim[index]=g.im[i]; } } return {re:gre,im:gim}; } function IFFT2(fre,fim,wr,hr){ var n=1<<wr; var m=1<<hr; var g; var mul=m*n; for(var i=0;i<m;i++){ for(var j=0;j<n;j++){ index=i*n+j; fim[index]=-fim[index]; } } var g=FFT2(fre,fim,wr,hr); for(var i=0;i<m;i++){ for(var j=0;j<n;j++){ index=i*n+j; g.re[index]=g.re[index]/mul; g.im[index]=-g.im[index]/mul; } } return g; }
看一个demo吧。(不要点DFT,会卡15秒钟,然后flashplayer可能会崩溃,不是BUG,是因为DFT太慢了)
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。