Opencv分水嶺算法學(xué)習(xí)
分水嶺算法可以將圖像中的邊緣轉(zhuǎn)化成“山脈”,將均勻區(qū)域轉(zhuǎn)化為“山谷”,這樣有助于分割目標(biāo)。
分水嶺算法是一種基于拓?fù)淅碚摰臄?shù)學(xué)形態(tài)學(xué)的分割方法,其基本思想是把圖像看作是測(cè)地學(xué)上的拓?fù)涞孛玻瑘D像中的每一點(diǎn)像素的灰度值表示該點(diǎn)的海拔高度,每一個(gè)局部極小值及其影響區(qū)域稱為集水盆,而集水盆的邊界則形成分水嶺。分水嶺的概念和形成可以通過(guò)模擬浸入過(guò)程來(lái)說(shuō)明:在每一個(gè)局部極小值表面,刺穿一個(gè)小孔,然后把整個(gè)模型慢慢浸入水中,隨著浸入的加深,每一個(gè)局部極小值的影響區(qū)域慢慢向外擴(kuò)展,在兩個(gè)集水盆匯合處構(gòu)筑大壩,即形成分水嶺。
分水嶺的計(jì)算過(guò)程是一個(gè)迭代標(biāo)注過(guò)程。分水嶺計(jì)算分成兩個(gè)步驟:一個(gè)是排序過(guò)程,一個(gè)是淹沒過(guò)程。首先對(duì)每個(gè)像素的灰度級(jí)進(jìn)行從低到高的排序,然后在從低到高實(shí)現(xiàn)淹沒的過(guò)程中,對(duì)每一個(gè)局部極小值在h階高度的影響域采用先進(jìn)先出(FIFO)結(jié)構(gòu)進(jìn)行判斷及標(biāo)注。分水嶺變換得到的是輸入圖像的集水盆圖像,集水盆之間的邊界點(diǎn)即為分水嶺。顯然,分水嶺表示的是輸入圖像的極大值點(diǎn)。
簡(jiǎn)而言之,分水嶺算法首先計(jì)算灰度圖的梯度,這對(duì)圖像中的“山谷”或沒有紋理的“盆地”(亮度值低的點(diǎn))的形成是很有效的,也對(duì)“山頭”或圖像中有主導(dǎo)線段的“山脈”(山脊對(duì)應(yīng)的邊緣)的形成有效。然后開始從用戶指定點(diǎn)(或者算法得到點(diǎn))開始持續(xù)“灌注”盆地直到這些區(qū)域連成一片?;谶@樣產(chǎn)生的標(biāo)記就可以把區(qū)域合并到0一起,合并后的區(qū)域又通過(guò)聚集的方式進(jìn)行分割,好像圖像被“填充”起來(lái)一樣。
實(shí)現(xiàn)分水嶺算法–watershed函數(shù)
函數(shù)watershed實(shí)現(xiàn)的分水嶺算法是基于標(biāo)記的分割算法中的一種。在把圖像傳給函數(shù)之前,需要大致勾畫標(biāo)記出圖像中的期望進(jìn)行分割的區(qū)域,它們被標(biāo)記為正指數(shù),所以,每一個(gè)區(qū)域都會(huì)被標(biāo)記為像素值1、2、3等,表示成為一個(gè)或者多個(gè)連接組件,這些標(biāo)記的值可以使用findContours函數(shù)和drawContours函數(shù)由二進(jìn)制的掩碼檢索出來(lái)。這些標(biāo)記就是即將繪制出來(lái)的分割區(qū)域的“種子”,而沒有標(biāo)記清楚的區(qū)域,被置為0,在函數(shù)的輸出中,每一個(gè)標(biāo)記中的像素被設(shè)置為“種子”的值,而區(qū)域間的值被設(shè)置為-1。
void watershed(inputArray,intputOutputArray markers)
*第一個(gè)參數(shù),輸入圖像,需為8位三通道的彩色圖像。
*第二個(gè)參數(shù),函數(shù)調(diào)用后的運(yùn)算結(jié)果存在這里,輸入/輸入32位單通道圖像的標(biāo)記結(jié)果。
#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); //全局函數(shù)聲明 static void on_Mouse(int event,int x,int y,int flags,void*); //主函數(shù) int main() { //載入源圖像 g_srcImage=imread("/Users/new/Desktop/1.jpg"); if(!g_srcImage.data){printf("讀取源圖像srcImage錯(cuò)誤~!\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); //設(shè)置鼠標(biāo)回調(diào)函數(shù) setMouseCallback(WINDOW_NAME, on_Mouse,0); //輪詢按鍵處理 while(1) { //獲取鍵值 int c=waitKey(0); //若按鍵為ESC時(shí),退出 if((char)c == 27) break; //若按鍵為2時(shí),恢復(fù)原圖 if((char)c=='2') { g_maskImage=Scalar::all(0); srcImage.copyTo(g_srcImage); imshow("image",g_srcImage); } //若按鍵為1,則進(jìn)行處理 if((char)c=='1') { //定義一些參數(shù) int i,j,compCount=0; vector<vector<Point>>contours; vector<Vec4i> hierarchy; //尋找輪廓 findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE); //輪廓為空時(shí)的處理 if(contours.empty()) continue; //復(fù)制掩膜 Mat maskImage(g_maskImage.size(),CV_32S); maskImage=Scalar::all(0); //循環(huán)繪制輪廓 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為零時(shí)的處理 if(compCount==0) continue; //生成隨機(jī)顏色 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)); } //計(jì)算處理時(shí)間并輸出到窗口中 double dTime=(double)getTickCount(); //進(jìn)行分水嶺算法 watershed(srcImage, maskImage); dTime=(double)getTickCount()-dTime; printf("\t 處理時(shí)間=%gms\n",dTime*1000./getTickFrequency()); //雙層循環(huán),將分水嶺圖像遍歷存入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; } //回調(diào)函數(shù)定義 void on_Mouse(int event,int x,int y,int flags,void*) { //處理鼠標(biāo)不在窗口中的情況 if(x<0||x>=g_srcImage.cols||y<0||y>=g_srcImage.rows) return; //處理鼠標(biāo)左鍵相關(guān)消息 if(event==EVENT_LBUTTONUP||!(flags & EVENT_FLAG_LBUTTON))//按下左鍵 prevPt=Point(-1,-1); else if(event==EVENT_LBUTTONDOWN)//松開左鍵 prevPt=Point(x,y);//鼠標(biāo)所指的位置 //鼠標(biāo)左鍵按下并移動(dòng),繪制出白色線條 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)計(jì)算算法運(yùn)行時(shí)間:
//計(jì)算處理時(shí)間并輸出到窗口中 double dTime=(double)getTickCount(); //進(jìn)行分水嶺算法 watershed(srcImage, maskImage); dTime=(double)getTickCount()-dTime; printf("\t 處理時(shí)間=%gms\n",dTime*1000./getTickFrequency());
(2)改變圖像某點(diǎn)像素值:Mat類中的at方法對(duì)于獲取圖像矩陣某點(diǎn)的RGB值或者改變某點(diǎn)的值很方便,
對(duì)于單通道的圖像:image.at<uchar>(i, j) 對(duì)于RGB通道的圖像:image.at<Vec3b>(i, j)[0] image.at<Vec3b>(i, j)[1] image.at<Vec3b>(i, j)[2]
(3)Point(-1,-1)解析:由于卷積過(guò)程,圖像矩陣要進(jìn)行填充,Point(-1,-1)即代表卷積開始的位置,這決定了不填充時(shí)的結(jié)果A處于填充后結(jié)果B的位置的那個(gè)部分,從(-1,-1)開始卷積的結(jié)果是A處于B的正中間那塊位置。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:C語(yǔ)言實(shí)現(xiàn)變色進(jìn)度條
欄 目:C語(yǔ)言
本文標(biāo)題:Opencv分水嶺算法學(xué)習(xí)
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/915.html
您可能感興趣的文章
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10深入第K大數(shù)問(wèn)題以及算法概要的詳解
- 01-10深入N皇后問(wèn)題的兩個(gè)最高效算法的詳解
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類算法
- 01-10深入全排列算法及其實(shí)現(xiàn)方法
- 01-10全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
- 01-10貪心算法 WOODEN STICKS 實(shí)例代碼
- 01-10輸出1000以內(nèi)的素?cái)?shù)的算法(實(shí)例代碼)
- 01-10快速模式匹配算法(KMP)的深入理解
- 01-10海量數(shù)據(jù)處理系列之:用C++實(shí)現(xiàn)Bitmap算法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10C#中split用法實(shí)例總結(jié)
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置