上篇讲述了一维FFT的GPU实现(FFT算法实现——基于GPU的基2快速傅里叶变换),后来我又由于需要做了一下二维FFT,大概思路如下。

首先看的肯定是公式:

如上面公式所描述的,2维FFT只需要拆分成行FFT,和列FFT就行了,其中我在下面的实现是假设原点在F(0,0),由于我的代码需要原点在中心,所以在最后我将原点移动到了中心。

下面是原点F(0,0)的2维FFT的伪代码:

    //C2DFFT
//被执行2DFFT的是一个N*N的矩阵,在source_2d中按行顺序储存
//水平方向FFT
for (int i=;i<N;i++)
{
fft1(&source_2d[i*N],&source_2d_1[i*N],N);
}
//转置列成行
for (int i=;i<N*N;i++)
{
int x = i%N;
int y = i/N;
int index = x*N+y;
source_2d[index] = source_2d_1[i];
}
//垂直FFT
for(int i=;i<N;i++)
{
fft1(&source_2d[i*N],&source_2d_1[i*N],N);
}
//转置回来
for (int i=;i<N*N;i++)
{
int x = i%N;
int y = i/N;
int index = x*N+y;
source_2d[index] = source_2d_1[i];
}

GPU实现无非把这些东西转换到GPU上。

我基于OpenGL的fragment shader来计算fft;数据都存放在纹理或者FBO里面。和1维fft不同的是,NXN的数据里面,只是对当前列或者当前排做一维FFT,所以bit反转表只需要一个1*N的buffer就可以了。对应的蝴蝶图数据也只需要1*N即可。所以我们有如下的分配:

static ofFbo _fbo_bitrev_table;
static ofFbo _origin_butterfly_2d; _fbo_bitrev_table.allocate(N,,GL_RGBA32F);
_origin_butterfly_2d.allocate(N,,GL_RGBA32F);

首先要做的是把长度为N的bit反转表求出来,这个只需要求一次,所以在最开始的时候就用CPU求出来:

    for(int i=;i<N;i++)
{
_bitrev_index_2d.setColor(i,,ofFloatColor(bit_rev(i,N-),,,));
} _bitrev_index_2d.update(); //翻转后的索引
_fbo_bitrev_table.begin();
_bitrev_index_2d.draw(,,N,);
_fbo_bitrev_table.end();

然后初始化最初的蝴蝶图,这个和1维FFT是一样的,只是长度不同而已:

for(int i=;i<N;i++)
{
//初始化二维蝴蝶图
if(i%==)
{
_data_2d.setColor(i,,ofFloatColor(.f,.f,,i+));
}
else
{
_data_2d.setColor(i,,ofFloatColor(.f,.f,,i-));
} } _data_2d.update(); /////////////////2D初始化/////////////////
//初始化2D蝴蝶图
_weight_index_2d[].begin();
_data_2d.draw(,,N,);
_weight_index_2d[].end();
//备份2D初始蝴蝶图,用于下一次新的计算
_origin_butterfly_2d.begin();
_data_2d.draw(,,N,);
_origin_butterfly_2d.end();

辅助函数:

    static unsigned int bit_rev(unsigned int v, unsigned int maxv)
{
unsigned int t = log(maxv + )/log();
unsigned int ret = ;
unsigned int s = 0x80000000>>();
for (unsigned int i = ; i < t; ++i)
{
unsigned int r = v&(s << i);
ret |= (r << (t-i-)) >> (i);
}
return ret;
} static void bit_reverse_copy(RBVector2 src[], RBVector2 des[], int len)
{
for (int i = ; i < len;i++)
{
des[bit_rev(i, len-)] = src[i];
}
}

下面定义计算2维IFFT的函数:

void GPUFFT::ifft_2d(ofFbo& in,ofFbo& out,int size);

其中in是输入,out是输出,size就是N,由初始化的时候传入了一次,在这里写是为了方便调试的时候临时改变尺寸。

Ifft本身的代码和上面形式一样,内容变成了各种shader计算:

void GPUFFT::ifft_2d(ofFbo& in,ofFbo& out,int size)
{
//禁用Alpha混合,否则绘制到FBO会混合Alpha,造成数据丢失
ofDisableAlphaBlending(); //水平FFT
_weight_index_2d[_cur_2d].begin();
_origin_butterfly_2d.draw(,,N,);
_weight_index_2d[_cur_2d].end(); _fbo_in_bitreved_2d.begin();
_bit_reverse_shader_2d.begin();
_bit_reverse_shader_2d.setUniform3f("iResolution",N,N,);
_bit_reverse_shader_2d.setUniform1i("N",N);
_bit_reverse_shader_2d.setUniform1i("dir",);
_bit_reverse_shader_2d.setUniformTexture("tex_origin",in.getTextureReference(),);
_bit_reverse_shader_2d.setUniformTexture("tex_bitreverse_table",_fbo_bitrev_table.getTextureReference(),);
ofRect(,,N,N);
_bit_reverse_shader_2d.end();
_fbo_in_bitreved_2d.end(); //翻转后的数据
_res_back_2d[_cur_2d].begin();
_fbo_in_bitreved_2d.draw(,,N,N);
_res_back_2d[_cur_2d].end(); for(int i = ;i<N;i*=)
{
_res_back_2d[-_cur_2d].begin();
ofClear(,,,);
_gpu_fft_shader_2d.begin();
_gpu_fft_shader_2d.setUniform1i("size",N);
_gpu_fft_shader_2d.setUniform1i("n_step",i);
_gpu_fft_shader_2d.setUniform3f("iResolution",N,N,);
_gpu_fft_shader_2d.setUniform1i("dir",);
_gpu_fft_shader_2d.setUniformTexture("tex_index_weight",_weight_index_2d[_cur_2d].getTextureReference(),);
_gpu_fft_shader_2d.setUniformTexture("tex_res_back",_res_back_2d[_cur_2d].getTextureReference(),);
//_gpu_fft_shader_2d.setUniformTexture("test",imag_test.getTextureReference(),4); ofRect(,,N,N); _gpu_fft_shader_2d.end(); _res_back_2d[-_cur_2d].end(); _weight_index_2d[-_cur_2d].begin();
ofClear(,,,); _weight_index_shader_2d.begin();
_weight_index_shader_2d.setUniform1i("size",N);
_weight_index_shader_2d.setUniform1i("n_step",i);
_weight_index_shader_2d.setUniform3f("iResolution",N,,);
_weight_index_shader_2d.setUniform1i("dir",);
_weight_index_shader_2d.setUniformTexture("tex_input",_weight_index_2d[_cur_2d].getTextureReference(),); ofRect(,,N,); _weight_index_shader_2d.end(); _weight_index_2d[-_cur_2d].end(); _cur_2d = - _cur_2d;
} //for ifft
_res_back_2d[-_cur_2d].begin();
_res_back_2d[_cur_2d].draw(,,N,N);
_res_back_2d[-_cur_2d].end(); _res_back_2d[_cur_2d].begin();
_ifft_div_shader_2d.begin();
_ifft_div_shader_2d.setUniform1i("N",N);
_ifft_div_shader_2d.setUniform3f("iResolution",N,N,);
_ifft_div_shader_2d.setUniformTexture("tex_rgb",_res_back_2d[-_cur_2d].getTextureReference(),);
ofRect(,,N,N);
_ifft_div_shader_2d.end();
_res_back_2d[_cur_2d].end(); //垂直FFT
//垂直方向的所有都是计算都按照垂直方向来
_weight_index_2d[_cur_2d].begin();
_origin_butterfly_2d.draw(,,N,);
_weight_index_2d[_cur_2d].end(); //这一步不会将垂直水平化
_fbo_in_bitreved_2d.begin();
_bit_reverse_shader_2d.begin();
_bit_reverse_shader_2d.setUniform3f("iResolution",N,N,);
_bit_reverse_shader_2d.setUniform1i("N",N);
_bit_reverse_shader_2d.setUniform1i("dir",);
_bit_reverse_shader_2d.setUniformTexture("tex_origin",_res_back_2d[_cur_2d].getTextureReference(),);
_bit_reverse_shader_2d.setUniformTexture("tex_bitreverse_table",_fbo_bitrev_table.getTextureReference(),);
ofRect(,,N,N);
_bit_reverse_shader_2d.end();
_fbo_in_bitreved_2d.end(); //翻转后的数据
_res_back_2d[_cur_2d].begin();
_fbo_in_bitreved_2d.draw(,,N,N);
_res_back_2d[_cur_2d].end(); for(int i = ;i<N;i*=)
{
_res_back_2d[-_cur_2d].begin();
ofClear(,,,);
_gpu_fft_shader_2d.begin();
_gpu_fft_shader_2d.setUniform1i("size",N);
_gpu_fft_shader_2d.setUniform1i("n_step",i);
_gpu_fft_shader_2d.setUniform3f("iResolution",N,N,);
_gpu_fft_shader_2d.setUniform1i("dir",);
_gpu_fft_shader_2d.setUniformTexture("tex_index_weight",_weight_index_2d[_cur_2d].getTextureReference(),);
_gpu_fft_shader_2d.setUniformTexture("tex_res_back",_res_back_2d[_cur_2d].getTextureReference(),);
//_gpu_fft_shader_2d.setUniformTexture("test",imag_test.getTextureReference(),4); ofRect(,,N,N); _gpu_fft_shader_2d.end(); _res_back_2d[-_cur_2d].end(); _weight_index_2d[-_cur_2d].begin();
ofClear(,,,); _weight_index_shader_2d.begin();
_weight_index_shader_2d.setUniform1i("size",N);
_weight_index_shader_2d.setUniform1i("n_step",i);
_weight_index_shader_2d.setUniform3f("iResolution",N,,);
_weight_index_shader_2d.setUniform1i("dir",);
_weight_index_shader_2d.setUniformTexture("tex_input",_weight_index_2d[_cur_2d].getTextureReference(),); ofRect(,,N,); _weight_index_shader_2d.end(); _weight_index_2d[-_cur_2d].end(); _cur_2d = - _cur_2d;
} //for ifft
_res_back_2d[-_cur_2d].begin();
_res_back_2d[_cur_2d].draw(,,N,N);
_res_back_2d[-_cur_2d].end(); _res_back_2d[_cur_2d].begin();
_ifft_div_shader_2d.begin();
_ifft_div_shader_2d.setUniform1i("N",N);
_ifft_div_shader_2d.setUniform3f("iResolution",N,N,);
_ifft_div_shader_2d.setUniformTexture("tex_rgb",_res_back_2d[-_cur_2d].getTextureReference(),);
ofRect(,,N,N);
_ifft_div_shader_2d.end();
_res_back_2d[_cur_2d].end(); out.begin();
_res_back_2d[_cur_2d].draw(,,N,N);
out.end(); //恢复Alpha混合
//ofEnableAlphaBlending();
}

现在来看shader内容:

_bit_reverse_shader_2d

这个shader用于将整个N*N的数据全部按照行或者按照列进行翻装,使之满足执行fft的条件:

#version
uniform sampler2D tex_origin;
//1xN查找表,用于查找索引对应的bitreverse数
uniform sampler2D tex_bitreverse_table;
//1 for x direction,2 for y direction
uniform int dir;
uniform int N;
uniform vec3 iResolution; out vec4 outColor; void main()
{
vec2 tex_coord = gl_FragCoord.xy/iResolution.xy; vec2 table_index;
table_index.y = 0.5;
if(dir==)
table_index.x = tex_coord.x;
else
table_index.x = tex_coord.y;
float bitreverse = texture(tex_bitreverse_table,table_index).r; vec2 origin_index;
if(dir==)
{
//x方向
origin_index.y = tex_coord.y;
origin_index.x = (bitreverse+0.5)/N;
}
else
{
//y方向
origin_index.x = tex_coord.x;
origin_index.y = (bitreverse+0.5)/N;
}
vec2 param = texture(tex_origin,origin_index).xy; outColor = vec4(param,,);
}

_gpu_fft_shader_2d

这是fft执行计算的部分,同样分为按行和按列:

#version
//NX1
uniform sampler2D tex_index_weight;
//NXN
uniform sampler2D tex_res_back;
uniform sampler2D test;
uniform int size;
uniform int n_step;
//1 for x direction,2 for y direction
uniform int dir; uniform vec3 iResolution; out vec4 outColor; void main()
{
vec2 tex_coord = gl_FragCoord.xy/iResolution.xy; vec2 first_index;
if(dir==)
{
first_index.y = 0.5;
first_index.x = tex_coord.x;
}
else
{
first_index.y = 0.5;
first_index.x = tex_coord.y;
} float cur_x = gl_FragCoord.x - 0.5;
float cur_y = gl_FragCoord.y - 0.5; vec2 outv; vec4 temp = texture(tex_index_weight,first_index);
//ifft
vec2 weight = vec2(cos(temp.r/temp.g**3.141592653),-sin(temp.r/temp.g**3.141592653));
//fft
//vec2 weight = vec2(cos(temp.r/temp.g*2*3.141592653),sin(temp.r/temp.g*2*3.141592653));
vec2 _param2_index; if(dir==)
{
_param2_index.x = (temp.a + 0.5)/size;
_param2_index.y = tex_coord.y;
}
else
{
_param2_index.x = tex_coord.x;
_param2_index.y = (temp.a + 0.5)/size;
} vec2 param1 = texture(tex_res_back,tex_coord).rg;
vec2 param2 = texture(tex_res_back,_param2_index).rg; float tex_coord_n1;
float tex_coord_n2;
if(dir==)
{
tex_coord_n1 = cur_x;
}
else
{
tex_coord_n1 = cur_y;
} tex_coord_n2 = temp.a; if(tex_coord_n1<tex_coord_n2)
{
outv.r = param1.r + param2.r*weight.r-weight.g*param2.g;
outv.g = param1.g +weight.r*param2.g + weight.g*param2.r;
}
else
{
outv.r = param2.r + param1.r*weight.r-weight.g*param1.g;
outv.g = param2.g +weight.r*param1.g + weight.g*param1.r;
} outColor = vec4(outv,,); }

_weight_index_shader_2d

更新蝴蝶图索引:

#version 

uniform sampler2D tex_input;
uniform int size;
uniform int n_total;
//start with 2
uniform int n_step;
//1 for x direction,2 for y direction
uniform int dir; uniform vec3 iResolution;
out vec4 outColor; void main()
{
vec2 tex_coord = gl_FragCoord.xy/iResolution.xy;
vec4 fetch = texture(tex_input,tex_coord);
float cur_x = gl_FragCoord.x - 0.5;
float cur_y = gl_FragCoord.y - 0.5; vec4 outv;
float tex_coord_n;
if(dir==)
{
//x dir
tex_coord_n = cur_x;
}
else
{
//y dir
tex_coord_n = cur_x;
} //updata weight
vec2 pre_w = fetch.rg;
float i = pre_w.r;
float n = pre_w.g;
float new_i;
float new_n;
new_i = i;
new_n = n*;
if(int(tex_coord_n)%(n_step*) > n_step*-)
{
new_i += n_step*;
}
outv.r = new_i;
outv.g = new_n;
//outv.rg = tex_coord; //updata index
vec2 pre_index = fetch.ba;
int x = int(pre_index.x);
int y = int(pre_index.y);
int ni = n_step*;
float new_tex_coord_n = tex_coord_n;
if((int(tex_coord_n)/ni)%==)
{
new_tex_coord_n += ni;
}
else
{
new_tex_coord_n -= ni;
} outv.b = ;
outv.a = new_tex_coord_n;
outColor = outv;
//outColor = vec4(tex_coord_n,tex_coord_n%n_step,tex_coord_n%n_step,tex_coord_n%n_step);
}

最后的

_ifft_div_shader_2d

是为了计算ifft,将每个计算结果除以一个N:

#version
uniform sampler2D tex_rgb;
uniform int N;
uniform vec3 iResolution; out vec4 outColor; void main()
{
vec2 tex_coord = gl_FragCoord.xy/iResolution.xy; vec2 outv; vec4 c = texture(tex_rgb,tex_coord); outv.r = c.r/N;
outv.g = c.g/N;
outColor = vec4(outv,,);
}

最后,out里面就是结果了。

对于将原点移动到中心多了以下shader:

        vec4 c;
if(tex_coord.x>0.5&&tex_coord.y>0.5)
{
c = texture(tex_rgb,tex_coord-vec2(0.5,0.5)); }
if(tex_coord.x>0.5&&tex_coord.y<0.5)
{
c = texture(tex_rgb,tex_coord+vec2(-0.5,0.5));
}
if(tex_coord.x<0.5&&tex_coord.y>0.5)
{
c = texture(tex_rgb,tex_coord+vec2(0.5,-0.5));
}
if(tex_coord.x<0.5&&tex_coord.y<0.5)
{
c = texture(tex_rgb,tex_coord+vec2(0.5,0.5));
}
outColor = c;

2维FFT算法实现——基于GPU的基2快速二维傅里叶变换的更多相关文章

  1. FFT算法实现——基于GPU的基2快速傅里叶变换

    最近做一个东西,要用到快速傅里叶变换,抱着蛋疼的心态,自己尝试写了一下,遇到一些问题. 首先看一下什么叫做快速傅里叶变换(FFT)(来自Wiki): 快速傅里叶变换(英语:Fast Fourier T ...

  2. JAVA描述算法和数据结构(01):稀疏数组和二维数组转换

    本文源码:GitHub·点这里 || GitEE·点这里 一.基本简介 1.基础概念 在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵:与之相反, ...

  3. 功能要求:定义一个两行三列的二维数组 names 并赋值,使用二重循环输出二维数组中的元素。

    功能要求:定义一个两行三列的二维数组 names 并赋值,使用二重循环输出二维数组中的元素 names={{"tom","jack","mike&qu ...

  4. 【算法系列学习】codeforces D. Mike and distribution 二维贪心

    http://codeforces.com/contest/798/problem/D http://blog.csdn.net/yasola/article/details/70477816 对于二 ...

  5. Android二维码开源项目zxing用例简化和生成二维码、条形码

    上一篇讲到:Android二维码开源项目zxing编译,编译出来后有一个自带的測试程序:CaptureActivity比較复杂,我仅仅要是把一些不用的东西去掉,用看起来更方便,二维码和条形码的流行性自 ...

  6. 今天网站后台登录页面需要生成一个二维码,然后在手机app上扫描这个二维码,实现网站登录的效果及其解决方案如下

    要实现二维码登录,需要解决2个技术,1.需要js websocket 与后台php实现长连接技术 2.实现二维码生成技术 要实现这个功能第二个算是比较简单,只需要下载一个php的二维码生成器即可,但要 ...

  7. 微信生成二维码 只需一个网址即刻 还有jquery生成二维码

    <div class="orderDetails-info"> <img src="http://qr.topscan.com/api.php?text ...

  8. 二维码解析:使用 JavaScript 库reqrcode.js解析二维码

    上次使用QRCode.js可以来生成二维码,但是我没有找到有文档说明可以对存在的二维码进行扫描解析其中的内容. 幸亏查找到了可行的解决方案,而且很好使哦!就是reqrcode.js 地址:https: ...

  9. 二维码生成:使用 JavaScript 库QRCode.js生成二维码

    QRCode.js:跨浏览器的javascript二维码生成库,支持html5的Canvas画布,没有任何依赖. Github 地址:https://github.com/davidshimjs/qr ...

随机推荐

  1. Linux下实现MySQL数据库自动备份

    1.给mysql创建用户备份的角色,并且授予角色SELECT, RELOAD, SHOW DATABASES, LOCK TABLES等权限. mysql> create user 'backu ...

  2. k8s architecture

    总体架构 对应的源码结构: https://docker-k8s-lab.readthedocs.io/en/latest/kubernetes/stepbystep.html

  3. 模拟登陆Github示例

    首先进入github登录页:https://github.com/login 输入账号密码,打开开发者工具,在Network页勾选上Preserve Log(显示持续日志),点击登录,查看Sessio ...

  4. Codeforces 543E. Listening to Music

    Description 题面 Solution 分块套分块,分别对时间和位置进行分块 差不多是一个定期保存信息的方法 对于询问我们不妨求出 \(>=x\) 的答案,然后用 \(m-(>=x ...

  5. BAT的关于程序员的那些事

    前言 你是否早有进入BAT公司的想法,但却因为对其不了解而在门外彷徨? 你是否想把技术团队打造成像BAT这些超级互联网公司,但却无从下手? 你是否已经进入了BAT,但是不知道如何晋升而苦恼? 那这篇文 ...

  6. redis(4)事务

    一.事务 一般来说,事务必须满足4个条件,也就是我们常说的ACID: 1)Atomicity 原子性:一个事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间的某个环节.事务在执行过程中发生错 ...

  7. mysql Backup &recovery

    备份数据库非常重要,这样您就可以恢复数据,并在发生问题时重新启动并运行,例如系统崩溃,硬件故障或用户错误地删除数据. 在升级MySQL安装之前,备份也是必不可少的保护措施,它们可用于将MySQL安装转 ...

  8. mysql中的find_in_set的使用

    原文 http://www.php-note.com/article/detail/383 举个例子来说: 有个文章表里面有个type字段,它存储的是文章类型,有 1头条.2推荐.3热点.4图文... ...

  9. Effective C++ .44 typename和class的不同

    在C++模板中的类型参数一般可以使用typename和class,两者没有什么不同.但是typename比class多项功能: “任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在 ...

  10. vue如何将单页面改造成多页面应用

    问题描述: 手头有一个项目是使用 vue-cli 搭建的单页面应用.项目分为了管理平台和用户查看页面,用户查看页面是很简单的页面,但是在加载过程中,却加载了整个应用的打包代码,量重且影响了响应和体验. ...