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] + ;
}
} 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 = ; i < Row*Col; i++)
{
dataA[i] = i+;
}
//将主机指针A指向设备数据位置,目的是让设备二级指针能够指向设备数据一级指针
//A 和 dataA 都传到了设备上,但是二者还没有建立对应关系
for (int i = ; 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(, );
dim3 grid( (Col + block.x - )/ block.x, (Row + block.y - ) / block.y );
addKernel << <grid, block >> > (d_C, d_A);
//拷贝计算数据-一级数据指针
cudaMemcpy(dataC, d_dataC, sizeof(int) * Row * Col, cudaMemcpyDeviceToHost); for (int i = ; i < Row*Col; i++)
{
if (i%Col == )
{
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[][idx];
uchar moonValue=pSrcImg[][idx];
pDstImg[idx]= uchar(0.5*lenaValue+0.5*moonValue);
}
} int main()
{
//OpenCV读取两幅图像
Mat img[];
img[]=imread("data/lena.jpg", );
img[]=imread("data/moon.jpg", );
int imgH=img[].rows;
int imgW=img[].cols;
//输出图像
Mat dstImg=Mat::zeros(imgH, imgW, CV_8UC1);
//主机指针
uchar **pImg=(uchar**)malloc(sizeof(uchar*)*); //输入 二级指针 //设备指针
uchar **pDevice;//输入 二级指针
uchar *pDeviceData;//输入 一级指针
uchar *pDstImgData;//输出图像对应设备指针 //分配GPU内存
cudaError err;
//目标输出图像分配GPU内存
err=cudaMalloc(&pDstImgData, imgW*imgH*sizeof(uchar));
//设备二级指针分配GPU内存
err=cudaMalloc(&pDevice, sizeof(uchar*)*);
//设备一级指针分配GPU内存
err=cudaMalloc(&pDeviceData, sizeof(uchar)*imgH*imgW*); //关键:主机二级指针指向设备一级指针位置,这样才能使设备的二级指针指向设备的一级指针位置
for (int i=; i<; i++)
{
pImg[i]=pDeviceData+i*imgW*imgH;
} //拷贝数据到GPU
//拷贝主机二级指针中的元素到设备二级指针指向的GPU位置 (这个二级指针中的元素是设备中一级指针的地址)
err=cudaMemcpy(pDevice, pImg, sizeof(uchar*)*, cudaMemcpyHostToDevice);
//拷贝图像数据(主机一级指针指向主机内存) 到 设备一级指针指向的GPU内存中
err=cudaMemcpy(pDeviceData, img[].data, sizeof(uchar)*imgH*imgW, cudaMemcpyHostToDevice);
err=cudaMemcpy(pDeviceData+imgH*imgW, img[].data, sizeof(uchar)*imgH*imgW, cudaMemcpyHostToDevice); //核函数实现lena图和moon图的简单加权和
dim3 block(, );
dim3 grid( (imgW+block.x-)/block.x, (imgH+block.y-)/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);
}

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

  1. 计算机二级-C语言-程序填空题-190109记录-对二维字符串数组的处理

    //给定程序,函数fun的功能是:求出形参ss所指字符串数组中最长字符串的长度,将其余字符串右边用字符*补齐,使其与最长的字符串等长.ss所指字符串数组中共有M个字符串,且串长<N. //重难点 ...

  2. 二维字符数组利用gets()函数输入

    举例: ][]; ;i<;i++) gets(a[i]); a是二维字符数组的数组名,相当于一维数组的指针, 所以a[i]就相当于指向第i个数组的指针,类型就相当于char *,相当于字符串.

  3. 分配一维动态数组or 二维动态数组的方法以及学习 new 方法or vector

    先来个开胃菜 // 使用new动态分配存储空间 #include<iostream> using std::cout; int main() { // 第1种方式 int *a=new i ...

  4. 【C/C++】二维数组的传参的方法/二维字符数组的声明,使用,输入,传参

    [问题] 定义了一个子函数,传参的内容是一个二维数组 编译提示错误 因为多维数组作为形参传入时,必须声明除第一位维外的确定值,否则系统无法编译(算不出偏移地址) [二维数组的传参] 方法一:形参为二维 ...

  5. PHP二维关联数组的遍历方式

    采用foreach循环对二维索引数组进行遍历,相对来讲速度更快,效率更高,foreach循环是PHP中专门用来循环数组的.实例也相对简单,多加练习,想清楚程序运行逻辑即可. <?php $arr ...

  6. Task 4.4二维环形数组求最大子矩阵之和

    任务: (1)输入一个二维整形数组,数组里有正数也有负数. (2)二维数组首尾相接,象个一条首尾相接带子一样. (3)数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. (4)求所有子数 ...

  7. 子串查询(二维前缀数组) 2018"百度之星"程序设计大赛 - 资格赛

    子串查询 Time Limit: 3500/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Subm ...

  8. go 动态数组 二维动态数组

    go使用动态数组还有点麻烦,比python麻烦一点,需要先定义. 动态数组申明 var dynaArr []string 动态数组添加成员 dynaArr = append(dynaArr, &quo ...

  9. int (*p)[4] p 是二级指针 二维数组 二级指针 .xml

    pre{ line-height:1; color:#2f88e4; background-color:#e9ffff; font-size:16px;}.sysFunc{color:#3d7477; ...

随机推荐

  1. Stochastic Gradient Descent

    一.从Multinomial Logistic模型说起 1.Multinomial Logistic 令为维输入向量; 为输出label;(一共k类); 为模型参数向量: Multinomial Lo ...

  2. FPGA IN 金融领域

    何为金融: 金融指货币的发行.流通和回笼,贷款的发放和收回,存款的存入和提取,汇兑的往来等经济活动.金融(FIN)就是对现有资源进行重新整合之后,实现价值和利润的等效流通. 金融主要包括银行.证券.基 ...

  3. JavaScript面向对象基础与this指向问题

      前  言           我们的程序语言经历了从"面向机器".到"面向过程".再到"面向对象"的一个过程.而JavaScript是一 ...

  4. 【学习】条码扫描器:QuaggaJS

    QuaggaJS是条形码扫描器完全用JavaScript编写,支持实时对各类条码进行定位和解码,如EAN和CODE128.该库还能够使用getUserMedia获得直接访问用户的摄像头流.为了充分利用 ...

  5. hibernate5使用注解遇到的问题

    问题描述 出现MappingException:Unknown entity,看到这个我以为在cfg配置文件中没有配置,实际上我是配置了的,那么问题出在那里呢,既然找不到实体,那么会不会是注解类出现了 ...

  6. Java IO(Properties/对象序列化/打印流/commons-io)

    Java IO(Properties/对象序列化/打印流/commons-io) Properties Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载. ...

  7. 如何学习LoadRunner性能测试?

    最近组内同事针对性能测试LR的脚本部分做了介绍,是个不错的分享.会后反思自己也有很长一段时间没做性能测试了,根据以往的经验,有必要做些整理和补充,本文主要介绍一些Loadrunner性能测试的学习方法 ...

  8. 读阮一峰老师 es6 入门笔记 —— 第一章

    鉴于最近用 vuejs 框架开发项目,其中有很多涉及到 es6 语法不太理解所以便认真地读了一下这本书. 地址:http://es6.ruanyifeng.com/#README 第一章:let ,c ...

  9. HTML笔记<note1>

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  10. python第四课——线程、进程、协程

    面试or笔试题:简述线程.进程.协程之间的关系? 内容概要 1.进程与线程优.缺点的比较 2.适用情况 3.线程 线程的创建 setDaemon join event RLock 队列 4.进程 创建 ...