连通域标记算法实现

这篇博客介绍了如何使用OpenCV进行连通域标记,包括深度优先搜索(DFS)和栈实现两种方法。通过遍历图像中的像素点,找到相同颜色的连通组件并进行标记,有效地应用于二值图像的分割和处理。代码示例详细展示了算法的实现过程。
摘要由CSDN通过智能技术生成

原理:
1.从左到右,从上到下遍历每一个像素点,然后判断该像素的值是否在集合V中(这里是一张二值图,我们假设黑色的值为0)。
2.如果遇到一个值为0的点,说明在该点附近可能存在与该点相连通的像素点,即可能存在连通域。那么我们就暂时停止之前的遍历,开始以该点为种子点,查找该点附近的邻域中是否存在与其连通的像素点,若与该点连通,则将其存到一个堆栈中,并将该点的标签赋值为与种子点相同的标签,并对访问过的像素点置一个表示已访问的标志,以避免后面对其重复访问。
3.接下来就从堆栈中取出点,查看该点的四邻域,依次把和该点连通的点压入到堆栈中。
4.继续从堆栈中取出点,并遍历该点的四邻域,如此循环,直到堆栈变为空,则说明已经遍历完了所有的与初始点相连通的连通成分。标签加1,以便对后面的连通成分标签和该连通成分标签加以区分。
5.接下来就可以继续之前的从左到右、从上到下的遍历工作了。访问过的点不再进行对其访问,直接跳过。
6.当访问到一个新的在集合V内的像素点时,则又以该点为种子点进行四邻域的遍历循环,并标记上相应的标签和访问标志,直到标记完整个连通成分,则标签加1,继续后面的遍历。如此循环往复。直到访问完最后一个像素点,我们的连通成分也标记完了。

实现:
递归方法(容易栈溢出)

/**
 * @description:	深度优先搜索
 * @param src	输入图像
 * @param dst	输出图像
 * @param i		行号
 * @param j		列号
 * @param num	区域序号
 */
void dfs(cv::Mat& src, cv::Mat& dst, int i, int j, int num)
{
	if (i >= 0 && i < src.rows && j >= 0 && j < src.cols && src.at<uchar>(i, j) && dst.at<cv::Vec3b>(i, j) == cv::Vec3b(0, 0, 0))
	{
		dst.at<cv::Vec3b>(i, j) = Vec3b(num * 80, 255 - num * 80, 0);
		dfs(src, dst, i - 1, j, num);
		dfs(src, dst, i + 1, j, num);
		dfs(src, dst, i, j - 1, num);
		dfs(src, dst, i, j + 1, num);
	}
}

/**
 * @description:	连通域标记
 * @param src		输入图像
 * @param dst		输出图像
 */
void getConnectedDomain(cv::Mat& src, cv::Mat &dst)
{
	dst = cv::Mat::zeros(src.size(), cv::CV_8UC3);
	int num = 0;

	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			if (src.at<uchar>(i, j)!=0 && dst.at<Vec3b>(i, j) == Vec3b(0, 0, 0))
			{
				dfs(src, dst, i, j, num);
				++num;
			}
		}
	}
}

用栈实现

/**
 * @description:	连通域标记
 * @param src		输入图像
 * @param dst		输出图像
 */
void getConnectedDomain(cv::Mat& src, cv::Mat &dst)
{
	dst = cv::Mat::zeros(src.size(), cv::CV_8UC3);
	int num = 0;

	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			int dir[4][2] = { { -1, 0 },{ 1, 0 },{ 0, -1 },{ 0, 1 }};
			std::vector<cv::Point> pts;
			cv::Point pt,p;

			if (src.at<uchar>(i, j)!=0 && dst.at<cv::Vec3b>(i, j) == cv::Vec3b(0, 0, 0))
			{
				pts.push_back(cv::Point(j, i));
				dst.at<cv::Vec3b>(i, j) = cv::Vec3b(num * 80, 255 - num * 80, 0);

				while (!pts.empty())
				{
					pt = pts.back();
					pts.pop_back();

					for (int k = 0; k < 4; ++k)
					{
						p.x = pt.x + dir[k][0];
						p.y = pt.y + dir[k][1];

						if (p.x < 0 || p.y < 0 || p.x >= src.cols || p.y >= src.rows)	continue;
						if (src.at<uchar>(p.y,p.x)!=0 && dst.at<cv::Vec3b>(p.y, p.x) == cv::Vec3b(0, 0, 0))
						{							
							dst.at<cv::Vec3b>(p.y, p.x) = cv::Vec3b(num * 80, 255 - num * 80, 0);
							pts.push_back(p);
						}
					}
				}
				++num;
			}
		}
	}
}

代码传送门:https://github.com/taifyang/OpenCV-algorithm

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

给算法爸爸上香

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

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

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

打赏作者

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

抵扣说明:

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

余额充值