OpenCV16-图像连通域分析
- 1.图像连通域分析
- 2.connectedComponents
- 3.connectedComponentsWithStatus
1.图像连通域分析
连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域。连通域分析是指在图像中寻找彼此互相独立的连通域并将其标记出来。
4邻域与8邻域的概念:点 P 0 ( x , y ) P_0(x,y) P0(x,y) 的4邻域为其上下左右4个像素点,其8邻域为上下左右再加上对角线方向的4个点。
根据两个像素相邻定义方式不同,得到的连通区域也不相同,因此,在分析连通域的同时,一定要声明是在哪种邻域条件下分析得到的结果。
2.connectedComponents
OpenCV提供了用于提取图像中不同连通域的 connectedComponent()
函数:
int connectedComponents(InputArray image, OutputArray labels, // 标记不同连通域后的输出图像int connectivity, // 标记连通域时使用的邻域种类,4表示4邻域int ltype, // 输出图像数据类型:CV_32S、CV_16Uint ccltype // 标记连通域时使用的算法类型
);int connectedComponents(InputArray image, OutputArray labels,int connectivity = 8, int ltype = CV_32S
);enum ConnectedComponentsAlgorithmsTypes {CCL_DEFAULT = -1, // 8邻域使用SAUF、4邻域使用SAUFCCL_WU = 0, // 8邻域使用BBDT、4邻域使用SAUFCCL_GRANA = 1, // 8邻域使用BBDT、4邻域使用SAUFCCL_BOLELLI = 2, CCL_SAUF = 3, CCL_BBDT = 4, CCL_SPAGHETTI = 5,
};
该函数用于计算二值图像中连通域的个数,并在图像中不同的连通域用不同的数字标签标记,其中标签0表示图像中的背景色。函数返回图像中连通域的数目。
示例代码:
3.connectedComponentsWithStatus
虽然 connectedComponents()
函数可以实现图像中多个连通域的统计,但是只能通过标签将图像中的不同连通域分开,无法得到更多的统计信息。有时,我们希望得到每个连通域中心位置或者在图像中标记出连通域所在的矩形区域, connectedComponents
便无法完成这个任务。
OpenCV中提供了 connectedComponentsWithStatus()
函数用于在标记出图像中不同连通域的同时统计连通域的位置、面积的信息:
int connectedComponentsWithStats(InputArray image, OutputArray labels, // 标记不同连通域后的输出图像OutputArray stats, // 含有不同连通域统计信息的矩阵,CV_32S。矩阵第i行是标签为i的连通域的统计信息。OutputArray centroids, // 每个连通域质心坐标,CV_64Fint connectivity, // 邻域种类int ltype, // 输出图像数据类型int ccltype // 标记连通域使用算法标志,同connectedComponents函数参数
);int connectedComponentsWithStats(InputArray image, OutputArray labels,OutputArray stats, OutputArray centroids,int connectivity = 8, int ltype = CV_32S
);
第三个参数为每个连通域统计信息矩阵:
enum ConnectedComponentsTypes {CC_STAT_LEFT = 0, // 连通域内最左侧像素的x坐标,它是水平方向上的包含连通域边界框的开始CC_STAT_TOP = 1, // 连通域内最上方像素的y坐标,它是垂直方向上的包含连通域边界框的开始CC_STAT_WIDTH = 2, // 宽CC_STAT_HEIGHT = 3, // 高CC_STAT_AREA = 4, // 连通域面积
#ifndef CV_DOXYGENCC_STAT_MAX = 5 // 统计信息种类数目,无实际含义
#endif
};
使用:stats.at<int>(i, CC_STAT_WIDTH)
。
第四个参数为每个连通域的质心坐标,使用:centroids.at<double>(i, 0)
取得x坐标,centroids.at<double>(i, 1)
取得y坐标。
示例代码:
#include <opencv2\opencv.hpp>
#include <opencv2/core/utils/logger.hpp> // debug no logusing namespace cv;
using namespace std;int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);//对图像进行距离变换Mat img = imread("rice.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("原图", img);Mat rice, riceBW;//将图像转成二值图像,用于统计连通域cvtColor(img, rice, COLOR_BGR2GRAY);threshold(rice, riceBW, 50, 255, THRESH_BINARY);//生成随机颜色,用于区分不同连通域RNG rng(10086);Mat out, stats, centroids;//统计图像中连通域的个数int number = connectedComponentsWithStats(riceBW, out, stats, centroids, 8, CV_16U);vector<Vec3b> colors;for (int i = 0; i < number; i++){//使用均匀分布的随机数确定颜色Vec3b vec3 = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));colors.push_back(vec3);}//以不同颜色标记出不同的连通域Mat result = Mat::zeros(rice.size(), img.type());int w = result.cols;int h = result.rows;for (int i = 1; i < number; i++){// 中心位置int center_x = centroids.at<double>(i, 0);int center_y = centroids.at<double>(i, 1);//矩形边框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);int area = stats.at<int>(i, CC_STAT_AREA);// 中心位置绘制circle(img, Point(center_x, center_y), 2, Scalar(0, 255, 0), 2, 8, 0);// 外接矩形Rect rect(x, y, w, h);rectangle(img, rect, colors[i], 1, 8, 0);putText(img, format("%d", i), Point(center_x, center_y),FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1);cout << "number: " << i << "\tarea: " << area << endl;}//显示结果imshow("标记后的图像", img);waitKey(0);return 0;
}