基于轮廓的连通域标记及应用(opencv及c++)

基于轮廓的连通域标记

本文在参考了https://www.cnblogs.com/ronny/p/img_aly_01.html
基于轮廓的连通域标记基础上,对其进行了一些应用并对其中内容进行了简单解释

相关函数及参数

连通域标记函数

int bwLabel(const Mat& imgBw, Mat & imgLabeled, vector<vector> &contours);//连通域标记
参数1:输入图像
参数2:输出一个imgLabeled图像,此图像为连通域标记图像
参数3:输出contours轮廓点集
返回值:返回连通域的总数(这里包含了背景,真实计算还需要-1)

显示不同连通域函数

void showColorLabels(Mat& image, int num_labels, Mat & labels);//以不同的颜色显示各个连通域
参数1:输入原始图像
参数2:输入连通域个数
参数3:输入标记后的图像

绘制轮廓函数

void MdrawContours(Mat & image, vector<vector> &contours);//绘制轮廓
参数1:输入原始图像
参数2:输入轮廓点集

具体实现效果

原始的图像这是个3通道的png图像:
原始图像

经过连通域标记后,再用不同颜色表示出来的图像:
在这里插入图片描述

轮廓在连通域标记的时候已经得到了,这里只是将其绘制出来,contours的点集如下所示,使用drawContours函数绘制出的轮廓和下方图像也是一样的。
Alt

具体代码实现(C++)

#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>

using namespace cv;
using namespace std;


int bwLabel(const Mat& imgBw, Mat & imgLabeled, vector<vector<Point>> &contours);//连通域标记
void showColorLabels(Mat& image, int num_labels, Mat & labels);//以不同的颜色显示各个连通域
void MdrawContours(Mat & image, vector<vector<Point>> &contours);//绘制轮廓

int main(){
	Mat image = imread("FigFindCountours2.bmp");//FigFindCountours2.bmp,3.PNG
	Mat gray, binary;

	//进行灰度化
	cvtColor(image, gray, COLOR_BGR2GRAY);
	//进行二值化
	threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	Mat labels;
	vector<vector<Point>> contours;

	int num_labels = bwLabel(binary, labels, contours);

	cout << "连通域数:" << (num_labels - 1) << endl;
	cout << "轮廓数:" << contours.size() << endl;

	showColorLabels(image, num_labels, labels);

	MdrawContours(image, contours);

	waitKey(0);
	return 0;
}

//基于轮廓的连通域标记
int bwLabel(const Mat& imgBw, Mat & labels, vector<vector<Point>> &contours)
{
	// 对图像周围扩充一格
	Mat imgClone = Mat(imgBw.rows + 1, imgBw.cols + 1, imgBw.type(), Scalar(0));
	imgBw.copyTo(imgClone(Rect(1, 1, imgBw.cols, imgBw.rows)));

	labels.create(imgClone.size(), imgClone.type());
	labels.setTo(Scalar::all(0));

	//vector<vector<Point>> contours
	vector<Vec4i> hierarchy;
	findContours(imgClone, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
		//	RETR_CCOMP模式是找到所有轮廓,层级只有两层(外轮廓和洞,洞里面再有轮廓按照外轮廓算,表现在层级关系上为:父轮廓无父轮廓,子轮廓无子轮廓),注意这里父轮廓只能指定一个子轮廓,但是多个子轮廓可以指定同一个父轮廓
		//	PS:RETR_CCOMP模式可以对打了lable的图片(比如通过connectcomponent函数或者watershed函数获得的结果)进行轮廓识别
		//CHAIN_APPROX_NONE是不做处理(得到的是原始点集)

	vector<int> contoursLabel(contours.size(), 0);
	int numlab;
	numlab = 1;
	// 标记外围轮廓
	for (vector<vector<Point>>::size_type i = 0; i < contours.size(); i++)
	{
		if (hierarchy[i][3] >= 0) // 有父轮廓
		{
			continue;
		}
		for (vector<Point>::size_type k = 0; k != contours[i].size(); k++)
		{
			labels.at<uchar>(contours[i][k].y, contours[i][k].x) = numlab;
		}
		contoursLabel[i] = numlab++;
	}
	// 标记内轮廓
	for (vector<vector<Point>>::size_type i = 0; i < contours.size(); i++)
	{
		if (hierarchy[i][3] < 0)
		{
			continue;
		}
		for (vector<Point>::size_type k = 0; k != contours[i].size(); k++)
		{
			labels.at<uchar>(contours[i][k].y, contours[i][k].x) = contoursLabel[hierarchy[i][3]];
		}
	}
	// 非轮廓像素的标记
	for (int i = 0; i < labels.rows; i++)
	{
		for (int j = 0; j < labels.cols; j++)
		{
			if (imgClone.at<uchar>(i, j) != 0 && labels.at<uchar>(i, j) == 0)
			{
				labels.at<uchar>(i, j) = labels.at<uchar>(i, j - 1);
			}
		}
	}
	labels = labels(Rect(1, 1, imgBw.cols, imgBw.rows)).clone(); // 将边界裁剪掉1像素

	return numlab;//返回连通域个数
}

//以不同的颜色显示各个连通域
void showColorLabels(Mat& image, int num_labels, Mat & labels){
	//以不同的颜色显示各个连通域
	cv::RNG rng(12345);
	vector<Vec3b> colors(num_labels);

	// background color
	colors[0] = Vec3b(0, 0, 0);

	// object color
	for (int i = 1; i < num_labels; i++) {
		colors[i] = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
	}

	// render result
	Mat dst = Mat::zeros(image.size(), image.type());
	int w = image.cols;
	int h = image.rows;
	for (int row = 0; row < h; row++) {
		for (int col = 0; col < w; col++) {
			int label = labels.at<uchar>(row, col);
			if (label == 0) continue;
			dst.at<Vec3b>(row, col) = colors[label];
		}
	}
	imshow("ccla-demo", dst);
	//imwrite("ccla_dst.png", dst);
}

//绘制轮廓模块
void MdrawContours( Mat & image, vector<vector<Point>> &contours){

	//绘制轮廓
	cv::Mat imageContours = Mat::zeros(image.size(), CV_8UC1);
	cv::Mat Contours = Mat::zeros(image.size(), CV_8UC1);
	//绘制,contours.size()代表轮廓个数。
	for (int i = 0; i<contours.size(); i++)
	{
		//contours[i]代表的是第i个轮廓,
		//contours[i].size()代表的是第i个轮廓上所有的像素点数。
		for (int j = 0; j<contours[i].size(); j++)
		{
			//绘制出contours向量内所有的像素点
			Point P = Point(contours[i][j].x, contours[i][j].y);
			Contours.at<uchar>(P) = 255;
		}
		//输出hierarchy向量内容
		//cout << "向量hierarchy第" << i << " 个元素内容:"
		//	<< hierarchy[i] << endl;
		//绘制轮廓
		drawContours(imageContours, contours, i, Scalar(255), 1, 8);
	}
	imshow("Point of Contours", Contours);	//显示所有轮廓点集
	imshow("Contour Image", imageContours);	//显示轮廓
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吾名招财

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值