OpenCV+CUDA 遍历cv::Mat笔记

之前因项目需要,除了用到OpenCV,还要将代码移植到GPU里,今天整理总结一下,防止自己忘记。主要参考以下几篇文章,个人觉得都很有用,尤其是对于小白。
【学习OpenCV】Mat::data指针
OpenCV-Efficient pixel access using cv::Mat::ptr pointer
https://www.cnblogs.com/hustwx/p/8425640.html
cuda练习(一):使用cuda将rbg图像转为灰度图像
CUDA精进之路(三):图像处理——图像灰度化、灰度直方图统计
OpenCV学习(4) Mat的基本操作(1)
【OpenCV & CUDA】OpenCV和Cuda结合编程

总结为以下几条:
1.最重要的概念cv::Mat::data指针
对于最常见的BGR三通道图片来说,cv::Mat::data指针默认是uchar* 类型,用的时候可以用强制类型转换成需要的类型,比如uchar3*。

2.假设cv::Mat image 是一个cv::Vec3b类型的图片,创建一个uchar* 类型的指针 uchar* imagePtr,那么分给imagePtr的空间应该等于image所占用的空间

uchar* imagePtr = (uchar*)malloc(sizeof(uchar) * height * width * 3);//imagePtr是一个超长数组
imagePtr[(i * width + j) * 3 + 0] = image.at<cv::Vec3b>(i,j)[0];
imagePtr[(i * width + j) * 3 + 1] = image.at<cv::Vec3b>(i,j)[1];
imagePtr[(i * width + j) * 3 + 2] = image.at<cv::Vec3b>(i,j)[2];

或者使用cv::Mat::ptr指针取值赋给imagePtr数组

for(int i = 0; i < height; i++)
{
    uchar3* ptr = image.ptr<cv::Vec3b>(i);//创建指针是要注意指针类型要与图片类型对应
    for(int j = 0; j < width; j++)
    {
	imagePtr[(i * width + j) * 3 + 0] = ptr[j].x;
	imagePtr[(i * width + j) * 3 + 1] = ptr[j].y;
	imagePtr[(i * width + j) * 3 + 2] = ptr[j].z;
    }
}

3.创建一个新的cv::Mat image_,与image同样的尺寸和类型,image_.data = imagePtr;
Mat image_(image_size,TYPE,imagePtr);最好是先创建好图片,最后cudaMemcpy的时候直接传入image_.data中。

__global__ void swap_rb_kernel(const uchar3* src,uchar3* dst,int width,int height)
{
    int x = threadIdx.x + blockIdx.x * blockDim.x;
    int y = threadIdx.y + blockIdx.y * blockDim.y;
    
    if(x < width && y < height)
    {
        uchar3 v = src[y * width + x];
        dst[y * width + x].x = v.z;
        dst[y * width + x].y = v.y;
        dst[y * width + x].z = v.x;
    }
}

void swap_rb_caller(const uchar3* src,uchar3* dst,int width,int height)
{
    dim3 block(32,8);
    dim3 grid((width + block.x - 1)/block.x,(height + block.y - 1)/block.y);
    
    swap_rb_kernel<<<grid,block,0>>>(src,dst,width,height);
    cudaThreadSynchronize();
}

int main()
{
    Mat image = imread("lena.jpg");
    imshow("src",image);
    
    size_t memSize = image.cols*image.rows*sizeof(uchar3);
    uchar3* d_src = NULL;
    uchar3* d_dst = NULL;
    cudaMalloc((void**)&d_src,memSize);
    cudaMalloc((void**)&d_dst,memSize);
    cudaMemcpy(d_src,image.data,memSize,cudaMemcpyHostToDevice);
    
    swap_rb_caller(d_src,d_dst,image.cols,image.rows);
    
    cudaMemcpy(image.data,d_dst,memSize,cudaMemcpyDeviceToHost);
    imshow("gpu",image);
    waitKey(0);
    
    cudaFree(d_src);
    cudaFree(d_dst);
    return 0;
}

4.通过对以下几行代码进行测试:

uchar3 u3_arr[10]; uchar3* p2_arr = u3_arr; //p2_arr 转换成 uchar* 之后好使
uchar u_arr[30]; uchar* p_arr = u_arr; //p_arr 转换成 uchar3* 之后不好使,没有规律
cv::Mat image1(10, 10, CV_8UC3, cv::Scalar(100, 255, 0)); //image1.data 转换成 uchar3* 之后好使
cv::Mat image2(10, 10, CV_8UC1, cv::Scalar(66)); //image2.data 转换成 uchar3* 之后不好使

进行uchar3* 和uchar* 反复进行强制类型转换测试,
猜测:如果image是CV_8UC3类型的,那么将指针image.data强制类型转换成uchar3* 之后,(uchar3*)image.data是可以正常使用(包括解引和传递进d_src)的。
相邻地址差12,目前不知道为什么(sizeof(uchar3) = 3)。
如果image是CV_8UC1类型的,那么将指针image.data强制类型转换成uchar3* 之后,(uchar3*)image.data是不可以正常使用的。

其实,如果直接用image.data传给d_in,然后输出d_out也可以是uchar3* 类型

//此时d_in是uchar* 类型
d_out[i * width + j].x = d_in[(i * width + j) * 3 + 0];
d_out[i * width + j].y = d_in[(i * width + j) * 3 + 1];
d_out[i * width + j].z = d_in[(i * width + j) * 3 + 2];
//此时d_in是uchar3*类型
d_out[i * width + j].x = d_in[i * width + j].x;
d_out[i * width + j].y = d_in[i * width + j].y;
d_out[i * width + j].z = d_in[i * width + j].z;
//传给uchar3*类型的h_out
cudaMemcpy(h_out, d_out, sizeof(uchar3) * width * height, cudaMemcpyDeviceToHost);

你可能感兴趣的:(opencv,指针)