關於Two-Pass標記連通域個數


關於Two-Pass標記連通域個數

背景

在完成圖像的一系列處理后,得到二值圖,一般會統計目標數量,即是獲取連通域個數,這里采用TwoPass的方法。

基本思想

在Two-pass連通域標記中,第一次標記(first pass)時從左向右,從上向下掃描,會將各個有效像素置一個label值,判斷規則如下(以4鄰域為例):

  1. 當該像素的左鄰像素和上鄰像素為無效值時,給該像素置一個新的label值,label ++;
  2. 該像素的左鄰像素或者上鄰像素有一個為有效值時,將有效值像素的label賦給該像素的label值;
  3. 當該像素的左鄰像素和上鄰像素都為有效值時,選取其中較小的label值賦給該像素的label值。

此時,還需維護一個關系表,記錄哪些label值屬於同一個連通域。這個關系表通常用union-find數據結構來實現。

原文在這
還有個圖:

示意圖

原文說用union-find結構保存連通域,沒仔細看,於是自己用了個數組保存,類似於hash...實現起來也並不是很麻煩,大概這樣,在開始建立一個map數組固定大小,一般而言應該與用於標記的最大數一致(注意,這里是標記數,是小於連通域數的),這里取uint16最大值,65535,這樣也就意味着連通域數不能太大,估計最大能達到萬把個吧,因為不同的圖標記數和連通域數關系是不一樣的。
在FirstPass時,取周圍點的標記最小值,map[標記]=min,這樣就構成了map數組,然后根據map數據進行第二次標記。

實現

int ImageAlgorithm::TwoPassConnetedDomin(Mat image) {
    Mat imageFlag;
    imageFlag.create(image.rows, image.cols, CV_16UC1);
    for (int k = 0; k < image.rows; ++k) {
        for (int i = 0; i < image.cols; ++i) {
            imageFlag.at<ushort>(k, i) = UINT16_MAX;
        }
    }
    uint16_t mapp[UINT16_MAX];
    memset(mapp, UINT16_MAX, sizeof(uint16_t));
    //第一次掃描,完成ImageFlag中的標記
    int num = 0;
    for (int i = 0; i < image.rows; ++i) {
        for (int j = 0; j < image.cols; ++j) {
            auto &cu = image.at<Vec3b>(i, j);
            if (cu[0] == 255) {
                uint16_t pos[4];
                auto &up = pos[0];
                auto &left = pos[1];
                auto &ul = pos[2];
                if (i > 0)
                    up = imageFlag.at<ushort>(i - 1, j);
                if (j > 0)
                    left = imageFlag.at<ushort>(i, j - 1);
                if (i > 0 && j > 0)
                    ul = imageFlag.at<ushort>(i - 1, j - 1);

                uint16_t min = pos[0];
                for (int m = 1; m < 3; m++)
                    if (min > pos[m])
                        min = pos[m];
                for (int m = 0; m < 3; m++) {
                    if (mapp[pos[m]] > min)
                        mapp[pos[m]] = min;
                }
                if (min == UINT16_MAX) {
                    imageFlag.at<ushort>(i, j) = num;
                    mapp[num] = num;
                    num++;
                    if(num>=UINT16_MAX)
                        return -1;
                } else {
                    imageFlag.at<ushort>(i, j) = min;
                }

            }
        }
    }
    //第二次掃描,進行標記,以及修改map
    map<ushort, uint32_t> colorMap;
    int total = 0;
    for (int n = 0; n < num; ++n) {
        if (mapp[n] == n) {
            total++;
            uint32_t t = 0;
            for (int i = 0; i < 3; ++i) {
                t += (uint32_t) (rand() / (RAND_MAX + 0.0) * 255) << (i * 8);
            }
            colorMap[n] = t;
        } else
            mapp[n] = mapp[mapp[n]];
    }

    for (int i = 0; i < imageFlag.rows; ++i) {
        for (int j = 0; j < imageFlag.cols; ++j) {
            auto t = imageFlag.at<ushort>(i, j);
            if (t != UINT16_MAX) {
                uint32_t c = colorMap[mapp[t]];
                image.at<Vec3b>(i, j)[0] = (uchar) c;
                image.at<Vec3b>(i, j)[1] = (uchar) (c >> 8);
                image.at<Vec3b>(i, j)[2] = (uchar) (c >> 16);
            }
        }
    }
    return total;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM