多边形填充-活动边表法

参考文档:
参考1:https://blog.csdn.net/u013044116/article/details/49737585
参考2:https://blog.csdn.net/keneyr/article/details/83747501

算法思想:
对多边形沿y轴从0开始遍历,建立边表NET。只记录顶点的x, dx, ymax。
根据NET构建活动边表AET(activate edge table)。射线与多边形的交点坐标为(x + △x, y + 1), △x是斜率的倒数(dx)。详解见 参考1

实现代码:

点击查看代码
# encoding=utf8
import math

import cv2
import numpy as np


class EdgeTable:
    def __init__(self, x, dx, ymax):
        self.x = x
        self.dx = dx
        self.ymax = ymax
        self.next = None


def fill_polys(polys):
    poly_x_max = float('-inf')
    poly_y_max = float('-inf')
    for poly in polys:
        poly_x_max = max(poly_x_max, poly[0])
        poly_y_max = max(poly_y_max, poly[1])

    poly_x_max += 1
    poly_y_max += 1
    point_count = len(polys)

    # build edge table
    net = [None for _ in range(poly_y_max)]
    for i in range(poly_y_max):
        for j in range(point_count):
            # 一个点跟前面的点形成线段 跟后边的点也形成线段
            if polys[j][1] == i:
                if polys[(j - 1 + point_count) % point_count][1] > polys[j][1]:
                    x_ = polys[j][0]
                    dx_ = (
                        polys[(j - 1 + point_count) % point_count][0] - polys[j][0]
                    ) / (polys[(j - 1 + point_count) % point_count][1] - polys[j][1])
                    ymax_ = polys[(j - 1 + point_count) % point_count][1]
                    pnt = EdgeTable(x_, dx_, ymax_)
                    pnt.next = net[i]
                    net[i] = pnt

                if polys[(j + 1 + point_count) % point_count][1] > polys[j][1]:
                    x_ = polys[j][0]
                    dx_ = (
                        polys[(j + 1 + point_count) % point_count][0] - polys[j][0]
                    ) / (polys[(j + 1 + point_count) % point_count][1] - polys[j][1])
                    ymax_ = polys[(j + 1 + point_count) % point_count][1]
                    pnt = EdgeTable(x_, dx_, ymax_)
                    pnt.next = net[i]
                    net[i] = pnt

    # build activate edge table
    bg = np.zeros((poly_x_max, poly_y_max), dtype=np.uint8)
    aet = EdgeTable(0, 0, 0)
    for i in range(poly_y_max):
        # 计算新的交点 更新aet表
        pa = aet.next
        while pa:
            pa.x = pa.x + pa.dx
            pa = pa.next

        # 更新aet表之后对ate表进行排序
        # 断表排序 不开辟新空间
        pa = aet
        pcur = aet.next
        pa.next = None
        while pcur:
            while pa.next and pcur.x >= pa.next.x:
                pa = pa.next
            pt = pcur.next
            pcur.next = pa.next
            pa.next = pcur
            pcur = pt
            pa = aet

        # net表中的点加入到aet表中,并用插值法按x排序插入
        pn = net[i]
        pa = aet
        while pn:
            while pa.next and pn.x >= pa.next.x:
                pa = pa.next
            pt = pn.next
            pn.next = pa.next
            pa.next = pn
            pn = pt
            pa = aet

        # 填充
        pcur = aet.next
        while pcur and pcur.next:
            # 浮点数转成整数 如果dx是负数 向下取整;如果dx是正数 向上取整
            s = math.ceil(pcur.x) if pcur.dx > 0 else math.floor(pcur.x)
            e = math.ceil(pcur.next.x) if pcur.next.dx > 0 else math.floor(pcur.next.x)
            # for x in range(pcur.x, pcur.next.x+1):
            for x in range(s, e + 1):
                bg[x][i] = 1
            pcur = pcur.next

        # 从aet表中删除ymax==i的结点
        pa = aet
        pcur = pa.next
        while pcur:
            if pcur.ymax == i:
                pa.next = pcur.next
                del pcur
                pcur = pa.next
            else:
                pcur = pcur.next
                pa = pa.next

    print(bg)


def matrix(polys):
    m = np.zeros((10, 10), dtype=np.uint8)
    for x, y in polys:
        m[x, y] = 1
    return m


def cv_fill(polys):
    mask = np.zeros((5, 5), dtype=np.uint8)
    cv2.fillPoly(mask, [np.array(polys)], 1, lineType=cv2.FILLED)
    return mask


if __name__ == "__main__":
    # r = matrix([[8, 8], [2, 7], [5, 5], [2, 2], [5, 1], [9, 3]])
    # print(r)
    polys = [[2, 4], [1, 2], [3, 0], [4, 1], [3, 2], [4, 3]]
    fill_polys(polys)
    # cv_fill(polys)

结果展示:
image

有空洞的情况:

点击查看代码
# encoding=utf8
import math

import cv2
import numpy as np
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
# EdgeTable = namedtuple("EdgeTable", ["x", "dx", "ymax", "next"])
class EdgeTable:
    def __init__(self, x, dx, ymax):
        self.x = x
        self.dx = dx
        self.ymax = ymax
        self.next = None


def fill_polys(polys, num=1):
    poly_collection = []
    each_poly_max_xy = []
    each_poly_min_xy = []
    poly_max_x = float("-inf")
    poly_max_y = float("-inf")

    for poly in polys:
        points = []
        max_x = float("-inf")
        max_y = float("-inf")
        min_x = float("inf")
        min_y = float("inf")
        for point in poly:
            p = Point(*point)
            points.append(p)
            max_x = max(max_x, p.x)
            max_y = max(max_y, p.y)
            min_x = min(min_x, p.x)
            min_y = min(min_y, p.y)
        each_poly_max_xy.append(Point(max_x, max_y))
        each_poly_min_xy.append(Point(min_x, min_y))
        poly_max_x = max(poly_max_x, max_x)
        poly_max_y = max(poly_max_y, max_y)
        poly_collection.append(points)

    poly_len_x = poly_max_x + 1
    poly_len_y = poly_max_y + 1
    bg = np.zeros((poly_len_x, poly_len_y), dtype=np.uint8)

    # build edge table
    count = -1
    for poly in poly_collection:
        count += 1
        point_num = len(poly)
        point_range_y = each_poly_max_xy[count].y + 1
        net = [None] * point_range_y
        for i in range(point_num):
            # 一个点跟前面的点形成线段 跟后边的点也形成线段
            before = (i-1+point_num) % point_num
            after = (i+1+point_num) % point_num
            for nbr in [before, after]:
                if poly[nbr].y > poly[i].y:
                    x_ = poly[i].x
                    dx_ = (poly[nbr].x - poly[i].x) / (poly[nbr].y - poly[i].y)
                    ymax_ = poly[nbr].y
                    pnt = EdgeTable(x_, dx_, ymax_)
                    pnt.next = net[poly[i].y]
                    net[poly[i].y] = pnt

        # build activate edge table
        aet = EdgeTable(0, 0, 0)
        for i in range(each_poly_min_xy[count].y, point_range_y):
            # 计算新的交点 更新aet表
            pa = aet.next
            while pa:
                pa.x = pa.x + pa.dx
                pa = pa.next

            # 更新aet表之后对ate表进行排序
            # 断表排序 不开辟新空间
            pa = aet
            pcur = aet.next
            pa.next = None
            while pcur:
                while pa.next and pcur.x >= pa.next.x:
                    pa = pa.next
                pt = pcur.next
                pcur.next = pa.next
                pa.next = pcur
                pcur = pt
                pa = aet

            # 从aet表中删除ymax==i的结点
            pa = aet
            pcur = pa.next
            # 当 i==poly_y_max 时不删除,否则没法填充 y 最大时的顶点
            # while pcur:
            while pcur and i < each_poly_max_xy[count].y:
                if pcur.ymax == i:
                    pa.next = pcur.next
                    del pcur
                    pcur = pa.next
                else:
                    pcur = pcur.next
                    pa = pa.next

            # net表中的点加入到aet表中,并用插值法按x排序插入
            pn = net[i]
            pa = aet
            while pn:
                while pa.next and pn.x >= pa.next.x:
                    pa = pa.next
                pt = pn.next
                pn.next = pa.next
                pa.next = pn
                pn = pt
                pa = aet

            # 填充
            pcur = aet.next
            while pcur and pcur.next:
                # 浮点数转成整数 如果dx是负数 向下取整;如果dx是正数 向上取整
                s = math.ceil(pcur.x) if pcur.dx > 0 else math.floor(pcur.x)
                e = math.ceil(pcur.next.x) if pcur.next.dx > 0 else math.floor(pcur.next.x)
                # for x in range(pcur.x, pcur.next.x+1):
                for x in range(s, e + 1):
                    bg[x][i] = num - bg[x][i]
                pcur = pcur.next

    print(bg)


def cv_fill(polys):
    mask = np.zeros((5, 5), dtype=np.uint8)
    cv2.fillPoly(mask, [np.array(polys)], 1, lineType=cv2.FILLED)
    return mask


if __name__ == "__main__":
    # r = matrix([[8, 8], [2, 7], [5, 5], [2, 2], [5, 1], [9, 3]])
    # print(r)
    # origin
    # polys = [[[2, 4], [1, 2], [3, 0], [4, 1], [3, 2], [4, 3]]]
    polys = [
        [
            [0, 5],
            [3, 2],
            [6, 1],
            [9, 3],
            [6, 4],
            [9, 3],
            [6, 7],
            [9, 8],
            [4, 10],
            [2, 9],
        ],
        [[2, 6], [4, 4], [5, 5], [4, 7]],
    ]
    fill_polys(polys)
    # cv_fill(polys)

结果展示

image

posted @ 2024-07-08 10:27  一枚码农  阅读(17)  评论(0编辑  收藏  举报