C++ 用英特尔MKL实现三维卷积

C++ 用英特尔MKL实现三维卷积,c++,c,3d,convolution,intel-mkl,C++,C,3d,Convolution,Intel Mkl,我已经编写了一个C/C++代码,它使用英特尔MKL计算一个数组的三维卷积,这个数组大约有300×200×200个元素。我想应用一个内核,它要么是3×3×3要么是5×5×5。3D输入数组和内核都有实际值 此3D数组以列方式存储为double类型的1D数组。类似地,内核的类型为double,并按列保存。比如说, for( int k = 0; k < nk; k++ ) // Loop through the height. for( int j = 0; j < nj; j++

我已经编写了一个
C/C++
代码,它使用英特尔MKL计算一个数组的三维卷积,这个数组大约有
300×200×200
个元素。我想应用一个内核,它要么是
3×3×3
要么是
5×5×5
。3D输入数组和内核都有实际值

此3D数组以列方式存储为
double
类型的1D数组。类似地,内核的类型为
double
,并按列保存。比如说,

for( int k = 0; k < nk; k++ ) // Loop through the height.
    for( int j = 0; j < nj; j++ ) // Loop through the rows.
        for( int i = 0; i < ni; i++ ) // Loop through the columns.
        {
            ijk = i + ni * j + ni * nj * k;
            my3Darray[ ijk ] = 1.0;
        }
这是我的
C/C++
代码:

#include <stdio.h>
#include "mkl.h"

void Conv3D(
    double *in, double *ker, double *out,
    int nRows, int nCols, int nHeights)
{

    int NI = nRows;
    int NJ = nCols;
    int NK = nHeights;

    double *in_fft  = new double [NI*NJ*NK];
    double *ker_fft = new double [NI*NJ*NK];

    DFTI_DESCRIPTOR_HANDLE fft_desc = 0;
    MKL_LONG sizes[]   = { NK, NJ, NI };
    MKL_LONG strides[] = { 0, NJ*NI, NI, 1 };

    DftiCreateDescriptor( &fft_desc, DFTI_DOUBLE, DFTI_REAL, 3, sizes     );
    DftiSetValue        (  fft_desc, DFTI_PLACEMENT     , DFTI_NOT_INPLACE);   // Out-of-place computation.
    DftiSetValue        (  fft_desc, DFTI_INPUT_STRIDES , strides         );
    DftiSetValue        (  fft_desc, DFTI_OUTPUT_STRIDES, strides         );
    DftiSetValue        (  fft_desc, DFTI_BACKWARD_SCALE, 1/NI/NJ/NK      );
    DftiCommitDescriptor(  fft_desc );

    DftiComputeForward  (  fft_desc, in , in_fft  );
    DftiComputeForward  (  fft_desc, ker, ker_fft );

    for (long long i = 0; i < (long long)NI*NJ*NK; ++i )
        out[i] = in_fft[i]*ker_fft[i];

    // In-place computation.
    DftiSetValue        (  fft_desc, DFTI_PLACEMENT, DFTI_INPLACE );
    DftiCommitDescriptor(  fft_desc      );
    DftiComputeBackward (  fft_desc, out );

    DftiFreeDescriptor  ( &fft_desc );

    delete[] in_fft;
    delete[] ker_fft;

}

int main(int argc, char* argv[])
{
    int n = 10;
    int nkernel = 3;

    double *a          = new double [n*n*n]; // This array is real.
    double *aconvolved = new double [n*n*n]; // The convolved array is also real.
    double *kernel     = new double [nkernel*nkernel*nkernel]; // kernel is real.

    // Fill the array with some 'real' numbers.
    for( int i = 0; i < n*n*n; i++ )
        a[ i ] = 1.0;

    // Fill the kernel with some 'real' numbers.
    for( int i = 0; i < nkernel*nkernel*nkernel; i++ )
        kernel[ i ] = 1.0;

    // Calculate the convolution.
    Conv3D( a, kernel, aconvolved, n, n, n );

    printf("Convolved:\n");
    for( int i = 0; i < n*n*n; i++ )
        printf( "%15.8f\n", aconvolved[i] );

    delete[] a;
    delete[] kernel;
    delete[] aconvolved;

    return 0;
}
#包括
#包括“mkl.h”
void Conv3D(
双进双出,
整数n行,整数n行,整数n行)
{
int NI=nRows;
int NJ=nCols;
int NK=n重量;
double*in_fft=新的double[NI*NJ*NK];
double*keru fft=新的双精度[NI*NJ*NK];
DFTI描述符处理fft描述=0;
MKL_长尺寸[]={NK,NJ,NI};
MKL_大步[]={0,NJ*NI,NI,1};
DftiCreateDescriptor(&fft_desc,DFTI_DOUBLE,DFTI_REAL,3,大小);
DftiSetValue(fft_desc,DFTI_PLACEMENT,DFTI_NOT_INPLACE);//错位计算。
DftiSetValue(fft_desc,DFTI_输入_步长,步长);
DftiSetValue(fft_desc,DFTI_输出_步长,步长);
DftiSetValue(fft描述,DFTI向后标度,1/NI/NJ/NK);
DftiCommitDescriptor(fft_desc);
DFTIComputerForward(fft_desc,in,in_fft);
DFTIComputerForward(fft_desc,ker,ker_fft);
对于(长i=0;i<(长)NI*NJ*NK;++i)
out[i]=in_fft[i]*ker_fft[i];
//就地计算。
DftiSetValue(fft描述、DFTI放置、DFTI放置);
DftiCommitDescriptor(fft_desc);
DFTICOMPUTER向后(fft_desc,out);
DftiFreeDescriptor(&fft_desc);
删除fft中的[];
删除[]k_fft;
}
int main(int argc,char*argv[])
{
int n=10;
int nkernel=3;
double*a=新的double[n*n*n];//此数组是实数组。
double*a卷积=新的double[n*n*n];//卷积数组也是实数组。
double*kernel=newdouble[nkernel*nkernel*nkernel];//内核是真实的。
//用一些“实数”填充数组。
对于(int i=0;i
不能使用实值频率数据(仅幅度)反转FFT。前向FFT需要输出复杂数据。这是通过将DFTI\U复数设置为

DftiCreateDescriptor( &fft_desc, DFTI_DOUBLE, DFTI_COMPLEX, 3, sizes     );
这样做会隐式地将向后域设置为complex

您还需要一个复杂的数据类型。大概是

MKL_Complex16* in_fft  = new MKL_Complex16[NI*NJ*NK];
这意味着您必须将实部和虚部相乘:

for (size_t i = 0; i < (size_t)NI*NJ*NK; ++i) {
    out_fft[i].real = in_fft[i].real * ker_fft[i].real;
    out_fft[i].imag = in_fft[i].imag * ker_fft[i].imag;
}
用于(尺寸i=0;i<(尺寸)NI*NJ*NK;++i){
out\u fft[i]。real=in\u fft[i]。real*ker\u fft[i]。real;
out_fft[i].imag=in_fft[i].imag*ker_fft[i].imag;
}
逆FFT的输出也很复杂,假设您的输入数据是真实的,您只需获取
.real
分量,这就是您的结果。这意味着您将需要一个临时的复杂输出数组(如上所述,
out\u fft

还要注意,为了避免伪影,您希望fft的大小在每个维度上(至少)为M+N-1。一般情况下,你会选择下一个最高的两倍的速度


我强烈建议您首先使用FFTs在MATLAB中实现它。有很多这样的实现(),但我会从基础开始,自己做一个简单的函数。

我喜欢你的问题,但我无能为力。然而,如果你给了别人期望的结果和你的结果,他们可能会帮助你。
for (size_t i = 0; i < (size_t)NI*NJ*NK; ++i) {
    out_fft[i].real = in_fft[i].real * ker_fft[i].real;
    out_fft[i].imag = in_fft[i].imag * ker_fft[i].imag;
}