本教程我學習一下opencv中分水嶺算法的具體實作方式。
原始圖像和Mark圖像,它們的大小都是32*32,分水嶺算法的結果是得到兩個連通域的輪廓圖。
原始圖像:(原始圖像必須是3通道圖像)
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcugjMjVWZyEGOxkDO2IDMwcjYyADZ5UTOhBDN0gzNhBDNfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
Mark圖像:
結果圖像:
初始的mark圖像資料如下,黃色的部分為我們的第一個mark區域,值為255,第二個區域為褐紅色的區域,值為128,第三個綠色的區域,值為64。
opencv分水嶺算法描述如下:
初始化mark矩陣,生成最初的注水區域。
1.設定mark圖像的邊框值為-1
2. 标記每個mark區域的邊界為-2
3. 對于mark圖像一個像素值,如果它本身值為0,但上下左右四鄰域有一個像素值不為0,則把該點按照RGB高度值放入相應的隊列。
舉例說明:如下圖像素點,它的mark值為0,但左和上像素值不為0,此時,我們求原始圖像中對應像素的高度值,高度值的計算方式如下面公式,其中R表示Red通道值,G表示Green通道值,B表示Blue通道值,下标L表示左,R表示右,T表示上,B表示下,abs表示取絕對值,min和max分别為最小值和最大值函數:
min(max(abs(R-RL), abs(G-GL), abs(B-BL)),max(abs(R-RT), abs(G-GT), abs(B-BT)),max(abs(R-RR), abs(G-GR), abs(B-BR)),max(abs(R-RB), abs(G-GB), abs(B-BB)))
上圖中指定的像素,它的高度值顯然為0,是以我們把(2,2)點放入高度為0的隊列中(總共有256個隊列,對應0-255的高度)
初始化階段完成後,我們得到下面的mark圖,并把-2對應的邊界像素點,按照其對應的RGB高度值放入相應的隊列。
之後就進入了遞歸注水過程,遞歸過程描述如下:
for(; ; )
{
掃描0-255高度值隊列,如果找到一個像素标記,則彈出該标記,并退出掃描。
如果該像素的四鄰域中存在兩個不同的非0值,表示該點為兩個注水盆地的邊緣,即分水嶺線,在mark圖像中标記該點為-1。
掃描該點的四鄰域,是否存在為0的mark域,存在的話這把該鄰域點按照rgb高度值,放入相應的隊列。
}
經過上述的遞歸過程,最後我們得到的mark圖像如下所示,其中綠色格子的-1即為所有的分水嶺邊界:
代碼參見工程:FirstOpenCV10
我們也可以把輸入圖像換成灰階圖,這樣求高度值時,就比較簡單,void WatershedGray(cv::Mat &src, cv::Mat &dst);該函數示範灰階圖的分水嶺算法。