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。
相應的程式碼如下:
利用highgui庫中的 createTrackbar 函式建立一個滑塊,函式的原型如下/// 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 );
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=(