Opencv 图片融合 addWeighted性能测试

原文:http://blog.csdn.net/u011503970/article/details/18615537

    这次介绍OpenCV中一个简单的点运算函数,用来实现图片合成。 对应于例程中的 (TUTORIAL) AddingImages 和 (TUTORIAL) AddingImagesTrackbar。

Opencv中提供的函数是addWeighted,用法很简单,为了比较性能了,我们手工实现了一段相同功能的代码,并比较两者的性能,到底快了多少,且看下文分解。

图像处理中一个简单而有趣的点运算操作可以用以下的公式表示,可以实现两张图片的线性融合。

这里α 表示两种图片的融合比例,这个g(x) 表示 融合图片中的像素点,f0(x) 和 f1(x) 分别表示背景和前景图片中的像素点。

下面为例程中的函数调用,

[cpp] view plain copy
  1. beta = ( 1.0 - alpha );  
  2. addWeighted( src1, alpha, src2, beta, 0.0, dst);  
opencv 通过 addWeighted 函数实现图片的线性融合,这个函数在之前的例程中也有提到过。

这个函数的原型如下所示,可以看出这个函数最小需要6个参数。

1、 第1个参数,输入图片1,

2、第2个参数,图片1的融合比例

3、第3个参数,输入图片2

4、第4个参数,图片2的融合比例

5、第5个参数,偏差

6、第6个参数,输出图片

[cpp] view plain copy
  1. //! computes weighted sum of two arrays (dst = alpha*src1 + beta*src2 + gamma)  
  2. CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2,  
  3.                               double beta, double gamma, OutputArray dst, int dtype=-1);  
 下面是程序运行结果

Opencv 图片融合 addWeighted性能测试_第1张图片

程序会提示输入第一个图片的融合比例,输入0.5时,图片的效果。

不过有时候会想,调节alpha 的值,查看不同情况下的融合结果。这个也不难做到,在例程(TUTORIAL) AddingImagesTrackbar中已经帮我实现了

先看看运行的结果。

Opencv 图片融合 addWeighted性能测试_第2张图片

相比之前的结果,可以看出在图像上方多了一个滑动条,用来改变alpha的值,范围从0~100。

相应的代码如下:

[cpp] view plain copy
  1. /// Create Windows  
  2. namedWindow("Linear Blend", 1);  
  3.   
  4. /// Create Trackbars  
  5. char TrackbarName[50];  
  6. sprintf( TrackbarName, "Alpha x %d", alpha_slider_max );  
  7. createTrackbar( TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar );  
  8.   
  9. /// Show some stuff  
  10. on_trackbar( alpha_slider, 0 );  
利用highgui库中的 createTrackbar 函数创建一个滑块,函数的原型如下
[cpp] view plain copy
  1. CV_EXPORTS int createTrackbar(const string& trackbarname, const string& winname,  
  2.                               int* value, int count,  
  3.                               TrackbarCallback onChange = 0,  
  4.                               void* userdata = 0);  
可以看到,这个函数有6个参数,最后一个为用户数据的指针,类似回调函数中的用户数据指针。

1、第一个参数,为滑块的名字

2、第二个参数,为滑块所在窗口的名字,用来获取窗口的handle

3、第三个参数,为传人一个数据的指针,通过滑块来改变该数据的值,该变量需要在回调函数TrackbarCallback 中作用域可见,因此例程中将它定义为全局变量

4、第四个参数,为传人数据的最大值,用来控制数据的变化范围

5、第五个参数,回调函数的函数指针,当滑块变化时,便调用回调函数,实现融合画面随着滑块的滑动而变化

下面我们来看下回调函数 on_trackbar, 它被定义了成一个static函数,意味着函数生命周期从被调用开始一直存在知道程序结束,第二个参数对应createTrackbar函数中的第6个参数,用户数据指针。

[cpp] view plain copy
  1. /** 
  2.  * @function on_trackbar 
  3.  * @brief Callback for trackbar 
  4.  */  
  5. static void on_trackbar( intvoid* )  
  6. {  
  7.    alpha = (double) alpha_slider/alpha_slider_max ;  
  8.   
  9.    beta = ( 1.0 - alpha );  
  10.   
  11.    addWeighted( src1, alpha, src2, beta, 0.0, dst);  
  12.   
  13.    imshow( "Linear Blend", dst );  
  14. }  


为了测试addWeighted 函数性能,我们手工打造了一段相同功能的函数MyaddWeighted,代码如下

[cpp] view plain copy
  1. void MyaddWeight(InputArray _src1, double alpha, InputArray _src2, double beta, double gamma, OutputArray _dst)  
  2. {     
  3.     CV_Assert(_src1.depth() == CV_8U && _src2.depth() == CV_8U);  
  4.     CV_Assert(_src1.channels() == _src2.channels() &&_src1.size() == _src2.size());  
  5.     _dst.create(_src1.size(),_src1.type());  
  6.   
  7.     Mat src1  = _src1.getMat();  
  8.     Mat src2  = _src2.getMat();  
  9.     Mat dst   = _dst.getMat();  
  10.   
  11.     int rows = src1.rows;  
  12.     int cols = src1.cols * src1.channels();  
  13.   
  14.     if (src1.isContinuous() && src2.isContinuous() && dst.isContinuous())  
  15.     {  
  16.         cols *= rows;  
  17.         rows = 1;  
  18.     }  
  19.   
  20.     uchar* pSrc1 = NULL;  
  21.     uchar* pSrc2 = NULL;  
  22.     uchar* pDst  = NULL;  
  23.     for (int i=0; i
  24.     {  
  25.         pSrc1 = src1.ptr(i);  
  26.         pSrc2 = src2.ptr(i);  
  27.         pDst  = dst.ptr(i);  
  28.         for (int j=0; j
  29.         {  
  30.             pDst[j] = saturate_cast(pSrc1[j]*alpha + pSrc2[j]*beta + gamma);  
  31.         }         
  32.     }  
  33. }  

在手工打造的程序中,运用C [ ]下标对图像像素点进行遍历,在前面的例程有过相关介绍,C[ ] 是三种遍历方法中性能最好的。

下图是各自方法 运行1000次的平均时间,可以看出手工打造的代码,虽然很好理解,但相对于opencv 的addWeightd函数性能差了很多,运行时间是6:1 。

Opencv 图片融合 addWeighted性能测试_第3张图片

注意到在我们代码中有用到 saturate_cast 进行类型转换以保证值的安全,那么这种类型转换方式对我们性能有多大的影响呢,我们通过去掉这条语句后,重新运行后的结果为

可以看出,平均运行时间从3.7毫秒缩短到了2.4毫秒,占了运行时间的1/3。

现在换过一个方法来写MyAddWeight 函数,还记得opencv中LUT函数中用到的NAryMatIterator,这次我们利用它来重写MyAddWeight

[cpp] view plain copy
  1. void MyaddWeight2(InputArray _src1, double alpha, InputArray _src2, double beta, double gamma, OutputArray _dst)  
  2. {  
  3.     CV_Assert(_src1.depth() == CV_8U && _src2.depth() == CV_8U);  
  4.     CV_Assert(_src1.channels() == _src2.channels() &&_src1.size() == _src2.size());  
  5.     _dst.create(_src1.size(),_src1.type());  
  6.   
  7.     Mat src1  = _src1.getMat();  
  8.     Mat src2  = _src2.getMat();  
  9.     Mat dst   = _dst.getMat();  
  10.   
  11.     int cn = src1.channels();  
  12.   
  13.     AddFun fun = AddFunTable[src1.depth()];   //  通过数据类型,从函数指针数组中选择相应的函数进行调用  
  14.   
  15.     const Mat* arrays[] = {&src1, &src2, &dst,0};   // 注意需要在最后加一个0,作为指针数组结束的标志,以确定数组中有效指针的个数  
  16.     uchar* ptrs[3];  
  17.     NAryMatIterator it(arrays, ptrs);  
  18.     int len = (int)it.size;  
  19.     forsize_t i = 0; i < it.nplanes; i++, ++it )  
  20.         fun(ptrs[0], ptrs[1], ptrs[2], len, cn, alpha, beta, gamma);  
  21. }  

AddFun 是我们定义的一个函数指针类型,利用它加上一个函数指针数组AddFunTable,我们可以很容易的扩展MyAddWeight的使用范围,使它对CV_16S, CV_32F的数据类型也能适用
[cpp] view plain copy
  1. typedef void (*AddFun) (const uchar*, const uchar*, const uchar*, intintdoubledoubledouble);  
  2.   
  3. static void ADD8u_(const uchar* src1, const uchar* src2, uchar* dst, int len, int cn, double aplha, double beta, double gamma)  
  4. {  
  5.     for (int i=0; i
  6.         dst[i] = src1[i]*aplha + src2[i]*beta + gamma;  
  7. }  

这个是函数ADD8u_ 的定义,相应的,我们可以快速的写出适合其他数据类型,如CV_8U,CV_16S,CV_32F的处理函数,
[cpp] view plain copy
  1. static AddFun AddFunTable[] =   
  2. {  
  3.     (AddFun)ADD8u_, 0  
  4. };  

这个是函数指针数组,目前只实现了对数据类型CV_8U的操作,这样就可以根据数据的类型,调用相应的函数,而不必针对每一种数据类型写一个函数,大大提高了代码的重用率,也大大减少了以后代码维护的工作量,是一个很值得学习的技巧。

下面看下,这种写法是否对性能有所提高。



可以看到,运行时间从2.48 毫秒减少到了2.29毫秒,性能提高了大约 7.6%左右,相对于代码重用方面,性能上面的提高不是很明显。

你可能感兴趣的:(opencv基础)