1、编程思路:通过以下两点优化普通的程序
a、运用gpu版本的cv库函数,但有一些限制,如Image process内部的函数很多是只支持 CV_8UC1的。
b、将基本数学运算换用gpu运算,相关函数在Per-element Operations内;而且gpu是对整个矩阵处理的,类似于matlab的风格,所以尽量少用循环这些逐点操作。
2、怎么学习gpu模块的编程:资料甚少,可能实际应用用得不多;毕竟cv提供的gpu接口并不是很丰富,限制了开发者的使用;其实也不是太多内容,看着官方文档和依靠源码跟踪就可以了解怎么用了,里面很多接口是跟cv本身的很相似的,例如cv::Mat::GpuMat和cv::Mat 的使用就很相似。
GPU的官方文档
3、gpu优化过的代码在面对大量循环的时候的确非常头疼,其效率基本等同于未经gpu优化的时候,所以要尽量避免大量循环。
4、关于GpuMat的数据类型
cv::gpu::divide(sdev,kernelsize,dst,CV_32F);sdev的类型是CV_64F,像上面这种变换是没有效果的,得到的dst依然是64位;要:
cv::gpu::divide(sdev,kernelsize,dst); dst.convertTo(dst,CV_32F);才可以实现类型转换
体会:GPU模块的函数太坑爹了,说明文档的东西不能尽信!
5、矩阵尽量不要用64位数据,因为部分基本运算最高支持32位,如:
C++: void gpu::abs(const GpuMat& src, GpuMat& dst, Stream& stream=Stream::Null()) Parameters: src – Source matrix. Supports CV_16S and CV_32F depth. dst – Destination matrix with the same size and type as src . stream – Stream for the asynchronous version.
C++: void gpu::sqr(const GpuMat& src, GpuMat& dst, Stream& stream=Stream::Null()) Parameters: src – Source matrix. Supports CV_8U , CV_16U , CV_16S and CV_32F depth. dst – Destination matrix with the same size and type as src . stream – Stream for the asynchronous version.
C++: void gpu::sqrt(const GpuMat& src, GpuMat& dst, Stream& stream=Stream::Null()) Parameters: src – Source matrix. Supports CV_8U , CV_16U , CV_16S and CV_32F depth. dst – Destination matrix with the same size and type as src . stream – Stream for the asynchronous version.
C++: void gpu::exp(const GpuMat& a, GpuMat& b, Stream& stream=Stream::Null() ) Parameters: a – Source matrix. Supports CV_8U , CV_16U , CV_16S and CV_32F depth. b – Destination matrix with the same size and type as a . stream – Stream for the asynchronous version.
C++: void gpu::log(const GpuMat& a, GpuMat& b, Stream& stream=Stream::Null() ) Parameters: a – Source matrix. Supports CV_8U , CV_16U , CV_16S and CV_32F depth. b – Destination matrix with the same size and type as a . stream – Stream for the asynchronous version.
C++: void gpu::pow(const GpuMat& src, double power, GpuMat& dst, Stream& stream=Stream::Null()) Parameters: src – Source matrix. Supports all type, except CV_64F depth. power – Exponent of power. dst – Destination matrix with the same size and type as src . stream – Stream for the asynchronous version.
6、使用滤波引擎
class gpu::FilterEngine_GPU
官方文档上提到了这点,详见:http://docs.opencv.org/modules/gpu/doc/image_filtering.html
By using FilterEngine_GPU instead of functions you can avoid unnecessary memory allocation for intermediate buffers and get better performance
经过实验,发现的确能提高代码效率,而且,直接调用滤波函数,如gpu::GaussianBlur,其效率比cv::GaussianBlur还低。
注:time0是cv::GaussianBlur;time1为cv::gpu::GaussianBlur;time2为FilterEngine_GPU;实验条件为输入CV_8UC1的数据,对其进行高斯模糊。
使用方法:
cv::Ptr<gpu::FilterEngine_GPU> filter = gpu::createGaussianFilter_GPU(CV_32FC1, Size(5, 5), 1.0, 1.0, BORDER_REFLECT,BORDER_REFLECT); //初始化滤波器 filter->apply(Gimg, Gimg2, cv::Rect(0, 0, Gimg.cols, Gimg.rows)); //滤波,需要制定ROI其中,GpuMat Gimg为输入,GpuMat Gimg1为输出
7、error:insufficient memory
显存不足导致
通过gpu::DeviceInfo::totalMemory可以查看当前显存,但不是opencv的可用显存(因为显寸还要用在其它地方)
8、更换显卡后,要重新编译源码,否则原来可以运行的代码会报错!这点非常不方便!所以,要在目标机器上开发,不要随便选择一台机器。说是N卡可以用CUDA,但还要看是什么型号的显卡,gpu模块的这一点真心不好
9、使用引用形参提高代码效率
这一点不仅是编写gpu代码,平时的C++代码也是要这样做的
使用引用形参的优点:在向函数传递大对象时,需要使用引用形参。虽然赋值实参对于内置数据类型的对象或者规模较小的类型来说没有什么问题,但是对于大部分的类型或者大型数组,它的效率就比较低了。另外,某些类型是无法复制的。使用引用形参,函数可以直接访问实参对象,而无须复制它。
如果不想修改实参的值,可以使用const
10、使用矩阵前先初始化,提前分配内存,可以大大提高代码效率!
例如:
//方法2,低层GPU高斯模糊 cv::Ptr<gpu::FilterEngine_GPU> filter = gpu::createGaussianFilter_GPU(CV_32FC1, Size(5, 5), 1.0, 1.0, BORDER_REFLECT,BORDER_REFLECT); //初始化滤波器 filter->apply(Gimg, Gimg2, cv::Rect(0, 0, Gimg.cols, Gimg.rows)); //滤波,需要制定ROI这是opencv里面效率最高的高斯模糊实现方式,但如果没有为Gimg2预分配内存,即仅仅定义了一个GpuMat Gimg2,那运行速度将会比cv::GaussianBlur还慢!这是因为gpu函数为变量分配显存是很费时的。
同样的道理,尽管不是gpu下的代码,也是需要养成先分配内存,后使用的习惯。
11、推荐CUDA程序优化的15个策略
http://tech.it168.com/a2011/0706/1213/000001213855.shtml
(TO BE CONTINUED)