zoukankan      html  css  js  c++  java
  • OpenCV二维Mat数组(二级指针)在CUDA中的使用

      CUDA用于并行计算非常方便,但是GPU与CPU之间的交互,比如传递参数等相对麻烦一些。在写CUDA核函数的时候形参往往会有很多个,动辄达到10-20个,如果能够在CPU中提前把数据组织好,比如使用二维数组,这样能够省去很多参数,在核函数中可以使用二维数组那样去取数据简化代码结构。当然使用二维数据会增加GPU内存的访问次数,不可避免会影响效率,这个不是今天讨论的重点了。

      举两个代码栗子来说明二维数组在CUDA中的使用(亲测可用):

    1. 普通二维数组示例:

    输入:二维数组A(8行4列)

    输出:二维数组C(8行4列)

    函数功能:将数组A中的每一个元素加上10,并保存到C中对应位置。

      这个是一个简单的示例,以一级指针和二级指针开访问二维数组中的数据,主要步骤如下:

    (1)为二级指针A、C和一级指针dataA、dataC分配CPU内存。二级指针指向的内存中保存的是一级指针的地址。一级指针指向的内存中保存的是输入、输出数据。

    (2)在设备端(GPU)上同样建立二级指针d_A、d_C和一级指针d_dataA、d_dataC,并分配GPU内存,原理同上,不过指向的内存都是GPU中的内存。

    (3)通过主机端一级指针dataA将输入数据保存到CPU中的二维数组中。

    (4)关键一步:将设备端一级指针的地址保存到主机端二级指针指向的CPU内存中。

    (5)关键一步:使用cudaMemcpy()函数,将主机端二级指针中的数据(设备端一级指针的地址)拷贝到设备端二级指针指向的GPU内存中。这样在设备端就可以使用二级指针来访问一级指针的地址,然后利用一级指针访问输入数据。也就是A[][]、C[][]的用法。

    (6)使用cudaMemcpy()函数将主机端一级指针指向的CPU内存空间中的输入数据,拷贝到设备端一级指针指向的GPU内存中,这样输入数据就算上传到设备端了。

    (7)在核函数addKernel()中就可以使用二维数组的方法进行数据的读取、运算和写入。

    (8)最后将设备端一级指针指向的GPU内存中的输出数据拷贝到主机端一级指针指向的CPU内存中,打印显示即可。

    #include <cuda_runtime.h>
    #include <device_launch_parameters.h>
    #include <opencv2opencv.hpp>
    #include <iostream>
    #include <string>
    
    using namespace cv;
    using namespace std;
    
    #define Row  8  
    #define Col 4  
      
    __global__ void addKernel(int **C,  int **A)  
    {  
        int idx = threadIdx.x + blockDim.x * blockIdx.x;  
        int idy = threadIdx.y + blockDim.y * blockIdx.y;  
        if (idx < Col && idy < Row)
        {  
            C[idy][idx] = A[idy][idx] + 10;  
        }  
    }  
    
    int main()  
    {  
        int **A = (int **)malloc(sizeof(int*) * Row);  
        int **C = (int **)malloc(sizeof(int*) * Row);  
        int *dataA = (int *)malloc(sizeof(int) * Row * Col);  
        int *dataC = (int *)malloc(sizeof(int) * Row * Col);  
    
        int **d_A;  
        int **d_C;  
        int *d_dataA;  
        int *d_dataC;  
        //malloc device memory
        cudaMalloc((void**)&d_A, sizeof(int **) * Row);  
        cudaMalloc((void**)&d_C, sizeof(int **) * Row);  
        cudaMalloc((void**)&d_dataA, sizeof(int) *Row*Col);  
        cudaMalloc((void**)&d_dataC, sizeof(int) *Row*Col);  
        //set value  
        for (int i = 0; i < Row*Col; i++) 
        {  
            dataA[i] = i+1;  
        }  
        //将主机指针A指向设备数据位置,目的是让设备二级指针能够指向设备数据一级指针
        //A 和 dataA 都传到了设备上,但是二者还没有建立对应关系
        for (int i = 0; i < Row; i++) 
        {  
            A[i] = d_dataA + Col * i;  
            C[i] = d_dataC + Col * i;  
        }  
          
        cudaMemcpy(d_A, A, sizeof(int*) * Row, cudaMemcpyHostToDevice);  
        cudaMemcpy(d_C, C, sizeof(int*) * Row, cudaMemcpyHostToDevice);  
        cudaMemcpy(d_dataA, dataA, sizeof(int) * Row * Col, cudaMemcpyHostToDevice);  
        dim3 block(4, 4);  
        dim3 grid( (Col + block.x - 1)/ block.x, (Row + block.y - 1) / block.y );  
        addKernel << <grid, block >> > (d_C, d_A);  
        //拷贝计算数据-一级数据指针  
        cudaMemcpy(dataC, d_dataC, sizeof(int) * Row * Col, cudaMemcpyDeviceToHost);  
      
        for (int i = 0; i < Row*Col; i++) 
        {  
            if (i%Col == 0) 
            {  
                printf("
    ");  
            }  
            printf("%5d", dataC[i]);  
        }  
        printf("
    ");  
    }  

    2.OpenCV中Mat数组示例

    输入:图像Lena.jpg

    输出:图像moon.jpg

    函数功能:求两幅图像加权和

      原理和上面一样,流程上的差别就是输入的二维数据是下面两幅图像数据,然后在CUDA中进行加权求和。

     

    效果如下:

    代码在此,以供参考

    #include <cuda_runtime.h>
    #include <device_launch_parameters.h>
    #include <opencv2opencv.hpp>
    #include <iostream>
    #include <string>
    
    using namespace cv;
    using namespace std;
      
    __global__ void addKernel(uchar **pSrcImg,  uchar* pDstImg, int imgW, int imgH)  
    {  
        int tidx = threadIdx.x + blockDim.x * blockIdx.x;  
        int tidy = threadIdx.y + blockDim.y * blockIdx.y;  
        if (tidx<imgW && tidy<imgH)
        {
            int idx=tidy*imgW+tidx;
            uchar lenaValue=pSrcImg[0][idx];
            uchar moonValue=pSrcImg[1][idx];
            pDstImg[idx]= uchar(0.5*lenaValue+0.5*moonValue);
        }
    }  
    
    int main()  
    {  
        //OpenCV读取两幅图像
        Mat img[2];
        img[0]=imread("data/lena.jpg", 0);
        img[1]=imread("data/moon.jpg", 0);
        int imgH=img[0].rows;
        int imgW=img[0].cols;
        //输出图像
        Mat dstImg=Mat::zeros(imgH, imgW, CV_8UC1);
        //主机指针
        uchar **pImg=(uchar**)malloc(sizeof(uchar*)*2); //输入 二级指针
    
        //设备指针
        uchar **pDevice;//输入 二级指针
        uchar *pDeviceData;//输入 一级指针
        uchar *pDstImgData;//输出图像对应设备指针
    
        //分配GPU内存
        cudaError err;
        //目标输出图像分配GPU内存
        err=cudaMalloc(&pDstImgData, imgW*imgH*sizeof(uchar));
        //设备二级指针分配GPU内存
        err=cudaMalloc(&pDevice, sizeof(uchar*)*2);
        //设备一级指针分配GPU内存
        err=cudaMalloc(&pDeviceData, sizeof(uchar)*imgH*imgW*2);
        
        //关键:主机二级指针指向设备一级指针位置,这样才能使设备的二级指针指向设备的一级指针位置
        for (int i=0; i<2; i++)
        {
            pImg[i]=pDeviceData+i*imgW*imgH;
        }
    
        //拷贝数据到GPU
        //拷贝主机二级指针中的元素到设备二级指针指向的GPU位置 (这个二级指针中的元素是设备中一级指针的地址)
        err=cudaMemcpy(pDevice, pImg, sizeof(uchar*)*2, cudaMemcpyHostToDevice);
        //拷贝图像数据(主机一级指针指向主机内存) 到 设备一级指针指向的GPU内存中
        err=cudaMemcpy(pDeviceData, img[0].data, sizeof(uchar)*imgH*imgW, cudaMemcpyHostToDevice);
        err=cudaMemcpy(pDeviceData+imgH*imgW, img[1].data, sizeof(uchar)*imgH*imgW, cudaMemcpyHostToDevice);
    
        //核函数实现lena图和moon图的简单加权和
        dim3 block(8, 8);
        dim3 grid( (imgW+block.x-1)/block.x, (imgH+block.y-1)/block.y);
        addKernel<<<grid, block>>>(pDevice, pDstImgData, imgW, imgH);
        cudaThreadSynchronize();
    
        //拷贝输出图像数据至主机,并写入到本地
        err=cudaMemcpy(dstImg.data, pDstImgData, imgW*imgH*sizeof(uchar), cudaMemcpyDeviceToHost);
        imwrite("data/synThsis.jpg", dstImg);
    }  
  • 相关阅读:
    Best wishes for a wonderful new year.
    Using X++ code Reading to CSV file
    Types of delete action
    get focus from the FORM in dynamcis AX 2009
    Database Lock
    Using x++ code export to CSV file from dynamics AX 2009
    Using x++ code updated to system filed values
    Merry Christmas and Best Wishes for a Happy New Year
    the most reluctant to delete to New Year SMS
    《那些年啊,那些事——一个程序员的奋斗史》——53
  • 原文地址:https://www.cnblogs.com/riddick/p/7821452.html
Copyright © 2011-2022 走看看