OpenCV 笔记(13):连通域分析

1. 图像的连通域以及连通域分析

在该系列第六篇文章中,曾经介绍过连通的概念,下面再来回顾一下。

连通 :若 S 是图像中的一个像素子集,对于任意的 <math xmlns="http://www.w3.org/1998/Math/MathML"> p 、 q ∈ S p、q \in S </math>p、q ∈ S。如果存在一条由 S 中像素组成的从 p 到 q 的通路,则称 p 在像素集 S 中与 q 连通

连通域是指具有相同属性的连通集合。例如,在一个二值图像中,具有相同像素值的区域构成一个连通域。

所以,连通连通域 是两个不同的概念,连通域连通的子集。连通域具有以下特性:

  • 一个连通域中的所有像素都具有相同的性质。
  • 一个连通域中的任意两个像素都可以通过连续的路径连接起来。
  • 一个连通域可以是单连通的,也可以是多连通的。

连通域分析 是指在图像中查找标记 连通域的算法。这是一种常用的图像处理技术,可以用于目标检测、图像分割和形状识别等等。本文是基于二值图像进行连通域的分析。

2. 连通域分析的算法

连通域分析的算法可以分为以下几类:

  • 基于标记的算法:该类算法首先将每个像素分配一个唯一的标记,然后使用某种策略将具有相同标记的像素连接起来。基于标记的算法包括两遍扫描法、种子填充法和快速连通域查找法。

  • 基于邻域查找的算法:该类算法从图像中的一个起始像素开始,逐个检查其邻域像素,如果邻域像素具有相同像素值,则将其加入到当前连通域中。基于邻域查找的算法包括深度优先搜索(DFS)和广度优先搜索(BFS)。

  • 基于分割的算法:该类算法将图像分割成多个区域,每个区域都具有相同的像素值。基于分割的算法包括图论方法和区域生长法。

其中,基于标记的算法是比较常用的连通域分析的算法。

2.1 两遍扫描法

两遍扫描算法的步骤如下

  • 第一遍扫描
  1. 从左到右,从上到下遍历图像。
  2. 将每个有效像素赋予一个唯一的标记。
  • 第二遍扫描
  1. 再次从左到右,从上到下遍历图像。
  2. 检查两个相邻像素是否具有相同的标记。
  3. 如果两个相邻像素具有相同的标记,则将它们连接起来。

2.2 种子填充法

种子填充算法的步骤如下:

  1. 初始化:将图像中的所有像素标记为未访问。

  2. 选择一个起始像素作为种子。

  3. 将种子像素标记为已访问。

  4. 检查种子像素的邻域像素。

    • 如果邻域像素具有相同的像素值,则将其标记为已访问。
    • 如果邻域像素具有不同的像素值,则忽略。
  5. 重复步骤 4,直到图像中的所有像素都被标记为已访问或直到没有未访问的邻域像素为止。

3. OpenCV 自带的连通域函数

OpenCV 提供了两个函数:connectedComponents()、connectedComponentsWithStats() 在二值图像中查找连通域。

下面的例子,在图中找到连通域并标记不同的颜色。

cpp 复制代码
#include <iostream>
#include <map>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

void labelColor(Mat& labelImg, Mat& dst)
{
    map<int, Scalar> colors;

    int width = labelImg.cols;
    int height = labelImg.rows;

    dst = Mat::zeros(labelImg.size(), CV_8UC3);

    uchar r = 255 * (rand()/(1.0 + RAND_MAX));
    uchar g = 255 * (rand()/(1.0 + RAND_MAX));
    uchar b = 255 * (rand()/(1.0 + RAND_MAX));

    for (int i = 0; i < height; i++)
    {
        int* data_src = (int*)labelImg.ptr<int>(i);
        uchar* data_dst = dst.ptr<uchar>(i);

        for (int j = 0; j < width; j++)
        {
            int pixelValue = data_src[j];
            if (pixelValue >= 1)
            {
                if (colors.count(pixelValue) == 0)
                {
                    colors[pixelValue] = Scalar(b,g,r);
                    r = 255 * (rand()/(1.0 + RAND_MAX));
                    g = 255 * (rand()/(1.0 + RAND_MAX));
                    b = 255 * (rand()/(1.0 + RAND_MAX));
                }

                Scalar color = colors[pixelValue];
                *data_dst++ = color[0];
                *data_dst++ = color[1];
                *data_dst++ = color[2];
            }
            else
            {
                data_dst++;
                data_dst++;
                data_dst++;
            }
        }
    }
}

int main(int argc, char **argv) {
    Mat src = imread(".../coins.jpg");
    imshow("src", src);

    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 灰度化
    imshow("gray", gray);

    Mat gauss;
    GaussianBlur(gray, gauss, Size(15, 15),0); //降噪

    threshold(gauss, thresh,0,255,THRESH_BINARY | THRESH_OTSU );
    imshow("thresh", thresh);

    Mat labels, stats, centroids;
    int num_labels = connectedComponents(thresh, labels,8,CV_32S);
    cout << "num_labels = " << num_labels << endl;

    Mat result;
    labelColor(labels,result);
    imshow("Connected Components", result);

    waitKey(0);

    return 0;
}
cpp 复制代码
int connectedComponents(InputArray image, OutputArray labels,int connectivity, int ltype, int ccltype);

其各个参数的含义:

第一个参数 image:输入图像必须是二值图像1。 第二个参数 labels:输出图像,其中每个像素值表示其所在连通域的标签。标签的值从 0 开始,依次递增。 第三个参数 connectivity:标记连通域时使用的邻域种类。可选的是 4、8,默认值是 8。 第四个参数 ltype:输出标签的类型。可以设置为 CV_32S 或 CV_16U,默认值是 CV_32S 。 第五个参数 ccltype:标记连通域分析算法的类型。

参数类型 作用
CCL_WU 0 8 邻域和 4 邻域均用 SAUF 算法。
CCL_DEFAULT -1 8 邻域用 BBDT 算法,4 邻域用 SAUF 算法。
CCL_GRANA 1 8 邻域用 BBDT 算法,4 邻域用 SAUF 算法。

connectedComponentsWithStats() 函数除了返回每个连通域的标签之外,还返回每个连通域的其他信息,包括:

  • 面积
  • 外接矩形
  • 中心坐标
cpp 复制代码
int main(int argc, char **argv) {
    Mat src = imread(".../coins.jpg");
    imshow("src", src);

    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 灰度化
    imshow("gray", gray);

    Mat gauss;
    GaussianBlur(gray, gauss, Size(15, 15),0); //降噪

    threshold(gauss, thresh,0,255,THRESH_BINARY | THRESH_OTSU );
    imshow("thresh", thresh);

    Mat labels, stats, centroids;
    int num_labels = connectedComponentsWithStats(thresh, labels, stats, centroids);
    cout << "num_labels = " << num_labels << endl;

    for (int i = 1; i < num_labels; i++)
    {
        int area = stats.at<int>(i, CC_STAT_AREA);
        int left = stats.at<int>(i, CC_STAT_LEFT);
        int top = stats.at<int>(i, CC_STAT_TOP);
        int width = stats.at<int>(i, CC_STAT_WIDTH);
        int height = stats.at<int>(i, CC_STAT_HEIGHT);
        int cx = centroids.at<double>(i, 0);
        int cy = centroids.at<double>(i, 1);

        cout << "Object " << i << ": " << "Area=" << area << ", Left=" << left << ", Top=" << top << ", Width=" << width << ", Height=" << height << ", Centroid=(" << cx << ", " << cy << ")" << endl;

        rectangle(src, Point(left, top), Point(left + width, top + height), Scalar(0,0,255), 2);
        circle(src, Point(cx, cy), 2, Scalar(0,0,255), 2);
    }
    imshow("Connected Components", src);
    waitKey(0);

    return 0;
}

执行结果:

ini 复制代码
num_labels = 8
Object 1: Area=17061, Left=344, Top=160, Width=144, Height=151, Centroid=(415, 234)
Object 2: Area=9655, Left=592, Top=411, Width=108, Height=114, Centroid=(645, 467)
Object 3: Area=9558, Left=776, Top=441, Width=108, Height=113, Centroid=(828, 497)
Object 4: Area=17874, Left=435, Top=517, Width=150, Height=152, Centroid=(509, 592)
Object 5: Area=17502, Left=660, Top=648, Width=148, Height=151, Centroid=(733, 723)
Object 6: Area=10420, Left=726, Top=864, Width=114, Height=117, Centroid=(782, 922)
Object 7: Area=15634, Left=628, Top=1075, Width=141, Height=143, Centroid=(698, 1146)

4. 总结

连通域分析是图像处理中常用的算法之一,用于在二值图像中找到具有相同像素值且相互连接的区域。它在图像处理中具有广泛的应用场景。

相关推荐
HelloTonyGo9 小时前
Ubuntu24.04 安装opencv4.10
opencv·ubuntu
Bill6610 小时前
OpenCV GUI常用函数详解
人工智能·opencv·计算机视觉
VB.Net12 小时前
EmguCV学习笔记 VB.Net 12.1 二维码解析
opencv·计算机视觉·c#·图像·vb.net·二维码·emgucv
jndingxin13 小时前
OpenCV特征检测(3)计算图像中每个像素处的特征值和特征向量函数cornerEigenValsAndVecs()的使用
人工智能·opencv·计算机视觉
CharGer.13 小时前
OpenCVHaar级联器实现人脸捕捉和微笑检测
opencv·视觉检测·人脸检测·haar·微笑检测
m_Molly13 小时前
vs2022配置opencv==4.9.0(C++)
c++·opencv
byxdaz13 小时前
基于OpenCV的YOLOv5图片检测
人工智能·opencv·yolo
jndingxin13 小时前
OpenCV特征检测(4)检测图像中的角点函数cornerHarris()的使用
人工智能·opencv·计算机视觉
虚假程序设计17 小时前
pythonnet python图像 C# .NET图像 互转
开发语言·人工智能·python·opencv·c#·.net
极客小张18 小时前
基于STM32MP157与OpenCV的嵌入式Linux人脸识别系统开发设计流程
linux·stm32·单片机·opencv·物联网