1. 程式人生 > >分水嶺演算法分割影象的原理概述及OpenCV程式碼實現

分水嶺演算法分割影象的原理概述及OpenCV程式碼實現

前面博文中提到的影象閾值化,影象邊緣檢測,影象輪廓檢測實際上都是一種影象分割法,影象分割在影象處理識別是非常重要的,這點沒有做過影象識別或影象處理的人都很容易想到,就不多解釋了。所以影象分割法就根據不同的應用需要出現了很多方法。

本篇博文介紹利用分水嶺進行影象分割的方法。它是一種區域分割法,區域分割法利用影象的空間性質,以畫素點之間的相似性為依據,根據不同的分割準則進行影象分割。這樣能彌補閾值、邊緣檢測、輪廓檢測中忽略畫素點空間關係的缺點。

分水嶺演算法應用於影象分割領域,不僅能夠保留了一些傳統分割方法的普遍優點(PS:從下面給出的程式碼中可以看出,在應用分山嶺演算法前,要用閾值化,邊緣檢測等對影象作處理得到分水嶺標記圖,所以,分水嶺演算法保留了一些傳統分割方法的普遍優點),還可以有效克服傳統影象分割演算法中存在的缺點和弊端,該演算法應用於影象分割已經越來越得到研究領域的重視,同時有更加廣闊的應用。

分水嶺分割是基於自然的啟發演算法來模擬水流通過地形起伏的現象從而研究總結出來的一種分割方法,其基本原理是將影象特徵看作地理上的地貌特徵,利用畫素的灰度值分佈特徵,對每個符合特徵的區域進行劃分,形成邊界以構成分水嶺。

下面是分水嶺演算法的物理模型

在上面的水嶺演算法示意圖中區域性極小值、積水盆地,分水嶺線以及水壩的概念可以描述為:
(1)區域極小值:導數為0的點,區域性範圍內的最小值點;
(2)集水盆(匯水盆地):當“水”落到匯水盆地時,“水”會自然而然地流到匯水盆地中的區域極小值點處。每一個匯水盆地中有且僅有一個區域極小值點;
(3)分水嶺:當“水”處於分水嶺的位置時,會等概率地流向多個與它相鄰的匯水盆地中;

(4)水壩:人為修建的分水嶺,防止相鄰匯水盆地之間的“水”互相交匯影響。

實現分水嶺演算法有多種方法,最通用最廣泛的實現方法的是Vincent和Soille在1991年提出的模擬浸沒演算法以及Meyer在1994年提出的基於距離函式的演算法,OpenCV提供了函式watershed()來實現分水嶺演算法,它採用的是Meyer在1994年提出的基於距離函式的演算法,具體的論文大家可以搜尋“Meyer, F. Color Image Segmentation, ICIP92, 1992”

watershed()函式的原型如下:

void watershed(InputArray image, InputOutputArray markers)

可見,非常簡單,就兩個引數,但實際上不是那麼簡單的,因為markers可不是給一個Mat就行的,官方文件對這個引數的說明如下:

Before passing the image to the function, you have to roughly outline the desired regions in the image markers with positive (>0) indices. So, every region is represented as one or more connected components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary mask using findContours() and drawContours() (see the watershed.cpp demo). The markers are “seeds” of the future image regions. All the other pixels in markers , whose relation to the outlined regions is not known and should be defined by the algorithm, should be set to 0’s. In the function output, each pixel in markers is set to a value of the “seed” components or to -1 at boundaries between the regions.

翻譯如下

markers中儲存了影象的大致輪廓,並且以值1,2,3...分別表示各個components.markers通常由函式findContours() 和 drawContours()結合使用來獲得。markers相當於watershed()執行時的種子引數。markers中,不屬於輪廓(outlined regions)的點的值應置為0.函式執行後,影象中的畫素如果是在由某個輪廓種子生成的區域中,那麼其值就置為這個種子的編號,如果畫素不在輪廓種子生成的區域中,則置為-1。

PS:上面這段話我想盡可能為大家描述清楚,但實在是文字水平有限,感覺還是說得不清楚,大家見諒。不過不要緊,如果看不懂,下面給出的程式碼看了之後你就全懂了。

程式碼如下

執行結果如下圖所示

幾點說明:

Canny邊緣檢測的閾值由thresh變數定,大家可以根據不同的影象去調整,如果想動態調整,那麼可以用createTrackbar建立滑動條的方法在視窗中調整閾值,詳情見我寫的博文http://blog.csdn.net/wenhao_ir/article/details/51539122

從執行結果來看,出現了分水嶺水割演算法常見的問題,即過度分割的問題。解決過度分割一般採用的方法是合併的方法。具體來說一般是先計算分割部分的畫素歸屬,利用直方直方圖資訊統計相關特徵,對每個分割部分統計直方圖對比的相似性,然後根據相似性判斷分割的兩個部分是否需要合併成一個區域。我根據這個思路寫了一個合併函式,然而效果不好,應該說是失敗的,所以就不把原始碼帖出來讓大家見笑了,工程名是“watershedSegment_08”,以後如果確實需要這個功能,可以再詳細研究。失敗的執行結果如下圖所示:

我在某部落格上看到過一種獲取輪廓之前對影象的操作的思路:首先,對源影象進行灰度化,並使用OTSU進行二值化操作;然後,對二值化影象進行形態學開操作,再利用distanceTransform完成影象距離變換操作;最後,歸一化距離變換的影象,再用這個影象求輪廓。我用這個思路寫了原始碼,效果也不理想,所以也不貼程式碼了,工程編號是“watermeshed_07”,執行結果如下圖所示:

如果你有影象處理開發方面的需求或想接影象處理開發方面的任務掙點零花錢,歡迎加Q2034196302諮詢!
如果你有影象處理開發方面的需求或想接影象處理開發方面的任務掙點零花錢,歡迎加Q2034196302諮詢!
如果你有影象處理開發方面的需求或想接影象處理開發方面的任務掙點零花錢,歡迎加Q2034196302諮詢!