我在尝试使用cuFFT对浮点数组执行二维变换时遇到问题。我看过文档,但有些信息是矛盾的/不清楚的;所以我有几个问题:
我的数据为480
行,包含640
列(例如float data[480][640]
但在单个维度中float data[480*640]
)
如果我们说我的输入维度(实际数据)是N1 = 480
和N2 = 640
。尺寸(在真实到复杂的变换之后)N1=480, N2=321
?
我可以将cudaMemcpy
数据直接导入相同大小的cufftReal
数组吗?或者它必须是cufftComplex
数组吗?
如果它必须是cufftComplex
数组,我假设元素需要代替实际组件?
根据上述值调用cufftPlan2d
,cufftExecR2C
和cufftC2R
的正确结构是什么。
我认为现在一切都是......
非常感谢提前
编辑:因此,我按照JackOLantern的建议实现了正向和反向变换。然而,我的结果并不是我所期望的(FFT之后的结果与之前相同)。我有image gallery here显示两组示例。第一个来自我的房间,第二个来自我的大学项目。
在cuFFT文档中,cufftPlan2d
的使用存在歧义(因此我问的原因)。在文档中,对于二维数组,应按上述方式输入数据(float data[480][640] == float data[NY][NX]
)因此NY
表示行。但是在cufftPlan2d
的函数列表中,它指出nx
(参数)用于行 ...
在函数调用中交换NX
和NY
的值会得到项目图像中的结果(正确的方向,但是以正常大小的1/4分成三个部分重叠的图像)但是,在他的答案中使用JackOLantern状态的参数给出了倾斜/倾斜的结果。
我在这里做错了吗?或者cuFFT库是否存在此类问题。
另外:我已经撤消了JackOLantern对这个问题所做的一些编辑,因为我的问题可能源于我的数据来自OpenCV。
答案 0 :(得分:2)
编辑:我最近发现我是在使用该功能时犯了错误的人。
最初我虽然函数定义是指传递给它的数据的大小。
然而,出现,参数实际上直接指向 REAL 部分的大小。
这意味着参数参考:
所以看起来cuFFT文档和库本身并不对应。
当执行R2C后跟C2R(从实数到复数,分别为复数到实数)时,文档说明对于NX x NY维度的实数输入,复数输出为NX x(floor(NY / 2)+ 1);反之亦然。
然而实际输出的尺寸为NX x NY,实际输入的尺寸为NX x NY。 是(一半)在第一页上提及
C2R - 实际输出的对称复数输入
暗示复杂数据必须是对称的,即除了非冗余数据外,还必须具有冗余数据。
文档中还有许多其他矛盾,我不会参与其中。
毋庸置疑,问题已经解决了。
我在下面加了一个MWE。在顶部附近有几行#define NUM_C2
和适当的评论。更改此更改是否遵循文档格式或我的“修复”。
输出
如果您认为我在某个地方犯了错误,请随意更改参数(NUM_R和NUM_C)并随意发表评论。
#include <iostream>
#include <math.h>
#include <cufft.h>
// e.g. float data[NUM_R][NUM_C]
#define NUM_R 12
#define NUM_C 16
// Documentation Version
//#define NUM_C2 (1+NUM_C/2)
// "Correct" Version
#define NUM_C2 NUM_C
using namespace std;
int main(int argc, char** argv)
{
cufftReal *in_h, *out_h, *in_d, *out_d;
cufftComplex *mid_d, *mid_h;
cufftHandle pF, pI;
int r, c;
in_h = (cufftReal*) malloc(NUM_R * NUM_C * sizeof(cufftReal));
out_h= (cufftReal*) malloc(NUM_R * NUM_C * sizeof(cufftReal));
mid_h= (cufftComplex*)malloc(NUM_C2*NUM_R*sizeof(cufftComplex));
cudaMalloc((void**) &in_d, NUM_R * NUM_C * sizeof(cufftReal));
cudaMalloc((void**)&out_d, NUM_R * NUM_C * sizeof(cufftReal));
cudaMalloc((void**)&mid_d, NUM_C2 * NUM_R * sizeof(cufftComplex));
cufftPlan2d(&pF, NUM_R, NUM_C, CUFFT_R2C);
cufftPlan2d(&pI, NUM_R,NUM_C2, CUFFT_C2R);
cout<<endl<<"------"<<endl;
for(r=0; r<NUM_R; r++)
{
for(c=0; c<NUM_C; c++)
{
in_h[c + NUM_C * r] = cos(2.0*M_PI*(c*7.0/NUM_C+r*3.0/NUM_R));
out_h[c+ NUM_C * r] = 0.f;
cout<<in_h[c+NUM_C*r];
if(c<(NUM_C-1)) cout<<", ";
else cout<<endl;
}
}
cudaMemcpy((cufftReal*)in_d, (cufftReal*)in_h, NUM_R * NUM_C * sizeof(cufftReal),cudaMemcpyHostToDevice);
cufftExecR2C(pF, (cufftReal*)in_d, (cufftComplex*)mid_d);
cudaMemcpy((cufftComplex*)mid_h, (cufftComplex*)mid_d, NUM_C2*NUM_R*sizeof(cufftComplex), cudaMemcpyDeviceToHost);
cout<<endl<<"------"<<endl;
for(r=0; r<NUM_R; r++)
{
for(c=0; c<NUM_C2; c++)
{
cout<<mid_h[c+(NUM_C2)*r].x<<"|"<<mid_h[c+(NUM_C2)*r].y;
if(c<(NUM_C2-1)) cout<<", ";
else cout<<endl;
}
}
cufftExecC2R(pI, (cufftComplex*)mid_d, (cufftReal*)out_d);
cudaMemcpy((cufftReal*)out_h, (cufftReal*)out_d, NUM_R*NUM_C*sizeof(cufftReal), cudaMemcpyDeviceToHost);
cout<<endl<<"------"<<endl;
for(r=0; r<NUM_R; r++)
{
for(c=0; c<NUM_C; c++)
{
cout<<out_h[c+NUM_C*r]/(NUM_R*NUM_C);
if(c<(NUM_C-1)) cout<<", ";
else cout<<endl;
}
}
cout<<endl<<"------"<<endl;
for(r=0; r<NUM_R; r++)
{
for(c=0; c<NUM_C; c++)
{
cout<<(out_h[c+NUM_C*r]/(NUM_R*NUM_C))/in_h[c+NUM_C*r];
if(c<(NUM_C-1)) cout<<", ";
else cout<<endl;
}
}
free(in_h);
free(out_h);
free(mid_h);
cudaFree(in_d);
cudaFree(out_h);
cudaFree(mid_d);
return 0;
}
答案 1 :(得分:1)
1)如果我们说我的输入维度(实际数据)是N1 = 480和N2 = 640.尺寸(在真实到复数变换之后)是N1 = 480,N2 = 321?
cufftExecR2C的输出是NX*(NY/2+1)
cufftComplex
矩阵。因此,在您的情况下,您将使用480x321
float2
矩阵作为输出。
2)我可以将数据直接发送到相同大小的
cufftReal
数组吗?或者它必须是cufftComplex
数组吗?如果它必须是
cufftComplex
数组,我假设元素需要代替实际组件?
是的,您可以将数据复制到cufftReal
数组和N1xN2
数据。
3)在给定上述值的情况下,对
cufftPlan2d
,cufftExecR2C
和cufftC2R
的调用的正确结构是什么。
cufftPlan2d(&plan, N1, N2, CUFFT_R2C);
cufftExecR2C(plan, (cufftReal*)idata, (cufftComplex*) odata);