下文主要内容来自《Learning OpenCV 3》page417-419和官方文档
在OpenCV 3中提供了两个很好的函数,在OpenCV 2中没有。
(1)cv::connectedComponents()
(2)cv::connectedComponentsWithStats()
对应的官方文档为:https://docs.opencv.org/3.3.1/d3/dc0/group__imgproc__shape.html#ga107a78bf7cd25dec05fb4dfc5c9e765f
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)
);
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)
);
其中 connectedComponents()仅仅创建了一个标记图(图中不同连通域使用不同的标记,和原图宽高一致), connectedComponentsWith Stats()也可以完成上面任务,除此之外,还可以返回每个连通区域的重要信息-- bounding box, area, and center of mass( centroid). 如果不需要连通域的质心,将参数centroids设置为 cv::noArray() ,这句话在我的版本中运行会出错。
函数返回值为连通区域的总数N,范围为[0,N-1],其中0代表背景。
下面是一个简单的示例,画出了带标记的连通区域,同时去除很小的连通域(相当于无损降噪)。
#include
#include
#include
using namespace std;
int main()
{
cv::Mat src_img, img_bool, labels, stats, centroids, img_color, img_gray;
if( (src_img = cv::imread("2.bmp",0)).empty())
{
cout<<"load image error!";
return -1;
}
cv::threshold(src_img, img_bool, 0, 255, cv::THRESH_OTSU);
//连通域计算
int nccomps = cv::connectedComponentsWithStats (
img_bool, //二值图像
labels, //和原图一样大的标记图
stats, //nccomps×5的矩阵 表示每个连通区域的外接矩形和面积(pixel)
centroids //nccomps×2的矩阵 表示每个连通区域的质心
);
//显示原图统计结果
char title[1024];
sprintf(title,"原图中连通区域数:%d\n",nccomps);
cv::String num_connect(title);
cv::imshow(num_connect, img_bool);
//去除过小区域,初始化颜色表
vector colors(nccomps);
colors[0] = cv::Vec3b(0,0,0); // background pixels remain black.
for(int i = 1; i < nccomps; i++ ) {
colors[i] = cv::Vec3b(rand()%256, rand()%256, rand()%256);
//去除面积小于100的连通域
if( stats.at(i, cv::CC_STAT_AREA) < 100 )
colors[i] = cv::Vec3b(0,0,0); // small regions are painted with black too.
}
//按照label值,对不同的连通域进行着色
img_color = cv::Mat::zeros(src_img.size(), CV_8UC3);
for( int y = 0; y < img_color.rows; y++ )
for( int x = 0; x < img_color.cols; x++ )
{
int label = labels.at(y, x);
CV_Assert(0 <= label && label <= nccomps);
img_color.at(y, x) = colors[label];
}
//统计降噪后的连通区域
cv::cvtColor(img_color,img_gray,cv::COLOR_BGR2GRAY);
cv::threshold(img_gray, img_gray, 1, 255, cv::THRESH_BINARY);
nccomps = cv::connectedComponentsWithStats (img_gray, labels,stats,centroids);
sprintf(title,"过滤小目标后的连通区域数量:%d\n",nccomps);
num_connect = title;
cv::imshow(num_connect, img_color);
cv::waitKey();
return 0;
}