【OpenCV學習】【一】關於影象疊加以及原理解釋(結合MATLAB)
最近在隨淺墨的部落格學習OpenCV,受益匪淺,這系列部落格記錄OpenCV的學習歷程,也對自己的學習做一個筆記。
首先感謝淺墨的OpenCV學習系列教程,幫助很大,在此新增淺墨文章的連結。
以下結合教程寫出關於使用OpenCV實現影象疊加的程式碼以及一些原理解釋,同時結合MATLAB進行學習。
影象的疊加:
由淺入深一步一步學習,首先實現影象疊加,這裡添出background圖(大圖),logo圖(小圖),實現logo嵌入background任意感興趣部分,這裡有兩種方法,先把兩種方法的程式碼添出,再做進一步討論。先添圖片,background和logo。
background:
logo:
方法一:
得到ROI圖與合成圖://--------------------------------------------------------------------------- // 【程式說明】 // 實現功能:影象疊加 // 重要函式: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; }
關於程式碼的標頭檔案包含以及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進行有關實現。