离散傅里叶变换公式:
F(k)=∑N−1n=0f(n)∗e−i∗2πnk/N=∑N−1n=0f(n)∗WnkN
由周期性:
Wn(N+k)N=e−i∗2πn(k+N)/N=e−i∗2πnk/N∗e−i∗2πn=e−i∗2πnk/N=WnkN
共轭性:
Wn(k+N/2)N=e−i∗2πn(k+N/2)/N=e−i∗2πnk/N∗e−i∗πn=−WnkN
规模性(等比例性):
Wn(k/m)N/m=e−i∗2πn(k/m)/(N/m)=WnkN
前提:假设N能够被2整除
将 f(n) 分为偶数序列 f1(n) 和奇数序列 f2(n) ,有:
F(k)=∑N/2−1n=0f(2n)∗e−i∗2π2nk/N+∑N/2−1n=0f(2n+1)∗e−i∗2π(2n+1)k/N
=∑N/2−1n=0f(2n)∗e−i∗2πnk/(N/2)+e−i∗2πk/N∗∑N/2−1n=0f(2n+1)∗e−i∗2πnk/(N/2)
=F1(k)+WkN∗F2(k)
根据共轭性有:
F(k+N/2)=F1−WkN∗F2
这样就可以实现分治法递归了:
前提,输入是2的整数次幂,二维变换只是做横纵两次一维变换而已
function F=my_fft(x)
%% 判断是否是向量
sz=size(x);
if sz(1)>1&&sz(2)>1||numel(sz)>2
F=-1;
return;
end
%%
N=max(sz(1:2));
if N==1
F=x;
return;
end
if sz(1)>1
F=zeros(N,1);
end
if sz(2)>1
F=zeros(1,N);
end
for k=1:N/2
[F(k),F(k+N/2)]=my_fft_ele(x,N,k);
end
function [Fk,Fkn]=my_fft_ele(x,N,k) %输入信号x,信号总长度N,频域坐标k
if N==1
Fk=x;
Fkn=x;
return;
else
x1=x(1:2:N-1);%奇数
x2=x(2:2:N);%偶数
F1=my_fft_ele(x1,N/2,k);
F2=my_fft_ele(x2,N/2,k);
Wkn=exp(-i*2*pi*(k-1)/N);
Fk=F1+Wkn*F2;
Fkn=F1-Wkn*F2;
end
function F=my_ifft(x)
%% 判断是否是向量
sz=size(x);
if sz(1)>1&&sz(2)>1||numel(sz)>2
F=-1;
return;
end
%%
N=max(sz(1:2));
if N==1
F=x;
return;
end
if sz(1)>1
F=zeros(N,1);
end
if sz(2)>1
F=zeros(1,N);
end
for k=1:N/2
[F(k),F(k+N/2)]=my_ifft_ele(x,N,k);
end
F=F./N;
function [Fk,Fkn]=my_ifft_ele(x,N,k) %输入信号x,信号总长度N,频域坐标k
if N==1
Fk=x;
Fkn=x;
return;
else
x1=x(1:2:N-1);%奇数
x2=x(2:2:N);%偶数
F1=my_ifft_ele(x1,N/2,k);
F2=my_ifft_ele(x2,N/2,k);
Wkn=exp(i*2*pi*(k-1)/N);
Fk=F1+Wkn*F2;
Fkn=F1-Wkn*F2;
end
function F=my_fft2(x)
sz=size(x);
r=sz(1);
c=sz(2);
F=zeros(r,c);
for k=1:r
F(k,:)=my_fft(x(k,:));
end
for k=1:c
F(:,k)=my_fft(F(:,k));
end
function F=my_ifft2(x)
sz=size(x);
r=sz(1);
c=sz(2);
F=zeros(r,c);
for k=1:r
F(k,:)=my_ifft(x(k,:));
end
for k=1:c
F(:,k)=my_ifft(F(:,k));
end
递归算法的问题
由于要不断地出栈入栈以及划分原序列,消耗了大量的时间
(不能直接上传图片真的蛋疼,还得先分享再用浏览器打开,复制图片得到链接)
通过上面两个公式反推,得到蝶形图,每一级(第k级)都是长度为$2^k$
序列的傅里叶变换结果。
初始的序列需要一个二进制反序排列,即将原序列的序号转换成二进制数,然后将其反序的数与其交换。
网上的 O(log(N)) 的算法太神棍看不懂,先给出自己写的 O(N) 的排序算法, O(log(N)) 的算法在使用的代码里:
F=x;
K=log2(N);
C=zeros(1,N);
for I=0:N-1
nx=dec2bin(I,K);%转换为二进制字符串
nx=nx(end:-1:1);%逆序
nv=bin2dec(nx);%转换为十进制数
if nv~=I&C(I+1)==0 %交换
T=F(I+1);
F(I+1)=F(nv+1);
F(nv+1)=T;
C(I+1)=1;
C(nv+1)=1;
end
end
利用变换核的 比例性质,指数函数只需要算一次
代码如下:
function F=myfft(x,N)
if ~ismatrix(x)
F=-1;
return;
end
%%
if N==1
F=x;
return;
end
%% 二进制逆序排序
F=x;
LH=N/2;
J=LH;
N1=N-2;
for I=1:1:N1
if I1);
F(I+1)=F(J+1);
F(J+1)=T;
end
K=LH; %K是比较位数的指示,一开始是二进制最高位,如果J的K位为1,则跳0,比较下一位
while J>=K %是0则不跳,退出循环
J=J-K;
K=K/2;
end
J=J+K; %K位跳1 还是不明白逻辑,太神棍了这段代码
end
Wmn=exp(-i*2*pi*(0:N/2-1)/N);
n=1;
n2=1;
group=N;
for k=1:K
n2=n;
n=n*2;
group=group/2;
for j=1:group %组数
st=(j-1)*n+1;
for m=st:(st+n2-1)
W1=Wmn((m-st)*N/n+1);
W=W1*F(m+n2);
F(m+n2)=F(m)-W;
F(m)=F(m)+W;
end
end
end
function F=myifft(x,N)
if ~ismatrix(x)
F=-1;
return;
end
%%
if N==1
F=x;
return;
end
%% 二进制逆序排序
F=x;
LH=N/2;
J=LH;
N1=N-2;
for I=1:1:N1
if I1);
F(I+1)=F(J+1);
F(J+1)=T;
end
K=LH; %K是比较位数的指示,一开始是二进制最高位,如果J的K位为1,则跳0,比较下一位
while J>=K %是0则不跳,退出循环
J=J-K;
K=K/2;
end
J=J+K; %K位跳1 还是不明白逻辑,太神棍了这段代码
end
Wmn=exp(i*2*pi*(0:N/2-1)/N);
n=1;
n2=1;
group=N;
for k=1:K
n2=n;
n=n*2;
group=group/2;
for j=1:group %组数
st=(j-1)*n+1;
for m=st:(st+n2-1)
W1=Wmn((m-st)*N/n+1);
W=W1*F(m+n2);
F(m+n2)=(F(m)-W);
end
end
end
F=F./N;
function F=myfft2(x,M,N)
F=zeros(M,N);
for k=1:M
F(k,:)=myfft(x(k,:),N);
end
for k=1:N
F(:,k)=myfft(F(:,k),M);
end
function F=myifft2(x,M,N)
F=zeros(M,N);
for k=1:M
F(k,:)=myifft(x(k,:),N);
end
for k=1:N
F(:,k)=myifft(F(:,k),M);
end
clc;
clear;
close all;
x=imread('temp\lena.jpg');
x=mat2gray(rgb2gray(x));
[M,N]=size(x);
start1=tic;%计时
F1=fft2(x);
end1=toc(start1)
start1=tic;
X1=ifft2(F1);
end1=toc(start1)
start2=tic;%计时
F2=myfft2(x,M,N);
end2=toc(start2)
star2=tic;%计时
X2=myifft2(F2,M,N);
end2=toc(start2)
diffF=F1-F2;
diffX=X1-X2;
diffF(find(diffF<10^-11))=0;
diffX(find(diffX<10^-11))=0;
figure;
imshow(mat2gray(log(abs(fftshift(F1))))),title('fft2');
figure;
imshow(mat2gray(log(abs(fftshift(F2))))),title('myfft2');
figure;
imshow(mat2gray(abs(X1))),title('ifft2');
figure;
imshow(mat2gray(abs(X2))),title('myifft2');
sum(sum(diffF))/(M*N)
sum(sum(diffX))/(M*N)
Matlab fft2正变换时间
0.0082s
Matlab fft2逆变换时间
0.0094s
myfft2正变换时间
0.6737s
myifft2逆变换时间
1.6599s
正变换误差
5.5511e-17
逆变换误差
0
考虑到matlab的for循环效率大约是C语言的几十分之一,则和Matlab的fft相比,消耗的时间在一个数量级上。