10---OpenCV:图像进阶操作之连通区域分析

一、图像连通区域

图像的连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域,连通域分析是指在图像中寻找出彼此互相独立的连通域并将其标记出来。提取图像中不同的连通域是图像处理中较为常用的方法,例如在车牌识别、文字识别、目标检测等领域对感兴趣区域分割与识别。一般情况下,一个连通域内只包含一个像素值,因此为了防止像素值波动对提取不同连通域的影响,连通域分析常处理的是二值化后的图像

二、邻域

邻域即相邻得区域,opencv中有以下两种形式得领域

  • 4-邻域:必须在水平和垂直方向上相邻,相邻的两个像素坐标必须只有一位不同而且只能相差1个像素

  • 8-邻域: 九宫格形式,相邻的两个像素坐标必须只有一位不同而且只能相差1个像素

图示如下:

三、图像邻域分析法

两遍扫描法

两遍扫描法会遍历两次图像,第一次遍历图像时会给每一个非0像素赋予一个数字标签,当某个像素的上方和左侧邻域内的像素已经有数字标签时,取两者中的最小值作为当前像素的标签,否则赋予当前像素一个新的数字标签。第一次遍历图像的时候同一个连通域可能会被赋予一个或者多个不同的标签

种子填充法

首先将所有非0像素放到一个集合中,之后在集合中随机选出一个像素作为种子像素,根据邻域关系不断扩充种子像素所在的连通域,并在集合中删除掉扩充出的像素,直到种子像素所在的连通域无法扩充,之后再从集合中随机选取一个像素作为新的种子像素,重复上述过程直到集合中没有像素(类似DFS)

四、连通区域操作

不带统计信息的API

此处的OutputArray labels是不能直接imshow的,需要进行相应的转换,才能正常显示

int connectedComponents(InputArray image, OutputArray labels,int connectivity = 8, int ltype = CV_32S);
/*******************************************************************
*           image:                  输入二值图像
*           labels:                 输出图像
*           connectivity:            邻域
*           ltype:                   输出图深度
*********************************************************************/

带有统计信息的API

int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids,int connectivity, int ltype, int ccltype);
/*******************************************************************
*           image:                  输入二值图像
*           labels:                 输出图像
*           stats:                   统计信息,包括每个组件的位置、宽、高与面积
*           centroids:               每个组件的中心位置坐标cx, cy
*           connectivity:            邻域
*           ltype:                   输出图深度
*           ccltype:                 连通组件算法
*********************************************************************/
//ccltype取值
enum ConnectedComponentsTypes {
    CC_STAT_LEFT   = 0, //        组件的左上角点像素点坐标的X位置             
    CC_STAT_TOP    = 1, //        组件的左上角点像素点坐标的Y位置
    CC_STAT_WIDTH  = 2, //        组件外接矩形的宽度
    CC_STAT_HEIGHT = 3, //        组件外接矩形的高度
    CC_STAT_AREA   = 4, //        当前连通组件的面积(像素单位)
    CC_STAT_MAX    = 5  //        最大枚举值,仅在内部用于内存分配(可忽略)
};

五、综合代码

注意at函数通过宏的设置获取Mat的一系列的属性(x,y,width,height)

#include <iostream>
#include <string>
#include <time.h>
#include <map>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Connection
{
public:
    Connection() :img(imread("text.jpg",IMREAD_GRAYSCALE))
    {//在构造函数中进行二值化处理.
        result["原图"] = img;
        threshold(img, result["阈值化"], 200, 255, THRESH_BINARY_INV);
    }
    //着色
    void DrawColor(Mat& src, Mat& result, int nCount)
    {
        vector<Vec3b> colors(nCount);
        for (int i = 1; i < nCount; i++)
        {
            colors[i] = Vec3b(rand() % 256, rand() % 256, rand() % 256);
        }
        //连通区域着色
        result = Mat::zeros(img.size(), CV_8UC3);
        //这里的CV_8UC3实际上就是完成了输出图片格式的转换。
        for (int y = 0; y < img.rows; y++)
        {
            for (int x = 0; x < img.cols; x++)
            {
                int label = src.at<int>(y, x);
                if (label > 0 && label <= nCount)
                {//着色
                    result.at<Vec3b>(y, x) = colors[label];
                }
                if (label == 0)
                {//黑色部分直接用白色来填充(一开始用的反二值化--->见下面的原理图)
                    result.at<Vec3b>(y, x) = Vec3b(255, 255, 255);
                }
            }
        }
​
    }
    void NoCount()
    {
        Mat temp;
        int nCount = connectedComponents(result["阈值化"], temp);
        //temp不能直接显示,需要转换后才能显示(在DrawColor里面res的CV_8UC3完成转换) 
        DrawColor(temp, result["不统计"], nCount);
    }
    void Count() 
    {
        Mat stats, center, temp;
        int nCount = connectedComponentsWithStats(result["阈值化"], temp, stats, center, 8, CC_STAT_AREA);
        DrawColor(temp, result["统计"], nCount);
        //利用统计信息标记连通域
        for (int i = 1; i < nCount; i++) 
        {
            int x = stats.at<int>(i, CC_STAT_LEFT);
            int y = stats.at<int>(i, CC_STAT_TOP);
            int w = stats.at<int>(i, CC_STAT_WIDTH);
            int h = stats.at<int>(i, CC_STAT_HEIGHT);
            rectangle(result["统计"], Rect(x, y, w, h), Scalar(0, 0, 0), 2);
        }
    }
    void Show() 
    {
        for (auto& v : result) 
        {
            imshow(v.first, v.second);
        }
        waitKey(0);
    }
protected:
    Mat img;
    map<string, Mat> result;
};
​
int main() 
{
    srand((unsigned int)time(nullptr));
    Connection* p = new Connection;
    p->NoCount();
    p->Count();
    p->Show();
    return 0;
}

  • 6
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Ocean__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值