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」

你可能感兴趣的:(opencv,计算机视觉,图像处理)