【OpenCV入門教程之四】 ROI區域圖像疊加&初級圖像混合 全剖析 淺墨_毛星云
2014-03-10 12:48:05 157425 收藏 19 最后發布:2014-03-10 12:48:05首發:2014-03-10 12:48:05
分類專欄: 【OpenCV】 【OpenCV】入門教程 版權聲明:本文為博主原創文章,遵循CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。 本文鏈接:https://blog.csdn.net/zhmxy555/article/details/20911629 展開
?
本系列文章由@淺墨_毛星云 出品,轉載請注明出處。??
文章鏈接:?http://blog.csdn.net/poem_qianmo/article/details/20911629
作者:毛星云(淺墨)????郵箱:?happylifemxy@163.com?
寫作當前博文時配套使用的OpenCV版本: 2.4.8
?
在這篇文章里,我們一起學習了在OpenCV中如何定義感興趣區域ROI,如何使用addWeighted函數進行圖像混合操作,以及將ROI和addWeighted函數結合起來使用,對指定區域進行圖像混合操作。
PS:文章末尾提供了博文配套程序源代碼的下載。
文章開頭,依舊是先放一張截圖:
?
?
一、設定感興趣區域——ROI(region of interest)
在圖像處理領域,我們常常需要設置感興趣區域(ROI,region of interest),來專注或者簡化我們的工作過程 。也就是從圖像中選擇的一個圖像區域,這個區域是我們圖像分析所關注的重點。我們圈定這個區域,以便進行進一步處理。而且,使用ROI指定我們想讀入的目標,可以減少處理時間,增加精度,給圖像處理來帶不小的便利。
?
?
ROI區域定義的兩種方法 ?
定義ROI區域有兩種方法,第一種是使用cv:Rect.顧名思義,cv::Rect表示一個矩形區域。指定矩形的左上角坐標(構造函數的前兩個參數)和矩形的長寬(構造函數的后兩個參數)就可以定義一個矩形區域。
?
Mat imageROI; imageROI=image(Rect(500 ,250 ,logo.cols,logo.rows));
另一種定義ROI的方式是指定感興趣行或列的范圍(Range)。Range是指從起始索引到終止索引(不包括終止索引)的一連段連續序列。cv::Range可以用來定義Range。如果使用cv::Range來定義ROI,那么前例中定義ROI的代碼可以重寫為:
imageROI=srcImage3(Range(250 ,250 +logoImage.rows),Range(200 ,200 +logoImage.cols));
好了,下面我們來看一個實例,顯示如何利用ROI將一幅圖加到另一幅圖的指定位置。大家如果需要拷貝如下的函數中的代碼直接運行的話,自己建一個基于console的程序,然后把函數體中的內容拷貝到main函數中,然后找兩幅大小合適的圖片,加入到工程目錄下,并和代碼中讀取的文件名一致即可。
在下面的代碼中,我們通過一個圖像掩膜(mask),直接將插入處的像素設置為logo圖像的像素值,這樣效果會很贊很逼真:
?
bool ROI_AddImage () { Mat srcImage1= imread("dota_pa.jpg" ); Mat logoImage= imread("dota_logo.jpg" ); if (!srcImage1.data ) { printf ("你妹,讀取srcImage1錯誤~! \n" ); return false ; } if (!logoImage.data ) { printf ("你妹,讀取logoImage錯誤~! \n" ); return false ; } Mat imageROI= srcImage1(Rect(200 ,250 ,logoImage.cols,logoImage.rows)); Mat mask= imread("dota_logo.jpg" ,0 ); logoImage.copyTo(imageROI,mask); namedWindow("<1>利用ROI實現圖像疊加示例窗口" ); imshow("<1>利用ROI實現圖像疊加示例窗口" ,srcImage1); return true ; }
這個函數首先是載入了兩張jpg圖片到srcImage1和logoImage中,然后定義了一個Mat類型的imageROI,并使用cv::Rect設置其感興趣區域為srcImage1中的一塊區域,將imageROI和srcImage1關聯起來。接著定義了一個Mat類型的的mask并讀入dota_logo.jpg,順勢使用Mat:: copyTo把mask中的內容拷貝到imageROI中,于是就得到了最終的效果圖,namedWindow和imshow配合使用,顯示出最終的結果。
運行結果如下:
?
這里白色的dota2 logo,就是通過操作之后加上去的圖像。
?
?
?
?
二、初級圖像混合——線性混合操作 ?
?
線性混合操作是一種典型的二元(兩個輸入)的像素操作,它的理論公式是這樣的:
??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
如果看過我之前寫的游戲編程Alpha混合那篇文章的朋友們應該有些熟悉,其實他們是差不多的:
?
【Visual C++】游戲開發五十五淺墨 DirectX教程二十二水乳交融的美學:alpha混合技術
?
我們通過在范圍0到1之間改變alpha值,來對兩幅圖像(f0(x)和f1(x))或兩段視頻(同樣為(f0(x)和f1(x))產生時間上的畫面疊化(cross-dissolve)效果,就像幻燈片放映和電影制作中的那樣。即在幻燈片翻頁時設置的前后頁緩慢過渡疊加效果,以及電影情節過渡時經常出現的畫面疊加效果。
實現方面,我們主要運用了OpenCV中addWeighted函數,我們來全面的了解一下它:
?
addWeighted函數
這個函數的作用是,計算兩個數組(圖像陣列)的加權和。原型如下:
?
void addWeighted (InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1 ) ;
第一個參數,InputArray類型的src1,表示需要加權的第一個數組,常常填一個Mat。 第二個參數,alpha,表示第一個數組的權重 第三個參數,src2,表示第二個數組,它需要和第一個數組擁有相同的尺寸和通道數。 第四個參數,beta,表示第二個數組的權重值。 第五個參數,dst,輸出的數組,它和輸入的兩個數組擁有相同的尺寸和通道數。 第六個參數,gamma,一個加到權重總和上的標量值。看下面的式子自然會理解。 第七個參數,dtype,輸出陣列的可選深度,有默認值-1。;當兩個輸入數組具有相同的深度時,這個參數設置為-1(默認值),即等同于src1.depth()。
?
如果用數學公式來表達,addWeighted函數計算如下兩個數組(src1和src2)的加權和,得到結果輸出給第四個參數。即addWeighted函數的作用可以被表示為為如下的矩陣表達式為:
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dst = src1[I]*alpha+ src2[I]*beta + gamma;
?
其中的I,是多維數組元素的索引值。而且,在遇到多通道數組的時候,每個通道都需要獨立地進行處理。另外需要注意的是,當輸出數組的深度為CV_32S時,這個函數就不適用了,這時候就會內存溢出或者算出的結果壓根不對。
?
?
理論和函數的講解就是上面這些,接著我們來看代碼實例,以融會貫通。
?
bool LinearBlending () { double alphaValue = 0.5 ; double betaValue; Mat srcImage2, srcImage3, dstImage; srcImage2= imread("mogu.jpg" ); srcImage3= imread("rain.jpg" ); if (!srcImage2.data ) { printf ("你妹,讀取srcImage2錯誤~! \n" ); return false ; } if (!srcImage3.data ) { printf ("你妹,讀取srcImage3錯誤~! \n" ); return false ; } betaValue= ( 1.0 - alphaValue ); addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0 , dstImage); namedWindow("<2>線性混合示例窗口【原圖】 by淺墨" , 1 ); imshow("<2>線性混合示例窗口【原圖】 by淺墨" , srcImage2 ); namedWindow("<3>線性混合示例窗口【效果圖】 by淺墨" , 1 ); imshow("<3>線性混合示例窗口【效果圖】 by淺墨" , dstImage ); return true ; }
代碼解析:
<0>首先當然是定義一些局部變量,alpha值beta值,三個Mat類型的變量。
?
double alphaValue = 0.5 ; double betaValue; Mat srcImage2, srcImage3, dstImage;
? ? ? ?
在這里我們設置alpha值為0.5。
?
<1>讀取兩幅圖像并作錯誤處理 這步很簡單,直接上代碼:
? ? ? ?
srcImage2= imread("mogu.jpg" ); srcImage3= imread("rain.jpg" ); if (!srcImage2.data ) { printf ("你妹,讀取srcImage2錯誤~! \n" ); return false ; } if (!srcImage3.data ) { printf ("你妹,讀取srcImage3錯誤~! \n" ); return false ; }
在這里需要注意的是,因為我們是對 srcImage1和srcImage2求和,所以它們必須要有相同的尺寸(寬度和高度)和類型,不然多余的部分沒有對應的“伴”,肯定會出問題。
<2> 進行圖像混合加權操作 載入圖像后,我們就可以來生成混合圖像,也就是之前公式中的g(x)。為此目的,使用函數 addWeighted 可以很方便地實現,也就是因為 addWeighted 進行了如下計算:
?
這里的對應于addWeighted的第2個參數alpha
這里的對應于addWeighted的第4個參數beta
這里的對應于addWeighted的第5個參數,在上面代碼中被我們設為0.0。
代碼其實很簡單,就是這樣:?
betaValue = ( 1.0 - alphaValue ); addWeighted( srcImage2, alphaValue, srcImage3,betaValue, 0.0 , dstImage); 其中beta值為1 -alpha,gamma值為0 。
<3>創建顯示窗口,顯示圖像。
namedWindow("<2>線性混合示例窗口【原圖】 by淺墨" , 1 ); imshow("<2>線性混合示例窗口【原圖】 by淺墨" , srcImage2 ); namedWindow("<3>線性混合示例窗口【效果圖】 by淺墨" , 1 ); imshow("<3>線性混合示例窗口【效果圖】 by淺墨" , dstImage );
接著來看一下運行效果圖,首先是原圖:
然后是效果圖:
?
?
三、綜合示例 ?
在前面分別介紹的設定感興趣區域ROI和使用addWeighted函數進行圖像線性混合的基礎上,我們還將他們兩者中和起來使用,也就是先指定ROI,并用addWeighted函數對我們指定的ROI區域的圖像進行混合操作,我們將其封裝在了一個名為ROI_LinearBlending的函數中,方便大家分塊學習。
?
bool ROI_LinearBlending () { Mat srcImage4= imread("dota_pa.jpg" ,1 ); Mat logoImage= imread("dota_logo.jpg" ); if (!srcImage4.data ) { printf ("你妹,讀取srcImage4錯誤~! \n" ); return false ; } if (!logoImage.data ) { printf ("你妹,讀取logoImage錯誤~! \n" ); return false ; } Mat imageROI; imageROI=srcImage4(Rect(200 ,250 ,logoImage.cols,logoImage.rows)); addWeighted(imageROI,0.5 ,logoImage,0.3 ,0. ,imageROI); namedWindow("<4>區域線性圖像混合示例窗口 by淺墨" ); imshow("<4>區域線性圖像混合示例窗口 by淺墨" ,srcImage4); return true ; }
從這篇文章開始,如果不出意外的話,為了方便大家分塊各個擊破學習,每講一個部分,示例代碼都將封裝在一個函數中,免得大家像學習各種不是特別地道的OpenCV教程時一樣,看到代碼全放在main函數中,心都碎了。
?
好了,下面放出詳細注釋的本篇文章的全部示例源代碼:
?
#include <cv.h> #include <highgui.h> #include <iostream> using namespace cv;using namespace std ; bool ROI_AddImage () ;bool LinearBlending () ;bool ROI_LinearBlending () ; int main ( ) { system("color 5E" ); if (ROI_AddImage()&& LinearBlending( )&&ROI_LinearBlending( )) { cout <<endl <<"嗯。好了,得出了你需要的圖像~! : )" ; } waitKey(0 ); return 0 ; } bool ROI_AddImage () { Mat srcImage1= imread("dota_pa.jpg" ); Mat logoImage= imread("dota_logo.jpg" ); if (!srcImage1.data ) { printf ("你妹,讀取srcImage1錯誤~! \n" ); return false ; } if (!logoImage.data ) { printf ("你妹,讀取logoImage錯誤~! \n" ); return false ; } Mat imageROI= srcImage1(Rect(200 ,250 ,logoImage.cols,logoImage.rows)); Mat mask= imread("dota_logo.jpg" ,0 ); logoImage.copyTo(imageROI,mask); namedWindow("<1>利用ROI實現圖像疊加示例窗口" ); imshow("<1>利用ROI實現圖像疊加示例窗口" ,srcImage1); return true ; } bool LinearBlending () { double alphaValue = 0.5 ; double betaValue; Mat srcImage2, srcImage3, dstImage; srcImage2= imread("mogu.jpg" ); srcImage3= imread("rain.jpg" ); if (!srcImage2.data ) { printf ("你妹,讀取srcImage2錯誤~! \n" ); return false ; } if (!srcImage3.data ) { printf ("你妹,讀取srcImage3錯誤~! \n" ); return false ; } betaValue= ( 1.0 - alphaValue ); addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0 , dstImage); namedWindow("<2>線性混合示例窗口【原圖】 by淺墨" , 1 ); imshow("<2>線性混合示例窗口【原圖】 by淺墨" , srcImage2 ); namedWindow("<3>線性混合示例窗口【效果圖】 by淺墨" , 1 ); imshow("<3>線性混合示例窗口【效果圖】 by淺墨" , dstImage ); return true ; } bool ROI_LinearBlending () { Mat srcImage4= imread("dota_pa.jpg" ,1 ); Mat logoImage= imread("dota_logo.jpg" ); if (!srcImage4.data ) { printf ("你妹,讀取srcImage4錯誤~! \n" ); return false ; } if (!logoImage.data ) { printf ("你妹,讀取logoImage錯誤~! \n" ); return false ; } Mat imageROI; imageROI=srcImage4(Rect(200 ,250 ,logoImage.cols,logoImage.rows)); addWeighted(imageROI,0.5 ,logoImage,0.3 ,0. ,imageROI); namedWindow("<4>區域線性圖像混合示例窗口 by淺墨" ); imshow("<4>區域線性圖像混合示例窗口 by淺墨" ,srcImage4); return true ; }
最后看一下整體的運行效果圖。
首先是經過背景顏色修改的console窗口:
然后依次是四張效果圖:
?嗯,本篇文章到這里就基本結束了,最后放出本篇文章配套示例程序的下載地址。
本篇文章的配套源代碼請點擊這里下載:
【淺墨OpenCV入門教程之四】配套源代碼下載
OK,本節的內容大概就是這些,我們下篇文章見:)
<div class="person-messagebox"><div class="left-message"><a href="https://blog.csdn.net/zhmxy555"><img src="https://profile.csdnimg.cn/0/4/9/3_zhmxy555" class="avatar_pic" username="zhmxy555"></a></div><div class="middle-message"><div class="title"><span class="tit "><a href="https://blog.csdn.net/zhmxy555" data-report-click="{"mod":"popu_379","ab":"new"}" target="_blank">淺墨_毛星云</a></span><!-- 等級,level --><img class="identity-icon" src="https://csdnimg.cn/identity/blog8.png"> <span class="flag expert"><a href="https://blog.csdn.net/home/help.html#classicfication" target="_blank"><img src="https://csdnimg.cn/release/phoenix/template/new_img/identityExpert.png" alt="">博客專家</a></span></div><div class="text"><span>原創文章 159</span><span>獲贊 9891</span><span>訪問量 755萬+</span></div></div><div class="right-message"><a class="btn btn-sm attented bt-button personal-watch" data-report-click="{"mod":"popu_379","ab":"new","extend1":"個人信息已關注"}">已關注</a><a href="https://bbs.csdn.net/topics/395528843" target="_blank" class="btn btn-sm bt-button personal-messageboard">他的留言板</a></div></div></div>
</article>
總結
以上是生活随笔 為你收集整理的转载:【OpenCV入门教程之四】 ROI区域图像叠加初级图像混合 全剖析 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。