cuda与openCV结合编程(一)
来源:互联网 发布:数据对接子系统 编辑:程序博客网 时间:2024/05/17 05:16
学习计算机图像处理算法的童鞋,就不得不学习cuda,为啥呢?因为图像处理一般都是矩阵运算,动不动就是百万的计算量这个时候优化计算时间是必不可少的。openCV本身提供了很多cuda函数,能够满足大多数用户的需求。但是也不绝对,有时候我们需要自己定义一个内核函数进行优化,当然你也可以用openGL或者多线程,openCV也提供较好的支持,掌握一种或多种加速算法,对程序员特别是算法工程师来讲很重要。
闲话不多说,再学习了cuda的基础以后【cuda并行编程基础(一)、cuda并行编程基础(二)】,我们其实就具备与opencv联合编程的能力,虽然不是最佳优化,但已经可以满足大多数需求了。
一、cuda与openCV结合方法
(下面仅涉及windows环境)
1.我们知道,cuda代码一般以.cu结尾(windows,其他系统除外,下同),它的编译器是nvcc,编译时它会将CPU代码和GPU代码分开,CPU部分其实与gcc编译差不多,GPU部分就按照nvcc的规则编译,这玩意其实并不复杂;
2.openCV的代码一般都是以.cpp结尾,它的编译器一般是gcc、g++(或者其他相似编译器),那么可不可以将openCV代码用nvcc编译呢?答案是肯定的,但在windows系统,你得把它改为.cu结尾.
3.所以,在windows系统里面,你有两种办法让openCV结合cuda编程:
a.openCV正常编译,cuda代码编译好后,作为静态库引入openCV调用;
b.openCV与cuda代码混在一起,统一用nvcc编译。
二、如何编写代码
(下面openCV基于3.2.0版本)
openCV是一个非常强大的视觉算法库,当然也支持cuda咯。
cv::cuda是一个专门处理cuda的命名空间,你在这个命名空间里面可以看到很多已经集成好的函数。
如:cuda::remap()、cuda::add()等
我们要用到的是cuda::PtrStepSz<T>的模板,以及cuda::GpuMat
比如:如果我们有一个cuda::GpuMat类型的img,我们怎么传入cuda里面呢?答案就是,直接将img传到cuda::PtrStepSz里面,他们是不是等同,但是可以互传数据,具体见样例。至于传到cuda::PtrStepSz里面如何操作,那就跟cuda差不多了。
除了cuda::PtrStepSz,openCV还有其他接口可以提供互传,自己去摸索啦,这里就不啰嗦了。
闲话不多说,再学习了cuda的基础以后【cuda并行编程基础(一)、cuda并行编程基础(二)】,我们其实就具备与opencv联合编程的能力,虽然不是最佳优化,但已经可以满足大多数需求了。
一、cuda与openCV结合方法
(下面仅涉及windows环境)
1.我们知道,cuda代码一般以.cu结尾(windows,其他系统除外,下同),它的编译器是nvcc,编译时它会将CPU代码和GPU代码分开,CPU部分其实与gcc编译差不多,GPU部分就按照nvcc的规则编译,这玩意其实并不复杂;
2.openCV的代码一般都是以.cpp结尾,它的编译器一般是gcc、g++(或者其他相似编译器),那么可不可以将openCV代码用nvcc编译呢?答案是肯定的,但在windows系统,你得把它改为.cu结尾.
3.所以,在windows系统里面,你有两种办法让openCV结合cuda编程:
a.openCV正常编译,cuda代码编译好后,作为静态库引入openCV调用;
b.openCV与cuda代码混在一起,统一用nvcc编译。
二、如何编写代码
(下面openCV基于3.2.0版本)
openCV是一个非常强大的视觉算法库,当然也支持cuda咯。
cv::cuda是一个专门处理cuda的命名空间,你在这个命名空间里面可以看到很多已经集成好的函数。
如:cuda::remap()、cuda::add()等
我们要用到的是cuda::PtrStepSz<T>的模板,以及cuda::GpuMat
比如:如果我们有一个cuda::GpuMat类型的img,我们怎么传入cuda里面呢?答案就是,直接将img传到cuda::PtrStepSz里面,他们是不是等同,但是可以互传数据,具体见样例。至于传到cuda::PtrStepSz里面如何操作,那就跟cuda差不多了。
除了cuda::PtrStepSz,openCV还有其他接口可以提供互传,自己去摸索啦,这里就不啰嗦了。
至于cuda与openCV的结合编程效率问题?哈,谁用谁知道,你不用也无需知道,有兴趣自己去测一下咯,反正笔者是墙裂推荐的,后面有空再讲效率问题。
三、常见错误
1.cudaErrorMemoryAllocation,主要是申请空间太大,超出了GPU限制;
2.cudaErrorLaunchFailure,访问了非法地址,比如index超过了数组大小;
3.cuda与vs2015结合编程,偶尔会出现抽筋的问题,比如你这次编译出错,改正了以后再编译还出错,建议要重新编译时,把以前的编译生成的东西全删掉,这样就保险多了,笔者遇见多次这种情况;
4.<<<>>>内核符号报错,要确定它出现在cu文件里而不是cpp文件里,cu文件会显示红色,不用管它;
5.静态库的编写规范,额,自己上网研究吧,其实我写得也不太规范,吐槽一下,网上好多技术文章抄来抄去很没意思,很多大牛又写得太过高深,研究不出个所以然来,也希望能够在各个层次都有合适的文章介绍吧,这样入门和进阶也不会太困难。
三、常见错误
1.cudaErrorMemoryAllocation,主要是申请空间太大,超出了GPU限制;
2.cudaErrorLaunchFailure,访问了非法地址,比如index超过了数组大小;
3.cuda与vs2015结合编程,偶尔会出现抽筋的问题,比如你这次编译出错,改正了以后再编译还出错,建议要重新编译时,把以前的编译生成的东西全删掉,这样就保险多了,笔者遇见多次这种情况;
4.<<<>>>内核符号报错,要确定它出现在cu文件里而不是cpp文件里,cu文件会显示红色,不用管它;
5.静态库的编写规范,额,自己上网研究吧,其实我写得也不太规范,吐槽一下,网上好多技术文章抄来抄去很没意思,很多大牛又写得太过高深,研究不出个所以然来,也希望能够在各个层次都有合适的文章介绍吧,这样入门和进阶也不会太困难。
第一个程序,直接在cu文件实现cuda与opencv结合编程,非常重要哦
一般我们不这么使用,因为cuda作为独立的编程方式,放在一起容易混乱,而且为支持高速运算,一般都使用c运算,而不是c++
一般我们不这么使用,因为cuda作为独立的编程方式,放在一起容易混乱,而且为支持高速运算,一般都使用c运算,而不是c++
//opencv_cuda.cu:使用自定义函数,实现cuda版本图片翻转//authored by alpc40//version:visual studio 2015\cuda toolkit 8.0\openCV 3.2.0#include "cuda_runtime.h"#include "device_launch_parameters.h"#include <stdio.h>#include<opencv2/opencv.hpp>#include<iostream>using namespace std;using namespace cv;#ifdef _DEBUG#pragma comment ( lib,"opencv_core320d.lib")#pragma comment ( lib,"opencv_highgui320d.lib")#pragma comment ( lib,"opencv_calib3d320d.lib")#pragma comment ( lib,"opencv_imgcodecs320d.lib")#pragma comment ( lib,"opencv_imgproc320d.lib")#pragma comment ( lib,"opencv_cudaimgproc320d.lib")#pragma comment ( lib,"opencv_cudaarithm320d.lib")#pragma comment ( lib,"cudart.lib")#else#pragma comment ( lib,"opencv_core320.lib")#pragma comment ( lib,"opencv_highgui320.lib")#pragma comment ( lib,"opencv_calib3d320.lib")#pragma comment ( lib,"opencv_imgcodecs320.lib")#pragma comment ( lib,"opencv_imgproc320.lib")#pragma comment ( lib,"opencv_cudaimgproc320.lib")#pragma comment ( lib,"opencv_cudaarithm320.lib")#pragma comment ( lib,"cudart.lib")#endif//出错处理函数#define CHECK_ERROR(call){\ const cudaError_t err = call;\ if (err != cudaSuccess)\ {\ printf("Error:%s,%d,",__FILE__,__LINE__);\ printf("code:%d,reason:%s\n",err,cudaGetErrorString(err));\ exit(1);\ }\}//内核函数:实现上下翻转__global__ void swap_image_kernel(cuda::PtrStepSz<uchar3> cu_src, cuda::PtrStepSz<uchar3> cu_dst, int h, int w){ //计算的方法:参看前面两文 unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; //为啥要这样限制:参看前面两文 if (x < cu_src.cols && y < cu_src.rows) { //为何不是h-y-1,而不是h-y,自己思考哦 cu_dst(y, x) = cu_src(h - y - 1, x); }}//调用函数,主要处理block和grid的关系void swap_image(cuda::GpuMat src,cuda::GpuMat dst,int h, int w){ assert(src.cols == w && src.rows ==h); int uint = 32; //参考前面两文的block和grid的计算方法,注意不要超过GPU限制 dim3 block(uint, uint); dim3 grid((w + block.x - 1) / block.x, (h + block.y - 1) / block.y); printf("grid = %4d %4d %4d\n",grid.x,grid.y,grid.z); printf("block= %4d %4d %4d\n",block.x,block.y,block.z); swap_image_kernel << <grid, block >> > (src,dst,h,w); //同步一下,因为计算量可能很大 CHECK_ERROR(cudaDeviceSynchronize());}int main(int argc,char **argv){ Mat src, dst; cuda::GpuMat cu_src, cu_dst; int h, w; //根据argv[1]读入图片数据,BGR格式读进来 src = imread(argv[1]); //检测是否正确读入 if (src.data == NULL) { cout << "Read image error" << endl; return -1; } h = src.rows; w = src.cols; cout <<"图片高:" << h << ",图片宽:" << w << endl; //上传CPU图像数据到GPU,跟cudaMalloc和cudaMemcpy很像哦,其实upload里面就是这么写的 cu_src.upload(src); //申请GPU空间,也可以到函数里申请,不管怎样总要申请,要不然内核函数会爆掉哦 cu_dst = cuda::GpuMat(h, w, CV_8UC3, Scalar(0, 0, 0)); //申请CPU空间 dst = Mat(h, w, CV_8UC3, Scalar(0, 0, 0)); //调用函数swap_image,由该函数调用内核函数,这样层次分明,不容易出错 //当然你也可以直接在这里调用内核函数,东西太多代码容易乱 swap_image(cu_src,cu_dst,h, w); //下载GPU数据到CPU,与upload()对应 cu_dst.download(dst); //显示cpu图像,如果安装了openCV集成了openGL,那可以直接显示GpuMat imshow("dst",dst); //等待按键 waitKey(); //写图片到文件 if(argc==3) imwrite(argv[2],dst); return 0;}
第二个程序,使用静态库的方式实现cuda与openCV的结合,非常重要哦
这种方式两相分离,更好实现了这种功能
这种方式两相分离,更好实现了这种功能
//swap_image.cu:生成swap_image.lib,供主函数调用
//authored by alpc40//version:visual studio 2015\cuda toolkit 8.0\openCV 3.2.0#include "cuda_runtime.h"#include "device_launch_parameters.h"#include <stdio.h>#include<opencv2/opencv.hpp>using namespace cv;//出错处理函数#define CHECK_ERROR(call){\ const cudaError_t err = call;\ if (err != cudaSuccess)\ {\ printf("Error:%s,%d,",__FILE__,__LINE__);\ printf("code:%d,reason:%s\n",err,cudaGetErrorString(err));\ exit(1);\ }\}//内核函数:实现上下翻转__global__ void swap_image_kernel(cuda::PtrStepSz<uchar3> cu_src, cuda::PtrStepSz<uchar3> cu_dst, int h, int w){ //计算的方法:参看前面两文 unsigned int x = blockDim.x * blockIdx.x + threadIdx.x; unsigned int y = blockDim.y * blockIdx.y + threadIdx.y; //为啥要这样限制:参看前面两文 if (x < cu_src.cols && y < cu_src.rows) { //为何不是h-y-1,而不是h-y,自己思考哦 cu_dst(y, x) = cu_src(h - y - 1, x); }}//调用函数,主要处理block和grid的关系,注意extern哦,它是库文件编写规范extern "C" void swap_image(cuda::GpuMat src, cuda::GpuMat dst, int h, int w){ assert(src.cols == w && src.rows == h); int uint = 32; //参考前面两文的block和grid的计算方法,注意不要超过GPU限制 dim3 block(uint, uint); dim3 grid((w + block.x - 1) / block.x, (h + block.y - 1) / block.y); printf("grid = %4d %4d %4d\n", grid.x, grid.y, grid.z); printf("block= %4d %4d %4d\n", block.x, block.y, block.z); swap_image_kernel << <grid, block >> > (src, dst, h, w); //同步一下,因为计算量可能很大 CHECK_ERROR(cudaDeviceSynchronize());}
//opencv_cuda.cpp:第二个程序主函数,使用自编静态库,实现cuda版本图片翻转//authored by alpc40//version:visual studio 2015\cuda toolkit 8.0\openCV 3.2.0#include <stdio.h>#include<opencv2/opencv.hpp>#include<iostream>using namespace std;using namespace cv;#ifdef _DEBUG#pragma comment ( lib,"opencv_core320d.lib")#pragma comment ( lib,"opencv_highgui320d.lib")#pragma comment ( lib,"opencv_calib3d320d.lib")#pragma comment ( lib,"opencv_imgcodecs320d.lib")#pragma comment ( lib,"opencv_imgproc320d.lib")#pragma comment ( lib,"opencv_cudaimgproc320d.lib")#pragma comment ( lib,"opencv_cudaarithm320d.lib")#pragma comment ( lib,"cudart.lib")#pragma comment ( lib,"swap_image.lib")//别忘了加库#else#pragma comment ( lib,"opencv_core320.lib")#pragma comment ( lib,"opencv_highgui320.lib")#pragma comment ( lib,"opencv_calib3d320.lib")#pragma comment ( lib,"opencv_imgcodecs320.lib")#pragma comment ( lib,"opencv_imgproc320.lib")#pragma comment ( lib,"opencv_cudaimgproc320.lib")#pragma comment ( lib,"opencv_cudaarithm320.lib")#pragma comment ( lib,"cudart.lib")#pragma comment ( lib,"swap_image.lib")//别忘了加库#endif//这个声明很重要,调用静态库extern "C" void swap_image(cuda::GpuMat src,cuda::GpuMat dst,int w,int h);int main(int argc, char **argv){ Mat src, dst; cuda::GpuMat cu_src, cu_dst; int h, w; //根据argv[1]读入图片数据,BGR格式读进来 src = imread(argv[1]); //检测是否正确读入 if (src.data == NULL) { cout << "Read image error" << endl; return -1; } h = src.rows; w = src.cols; cout << "图片高:" << h << ",图片宽:" << w << endl; //上传CPU图像数据到GPU,跟cudaMalloc和cudaMemcpy很像哦,其实upload里面就是这么写的 cu_src.upload(src); //申请GPU空间,也可以到函数里申请,不管怎样总要申请,要不然内核函数会爆掉哦 cu_dst = cuda::GpuMat(h, w, CV_8UC3, Scalar(0, 0, 0)); //申请CPU空间 dst = Mat(h, w, CV_8UC3, Scalar(0, 0, 0)); //调用函数swap_image,由该函数调用内核函数,这样层次分明,不容易出错 //当然你也可以直接在这里调用内核函数,东西太多代码容易乱 swap_image(cu_src, cu_dst, h, w); //下载GPU数据到CPU,与upload()对应 cu_dst.download(dst); //显示cpu图像,如果安装了openCV集成了openGL,那可以直接显示GpuMat imshow("dst", dst); //等待按键 waitKey(); //写图片到文件 if (argc == 3) imwrite(argv[2], dst); return 0;}
PS:以下为图片效果,顺便为我家赣南脐橙打个广告
阅读全文
0 0
- cuda与openCV结合编程(一)
- 【OpenCV & CUDA】OpenCV和Cuda结合编程
- OpenCV和Cuda结合编程
- OpenCV和Cuda结合编程
- opencv与cuda的结合使用
- OpenCV 2.2与CUDA 4.0的硬性结合
- CUDA编程->CUDA入门了解(一)
- Opencv与QT结合编程显示图像
- cuda矩阵编程(一)
- CUDA编程实践(一)
- CUDA编程(一):背景与安装
- Opencv与CUDA
- CUDA学习笔记(一) CUDA编程模型1
- (CUDA 编程3).CUDA硬件实现分析(一)------安营扎寨
- CUDA C 编程指导(一):CUDA介绍
- CUDA编程(一)第一个CUDA程序
- CUDA编程(一)第一个CUDA程序
- cuda入门——结合opncv和cuda编程(2)
- C++内存错误检测工具
- 椭圆检测及提取
- openvc之亮度与对比度处理
- 分治排序算法
- linux 常用命令大全
- cuda与openCV结合编程(一)
- 配置Zabbix所需环境要求
- 正则表达式
- windows下安装MongoDB进度条不动
- 女程序媛与男程序猿的一天
- 为什么程序员发现不了自己的BUG?
- test
- 开始旅程,数组的输入环境搭建
- 拥塞控制与多径路由