OpenCV圖像分割中的分水嶺算法原理與應(yīng)用詳解
圖像分割是按照一定的原則,將一幅圖像分為若干個(gè)互不相交的小局域的過(guò)程,它是圖像處理中最為基礎(chǔ)的研究領(lǐng)域之一。目前有很多圖像分割方法,其中分水嶺算法是一種基于區(qū)域的圖像分割算法,分水嶺算法因?qū)崿F(xiàn)方便,已經(jīng)在醫(yī)療圖像,模式識(shí)別等領(lǐng)域得到了廣泛的應(yīng)用。
1.傳統(tǒng)分水嶺算法基本原理
分水嶺比較經(jīng)典的計(jì)算方法是L.Vincent于1991年在PAMI上提出的[1]。傳統(tǒng)的分水嶺分割方法,是一種基于拓?fù)淅碚摰臄?shù)學(xué)形態(tài)學(xué)的分割方法,其基本思想是把圖像看作是測(cè)地學(xué)上的拓?fù)涞孛玻瑘D像中每一像素的灰度值表示該點(diǎn)的海拔高度,每一個(gè)局部極小值及其影響區(qū)域稱為集水盆地,而集水盆地的邊界則形成分水嶺。分水嶺的概念和形成可以通過(guò)模擬浸入過(guò)程來(lái)說(shuō)明。在每一個(gè)局部極小值表面,刺穿一個(gè)小孔,然后把整個(gè)模型慢慢浸人水中,隨著浸入的加深,每一個(gè)局部極小值的影響域慢慢向外擴(kuò)展,在兩個(gè)集水盆匯合處構(gòu)筑大壩如下圖所示,即形成分水嶺。
傳統(tǒng)分水嶺算法示意圖
然而基于梯度圖像的直接分水嶺算法容易導(dǎo)致圖像的過(guò)分割,產(chǎn)生這一現(xiàn)象的原因主要是由于輸入的圖像存在過(guò)多的極小區(qū)域而產(chǎn)生許多小的集水盆地,從而導(dǎo)致分割后的圖像不能將圖像中有意義的區(qū)域表示出來(lái)。所以必須對(duì)分割結(jié)果的相似區(qū)域進(jìn)行合并。
[1]L.Vincent, P Soille. Watersheds in digital space: An efficientalgorithms based on immersion simulation[J]. IEEE Trans. on Pattern Analysisand Machine Intelligence, 1991, 13(6): 583-598.
2.改進(jìn)的分水嶺算法基本原理
因?yàn)閭鹘y(tǒng)分水嶺算法存在過(guò)分割的不足,OpenCV提供了一種改進(jìn)的分水嶺算法,使用一系列預(yù)定義標(biāo)記來(lái)引導(dǎo)圖像分割的定義方式。使用OpenCV的分水嶺算法cv::wathershed,需要輸入一個(gè)標(biāo)記圖像,圖像的像素值為32位有符號(hào)正數(shù)(CV_32S類型),每個(gè)非零像素代表一個(gè)標(biāo)簽。它的原理是對(duì)圖像中部分像素做標(biāo)記,表明它的所屬區(qū)域是已知的。分水嶺算法可以根據(jù)這個(gè)初始標(biāo)簽確定其他像素所屬的區(qū)域。傳統(tǒng)的基于梯度的分水嶺算法和改進(jìn)后基于標(biāo)記的分水嶺算法示意圖如下圖所示。
傳統(tǒng)基于梯度的分水嶺算法和基于標(biāo)記的分水嶺算法原理圖
從上圖可以看出,傳統(tǒng)基于梯度的分水嶺算法由于局部最小值過(guò)多造成分割后的分水嶺較多。而基于標(biāo)記的分水嶺算法,水淹過(guò)程從預(yù)先定義好的標(biāo)記圖像(像素)開始,較好的克服了過(guò)度分割的不足。本質(zhì)上講,基于標(biāo)記點(diǎn)的改進(jìn)算法是利用先驗(yàn)知識(shí)來(lái)幫助分割的一種方法。因此,改進(jìn)算法的關(guān)鍵在于如何獲得準(zhǔn)確的標(biāo)記圖像,即如何將前景物體與背景準(zhǔn)確的標(biāo)記出來(lái)。
3.基于標(biāo)記點(diǎn)的分水嶺算法應(yīng)用
基于標(biāo)記點(diǎn)的分水嶺算法應(yīng)用步驟
● 封裝分水嶺算法類
● 獲取標(biāo)記圖像
獲取前景像素,并用255標(biāo)記前景
獲取背景像素,并用128標(biāo)記背景,未知像素,使用0標(biāo)記
合成標(biāo)記圖像
● 將原圖和標(biāo)記圖像輸入分水嶺算法
● 顯示結(jié)果
(1)封裝分水嶺算法類
將分水嶺算法cv::watershed(image,markers)封裝進(jìn)類WatershedSegmenter,并保存為頭文件以便于操作。(本段封裝代碼參考《OpenCV計(jì)算機(jī)視覺編程攻略(第二版)》)
#if !defined WATERSHS #define WATERSHS #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> class WatershedSegmenter { private: cv::Mat markers; public: void setMarkers(const cv::Mat& markerImage) { // Convert to image of ints markerImage.convertTo(markers,CV_32S); } cv::Mat process(const cv::Mat &image) { // Apply watershed cv::watershed(image,markers); return markers; } // Return result in the form of an image cv::Mat getSegmentation() { cv::Mat tmp; // all segment with label higher than 255 // will be assigned value 255 markers.convertTo(tmp,CV_8U); return tmp; } // Return watershed in the form of an image以圖像的形式返回分水嶺 cv::Mat getWatersheds() { cv::Mat tmp; //在變換前,把每個(gè)像素p轉(zhuǎn)換為255p+255(在conertTo中實(shí)現(xiàn)) markers.convertTo(tmp,CV_8U,255,255); return tmp; } }; #endif
(2)獲取標(biāo)記圖像
標(biāo)記前景
讀取原圖
// Read input image cv::Mat image1= cv::imread("image.jpg"); if (!image1.data) return 0; // Display the color image cv::resize(image1, image1, cv::Size(), 0.7, 0.7); cv::namedWindow("Original Image1"); cv::imshow("Original Image1",image1);
原圖
以下代碼目的是獲取前景物體的像素,并用255標(biāo)記。這里使用閾值分割初步分割前景和背景,接著使用形態(tài)學(xué)閉運(yùn)算連接二值圖像中前景的各個(gè)部分,并平滑邊緣。如何更好的獲取前景像素,需要根據(jù)實(shí)際圖像的情況靈活處理。
// Identify image pixels with object Mat binary; cv::cvtColor(image1,binary,COLOR_BGRA2GRAY); cv::threshold(binary,binary,30,255,THRESH_BINARY_INV);//閾值分割原圖的灰度圖,獲得二值圖像 // Display the binary image cv::namedWindow("binary Image1"); cv::imshow("binary Image1",binary); waitKey(); // CLOSE operation cv::Mat element5(5,5,CV_8U,cv::Scalar(1));//5*5正方形,8位uchar型,全1結(jié)構(gòu)元素 cv::Mat fg1; cv::morphologyEx(binary, fg1,cv::MORPH_CLOSE,element5,Point(-1,-1),1);// 閉運(yùn)算填充物體內(nèi)細(xì)小空洞、連接鄰近物體 // Display the foreground image cv::namedWindow("Foreground Image"); cv::imshow("Foreground Image",fg1); waitKey();
閾值分割原圖像的灰度圖
閉運(yùn)算獲取前景
標(biāo)記背景和未知區(qū)域
在上面閾值分割得到的二值圖像binary的基礎(chǔ)上,通過(guò)對(duì)白色前景的深度膨脹運(yùn)算獲得一個(gè)超過(guò)前景實(shí)際大小的物體,緊接著用反向閾值將深度膨脹后的圖像中的黑色部分轉(zhuǎn)換成128,即完成了對(duì)背景像素的標(biāo)記。實(shí)際上,在0~255范圍內(nèi),任意不為0或255的值均可作為背景的標(biāo)記。當(dāng)然如果有其他類型的物體,可以使用另外一個(gè)數(shù)值作為其標(biāo)記。也就是說(shuō),多個(gè)目標(biāo)可以有多個(gè)標(biāo)記來(lái)幫助分水嶺算法正確分割圖像。
// Identify image pixels without objects cv::Mat bg1; cv::dilate(binary,bg1,cv::Mat(),cv::Point(-1,-1),4);//膨脹4次,錨點(diǎn)為結(jié)構(gòu)元素中心點(diǎn) cv::threshold(bg1,bg1,1,128,cv::THRESH_BINARY_INV);//>=1的像素設(shè)置為128(即背景) // Display the background image cv::namedWindow("Background Image"); cv::imshow("Background Image",bg1); waitKey();
將背景設(shè)置為128,未知區(qū)域設(shè)置為0
合成標(biāo)記圖像
將前景、背景及未知區(qū)域合成為一個(gè)標(biāo)記圖像。則標(biāo)記圖像中通過(guò)255標(biāo)記前景物體,通過(guò)128標(biāo)記背景,通過(guò)0標(biāo)記未知區(qū)域。
//Get markers image Mat markers1 = fg1 + bg1; //使用Mat類的重載運(yùn)算符+來(lái)合并圖像。 cv::namedWindow("markers Image"); cv::imshow("markers Image",markers1); waitKey();
標(biāo)記圖像
(3)分水嶺算法分割圖像
將標(biāo)記圖像和原圖輸入分水嶺算法封裝的類WatershedSegmenter,執(zhí)行分水嶺算法,并顯示算法運(yùn)行的結(jié)果。
// Apply watershed segmentation WatershedSegmenter segmenter1; //實(shí)例化一個(gè)分水嶺分割方法的對(duì)象 segmenter1.setMarkers(markers1);//設(shè)置算法的標(biāo)記圖像,使得水淹過(guò)程從這組預(yù)先定義好的標(biāo)記像素開始 segmenter1.process(image1); //傳入待分割原圖 // Display segmentation result cv::namedWindow("Segmentation1"); cv::imshow("Segmentation1",segmenter1.getSegmentation());//將修改后的標(biāo)記圖markers轉(zhuǎn)換為可顯示的8位灰度圖并返回分割結(jié)果(白色為前景,灰色為背景,0為邊緣) waitKey(); // Display watersheds cv::namedWindow("Watersheds1"); cv::imshow("Watersheds1",segmenter1.getWatersheds());//以圖像的形式返回分水嶺(分割線條) waitKey();
代碼segmenter1.process(image)將修改標(biāo)記圖像markers,每個(gè)值為0的像素都會(huì)被賦予一個(gè)輸入標(biāo)簽,而邊緣處的像素賦值為-1,得到的標(biāo)簽圖像如下圖所示。
顯示分水嶺分割圖像
分水嶺分割線顯示
(4)顯示結(jié)果圖像
本步驟的目的是將前景物體的分割結(jié)果在黑/白底色中顯示出來(lái)。背景顏色由黑轉(zhuǎn)白時(shí)使用了Mat矩陣掃描的.ptr方法與指針運(yùn)算。
// Get the masked image Mat maskimage = segmenter1.getSegmentation(); cv::threshold(maskimage,maskimage,250,1,THRESH_BINARY); cv::cvtColor(maskimage,maskimage,COLOR_GRAY2BGR); maskimage = image1.mul(maskimage); cv::namedWindow("maskimage"); cv::imshow("maskimage",maskimage); waitKey(); // Turn background (0) to white (255) int nl= maskimage.rows; // number of lines int nc= maskimage.cols * maskimage.channels(); // total number of elements per line for (int j=0; j<nl; j++) { uchar* data= maskimage.ptr<uchar>(j); for (int i=0; i<nc; i++) { // process each pixel --------------------- if (*data==0) //將背景由黑色改為白色顯示 *data=255; data++;//指針操作:如為uchar型指針則移動(dòng)1個(gè)字節(jié),即移動(dòng)到下1列 } } cv::namedWindow("result"); cv::imshow("result",maskimage); waitKey();
原圖的前景分割圖(黑色背景)
原圖的前景分割圖(白色背景)
從上圖的分割結(jié)果可以看出,基于標(biāo)記圖像的分水嶺算法較好的實(shí)現(xiàn)了復(fù)雜背景下前景目標(biāo)分割。算法應(yīng)用的關(guān)鍵步驟為標(biāo)記圖像的獲取,目前很多文獻(xiàn)提出了各類獲取標(biāo)記圖像的方法,如何使用還需要根據(jù)所處理的圖像來(lái)量身確定。
貼出實(shí)驗(yàn)原始圖像:)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:C語(yǔ)言實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(單鏈表)
欄 目:C語(yǔ)言
下一篇:C++實(shí)現(xiàn)企業(yè)職工工資管理系統(tǒng)
本文標(biāo)題:OpenCV圖像分割中的分水嶺算法原理與應(yīng)用詳解
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/939.html
您可能感興趣的文章
- 01-10實(shí)現(xiàn)opencv圖像裁剪分屏顯示示例
- 01-10使用opencv拉伸圖像擴(kuò)大分辨率示例
- 01-10C++基于Directx MMX實(shí)現(xiàn)的圖像灰度轉(zhuǎn)換代碼
- 01-10C++常用字符串分割方法實(shí)例匯總
- 01-10VC實(shí)現(xiàn)對(duì)話框窗口任意分割
- 01-10C++將CBitmap類中的圖像保存到文件的方法
- 01-10C語(yǔ)言中計(jì)算字符串長(zhǎng)度與分割字符串的方法
- 01-10VC++中圖像處理類CBitmap的用法
- 01-10基于C++實(shí)現(xiàn)kinect+opencv 獲取深度及彩色數(shù)據(jù)
- 01-10淺談C語(yǔ)言的字符串分割


閱讀排行
- 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ī)閱讀
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10delphi制作wav文件的方法
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?