您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Opencv如何實現分水嶺算法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
分水嶺算法可以將圖像中的邊緣轉化成“山脈”,將均勻區域轉化為“山谷”,這樣有助于分割目標。
分水嶺算法是一種基于拓撲理論的數學形態學的分割方法,其基本思想是把圖像看作是測地學上的拓撲地貌,圖像中的每一點像素的灰度值表示該點的海拔高度,每一個局部極小值及其影響區域稱為集水盆,而集水盆的邊界則形成分水嶺。分水嶺的概念和形成可以通過模擬浸入過程來說明:在每一個局部極小值表面,刺穿一個小孔,然后把整個模型慢慢浸入水中,隨著浸入的加深,每一個局部極小值的影響區域慢慢向外擴展,在兩個集水盆匯合處構筑大壩,即形成分水嶺。
分水嶺的計算過程是一個迭代標注過程。分水嶺計算分成兩個步驟:一個是排序過程,一個是淹沒過程。首先對每個像素的灰度級進行從低到高的排序,然后在從低到高實現淹沒的過程中,對每一個局部極小值在h階高度的影響域采用先進先出(FIFO)結構進行判斷及標注。分水嶺變換得到的是輸入圖像的集水盆圖像,集水盆之間的邊界點即為分水嶺。顯然,分水嶺表示的是輸入圖像的極大值點。
簡而言之,分水嶺算法首先計算灰度圖的梯度,這對圖像中的“山谷”或沒有紋理的“盆地”(亮度值低的點)的形成是很有效的,也對“山頭”或圖像中有主導線段的“山脈”(山脊對應的邊緣)的形成有效。然后開始從用戶指定點(或者算法得到點)開始持續“灌注”盆地直到這些區域連成一片。基于這樣產生的標記就可以把區域合并到0一起,合并后的區域又通過聚集的方式進行分割,好像圖像被“填充”起來一樣。
實現分水嶺算法–watershed函數
函數watershed實現的分水嶺算法是基于標記的分割算法中的一種。在把圖像傳給函數之前,需要大致勾畫標記出圖像中的期望進行分割的區域,它們被標記為正指數,所以,每一個區域都會被標記為像素值1、2、3等,表示成為一個或者多個連接組件,這些標記的值可以使用findContours函數和drawContours函數由二進制的掩碼檢索出來。這些標記就是即將繪制出來的分割區域的“種子”,而沒有標記清楚的區域,被置為0,在函數的輸出中,每一個標記中的像素被設置為“種子”的值,而區域間的值被設置為-1。
void watershed(inputArray,intputOutputArray markers)
*第一個參數,輸入圖像,需為8位三通道的彩色圖像。
*第二個參數,函數調用后的運算結果存在這里,輸入/輸入32位單通道圖像的標記結果。
#include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> #include<iostream> using namespace cv; using namespace std; //宏定義 #define WINDOW_NAME "image[procedure window]" //全局變量聲明 Mat g_srcImage,g_maskImage; Point prevPt(-1,-1); //全局函數聲明 static void on_Mouse(int event,int x,int y,int flags,void*); //主函數 int main() { //載入源圖像 g_srcImage=imread("/Users/new/Desktop/1.jpg"); if(!g_srcImage.data){printf("讀取源圖像srcImage錯誤~!\n");return false;} //顯示源圖像 imshow(WINDOW_NAME,g_srcImage); Mat srcImage,grayImage; g_srcImage.copyTo(srcImage); //灰度化 cvtColor(srcImage, g_maskImage, COLOR_BGR2GRAY); //imshow("image[mask]",g_maskImage); cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR); //imshow("image[gray]",grayImage); //掩膜圖像初始化為0 g_maskImage=Scalar::all(0); //設置鼠標回調函數 setMouseCallback(WINDOW_NAME, on_Mouse,0); //輪詢按鍵處理 while(1) { //獲取鍵值 int c=waitKey(0); //若按鍵為ESC時,退出 if((char)c == 27) break; //若按鍵為2時,恢復原圖 if((char)c=='2') { g_maskImage=Scalar::all(0); srcImage.copyTo(g_srcImage); imshow("image",g_srcImage); } //若按鍵為1,則進行處理 if((char)c=='1') { //定義一些參數 int i,j,compCount=0; vector<vector<Point>>contours; vector<Vec4i> hierarchy; //尋找輪廓 findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE); //輪廓為空時的處理 if(contours.empty()) continue; //復制掩膜 Mat maskImage(g_maskImage.size(),CV_32S); maskImage=Scalar::all(0); //循環繪制輪廓 for(int index=0;index>=0;index=hierarchy[index][0],++compCount) drawContours(maskImage, contours, index, Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX); //compCount為零時的處理 if(compCount==0) continue; //生成隨機顏色 vector<Vec3b>colorTab; for(int i=0;i<compCount;++i) { int b=theRNG().uniform(0, 255); int g=theRNG().uniform(0, 255); int r=theRNG().uniform(0, 255); colorTab.push_back(Vec3b((uchar)b,(uchar)g,(uchar)r)); } //計算處理時間并輸出到窗口中 double dTime=(double)getTickCount(); //進行分水嶺算法 watershed(srcImage, maskImage); dTime=(double)getTickCount()-dTime; printf("\t 處理時間=%gms\n",dTime*1000./getTickFrequency()); //雙層循環,將分水嶺圖像遍歷存入watershedImage中 Mat watershedImage(maskImage.size(),CV_8UC3); for(i=0;i<maskImage.rows;++i) for(j=0;j<maskImage.cols;++j) { int index=maskImage.at<int>(i,j); if(index==-1) watershedImage.at<Vec3b>(i,j)=Vec3b(255,255,255);//圖像變白色 else if(index<=0||index>compCount) watershedImage.at<Vec3b>(i,j)=Vec3b(0,0,0);//圖像變黑色 else watershedImage.at<Vec3b>(i,j)=colorTab[index-1]; } //混合灰度圖和分水嶺效果圖并顯示最終的窗口 watershedImage=watershedImage*0.5+grayImage*0.5; imshow("image[watershed]",watershedImage); } } return 0; } //回調函數定義 void on_Mouse(int event,int x,int y,int flags,void*) { //處理鼠標不在窗口中的情況 if(x<0||x>=g_srcImage.cols||y<0||y>=g_srcImage.rows) return; //處理鼠標左鍵相關消息 if(event==EVENT_LBUTTONUP||!(flags & EVENT_FLAG_LBUTTON))//按下左鍵 prevPt=Point(-1,-1); else if(event==EVENT_LBUTTONDOWN)//松開左鍵 prevPt=Point(x,y);//鼠標所指的位置 //鼠標左鍵按下并移動,繪制出白色線條 else if(event==EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) { Point pt(x,y); if(prevPt.x<0)//如果指出去了,返回 prevPt=pt; line(g_maskImage, prevPt, pt, Scalar::all(255),2,8,0);//畫白線 line(g_srcImage,prevPt,pt,Scalar::all(255),2,8,0);//畫白線 prevPt=pt; imshow(WINDOW_NAME, g_srcImage); } }
Opencv技巧
(1)計算算法運行時間:
//計算處理時間并輸出到窗口中 double dTime=(double)getTickCount(); //進行分水嶺算法 watershed(srcImage, maskImage); dTime=(double)getTickCount()-dTime; printf("\t 處理時間=%gms\n",dTime*1000./getTickFrequency());
(2)改變圖像某點像素值:Mat類中的at方法對于獲取圖像矩陣某點的RGB值或者改變某點的值很方便,
對于單通道的圖像:image.at<uchar>(i, j) 對于RGB通道的圖像:image.at<Vec3b>(i, j)[0] image.at<Vec3b>(i, j)[1] image.at<Vec3b>(i, j)[2]
(3)Point(-1,-1)解析:由于卷積過程,圖像矩陣要進行填充,Point(-1,-1)即代表卷積開始的位置,這決定了不填充時的結果A處于填充后結果B的位置的那個部分,從(-1,-1)開始卷積的結果是A處于B的正中間那塊位置。
關于“Opencv如何實現分水嶺算法”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。