Two Pass方法连通域检测

原理:

Two-Pass方法检测连通域的原理可参见这篇博客:Two-Pass连通域标记中的Union-Find结构

参考下面动图,一目了然。

7A6AC58E-17EB-24A5-7D16-CBBB9685E9DD.gif

代码:

代码中标记图的数据类型要注意,如果first pass中标记数多于255,就不要用uchar类型,我直接设置为int类型。

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <map>
#include <cassert>
#include <iostream>

using namespace std;

const int max_size = 1000;
int parent[max_size] = { 0 };

// 找到label x的根节点
int Find(int x, int parent[])
{
    assert(x < max_size);
    int i = x;
    while (0 != parent[i])
        i = parent[i];
    return i;
}

// 将label x 和 label y合并到同一个连通域
void Union(int x, int y, int parent[])
{
    assert(x < max_size && y < max_size);
    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;
}

cv::Mat twoPassConnectComponent(cv::Mat &binaryImg)
{
    int imgW = binaryImg.cols;
    int imgH = binaryImg.rows;
    int channel = binaryImg.channels();
    int type = binaryImg.type();
    // first pass
    int label = 0;

    cv::Mat dst = cv::Mat::zeros(cv::Size(imgW, imgH), CV_32SC1);
    for (int y = 0; y < imgH; y++)
    {
        for (int x = 0; x < imgW; x++)
        {
            if (binaryImg.at<uchar>(y, x) != 0)
            {
                int left = (x - 1 < 0) ? 0 : dst.at<int>(y, x - 1);
                int up = (y - 1 < 0) ? 0 : dst.at<int>(y - 1, x);

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

    //second pass 
    for (int y = 0; y < imgH; y++)
    {
        for (int x = 0; x < imgW; x++)
        {
            if (binaryImg.at<uchar>(y, x) != 0)
                dst.at<int>(y, x) = Find(dst.at<int>(y, x), parent);
        }
    }

    return dst;
}

cv::Scalar getRandomColor()
{
    uchar r = 255 * (rand() / (1.0 + RAND_MAX));
    uchar g = 255 * (rand() / (1.0 + RAND_MAX));
    uchar b = 255 * (rand() / (1.0 + RAND_MAX));
    return cv::Scalar(b, g, r);
}

cv::Mat showColorLabel(cv::Mat label)
{
    int imgW = label.cols;
    int imgH = label.rows;
    std::map<int, cv::Scalar> colors;

    cv::Mat colorLabel = cv::Mat::zeros(imgH, imgW, CV_8UC3);
    int *pLabel = (int*)label.data;
    uchar *pColorLabel = colorLabel.data;
    for (int i = 0; i < imgH; i++)
    {
        for (int j = 0; j < imgW; j++)
        {
            int idx = (i*imgW + j) * 3;
            int pixelValue = pLabel[i*imgW + j];
            if (pixelValue > 0)
            {
                if (colors.count(pixelValue) <= 0)
                {
                    colors[pixelValue] = getRandomColor();
                }
                cv::Scalar color = colors[pixelValue];
                pColorLabel[idx + 0] = color[0];
                pColorLabel[idx + 1] = color[1];
                pColorLabel[idx + 2] = color[2];
            }
        }
    }

    return colorLabel;
}

int main() 
{
    // 加载图像
    string imageName = "data/source_images/logo.png";
    cv::Mat image = cv::imread(imageName, 1);
    if (!image.data) 
    {
        cout << "No image data" << endl;
        getchar();
        return -1;
    }
    //转为灰度图
    cv::cvtColor(image, image, CV_RGB2GRAY);
    //阈值化,情景为255,背景为0
    cv::Mat threshImg;
    cv::threshold(image, threshImg, 200, 255, cv::THRESH_BINARY);
    cv::bitwise_not(threshImg, threshImg);

    //连通域检测 two Pass方法标记图像
    cv::Mat labelImg = twoPassConnectComponent(threshImg);

    //不同连通区域用不同颜色表示
    cv::Mat colorLabelImg=showColorLabel(labelImg);

    //显示
    cv::imshow("thresh", threshImg);
    cv::imshow("label", labelImg*20);
    cv::imshow("colorLabel", colorLabelImg);
    cv::waitKey(0);
}
结果:

使用OpenCV的logo为素材图,如下:

(1)转为灰度图然后阈值化

(2)寻找连通域

(3)不同连通区域不同颜色显示

E99FC64A-F0E7-7671-B2BD-168AE031C754.png9C396694-CC79-2E53-0847-ABFCA04630D4.png

ACE6C6DE-5AC2-1376-DCEF-00C601E2E138.pngD8C4841E-8EE8-BCE7-A463-C7EEC69C3049.png

封装后的代码见我的码云code:https://gitee.com/rxdj/twoPassMethod.git

收藏 (0)
评论列表
正在载入评论列表...
我是有底线的