1、概念
连通组件指在图像上通过四邻域或八邻域法,连接起来的像素值大于某一阈值的区域(这些像素点被称为前景像素),而小于阈值的区域被称为背景。如下图的4个连通组件。
四邻域、八邻域:
2、常用算法
a)基于像素的扫描方法 b)基于块的扫描方法
说明:与卷积类似,利用上述模板在图像上平移扫描前景像素,则扫描区域内其余的前景像素与当前像素连通。
c)两步法扫描
1、同样采用像素模板对图像进行扫描,给予其标记,并获得等价队列(连通的不同标记队列)。
2、将一个等价队列中的标记全部置为最小标记值,如上{1,3,5}队列全置为1。
d)opencv中的连通组件扫描算法——BBDT
true判定:
将上图左边的块模板分为右边所示的大块分区P、Q、R、S、X。要获取块X与块P连通的结论(),则需满足,小像素点h和o均为前景像素。
3、代码示例:
3.1、 API:
int cv::connectedComponents ( InputArray image,
OutputArray labels,
int connectivity,
int ltype,
int ccltype
)
int cv::connectedComponentsWithStats( InputArray image,
OutputArray labels,
OutputArray stats,
OutputArray centroids,
int connectivity,
int ltype,
int ccltype
)
labels——带标记的与输入图像同大小类型的图像。
stats——每个连通组件的(包括背景)的外接矩形信息,具体如下:
centroids——每个连通组件的(包括背景)的中心坐标信息,数据类型为CV_64F
connectivity——邻域连通方法,4/8邻域。
ltype——输出label图像类型,目前仅有CV_32S 和CV_16U类型可选用。
ccltype ——连通组件扫描方法,可选用如下方法:
connectedComponents 示例:
void QuickDemo::scan_neighbor(Mat& image)
{
//高斯模糊
GaussianBlur(image, image, Size(3, 3), 0);
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
Mat binary;
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
namedWindow("adaptiveThreshold", WINDOW_FREERATIO);
imshow("adaptiveThreshold", binary);
Mat labels = Mat::zeros(binary.size(), binary.type());
int num_label = connectedComponents(binary, labels, 8, CV_32S, CCL_DEFAULT);
//显示labels
//1、生成颜色数组
RNG rng(12345);
vector<Vec3b> colorTable(num_label);//定义一个num_label维度的空数组
colorTable[0] = Vec3b(0, 0, 0);//将背景颜色置为黑色
for (int i = 1; i < num_label; ++i) {
//对其余标签赋予颜色数据
colorTable[i] = Vec3b(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
}
Mat result = Mat::zeros(image.size(), image.type());
//2、通过循环对不同的标签填入随机生成的颜色
int h = image.rows, w = image.cols;
for (int row = 0; row < h; ++row) {
for (int col= 0; col < w; ++col) {
int label = labels.at<int>(row, col);//将labels图像上对应像素坐标上的标记值取出
result.at<Vec3b>(row, col) = colorTable[label];//将result图像上对应像素坐标赋予颜色
}
}
//在图像上显示有多少个字体
putText(result, format("number of %d", num_label - 1), Point(50, 50), FONT_HERSHEY_PLAIN, 3, Scalar(0, 50, 70), 1);
namedWindow("labels", WINDOW_FREERATIO);
imshow("labels", result);
}
connectedComponentsWithStats示例:
void QuickDemo::scan_neighbor(Mat& image)
{
//高斯模糊
GaussianBlur(image, image, Size(3, 3), 0);
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
Mat binary;
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
namedWindow("adaptiveThreshold", WINDOW_FREERATIO);
imshow("adaptiveThreshold", binary);
Mat labels = Mat::zeros(binary.size(), binary.type());
Mat stats, centroids;
int num_label = connectedComponentsWithStats(binary, labels,stats,centroids, 8, CV_32S, CCL_DEFAULT);
//显示labels
//1、生成颜色数组
RNG rng(12345);
vector<Vec3b> colorTable(num_label);//定义一个num_label维度的空数组
colorTable[0] = Vec3b(0, 0, 0);//将背景颜色置为黑色
for (int i = 1; i < num_label; ++i) {
//对其余标签赋予颜色数据
colorTable[i] = Vec3b(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
}
Mat result = Mat::zeros(image.size(), image.type());
//2、通过循环对不同的标签填入随机生成的颜色
int h = image.rows, w = image.cols;
for (int row = 0; row < h; ++row) {
for (int col = 0; col < w; ++col) {
int label = labels.at<int>(row, col);//将labels图像上对应像素坐标上的标记值取出
result.at<Vec3b>(row, col) = colorTable[label];//将result图像上对应像素坐标赋予颜色
}
}
//绘制外接矩形和中心点圆
for (int i = 1; i < num_label; ++i) {
//先获取连接组件的中心坐标
int cx = centroids.at<double>(i, 0);
int cy = centroids.at<double>(i, 1);
int x = stats.at<int>(i, CC_STAT_LEFT);//矩形左边界与整个图像左边的距离
int y = stats.at<int>(i, CC_STAT_TOP);//矩形上边界与整个图像上边的距离
int width = stats.at<int>(i, CC_STAT_WIDTH);//矩形宽度
int height = stats.at<int>(i, CC_STAT_HEIGHT);//矩形高度
int area = stats.at<int>(i, CC_STAT_AREA);//矩形面积
//绘制
circle(result, Point(cx, cy), 3, Scalar(0, 0, 255), 2, 8);
// 外接矩形
Rect box(x, y, width, height);
rectangle(result, box, Scalar(0, 255, 0), 2, 8);
putText(result, format("%d", area), Point(x, y), FONT_HERSHEY_PLAIN, 3, Scalar(0, 50, 70), 1);
}
namedWindow("labels", WINDOW_FREERATIO);
imshow("labels", result);
}