多边形的扫描转化算法

2023-11-08

多边形的扫描转化算法(python 实现)


实验目的

实现从多边形顶点表示到点阵表示的转换,从多边形给定的边界出发,通过扫描线的方式求出位于其内部各个像素,从而达到对多边形填充的作用。


算法思想

按扫描线顺序,计算扫描线与多边形的相交的交点,这些交点将扫描线分割成落在多边形内部的线段和落在多边形外部的线段,并且二者相间排列。再用要求的颜色显示这些区间的所有象素。

有效边:指与当前扫描线相交的多边形的边,也称为活性边。

有效边表(AET):把有效边按与扫描线交点 x 坐标递增的顺序存在一个链表中,此链表称为有效边表。只需对当前扫描线的活动边表作更新,即可得到下一条扫描线的活动边表。

为了方便灵活边表的建立与更新,我们为每一条扫描线建立一个新边表NET,用来存放在该扫描线第一次出现的边。

存储内容为:
ymax:边的上端点的 y 坐标;
x:在 ET 中表示边的下端点的 x 坐标,在 AEL 中则表示边与扫描线的交点的坐标;
Δx:边的斜率的倒数;next:指向下一条边的指针。


算法步骤

1、大致确定多边形的范围,进而确定扫描线的范围

2、初始化并建立 NET 表

3、遍历每一条扫描建立 ET:对于每一个多边形点,寻找与其构成边的 两点,如果寻找到的点在此点的上方(即 y0小于y1),则将此边加入到ET[i]中(i 对应的 y0 的坐标)。这样每次加入的边都是向上,不会重复。

4、置 AET 为空;

5、执行下列步骤直至 NET 和 AET 都为空.
A.更新。如 ET 中的 y 非空,则将其中所有边取出并插入 AET 中;
B.填充。对 AEL 中的边两两配对,每对边中 x 坐标按规则取整,获得有效的填充区段,再填充.
C.排序。如果有新边插入 AET,则对 AET 中各边排序;
D.删除。将 AEL 中满足 y=ymax 边删去(因为每条边被看作下闭上开的)
E.对 AEL 中剩下的每一条边的 x 递增Δx,即 x = x+Δx;
F.将当前扫描线纵坐标 y 值递值 1;


测试代码

根据多边形的分类,用凹多边形测试更具代表性,而且选取的图像具有水平线段,这样更具有良好的测试作用

凹:
polygon=[
[20,20],
[70,100],
[50,80],
[30,120],
[20,50],
[50,50]
]


实验结果与分析
这里写图片描述

空间复杂度:数据结构为链表,链表中存放的是多边形的边长,当多形边数较少时,空间复杂度可忽略不计
时间复杂度:初始化建立 NET 时,需要两个 for 循环遍历每条扫描线与多边形的边长数,O((ymax-ymin)*多边形边长数),当多边形边数较少时,时间复杂度近似为 O(n)。


注意的问题与解决方法

多边形的扫描转化算法是按照扫描线顺序,计算扫描线与多边形的相交区间,来完成填充工作,因为原理比较简单,比较需要考虑的是对一些情况的规定注意的问题:若扫描线与多边形相交的边分处扫描线的两侧,遵循左开右闭的原则,配对交点;若扫描线与多边形顶点相交,则判断此顶点左右两个顶点y1 坐标与此顶点 y 坐标的大小,选择上方顶点(即 y1>y)与此顶点构成的边加入 NET;若扫描线与多边形边界重合,则计一个交点等等。在此编写代码期间,忽略一些简单的编译错误,最值得注意的问题是:

在建立 NET 时,需要通过遍历每条扫描线与多边形的顶点 y 坐标进行匹配,选择其左右顶点坐标 y1>y 的顶点构成的边。这个时候就需要使用 if 语句来判断 y1 与 y 的关系。因为一个顶点与两条边相关联,所以需要用两个 if 做分别来判断。然而我在进行两个 if 判断的时候,没有考虑到重复问题,重复将 NET[i] = SingleLinkList()链表。以至于在扫描线 i 的情况下,同一个 NET[i]链表化了


代码

'''
多边形扫描转换算法
class SingleLinkList:用类代替链表
PoliFill(image, polygon, color):扫描转换

'''


import numpy as np
import matplotlib.pyplot as plt

class Node:
    # 定义节点

    def __init__(self, data):
        self._data = data
        self._next = None

    def get_data(self):
        return self._data

    def get_next(self):
        return self._next

    def set_data(self, ddata):
        self._data = ddata

    def set_next(self, nnext):
        self._next = nnext

class SingleLinkList:
    # 定义链表

    def __init__(self):
        #初始化链表为空
        self._head = None
        self._size = 0

    def get_head(self):
        #获取链表头
        return self._head

    def is_empty(self):
        #判断链表是否为空
        return self._head is None

    def append(self, data):
        #在链表尾部追加一个节点
        temp = Node(data)
        if self._head is None:
            self._head = temp
        else:
            node = self._head
            while node.get_next() is not None:
                node = node.get_next()
            node.set_next(temp)
        self._size += 1

    def remove(self, data):
        # 在链表尾部删除一个节点
        node = self._head
        prev = None
        while node is not None:
            if node.get_data() == data:
                if not prev:
                    # 父节点为None
                    self._head = node.get_next()
                else:
                    prev.set_next(node.get_next())
                break
            else:
                prev = node
                node = node.get_next()
        self._size -= 1


def PoliFill(image, polygon, color):
    l = len(polygon)
    Ymax=0
    Ymin=np.shape(image)[1]
    (width, height) = np.shape(image)
    #求最大最小边
    for [x, y] in enumerate(polygon):
        if y[1] < Ymin:
            Ymin=y[1]
        if y[1] > Ymax:
            Ymax=y[1]

    #初始化并建立NET表
    NET = []
    for i in range(height):
        NET.append(None)


    for i in range(Ymin, Ymax + 1):
        for j in range(0, l):
            if polygon[j][1]==i:
                #左边顶点y是否大于y0
                if(polygon[(j-1+l)%l][1])>polygon[j][1]:
                    [x1,y1]=polygon[(j-1+l)%l]
                    [x0,y0]=polygon[j]
                    delta_x=(x1-x0)/(y1-y0)
                    NET[i] = SingleLinkList()
                    NET[i].append([x0, delta_x, y1])

                # 右边顶点y是否大于y0
                if (polygon[(j+1+l)%l][1])>polygon[j][1]:
                    [x1, y1] = polygon[(j + 1 + l) % l]
                    [x0, y0] = polygon[j]
                    delta_x = (x1 - x0) / (y1 - y0)
                    if(NET[i] is not None):
                        NET[i].append([x0, delta_x, y1])
                    else:
                        NET[i] = SingleLinkList()
                        NET[i].append([x0, delta_x, y1])


    #建立活性边表
    AET = SingleLinkList()
    for y in range(Ymin , Ymax+1):
        # 更新 start_x
        if not AET.is_empty():
            node = AET.get_head()
            while True:
                [start_x,delta_x,ymax] = node.get_data()
                start_x += delta_x
                node.set_data([start_x,delta_x,ymax])
                node = node.get_next()
                if node is None:
                    break

        # 填充
        if not AET.is_empty():
            node = AET.get_head()
            x_list = []
            # 获取所有交点的x坐标
            while True:
                [start_x,_,_] = node.get_data()
                x_list.append(start_x)
                node = node.get_next()
                if node is None:
                    break

            # 排序
            x_list.sort()
            # 两两配对填充
            for i in range(0,len(x_list),2):
                x1 = x_list[i]
                x2 = x_list[i+1]
                for pixel in range(int(x1),int(x2)+1):
                    image[y][pixel] = color

        if not AET.is_empty():
            # 删除非活性边
            node = AET.get_head()
            while True:
                [start_x,delta_x,ymax] = node.get_data()
                if ymax == y:
                    AET.remove([start_x,delta_x,ymax])
                node = node.get_next()
                if node is None:
                    break

        # 添加活性边
        if NET[y] is not None:
            node = NET[y].get_head()
            while True:
                AET.append(node.get_data())
                node = node.get_next()
                if node is None:
                    break


if __name__ == '__main__':
    image = np.ones([150, 150])

    plt.xlim(0,150)
    plt.ylim(0,150)
    polygon = [
        [20, 20],
        [120, 20],
        [70, 100],
        [50, 80],
        [30, 120],
        [20, 50],
        [50, 50]
     ]
    PoliFill(image, polygon,False)
    plt.imshow(image, plt.cm.magma)
    plt.show()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多边形的扫描转化算法 的相关文章

  • 将 Matplotlib 误差线放置在不位于条形中心的位置

    我正在 Matplotlib 中生成带有错误栏的堆积条形图 不幸的是 某些层相对较小且数据多样 因此多个层的错误条可能重叠 从而使它们难以或无法读取 Example 有没有办法设置每个误差条的位置 即沿 x 轴移动它 以便重叠的线显示在彼此
  • OpenCV Python cv2.mixChannels()

    我试图将其从 C 转换为 Python 但它给出了不同的色调结果 In C Transform it to HSV cvtColor src hsv CV BGR2HSV Use only the Hue value hue create
  • 根据列值突出显示数据框中的行?

    假设我有这样的数据框 col1 col2 col3 col4 0 A A 1 pass 2 1 A A 2 pass 4 2 A A 1 fail 4 3 A A 1 fail 5 4 A A 1 pass 3 5 A A 2 fail 2
  • 如何从网页中嵌入的 Tableau 图表中抓取工具提示值

    我试图弄清楚是否有一种方法以及如何使用 python 从网页中的 Tableau 嵌入图形中抓取工具提示值 以下是当用户将鼠标悬停在条形上时带有工具提示的图表示例 我从要从中抓取的原始网页中获取了此网址 https covid19 colo
  • 测试 python Counter 是否包含在另一个 Counter 中

    如何测试是否是pythonCounter https docs python org 2 library collections html collections Counter is 包含在另一个中使用以下定义 柜台a包含在计数器中b当且
  • OpenCV 无法从 MacBook Pro iSight 捕获

    几天后 我无法再从 opencv 应用程序内部打开我的 iSight 相机 cap cv2 VideoCapture 0 返回 并且cap isOpened 回报true 然而 cap grab 刚刚返回false 有任何想法吗 示例代码
  • 如何在Python中获取葡萄牙语字符?

    我正在研究葡萄牙语 角色看起来很奇怪 我怎样才能解决这个问题 代码 import feedparser import random Vou definir os feeds feeds conf feedurl http pplware s
  • 如何在ipywidget按钮中显示全文?

    我正在创建一个ipywidget带有一些文本的按钮 但按钮中未显示全文 我使用的代码如下 import ipywidgets as widgets from IPython display import display button wid
  • 在Python中获取文件描述符的位置

    比如说 我有一个原始数字文件描述符 我需要根据它获取文件中的当前位置 import os psutil some code that works with file lp lib open path to file p psutil Pro
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • 使用 \r 并打印一些文本后如何清除控制台中的一行?

    对于我当前的项目 有一些代码很慢并且我无法使其更快 为了获得一些关于已完成 必须完成多少的反馈 我创建了一个进度片段 您可以在下面看到 当你看到最后一行时 sys stdout write r100 80 n I use 80覆盖最终剩余的
  • 将图像分割成多个网格

    我使用下面的代码将图像分割成网格的 20 个相等的部分 import cv2 im cv2 imread apple jpg im cv2 resize im 1000 500 imgwidth im shape 0 imgheight i
  • 向 Altair 图表添加背景实心填充

    I like Altair a lot for making graphs in Python As a tribute I wanted to regenerate the Economist graph s in Mistakes we
  • 解释 Python 中的数字范围

    在 Pylons Web 应用程序中 我需要获取一个字符串 例如 关于如何做到这一点有什么建议吗 我是 Python 新手 我还没有找到任何可以帮助解决此类问题的东西 该列表将是 1 2 3 45 46 48 49 50 51 77 使用
  • Python:如何将列表列表的元素转换为无向图?

    我有一个程序 可以检索 PubMed 出版物列表 并希望构建一个共同作者图 这意味着对于每篇文章 我想将每个作者 如果尚未存在 添加为顶点 并添加无向边 或增加每个合著者之间的权重 我设法编写了第一个程序 该程序检索每个出版物的作者列表 并
  • 在 Qt 中自动调整标签文本大小 - 奇怪的行为

    在 Qt 中 我有一个复合小部件 它由排列在 QBoxLayouts 内的多个 QLabels 组成 当小部件调整大小时 我希望标签文本缩放以填充标签区域 并且我已经在 resizeEvent 中实现了文本大小的调整 这可行 但似乎发生了某
  • 使用 Python 的 matplotlib 选择在屏幕上显示哪些图形以及将哪些图形保存到文件中

    我想用Python创建不同的图形matplotlib pyplot 然后 我想将其中一些保存到文件中 而另一些则应使用show 命令 然而 show 显示all创建的数字 我可以通过调用来避免这种情况close 创建我不想在屏幕上显示的绘图
  • Rocket UniData/UniVerse:ODBC 无法分配足够的内存

    每当我尝试使用pyodbc连接到 Rocket UniData UniVerse 数据时我不断遇到错误 pyodbc Error 00000 00000 Rocket U2 U2ODBC 0302810 Unable to allocate
  • Python 类继承 - 诡异的动作

    我观察到类继承有一个奇怪的效果 对于我正在处理的项目 我正在创建一个类来充当另一个模块的类的包装器 我正在使用第 3 方 aeidon 模块 用于操作字幕文件 但问题可能不太具体 以下是您通常如何使用该模块 project aeidon P
  • 导入错误:没有名为 site 的模块 - mac

    我已经有这个问题几个月了 每次我想获取一个新的 python 包并使用它时 我都会在终端中收到此错误 ImportError No module named site 我不知道为什么会出现这个错误 实际上 我无法使用任何新软件包 因为每次我

随机推荐

  • 【LeetCode刷题】145 二叉树的后序遍历 java

    题目 给你一棵二叉树的根节点 root 返回其节点值的 后序遍历 示例 方法一 递归 class Solution public List
  • Vite 基本配置及原理

    Vite 基本配置及原理 介绍 vite config js optimizeDeps exclude 不同环境的 vite 配置 css配置 Vite 对 css 的处理 Vite 对 cssmodule 的处理和配置 Vite 对预处理
  • 深入学习jquery源码之查询选择插件的实现

    深入学习jquery源码之上传查询选择插件的实现 function var defaults url fieldCode multi false area 40 80 code code name 注意顺序 先是code 再是name fu
  • linux vscode 下开发

    linux vscode 下开发 java jdk 插件 查看调用层次 java jdk 各种JAVA JDK的镜像分发 编程宝库 技术改变世界 jdk 镜像 ubuntu22 04 安装 Linux x64 64位 jdk 8u351 l
  • 用递归实现输入一系列整数后逆序输出

    对于输入 一系列整数的逆序输出 最容易想到是用堆栈来实现 但是如果是自己去定义堆栈抽象结构 实现堆栈的初始化 Push Pop 以及堆栈的释放等操作 给人以 杀机用牛刀 的感觉 但是 堆栈的想法还是给我们以启迪 要知道 我们可以用堆栈来实现
  • 8位, 16位,24位,32位图片显示原理及对比

    我们都知道一张图片可以保存为很多种不同的格式 比如bmp png jpeg gif等等 这个是从文件格式的角度看 我们抛开文件格式 看图片本身 我们可以分为8位 16位 24位 32位等 单击右键 属性 gt 详细信息即可查看图片位深度 8
  • Multimap运用

    Multimap概念 Multimap的特点其实就是可以包含有几个重复key的value值 你可以put进多个不同的value 但是key相同 但是又不是让后面的覆盖前面的内容 业务场景 当你需要构造像Map
  • 判断无向图G是否连通。若连通返回1,否则返回0

    判断无向图G是否连通 若连通返回1 否则返回0 CODE 判断无向图G是否连通 若连通返回1 否则返回0 define N 1 gt gt 8 代替无穷大 默认 邻接矩阵 define size 6 include
  • Java学习笔记 面向对象(中)

    第五章 面向对象 中 1 封装 2 继承 3 多态 1 封装 有public protect private 三种控制权限 可以修饰类 属性成员 方法 下表为修饰类和类属性成员与方法时 可以被谁访问 类前修饰符 行 类属性成员与方法 列 p
  • gdb调试常见命令详细总结(附示例操作)

    一 简介 通过gdb调试我们可以监控程序执行的每一个细节 包括变量的值 函数的调用过程 内存中数据 线程的调度等 从而发现隐藏的错误或者低效的代码 程序的调试过程主要有 单步执行 跳入函数 跳出函数 设置断点 设置观察点 查看变量 本文将主
  • TurboDX

    应用场景 当需要使用从一个库数据抽取 清洗到另一个库中 需要使用到ETL也就是kettle数据采集工具 但是KETTLE是CS架构的 并且配置流程 配置任务还是比较复杂的 比如配置一个增量更新 那么就需要使用触发器 时间戳 MD5等方式 配
  • matlab 状态方程 仿真,Subspace Identification for Linear Systems 状态空间方程的子空间辨识 matlab工程仿真案例(subspace identif...

    压缩包 ddd1230317d25772a9b2be7a941a514 zip 列表 vanoverschee vanoverschee APPLIC vanoverschee APPLIC appl1 m vanoverschee APP
  • [Vulkan教程] 一: 创建VkDevice

    这个系列的文章是写给对已有的D3D11和GL比较熟悉并理解多线程 资源暂存 同步等概念但想进一步了解它们是如何以Vulkan实现的读者 文章并不追求通俗易懂 里面有很多术语 为了解里面的晦涩内容 建议去阅读Vulkan规范或者更深度的教程
  • 【概率论与数理统计】期末不挂科复习笔记

    概率论与数理统计 期末不挂科复习笔记 只能说最好先看看老师的ppt 在看看猴博士就全懂了 第一章 条件概率 全概率 贝叶斯公式 1 无放回类题目 无放回 直接用C解 2 有放回类题目 有放回 使用 n1 n2 n1 n2 然后乘上每种的概率
  • private static final long serialVersionUID = 1L;是用来做什么的

    private static final long serialVersionUID 1L 是定义以一个序列号 java源码里有大量的类都有这么一个序列号 目的就是把java对象序列化而后进行保存 java的序列化机制式通过判断类的seri
  • IDEA上配置mysql

    IDEA是一个集成工具 它为很多工具提供了快速便捷的配置方式 配置mysql我们只需要添加Database就行了 Database一般是在右侧 找不到的话可以在View中里找到打开 如下图 添加数据库步骤 这里选上mysql 填写相应信息
  • JMeter界面字体大小修改

    1 找到jmeter所在目录 gt bin gt jmeter properties 搜索jsyntaxtextarea font size 去掉 把14改成18 2 修改右侧参数比例 jmeter所在目录 gt bin gt jmeter
  • Spring Boot 整合 Mybatis 实现 Druid 多数据源配置

    目录 1 多数据源的应 场景 2 数据库脚本 3 项目结构 4 代码 依赖 pom xml 配置文件 数据源配置类 实体类 sql映射文件 dao srvice controller 启动类 5 小节 6 事务问解决 1 多数据源的应 场景
  • C++-FFmpeg-1-VS2019-x264-fdk_aac-x265-pdb-QT5.14-makefile

    1 环境搭建 1 1VS2019 用的是控制台编译 1 2 msys2 模拟linux的命令和指令 2 源码编译与安装 2 1 x264 ffmpeg 编码用X264 2 2x265 ffmpeg 编码用X265 c 写的 msys2编译
  • 多边形的扫描转化算法

    多边形的扫描转化算法 python 实现 实验目的 实现从多边形顶点表示到点阵表示的转换 从多边形给定的边界出发 通过扫描线的方式求出位于其内部各个像素 从而达到对多边形填充的作用 算法思想 按扫描线顺序 计算扫描线与多边形的相交的交点 这