图像处理(五):八邻域边缘跟踪与区域生长算法

通常在进行边缘检测之后,需要通过边缘跟踪来将离散的边缘串接起来,常使用的方法为:边缘跟踪和区域生长两种方法。边缘跟踪又分为八邻域和四邻域两种,具体原理可以参考残影、的博客。

实现步骤:

1、灰度化并进行Canny边缘检测

2、按照预先设定的跟踪方向(顺时针)进行边缘跟踪

3、每次跟踪的终止条件为:8邻域都不存在轮廓

这里需要理解的点:

代码中为什么更新当前方向时,需要curr_d -= 2,原因如下:

一次八领域搜索是以当前点pt1为中心,顺时针(或逆时针)遍历八个邻近像素以后,定位到下一个像素点pt2,下一次遍历时,就以pt2为中心,再次遍历八个邻近像素,第二次遍历过程与第一次遍历过程存在重叠部分,这里 -2的目的就是为了避免与第一次遍历重复,减少无用功,至于为什么是2,你画个图自己推一推就知道了。


这里将残影博客中的图拿过来贴一下,帮助大家理解:

这里pt1为红色的3,第一次遍历时,找到7,因此,pt2为黑色的7。第一次遍历找到黑色点7意味着黑色的0-6位置不存在边缘像素,而黑色的0、6位置及pt1位置与红色的2、3、4位置是重叠的,这些位置不需要再次去遍历,而当前方向为红色7,将7-2=5,从5开始遍历,就可以避免再次遍历到红色2,3,4位置。


代码如下:

 

#include <opencv2/opencv.hpp>
#include <vector>
#include <time.h>

using namespace cv;
using namespace std;

// 8 neighbors
const Point directions[8] = { { 0, 1 }, {1,1}, { 1, 0 }, { 1, -1 }, { 0, -1 },  { -1, -1 }, { -1, 0 },{ -1, 1 } };

int main()
{
	// 生成随机数
	RNG rng(time(0));

	Mat src = imread("1.jpg");
	Mat gray;
	cvtColor(src, gray, CV_BGR2GRAY);

	Mat Edge;

	// Canny边缘检测
	Canny(gray, Edge, 50, 100);

	
	vector<Point> edge_t;
	vector<vector<Point>> edges;

	// 边缘跟踪
	int i, j, counts = 0, curr_d = 0;
	for (i = 1; i < Edge.rows - 1; i++)
		for (j = 1; j < Edge.cols - 1; j++)
		{
			// 起始点及当前点
			//Point s_pt = Point(i, j);
			Point b_pt = Point(i, j);
			Point c_pt = Point(i, j);

			// 如果当前点为前景点
			if (255 == Edge.at<uchar>(c_pt.x, c_pt.y))
			{
				edge_t.clear();
				bool tra_flag = false;
				// 存入
				edge_t.push_back(c_pt);
				Edge.at<uchar>(c_pt.x, c_pt.y) = 0;    // 用过的点直接给设置为0

				// 进行跟踪
				while (!tra_flag)
				{			
					// 循环八次
					for (counts = 0; counts < 8; counts++)
					{
						// 防止索引出界
						if (curr_d >= 8)
						{
							curr_d -= 8;
						}
						if (curr_d < 0)
						{
							curr_d += 8;
						}

						// 当前点坐标
						// 跟踪的过程,应该是个连续的过程,需要不停的更新搜索的root点
						c_pt = Point(b_pt.x + directions[curr_d].x, b_pt.y + directions[curr_d].y);

						// 边界判断
						if ((c_pt.x > 0) && (c_pt.x < Edge.cols - 1) &&
							(c_pt.y > 0) && (c_pt.y < Edge.rows - 1))
						{
							// 如果存在边缘
							if (255 == Edge.at<uchar>(c_pt.x, c_pt.y))
							{
								curr_d -= 2;   // 更新当前方向
								edge_t.push_back(c_pt);
								Edge.at<uchar>(c_pt.x, c_pt.y) = 0;

								// 更新b_pt:跟踪的root点
								b_pt.x = c_pt.x;
								b_pt.y = c_pt.y;

								//cout << c_pt.x << " " << c_pt.y << endl;

								break;   // 跳出for循环
							}
						}
						curr_d++;
					}   // end for
					// 跟踪的终止条件:如果8邻域都不存在边缘
					if (8 == counts )
					{
						// 清零
						curr_d = 0;
						tra_flag = true;
						edges.push_back(edge_t);

						break;
					}

				}  // end if
			}  // end while
			
		}

	// 显示一下
	Mat trace_edge = Mat::zeros(Edge.rows, Edge.cols, CV_8UC1);
	Mat trace_edge_color;
	cvtColor(trace_edge, trace_edge_color, CV_GRAY2BGR);
	for (i = 0; i < edges.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));

		//cout << edges[i].size() << endl;
		// 过滤掉较小的边缘
		if (edges[i].size() > 5)
		{
			for (j = 0; j < edges[i].size(); j++)
			{
				trace_edge_color.at<Vec3b>(edges[i][j].x, edges[i][j].y)[0] = color[0];
				trace_edge_color.at<Vec3b>(edges[i][j].x, edges[i][j].y)[1] = color[1];
				trace_edge_color.at<Vec3b>(edges[i][j].x, edges[i][j].y)[2] = color[2];
			}
		}
		
	}
		


	imshow("edge", trace_edge_color);


	waitKey();

	return 0;

}


效果如下:

 

至于区域生长算法可以直接参考博文点击打开链接,代码写的很棒!

该勤快的时候不能懒,好吧,还是自己实现了一遍,代码如下:

 

#include <opencv2/opencv.hpp>
#include <vector>
#include <time.h>

using namespace cv;
using namespace std;

// 8邻域
const Point neighbors[8] = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, 
                             { 0, -1 }, { -1, -1 }, { -1, 0 }, {-1, 1} };

int main()
{
	// 生成随机数  
	RNG rng(time(0));

	Mat src = imread("1.jpg");
	Mat gray;
	cvtColor(src, gray, CV_BGR2GRAY);

	Mat edges;
	Canny(gray, edges, 30, 100);

	vector<Point> seeds;
	vector<Point> contour;
	vector<vector<Point>> contours;

	int i, j, k;
	for (i = 0; i < edges.rows; i++)
		for (j = 0; j < edges.cols; j++)
		{
			Point c_pt = Point(i, j);

			//如果当前点为轮廓点
			if (edges.at<uchar>(c_pt.x, c_pt.y) == 255)
			{

				contour.clear();
				// 当前点清零
				edges.at<uchar>(c_pt.x, c_pt.y) = 0;

				// 存入种子点及轮廓
				seeds.push_back(c_pt);
				contour.push_back(c_pt);

				// 区域生长
				while (seeds.size() > 0)
				{
					// 遍历8邻域
					for (k = 0; k < 8; k++)
					{	
						// 更新当前点坐标
						c_pt.x = seeds[0].x + neighbors[k].x;
						c_pt.y = seeds[0].y + neighbors[k].y;

						// 边界界定
						if ((c_pt.x >= 0)  && (c_pt.x <= edges.rows - 1) &&
							(c_pt.y >= 0) && (c_pt.y <= edges.cols - 1))
						{
							if (edges.at<uchar>(c_pt.x, c_pt.y) == 255)
							{
								// 当前点清零
								edges.at<uchar>(c_pt.x, c_pt.y) = 0;

								// 存入种子点及轮廓
								seeds.push_back(c_pt);
								contour.push_back(c_pt);
							}// end if		
						}										
					} // end for

					// 删除第一个元素
					seeds.erase(seeds.begin());

				}// end while

				contours.push_back(contour);

			}// end if
		}


	// 显示一下  
	Mat trace_edge = Mat::zeros(edges.rows, edges.cols, CV_8UC1);
	Mat trace_edge_color;
	cvtColor(trace_edge, trace_edge_color, CV_GRAY2BGR);
	for (i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));

		//cout << edges[i].size() << endl;  
		// 过滤掉较小的边缘  
		if (contours[i].size() > 5)
		{
			for (j = 0; j < contours[i].size(); j++)
			{
				trace_edge_color.at<Vec3b>(contours[i][j].x, contours[i][j].y)[0] = color[0];
				trace_edge_color.at<Vec3b>(contours[i][j].x, contours[i][j].y)[1] = color[1];
				trace_edge_color.at<Vec3b>(contours[i][j].x, contours[i][j].y)[2] = color[2];
			}
		}

	}



	imshow("edge", trace_edge_color);


	waitKey();

	return 0;




}


效果如下:

 

参考资料:

[1] https://blog.csdn.net/liumangmao1314/article/details/53844570

  • 63
    点赞
  • 624
    收藏
    觉得还不错? 一键收藏
  • 30
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值