• opencv连通域标记 connectedComponentsWithStats()函数


    1.背景
    由于需要将图像中的目标提取出来,采用了先分割得到二值化图,然后再进行连通域统计找到最大的连通域,计算其外接矩形作为目标框的方法。
    2.函数定义
    通过搜索,发现在OpenCV 3中提供了连通域标记相关的两个很好的函数,分别是

    cv::connectedComponents() 和cv::connectedComponentsWithStats(),在OpenCV 2中没有这两个函数。
    2.1 connectedComponents()
    仅仅创建了一个标记图(图中不同连通域使用不同的标记,和原图宽高一致);
    调用格式为:

    int cv::connectedComponents (
        cv::InputArrayn image, // input 8-bit single-channel (binary)
        cv::OutputArray labels, // output label map
        int connectivity = 8, // 4- or 8-connected components
        int ltype = CV_32S // Output label type (CV_32S or CV_16U)
    );
    

    2.2 connectedComponentsWithStats()
    也可以完成上面任务,除此之外,还可以返回每个连通区域的重要信息-- bounding box, area, and center of mass( centroid)。函数返回值为连通区域的总数N,范围为[0,N-1],其中0代表背景
    调用格式为:

    int cv::connectedComponentsWithStats (
        cv::InputArrayn image, // input 8-bit single-channel (binary)
        cv::OutputArray labels, // output label map
        cv::OutputArray stats, // Nx5 matrix (CV_32S) of statistics:
        // [x0, y0, width0, height0, area0;
        // ... ; x(N-1), y(N-1), width(N-1),
        // height(N-1), area(N-1)]
        cv::OutputArray centroids, // Nx2 CV_64F matrix of centroids:
        // [ cx0, cy0; ... ; cx(N-1), cy(N-1)]
        int connectivity = 8, // 4- or 8-connected components
        int ltype = CV_32S // Output label type (CV_32S or CV_16U)
    );
    
    

    参数介绍如下:
    左侧返回值
    num_labels:所有连通域的数目,注:第0个代表背景
    右侧输入参数
    image:也就是输入图像,必须是二值图,即8位单通道图像。(因此输入图像必须先进行二值化处理才能被这个函数接受)
    labels:图像上每一像素的标记,用数字1、2、3…表示(不同的数字表示不同的连通域)
    stats:每一个标记的统计信息,是一个5列的矩阵,每一行对应每个连通区域的外接矩形的左上角坐标x、y,以及外接矩形的宽高width、height和面积area,
    centroids:连通域的中心点
    connectivity:可选值为4或8,也就是使用4连通还是8连通。
    ltype:输出图像标记的类型,目前支持CV_32S 和 CV_16U。 返回值:

    其中, stats包含了标签为i的连通域的一些信息,可以如下访问标签为i的连通域的面积

    stats.at<int>(i, CC_STAT_AREA)
    

    访问外接矩形的参数:

    //矩形左上角x
    x = stats.at<int>(max_idx, cv::CC_STAT_LEFT);
    //矩形左上角y
    y = stats.at<int>(max_idx, cv::CC_STAT_TOP);
    //矩形高
    h = stats.at<int>(max_idx, cv::CC_STAT_HEIGHT);
    //矩形宽
    w = stats.at<int>(max_idx, cv::CC_STAT_WIDTH);
    

    3.过程
    一开始再手机上查到这个函数,使用了索引0,结果直接把全图给框住了,后来仔细看了函数的定义,选择1,把除了背景以外的最大值给圈出来了。

    Mat img = imread("F:\\C++_Project\\1.jpg",0);
    Mat labels, stats, centroids,BW;
    //对于OpenCV来说,白色代表有数据,黑色代表没有数据,所以图像输入之前要转换成”黑底白图“
    threshold(img, BW, 11, 255, THRESH_BINARY);
    int nccomps = connectedComponentsWithStats(BW, labels, stats, centroids);
    int max_area=0, max_idx=0;
    //找到最大的连通域区域和索引
    for (int i = 1; i < stats.rows; i++)
    {
    	if (stats.at<int>(i, cv::CC_STAT_AREA)>max_area)
    	{
    		max_area = stats.at<int>(i, cv::CC_STAT_AREA);
    		max_idx = i;
    	}
    }
    //提取最大区域的外接矩形参数
    int x0, y0, w, h, x1, y1;
    x0 = stats.at<int>(max_idx, cv::CC_STAT_LEFT);
    y0 = stats.at<int>(max_idx, cv::CC_STAT_TOP);
    h = stats.at<int>(max_idx, cv::CC_STAT_HEIGHT);
    w = stats.at<int>(max_idx, cv::CC_STAT_WIDTH);
    //标记矩形
    cv::Rect rect(x0, y0, w, h);
    cv::rectangle(img, rect, cv::Scalar::all(int(255)), 1);
    cv::imshow("BW img", img);
    cv::waitKey(0);
    return 0;
    

    4.小结
    一个小问题,但由于没有理清函数定义和用法,就直接用了,导致查bug花了不少时间。以后遇到一个新的函数时,应该先看定义,输入、输出的含义,再应用。
    ————————————————
    参考:1.CSDN博主「i_chaoren」
    2.CSDN博主「NCUTer」

  • 相关阅读:
    Java开发:Java SE 基础知识篇
    c++day2
    简单易上手,亚马逊云科技Amazon CodeWhisperer个性化辅助功能成为开发者好帮手
    当酷雷曼VR直播遇上视频号,会摩擦出怎样的火花?
    《优化接口设计的思路》系列:第1篇—什么是接口缓存
    将对象从3D坐标系转换为2D显示空间(立方体)
    回归预测特征组合最优选择
    Linux脚本练习之script081-nginx日志分析之统计某个时间段的IP
    基于ssm+vue的线上点餐系统
    SBT 综述
  • 原文地址:https://blog.csdn.net/alansss/article/details/127042625