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 <opencv2\opencv.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("\n");
        }
        printf("%5d", dataC[i]);
    }
    printf("\n");
}  

2.OpenCV中Mat数组示例

输入:图像Lena.jpg

输出:图像moon.jpg

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

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

 

效果如下:

代码在此,以供参考

#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <opencv2\opencv.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);
}  
时间: 2024-11-09 05:04:29

OpenCV二维Mat数组(二级指针)在CUDA中的使用的相关文章

【C语言学习】指针再学习(二)之数组的指针

★一维数组 一个整型数据为4个字节.4个字节就是32位,即可以表示2^32个数字 在程序中定义一个数组a[5] = {1,2,3,4,5}; 那么a[0]的地址就是0x00000000,数组名a是数组首元素的地址,a的地址也是0x00000000.a+1则表示的地址是0x00000004,而不是0x00000001.因为1这个值,根据前面的指针a所指向的类型的长度来调整自己的长度.也就是说如果a是指向整型的指针,那么后面加的1也表示4个字节,如果a是指向字符型的指针,那么后面加的1表示1个字节.

一维动态数组和二维动态数组的创建和使用

#include<stdio.h> #include<malloc.h> void main(){ int *a,n=10,i; /* calloc()函数的原型是:(void *)calloc(unsigned n,unsigned size) calloc()函数用于向系统动态申请n个,每个占sizege字节的内存单元,函数返回值为所申请的内存空间首地址 malloc和calloc主要区别在于,当系统的内存只剩下一些非常小的碎片时,用calloc函数设计的动态数组的时间效率优于

字符串分割到二维字符数组中:

/* *字符串分割,把一个长的字符串(可能有空格),分割到一个二维字符数组中. *并且输出 * *时间复杂度O(N) *注意在操作二维字符串数组时:使用"数组指针"操作能方便 int(*p)[LEN]; * */ #include<stdio.h> #include<string.h> #include<stdlib.h> #include<stdbool.h> #define NDEBUG #include<assert.h>

结对开发之返回一个二维整数数组中最大联通子数组的和

一.题目要求 输入一个二维整形数组,数组里有正数也有负数.二维数组首尾相接,象个一条首尾相接带子一样.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n)题目:返回一个二维整数数组中最大子数组的和 二.解题思路 先对二维数组进行了重构,形成一个环状二维数组,然后再用求二维数组子矩阵最大和的方法求得最终结果. 三.程序代码 2 #include<iostream.h> 3 int main(int argc, char* argv[]

返回一个二维整数数组中最大联通子数组的和

题目: 输入一个二维整形数组,数组里有正数也有负数. 求所有子数组的和的最大值. 要求: 两人结对完成编程任务. 一人主要负责程序分析,代码编程. 一人负责代码复审和代码测试计划. 发表一篇博客文章发表一篇博客文章讲述设计思想,出现的问题,可能的解决方案(多选).源代码.结果截图.总结. 思想: 在看到本题目后,想了很久也没有想到比较满意的解决方法,觉得题目比较难,超出了我的能力范围.不过,个人认为可能用到了图论的知识,但是学的不好.根据上图给定的二维数组求解最大联通数组也许比较简单.以一个非负

返回一个二维整数数组中最大联通子数组的和6

1 问题:返回一个二维整数数组中最大联通子数组的和 2 思路:对n*m的二维数组进行分解,分解为n个一维数组,再先求这n个一维数组的最大子数组和,并记下每行最大一维子数组的下标如2-5,这是就会分两种情况第一种是行之间的最大子数组是相连的,如第一行是2-5,第二行是3-6,这是直接相加就行.第二种是不相连的如第一行是2-5,第二行是6-7,这时候就把每行的最大子数组看成一个整体,再使每个最大数组块进行相连,求使其相连的最小代价.最后就可求出最大联通子数组的和. 3 代码 #include<ios

返回一个首尾相接的二维整数数组中最大子数组的和

一.题目:返回一个二维整数数组中最大子数组的和. 二.要求: (1)输入一个二维整形数组,数组里有正数也有负数. (2)二维数组首尾相接,象个一条首尾相接带子一样. (3)数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. (4)求所有子数组的和的最大值. 三.解题思路: 将两个同样的数组首尾相接合并成一个数组,在用以前求二维数组最大子数组的方法求解 四.源代码: #include<iostream> using namespace std; #include"math

二维循环数组

一.题目与要求 题目.返回一个二维整数数组中最大子数组的和 要求.1.输入一个二维整形数组,数组里有正数也有负数. 2.二维数组首尾相接,象个一条首尾相接带子一样. 3.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和 二.设计思路 在上一次的以为循环数组的基础上,和二维数组求最大子数组相结合,将功能合并,完成题目要求. 第一步.将每一行的数组作为循环数组求最大子数组 第二步.通过枚举的方式,将每一种情况的和存入到新建二维数组中 第三部.通过逐列计算最大子数组的方法求所有子矩阵的最

leetcode——Search a 2D Matrix 二维有序数组查找(AC)

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: Integers in each row are sorted from left to right. The first integer of each row is greater than the last integer of the previous ro