计算机图形学 第四讲 区域填充
计算机图形学--区域填充算法的实现

实验四区域填充算法的实现班级信计2班学号 20080502090 姓名张进分数一、实验目的和要求:1、理解区域的表示和类型,能正确区分四连通和八连通的区域2、了解区域填充的实现原理3、利用TurboC实现区域填充的递归算法二、实验内容:当给定种子点(x,y)时,首先填充种子点所在的扫描线上的位于给定区域的一个区段,然后确定与这一区段连通的上下两条扫描线上位于给定区域的区段依次保存下来。
反复这个过程,直到填充结束.三、实验结果分析1、种子填充算法假设在多边内有一像素已知,由此出发利用连通性找到区域内的所有像素2、设(x,y)为内点表示的四连通区域内的一点,oldcolor为区域的原色。
现取(x,y)为种子点,要将整个区域填充为显得颜色newcolor,递归填充过程如下:先判别像素(x,y)的颜色,若它的值不等于oldcolor,说明该像素或者位于区域之外或者已经被置于newcolor,不需要填充,算法结束,否则置该像素的颜色为newcolor,再对与其相邻上、下、左、右四个相邻像素分别作递归填充。
1、程序代码:#include "Conio.h"#include "graphics.h" /*for initgr()*/#include "stdio.h" /*for NULL */#define closegr closegraphvoid initgr(void) /* BGI初始化*/{int gd = DETECT, gm = 0; /* 和gd = VGA,gm = VGAHI是同样效果*/ registerbgidriver(EGAVGA_driver);/* 注册BGI驱动后可以不需要.BGI文件的支持运行*/initgraph(&gd, &gm, "");}enum BOOL{FALSE = 0, TRUE = 1};typedef struct{int y;int xLeft;int xRight;}Span;/*区段*/typedef struct stacknode{Span span;struct stacknode *next;}stacknode;typedef struct{stacknode *top;}linkstack;/*-----------------进栈操作----------------------------------------*/ void PushStack(linkstack *s, Span *span){stacknode *p=(stacknode*)malloc(sizeof(stacknode));p->span.y = span->y;p->span.xLeft = span->xLeft;p->span.xRight = span->xRight;p->next=s->top;s->top=p;}/*-----------------出栈操作------------------------------------------*/ void PopStack(linkstack *s,Span *span){int x;stacknode *p=s->top;span->y = p->span.y;span->xLeft = p->span.xLeft;span->xRight = p->span.xRight;s->top=p->next;free(p);}/*-----------------将栈清空------------------------------------------*/ void SetStackEmpty(linkstack *s){stacknode *p=s->top;while( s->top != NULL){free(p);s->top=p->next;}}/*--------------判断栈是否为空----------------------------------------*/ int IsStackEmpty(linkstack *s){if(s->top == NULL)return 1;elsereturn 0;}/*----------------核心程序开始----------------------------------------*/ void ScanLineFill4(int x,int y,int oldColor,int newColor){int xLeft,xRight;int i;enum BOOL isLeftEndSet, spanNeedFill;Span span;linkstack *s=(linkstack*)malloc(sizeof(linkstack));s->top = NULL;/*填充并确定种子点(x,y)所在的区段*/i = x;while(getpixel(i,y) == oldColor)/*向右填充*/{putpixel(i,y,newColor);i++;}span.xRight = i - 1; /*确定区段右边界*/i = x - 1;while(getpixel(i,y) == oldColor)/*向左填充*/{putpixel(i,y,newColor);i--;}span.xLeft = i + 1; /*确定区段左边界*//*初始化*/SetStackEmpty(s);span.y = y;PushStack(s,&span);/*将前面生成的区段压入堆栈*/while( ! IsStackEmpty(s) )/*终止判断*/{/*出栈*/PopStack(s, &span);/*处理上面扫描线*/y = span.y + 1;xRight = span.xRight;i = span.xLeft - 1;isLeftEndSet = FALSE;while(getpixel(i,y) == oldColor)/*向左填充*/ {putpixel(i, y, newColor);i--;}if( i != span.xLeft - 1)/*确定区段左边界*/{isLeftEndSet = TRUE;xLeft = i + 1;}i = span.xLeft;while( i < xRight){spanNeedFill = FALSE;while(getpixel(i,y) == oldColor) /*向右填充*/{if( ! spanNeedFill){spanNeedFill = TRUE;if( ! isLeftEndSet){isLeftEndSet = TRUE;xLeft = i;}}putpixel(i,y,newColor);i++;}if( spanNeedFill ){span.y = y;span.xLeft = xLeft;span.xRight = i - 1;PushStack(s, &span); /*将区段压入堆栈*/isLeftEndSet = FALSE;spanNeedFill = FALSE;}/* while(getpixel(i,y) != oldColor) */i++;}/*end of while( i < xRight) *//*处理下面一条扫描线,与处理上面一条扫描线完全类似*/ y = y - 2;xRight = span.xRight;i = span.xLeft - 1;isLeftEndSet = FALSE;while(getpixel(i,y) == oldColor)/*向左填充*/{putpixel(i, y, newColor);i--;}if( i != span.xLeft - 1)/*确定区段左边界*/{isLeftEndSet = TRUE;xLeft = i + 1;}i = span.xLeft;while( i < xRight){spanNeedFill = FALSE;while(getpixel(i,y) == oldColor) /*向右填充*/{if( ! spanNeedFill){spanNeedFill = TRUE;if( ! isLeftEndSet){isLeftEndSet = TRUE;xLeft = i;}}putpixel(i,y,newColor);i++;}if( spanNeedFill ){span.y = y;span.xLeft = xLeft;span.xRight = i - 1;PushStack(s, &span); /*将区段压入堆栈*/isLeftEndSet = FALSE;spanNeedFill = FALSE;}/* while(getpixel(i,y) != oldColor) */i++;}/*end of while( i < xRight) */delay(2000); /*延时*/}/*end of while( ! isStackEmpty() ) */}/*end of ScanLineFill4() *//*---------------------main()------------------------------------------*/ int main(){initgr(); /* BGI初始化*/setbkcolor(3);setcolor(5);moveto(50, 50); /*绘制4连通区域*/lineto(400, 50);lineto(400,300);lineto(150,300);lineto(150,400);lineto(50, 400);lineto(50, 50);ScanLineFill4(150,150,0,14); /*相与后oldColor == 0*/getch(); /* 暂停一下,看看前面绘图代码的运行结果*/ closegr(); /* 恢复TEXT屏幕模式*/return 0;}1.运行结果:。
第四章 区域填充应用课件

属于谁?
图4-1-1
浙江大学工程及计算机图学所
4.1.1 扫描转换矩形
方法:
void FillRectangle(Rectangle *rect,int color) { int x,y; for(y = rect->ymin;y <= rect->ymax;y++) for(x = rect->xmin;x <= rect->xmax;x++) PutPixel(x,y,color); }/*end of FillRectangle() */
如何判断多边形的一条边与扫描线是否相交? 与当前扫描线相交的边称为活性边(active edge),把它们按与扫描线交点x坐标递增的顺序存入 一个链表中,边的活化链表 ( AEL, Active edge table)。它记录了多边形边沿扫描线的交点序列。 只需对当前扫描线的活动边表作更新,即可得到 下一条扫描线的活动边表。
图4-1-2
浙江大学工程及计算机图学所
4.1.2 扫描转换多边形
多边形分为凸多边形、凹多边形、含内环的多边形。
图4-1-3
浙江大学工程及计算机图学所
4.1.2 扫描转换多边形
多边形的表示方法
• 顶点表示 用多边形顶点的序列来刻划多边形。直观、几何意义强、占
内存少;不能直接用于面着色。
图4-1-4
区域填充算法要求区域是连通的浙江大学工程及计算机图学所内点表示枚举处区域内部的所有像素内部的所有像素着同一个颜色边界像素着与内部像素不同的颜色边界表示枚举出边界上所有的像素边界上的所有像素着同一颜色内部像素着与边界像素不同的颜色43区域填充算法图431浙江大学工程及计算机图学所区域填充要求区域是连通的连通性4连通8连通8连通43区域填充算法图432浙江大学工程及计算机图学所4连通与8连通区域的区别连通性
计算机图形学--填充

扫描线多边形填充算法扫描线多边形区域填充算法是按照扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些去区间的像素(即完成填充)。
填充过程:1 求交:计算扫面线与多边形个边的交点。
2 排序: 把所有交点按x值地震顺序排序。
2 配对:两两配对,1,和2,3和4 等等。
每对交点代表扫面线与多边形的一个相交区间。
4 填充:把相交区间内的像素设置成多边形颜色。
相交顶点的数目确定:检查相交顶点的两条边的另外两个定点的y值。
按这两个y值中大于交点y值得个数是0,1,2来决定是取0,1或2个。
边界像素取舍:对扫描线与多边形的相交区间取左闭右开。
水平边界处理:水平边不参与求交计算,跳过。
相交:把多边形的所有边放在一个表中,处理每条扫描线是,按顺序从表中取出所有边,分别与扫面线求交。
改进:效率低,可只求与它相交的多边形的边进行求交运算。
算法思想及实现:活性边:与当前扫描线相交的边。
活性边表:把活性边按与扫描线线交点x坐标递增的顺序存放在一个链表中。
活性边的每个节点的内容:X ,X的变化量,Y的最大值,一个指针。
1 存放当前扫描线与边的交点坐标x值。
2 存放从当前扫描线到下一条扫描线间x的增量3 存放该边所交的最高扫面线号ymax;4 存放指向下一条边的指针。
算法的主要步骤:建立NET(new edge list)从最低扫面线开始到最高扫面线循环。
建立或调整AET(active edge list)按照AET总的接点顺序填充。
算法描述:算法描述:void polyfill (多边形polygon, 颜色color){for (各条扫描线i ){ 初始化新边表头指针NET [i];把ymin = i 的边放进边表NET [i];}y = 最低扫描线号;初始化活性边表AET为空;for (各条扫描线i ){ 把新边表NET[i]中的边结点用插入排序法插入AET表,使之按x坐标递增顺序排列;遍历AET表,把配对交点区间(左闭右开)上的象素(x,y),用drawpixel (x, y, color) 改写象素颜色值;遍历AET表,把y max= i +1的结点从AET表中删除,并把y max > i+1结点的x值递增D x;若允许多边形的边自相交,则用冒泡排序法对AET表重新排序;}} /* polyfill */。
计算机图形学-区域填充的扫描线算法

计算机图形学-区域填充的扫描线算法计算机图形学——区域填充的扫描线算法一.实验名称:区域填充的扫描线算法二.实验目的:1、理解区域填充扫描线算法的原理;2、实现区域填充的扫描线算法并测试;三.算法原理:算法基本思想: 首先填充种子点所在扫描线上位于区域内的区段,然后确定与该区段相邻的上下两条扫描线上位于区域内的区段,并依次将各区段的起始位置保存, 这些区段分别被用区域边界色显示的像素点所包围。
随后,逐步取出一开始点并重复上述过程,直到所保存各区段都填充完毕为止。
借助于栈结构,区域填充的扫描线算法之步骤如下:Step 1. 初始化种子点栈:置种子点栈为空栈,并将给定的种子点入栈;Step 2. 出栈:若种子点栈为空,算法结束;否则,取栈顶元素(x,y)为种子点;Step 3. 区段填充:从种子点(x, y) 开始沿纵坐标为y 的当前扫描线向左右两个方向逐像素点进行填色,其颜色值置为newcolor 直至到达区域边界。
分别以xl 和xr 表示该填充区段两端点的横坐标;Step 4. 新种子点入栈: 分别确定当前扫描线上、下相邻的两条扫描线上位于区段[xl, xr] 内的区域内的区段。
若这些区段内的像素点颜色值为newolor ,则转至Step 2;否则以区段的右端点为种子点入种子点栈,再转至Step 2。
四.原程序代码:/*****************************************//*4-ScanLineFill 区域填充的扫描线算法实现*//*****************************************/#include#include#include#include#define Stack_Size 100 //栈的大小常量//定义结构体,记录种子点typedef struct{int x;int y;}Seed;//定义顺序栈(种子点)typedef struct{Seed Point[Stack_Size];int top;}SeqStack;//初始化栈操作void InitStack(SeqStack *&S){S=(SeqStack *)malloc(sizeof(SeqStack)); S->top=-1;}//种子点栈置空;void setstackempty (SeqStack *S){S->top==-1;}//种子点栈状态检测函数int isstackempty (SeqStack *S){if(S->top==-1)return true; //空栈返回trueelsereturn false; //非空栈返回false}//种子点入栈;int stackpush (SeqStack *&S,Seed point){if(S->top==Stack_Size-1)//栈已满,返回false return false; S->top++;//栈未满,栈顶元素加1S->Point[S->top]= point;return true;}//取栈顶元素;int stackpop (SeqStack *&S,Seed &point){if(S->top==-1)//栈为空,返回falsereturn false;point=S->Point[S->top];S->top --;//栈未空,top减1return true;}//画圆void CirclePoints (int xc, int yc, int x, int y, int Color) { putpixel (xc + x, yc + y, Color);putpixel (xc + x, yc - y, Color);putpixel (xc - x, yc + y, Color);putpixel (xc - x, yc - y, Color);putpixel (xc + y, yc + x, Color);putpixel (xc + y, yc - x, Color);putpixel (xc - y, yc + x, Color);putpixel (xc - y, yc - x, Color); }//中点画圆算法void MidpointCircle(int radius, int Color) {int x, y;float d;x=0;y=radius;d=5.0/4-radius;CirclePoints(250,250,x,y,Color);while(x<y)< p="">{if (d<0){d+=x*2.0+3;}else{d+=(x-y)*2.0+5;y--;}x++;CirclePoints(250,250,x,y,Color);}}//四连通扫描线算法void ScanLineFill4(int x, int y, int oldcolor, int newcolor) { int xl, xr, i;bool SpanNeedFill;Seed pt;//种子点SeqStack *S;//定义顺序栈InitStack(S);//定义了栈之后必须把栈先初始化setstackempty(S);//种子点栈置空;pt.x = x;pt.y = y;stackpush (S,pt); // 种子点(x, y)入栈while (!isstackempty(S)){stackpop (S,pt);//取种子点y = pt.y;x = pt.x;while (getpixel (x,y)==oldcolor) {// 从种子点开始向右填充putpixel (x, y, newcolor);x++;}xr = x -1;x = pt.x -1;while (getpixel (x,y)==oldcolor) { // 从种子点开始向左填充putpixel (x, y, newcolor);x--;}xl = x + 1;x = xl;y = y +1; // 处理上面一条扫描线while (x < xr){SpanNeedFill = false;while (getpixel (x, y)==oldcolor){SpanNeedFill = true;x++ ;} // 待填充区段搜索完毕if (SpanNeedFill){// 将右端点作为种子点入栈pt.x = x - 1;pt.y = y;stackpush (S,pt);SpanNeedFill = false;} //继续向右检查以防遗漏while ((getpixel (x, y)!=oldcolor) && (x< xr)) x++; } //上一条扫描线上检查完毕x = xl;y=y-2; // 处理下面一条扫描线while (x < xr){SpanNeedFill = false;while (getpixel (x, y)==oldcolor){SpanNeedFill=true;x++ ;}if (SpanNeedFill){pt.x= x - 1;pt.y = y;stackpush (S,pt);SpanNeedFill=false;}while ((getpixel (x, y)!=oldcolor) && (x < xr))x++;}}}//主函数检测void main(){int radius,color;int x,y;//种子点int oldcolor,newcolor;//原色与填充色//输入参数值printf("input radius and color:\n");//画圆参数scanf("%d,%d",&radius,&color);printf("input x and y:\n"); //读入内点scanf("%d,%d", &x, &y);printf("input oldcolor and newcolor:\n"); //读入原色与填充色scanf("%d,%d", &oldcolor, &newcolor);int gdriver = DETECT,gmode;initgraph(&gdriver, &gmode, "c:\\tc");// 用背景色清空屏幕cleardevice();// 设置绘图色为红色setcolor(RED);MidpointCircle(radius,color);//用中点画圆算法画圆rectangle(150, 150, 350, 350);//再画一个矩形区域ScanLineFill4 (x,y,oldcolor,newcolor);//扫描线区域填充getch();closegraph();}五.运行结果与讨论:测试结果1:测试结果2:六.实验分析与讨论:1.通过借助栈这一数据结构,完成了区域填充的扫描线算法的实现,并利用以前所学的画圆等算法,进行综合运用,在此基础上进行扩充,设计多种图案,进行扫描线填充算法的检测,都得到了理想的结果,体现了算法的有效性;2.栈的数据结构给种子点的操作带来了极大的方便,为算法的实现提供了便利,同时还提高了算法的复用性和可靠性;3.此扫描线填充算法能够对多种图案进行填充,展现了算法的实用性。
计算机图形学实验四 区域填充算法的实现

实验四区域填充算法的实现班级 08信计2班学号 20080502082 姓名分数一、实验目的和要求:1、理解区域的表示和类型。
2、能正确区分四连通和八连通的区域3、了解区域填充的实验原理。
4、利用C++实现区域填充的递归算法。
二、实验内容:1假设在多边形内有一像素已知,由此出发利用连通性找到区域内所有像素。
2 取(x,y)为种子点将整个区域填充为新的颜色。
3 进行递归填充。
三、实验结果分析区域填充属性包括填充样式,填充颜色和填充图案的类型。
C语言中定义了某种图形后,即可调用-floodfill函数,对指定区域进行填充. 程序代码#define pi 3.141592#define MAX(a,b) (a>b)? a:b#define MIN(a,b) (a<b)? a:b#include "graphics.h"#include "math.h"struct edge {int ymax;float x;float delat;struct edge * pedge; };struct point{int x;int y;} ;struct et { struct edge * pedge;int n;};struct edge g_aet[10];struct edge dge[10];struct et g_et[10];struct point point1,point2;int ZUO(float x){ if((int)x==x)return (int)x;return (int)x+1;}int YOU(float x){ if((int)x==x)return (int)x-1;return (int)x;}int k=400,l=0;void draw1(){int i,t,j,a,c,p,z; float b;struct edge temp;for(i=k;i<=l;i++){a=0;for(t=0;t<=9;t++){ if(g_et[t].n==i) break;}for(j=0;j<=9;j++){ if(g_aet[j].ymax==0) break;}if(t!=10){ g_aet[j].ymax=g_et[t].pedge->ymax;g_aet[j].x=g_et[t].pedge->x;g_aet[j].delat=g_et[t].pedge->delat;if(g_et[t].pedge->pedge!=0){g_aet[j+1].ymax=g_et[t].pedge->pedge->ymax;g_aet[j+1].x=g_et[t].pedge->pedge->x;g_aet[j+1].delat=g_et[t].pedge->pedge->delat; }}for(j=0;j<=9;j++){ if(g_aet[j].ymax==0) break; }j--;for(t=0;t<=j;t++){ for(z=0;z<=j-1;z++){if(g_aet[z].x>g_aet[z+1].x){ temp.ymax=g_aet[z].ymax;temp.x=g_aet[z].x;temp.delat=g_aet[z].delat;g_aet[z].ymax=g_aet[z+1].ymax;g_aet[z].x=g_aet[z+1].x;g_aet[z].delat=g_aet[z+1].delat;g_aet[z+1].ymax=temp.ymax;g_aet[z+1].x=temp.x;g_aet[z+1].delat=temp.delat;}}}for(j=0;j<=9;j++){ if(g_aet[j].ymax==0) break; }j--;for(p=0;p<=j;p++){ a++;if(a%2!=0)b=g_aet[p].x;else{for(c=ZUO(b);c<=YOU(g_aet[p].x);c++)putpixel(c,i,2);}}for(t=0;t<=j;t++){ if(g_aet[t].ymax==(i+1)){ g_aet[t].ymax=0;g_aet[t].x=0;g_aet[t].delat=0;}g_aet[t].x+=g_aet[t].delat;}for(t=0;t<=j;t++){ for(z=0;z<=j-1;z++){if(g_aet[z].x<g_aet[z+1].x){ temp.ymax=g_aet[z].ymax;temp.x=g_aet[z].x;temp.delat=g_aet[z].delat;g_aet[z].ymax=g_aet[z+1].ymax;g_aet[z].x=g_aet[z+1].x;g_aet[z].delat=g_aet[z+1].delat;g_aet[z+1].ymax=temp.ymax;g_aet[z+1].x=temp.x;g_aet[z+1].delat=temp.delat;}}}}}void generate(){int i,y,n=1,m,q,p;float x;for(i=0;i<=9;i++){if(n==1){ point2.x=point1.x=300;point2.y=point1.y=200;n++;}else{ if(n%2==0){ x=40*cos(i*pi/5)+200;y=40*sin(i*pi/5)+200;}else{ x=100*cos(i*pi/5)+200;y=100*sin(i*pi/5)+200;}if(point1.y==y) { n++; continue;}m=MIN(point1.y,y);if(x==point1.x){ dge[i-1].delat=0;dge[i-1].ymax=MAX(point1.y,y);dge[i-1].x=x;dge[i-1].pedge=0;for(q=0;q<=9;q++){ if(g_et[q].n==m) break;}if(q==10){g_et[i-1].pedge=&dge[i-1];g_et[i-1].n=m;}else{g_et[q].pedge->pedge=&dge[i-1];g_et[i-1].n=0;}}else{dge[i-1].delat=(float)(x-point1.x)/(y-point1.y);dge[i-1].ymax=MAX(point1.y,y);if(point1.y>y) dge[i-1].x=x;else {dge[i-1].x=point1.x; }dge[i-1].pedge=0;for(q=0;q<=9;q++){ if(g_et[q].n==m) break;}if(q==10){ g_et[i-1].pedge=&dge[i-1];g_et[i-1].n=m;}else{g_et[q].pedge->pedge=&dge[i-1];g_et[i-1].n=0;}}p=MAX(point1.y,y);k=MIN(k,m);l=MAX(l,p);point1.x=x;point1.y=y;n++;}}if(point1.y==point2.y) return;else{if(point2.x==point1.x){dge[i-1].delat=0;dge[i-1].ymax=MAX(point1.y,point2.y);dge[i-1].x=point2.x;}else{ dge[i-1].ymax=MAX(point1.y,point2.y);if(point1.y>point2.y) dge[i-1].x=point2.x;else {dge[i-1].x=point1.x;}dge[i-1].delat=(float)(point2.x-point1.x)/(point2.y-point1.y);}}m=MIN(point1.y,point2.y);k=MIN(k,m);l=MAX(l,dge[i-1].ymax);g_et[i-1].n=m;g_et[i-1].pedge=&dge[i-1];}void main(){ int driver=DETECT,mode; int i; registerbgidriver(EGA VGA_driver);initgraph(&driver,&mode,"\\tc");initgraph(&driver,&mode,"\\tc");for(i=0;i<=9;i++){ g_aet[i].ymax=0; g_aet[i].x=0; g_aet[i].delat=0;g_et[i].pedge=0;}generate();draw1();circle(200,200,100); circle(200,200,40);getch();closegraph();}。
计算机图形学——区域填充算法(基本光栅图形算法)

计算机图形学——区域填充算法(基本光栅图形算法)⼀、区域填充概念区域:指已经表⽰成点阵形式的填充图形,是象素的集合。
区域填充:将区域内的⼀点(常称【种⼦点】)赋予给定颜⾊,然后将这种颜⾊扩展到整个区域内的过程。
区域填充算法要求区域是连通的,因为只有在连通区域中,才可能将种⼦点的颜⾊扩展到区域内的其它点。
1、区域有两种表⽰形式1)内点表⽰:枚举出区域内部的所有象素,内部所有象素着同⼀个颜⾊,边界像素着与内部象素不同的颜⾊。
2)边界表⽰:枚举出区域外部的所有象素,边界上的所有象素着同⼀个颜⾊,内部像素着与边界象素不同的颜⾊。
21)四向连通区域:从区域上⼀点出发可通过【上、下、左、右】四个⽅向移动的组合,在不越出区域的前提下,到达区域内的任意象素。
2)⼋向连通区域:从区域上⼀点出发可通过【上、下、左、右、左上、右上、左下、右下】⼋个⽅向移动的组合,在不越出区域的前提下,到达区域内的任意象素。
⼆、简单种⼦填充算法给定区域G⼀种⼦点(x, y),⾸先判断该点是否是区域内的⼀点,如果是,则将该点填充为新的颜⾊,然后将该点周围的四个点(四连通)或⼋个点(⼋连通)作为新的种⼦点进⾏同样的处理,通过这种扩散完成对整个区域的填充。
这⾥给出⼀个四连通的种⼦填充算法(区域填充递归算法),使⽤【栈结构】来实现原理算法原理如下:种⼦像素⼊栈,当【栈⾮空】时重复如下三步:这⾥给出⼋连通的种⼦填充算法的代码:void flood_fill_8(int[] pixels, int x, int y, int old_color, int new_color){if(x<w&&x>0&&y<h&&y>0){if (pixels[y*w+x]==old_color){pixels[y*w+x]== new_color);flood_fill_8(pixels, x,y+1,old_color,new_color);flood_fill_8(pixels, x,y-1,old_color,new_color);flood_fill_8(pixels, x-1,y,old_color,new_color);flood_fill_8(pixels, x+1,y,old_color,new_color);flood_fill_8(pixels, x+1,y+1,old_color,new_color);flood_fill_8(pixels, x+1,y-1,old_color,new_color);flood_fill_8(pixels, x-1,y+1,old_color,new_color);flood_fill_8(pixels, x-1,y-1,old_color,new_color);}}}简单种⼦填充算法的不⾜a)有些像素会多次⼊栈,降低算法效率,栈结构占空间b)递归执⾏,算法简单,但效率不⾼,区域内每⼀像素都要进/出栈,费时费内存c)改进算法,减少递归次数,提⾼效率三、扫描线种⼦填充算法基本思想从给定的种⼦点开始,填充当前扫描线上种⼦点所在的⼀区段,然后确定与这⼀段相邻的上下两条扫描线上位于区域内的区段(需要填充的区间),从这些区间上各取⼀个种⼦点依次把它们存起来,作为下次填充的种⼦点。
计算机图形学-区域填充

实验(No. 4)题目:区域填充实验目的及要求:一、实验目的:掌握基本光栅图形的生成原理和算法。
使用Visual C++实现区域填充。
二、实验要求:1 在OnDraw函数里绘制一个欲填充的多边形区域;--用CPen类;2 在C**View类里添加一个实现函数FloodFill4;-p443 在OnDraw函数里确定种子点,调用该实现函数;4 每人单独完成实验,多边形的边的数目和座标不能和例子相同。
5 不要取中文类名;工程(project)以自己名字全拼和/或学号来命名;6 思考能否像实验五那样,做出一个菜单来实现填充算法?三、实验设备:微机,Visual C++6.0四、实验内容及步骤:1 打开VC,打开原来建立的工程CG。
文件名为cg.dsw。
2 在CGView.cpp文件中(类CCGView中),添加函数void FloodFill4(int x, int y, COLORREFoldColor, COLORREF newColor)。
方法如下:在VC界面的ClassView中,右键单击CCGView 类,出现图1所示的界面,点击“Add Member Function…”菜单,出现图2的对话框,在其中分别完成对函数FloodFill的定义和声明。
图1图23 在函数体void FloodFill4(int x, int y, COLORREF oldColor, COLORREF newColor)中,实现这个内点表示的4连通区域的递归填充算法。
具体代码参考教材P44。
需要注意的是,FloodFill4函数中调用GetPixel和SetPixel函数之前需要得到设备上下文,即在函数体前部加入如下代码:CDC *pDC = this->GetDC();调用方式为:COLORREF color = pDC->GetPixel(x,y);同理,调用SetPixel函数的方式为:pDC->SetPixel(x,y,newColor);并在FloodFill4函数结尾前加上代码:this->ReleaseDC(pDC);以释放DC,避免对资源的过多占用。
计算机图形学--第四讲 区域填充算法

任课教师:李陶深教授tshli@12直线生成算法圆与椭圆的绘制算法5图元的概念436区域填充算法裁剪反走样技术4.4 区域填充算法4.4 区域填充算法—基础知识(3)线框多边形物体:只需扫描转换线段填充多边形物体:要扫描转换多边形本质:点阵表示。
特点:面着色,画面明暗自然、色彩丰富。
4.4 区域填充算法4.4 区域填充算法—基础知识(4)图形学中多边形的两种表示方式顶点表示:用多边形的有序顶点序列表示多边形点阵表示:用位于多边形内部的像素集合来表示多边形4.4 区域填充算法多边形边界的矢量形式数据之上,可用于程序填色,也可用于交互填色。
形边界的图像形式数据之上,并还需提供多边形边界内一点的坐标。
概括地说,该算法先画边界,然后对内定义区域填充。
所以,它一般只能用于人机交互填色,而难以用于程序填色。
4.4 区域填充算法—多边形填色算法的问题多边形填色算法面临的一个首要问题,是判断一个像素是在多边形内还是多边形外。
Question1: How to Judge…?Question2: How to improve …?图4.14 射线法图4.15 转角法4.4 区域填充算法4.4 区域填充算法4.4 区域填充算法4.4 区域填充算法4.4 区域填充算法大量的求交、乘除运算4.4 区域填充算法—扫描线填色算法(1)基本思路:扫描线算法按扫描线的顺序计算出扫描线与多边形的相交区间,然后用要求的颜色填充这些区间内的像素。
该算法利用了扫描线的连续性和边的连续性,避免对像素的逐点判断和反复求交运算,减少了计算量,提高了算法速度。
具体处理过程:先求出扫描线与多边形边的交点,利用扫描线的连续性求出多边形与扫描线相交的连续区域,然后利用多边形边的连续性,求出下一条扫描线与多边形的交点,对所有扫描线由上到下依次处理。
4.4 区域填充算法—扫描线填色算法(2) 算法实现的步骤:对每一条扫描线执行如下四步:(1) 求交:求扫描线与多边形各边的交点;(2) 排序:将求得的交点按递增顺序进行排序;(3) 交点配对:确定相交区间;(4) 区间填色:将相交区间内的像素置成多边形色, 相交区间外的像素置成背景色。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
非零环绕数规则
• 环绕数初始为零; • 从位置P作不经过 顶点的射线; • 多边形从右至左穿 过射线,加1; • 多边形从左至右穿 过射线,减1; • 非零为内部点;
举例:
种子填充算法
区域:用点阵形式表示的填充图形,是像素的集合。 区域可采用内点表示和边界表示两种表示形式。 内点表示法,指区域内的所有像素着同一颜色;
i1
a
i1
i
a
其中 x
b 为常数, a
交点数的处理
• 多边形Pi的顶点可分为两类:如果(yi-1 - yi)(yi+1 - yi)≥0,则称顶点Pi为局 部最高点或最低点,按两个交点计算, 否则按一个交点计算。
水平边处理
• 不计算水平边和 扫描线的交点
数据结构与实现步骤
• 输入参数 顶点数和顶点坐标 • 数据结构 有序边表 活化边表
(1) 求交:计算扫描线与多边形各边的交点;
(2) 排序:把所有交点按x值递增顺序排序; (3) 配对:第一个与第二个,第三个与第四 个等等;每对交点代表扫描线与多边形的一 个相交区间;
(4)填色:把相交区间内的像素置成多边形 颜色,把相交区间外的像素置成背景色。
交点计算
为了提高速度,假定当前扫描线与多边形某一条边的交点的 x 坐标为 xi ,则下一条扫描线与该边的交点不要重新计算,而是 通过增加一个增量△x来获得。具体方法是: 设该边的直线方程为:ax+by+c=0; 若y=yi,x=xi;则当y = yi+1= yi+1时, yi+1 yi b 1 (xi,yi) x ( b y c ) x ;
扫描线种子填充算法
简单种子填充算法原理和程序都很简单,但由于多次递归, 费时、费内存,效率不高。为了减少递归次数,提高效率可 以采用扫描线种子填充算法。 算法的基本过程如下:当给定种子点 (x,y)时,首先填充种子 点所在扫描线上的位于给定区域的一个区段,然后确定与这 一区段相连通的上、下两条扫描线上位于给定区域内的区段, 并依次保存下来。反复这个过程,直到填充结束。
活化边表
把与当前扫描线 相交的边称为活 化边,并把它们 按与扫描线交点 X坐标递增的顺 序存放在一个链 表中,形成活化 边表。
表结构
算法中采用较灵活的数据结构。它由边的分有序边 表ET(Edge Table)和边的活化边表AEL(Active Edge Table )两部分组成。 表结构ET和AET中的基本元素为多边形的边。边 的结构由以下四个域组成: ymax 边的上端点的y坐标; x 在ET中表示边的下端点的x坐标,在 AET中则表示边与扫描线的交点的坐标; Δx 边的斜率的倒数; next 指向下一条边的指针。
第四讲 区域填充
计算机图形学中,多边形区域有两种重要的表示方法:顶 点表示和点阵表示。 所谓顶点表示,即是用多边形的顶点序列来表示多边形。 这种表示直观、几何意义强、占内存少,易于进行几何变换, 但由于它没有明确指出哪些像素在多边形内,故不能直接用 于区域填充。 所谓点阵表示,则是用位于多边形内的像素集合来刻画多 边形。这种表示丢失了许多几何信息,但便于进行填充。 根据区域的定义,可以采用不同的填充算法,其中最具代表 性的是:适应于顶点表示的扫描线类算法和适应于点阵表示 的种子填充类算法。
P 2 P1
扫描线6
7
2
4
7
8
-1
^
多边形填充的算法流程
void polyfill (polygon, color)
{
for (各条扫描线,标识为i) { 初始化新边表头指针NET [i]; 把ymin = i 的边放进边表NET [i]; } y = 最低扫描线号; 初始化活性边表AET为空; for (各条扫描线i)
举例:
有序边表
^ 7
P5 P1
2 4 ^
P2 P1 7 ^ 6 ^ 3 8 -1 ^
(Ymax, x,Δ x, next)
P4 P5 2 0 ^ P3 P2 5 6 0.5 ^
P3 P4 6 -2
•活化边表
AET指针 扫描线1 3
P3 P4 6 -2 P3 P4 5
P3 P2 6 0.5 P3 P2 ^
•简单边界 通过扫描线与边 界交点确定填充 区域
•复杂边界 从内部指定位置 开始填充,递归 填充直至边界
扫描线算法
扫描线多边形区域填充算法基本原理是,待填充区域按 Y方向 (X 方向亦可 ) 扫描线顺序扫描生成。具体实现时,首先按扫描 线顺序,计算扫描线与多边形的相交区间;再用要求的颜色填 充这些区间内的像素,即完成这一条扫描线的填充工作。区间 的端点可以通过计算扫描线与多边形边界线的交点获得。对于 一条扫描线,多边形的填充过程可以分为四个步骤:
} }
扫描线算法
• 特点:算法效率较高。
• 缺点:对各种表的维持和排序开销太 大,适合软件实现而不适合硬件实现。
内外测试
• 目标:鉴别非标准多边形的内部区域 自相交多边形。 • 方法 奇偶规则 非零环绕数规则
奇偶规则
• 从位置P作不经 过顶点的射线 • 计算射线穿过多 边形的边数 • 奇数为内部点, 否则为外部点
4 3 2 1
5 4 3 2 1
5 4 3 2 1
4 3 2 1
4 3 2 1
3 2 1
区域填充的扫描线算法可由下列四个步骤实现:
(1)初始化:堆栈置空。将种子点(x,y)入栈;
(2)出栈:若栈空则结束。否则取栈顶元素(x,y),以y作为当 前扫描线;
(3)填充并确定种子点所在区段:从种子点(x,y)出发,沿当 前扫描线向左、右两个方向填充,直到边界。分别标记区段 的左、右端点坐标为xl和xr; (4)并确定新的种子点:在区间[xl,xr]中检查与当前扫描线y 上、下相邻的两条扫描线上的像素。若存在非边界、未填充 的像素,则把每一区间的最右像素作为种子点压入堆栈,返 回第(2)步。
四个方向运动 八个方向运动
四连通区域
八连通区域
算法原理:
填充区域边界 以一种颜色指 定,从区域的 一个内部点开 始,由内至外 绘制直到边界。 适用于单色边 界的填充。
四连通的局限性
简单的种子填充算法
设(x,y)为内点表示的4连通区域内的一点,oldcolor为区域的 原色,要将整个区域填充为新的颜色newcolor。内点表示的 4连通区域的递归填充算法: void FloodFill4(int x,int y,int oldcolor,int newcolor) { if(GetPixel(x,y)==oldcolor) { SetPixel(x,y,newcolor); FloodFill4(x,y+1,oldcolor,newcolor); FloodFill4(x,y-1,oldcolor,newcolor); FloodFill4(x-1,y,oldcolor,newcolor); FloodFill4(x+1,y,oldcolor,newcolor); } }
有序边表
• 有序边表的构建
按顶点输入顺序依次形 成边,存储到每条最小 Y值所对应的扫描线位 置(水平边除外),相 同最小Y值的边按较低 顶点X值的升序排列。
有序边表结构
typedef struct tEage{ int yUpper; float xIntersect; float dxPerScan; struct tEage *next; } Eage
边界表示法,则是指区域的边界点着同一颜色。
表示内点
表示边界点
区域填充算法要求区域是连通的,因为只有在连通区域中,才 可能将种子点的颜色扩展到区域内的其它点。区域可分为4向 连通区域和8向连通区域。
4向连通区域指的是从区域上一点出发,可通过四个方向, 即上、下、左、右移动的组合,在不越出区域的前提下,到达 区域内的任意像素; 8向连通区域指的是从区域内每一像素出发,可通过八个方 向,即上、下、左、右、左上、右上、左下、右下这八个方向 的移动的组合来到达。
AET指针 扫描线2
AET指针 扫描线3 6
3
4
P 4 P5 2
-2
5
6.5 0.5
P3 P2
^
0
5
7
0.5பைடு நூலகம்
^
(Ymax, x,Δ x, next)
AET指针 扫描线4 AET指针 6
P4 P5 2 0 5
P 3 P2 7.5 0.5 P 2 P1 ^
P4 P5
扫描线5
AET指针
6
2
0
7
8
-1
^
P5 P1
边界表示的4连通区域的递归填充算法:
void BoundaryFill4(int x,int y,int boundarycolor,int newcolor) { int color; if(color!=newcolor && color!=boundarycolor) { SetPixel(x,y,newcolor); BoundaryFill4 (x,y+1, boundarycolor,newcolor); BoundaryFill4 (x,y-1, boundarycolor,newcolor); BoundaryFill4 (x-1,y, boundarycolor,newcolor); BoundaryFill4 (x+1,y, boundarycolor,newcolor); } }
{
把新边表NET[i]中的边结点用插入排序法插入AET表,使之按x坐标递增顺序排列; 遍历AET表,把配对交点区间上的像素(x, y),用drawpixel (x, y, color)改写像素颜色 值; 遍历AET表,把y max= i的结点从AET表中删除,并把y max > i结点的x值递增x;