边标志填充算法:
基本原理:考虑每一条水平扫描线与每一条多边形边的交点,将该扫描线上交点右边的所有像素求补。对多边形的每条边作此处理,顺序随意。
本算法的特点:
优点:简单易行;
缺点:对于复杂图形而言,一些像素的颜色值需反复改变多次,并且多边形外的像素处理过多,输入/输出的量比有序边表算法大得多。
边填充算法的改进——栅栏填充算法
栅栏:指的是一条与水平扫描线垂直的直线,栅栏位置通常取过多边形顶点且把多边形分为左右两半。
基本原理:对于每条扫描线与多边形的交点,将交点与栅栏之间的扫描线上的像素取补。若交点位于栅栏左侧,则将交点之右至栅栏之左的所有像素取补;若交点位于栅栏右侧,则将栅栏之右至交点之左的所有像素取补。
栅栏填充算法的特点:
多边形外的像素处理大大减少,被重复取补的像素数目也有减少,但仍有一些像素被重复取补。
算法评价:
可以看到该算法思想简单,实现容易。算法既不需要初始化边表、求交点、交点排序等额外操作,也不需要使用链表、堆栈等数据结构。
需要明确的关键细节问题:
(1) 边标志算法虽然简单,但是使用时对边界的处理要格外小心,否则很容易出错。比如上下顶点之类的局部极值点,会造成标志的奇偶计数不匹配,填充时会出现“抽丝”现象,也就是扫描线上不该填充的区段被填上色,而应该填充的区段却没有填上色。
解决方法:解决的方法是修改边的区间,将y 值大的一端顶点排除在区间之外,也就是删除这条边的终点,即假设本来线段的 y 值的取值范围为 [ 2, 9 ] ,修改后线段的 y 值的取值范围为 [ 2, 9 )。这样在计算交点时,上顶点就计算零个交点,下顶点就计算两个交点,左右顶点就计算一个交点。
体现在本程序中则是:
for y in range(p0.y(), p1.y()):
i = maxy - y
x = int(fx)
for x1 in range(x, maxx):
j = x1 - minx
mat[i][j] = not mat[i][j]
fx = fx + dx
外层 for 循环中 ,y 的取值为 [ p0.y() , p1.y() ) .
(2) 边界像素点重复的问题,多边形的边界是利用直线的生成算法依次画出的,当扫描线遇到斜率小于1的边时,容易产生重
复的边界点,如图所示,引起标志的无序改变,从而造成填充错误。
解决方法:解决的方法是在计算水平扫描线与多边形边的交点时,不直接使用直线的生成算法。而是不管斜率如何,都采用使 y 步增1,计算 x 值的方法,这样就能保证 一条水平扫描线与多变形的某一条边只相交于一点。
核心代码:
(在实施边标志填充算法通常先求出多边形所在图像区域的最小包围盒,以便缩小扫描范围,加快填充速度。)
边标志填充算法:
from PySide2.QtCore import *
class Polygon:
def __init__(self, vs):
self.vertex_pts = vs
def max_x(self):
max1 = 0
for p in self.vertex_pts:
if p.x() > max1:
max1 = p.x()
return max1
def min_x(self):
min1 = self.max_x()
for p in self.vertex_pts:
if p.x() < min1:
min1 = p.x()
return min1
def max_y(self):
max1 = 0
for p in self.vertex_pts:
if p.y() > max1:
max1 = p.y()
return max1
def min_y(self):
min1 = self.max_y()
for p in self.vertex_pts:
if p.y() < min1:
min1 = p.y()
return min1
def pts_sets(self):
pts = [] #多边形像素点集
if len(self.vertex_pts) < 3:
return pts
minx = self.min_x()
miny = self.min_y()
maxx = self.max_x()
maxy = self.max_y()
m = maxy - miny + 1
n = maxx - minx + 1
# 多边形最小包围盒对应的矩阵总的元素个数,这里将最小包围盒中的像素全部初始化为 False
mat = [[False for col in range(n)] for row in range(m)]
#多边形顶点数
for k in range(len(self.vertex_pts)):
# 取多边形中一条边
if k < len(self.vertex_pts) -1 :
p0 = self.vertex_pts[k]
p1 = self.vertex_pts[k+1]
else:
p0 = self.vertex_pts[k]
p1 = self.vertex_pts[0]
if p0.y() > p1.y():
temp = p0
p0 = p1
p1 = temp
print(p0,p1)
if p0.y() != p1.y(): #非水平边
dx = (p1.x()-p0.x())/(p1.y()-p0.y())
fx = p0.x()
for y in range(p0.y(), p1.y()):
i = maxy - y
x = int(fx)
for x1 in range(x, maxx):
j = x1 - minx
mat[i][j] = not mat[i][j]
fx = fx + dx
for i in range(m):
for j in range(n):
if mat[i][j]:
pts.append(QPoint(j+minx, maxy-i))
return pts
边填充算法的改进——栅栏填充算法:
from PySide2.QtCore import *
class Polygon:
def __init__(self, vs):
self.vertex_pts = vs
def max_x(self):
max1 = 0
for p in self.vertex_pts:
if p.x() > max1:
max1 = p.x()
return max1
def min_x(self):
min1 = self.max_x()
for p in self.vertex_pts:
if p.x() < min1:
min1 = p.x()
return min1
def max_y(self):
max1 = 0
for p in self.vertex_pts:
if p.y() > max1:
max1 = p.y()
return max1
def min_y(self):
min1 = self.max_y()
for p in self.vertex_pts:
if p.y() < min1:
min1 = p.y()
return min1
def pts_sets(self):
pts = [] #多边形像素点集
if len(self.vertex_pts) < 3:
return pts
minx = self.min_x()
miny = self.min_y()
maxx = self.max_x()
maxy = self.max_y()
m = maxy - miny + 1
n = maxx - minx + 1
fence = int((self.max_x() - self.min_x()) / 2)
# 多边形最小包围盒对应的矩阵总的元素个数,这里将最小包围盒中的像素全部初始化为 False
mat = [[False for col in range(n)] for row in range(m)]
#多边形顶点数
for k in range(len(self.vertex_pts)):
# 取多边形中一条边
if k < len(self.vertex_pts) -1 :
p0 = self.vertex_pts[k]
p1 = self.vertex_pts[k+1]
else:
p0 = self.vertex_pts[k]
p1 = self.vertex_pts[0]
if p0.y() > p1.y():
temp = p0
p0 = p1
p1 = temp
if p0.y() != p1.y(): #非水平边
dx = (p1.x()-p0.x())/(p1.y()-p0.y())
fx = p0.x()
for y in range(p0.y(), p1.y()):
i = maxy - y
x = int(fx)
if x < fence:
for x1 in range(x, fence):
j = x1 - minx
mat[i][j] = not mat[i][j]
else:
for x1 in range(fence, x):
j = x1 - minx
mat[i][j] = not mat[i][j]
fx = fx + dx
for i in range(m):
for j in range(n):
if mat[i][j]:
pts.append(QPoint(j+minx, maxy-i))
return pts
加上UI界面实现效果:
PS: 如需参考完整代码,请移步:https://download.csdn.net/download/qq_42185999/11862942 进行下载