多邊形填充-活動邊表法

一枚码农發表於2024-07-08

參考文件:
參考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

相關文章