1. 程式人生 > >Opencv 例程講解 6 ---- 圖片融合 addWeighted到底有多快?

Opencv 例程講解 6 ---- 圖片融合 addWeighted到底有多快?

    這次介紹opencv中一個簡單的點運算函式,用來實現圖片合成。 對應於例程中的 (TUTORIAL) AddingImages 和 (TUTORIAL) AddingImagesTrackbar。

Opencv中提供的函式是addWeighted,用法很簡單,為了比較效能了,我們手工實現了一段相同功能的程式碼,並比較兩者的效能,到底快了多少,且看下文分解。

影象處理中一個簡單而有趣的點運算操作可以用以下的公式表示,可以實現兩張圖片的線性融合。

這裡α 表示兩種圖片的融合比例,這個g(x) 表示 融合圖片中的畫素點,f0(x) 和 f1(x) 分別表示背景和前景圖片中的畫素點。

下面為例程中的函式呼叫,

   beta = ( 1.0 - alpha );
   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個引數,輸出圖片

//! computes weighted sum of two arrays (dst = alpha*src1 + beta*src2 + gamma)
CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2,
                              double beta, double gamma, OutputArray dst, int dtype=-1);
 下面是程式執行結果

程式會提示輸入第一個圖片的融合比例,輸入0.5時,圖片的效果。

不過有時候會想,調節alpha 的值,檢視不同情況下的融合結果。這個也不難做到,在例程(TUTORIAL) AddingImagesTrackbar中已經幫我實現了

先看看執行的結果。

相比之前的結果,可以看出在影象上方多了一個滑動條,用來改變alpha的值,範圍從0~100。

相應的程式碼如下:

   /// Create Windows
   namedWindow("Linear Blend", 1);

   /// Create Trackbars
   char TrackbarName[50];
   sprintf( TrackbarName, "Alpha x %d", alpha_slider_max );
   createTrackbar( TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar );

   /// Show some stuff
   on_trackbar( alpha_slider, 0 );
利用highgui庫中的 createTrackbar 函式建立一個滑塊,函式的原型如下
CV_EXPORTS int createTrackbar(const string& trackbarname, const string& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);
可以看到,這個函式有6個引數,最後一個為使用者資料的指標,類似回撥函式中的使用者資料指標。

1、第一個引數,為滑塊的名字

2、第二個引數,為滑塊所在視窗的名字,用來獲取視窗的handle

3、第三個引數,為傳人一個數據的指標,通過滑塊來改變該資料的值,該變數需要在回撥函式TrackbarCallback 中作用域可見,因此例程中將它定義為全域性變數

4、第四個引數,為傳人資料的最大值,用來控制資料的變化範圍

5、第五個引數,回撥函式的函式指標,當滑塊變化時,便呼叫回撥函式,實現融合畫面隨著滑塊的滑動而變化

下面我們來看下回調函式 on_trackbar, 它被定義了成一個static函式,意味著函式生命週期從被呼叫開始一直存在知道程式結束,第二個引數對應createTrackbar函式中的第6個引數,使用者資料指標。

/**
 * @function on_trackbar
 * @brief Callback for trackbar
 */
static void on_trackbar( int, void* )
{
   alpha = (double) alpha_slider/alpha_slider_max ;

   beta = ( 1.0 - alpha );

   addWeighted( src1, alpha, src2, beta, 0.0, dst);

   imshow( "Linear Blend", dst );
}

為了測試addWeighted 函式效能,我們手工打造了一段相同功能的函式MyaddWeighted,程式碼如下

void MyaddWeight(InputArray _src1, double alpha, InputArray _src2, double beta, double gamma, OutputArray _dst)
{	
	CV_Assert(_src1.depth() == CV_8U && _src2.depth() == CV_8U);
	CV_Assert(_src1.channels() == _src2.channels() &&_src1.size() == _src2.size());
	_dst.create(_src1.size(),_src1.type());

	Mat src1  = _src1.getMat();
	Mat src2  = _src2.getMat();
	Mat dst   = _dst.getMat();

	int rows = src1.rows;
	int cols = src1.cols * src1.channels();

	if (src1.isContinuous() && src2.isContinuous() && dst.isContinuous())
	{
		cols *= rows;
		rows = 1;
	}

	uchar* pSrc1 = NULL;
	uchar* pSrc2 = NULL;
	uchar* pDst  = NULL;
	for (int i=0; i<rows; i++)
	{
		pSrc1 = src1.ptr<uchar>(i);
		pSrc2 = src2.ptr<uchar>(i);
		pDst  = dst.ptr<uchar>(i);
		for (int j=0; j<cols; j++)
		{
			pDst[j] = saturate_cast<uchar>(pSrc1[j]*alpha + pSrc2[j]*beta + gamma);
		}		
	}
}

在手工打造的程式中,運用C [ ]下標對影象畫素點進行遍歷,在前面的例程有過相關介紹,C[ ] 是三種遍歷方法中效能最好的。

下圖是各自方法 執行1000次的平均時間,可以看出手工打造的程式碼,雖然很好理解,但相對於opencv 的addWeightd函式效能差了很多,執行時間是6:1 。


注意到在我們程式碼中有用到 saturate_cast<uchar> 進行型別轉換以保證值的安全,那麼這種型別轉換方式對我們效能有多大的影響呢,我們通過去掉這條語句後,重新執行後的結果為

可以看出,平均執行時間從3.7毫秒縮短到了2.4毫秒,佔了執行時間的1/3。

現在換過一個方法來寫MyAddWeight 函式,還記得opencv中LUT函式中用到的NAryMatIterator,這次我們利用它來重寫MyAddWeight

void MyaddWeight2(InputArray _src1, double alpha, InputArray _src2, double beta, double gamma, OutputArray _dst)
{
	CV_Assert(_src1.depth() == CV_8U && _src2.depth() == CV_8U);
	CV_Assert(_src1.channels() == _src2.channels() &&_src1.size() == _src2.size());
	_dst.create(_src1.size(),_src1.type());

	Mat src1  = _src1.getMat();
	Mat src2  = _src2.getMat();
	Mat dst   = _dst.getMat();

	int cn = src1.channels();

	AddFun fun = AddFunTable[src1.depth()];   //  通過資料型別,從函式指標陣列中選擇相應的函式進行呼叫

	const Mat* arrays[] = {&src1, &src2, &dst,0};   // 注意需要在最後加一個0,作為指標陣列結束的標誌,以確定陣列中有效指標的個數
	uchar* ptrs[3];
	NAryMatIterator it(arrays, ptrs);
	int len = (int)it.size;
	for( size_t i = 0; i < it.nplanes; i++, ++it )
	 	fun(ptrs[0], ptrs[1], ptrs[2], len, cn, alpha, beta, gamma);
}

AddFun 是我們定義的一個函式指標型別,利用它加上一個函式指標陣列AddFunTable,我們可以很容易的擴充套件MyAddWeight的使用範圍,使它對CV_16S, CV_32F的資料型別也能適用
typedef void (*AddFun) (const uchar*, const uchar*, const uchar*, int, int, double, double, double);

static void ADD8u_(const uchar* src1, const uchar* src2, uchar* dst, int len, int cn, double aplha, double beta, double gamma)
{
	for (int i=0; i<len*cn; i++)
		dst[i] = src1[i]*aplha + src2[i]*beta + gamma;
}

這個是函式ADD8u_ 的定義,相應的,我們可以快速的寫出適合其他資料型別,如CV_8U,CV_16S,CV_32F的處理函式,
static AddFun AddFunTable[] = 
{
	(AddFun)ADD8u_, 0
};

這個是函式指標陣列,目前只實現了對資料型別CV_8U的操作,這樣就可以根據資料的型別,呼叫相應的函式,而不必針對每一種資料型別寫一個函式,大大提高了程式碼的重用率,也大大減少了以後程式碼維護的工作量,是一個很值得學習的技巧。

下面看下,這種寫法是否對效能有所提高。



可以看到,執行時間從2.48 毫秒減少到了2.29毫秒,效能提高了大約 7.6%左右,相對於程式碼重用方面,效能上面的提高不是很明顯。

相關推薦

Opencv 講解 6 ---- 圖片融合 addWeighted到底

    這次介紹opencv中一個簡單的點運算函式,用來實現圖片合成。 對應於例程中的 (TUTORIAL) AddingImages 和 (TUTORIAL) AddingImagesTrackbar。 Opencv中提供的函式是addWeighted,用法很簡單,為了比

Opencv 講解 3 ----如何高效的遍歷影象

         上次例程中簡單提到了3種遍歷影象畫素的方式,但對於他們遍歷的效能我們卻一無所知。這次將詳細介紹下opencv中遍歷影象畫素的方法,例程對應為 (TUTORIAL) how_to_scan_images,該例程將這3種方法分別用於影象的畫素量化時候,通過測量

OpenCV之基本影象讀入與顯示

OpenCV是Intel®開源計算機視覺庫。它由一系列 C 函式和少量 C++ 類構成,實現了影象處理和計算機視覺方面的很多通用演算法。 1.  OpenCV與數字影象 在使用OpenCV做視覺或數字影象預處理時,需要讀入相機採集來的影象或視訊資訊。影象資料的獲取主要有以下

學習OpenCV-實踐(持續更新)

 例4-1:用滑鼠在視窗中畫方形的程式 #include"pch.h"//看情況加 #include <cv.h> #include <highgui.h> CvRect box; //定義繪製的矩形 bool drawing_box = false; //狀

【雙目備課】OpenCV_stereo_calib.cpp解析

cpp family odi 尺寸 棋盤 耗時 char 設置 play stereo_calib是OpenCV官方代碼中提供的最正統的雙目demo,無論數據集還是代碼都有很好實現。一、代碼效果:相關的內容包括28張圖片,1個xml和stereo_calib.cpp的代碼直

Java並發編6):Runnable和Thread實現的區別(含代碼)

線程休眠 ket out dde 可能 休眠 stat for oid Java中實現多線程有兩種方法:繼承Thread類、實現Runnable接口,在程序開發中只要是多線程,肯定永遠以實現Runnable接口為主,因為實現Runnable接口相比繼承Thread類有如下優

OpenCV3:圖片融合——addWeighted(),createTrackbar()函式使用

滑動條的建立和使用: addWeighted(),createTrackbar()函式 //---------------------【滑動條的建立和使用】----------------------- #include "pch.h" //#include <

linux openCV 顯示圖片

1.編寫程式碼 opencv_test.cpp #include <stdio.h> #include <cv.h> #include <highgui.h>

OpenCV庫中watershed函式(分水嶺演算法)的詳細使用

#include <iostream> #include <opencv2\opencv.hpp> using namespace std; using namespace cv; Mat srcImage, srcImage_, maskImage; Mat maskWaterS

opencv學習haar分類器

# -*- coding: utf-8 -*-import cv2import sys img = cv2.imread(sys.argv[1]) # 載入分類器face_haar = cv2.CascadeClassifier("data/haarcascades/haar

OpenCV成長之路6:實現讀入圖片並且對圖片進行復制

複製: cvCopy(img1,img2); 儲存:cvSaveImage(filename,img); OpenCV實現對圖的儲存和複製大概就是這兩個函式把 必須要提的是:OpenCV不支援中文路

OpenCV學習筆記_圖片融合cvAddWeighted

/*使用cvAddWeighted函式將兩張圖片進行融合時,載入圖片的型別要是相同的*/ int main(void){ IplImage* src1; IplImage* src2; if

TensorFlow學習筆記6——《面向機器智慧的TensorFlow實踐》StanfordDog修改記錄

    《面向機器智慧的TensorFlow實踐》深入淺出,將tensorflow的很多概念講的很清楚,很適合tensorflow的初學者學習。該書完整的程式碼在https://github.com

opencv C++完成了一個影象壓縮的

這裡用的是openCV3.4.1和VS2017版本,引用了一個opencv的庫函式。具體看下面的程式碼把。 #include <iostream> using namespace std; #include "cv.h" #include

OpenCV 自帶總結

http://lv.xiao.yu.blog.163.com/blog/static/5419127320138191291534/ opencv 2.4.4版本共100個自帶例子。 parter 1: No1. adaptiveskindetector.cpp 利用HSV空間的色調資訊的面板檢測,背景不能有

Python卷積——和OpenCV函式對比

機子上面要先安裝好scikit-image、numpy、imutils、opencv這幾個包。 # import the necessary packages from skimage.exposure import rescale_intensity import n

OpenCV_KLT 特徵提取

KLT 特徵提取 KLT演算法的理論部分參考自: opencv中 封裝好了 KLT特徵點提取函式 //呼叫函式進行Shi-Tomasi角點檢測 goodFeaturesToTrack( s

Qt跨平臺的一個

條件 手機 net mx4 qml .com ubun 介紹 桌面 我的同事penk在近期北京的Hackathon展示了一個在多平臺的例程。非常多開發人員對這個挺感興趣的。今天我就把這個資源介紹給大家。這是同一個用Qt寫的應用。能夠同一時候在Ubuntu Destkop

第七章之main函數和啟動

gcc 清理 其它 運行 start call 返回 argv -a main函數和啟動例程 為什麽匯編程序的入口是_start,而C程序的入口是main函數呢?本節就來解釋這個問題。在講例 18.1 “最簡單的匯編程序”時,我們的匯編和鏈接步驟是: $ as hello

malloc 和free

就會 ret sca stdlib.h int 註意 申請 printf malloc #include <stdio.h>#include <stdlib.h>int main(){int a;scanf("%d",&a);int *p=(