`
ldb19890624
  • 浏览: 243682 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

算法系列之十二:多边形区域填充算法--扫描线种子填充算法

 
阅读更多

1.3扫描线种子填充算法

1.1和1.2节介绍的两种种子填充算法的优点是非常简单,缺点是使用了递归算法,这不但需要大量栈空间来存储相邻的点,而且效率不高。为了减少算法中的递归调用,节省栈空间的使用,人们提出了很多改进算法,其中一种就是扫描线种子填充算法。扫描线种子填充算法不再采用递归的方式处理“4-联通”和“8-联通”的相邻点,而是通过沿水平扫描线填充像素段,一段一段地来处理“4-联通”和“8-联通”的相邻点。这样算法处理过程中就只需要将每个水平像素段的起始点位置压入一个特殊的栈,而不需要象递归算法那样将当前位置周围尚未处理的所有相邻点都压入堆栈,从而可以节省堆栈空间。应该说,扫描线填充算法只是一种避免递归,提高效率的思想,前面提到的注入填充算法和边界填充算法都可以改进成扫描线填充算法,下面介绍的就是结合了边界填充算法的扫描线种子填充算法。

扫描线种子填充算法的基本过程如下:当给定种子点(x, y)时,首先分别向左和向右两个方向填充种子点所在扫描线上的位于给定区域的一个区段,同时记下这个区段的范围[xLeft, xRight],然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。反复这个过程,直到填充结束。

扫描线种子填充算法可由下列四个步骤实现:

(1) 初始化一个空的栈用于存放种子点,将种子点(x, y)入栈;

(2) 判断栈是否为空,如果栈为空则结束算法,否则取出栈顶元素作为当前扫描线的种子点(x, y),y是当前的扫描线;

(3) 从种子点(x, y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xLeft和xRight;

(4) 分别检查与当前扫描线相邻的y - 1和y + 1两条扫描线在区间[xLeft, xRight]中的像素,从xLeft开始向xRight方向搜索,若存在非边界且未填充的像素点,则找出这些相邻的像素点中最右边的一个,并将其作为种子点压入栈中,然后返回第(2)步;

这个算法中最关键的是第(4)步,就是从当前扫描线的上一条扫描线和下一条扫描线中寻找新的种子点。这里比较难理解的一点就是为什么只是检查新扫描线上区间[xLeft, xRight]中的像素?如果新扫描线的实际范围比这个区间大(而且不连续)怎么处理?我查了很多计算机图形学的书籍和论文,好像都没有对此做过特殊说明,这使得很多人在学习这门课程时对此有挥之不去的疑惑。本着“毁人”不倦的思想,本文就罗嗦解释一下,希望能解除大家的疑惑。

如果新扫描线上实际点的区间比当前扫描线的[xLeft, xRight]区间大,而且是连续的情况下,算法的第(3)步就处理了这种情况。如图(4)所示:

图(4) 新扫描线区间增大且连续的情况

假设当前处理的扫描线是黄色点所在的第7行,则经过第3步处理后可以得到一个区间[6,10]。然后第4步操作,从相邻的第6行和第8行两条扫描线的第6列开始向右搜索,确定红色的两个点分别是第6行和第8行的种子点,于是按照顺序将(6, 10)和(8, 10)两个种子点入栈。接下来的循环会处理(8, 10)这个种子点,根据算法第3步说明,会从(8, 10)开始向左和向右填充,由于中间没有边界点,因此填充会直到遇到边界为止,所以尽管第8行实际区域比第7行的区间[6,10]大,但是仍然得到了正确的填充。

如果新扫描线上实际点的区间比当前扫描线的[xLeft, xRight]区间大,而且中间有边界点的情况,算法又是怎么处理呢?算法描述中虽然没有明确对这种情况的处理方法,但是第4步确定上、下相邻扫描线的种子点的方法,以及靠右取点的原则,实际上暗含了从相邻扫描线绕过障碍点的方法。下面以图(5)为例说明:

图(5) 新扫描线区间增大且不连续的情况

算法第3步处理完第5行后,确定了区间[7, 9],相邻的第4行虽然实际范围比区间[7, 9]大,但是因为被(4, 6)这个边界点阻碍,使得在确定种子点(4, 9)后向左填充只能填充右边的第7列到第10列之间的区域,而左边的第3列到第5列之间的区域没有填充。虽然作为第5行的相邻行,第一次对第4行的扫描根据靠右原则只确定了(4, 9)一个种子点。但是对第3行处理完后,第4行的左边部分作为第3行下边的相邻行,再次得到扫描的机会。第3行的区间是[3, 9],向左跨过了第6列这个障碍点,第2次扫描第4行的时候就从第3列开始,向右找,可以确定种子点(4, 5)。这样第4行就有了两个种子点,就可以被完整地填充了。

由此可见,对于有障碍点的行,通过相邻边的关系,可以跨越障碍点,通过多次扫描得到完整的填充,算法已经隐含了对这种情况的处理。根据本节总结的四个步骤,扫描线种子填充算法的实现如下:

263void ScanLineSeedFill(int x, int y, int new_color, int boundary_color)

264{

265 std::stack<Point> stk;

266

267 stk.push(Point(x, y)); //第1步,种子点入站

268 while(!stk.empty())

269 {

270 Point seed = stk.top(); //第2步,取当前种子点

271 stk.pop();

272

273 //第3步,向左右填充

274 int count = FillLineRight(seed.x, seed.y, new_color, boundary_color);//向'cf?右'd3?填'cc?充'b3?

275 int xRight = seed.x + count - 1;

276 count = FillLineLeft(seed.x - 1, seed.y, new_color, boundary_color);//向'cf?左'd7?填'cc?充'b3?

277 int xLeft = seed.x - count;

278

279 //第4步,处理相邻两条扫描线

280 SearchLineNewSeed(stk, xLeft, xRight, seed.y - 1, new_color, boundary_color);

281 SearchLineNewSeed(stk, xLeft, xRight, seed.y + 1, new_color, boundary_color);

282 }

283}

FillLineRight()和FillLineLeft()两个函数就是从种子点分别向右和向左填充颜色,直到遇到边界点,同时返回填充的点的个数。这两个函数返回填充点的个数是为了正确调整当前种子点所在的扫描线的区间[xLeft, xRight]。SearchLineNewSeed()函数完成算法第4步所描述的操作,就是在新扫描线上寻找种子点,并将种子点入栈,新扫描线的区间是xLeft和xRight参数确定的:

234void SearchLineNewSeed(std::stack<Point>& stk, int xLeft, int xRight,

235 int y, int new_color, int boundary_color)

236{

237 int xt = xLeft;

238 bool findNewSeed = false;

239

240 while(xt <= xRight)

241 {

242 findNewSeed = false;

243 while(IsPixelValid(xt, y, new_color, boundary_color) && (xt < xRight))

244 {

245 findNewSeed = true;

246 xt++;

247 }

248 if(findNewSeed)

249 {

250 if(IsPixelValid(xt, y, new_color, boundary_color) && (xt == xRight))

251 stk.push(Point(xt, y));

252 else

253 stk.push(Point(xt - 1, y));

254 }

255

256 /*向右跳过内部的无效点(处理区间右端有障碍点的情况)*/

257 int xspan = SkipInvalidInLine(xt, y, xRight, new_color, boundary_color);

258 xt += (xspan == 0) ? 1 : xspan;

259 /*处理特殊情况,以退出while(x<=xright)循环*/

260 }

261}

最外层的while循环是为了保证区间[xLeft, xRight]右端被障碍点分隔成多段的情况能够得到正确处理,通过外层while循环,可以确保为每一段都找到一个种子点(对于障碍点在区间左端的情况,请参考图(5)所示实例的解释,是隐含在算法中完成的)。内层的while循环只是为了找到每一段最右端的一个可填充点作为种子点。SkipInvalidInLine()函数的作用就是跳过区间内的障碍点,确定下一个分隔段的开始位置。循环内的最后一行代码有点奇怪,其实只是用了一个小“诡计”,确保在遇到真正的边界点时循环能够正确退出。这不是一个值得称道的做法,实现此类软件控制有更好的方法,本文这样做的目的只是为了使代码简短一些,让读者把注意力集中在算法处理逻辑上,而不是冗杂难懂的循环控制条件上。

算法的实现其实就在ScanLineSeedFill()和SearchLineNewSeed()两个函数中,神秘的扫描线种子填充算法也并不复杂,对吧?至此,种子填充算法的几种常见算法都已经介绍完毕,接下来将介绍两种适合矢量图形区域填充的填充算法,分别是扫描线算法和边标志填充算法,注意适合矢量图形的扫描线填充算法有时又被称为“有序边表法”,和扫描线种子填充算法是有区别的。

<下一篇:扫描线算法(有序边表法)>

分享到:
评论

相关推荐

    种子填充算法,扫描线填充算法,带报告

    用种子填充算法和扫描线填充算法等任意两种算法实现指定多边形的区域填充。 实验步骤 1. 复习有关算法,明确实验目的和要求; 2. 依据算法思想,绘制程序流程图(指定填充多边形); 3. 设计程序界面,要求操作方便...

    多边形填充扫描线算法

    扫描线算法是一种将多边形分解为一系列水平线段的方法,然后根据这些线段填充像素。它的工作流程大致分为以下几个步骤: 1. **排序顶点**:对多边形的顶点按y坐标进行排序,从最小到最大。 2. **生成边表**:基于...

    java多边形填充扫描线种子算法

    使用java编程 多边形画法:先选择画图中的多边形,然后在面板里单击鼠标左键,画点...扫描线种子填充的算法适合于任意图形,不会出现部分区域填补上的现象。 程序没有任何问题~ 有不明白的可以联系我~ qq:815366795~

    多边形扫描填充算法

    1. **扫描线算法**:这是最基础的填充方法,它基于扫描线的概念。首先,确定多边形的顶点并按Y轴排序,然后从最底部的扫描线开始,判断扫描线与多边形的交点。如果扫描线穿过多边形的边界,就从边界的一侧到另一侧...

    多边形区域的扫描线填充、扫描线种子填充算法实现

    本文将深入探讨两种常见的填充算法:扫描线填充算法和种子填充算法,并在MFC(Microsoft Foundation Classes)框架下实现它们。 首先,扫描线填充算法是一种基于垂直扫描线的填充方法。其基本思想是从图形的最高点...

    MFC扫描线区域填充算法

    《MFC扫描线区域填充算法详解》 在计算机图形学领域,区域填充是一种常见的任务,它涉及将指定区域内填充特定的颜色或图案。MFC(Microsoft Foundation Classes)是微软提供的一套C++类库,用于构建Windows应用程序...

    多边形区域扫描线填充或种子填充

    标题与描述解析:“多边形区域扫描线填充或种子填充”这一标题暗示了文章将讨论在计算机图形学中用于填充多边形区域的两种主要算法:扫描线填充算法和种子填充算法。描述部分提到“高手复制的代码,编译运行都没有...

    python实现扫描线填充算法,可以画凹多边形,采用matplotlib模块绘制图形

    python实现扫描线填充算法,使用matplotlib模块将绘制的图形保存并画出来,可以画凹多边形

    基于MFC实现多边形填充算法完整代码

    种子填充算法,也称为扫描线算法或Bresenham-like算法,是一种简单而有效的填充方法。它的基本思想是从一个或多个人工设定的种子点开始,通过判断当前像素是否与已填充区域相邻,逐渐扩展填充到整个多边形内部。该...

    C#实现种子扫描线填充算法

    种子扫描线填充算法是一种在计算机图形学中广泛用于填充二维图形内部区域的算法。它主要应用于图像处理、游戏开发和2D图形用户界面设计等领域。本文将深入探讨如何使用C#语言来实现这一算法。 首先,理解种子填充...

    多边形区域填充算法

    总结来说,多边形区域填充算法是计算机图形学中的核心技术,包括扫描线填充、种子填充、递归填充、边标志填充等多种方法,每种都有其适用场景和优缺点。通过深入理解和掌握这些算法,我们可以更好地实现复杂的图形...

    多边形扫描线填充(有序边算法实现)

    在C++ MFC(Microsoft Foundation Classes)环境中,我们可以构建一个用户界面,允许用户通过鼠标绘制多边形,然后应用扫描线填充算法来填充它们。 MFC是Microsoft开发的一个C++类库,用于构建Windows应用程序。它...

    多边形填充的扫描线算法实现

    3. **扫描线算法原理**:扫描线算法采用“分而治之”的策略,首先确定多边形的最高点和最低点,以此将多边形分割成多个小区域,分别进行填充。接着,对于每一条扫描线,找出其与多边形边界的交点,从而确定待填充的...

    扫描线多边形填充算法实现

    **扫描线多边形填充算法实现** 在计算机图形学领域,多边形填充是一种常见的任务,用于绘制出具有内部颜色的多边形。本篇将详细介绍如何在Microsoft Foundation Classes (MFC) 框架中实现扫描线填充算法,这是一种...

    计算机图形学 区域填充

    题目:用种子填充算法(或扫描线填充算法)填充任一多边形域 基本要求: (1)数据输入项为:多边形的顶点数、各顶点x,y坐标。 对于种子填充算法要输入种子象素的x,y坐标。 对于扫描线填充算法要输入扫描线间距。 ...

    多边形的扫描填充算法

    - 扫描线算法是填充多边形的核心方法之一,它通过逐行处理屏幕上的扫描线来填充多边形。首先,确定多边形的顶点,并按y坐标排序。对于每一扫描线,找到与扫描线相交的所有边,形成边的集合。然后根据边的相对位置...

    扫描线法填充多边形(完整C++代码)(QT工程)

    扫描线法是一种基于水平线(扫描线)来填充多边形的算法,其基本思想是将屏幕上的每一条水平线视为一条扫描线,然后根据多边形边界与扫描线的交点来判断像素是否属于多边形。具体步骤如下: 1. **预处理阶段**:...

    计算机图形学 扫描线种子填充算法c#实现

    计算机图形学 扫描线种子填充算法实现 1、初始化堆栈。  2、种子压入堆栈。  3、while(堆栈非空)  {  (1)从堆栈弹出种子象素。  (2)如果种子象素尚未填充,则:  a.求出种子区段:xleft、xright;  b.填充...

    多边形扫描线填充算法 多边形扫描线填充源代码 未橡皮筋实现 类实现的算法 开源

    多边形扫描线填充算法是计算机图形学中的一个重要概念,主要应用于二维图形的填充,例如在计算机绘图软件中绘制复杂形状。这个算法的核心思想是将屏幕上的每一水平线(扫描线)作为处理对象,通过判断扫描线与多边形...

Global site tag (gtag.js) - Google Analytics