1. 程式人生 > >【OpenCV學習】【一】關於影象疊加以及原理解釋(結合MATLAB)

【OpenCV學習】【一】關於影象疊加以及原理解釋(結合MATLAB)

            最近在隨淺墨的部落格學習OpenCV,受益匪淺,這系列部落格記錄OpenCV的學習歷程,也對自己的學習做一個筆記。

首先感謝淺墨的OpenCV學習系列教程,幫助很大,在此新增淺墨文章的連結。

以下結合教程寫出關於使用OpenCV實現影象疊加的程式碼以及一些原理解釋,同時結合MATLAB進行學習。

影象的疊加:

由淺入深一步一步學習,首先實現影象疊加,這裡添出background圖(大圖),logo圖(小圖),實現logo嵌入background任意感興趣部分,這裡有兩種方法,先把兩種方法的程式碼添出,再做進一步討論。先添圖片,background和logo。

background:


logo:


方法一:

//---------------------------------------------------------------------------
// 【程式說明】
//	實現功能:影象疊加
//	重要函式:imread,imshow,namedWindow(可參考淺墨文章)
//			  Rect,Range,copyTo,addWeighted(參考文章分解)
//---------------------------------------------------------------------------
 
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv; 

int main( )
{	
	Mat background= imread("dota2.jpg");  
	Mat logo= imread("logo.jpg");  
   
	//載入後先顯示  
	namedWindow("background");  
	imshow("background",background);  
  
	namedWindow("logo");  
	imshow("logo",logo);  
	
	Mat imageROI_1;
	//ROI影象與原始影象共享資料
	imageROI_1=background(Rect(200,10,logo.cols,logo.rows));

	namedWindow("imageROI_1");
	imshow("imageROI_1",imageROI_1);//顯示ROI

	logo.copyTo(imageROI_1);//覆蓋logo

	namedWindow("合成圖1");
	imshow("合成圖1",background);

	waitKey();
 
	return 0;
}
得到ROI圖與合成圖:


關於程式碼的標頭檔案包含以及using類的運用,建議檢視淺墨文章。這裡不作闡述這裡主要解析一下

imageROI_1=background(Rect(200,10,logo.cols,logo.rows));

以及

logo.copyTo(imageROI_1);

兩段程式碼。

Rect函式是在圖片當中選出ROI影象部分(即你感興趣的部分),輸入圖片為background。

Rect(200,10,logo.cols,logo.rows);其中200,10為background圖片當中的畫素座標,也是你感興趣部分(這個部分為一個矩形)的最左上角的點的座標,而後面兩個引數分別為感興趣部分矩形的橫向長度(logo.cols即logo的列數)與豎向長度(logo.rows即logo的行數),我們這裡想要選取的感興趣的矩形與我們要嵌入到background中的logo的大小需要一致。

注:我們可以參考上面添出的ROI圖可以看到,ROI圖就是從background提出的感興趣部分矩形。但要注意一點,ROI圖經過Rect提出過後,並不是單純的複製資料,ROI圖的資料與background中感興趣部分矩形的資料是共享的,如果你改變ROI圖的資料,background中感興趣部分矩形的資料也會相應改變,這正是我們下一個函式copyTo想要做的。logo.copyTo(imageROI_1);此函式的輸入為logo圖片,將logo複製到ROI圖片當中,background的相應感興趣的部分也會相應的改變(正如上面所說的),從而達到圖片疊加的效果。

注:文章中的東西都是我通俗的理解,語言比較粗糙。將專業的知識轉化成為我們通俗的直感,我一直以來覺得很有必要的,望海涵。

方法二:background與logo都不變,只是選取的區域改變了,程式碼如下:

//---------------------------------------------------------------------------------

// 【程式說明】
//	實現功能:影象疊加
//	重要函式:imread,imshow,namedWindow(可參考淺墨文章)
//			  Rect,Range,copyTo,addWeighted(參考文章分解)
//---------------------------------------------------------------------------------
 
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv; 

int main( )
{	
	Mat background= imread("dota2.jpg");  
	Mat logo= imread("logo.jpg");  
   
	//載入後先顯示  
	namedWindow("background");  
	imshow("background",background);  
  
	namedWindow("logo");  
	imshow("logo",logo);  
	
	Mat imageROI_2;
	//ROI影象與原始影象共享資料
	imageROI_2=background(Range(250,250+logo.rows),Range(350,350+logo.cols));
	namedWindow("imageROI_2");
	imshow("imageROI_2",imageROI_2);//顯示ROI

	<span style="font-size:24px;">addWeighted(imageROI_2,0.5,logo,0.5,0.,imageROI_2);</span>

	namedWindow("合成圖2");
	imshow("合成圖2",background);

	waitKey();
 
	return 0;
}
先添出合成圖效果:



程式碼和方法一的程式碼大同小異,只是有兩個不同的函式

imageROI_2=background(Range(250,250+logo.rows),Range(350,350+logo.cols));

Range是定義一個範圍的陣列,從橫向和豎向兩個方向定義就可以框出一個感興趣的區域。第一個Range(250,250+logo.rows)定義的是豎向的範圍(注意,與Rect定義的順序不同,Rect是先定義橫向),第二個Range(350,350+logo.cols)定義的是橫向的範圍,以這樣的方法也可以選定出自己感興趣的矩形,很簡單。

下面這個函式就是addWeighted,作用是計算兩個陣列(影象陣列)的加權和:

addWeighted(imageROI_2,0.5,logo,0.5,0.,imageROI_2);

對於這個函式的引數,共有六個(後面用符號代替):

imageROI_2-----------M1

0.5------------------------a1

logo----------------------M2

0.5------------------------a2

0.--------------------------a3

imageROI_2------------M3

這個函式即經過的如下運算:

M3=M1*a1+M2*a2+a3

其中M3為這個函式的輸出,M1,M2為Mat型別的陣列或是影象陣列,這裡M1為ROI圖,M2為logo圖,a1為ROI圖的加權係數,a2為M2的加權係數,a3是一個直流分量,可取0(0.就是0)。

所以你可以看到第二個方法中,嵌入的logo有一定的透明性,是因為logo的加權係數a2沒有取到1,如果把a2取到1,a1取0,效果就和方法一copyTo相同了。

想看關於addWeighted更專業的分解,可參考淺墨入門教程四

把兩種方法合在一幅圖上做做對比:


背景透明化

如果我們嵌入的logo圖不想要它的背景,比如我們的dota2的logo圖片,我們不想要它的白色背景,只要前面的標記,刀塔的字樣,那我們需要將logo圖片進行預處理,將你不想要的部分全部塗成黑色,便可利用copyTo函式進行透明化處理,我在網上重新下載了logo和background,先貼出來:



程式碼如下:

//---------------------------------------------------------------------------------
// 【程式說明】
//	實現功能:影象疊加
//	重要函式:imread,imshow,namedWindow(可參考淺墨文章)
//			  Rect,Range,copyTo,addWeighted(參考文章分解)
//---------------------------------------------------------------------------------
 
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv; 

int main( )
{	
	Mat background= imread("pic.jpg");  
	Mat logo= imread("alpha.png");  
        Mat mask=imread("alpha.png",1);
	//載入後先顯示  
	namedWindow("background");  
	imshow("background",background);  
  
	namedWindow("logo");  
	imshow("logo",logo);  
	
	Mat imageROI_4;
	//ROI影象與原始影象共享資料
	imageROI_4=background(Rect(40,10,logo.cols,logo.rows));

	namedWindow("imageROI_4");
	imshow("imageROI_4",imageROI_4);//顯示ROI

	logo.copyTo(imageROI_4,mask);//覆蓋logo

	namedWindow("合成圖4");
	imshow("合成圖4",background);

	waitKey();
 
	return 0;
}
這個與上面方法一的程式碼基本一樣,但是有兩個不同。

多定義了一個mask:

Mat mask=imread("alpha.png",1);

這個mask被稱為掩碼,是一個標準,後面的引數1在這裡有沒有都沒關係,都是輸出原影象,如果你把引數改為0,輸出的是這個影象的灰度圖。原圖與灰度圖都可以作為掩碼,我這裡使用原圖作為掩碼效果較好,讀者可以使用灰度圖作為掩碼試試。

貼出最後的合成效果圖:



另一個不同就是copyTo函式這裡

logo.copyTo(imageROI_4,mask);

對掩碼mask進行了一個使用,這裡將logo複製到ROI圖當中的時候,首先對mask進行一個判斷。mask當中灰度值為0的區域,相對應的ROI當中的區域就不變化,維持原樣;當mask當中灰度值不為0的區域,相對應的ROI當中的區域就把logo複製上去。

之所以我們要把我們logo背景全部塗成黑色,就是為了當logo作為mask進行判斷時,我們的背景區域不需要,就把它搞成0(黑色),就不會進行操作,維持ROI的原樣,而其他的部分,我們用logo複製到ROI中,相應的background就會發生變化(資料共享嘛,前面說過了的)。

以上就是這個第一階段的OpenCV的學習,先到這裡,稍後繼續新增內容,以及用MATLAB進行有關實現。