【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)

  • 一、二维FFTFFTFFT快速傅里叶变换 公式推导
  • 二、二维FFTFFTFFT 及 IFFTIFFTIFFT代码实现(迭代法)
    • 2.1 运行结果
  • 三、二维FFTFFTFFT 及 IFFTIFFTIFFT代码实现(递归法)
    • 3.1 运行结果

前面我们实现了一维快速傅里叶变换《【经典算法实现 43】理解FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)》
本文开始二维傅里叶变换的公式推导及代码实现

一、二维FFTFFTFFT快速傅里叶变换 公式推导

本文链接《【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)》

二维傅里叶变换:

F(u,v)=∑Mx=0Mx−1∑My=0My−1f(x,y)e−j2π(uxMx+vyMy),u,v=0,1,2,⋯,Mu−1∣Mv−1F(u,v) = \sum_{M_x=0}^{M_x-1} \sum_{M_y=0}^{M_y-1} f(x,y) e^{-j 2\pi (\frac {ux} {M_x}+\frac {vy} {M_y})}, u,v=0,1,2,\cdots,Mu-1|Mv-1F(u,v)=Mx​=0∑Mx​−1​My​=0∑My​−1​f(x,y)e−j2π(Mx​ux​+My​vy​),u,v=0,1,2,⋯,Mu−1∣Mv−1

二维傅里叶逆变换:
f(x,y)=1Mu⋅Mv∑Mu=0Mu−1∑Mv=0Mv−1F(u,v)e−j2π(uxMx+vyMy),x,y=0,1,2,⋯,Mx−1∣My−1f(x,y) = {\frac 1 {M_u\cdot M_v}} \sum_{M_u=0}^{M_u-1} \sum_{M_v=0}^{M_v-1} F(u,v) e^{-j 2\pi (\frac {ux} {M_x}+\frac {vy} {M_y})}, x,y=0,1,2,\cdots,Mx-1|My-1f(x,y)=Mu​⋅Mv​1​Mu​=0∑Mu​−1​Mv​=0∑Mv​−1​F(u,v)e−j2π(Mx​ux​+My​vy​),x,y=0,1,2,⋯,Mx−1∣My−1


令 WMxx=e−j2πxMxW_{M_x}^x=e^{-j2\pi {\frac x {M_x}} }WMx​x​=e−j2πMx​x​,则可以得到:

e−j2π(uxMx+vyMy)=e−j2πuxMx⋅e−j2πvyMy=WMxux⋅WMyvye^{-j 2\pi (\frac {ux} {M_x}+\frac {vy} {M_y})} = e^{-j2\pi{\frac {ux} {M_x}}} \cdot e^{-j2\pi{\frac {vy} {M_y}}}=W_{M_x}^{ux}\cdot W_{M_y}^{vy}e−j2π(Mx​ux​+My​vy​)=e−j2πMx​ux​⋅e−j2πMy​vy​=WMx​ux​⋅WMy​vy​


代入傅里叶变换公式:

F(u,v)=∑Mx=0Mx−1∑My=0My−1f(x,y)⋅WMxux⋅WMyvy,u,v=0,1,2,⋯,Mu−1∣Mv−1F(u,v) = \sum_{M_x=0}^{M_x-1} \sum_{M_y=0}^{M_y-1} f(x,y) \cdot W_{M_x}^{ux}\cdot W_{M_y}^{vy}, u,v=0,1,2,\cdots,Mu-1|Mv-1F(u,v)=Mx​=0∑Mx​−1​My​=0∑My​−1​f(x,y)⋅WMx​ux​⋅WMy​vy​,u,v=0,1,2,⋯,Mu−1∣Mv−1

令 wux=WMxuxw^{ux} = W_{M_x}^{ux}wux=WMx​ux​,则上述公式可以进一步简化:
F(u,v)=∑My=0My−1{∑Mx=0Mx−1f(x,y)⋅wux}⋅wvy,u,v=0,1,2,⋯,Mu−1∣Mv−1F(u,v) = \sum_{M_y=0}^{M_y-1} \lbrace \sum_{M_x=0}^{M_x-1} f(x,y) \cdot w^{ux} \rbrace \cdot w^{vy}, u,v=0,1,2,\cdots,Mu-1|Mv-1F(u,v)=My​=0∑My​−1​{Mx​=0∑Mx​−1​f(x,y)⋅wux}⋅wvy,u,v=0,1,2,⋯,Mu−1∣Mv−1


为更好理解,我们特画了一幅图

F(u,v)=∑My=0My−1{∑Mx=0Mx−1f(x,y)⋅wux}⋅wvy,u,v=0,1,2,⋯,Mu−1∣Mv−1F(u,v) = \sum_{M_y=0}^{M_y-1} \lbrace \sum_{M_x=0}^{M_x-1} f(x,y) \cdot w^{ux} \rbrace \cdot w^{vy}, u,v=0,1,2,\cdots,Mu-1|Mv-1F(u,v)=My​=0∑My​−1​{Mx​=0∑Mx​−1​f(x,y)⋅wux}⋅wvy,u,v=0,1,2,⋯,Mu−1∣Mv−1

我们假设
A(x,y)=∑Mx=0Mx−1f(x,y)⋅wux,x∈(1,2,3,4,⋯,Mx−1)A(x,y) = \sum_{M_x=0}^{M_x-1} f(x,y) \cdot w^{ux},x \in(1,2,3,4,\cdots,M_x-1)A(x,y)=Mx​=0∑Mx​−1​f(x,y)⋅wux,x∈(1,2,3,4,⋯,Mx​−1)

这样就相当于,对 某特定 yyy 行,对这一行的所有f(x,y)f(x,y)f(x,y) 的一维傅里叶变换。

再假设
B(x,y)=∑My=0My−1f(x,y)⋅wvy,y∈(1,2,3,4,⋯,Mx−1)B(x,y) = \sum_{M_y=0}^{M_y-1} f(x,y) \cdot w^{vy},y \in(1,2,3,4,\cdots,M_x-1)B(x,y)=My​=0∑My​−1​f(x,y)⋅wvy,y∈(1,2,3,4,⋯,Mx​−1)

这样就相当于,对某特定 xxx列,求这一列所有f(x,y)f(x,y)f(x,y)的一维傅里叶变换

结合这两个式子:

F(u,v)=∑My=0My−1{∑Mx=0Mx−1f(x,y)⋅wux}⋅wvy,u,v=0,1,2,⋯,Mu−1∣Mv−1F(u,v) = \sum_{M_y=0}^{M_y-1} \lbrace \sum_{M_x=0}^{M_x-1} f(x,y) \cdot w^{ux} \rbrace \cdot w^{vy}, u,v=0,1,2,\cdots,Mu-1|Mv-1F(u,v)=My​=0∑My​−1​{Mx​=0∑Mx​−1​f(x,y)⋅wux}⋅wvy,u,v=0,1,2,⋯,Mu−1∣Mv−1

F(u,v)=B(A(x,y))F(u,v) = B( A(x,y) )F(u,v)=B(A(x,y))

这样就,相当于,求先求每一行的傅里叶变换,再求每一列的傅里叶变换,
这样就成功的把二维傅里叶变换,变成了一维傅里叶变换的求解。

结合前面推导的一维傅里叶变换公式:
A0A_0A0​为偶项,A1A_1A1​为奇项,w=WMu=e−ju2πM=cos⁡(ju2πM)−jsin⁡(ju2πM)w = W_M^{u}=e^{-j u \frac {2\pi} M}= \cos(j u \frac {2\pi} M) - j \sin(j u \frac {2\pi} M)w=WMu​=e−juM2π​=cos(juM2π​)−jsin(juM2π​)

F(u)=A0+w⋅A1,u∈(0,1,2,3,4...M/2−1)F(u) = A_0+w \cdot A_1, u\in (0,1,2,3,4...M/2-1)F(u)=A0​+w⋅A1​,u∈(0,1,2,3,4...M/2−1)

F(u+M2)=A0−w⋅A1,u∈(0,1,2,3,4...M/2−1)F(u+\frac M 2) = A_0-w \cdot A_1,u\in (0,1,2,3,4...M/2-1)F(u+2M​)=A0​−w⋅A1​,u∈(0,1,2,3,4...M/2−1)

本文链接《【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)》

二、二维FFTFFTFFT 及 IFFTIFFTIFFT代码实现(迭代法)

变换思路就是:先对每一行做傅里叶变换,再针对变换结果的每一列做傅里叶变换。

c文件代码已上传:《二维快速傅里叶变换-C语言-迭代法.c》

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>#define MAX_VALUE 255// 快速傅里叶变换的数据必须是 2^n
#define NUM_x   (int)(1<<3)
#define NUM_y   (int)(1<<3)struct _complex  f[NUM_y][NUM_x];      // 原始数据
struct _complex  F[NUM_y][NUM_x];       // 傅里叶变换的数据
struct _complex IF[NUM_y][NUM_x];       // 傅里叶逆变换的数据void Init_data(void)
{   int x,y;srand(time(NULL));printf("初始化数据:\n");for(y = 0; y<NUM_y; y++){for(x = 0; x<NUM_x; x++){//f[y][x].x = rand()% MAX_VALUE;f[y][x].x = x+y;f[y][x].y = 0;printf("%3.0f  ", f[y][x].x); F[y][x].x = 0;F[y][x].y = 0;IF[y][x].x = 0;IF[y][x].y = 0;}printf("\n");}
} // 以 {0,2,4,6, 1,3,5,7} 排序,
// 数组传参使用 &f[y][x] 的形式传入,取数据时使用 f[y*NUM_x + x].x 形式获取
// split_array(&f[u][0], &F[u][0], NUM_x, 0, 0);    // 对 x分组
// split_array(&f[u][0], &F[u][0], 0, NUM_y, 1);    // 对 y分组
// flag: 0 时,对x 分组
// flag: 1 时,对y 分组
void split_array(struct _complex *src, struct _complex *dst , int x_n, int y_n, int flag)
{   int i;struct _complex t[flag == 0 ? x_n/2 : y_n/2], *s = src, *d = dst;if(flag == 0){ if(x_n <= 1)return;for(i = 0; i<x_n/2 ; i++){t[i].x = s[i*2 + 1].x;     // 暂存奇数项 t[i].y = s[i*2 + 1].y;d[i].x = s[i*2].x;            // 拷贝偶数项到低位 d[i].y = s[i*2].y;}for(i = 0; i<x_n/2 ; i++){d[i + x_n/2].x = t[i].x;  // 拷贝奇数项到高位 d[i + x_n/2].y = t[i].y;}split_array(dst,         dst,        x_n/2, y_n, flag);split_array(dst+x_n/2,   dst+x_n/2,     x_n/2, y_n, flag);  } else{if(y_n <= 1) return;for(i = 0; i<y_n/2 ; i++){t[i].x = s[(i*2 + 1)*NUM_x ].x;        // 暂存奇数项 t[i].y = s[(i*2 + 1)*NUM_x ].y;d[i*NUM_x ].x = s[(i*2)*NUM_x ].x;           // 拷贝偶数项到低位 d[i*NUM_x ].y = s[(i*2)*NUM_x ].y;}for(i = 0; i<y_n/2 ; i++){d[(i+y_n/2)*NUM_x ].x = t[i].x;   // 拷贝奇数项到高位 d[(i+y_n/2)*NUM_x ].y = t[i].y;}split_array(dst,              dst,                x_n, y_n/2, flag);split_array(dst+NUM_x*y_n/2,     dst+NUM_x*y_n/2,   x_n, y_n/2, flag);  }
}// src:源数组 &f[y][x]  dst:结果数组&F[y][x]   flag: 1:正变换   -1 逆变换
void fft(struct _complex *src, struct _complex *dst, int flag)
{int y, x, i, u, k , n;double wu;struct _complex w, a0, a1, t; clock_t start, end;start = clock();/// // 对x每一行做傅里叶变换 for(y = 0; y<NUM_y; y++){   // 先分割数组 split_array(&src[y*NUM_x+0], &dst[y*NUM_x+0], NUM_x, 0, 0);  // 对 x分组    // 对 f[y][] 这一组数进行傅里叶变换 for(i = 0; i<log2(NUM_x); i++)    //计算次数为 2^n = num,即n = log2^num {// 每次计算的间隔是 2^n,分别为 1,2,4,8 n = 2 * pow(2, i);              // 本轮一组个数为 2 * 2^n,分别为 2,4,8,则好3轮 for(k = 0; k<NUM_x/n; k++)       // num/n 为当前的组数,分别为 4,2,1 {for(u=0; u<n/2; u++)      // 对每组进行计算, a0 和 b0 的个数分别为 n/2 { wu = -1 * 2 * M_PI * u / n;   // 计算旋转因子w.x = cos(wu);w.y = flag * sin(wu);      //  如果是傅里叶逆变换,此处 flag = -1 a0 = dst[y*NUM_x + k*n + u];          // 奇数项  [y][k*n+u]a1 = dst[y*NUM_x + k*n + u + n/2];       // 偶数项  [y][k*n+u+n/2]t.x =  w.x*a1.x - w.y*a1.y;t.y =  w.x*a1.y + w.y*a1.x;dst[y*NUM_x + k*n + u].x =  a0.x + t.x;                // F[u] = A0 + wA1dst[y*NUM_x + k*n + u].y =  a0.y + t.y;dst[y*NUM_x + k*n + u + n/2].x =  a0.x - t.x;        // F[u+n/2] = A0 - wA1dst[y*NUM_x + k*n + u + n/2].y =  a0.y - t.y;}}}if(flag == -1){for(u = 0; u<NUM_x; u++){dst[y*NUM_x + u].x /= NUM_x;dst[y*NUM_x + u].y /= NUM_x;}}  }// 打印当变换结果 end = clock();printf("\n\n每行傅里叶%s变换结果为: 耗时%fs, NUM=%d x %d\n",flag==1?"":"逆", (double)(end-start)/CLOCKS_PER_SEC, NUM_y, NUM_x);for(y = 0; y<NUM_y; y++){for(x = 0; x<NUM_x; x++){printf("[%7.2f+%7.2fj] ", dst[y*NUM_x +x].x, dst[y*NUM_x +x].y);}printf("\n");}/// // 对y每一列做傅里叶变换 for(x = 0; x<NUM_x; x++){// 先分割数组 split_array(&dst[0*NUM_x+x], &dst[0*NUM_x+x], 0, NUM_y, 1);   // 对 y分组    // 对 f[][x] 这一组数进行傅里叶变换 for(i = 0; i<log2(NUM_y); i++)    //计算次数为 2^n = num,即n = log2^num {// 每次计算的间隔是 2^n,分别为 1,2,4,8 n = 2 * pow(2, i);              // 本轮一组个数为 2 * 2^n,分别为 2,4,8,则好3轮 for(k = 0; k<NUM_y/n; k++)       // num/n 为当前的组数,分别为 4,2,1 {for(u=0; u<n/2; u++)      // 对每组进行计算, a0 和 b0 的个数分别为 n/2 { wu = -1 * 2 * M_PI * u / n;   // 计算旋转因子w.x = cos(wu);w.y = flag * sin(wu);      //  如果是傅里叶逆变换,此处 flag = -1 a0 = dst[(k*n + u)*NUM_x       + x ];         // 奇数项  [k*n+u][x]a1 = dst[(k*n + u + n/2)*NUM_x    + x ];     // 偶数项      [(k*n + u + n/2)*NUM_y    + x ][x]t.x =  w.x*a1.x - w.y*a1.y;t.y =  w.x*a1.y + w.y*a1.x;dst[(k*n + u)*NUM_x      + x ].x =  a0.x + t.x;               // F[u] = A0 + wA1dst[(k*n + u)*NUM_x        + x ].y =  a0.y + t.y;dst[(k*n + u + n/2)*NUM_x    + x ].x =  a0.x - t.x;                // F[u+n/2] = A0 - wA1dst[(k*n + u + n/2)*NUM_x     + x ].y =  a0.y - t.y;}}}if(flag == -1){for(u = 0; u<NUM_y; u++){dst[u*NUM_x + x].x /= NUM_y;dst[u*NUM_x + x].y /= NUM_y;}}   }// 打印当变换结果 end = clock(); printf("\n\n最终傅里叶%s变换结果为: 耗时%fs, NUM=%d x %d\n",flag==1?"":"逆", (double)(end-start)/CLOCKS_PER_SEC, NUM_y, NUM_x);for(y = 0; y<NUM_y; y++){for(x = 0; x<NUM_x; x++){if(flag == 1) printf("[%7.2f+%7.2fj] ", dst[y*NUM_x +x].x, dst[y*NUM_x +x].y);elseprintf("[%3.0f+%3.0fj] ", dst[y*NUM_x +x].x, dst[y*NUM_x +x].y); }printf("\n");}
}int main(void)
{int u,v;Init_data();fft(&f[0][0], &F[0][0], 1);    // 快速傅里叶变换 fft(&F[0][0], &IF[0][0], -1);    // 快速傅里叶逆变换 printf("\n\n");return 0;
}

2.1 运行结果

初始化数据:0    1    2    31    2    3    42    3    4    53    4    5    64    5    6    75    6    7    86    7    8    97    8    9   10每行傅里叶变换结果为: 耗时0.000000s, NUM=8 x 4
[   6.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  10.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  14.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  18.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  22.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  26.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  30.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]
[  34.00+   0.00j] [  -2.00+   2.00j] [  -2.00+   0.00j] [  -2.00+  -2.00j]最终傅里叶变换结果为: 耗时0.002000s, NUM=8 x 4
[ 160.00+   0.00j] [ -16.00+  16.00j] [ -16.00+   0.00j] [ -16.00+ -16.00j]
[ -16.00+  38.63j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -16.00+  16.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -16.00+   6.63j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -16.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -16.00+  -6.63j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -16.00+ -16.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -16.00+ -38.63j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]每行傅里叶逆变换结果为: 耗时0.000000s, NUM=8 x 4
[  28.00+   0.00j] [  36.00+   0.00j] [  44.00+   0.00j] [  52.00+  -0.00j]
[  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j]
[  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j]
[  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j]
[  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j]
[  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j]
[  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j]
[  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j]最终傅里叶逆变换结果为: 耗时0.003000s, NUM=8 x 4
[  0+  0j] [  1+  0j] [  2+  0j] [  3+ -0j]
[  1+  0j] [  2+  0j] [  3+  0j] [  4+  0j]
[  2+ -0j] [  3+ -0j] [  4+ -0j] [  5+ -0j]
[  3+  0j] [  4+  0j] [  5+  0j] [  6+  0j]
[  4+  0j] [  5+  0j] [  6+  0j] [  7+ -0j]
[  5+ -0j] [  6+ -0j] [  7+ -0j] [  8+ -0j]
[  6+  0j] [  7+  0j] [  8+  0j] [  9+  0j]
[  7+ -0j] [  8+ -0j] [  9+ -0j] [ 10+ -0j]

三、二维FFTFFTFFT 及 IFFTIFFTIFFT代码实现(递归法)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>#define MAX_VALUE 255// 快速傅里叶变换的数据必须是 2^n
#define NUM_x   (int)(1<<3)
#define NUM_y   (int)(1<<3)struct _complex  f[NUM_y][NUM_x];      // 原始数据
struct _complex  F[NUM_y][NUM_x];       // 傅里叶变换的数据
struct _complex IF[NUM_y][NUM_x];       // 傅里叶逆变换的数据void Init_data(void)
{   int x,y;srand(time(NULL));printf("初始化数据:\n");for(y = 0; y<NUM_y; y++){for(x = 0; x<NUM_x; x++){//f[y][x].x = rand()% MAX_VALUE;f[y][x].x = x+y;f[y][x].y = 0;printf("%3.0f  ", f[y][x].x); F[y][x].x = 0;F[y][x].y = 0;IF[y][x].x = 0;IF[y][x].y = 0;}printf("\n");}
} // 以 {0,2,4,6, 1,3,5,7} 排序,
// 数组传参使用 &f[y][x] 的形式传入,取数据时使用 f[y*NUM_x + x].x 形式获取
// split_array(&f[u][0], &F[u][0], NUM_x, 0, 0);    // 对 x分组
// split_array(&f[u][0], &F[u][0], 0, NUM_y, 1);    // 对 y分组
// flag: 0 时,对x 分组
// flag: 1 时,对y 分组
void split_array(struct _complex *src, struct _complex *dst , int x_n, int y_n, int flag)
{   int i;struct _complex t[flag == 0 ? x_n/2 : y_n/2], *s = src, *d = dst;if(flag == 0){ if(x_n <= 1)return;for(i = 0; i<x_n/2 ; i++){t[i].x = s[i*2 + 1].x;     // 暂存奇数项 t[i].y = s[i*2 + 1].y;d[i].x = s[i*2].x;            // 拷贝偶数项到低位 d[i].y = s[i*2].y;}for(i = 0; i<x_n/2 ; i++){d[i + x_n/2].x = t[i].x;  // 拷贝奇数项到高位 d[i + x_n/2].y = t[i].y;}//split_array(dst,       dst,        x_n/2, y_n, flag);//split_array(dst+x_n/2,     dst+x_n/2,     x_n/2, y_n, flag);  } else{if(y_n <= 1) return;for(i = 0; i<y_n/2 ; i++){t[i].x = s[(i*2 + 1)*NUM_x ].x;        // 暂存奇数项 t[i].y = s[(i*2 + 1)*NUM_x ].y;d[i*NUM_x ].x = s[(i*2)*NUM_x ].x;           // 拷贝偶数项到低位 d[i*NUM_x ].y = s[(i*2)*NUM_x ].y;}for(i = 0; i<y_n/2 ; i++){d[(i+y_n/2)*NUM_x ].x = t[i].x;   // 拷贝奇数项到高位 d[(i+y_n/2)*NUM_x ].y = t[i].y;}//split_array(dst,                dst,                x_n, y_n/2, flag);//split_array(dst+NUM_y*y_n/2,   dst+NUM_y*y_n/2,   x_n, y_n/2, flag);  }
}//flag =1 : FFT快速傅里叶变换
//flag =-1 : IFFT快速傅里叶逆变换
void fft_x(struct _complex *src, struct _complex *dst, int num, int flag)
{int u;double x;struct _complex w, a0, a1, t;if(num ==1) return;split_array(src, dst, num, 0, 0);     // 分割 x 数组fft_x(dst,        dst,        num/2, flag);           // 递归fft_x(dst+num/2,  dst+num/2, num/2, flag);for(u = 0; u<num/2; u++){x = -1* 2 * M_PI * u / num; // 计算旋转因子 w.x = cos(x);w.y = flag * sin(x);      // 正变换,此处为 +,逆变以换,此处为 - a0 = dst[u];             // 偶项 a1 = dst[u + num/2];        // 奇项 t.x = a1.x*w.x - a1.y*w.y;   // 计算 t = a1 * wt.y = a1.y*w.x + a1.x*w.y;dst[u].x = a0.x + t.x;dst[u].y = a0.y + t.y;dst[u+num/2].x = a0.x - t.x; dst[u+num/2].y = a0.y - t.y;}if(num == NUM_x && flag == -1)for(u = 0; u<NUM_x; u++){dst[u].x /= (double)NUM_x;dst[u].y /= (double)NUM_x;}}//flag =1  : FFT快速傅里叶变换
//flag =-1 : IFFT快速傅里叶逆变换
void fft_y(struct _complex *src, struct _complex *dst, int num, int flag)
{int u;double x;struct _complex w, a0, a1, t;if(num ==1) return;split_array(src, dst, 0, num, 1);     // 分割 x 数组fft_y(dst,                    dst,                num/2, flag);           // 递归fft_y(dst+NUM_x*(num/2),  dst+NUM_x*(num/2), num/2, flag);for(u = 0; u<num/2; u++){x = -1* 2 * M_PI * u / num; // 计算旋转因子 w.x = cos(x);w.y = flag * sin(x);      // 正变换,此处为 +,逆变以换,此处为 - a0 = dst[u*NUM_x];               // 偶项 a1 = dst[(u+num/2)*NUM_x];      // 奇项 t.x = a1.x*w.x - a1.y*w.y;   // 计算 t = a1 * wt.y = a1.y*w.x + a1.x*w.y;dst[u*NUM_x].x = a0.x + t.x;dst[u*NUM_x].y = a0.y + t.y;dst[(u+num/2)*NUM_x].x = a0.x - t.x; dst[(u+num/2)*NUM_x].y = a0.y - t.y;}if(num == NUM_y && flag == -1)for(u = 0; u<NUM_y; u++){dst[u*NUM_x].x /= (double)NUM_y;dst[u*NUM_x].y /= (double)NUM_y;}}void fft(struct _complex *src, struct _complex *dst, int flag)
{int x,y;for(y = 0; y<NUM_y; y++)                 // 对每一行递归做傅里叶变换 fft_x(src+y*NUM_x, dst+y*NUM_x, NUM_x, flag);         // 快速傅里叶变换 printf("\n\n每行傅里叶%s变换结果为: NUM=%d x %d\n",flag==1?"":"逆", NUM_y, NUM_x);for(y = 0; y<NUM_y; y++){for(x = 0; x<NUM_x; x++){printf("[%7.2f+%7.2fj] ", dst[y*NUM_x + x].x, dst[y*NUM_x + x].y);}printf("\n");} for(x = 0; x<NUM_x; x++)                 // 对每一列递归做傅里叶变换 fft_y(dst+x, dst+x, NUM_y, flag);         // 快速傅里叶变换 printf("\n\n最终傅里叶%s变换结果为: NUM=%d x %d\n",flag==1?"":"逆", NUM_y, NUM_x);for(y = 0; y<NUM_y; y++){for(x = 0; x<NUM_x; x++){printf("[%7.2f+%7.2fj] ", dst[y*NUM_x + x].x, dst[y*NUM_x + x].y);}printf("\n");}
}int main(void)
{int x,y;Init_data();   fft(&f[0][0], &F[0][0], 1);         // 傅里叶变换 fft(&F[0][0], &IF[0][0], -1);      // 傅里叶逆变换 return 0;
}

3.1 运行结果

初始化数据:0    1    2    3    4    5    6    71    2    3    4    5    6    7    82    3    4    5    6    7    8    93    4    5    6    7    8    9   104    5    6    7    8    9   10   115    6    7    8    9   10   11   126    7    8    9   10   11   12   137    8    9   10   11   12   13   14每行傅里叶变换结果为: NUM=8 x 8
[  28.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  36.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  44.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  52.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  60.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  68.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  76.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]
[  84.00+   0.00j] [  -4.00+   9.66j] [  -4.00+   4.00j] [  -4.00+   1.66j] [  -4.00+   0.00j] [  -4.00+  -1.66j] [  -4.00+  -4.00j] [  -4.00+  -9.66j]最终傅里叶变换结果为: NUM=8 x 8
[ 448.00+   0.00j] [ -32.00+  77.25j] [ -32.00+  32.00j] [ -32.00+  13.25j] [ -32.00+   0.00j] [ -32.00+ -13.25j] [ -32.00+ -32.00j] [ -32.00+ -77.25j]
[ -32.00+  77.25j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -32.00+  32.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -32.00+  13.25j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -32.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -32.00+ -13.25j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -32.00+ -32.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]
[ -32.00+ -77.25j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j] [   0.00+   0.00j]每行傅里叶逆变换结果为: NUM=8 x 8
[  28.00+   0.00j] [  36.00+   0.00j] [  44.00+  -0.00j] [  52.00+   0.00j] [  60.00+   0.00j] [  68.00+  -0.00j] [  76.00+   0.00j] [  84.00+  -0.00j]
[  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j] [  -4.00+   9.66j]
[  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j] [  -4.00+   4.00j]
[  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j] [  -4.00+   1.66j]
[  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j] [  -4.00+   0.00j]
[  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j] [  -4.00+  -1.66j]
[  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j] [  -4.00+  -4.00j]
[  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j] [  -4.00+  -9.66j]最终傅里叶逆变换结果为: NUM=8 x 8
[   0.00+   0.00j] [   1.00+   0.00j] [   2.00+  -0.00j] [   3.00+   0.00j] [   4.00+   0.00j] [   5.00+  -0.00j] [   6.00+   0.00j] [   7.00+  -0.00j]
[   1.00+   0.00j] [   2.00+   0.00j] [   3.00+   0.00j] [   4.00+   0.00j] [   5.00+   0.00j] [   6.00+   0.00j] [   7.00+   0.00j] [   8.00+   0.00j]
[   2.00+  -0.00j] [   3.00+   0.00j] [   4.00+  -0.00j] [   5.00+   0.00j] [   6.00+  -0.00j] [   7.00+  -0.00j] [   8.00+   0.00j] [   9.00+  -0.00j]
[   3.00+   0.00j] [   4.00+   0.00j] [   5.00+   0.00j] [   6.00+   0.00j] [   7.00+   0.00j] [   8.00+   0.00j] [   9.00+   0.00j] [  10.00+  -0.00j]
[   4.00+   0.00j] [   5.00+   0.00j] [   6.00+  -0.00j] [   7.00+   0.00j] [   8.00+   0.00j] [   9.00+  -0.00j] [  10.00+   0.00j] [  11.00+  -0.00j]
[   5.00+  -0.00j] [   6.00+   0.00j] [   7.00+  -0.00j] [   8.00+   0.00j] [   9.00+  -0.00j] [  10.00+  -0.00j] [  11.00+  -0.00j] [  12.00+  -0.00j]
[   6.00+   0.00j] [   7.00+   0.00j] [   8.00+   0.00j] [   9.00+   0.00j] [  10.00+   0.00j] [  11.00+  -0.00j] [  12.00+   0.00j] [  13.00+  -0.00j]
[   7.00+  -0.00j] [   8.00+   0.00j] [   9.00+  -0.00j] [  10.00+  -0.00j] [  11.00+  -0.00j] [  12.00+  -0.00j] [  13.00+  -0.00j] [  14.00+  -0.00j]

《【数字图像处理】2.4:二维FFT,IFFT,c语言实现》

【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)相关推荐

  1. 快速傅里叶变换c语言函数,C语言实现FFT(快速傅里叶变换)

    while(1); } #include #include /********************************************************************* ...

  2. TMS320C6455二维FFT和IFFT实现

    目录 FFT原理简介 DSPLib配置 图像数据生成 DSPLib中的FFT和IFFT 二维FFT和IFFT实现 图像分析工具(Image Analyzer) 测试结果 相关资源链接 参考资料: Ra ...

  3. 经典算法研究系列:二、Dijkstra 算法初探

    经典算法研究系列:二.Dijkstra 算法初探  July   二零一一年一月 ====================== 本文主要参考:算法导论 第二版.维基百科. 写的不好之处,还望见谅. 本 ...

  4. c代码实现 ifft运算_二维FFT,IFFT,c语言实现 | 学步园

    学习DIP第6天 网上关于FFT的实例有很多,具体也可以参照上一篇,其实Matlab,OpenCV都可以很轻松的实现相关操作,但是对于学习其原理,还是自己操作下比较好. 二维FFT的是实现方法是先对行 ...

  5. 通过二维FFT变换对比加入窗函数之后的图像频谱和相位

    目录 一.理论基础 1.1二维FFT变换 1.2窗函数 二.核心程序 三.测试结果 一.理论基础 1.1二维FFT变换 以下公式定义 m×n 矩阵 X 的离散傅里叶变换 Y: ωm 和 ωn 是复单位 ...

  6. 算法笔记-螺旋输出二维数组

    算法笔记-螺旋输出二维数组 1.思路:二维数组看做一个坐标,遍历者当成一个人,那么我们定义这个人的位置,以及当前面朝的方向,还有这个人转向次数.初始位置,人在(x,y)=(0,0)处,面向右方,右方的 ...

  7. 深入理解二维码生成尺寸

    深入理解二维码生成尺寸 详细了解二维码的原理,CSDN这两篇博客不错: 转自MachineChen的博客:http://blog.csdn.net/u012611878/article/details ...

  8. opencv学习:二维浮点数离散傅里叶变换及其扩展边界优化

    opencv中提供了傅里叶变换函数cvDFT,执行二维浮点数离散傅里叶变换的代码如下: void fft2(const IplImage* src, IplImage* dst) {//实部.虚部 I ...

  9. FFT快速傅里叶变换 超详细的入门学习总结

    FFT快速傅里叶变换 说明 本文创作的目的是为自己巩固该算法,加深印象并深入理解,同时也为FFT入门学者提供一份可鉴的学习总结. 原文链接:https://blog.csdn.net/qq_39565 ...

最新文章

  1. 学习笔记TF065:TensorFlowOnSpark
  2. SAP MM 物料主数据的Document Data
  3. Android热补丁技术—dexposed原理简析(手机淘宝采用方案)
  4. 违反了primarykey约束怎么解决_前期物业服务合同对主业有约束力吗?
  5. c++ 12.一维数组冒泡排序
  6. 5分钟轻松教您如果组建100-500路大型拼接监控系统!
  7. 94年出生,她们如今都是985高校博士生导师!
  8. 正确退出activity_如何退出Activity
  9. 安卓安装kali linux之Termux
  10. 【Java】函数式接口与Lambda表达式
  11. 设计模式之我见系列——策略模式
  12. 网络中没有 计算机,计算机中没有检测到任何网络硬件是什么原因
  13. 田间小麦病害自动诊断系统(野外复杂环境)
  14. c语言从入门到秃头表情包,C语言从入门到入土表情包 - C语言从入门到入土微信表情包 - C语言从入门到入土QQ表情包 - 发表情 fabiaoqing.com...
  15. 文书录入登记软件的其它模块源码
  16. linux下的系统监控软件,管理员必备的20个Linux系统监控工具
  17. spring集成shiro原理
  18. matlab trapz二重积分函数_「matlab 积分」使用Matlab求解定积分/不定积分 - seo实验室...
  19. Android App设置状态栏颜色
  20. H.265/HEVC解码器 C 参考代码

热门文章

  1. 2021年二级c语言软件下载,2021计算机二级宝典
  2. java程序如何解代数方程_基于代数方程库Algebra.js解二元一次方程功能示例
  3. 现在这么卷,软件测试的岗位会越来越少吗?
  4. 爬去东方财富网龙虎榜(wechat:15353378609)
  5. Unity 第三方SDK框架接入 (Android Studio)
  6. JVM 垃圾回收简介
  7. 1加9pro刷个lineageOS Android11
  8. 计算机怎样将多行文字转换成表格,怎么把表格里的字变成两行
  9. 一个隐藏在角落的文章
  10. JAVA登录界面学生和老师_学生信息管理系统之第三篇登录界面java代码