如何在CUDA的内核函数中乘以两个openCV矩阵?

时间:2017-05-25 06:50:05

标签: c++ opencv matrix cuda

我有以下最小的代码片段,想知道如何在我的内核函数中乘以两个矩阵? 我可以在内核函数中创建一个Mat(就像在openCV中一样)。

  __global__ void myMatKernel(int N, Mat *b)
  {
       Mat a;   // creates compilation error 1

      // b = a*b;      <---- what I would need


  }

  int main (void)
  {
        Mat a(10, 1, CV_64F);
        a.setTo(Scalar(2.2));
        Mat c(1, 10, CV_64F);
        c.setTo(Scalar(3.35));
        Mat d;

        d = a*c;    // works perfectly fine, but would like to do this operation on the GPU

        Mat *b;
        cudaMallocManaged(&b, sizeof(Mat));
        cudaDeviceSynchronize();
       //assign somehow values to matrix b before passing it to the function

        myMatKernel<<<1,256>>>(1, b) ;   
        cudaFree(b);
  }

编译错误1:&#34;错误:不允许从__global__函数(&#34; myKernel&#34;)调用__host__函数(&#34; cv :: Mat :: Mat&#34;) #34;

有人可以解释/说明如何解决这些问题吗?

2 个答案:

答案 0 :(得分:1)

由于OpenCV为cv::Mat分配主机内存,因此您无法在内核中使用Mat和相关的OpenCV API,就像在主机代码中使用它一样。所以你必须为矩阵乘法编写自己的内核。

OpenCV提供了一个名为cv::cuda::GpuMat的类。 OpenCV为它们分配设备内存。但是,与GpuMat相关的API旨在用于主机代码。对于矩阵乘法,你必须编写自己的内核。

但是我有时会为GpuMat找到一些方便的API,例如使用其构造函数分配设备内存,并使用download()upload()在主机矩阵和设备矩阵之间复制数据。此外,Gpumat类会将矩阵的属性(例如rowscolstype()step等保留在一个结构中。对于某些情况,这可能会派上用场。

以下示例代码使用GpuMat

int main (void)
{
    Mat a{ 10, 1, CV_64FC1 }; // 10x1 matrix
    Mat b{ 1, 10, CV_64FC1 }; // 1x10 matrix
    Mat c{ 10, 10, CV_64FC1 }; // multiplying a and b results in 10x10 matrix
    a.setTo(Scalar(2.2f));
    b.setTo(Scalar(3.35f));

    cv::cuda::GpuMat d_a{ a.rows, a.cols, CV_64FC1 };
    cv::cuda::GpuMat d_b{ b.rows, b.cols, CV_64FC1 };
    cv::cuda::GpuMat d_c{ c.rows, c.cols, CV_64FC1 };

    d_a.upload(a);
    d_b.upload(b);

    MatMul<<<1, dim3(c.cols, c.rows)>>>((double*)d_a.data, d_a.step,
                                        (double*)d_b.data, d_b.step,
                                        (double*)d_c.data, d_c.step,
                                        a.cols);

    d_c.download(c);
}

__global__ void MatMul(const double* const a, const int a_step,
                       const double* const b, const int b_step,
                       double* const c, const int c_step,
                       const int a_cols)
{
    int c_row = threadIdx.y;
    int c_col = threadIdx.x;

    double sum = 0;
    for (int i = 0; i < a_cols; i++)
        sum += ((double*)((unsigned char*)a + c_row * a_step))[i]
             * ((double*)((unsigned char*)b + i * b_step))[c_col];

    ((double*)((unsigned char*)c + c_row * c_step))[c_col] = sum;

}

请注意,如果结果矩阵c的元素数超过了块中的最大线程数(对于cc&gt; = 2.0,则为1024),则此代码将不起作用。内核的设计应该不同。

修改

((double*)((unsigned char*)c + c_row * c_step))[c_col];

以上语句访问c_row - 矩阵c_col的第n行和第c列元素。该矩阵是单通道矩阵,元素类型是double。它的步骤由c_step给出。在OpenCV中,step指的是每行分配的字节数。它大于或等于每行中实际像素的总大小,以满足内存对齐,从而使内存访问更快。

上述语句首先将c(类型为double*)强制转换为unsigned char*,因为c_step以字节计算。将c_row * c_step添加到(unsigned char*)c可以指向c_row行的第0列。它现在将指针转换为double*以访问c_col - 具有标准数组访问运算符[]的列。

答案 1 :(得分:0)

我有这个问题,我无法将Mat对象发送到CUDA内核,我通过使用Mat类(.data)中的图像指针作为参数克服了这个问题,

所以代码将是:(我还没有编译它)

  __global__ void myMatKernel(double *d_a, double *d_b)
   {
     //index of this pixel
     int j = (blockIdx.x * blockDim.x) + threadIdx.x; //width
     int i = (blockIdx.y * blockDim.y) + threadIdx.y; //height

  // b = a*b;      <---- what I would need


   }

  int main (void)
  {
    Mat a(10, 1, CV_64F);
    a.setTo(Scalar(2.2));
    Mat c(1, 10, CV_64F);
    c.setTo(Scalar(3.35));
    Mat d;
    double* d_a;
    double* d_b;




    d = a*c;    // works perfectly fine, but would like to do this  operation on the GPU

    cudaMalloc((void**) &d_a, (a.rows)*(a.cols)*sizeof(double));
    cudaMalloc((void**) &d_b, (b.rows)*(b.cols)*sizeof(double));
    cudaMemcpy(d_a, a.data, (a.rows)*(a.cols)*sizeof(double), cudaMemcpyHostToDevice); 
    cudaMemcpy(d_b, b.data, (b.rows)*(b.cols)*sizeof(double), cudaMemcpyHostToDevice);

   //assign somehow values to matrix b before passing it to the function

    myMatKernel<<<1,256>>>(a,b) ;   
    cudaDeviceSynchronize();
    cudaFree(d_a);
    cudaFree(d_b);
  }

我一直在使用CUDA和openCV,检查这个code,它用于从视差图计算占用网格(它的功能是无关紧要的,检查它的结构可能会有所帮助)