0区域填充算法的研究.docx
- 文档编号:23882915
- 上传时间:2023-05-21
- 格式:DOCX
- 页数:20
- 大小:160.21KB
0区域填充算法的研究.docx
《0区域填充算法的研究.docx》由会员分享,可在线阅读,更多相关《0区域填充算法的研究.docx(20页珍藏版)》请在冰豆网上搜索。
0区域填充算法的研究
区域填充算法的研究
摘要:
本文主要介绍了一种常见的区域填充算法扫描线算法,其核心思想是利用区域的连贯性,扫描线的连贯性和边的连贯性,实现时首先计算扫描线与多边形区间,再用颜色或图案来显示这些像素,利用边的相关性及记录,边表及活动边表来完成多边形的区域填充。
最后又介绍了一系列区域填充的改进算法,奇一偶规则扫描线算法,非零环绕规则的改进算法,逐点判断填充算法,种子填充算法,填充图案填充算法,边界标志算法和边缘填充算法以及他们的实现过程,对他们做出了比较,指出了每种算法存在的优点和不足,并加以改进,最后又对这几种算法的结果做出了分析,得出了扫描线算法比较快,适合用软件实现,但算法比较复杂,对于有边相交的情况,有可能出现异常的结论。
对于在其基础上进行的改进算法,也都各有千秋,为此我们可以根据图形的具体情况选择最适用的填充算法。
关键字:
区域填充,扫描线算法,区域填充的改进算法
本文由天空乐园大学生旅游网分享
引言
图形的区域填充是计算机图形学的基本图形操作。
计算机图形学是计算机应用技术的基础内容之一,尤其是在虚拟现实技术、科学计算可视化、网络通信界面设计等领域有着越来越广泛的应用。
其中,图形区域填充算法的优劣直接影响图形显示速度和显示质量.传统的多边形填充经典算法有扫描线填充算法和种子填充算法两种。
而扫描线算法是从多边形的顶端开始,到多边形的底端为止,即先用一根根水平线进行扫描,并求得每一条水平扫描线与多边形各边的交点,然后将交点配成“交点对”,并在其间进行填充,这是最基础的一种区域填充算法。
但是区域填充的扫描线算法又有很大的局限性,对于有边相交的情况还可能会出现异常,为此我们还要对其进行改进,以更好的应用于实践。
一、基本理论
扫描线多边形区域填充算法基本原理是待填充区域按Y方向(X方向亦可)扫描线顺序扫描生成。
具体实现时,首先按扫描线顺序,计算扫描线与多边形的相交区间;再用要求的颜色填充这些区间内的像素,即完成这一条扫描线的填充工作。
区间的端点可以通过计算扫描线与多边形边界线的交点获得。
1.利用边的相关性可以简单有效的解决这个问题。
对于一条扫描线,多边形的填充过程可以分为四个步骤:
1)求交:
计算扫描线与多边形各边的交点;
2)排序:
把所有交点按x值递增顺序排序;
3)配对:
第一个与第二个,第三个与第四个等等;每对交点代表扫描线与多边形的一个相交区间;
4)填色:
把相交区间内的像素置成多边形颜色,把相交区间外的像素置成背景色。
重合点的处理:
当扫描线和边界相交于边界顶点时,同时产生两个交点,通常采用“起闭终开”或“起开终闭”。
水平边处理
2.边表(ET)和活动表(AET)
边表是一个包含多边形全部边记录的表,又称ET表,它按y坐标(与扫描线一一对应)递增(或递减)的顺序存放边界区域的所有边。
每个y坐标值存放一个或几个边记录。
把与当前扫描线相交的边称为活性边,并把它们按与扫描线交点x坐标递增的顺序存放在一个链表中,称此链表为活性边表(AET)。
随着扫描线从一条到另一条的转换,AET表也应随之变动,利用公式可以算出AET表中x域中的新值xi。
凡是与这另一条扫描线相交的任何新边都加到AET表中,而与之不相交的边又被从AET表中删除。
活性边表的每个节点的内容:
第1项存当前扫描线与边的交点坐标x值;
第2项存从当前扫描线到下一条扫描线间x的增量Dx;
第3项存该边所交的最高扫描线号ymax;
第4项存指向下一条边的指针。
假定当前扫描线与多边形某一条边的交点的x坐标为x,则下一条扫描线与该边的交点不要重计算,只要加一个增量△x。
(连贯性)
设该边的直线方程为:
ax+by+c=0;
若y=yi,x=xi;则当y=yi+1时,
xi+1=xi-b/a
其中ΔX=-b/a为常数,
另外使用增量法计算时,我们需要知道一条边何时不再与下一条扫描线相交,以便及时把它从活性边表中删除出去。
扫描线6的活性边表
扫描线7的活性边表
为了方便活性边表的建立与更新,我们为每一条扫描线建立一个新边表(NET),存放在该扫描线第一次出现的边。
也就是说,若某边的较低端点为ymin,则该边就放在扫描线ymin的新边表中。
当某条扫描线yi碰到多边形边界的新边时(以边线低端为准),则在ET表中相应的y坐标值处写入一个边记录。
当同时有多条边进入时,则在ET表中按链表结构写入相应数目的多个记录,这些记录是按边线较低端点的x值增加的顺序排列。
当没有新边加入时,表中对应的y坐标值储存内容为空。
注意:
在ET表中:
1、与x轴平行的边不记录;2、多边形的顶点分为两大类,一类是局部极值点,另一类是非极值点。
当扫描线与第一类顶点相遇时,应看作两个点;当扫描线与第二类顶点相遇时,应视为一个点。
3.算法过程
(1)根据给出的顶点坐标数据,按y递增顺序建立ET表。
(2)根据AET指针,使之为空。
(3)使yi=ymin(ymin为顶点坐标中最小y值)。
(4)反复做下述各步,直至yi=ymax(顶点坐标中y的最大值)
或ET与AET为空:
1、将ET表加入到AET中,并保持AET链中的记录按x值增大排序;
2、对扫描线yi依次成对取出AET中xi值,并在每对xi之间
填上所要求的颜色或图案;
3、从AET表中删去yi=ymax的记录;
4、对保留下来的AET中的每个记录,用xi+1/m代替xi,
并重新按x递增排序;
5、使yi+1,以便进入下一轮循环。
区域填充算法的扫描线算法理论上可以采用,但是再实际实现过程中效率却不是很高,所以下面在此基础上对其进行了一系列的改进,他们都在一定程度上实现了区域填充算法的改进。
二、改进算法
1.奇一偶规则扫描线算法
解决奇一偶规则扫描线算法的有效方法是先建立扫描线多边形有序边表(sortededgetable),然后,从多边形的底部到顶部处理扫描线,对每条与多边形相交的扫描线生成一个活化边表(AET表),填充位于AET表中奇数交点和偶数交点之间的扫描区段。
(1)建立有序边表
对每条边建立如下结点信息:
TypedefstructtEdge
{IntyUpper;
FloatxIntersect;
FloatdxPerScan;
StructtEdge*next;
}Edge;
根据上述结点信息,建立多边形的有序边表.
(2)建立活化边表(AET)及填充算法
设Ymax为最大扫描线的Y坐标值,Yi为第I条扫描线。
①按上述要求建立多边形有序边表(Y桶链表)
②I=0,AET置空
③把有序边表中Yi所指向的结点链到AET链尾,并对AET链按结点中X坐标值的不减次序重排各结点。
④填充AET链中位于奇数结点和偶数结点之间的扫描区段
⑤更新AET链表结点,删除结点分量yUpper=I的结点,对其余结点P用公式
P->xIntersect=P->xIntersect+P->dxPerScan更新xIntersect分量
⑥I=I+1,若I<=Ymax则转,否则算法结束
2.改进算法以适应非零环绕规则
根据非零环绕规则和环绕数计算方法,我们可以从以下几个方面对算法1进行修该:
不失一般性,在计算环绕方法中从P点引射线可以取成从P点沿X轴负方向引水平射线,则
射线PQ的向量u的值为(XQ–XP,0)
∴公式
(1)Z=UXEY–UYEx=(XQ-XP)EY
∵XQ-XP<0
∴Z与Ex异号
故只要能保存多边形有向边的Y方向上的符号,就可以计算出点P的环绕数。
为此改变有序边表中的结点结构:
只须增加一个ySignEdge分量,当yUpper=有向边终点的y坐标值时,ySignEdge=1;否则当yUpper=有向边起点的y坐标值时,
ySignEdge=-1.
改变后的点结构为:
yUpperxIntersectdxPerScanySignEdgenext
改进算法1
对于算法1,只须改动第④步,其余步骤无须变动.
改动后的第④步为:
设nonzeroNumber=0;NodeNumber=0;顺序遍历AET链表,对每一结点P置nonzeroNumber=nonzeroNumber+P->ySignEdge;
NodeNumber=NodeNumber+1;
若对nonzeroNumber≠0的后续区段进行填充,则完成非零环绕数规则填充算法.若对NodeNumber为奇数的后续区段进行填充,则完成奇一偶规则填充算法其中后续区段指当前结点和下结点X坐标之间的区域。
3.逐点判断填充算法
逐点判断填充算法的基本思想是逐点判断绘图窗口内的每一个像素,若在区域的内部则用指定的属性设置该点,否则不予处理。
设有如下函数:
取矩形R(x1≤x≤x2,y1≤y≤y2),使R包围D,
则逐点判断填充算法如下:
for(y=y1;y<=y2;y++)
for(x=x1;x<=x2;x++)
if(inside(D,x,y))
drawpixel(x,y,color);
上述算法原理简单、实用,但效率低;
效率低的原因是没有考虑各象素之间的联系,孤立地考察象素与区域的关系,使得几十万甚至几百万个象素都要一一判别,每次判别又要多次求交点,需要做大量的乘除运算,花费很多时间。
一般用射线法来判断点在多边形的内或外。
过被检测点任作一条射线,求其与边界的交点,若交点数为偶数,则该点在边界之外,否则在边界之内。
当射线经过顶点时,可通过”左开右闭”,或”上开下闭”进行处理。
逐点判断法计算量比较大,比较慢,但方法比较简单,填充的图形类型比较多。
4.种子填充算法
种子填充算法假设在多边形内有一象素已知,由此出发利用连通性找到区域内的所有象素,并进行填充。
种子填充算法又分为深度递归的种子填充算法和扫描线种子填充算法两种。
下面是对这两种算法的介绍:
(1)深度递归的种子填充算法(漫水法)
从已知种子点出发,每填充一点,在其周围寻找新种子点,重复进行,直到再无未填充的点为止。
针对内点连通区域的递归填充具体步骤:
1.种子入栈.
2.当栈非空时,进行下面的操作,否则结束.
3.栈顶元素出栈,如果是未填充的内部点,则将其填充.继续考察与其连通的点,若是未填充的内部点,则该点入栈.返回2.
准备工作:
typedefstruct{intx,y;}seed;
typedefstruct{seeds[6400];inttop;}seedstack;
(2)扫描线种子填充算法
1.种子填充的递归算法原理和程序都很简单,但由于多次递归,费时、费内存,效率不高。
为了减少递归次数,提高效率可以采用采用扫描线算法。
2.算法的基本过程如下:
当给定种子点(x,y)时,首先填充种子点所在扫描线上的位于给定区域的一个区段,然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,确定新种子点,并依次保存下来。
反复这个过程,直到填充结束。
上述算法对于每一个待填充区段,只需压栈一次;而在递归算法中,每个象素都需要压栈。
因此,扫描线填充算法提高了区域填充的效率。
扫描线种子区域填充算法
可由下列四个步骤实现:
1.初始化:
堆栈置空。
将种子点(x,y)入栈。
2.出栈:
若栈空则结束。
否则取栈顶元素(x,y),以y作为当前扫描线。
3.填充并确定种子点所在区段:
从种子点(x,y)出发,沿当前扫描线向左、右两个方向填充,直到非内部。
分别标记区段的左、右端点坐标为xl和xr。
4.并确定新的种子点:
在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的象素。
若存在非边界、未填充的象素,则把每一区间的最右象素作为种子点压入堆栈,返回第2步。
5.填充图案
前面介绍的那些区域填充算法,都是把区域内部的全部的象素设置为同一种颜色。
但在实际应用中,有时需要用一种图案来填充平面区域。
可以通过对前述算法中写象素的那部分内容稍作修改来实现:
在确定了区域内一象素之后,不是马上往该象素填色,而是先查询图案位图的对应位置。
进行图案填充时,在不考虑图案旋转的情况下,必须确定区域与图案之间的位置关系。
这可以通过把图案原点与区域某点对齐的办法来实现。
1.对齐方法有两种:
第一种是把图案原点与填充区域边界或内部某点对齐。
第二种方法是把图案原点与填充区域外部的某点对齐。
2.用第一种方法填充的图案,将随着区域的移动而移动,看起来很自然。
对于多边形,可取区域边界上最左边的顶点。
而对于圆和椭圆这样具有光滑边界的区域,则最好取区域内部的某一点,如中心,对应图案原点。
3.从算法复杂性看,第二种方法比较简单,并且在相邻区域用同一图案填充时,可以达到无缝连接的效果。
但当区域移动时,图案不会跟着移动,区域内的图案变了。
4.下面来讨论在第二种方式下,如何对平面区域进行填充。
假定图案是一个M*N位图,用M*N数组存放。
M,N一般比需要填充的区域的尺寸小得多,所以图案总是周期性的使用。
假定区域内一象素(x,y),
对应于图案上的(x%M,y%N)。
若以透明方式填充图案,
if(pattern[x%M][y%N])putpixel(x,y);
å以不透明方式填充图案,
if(pattern[x%M][y%N])
putpixel(x,y,color);
else
putpixel(x,y,bkcolor
6.边界标志算法
边界标志算法的基本思想是:
在帧缓冲器中对多边形的每条边进行直线扫描转换,亦即对多边形边界所经过的象素打上标志。
然后再采用和扫描线算法类似的方法将位于多边形内的各个区段着上所需颜色。
对每条与多边形相交的扫描线依从左到右的顺序,逐个访问该扫描线上的象素。
使用一个布尔量inside来指示当前点是否在多边形内的状态。
Inside的初值为假,每当当前访问的象素为被打上边标志的点,就把inside取反。
对未打标志的象素,inside不变。
若访问当前象素时,inside为真,说明该象素在多边形内,则把该象素置为填充颜色。
边界标志算法步骤
1.对多边形的每一条边进行扫描转换,即对多边形边界所经过的象素作一个边界标志。
2.填充。
对每条与多边形相交的扫描线,按从左到右的顺序,逐个访问该扫描线上的象素。
取一个布尔变量inside来指示当前点的状态,若点在多边形内,则inside为真。
若点在多边形外,则inside为假。
Inside的初始值为假,每当当前访问象素为被打上标志的点,就把inside取反。
对未打标志的点,inside不变。
边界标志算法:
算法过程
voidedgemark_fill(polydef,color)
多边形定义polydef;intcolor;
{对多边形polydef每条边进行直线扫描转换;
inside=FALSE;
for(每条与多边形polydef相交的扫描线y)
for(扫描线上每个象素x)
{if(象素x被打上边标志)
inside=!
(inside);
if(inside!
=FALSE)
drawpixel(x,y,color);
elsedrawpixel(x,y,background);
}
}
边界标志算法:
voidedgemark_fill(polydef,color)
多边形定义polydef;intcolor;
{ 对多边形polydef每条边进行直线扫描转换;
inside=FALSE;
for(每条与多边形polydef相交的扫描线y)
for(扫描线上每个象素x)
{if(象素x被打上边标志)inside=!
(inside);
if(inside!
=FALSE) drawpixel(x,y,color);
else drawpixel(x,y,background);
}
}
用软件实现时,边界标志算法与扫描线算法的执行速度几乎相同,但由于边界标志算法不必建立维护边表以及对它进行排序,所以边界标志算法更适合硬件实现,这时它的执行速度比有序边表算法快一至两个数量级。
在理想情况下,该方法是能很好工作的。
但由于计算机显示出的点是离散化的,会出现一些异常。
比如说:
如果直线斜率不为0,理论上不会有两个不同点的纵坐标Y相同,但屏幕显示时确有可能出现这样的情况,在这种情况下,如果直接使用上述算法,会出现问题。
我是这样处理的:
为了保证不会出现上面的情况,强制一条直线与扫描线有最多只有一个交点,当出现多个时,不同点的纵坐标Y相同时,只对较左的一点着上边界标志色;这样可以保证一条直线与扫描线最多只有一个交点。
另一种异常是:
当多边形的角很尖时,有可能使相邻两边的部分点重合在一起,这样读点时只能读到一次边界标志色,如再使用奇偶规则,会导致多边形外面的点被着色。
我是这样处理的:
对边着边界标志色时,先对其判断,如果该点已是边界色了,就对该点着上填充色。
这样便可解决上述问题。
但对于某些图形,还有会有个别异常,有待进一步调试。
7.边缘填充算法(正负相消法)
(1)边缘填充算法
边缘填充算法的基本思想是对于每条边上的每一点都将所在扫描线上其右方的所有象素的颜色取补。
这种算法简单,但对于复杂图型,每一象素可能被访问多次。
边缘填充算法的实现过程:
求余运算:
假定A为一个正整数,则M的余定义为A–M,记为。
计算机中取A为n位能表示的最大整数。
即,A=0xFFFFFFFF
光栅图形中,如果某区域已着上值为M的颜色值做偶数次求余运算,该区域颜色不变;而做奇数次求余运算,则该区域颜色变为值为颜色。
这一规律应用于多边形扫描转换,就为边缘填充算法。
算法实现的基本思想:
描线和每条多边形边的交点,将该扫描线上交点右方的所有象素取余。
算法1(以扫描线为中心的边缘填充算法)
1、将当前扫描线上的所有象素着上的颜色;
2、求余:
for(i=0;i<=m;i++)
在当前扫描线上,
从横坐标为Xi的交
点向右求余;
算法2(以边为中心的边缘填充算法)
1、将绘图窗口的背景色置为;
2、对多边形的每一条非水平边做:
从该边上的每个象素开始向右求余;
边缘填充算法适合用于具有帧缓存的图形系统。
处理后,按扫描线顺序读出帧缓存的内容,送入显示设备。
优点:
算法简单
缺点:
对于复杂图形,每一象素可能被访问多次,输入/输出的量比有序边表算法大得多。
为此边缘填充算法中引入了栅栏,以减少填充算法访问象素的次数,栅栏指的是一条过多边形顶点且与扫描线垂直的直线。
它把多边形分为两半。
(2)栅栏填充算法
栅栏填充算法的本思想:
扫描线与多边形的边求交,将交点与栅栏之间的象素取补。
根据余数的性质,即对M作偶数次求余运算,其结果是M;而对M作奇数次求余运算的结果是/M。
在光栅图形中,如某区域已着上值为M的某种颜色,对M作偶数次求余运算后,该区域颜色不变;而作奇数次求余运算后,该区域颜色则变为值为/M的颜色。
具体算法为:
先求出包含该多边形的最小矩形;对每边的右边的点做求余填充。
程序中取A=RGB(255,255,255),于背景色是白色,所以M=RGB(255,255,255);根据奇偶规则易知:
在多边形内的点被填充了奇数次,所得颜色为:
/M=A-M=(0,0,0)即黑色,而对多边形外的点被填充的偶数次,颜色为M,即白色,还是背景色。
栅栏填充算法在一定程度上减少了象素重复访问数目,但不彻底。
三、结果分析
根据多边形的各个边求出所有交点插入链表的相应位置,然后进行XT链的排序。
对于凸多边形来说,每一条子XT链仅有两个结点,即每条水平线与凸多边形相交最多有两个交点。
而对于凹多边形的处理,算法就比较复杂。
一是扫描线与多边形的交点不一定是两个。
处理的方法是将所有的交点按X值的不减的顺序进行排序,再两两配对画线;二是有奇异点的情况,通过该结点的扫描线与多边形的交点为奇数个,从而无法两两配对画线。
据此算法,编制了程序,通过实例验算,实现了“扫描线算法”。
对算法的进行分析或是实际算例的演算结果都表明,扫描线算法较具有减少了乘除运算、基本上没有多余判断的特点,是一种效率较高、有应用价值的算法。
边界标志算法用软件实现时,扫描线算法与边界标志算法的执行速度几乎相同,但由于边界标志算法不必建立维护边表以及对它进行排序,所以边界标志算法更适合硬件实现,这时它的执行速度比有序边表算法快一至两个数量级。
四、总结
扫描线算法有一定的优缺点,优点是:
简单实用,对于边数不多,面积不是很大的任意多边形来说,算法的时间和空间复杂度是可以接受的。
缺点是效率不高是主要问题,同时多边形中存在奇异点的情况,存在和扫描线重叠的情况,因而需要加入特殊处理。
特殊情况:
1)多边形有凸多边形和凹多边形,对于凸多边形过极值点时满足奇数点进入多边形,偶数点离开多边形,凹多边形则相反,因而此为一相异情况,如图2中的c和d扫描线。
2)多边形的顶点为一特殊情况,有极值点和非极值点,极值点时既为多边形进入点,又是多边形离开点,非极值点则相反,如图2中的a和d扫描线。
3)采用X轴或Y轴扫描线填充存在边与X轴或Y轴重叠的情况,如图2中的b扫描线。
根据扫描线算法的优缺点,机扫描线的奇异情况有如下的算法的改进方向:
为了消除填充算法存在的奇异情况,提出一个修正的方案,假设采用X轴作为扫描线,多边形在Y轴上的最大值和最小值分别为a和b,新建(a-b+1)个整形数组来保存相交的点,在用多边形的边去切扫描线时,先判断此边是否为与X轴平行的直线,如是则将此边作一个标记,将此点的2个端点的X值存入到对应的数组中去,在画扫描线时先判断此数组中存的从小到大的X值,如果此点前面没有比它小的X值,则此直线的左端点值为无效交点,反之则为有效交点。
如果此边不是与X轴平行的直线,则判断此边的2个端点是否为极值点,极值点则在相应的数组中存入2个一样的X值,否则存入一个X值。
上面介绍的几种改进算法,各有千秋,都在一定程度上实现了区域填充算法的改进,扫描线算法比较快,适合用软件实现,但算法比较复杂,对于有边相交的情况,有可能出现异常。
边缘填充,比较慢,对同一点有很多重复操作,方法简单;逐点判断法计算量比较大,比较慢,但方法比较简单,填充的图形类型比较多;边界标志法,比较快,特别适合用硬件实现,方法思想很简单,但细节比较多,要用到一些技巧。
要是能想把这些填充算法很好的应用于实践还需要做进一步的完善,这还需要我们共同的努力。
参考文献
[1]KennethR.Castleman,“DigitalImageProcessing”,Prentice-HallInternational,Inc,1996
[2]JamesSharman.TheMarchingCubesAlgorithm[EB].
[3]费广正,芦丽丹,陈立新.可视化OpenGL程序设计[M].北京:
清华大学出版社,2001.
[4]孙家广,许隆文.计算机图形学.北京:
清华大学出版社,1995
[5] 苏步青,刘鼎元.计算几何.上海科技出版社,1980
[6]孙燮华.2000.扫描线种子填充算法的改进.计算机工程,26(
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 区域 填充 算法 研究