OpenCV学习(二十一) :计算图像连通分量:connectedComponents(),connectedComponentsWithStats()
1、connectedComponents()函数
Connected Components即连通体算法用id标注图中每个连通体,将连通体中序号最小的顶点的id作为连通体的id。如果在图G中,任意2个顶点之间都存在路径,那么称G为连通图,否则称该图为非连通图,则其中的极大连通子图称为连通体,如下图所示,该图中有两个连通体:
计算二值图像中为图像的连通分量标注(标记)
int cv::connectedComponents(
cv::InputArrayn image, // 输入8位单通道(二进制)
cv::OutputArray labels, // 输出标签图
int connectivity = 8, // 4-或8-连接组件
int ltype = CV_32S // 输出标签类型 (CV_32S or CV_16U));
2、connectedComponentsWithStats()函数
这是一个重载的成员函数,它与上述函数的不同之处在于它只接受什么参数。
注意0的区域标识的是background,而centroids则对应的是中心点,而label则对应于表示是当前像素是第几个轮廓
int cv::connectedComponentsWithStats (
cv::InputArrayn image, // 输入8位单通道(二进制)
cv::OutputArray labels, // 输出标签地图
cv::OutputArray stats, // 统计量的Nx5矩阵(CV_32S):分别对应各个轮廓的包围框的起始点坐标x,y,// 各个轮廓的包围框的 width,height和面积:// [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中心矩阵:// [ cx0, cy0; ... ; cx(N-1), cy(N-1)]
int connectivity = 8, // 4-或8-连接组件
int ltype = CV_32S // 输出标签类型 (CV_32S or CV_16U)
);
3、示例:
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main()
{Mat src =imread("F:/C++/2. OPENCV 3.1.0/TEST/test5.PNG",1);if(!src.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }imshow( "image", src );// 转灰度Mat src_gray;cvtColor( src, src_gray, CV_BGR2GRAY );cv::Mat img_edge, labels, img_color, stats,centroids;// 二值化cv::threshold(src_gray, img_edge, 125, 255, cv::THRESH_BINARY);cv::imshow("Image before threshold", img_edge);// 白色代表有数据,黑色代表没有数据,所以图像输入之前要转换成”黑底白图“bitwise_not(img_edge,img_edge); // 该函数计算输入数组的逐元素逐位反转:cv::imshow("Image after threshold", img_edge);// 计算连通分量// labels 输出标签图// stats Nx5矩阵(CV_32S): 分别对应各个轮廓的x,y,width,height和面积int nccomps = cv::connectedComponentsWithStats ( img_edge, labels, stats, centroids);cout << "检测到总连接组件: " << nccomps << endl;vector<cv::Vec3b> colors(nccomps+1);colors[0] = Vec3b(0,0,0); // 背景像素保持黑色。for( int i = 1; i < nccomps; i++ ) // 为每个标签设置颜色{colors[i] = Vec3b(rand()%256, rand()%256, rand()%256);if( stats.at<int>(i, cv::CC_STAT_AREA) < 200 ) // 面积小于200 的colors[i] = Vec3b(0,0,0); // 小区域也被涂成黑色}//img_color = Mat::zeros(src.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<int>(y, x); // 获得每个 轮廓图标签CV_Assert(0 <= label && label <= nccomps);img_color.at<cv::Vec3b>(y, x) = colors[label];}cv::imshow("Labeled map", img_color);waitKey(0);return 0;
}
结果:
分析:
1、看输出参数stats:Nx5矩阵(CV_32S),其中第1 2 6 个的面积小于200:
2、labels 标签图中:
4、通过findContours(),drawContours()函数计算连通量
在以前,常用的方法是”是先调用 cv::findContours() 函数(传入cv::RETR_CCOMP 标志),随后在得到的连通区域上循环调用 cv::drawContours() “
示例:
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;// 寻找最大的轮廓、按面积排序 函数
vector<vector<Point>> connection_sort(Mat src)
{vector<vector<Point>>contours;findContours(src.clone(),contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);//由于给大的区域着色会覆盖小的区域,所以首先进行排序操作//冒泡排序算法,由小到大排序vector<Point> vptmp;for(size_t i=1;i<contours.size();i++){for(size_t j=contours.size()-1;j>=i;j--){if (contourArea(contours[j]) < contourArea(contours[j-1])){vptmp = contours[j-1];contours[j-1] = contours[j];contours[j] = vptmp;}}}// 绘制 轮廓Mat draw = Mat::zeros(src.size(),CV_8UC3);for( int index = 0; index < contours.size(); index ++ ) // 绘制所有内外轮廓{Scalar color( rand()&255, rand()&255, rand()&255 );drawContours( draw, contours, index, color, 1, LINE_8 ); //CV_FILLED -1// 添加标记char text[100];sprintf(text, "%d", index+1);RotatedRect box = minAreaRect(contours.at(index)); // 获取中心putText(draw, text, box.center, FONT_HERSHEY_SIMPLEX, 0.5, cvScalar(0,0,255),1.5);}imshow("contours_sort_drawing",draw);return contours; // 返回排序后的 轮廓集
}// 寻找并绘制出彩色联通区域
vector<Point> FindBigestContour(Mat src,int& imax, int& imaxcontour)
{
// imax = 0; //代表最大轮廓的序号
// imaxcontour = -1; //代表最大轮廓的大小vector<vector<Point>>contours,contour_max;findContours(src.clone(),contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);for (int i=0;i<contours.size();i++) // 遍历每个轮廓点集{int itmp = contourArea(contours[i]);// 计算每个轮廓点集面积if (imaxcontour < itmp ){imax = i;imaxcontour = itmp;}}// 显示 最大轮廓Mat draw = Mat::zeros(src.size(),CV_8UC3);drawContours( draw, contours, imax, Scalar(0,255,0), 1, LINE_8 );imshow( "Contours_Max", draw );return contours[imax];
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Mat src =imread("F:/C++/2. OPENCV 3.1.0/TEST/test3.PNG",1);if(!src.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }imshow( "image", src );// 转灰度Mat src_gray;cvtColor( src, src_gray, CV_BGR2GRAY );src_gray = src_gray >1;imshow( "src_gray", src_gray );// 获得 新排序后轮廓、显示vector<vector<Point>> contours_sort;contours_sort = connection_sort(src_gray); // 绘制最大轮廓// 显示 最大轮廓int imax=0,imax_area=0;vector<vector<Point>>contour_max;contour_max.push_back( FindBigestContour(src_gray,imax,imax_area)); // 获得最大轮廓点集printf(" %d 轮廓为面积最大轮廓=%d (面积)",imax,imax_area);waitKey(0);return a.exec();
}
结果: