Oct 14, 2018 原创文章
OpenCV GPU 模块学习 (2)GPU的流操作
软件信息:
OpenCV Version : 2.4.13.6
CUDA Version : 8.0
OpenCV gpu::Stream 手册
https://docs.opencv.org/2.4.13.6/modules/gpu/doc/data_structures.html#gpu::Stream
OpenCV 通过 gpu::Stream 类封装了一个异步调用。
在OpenCV的gpu模块中提供的一些函数具有附加 gpu::Stream 参数的重载,这些函数可以进行初始化工作,启动GPU核函数,并且可以在结果计算完成之前返回。
编程思想:
新建一个Stream队列,使用 gpu::Strean::enqueueUpload() 将需要操作的数据放入队列中,然后在队列中进行所需要的操作,可以通过gpu::Stream::queryIfComplete() 和 gpu::Stream::waitForCompletion() 判断和检测操作是否完成。等待队列中的操作完成,使用 gpu::strean::enqueueDownload() 将数据返回。相较于默认的数据传输方法,使用Stream队列实现了数据的异步调用,不需要等待数据全部上传完成后再对数据进行操作,只需要在确定所需的数据,即可在上传数据的同时对数据进行操作,达到GPU计算的同时进行GPU与CPU之间的数据交换,从而达到提升效率的目。
关于CPU和GPU异步并行的概念和意义详见:《CUDA并行编程学习(5)– 异步并行》
类的内容:
class CV_EXPORTS Stream
{
public:
Stream();
~Stream();
Stream(const Stream&);
Stream& operator=(const Stream&);
bool queryIfComplete();
void waitForCompletion();
void enqueueDownload(const GpuMat& src, CudaMem& dst);
void enqueueDownload(const GpuMat& src, Mat& dst);
void enqueueUpload(const CudaMem& src, GpuMat& dst);
void enqueueUpload(const Mat& src, GpuMat& dst);
void enqueueCopy(const GpuMat& src, GpuMat& dst);
void enqueueMemSet(const GpuMat& src, Scalar val);
void enqueueMemSet(const GpuMat& src, Scalar val, const GpuMat& mask);
void enqueueConvert(const GpuMat& src, GpuMat& dst, int type,
double a = 1, double b = 0);
typedef void (*StreamCallback)(Stream& stream, int status, void* userData);
void enqueueHostCallback(StreamCallback callback, void* userData);
};
例程:
通过该例程,简单展示了opencv gpu::Stream 类及其方法的使用。
#include "iostream"
#include "opencv2/opencv.hpp"
#include "opencv2/gpu/gpu.hpp"
int main(int argc, char *argv[])
{
// 读取图片为 8bit单通道 Mat
cv::Mat rawImage = cv::imread("lena.jpg", CV_8UC1);
cv::imshow("rawResult", rawImage);
// 申请Page-locked内存 allocate page-locked memory
cv::gpu::CudaMem host_src_pl(rawImage.cols, rawImage.rows, CV_8UC1, cv::gpu::CudaMem::ALLOC_PAGE_LOCKED);
cv::gpu::CudaMem host_dst_pl;
// 填满申请到的PL内存 Do something to fill host_src_pl
memcpy(host_src_pl.datastart, rawImage.datastart, host_src_pl.cols * host_src_pl.rows);
// 创建GpuMat
cv::gpu::GpuMat gpu_src,gpu_dst;
// 创建Stream
cv::gpu::Stream stream;
// 从主机(CPU)向设备(GPU)复制数据
stream.enqueueUpload(host_src_pl, gpu_src);
// 在设备中对数据进行处理
cv::gpu::threshold(gpu_src, gpu_dst, 128.0, 255.0, CV_THRESH_BINARY, stream);
// 从设备向主机复制数据
stream.enqueueDownload(gpu_dst, host_dst_pl);
// 在主机中与设备并行的进行其他的处理
cv::Mat binImage;
cv::threshold(rawImage, binImage, 128.0, 255.0, CV_THRESH_BINARY);
// 等待设备完成计算任务
stream.waitForCompletion();
// 现在可以使用设备中计算得到的数据
cv::Mat host_dst = host_dst_pl;
cv::imshow("gpuResult", host_dst);
cv::imshow("cpuResult", binImage);
cv::waitKey();
return 0;
}
参考资料:
1、opencv中GPU的流操作 https://blog.csdn.net/eyes366/article/details/48438397
2、how to use gpu::Stream in OpenCV? https://stackoverflow.com/questions/17842827/how-to-use-gpustream-in-opencv