c++实现扫描线种子填充算法

今天来介绍一种利用堆栈实现的填充算法,这种算法相比于直接使用递归实现的填充算法(如内点表示的四连通种子填充算法)来说,它需要的堆栈大小不需要那么庞大,下面是在MFC中实现方法。

一.基本思想

在任意不间断区间(一条扫描线上的一组相邻像素)中,只取一个种子像素,填充当前扫描线上的该段区间,然后确定与这一段相邻的上下两条扫描线位于区域内的区段,并依次把他们保存起来,反复进行这个过程,直到所保存的每个区段都填充完毕

二.具体算法

  1. 初始化:堆栈置空,将种子点入栈。
  2. 出栈:若栈空则结束,否则栈顶元素(x,y)加粗样式出栈,并以y值作为当前扫描线号。
  3. 填充并确定种子点所在区段:从种子(x,y)出发,沿当前扫描线向左、向右两个方向逐个像素填充,直到遇到边界像素为止。分别标记区段的左、右端点坐标为xl和xr。
  4. 确定新的种子点:在区间[xl,xr]中检查与当前扫描线y相邻的上下两条扫描线上的像素。若存在非边界、未填充的像素,则把每一区间的最右像素作为种子点压入堆栈,返回第2步。否则直接返回第2步。

其中确定新的种子点的具体算法如下(以当前扫描线y上传为例,下侧也类似)

横坐标i从 xl+1开始,当小于xr时进行下面的循环:

  1. 当以(i,y+1)为坐标的像素点颜色不为边界颜色且也未被填充时,即说明找到一个未填充区域,此时要寻找区域最右端坐标。
  2. 令j=i+1
  3. 以(j,y+1)为坐标的像素点颜色不是边界颜色且未被填充时,循环执行j++,否则退出循环(此时说明到达该区间的最右端)
  4. 令i=j-1(i下一次寻找的坐标从当前区间的最右像素点开始)
  5. i++
  6. 返回步骤1

实现代码

//在当前扫描线上下两端寻找新的种子
//stack为系统堆栈
void FindNewSeed(stack<CPoint>& s, int left, int right, int y, COLORREF bcolor, COLORREF ncolor)
{
	for (int i = left + 1; i < right; i++)
	{
		if (GetPixel(i, y) != bcolor && GetPixel(i, y) != ncolor)
		{
			int j = i + 1;
			while (GetPixel(j, y) != bcolor)
				j++;
			i = j--;
			s.push(CPoint(j, y));
		}
	}
}
//扫描线种子填充算法
void ScanLineFlood(int x, int y, COLORREF bcolor, COLORREF ncolor)
{
	stack<CPoint> s;
	CPoint p;
	int left, right;
	s.push(CPoint(x, y));
	while (!s.empty())
	{
		//栈顶元素出栈
		p = s.top();
		s.pop();

		//向左填充
		for (left = p.x; GetPixel(left, p.y) != bcolor; left--)
			SetPixel(left, p.y, ncolor);
		//向右填充
		for (right = p.x + 1; GetPixel(right, p.y) != bcolor; right++)
			SetPixel(right, p.y, ncolor);

		//在当前行的下一行寻找确定新的种子点
		FindNewSeed(s, left, right, p.y-1, bcolor, ncolor);
		//在当前行的上一行寻找确定新的种子点
		FindNewSeed(s,left,right,p.y+1,bcolor,ncolor);
	}
}

若有不对的地方请指正,不胜感激!!!

发布了16 篇原创文章 · 获赞 18 · 访问量 4131

猜你喜欢

转载自blog.csdn.net/qq_42103091/article/details/102597112