四、边界标志填充算法
在光栅显示平面上,多边形是封闭的,它是用某一边界色围成的一个闭合区域,填充是逐行进行的,即用扫描线逐行对多边形求交,在交点对之间填充。边界标志填充算法就是在逐行处理时,利用边界或边界颜色作为标志来进行填充的。准确地说,边界标志填充算法不是指某种具体的填充算法,而是一类利用扫描线连贯性思想的填充算法的总称。这类算法有很多种,本篇就介绍几种。
首先介绍一种以边为中心的边缘填充算法,这种边界标志算法的基本思想是:对于每一条扫描线和每一条多边形边的交点(xi,yi),将该扫描线上交点右方的所有象素取补,依次对多边形的每条边作此处理,直到最终完成填充。这里要介绍一下取补的定义,假设某点的颜色是M,则对该点的颜色取补得到M’ = A – M,A是一个很大的数字,至少要比所有合法的颜色值大。根据取补的定义,如果对光栅位图某区域已经标记为M的颜色值做偶数次取补运算,该区域颜色不变;而做奇数次取补运算,则该区域颜色变为值为M’的颜色。算法可以简单描述为两个步骤:
1、将绘图窗口的背景色置为M’颜色;
2、对多边形的每一条非水平边,从该边上的每个象素开始向右求余;
算法的处理过程如图(12)所示,左边是多边形的形状,右边分别是对每条边处理完成后填充区域的颜况,初始背景颜色是M’,经过处理后,需要填充的区域是奇数次取补,最终的颜色是要填充的正确值M,非填充区域经过偶数次取补,仍然是背景色M’:
图(12)边缘填充算法的处理过程
算法的实现非常简单,对于光栅位图的展示,我们仍然采用前文所用的方法,用数字矩阵表示一块光栅位图区域,矩阵的每个位置表示一个像素点,用0-9表示颜色值。本算法示例用9表示最大值A,0表示无效的区域,合法的颜色值就是1-8。
87void EdgeCenterMarkFill(const Polygon& py, int color)
88{
89 std::vector<EDGE3> et;
90
91 InitScanLineEdgesTable(et, py);//初始化边表
92
93 FillBackground(A - color); //对整个填充区域背景颜色取补
94 for_each(et.begin(), et.end(), EdgeScanMarkColor);//依次处理每一条边
95}
|
76void EdgeScanMarkColor(EDGE3& e)
77{
78 for(int y = e.ymax; y >= e.ymin; y--)
79 {
80 int x = ROUND_INT(e.xi);
81 ComplementScanLineColor(x, MAX_X_CORD, y);
82
83 e.xi -= e.dx;
84 }
85}
|
这个算法非常简单,所有的函数实现也很简单,InitScanLineEdgesTable()函数前面已经介绍过,FillBackground()函数将填充背景初始化为要填充颜色的取补颜色,EdgeScanMarkColor()函数负责对每条非水平边进行处理,逐条扫描线进行颜色取补,ComplementScanLineColor()函数负责对y扫描线上[x1, x2]区间的点的颜色值取补。
以上以边为中心的填充算法的优点是简单,缺点是对于复杂多边形,每一象素可能被访问多次(多次取补),效率不高。考虑对此算法改进,人们提出了栅栏填充算法。栅栏填充算法的基本思想是:经过多边形的某个顶点,在多边形内部建立一个与扫描线垂直的“栅栏”,当扫描线与多边形边有交点,就将交点与栅栏之间的象素取补。若交点位于栅栏左边,则将交点之右,栅栏之左的所有象素取补;若交点位于栅栏右边,则将栅栏之右,交点之左的象素取补。
仍以图(12)所示的多边形为例,假设经过P4点建立一条栅栏,则改进的栅栏填充算法处理过程就如图(13)所示:
图(13)栅栏填充算法的处理过程
栅栏填充算法的实现和以边为中心的边缘填充算法类似,只是对每条边的扫描线取补处理的范围控制有区别,算法需要指定一个“栅栏”:
115void EdgeFenceMarkFill(const Polygon& py, int fence, int color)
116{
117 std::vector<EDGE3> et;
118
119 InitScanLineEdgesTable(et, py);//初始化边表
120
121 FillBackground(A - color); //对整个填充区域背景颜色取补
122 for_each(et.begin(), et.end(),
123 std::bind2nd(std::ptr_fun(FenceScanMarkColor), fence));//依次处理每一条边
124}
|
97void FenceScanMarkColor(EDGE3 e, int fence)
98{
99 for(int y = e.ymax; y >= e.ymin; y--)
100 {
101 int x = ROUND_INT(e.xi);
102 if(x > fence)
103 {
104 ComplementScanLineColor(fence, x, y);
105 }
106 else
107 {
108 ComplementScanLineColor(x, fence - 1, y);
109 }
110
111 e.xi -= e.dx;
112 }
113}
|
注意FenceScanMarkColor()函数和EdgeScanMarkColor()函数的区别,就是这点区别使得栅栏填充算法主动减少了很多像素被访问的次数,而多边形之外的像素也不会被多余处理,效率提高了不少。
栅栏填充算法只是减少了被重复访问的象素的数目,但仍有一些象素会被重复访问。从图(13)中很容易看出这一点。下面介绍的边标志算法利用扫描线的连贯性,对每个像素只访问一次即可完成多边形区域的填充。边标志算法的思想是设置一个标志,沿着扫描线从左到右访问多边形所在区域的像素的时候,用这个标志标识是否扫描到了多边形的边界。如果碰到边界(边界用特殊的颜色标识),则改变标识状态,接下来根据标识状态决定扫描到的像素点是否要填充成指定颜色。
在实施边标志填充算法通常先求出多边形所在图像区域的最小包围盒,以便缩小扫描范围,加快填充速度。完整的填充算法如下:
5void EdgeMarkFill(int xmin, int xmax, int ymin, int ymax,
6 int boundarycolor, int color)
7{
8 int flag = -1;
9 for(int y = ymin; y <= ymax; y++)
10 {
11 flag = -1;
12 for(int x = xmin; x <= xmax; x++)
13 {
14 if(GetPixelColor(x, y) == boundarycolor)
15 {
16 flag = -flag;
17 }
18 if(flag == 1)
19 {
20 SetPixelColor(x, y, color);
21 }
22 }
23 }
24}
|
可以看到该算法思想简单,实现容易。算法既不需要初始化边表、求交点、交点排序等额外操作,也不需要使用链表、堆栈等数据结构。不过,边标志算法虽然简单,但是使用时对边界的处理要格外小心,否则很容易出错。比如上下顶点之类的局部极值点,会造成标志的奇偶计数不匹配,填充时会出现“抽丝”现象,也就是扫描线上不该填充的区段被填上色,而应该填充的区段却没有填上色。再比如边界像素点重复的问题,多边形的边界是利用直线的生成算法依次画出的,当扫描线遇到斜率小于1的边时,容易产生重复的边界点,如图(14)所示,引起标志的无序改变,从而造成填充错误。
图(14)斜率小于1的边的光栅扫描转换问题
至此,本文完整介绍了8中常见的多边形区域填充算法,拖拖沓沓写了一个多月,总算完成了,居然有将近30页,总计13000多字(24000多可见字符),毕业以后就没写过这么长的文档了,感慨一下。本文给出的示例代码都是为了演示,基于可读性而设计的,大家在实际的图形处理软件中看到的算法实现可能和本文的示例算法“大相径庭”,但是基本原理都是一样的。
参考资料:
【1】计算几何:算法设计与分析 周培德 清华大学出版社 2005年
【2】计算几何:算法与应用 德贝尔赫(邓俊辉译) 清华大学出版社 2005年
【3】计算机图形学 孙家广、杨常贵 清华大学出版社 1995年
分享到:
相关推荐
在本项目中,我们关注的是“多边形有效边表填充算法”(Polygon Edge Flag Algorithm,简称PEFA),它是计算机图形学中用于填充二维多边形内部的一种经典算法。MFC(Microsoft Foundation Classes)是微软提供的一个...
总结来说,多边形区域填充算法是计算机图形学中的核心技术,包括扫描线填充、种子填充、递归填充、边标志填充等多种方法,每种都有其适用场景和优缺点。通过深入理解和掌握这些算法,我们可以更好地实现复杂的图形...
在计算机图形学中,边缘填充算法是一种用于填充二维图形内部的重要技术,特别是在图像处理和计算机视觉领域。本文将深入探讨C++实现的多边形边缘填充算法,以及它如何应用于图像填充。 首先,边缘填充的基本原理是...
多边形有效边表填充算法(扫描线填充算法的一种)是一种在计算机图形学中用于填充二维多边形内部像素的方法。这种算法的核心思想是通过构建一个有效边表(Edge Table),然后结合扫描线来逐行填充多边形的内部。在...
常见的填充算法有以下几种: 1. **扫描线算法**:这是最基础的填充方法,它基于扫描线的概念。首先,确定多边形的顶点并按Y轴排序,然后从最底部的扫描线开始,判断扫描线与多边形的交点。如果扫描线穿过多边形的...
在本资源中,我们关注的是基于MFC(Microsoft Foundation Classes)框架实现的多边形填充算法,特别是种子填充算法。MFC是微软为Windows应用程序开发提供的一套C++类库,它简化了Windows API的使用。 种子填充算法...
计算机图形学多边形填充算法实验报告 计算机图形学是计算机科学的一个分支,它研究如何使用计算机生成和处理图形信息。多边形填充算法是计算机图形学中一个重要的研究方向,本实验报告将对多边形填充算法的实现和...
区域填充算法是计算机图形学中的一个基础且重要的概念,它主要应用于图像处理、绘图软件以及游戏开发等领域。这种算法的目的是将用户在屏幕上画出的多边形或选定的像素区域染上特定的颜色,使得视觉效果更加完整和...
- **定义渲染函数**:在OpenGL的绘制回调函数中,我们执行扫描线填充算法。首先绘制多边形的边界,然后使用扫描线方法填充内部。 - **调用`glBegin`和`glEnd`**:在`glBegin(GL_POLYGON)`和`glEnd()`之间,我们可以...
有效边表填充算法(扫描线填充算法的一种)是基于扫描线方法来处理多边形填充的。该算法的核心思想是沿着水平方向(通常是Y轴)从上到下扫描,维护一个“有效边表”(Edge Table),记录当前扫描线与多边形边界相交...
本文将深入探讨C++实现多边形有效边填充算法的原理和方法。 首先,我们要理解多边形的有效边填充算法,通常基于扫描线算法。扫描线算法是一种逐行处理图像的方法,它通过在图像的水平线上移动(即扫描线)来填充...
1. 通过实验,进一步理解和掌握几种常用多边形填充算法的基本原理 2. 掌握多边形区域填充算法的基本过程 3. 掌握在C/C++环境下用多边形填充算法编程实现指定多边形的填充。 实验设备及实验环境 计算机(每人一台) ...
在给定的标题“多边形填充算法java实现”中,我们可以推断这是一个Java编程项目,它实现了对多边形内部进行填充的功能。描述中提到的“扫描线算法”是实现这一功能的常见方法,这种方法基于逐行扫描图像并处理与...
3. **判断点是否在多边形内**:多边形填充算法通常依赖于一种称为“扫描线”或“扫描转换”的方法,该方法通过在每个水平线上检查垂直交点的数量来确定点是否位于多边形内。这可以通过Ray Casting算法实现,即从被...
填充算法是一种用于将特定区域(如多边形内部)涂上某种颜色或图案的算法。在计算机图形学中,有两种主要类型的填充算法:边界填充算法和区域填充算法。 - **边界填充算法**:这类算法通过识别边界颜色与填充颜色的...
经典的多边形填充算法有以下几种: 1. **扫描线算法**:扫描线算法首先确定多边形的顶点,然后按照Y轴顺序排序。对于每一条扫描线,找出与之相交的边,根据边的信息确定填充区域。 2. **扫描转换**:扫描转换算法...
常见的填充算法有以下几种: 1. **扫描线算法**:该方法通过逐行扫描屏幕,对每一行的像素进行判断。首先找到多边形的所有顶点,然后根据顶点的y坐标排序。对于每一行,找出与该行相交的边,并利用这些边形成一个...
实现凹多边形种子填充算法时,有几种常见的优化策略: - **八邻域连接**:考虑像素的上下左右和对角线邻居,以更快速地遍历图像。 - **双缓冲**:使用双缓冲技术可以避免在填充过程中屏幕闪烁,提高用户体验。 - **...
在本项目中,我们关注的是如何在VC++环境中利用两种不同的算法——扫描线算法和种子填充算法来实现这一功能。同时,这个实现还包含了画线和绘制多边形的能力,这些都是图形用户界面的基本元素。 首先,让我们详细...