Two-pass連通域標記中的union-find結構
在Two-pass連通域標記中,第一次標記(first pass)時從左向右,從上向下掃描,會將各個有效畫素置一個label值,判斷規則如下(以4鄰域為例):
1) 當該畫素的左鄰畫素和上鄰畫素為無效值時,給該畫素置一個新的label值,label ++;
2) 當該畫素的左鄰畫素或者上鄰畫素有一個為有效值時,將有效值畫素的label賦給該畫素的label值;
3) 當該畫素的左鄰畫素和上鄰畫素都為有效值時,選取其中較小的label值賦給該畫素的label值。
此時,還需維護一個關係表,記錄哪些label值屬於同一個連通域。這個關係表通常用union-find
在union-find結構中,屬於同一個連通域的label值被儲存到同一個樹形結構中,如圖1所示,{1,2,3,4,8}屬於同一個連通域,{5,6,7}屬於同一個連通域。同時,樹形結構的資料儲存到一個vector或array中,vector的下標為label值,vector的儲存值為該label的父節點label值,當vector的儲存值為0時,說明該label是根節點。這樣,對於任意一個label,我們都可以尋找其根節點,作為所在連通域的label值,即某一連通域所有畫素都被置為同一個label值,即根節點label值。對給定的任意一個label,我們可以通過find演算法尋找其根節點,如圖2
如果知道兩個label同屬於一個連通域,要如何在vector中實現其儲存關係呢?首先,各自尋找兩個label的根節點,如果二者的根節點相同,則二者已經屬於同一個連通域;如果二者的根節點不同,那麼把其中一個根節點作為另一個的父節點即可,即將兩個label劃入同一個連通域,或者叫做連通域合併,演算法如圖3所示。
那麼這個儲存label值的vector要如何初始化呢?將vector初始化為0即可,即各個label值都是根節點,大家互不相連。同時注意,vector[0]不使用。
實現程式碼如下:
const int max_size = 100; int parent[100] = {0}; // 找到label x的根節點 int find(int x, int parent[]){ int i = x; while(0 != parent[i]) i = parent[i]; return i; } // 將label x 和 label y合併到同一個連通域 void union_label(int x, int y, int parent[]){ int i = x; int j = y; while(0 != parent[i]) i = parent[i]; while(0 != parent[j]) j = parent[j]; if(i != j) parent[i] = j; }
reference: Shapiro, L., and Stockman, G. (2002).Computer Vision.
Prentice Hall. pp. 69–73.