連通分量標記算法

引言

圖像分割領域中一個重要步驟是求取圖像的連通區域,後續圖像輪廓理解的基石。
Matlab圖像工具箱函數bwlabel函數,就是對二值圖像連通區域進行標記的函數。

bwlabel
Label connected components in 2-D binary image。
Syntax
L = bwlabel(BW, n)
[L, num] = bwlabel(BW, n)
Description
L = bwlabel(BW, n) returns a matrix L, of the same size as BW, containing labels for the connected objects in BW. The variable n can have a value of either 4 or 8, where 4 specifies 4-connected objects and 8 specifies 8-connected objects. If the argument is omitted, it defaults to 8.

該函數返回一個與原圖大小一致的標記圖像矩陣。岡薩雷斯書中只介紹了其使用方法,沒有介紹其實現原理等,OpenCV也有connectedComponents函數。有必要造輪子,理解步驟。

Two-pass算法

這裏我實現的是Two Pass算法,四鄰域。該算法對二值圖像掃描兩次:

  1. 當前像素點若是前景(非零),判斷其已掃描過的鄰接節點(上鄰像素,左鄰像素)有無已標記的。若僅有一個含有標記值,則將該標記賦予該位置,若兩個都含有標記值,將最小標記賦予當前值,並將這兩個標記值合併(Union),歸爲同一類;若無,新增標記賦予當前值,即label++。
  2. 對上述含有標記的像素,查找其集合的根節點(Find),用根節點對當前值賦值。

執行動態圖如下(該圖是盜網友的,上面有logo的)。

並查集

這裏使用了一種並查集的數據結構,可以看作數組方式實現的樹結構。該結構主要含有兩個操作:

  • 快速對集合元素合併歸類
  • 快速查找一個元素的根節點

這在嚴蔚敏的數據結構書中有介紹過的。看一下下圖,希望能趕緊回憶起來吧。


並查集

這個並查集數組,初始化的時候全爲0,即每個都是根節點。然後我們發現有些元素屬於同一類,就將其一個節點作爲另一個節點的孩子,合併爲同一個樹。最後,我們都用每個集合的根節點的作爲該集合的標誌。

代碼

#define MAXLABEL 500
uchar parent[MAXLABEL] = {0};

int Find(uchar x, uchar parent[]) 
{
    int i = x;
    while (0 != parent[i])
        i = parent[i];
    return i;
}

void Union(uchar big, uchar small, uchar parent[]) 
{
    uchar i = big;
    uchar j = small;
    while (0 != parent[i])
        i = parent[i];
    while (0 != parent[j])
        j = parent[j];
    if (i != j)
        parent[i] = j;
}

Mat Label(Mat &I)
{
    /// first pass
    int label = 0;

    Mat dst = Mat::zeros(I.size(), I.type());
    for (int nY = 0; nY < I.rows; nY++)
    {
        for (int nX = 0; nX < I.cols;nX++)
        {
            if (I.at<uchar>(nY,nX) != 0)
            {
                uchar left = nX - 1<0?0:dst.at<uchar>(nY, nX - 1);
                uchar up = nY - 1<0?0:dst.at<uchar>(nY - 1, nX);

                if (left != 0 || up != 0)
                {
                    if (left != 0 && up != 0)
                    {
                        dst.at<uchar>(nY, nX) = min(left, up);
                        if(left < up)
                            Union(up,left,parent);
                        else if(up<left)
                            Union(left, up, parent);
                    }
                    else
                        dst.at<uchar>(nY, nX) = max(left, up);
                }
                else
                {
                    dst.at<uchar>(nY, nX) = ++label;

                }
            }
        }
    }

    /// second pass 
    for (int nY = 0; nY < I.rows; nY++)
    {
        for (int nX = 0; nX < I.cols; nX++)
        {
            if (I.at<uchar>(nY, nX) == 1)
                dst.at<uchar>(nY, nX) = Find(dst.at<uchar>(nY, nX), parent);
        }
    }
    return dst;
}

效果

爲了顯示好看,我將連通區域着色,這裏使用哈希函數,將每個標記乘以一個素數對256求餘,這樣可以將顏色散列開來。

並查集並查集

更多閱讀

George Stockman and Linda G. Shapiro. 2001. Computer Vision (1st ed.). Prentice Hall PTR, Upper Saddle River, NJ, USA. Chapter 3.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章