使用OpenCV作为数据源,使用CUFFT实现复杂FFT

时间:2014-01-30 16:39:36

标签: opencv cuda fft

我在尝试使用cuFFT对浮点数组执行二维变换时遇到问题。我看过文档,但有些信息是矛盾的/不清楚的;所以我有几个问题:

我的数据为480行,包含640列(例如float data[480][640]但在单个维度中float data[480*640]

  1. 如果我们说我的输入维度(实际数据)是N1 = 480N2 = 640。尺寸(在真实到复杂的变换之后)N1=480, N2=321

  2. 我可以将cudaMemcpy数据直接导入相同大小的cufftReal数组吗?或者它必须是cufftComplex数组吗?

    如果它必须是cufftComplex数组,我假设元素需要代替实际组件?

  3. 根据上述值调用cufftPlan2dcufftExecR2CcufftC2R的正确结构是什么。

  4. 我认为现在一切都是......

    非常感谢提前

    编辑:因此,我按照JackOLantern的建议实现了正向和反向变换。然而,我的结果并不是我所期望的(FFT之后的结果与之前相同)。我有image gallery here显示两组示例。第一个来自我的房间,第二个来自我的大学项目。

    在cuFFT文档中,cufftPlan2d的使用存在歧义(因此我问的原因)。在文档中,对于二维数组,应按上述方式输入数据(float data[480][640] == float data[NY][NX])因此NY表示。但是在cufftPlan2d的函数列表中,它指出nx(参数)用于 ...

    在函数调用中交换NXNY的值会得到项目图像中的结果(正确的方向,但是以正常大小的1/4分成三个部分重叠的图像)但是,在他的答案中使用JackOLantern状态的参数给出了倾斜/倾斜的结果。

    我在这里做错了吗?或者cuFFT库是否存在此类问题。

    另外:我已经撤消了JackOLantern对这个问题所做的一些编辑,因为我的问题可能源于我的数据来自OpenCV。

2 个答案:

答案 0 :(得分:2)

编辑:我最近发现我是在使用该功能时犯了错误的人。

最初我虽然函数定义是指传递给它的数据的大小。

然而,出现,参数实际上直接指向 REAL 部分的大小。

这意味着参数参考:

  • 使用R2C(Real to Complex)时输入数据的大小
  • 使用C2R(Complex to Real)
  • 时输出数据的大小

所以看起来cuFFT文档和库本身并不对应。

当执行R2C后跟C2R(从实数到复数,分别为复数到实数)时,文档说明对于NX x NY维度的实数输入,复数输出为NX x(floor(NY / 2)+ 1);反之亦然。

然而实际输出的尺寸为NX x NY,实际输入的尺寸为NX x NY。 (一半)在第一页上提及

  

C2R - 实际输出的对称复数输入

暗示复杂数据必须是对称的,即除了非冗余数据外,还必须具有冗余数据。

文档中还有许多其他矛盾,我不会参与其中。

毋庸置疑,问题已经解决了。


我在下面加了一个MWE。在顶部附近有几行#define NUM_C2和适当的评论。更改此更改是否遵循文档格式或我的“修复”。

输出

  1. 输入实际数据
  2. 中级复杂数据
  3. 输出真实数据
  4. 输出数据与输入数据的比率(存在微小的FFT误差,~1表示正确)
  5. 如果您认为我在某个地方犯了错误,请随意更改参数(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)在给定上述值的情况下,对cufftPlan2dcufftExecR2CcufftC2R的调用的正确结构是什么。

cufftPlan2d(&plan, N1, N2, CUFFT_R2C);
cufftExecR2C(plan, (cufftReal*)idata, (cufftComplex*) odata);