我有以下最小的代码片段,想知道如何在我的内核函数中乘以两个矩阵? 我可以在内核函数中创建一个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;
有人可以解释/说明如何解决这些问题吗?
答案 0 :(得分:1)
由于OpenCV为cv::Mat
分配主机内存,因此您无法在内核中使用Mat
和相关的OpenCV API,就像在主机代码中使用它一样。所以你必须为矩阵乘法编写自己的内核。
OpenCV提供了一个名为cv::cuda::GpuMat
的类。 OpenCV为它们分配设备内存。但是,与GpuMat
相关的API旨在用于主机代码。对于矩阵乘法,你必须编写自己的内核。
但是我有时会为GpuMat
找到一些方便的API,例如使用其构造函数分配设备内存,并使用download()
和upload()
在主机矩阵和设备矩阵之间复制数据。此外,Gpumat
类会将矩阵的属性(例如rows
,cols
,type()
,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,它用于从视差图计算占用网格(它的功能是无关紧要的,检查它的结构可能会有所帮助)