一、設定感興趣的區域---ROI(region of interest)
在圖像處理領域,我們常常需要設置感興趣區域(ROI,region of interest),來專注或者簡化我們的工作過程 。也就是從圖像中選擇的一個圖像區域,這個區域是我們圖像分析所關注的重點。我們圈定這個區域,以便進行進一步處理。而且,使用ROI指定我們想讀入的目標,可以減少處理時間,增加精度,給圖像處理來帶不小的便利。
ROI區域定義的兩種方法
定義ROI區域有兩種方法,第一種是使用cv:Rect.顧名思義,cv::Rect表示一個矩形區域。指定矩形的左上角坐標(構造函數的前兩個參數)和矩形的長寬(構造函數的后兩個參數)就可以定義一個矩形區域。
1 //定義一個Mat類型並給其設定ROI區域 2 Mat imageROI; 3 //方法一 4 imageROI=image(Rect(500,250,logo.cols,logo.rows));
另一種定義ROI的方式是指定感興趣行或列的范圍(Range)。Range是指從起始索引到終止索引(不包括終止索引)的一連段連續序列。
cv::Range可以用來定義Range。如果使用cv::Range來定義ROI,那么前例中定義ROI的代碼可以重寫為:
1 //方法二 2 imageROI=srcImage3(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
二、mask掩碼
1 mask(掩碼或者掩膜):是一個8位單通道圖像(灰度圖像/二值圖像) 2 掩碼某個位置如果為0,則在此位置上的操作不起作用 3 掩碼某個位置如果不為0,則在此位置上的操作會起作用。 4 可以用來提取不規則ROI
在下面的示例中,我們通過一個圖像掩膜(mask),直接將插入處的像素設置為logo圖像的像素值,這樣效果會很贊很逼真:
1 // 描述:利用感興趣區域ROI實現圖像疊加 2 #include<iostream> 3 #include<opencv2/core/core.hpp> 4 #include<opencv2/highgui/highgui.hpp> 5 6 using namespace std; 7 using namespace cv;//OpenCV中的C++類和函數都是定義在命名空間cv之內的 8 9 int main() 10 { 11 //【1】讀入圖像 12 Mat srcImage1= imread("poster_dota.jpg"); 13 if(!srcImage1.data ) 14 { 15 cout << "讀取錯誤!" << endl; 16 return false; 17 } 18 19 Mat logoImage= imread("poster_dota_logo.jpg"); 20 if(!logoImage.data ) 21 { 22 cout << "讀取錯誤!" << endl; 23 return false; 24 } 25 26 //【2】定義一個Mat類型並給其設定ROI區域Rect()函數的前兩個參數表示左上角的起始坐標,后兩個參數表示長方向的長、寬 27 Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows)); 28 29 //【3】加載掩模(必須是灰度圖) 30 Mat mask= imread("dota_logo.jpg",0); 31 32 //【4】將掩膜拷貝到ROI 33 logoImage.copyTo(imageROI,mask); 34 35 //【5】顯示結果 36 namedWindow("<1>利用ROI實現圖像疊加示例窗口"); 37 imshow("<1>利用ROI實現圖像疊加示例窗口",srcImage1); 38 39 waitKey(0);//等待按鍵觸發 40 return true; 41 }
首先是載入了兩張jpg圖片到srcImage1和logoImage中,然后定義了一個Mat類型的imageROI,並使用cv::Rect設置其感興趣區域為srcImage1中的一塊區域,將imageROI和srcImage1關聯起來。
接着定義了一個Mat類型的的mask並讀入dota_logo.jpg,順勢使用Mat:: copyTo把mask中的內容拷貝到imageROI中,於是就得到了最終的效果圖,namedWindow和imshow配合使用,顯示出最終的結果。
這里白色的dota2 logo,就是通過操作之后加上去的圖像。
三、初級圖像混合 --- 線性混合
線性混合操作是一種典型的二元(兩個輸入)的像素操作,它的理論公式是這樣的:
我們通過在范圍0到1之間改變alpha值,來對兩幅圖像(f0(x)和f1(x))或兩段視頻(同樣為(f0(x)和f1(x))產生時間上的畫面疊化(cross-dissolve)效果,就像幻燈片放映和電影制作中的那樣。
實現方面,我們主要運用了OpenCV中addWeighted函數。
addWeighted函數
這個函數的作用是,計算兩個數組(圖像陣列)的加權和。原型如下:
1 void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
1 第一個參數,InputArray類型的src1,表示需要加權的第一個數組,常常填一個Mat。 2 第二個參數,alpha,表示第一個數組的權重 3 第三個參數,src2,表示第二個數組,它需要和第一個數組擁有相同的尺寸和通道數。 4 第四個參數,beta,表示第二個數組的權重值。 5 第五個參數,gamma,一個加到權重總和上的標量值。看下面的式子自然會理解。 6 第六個參數,dst,輸出的數組,它和輸入的兩個數組擁有相同的尺寸和通道數。 7 第七個參數,dtype,輸出陣列的可選深度,有默認值-1。;當兩個輸入數組具有相同的深度時,這個參數設置為-1(默認值),即等同於src1.depth()。
如果用數學公式來表達,addWeighted函數計算如下兩個數組(src1和src2)的加權和,得到結果輸出給第四個參數。即addWeighted函數的作用可以被表示為為如下的矩陣表達式為:
dst = src1[I]*alpha+ src2[I]*beta + gamma;
其中的I,是多維數組元素的索引值。而且,在遇到多通道數組的時候,每個通道都需要獨立地進行處理。另外需要注意的是,當輸出數組的深度為CV_32S時,這個函數就不適用了,這時候就會內存溢出或者算出的結果壓根不對。
【示例】
1 //初級圖像混合 --- 線性混合 2 #include<iostream> 3 #include<opencv2/core/core.hpp> 4 #include<opencv2/highgui/highgui.hpp> 5 6 using namespace std; 7 using namespace cv;//OpenCV中的C++類和函數都是定義在命名空間cv之內的 8 9 int main() 10 { 11 double alphaValue = 0.5; 12 double betaValue; 13 14 Mat srcImage2, srcImage3, dstImage; 15 16 //讀取圖像 17 srcImage2 = imread("1.jpg"); 18 if(!srcImage2.data) 19 { 20 cout << "讀取錯誤!" << endl; 21 return false; 22 } 23 srcImage3 = imread("2.jpg"); 24 if(!srcImage3.data) 25 { 26 cout << "讀取錯誤!" << endl; 27 return false; 28 } 29 30 //圖像混合加權操作 31 betaValue = (1.0 - alphaValue); 32 addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); 33 34 //創建窗口並顯示原圖 35 namedWindow("線性混合示例窗口【原圖】", 1); 36 imshow("線性混合示例窗口【原圖】",srcImage2); 37 38 //創建窗口並顯示混合后的圖像 39 namedWindow("線性混合示例窗口【效果圖】", 1); 40 imshow("線性混合示例窗口【效果圖】", dstImage); 41 42 waitKey(0);//等待按鍵輸入 43 return true; 44 }
四、綜合示例
在前面分別介紹的設定感興趣區域ROI和使用addWeighted函數進行圖像線性混合的基礎上,我們還將他們兩者中和起來使用,也就是先指定ROI,並用addWeighted函數對我們指定的ROI區域的圖像進行混合操作。
【示例】
1 //綜合示例 ROI區域圖像疊加 圖像線性混合 2 #include<iostream> 3 #include<opencv2/core/core.hpp> 4 #include<opencv2/highgui/highgui.hpp> 5 6 using namespace cv; 7 using namespace std; 8 9 //函數聲明 10 bool ROI_AddImage(); 11 bool LinearBlending(); 12 bool ROI_LinearBlending(); 13 14 int main() 15 { 16 system("color 5E"); 17 if(ROI_AddImage() && LinearBlending() && ROI_LinearBlending()) 18 { 19 cout<<endl<<"嗯。好了,得出了你需要的圖像~! : )"; 20 } 21 22 waitKey(0); 23 return 0; 24 } 25 26 //ROI圖像疊加 27 bool ROI_AddImage() 28 { 29 30 //【1】讀入圖像 31 Mat srcImage1= imread("dota.jpg"); 32 if(!srcImage1.data ) 33 { 34 cout << "讀取錯誤!" << endl; 35 return false; 36 } 37 38 Mat logoImage= imread("dota_logo.jpg"); 39 if(!logoImage.data ) 40 { 41 cout << "讀取錯誤!" << endl; 42 return false; 43 } 44 45 //【2】方法一:定義一個Mat類型並給其設定ROI區域 46 Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows)); 47 //方法二:定義一個Mat類型並給其設定ROI區域 48 //Mat imageROI=srcImage1(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols)); 49 50 //【3】加載掩模(必須是灰度圖) 51 Mat mask= imread("dota_logo.jpg",0); 52 53 //【4】將掩膜拷貝到ROI 54 logoImage.copyTo(imageROI,mask); 55 56 //【5】顯示結果 57 namedWindow("<1>利用ROI實現圖像疊加示例窗口"); 58 imshow("<1>利用ROI實現圖像疊加示例窗口",srcImage1); 59 //waitKey(0);//等待按鍵輸入 60 return true; 61 } 62 //圖像的線性混合 63 bool LinearBlending() 64 { 65 double alphaValue = 0.5; 66 double betaValue; 67 68 Mat srcImage2, srcImage3, dstImage; 69 70 //讀取圖像 71 srcImage2 = imread("1.jpg"); 72 if(!srcImage2.data) 73 { 74 cout << "讀取錯誤!" << endl; 75 return false; 76 } 77 srcImage3 = imread("2.jpg"); 78 if(!srcImage3.data) 79 { 80 cout << "讀取錯誤!" << endl; 81 return false; 82 } 83 84 //圖像混合加權操作 85 betaValue = (1.0 - alphaValue); 86 addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 1.0, dstImage); 87 88 //創建窗口並顯示原圖 89 namedWindow("線性混合示例窗口【原圖】", 1); 90 imshow("線性混合示例窗口【原圖】",srcImage2); 91 92 //創建窗口並顯示混合后的圖像 93 namedWindow("線性混合示例窗口【效果圖】", 1); 94 imshow("線性混合示例窗口【效果圖】", dstImage); 95 96 //waitKey(0);//等待按鍵輸入 97 return true; 98 } 99 100 //指定區域的線性混合 101 bool ROI_LinearBlending() 102 { 103 //【1】讀取圖像 104 Mat srcImage4= imread("dota.jpg",1); 105 Mat logoImage= imread("dota_logo.jpg"); 106 107 if(!srcImage4.data ) 108 { 109 cout << "讀取錯誤!" << endl; 110 return false; 111 } 112 if(!logoImage.data ) 113 { 114 cout << "讀取錯誤!" << endl; 115 return false; 116 } 117 118 //【2】定義一個Mat類型並給其設定ROI區域 119 Mat imageROI; 120 //方法一 121 imageROI=srcImage4(Rect(200,250,logoImage.cols,logoImage.rows)); 122 //方法二 123 //imageROI=srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols)); 124 125 //【3】將logo加到原圖上 126 addWeighted(imageROI,0.5,logoImage,0.3,0.0,imageROI); 127 128 //【4】顯示結果 129 namedWindow("<4>ROI區域線性圖像混合示例窗口 by淺墨"); 130 imshow("<4>ROI區域線性圖像混合示例窗口 by淺墨",srcImage4); 131 //waitKey(0);//等待按鍵輸入 132 return true; 133 }