带边框的voronoi图求解

带边框的Voronoi图求解问题

Voronoi图的实现原理请自行搜索。基本原理大致为将任意两个障碍做中垂线,所有障碍中垂线的焦点为顶点,顶点以此相连即可得到Voronoi图。

Voronoi图可以使用scipy.spatial中的Voronoi求解,但其求解答案是没有边界显示的Voronoi图。

from scipy.spatial import Voronoi
from scipy.spatial import voronoi_plot_2d

vor = Voronoi(barr_center)
voronoi_plot_2d(vor)
plt.show()

barr_certer表示障碍物二维坐标,仅有x轴与y轴即可。

在这里插入图片描述

如图所示,其中蓝点表示障碍,橙点表示Voronoi所求得的顶点,两个顶点相连得到实线(下图为上图中右侧实线与边界相交部分),如果障碍间存在中垂线(Voronoi的实现原理),但并未与其余中垂线相交,得到的为虚线。
在这里插入图片描述

如果将Voronoi图存在边界,则其边界外的点则不能使用,需要从新求的其边与边界的交点,采用使用Voronoi做路径规划等方面的操作。

在这里插入图片描述

如上图所示,红色边框为所需要的边框,边界与Voronoi图均有交点。本人查阅大量资料,并未找到有某集成程序可以实现,所以自己编写Python程序。如有错误,还请指正。

1. scipy.spatial中的Voronoi函数的了解

由于使用scipy.spatial中的Voronoi实现处理的Voronoi图,所以首先需要了解Voronoi类的结果含义。

python 泰森多边形法函数属性理解(python Voronoi function properties explained in detial)

详细请查看上文,本代码只是用了vor中的vertices、ridge_vertices。

vertices表示Voronoi的各顶点坐标,即橙点部分。

ridge_vertices表示顶点的连接顺序,其中-1表示没有顶点,即上述虚线部分。

2. 求边框Voronoi的总体思路

求解带框的Voronoi图时,不需要将上述scipy.spatial中的Voronoi类的所有参数,只需要确定交点(顶点)以及交点(顶点)连接的顺序即可。所以获得思路:

在这里插入图片描述

ridge_vertices表示的为交点(顶点)的连接顺序,即根据交点(顶点)的顺序编号,在ridge_vertices中记录两个编号的顶点连接。其中-1表示没有交点(顶点),即不存在交点(顶点),但由于Voronoi图的构成原理,而存在的点(构成的为图中的虚线)。

上图的解释:

如果ridge_vertices中存在-1,即表示虚拟点必在边框外(由于是虚拟点,其余边框的交点可以理解为射线,所以可以理解虚拟点在边框外);然后判断另一个点是否在边框内,如果不在,舍去(与边框没有交点);如果在,求其与边框的交点,

如果ridge_vertices中不存在-1,需要判断两个点是否都在边框内。两个都不在直接舍去;两个都在即为图中的实线,易于求解;一个在的话求其余边框的交点即可。

最后需要根据求得的交点顺序求得线的顺序(只需记录首尾交点的顺序即可)。

3. 求解难点

由求解思路可以得到,有三类结果需要处理:1. 舍弃的点。2. 在边框内的点。3. 与边框有交点的点。

其中舍弃的点直接舍弃即可;边框内的点,由于点与连接顺序都已知,所以只需根据记录顺序从新记录即可;最难的为与边框的交点。

与边框的交点分为两类:已知两个点的与边框的交点与只知道一个点的与边框的交点。

已知两个点的与边框交点

求线段与线段交点即可。两点的线段以经确定,所以只需确定其与哪一个边框的线段有交点,然后求得其交点即可得到最终结果。

1. 两线段的交点_qingkong1994的博客-CSDN博客_python 线段交点

2. 判断线段相交的最简方法【Python 实现】_学徒小明的博客-CSDN博客_python 线段相交

参考上述两个网址,求得两线段的交点。

只知道一个点与边框的交点

由于Voronoi图的构建思路为根据两障碍的中垂线画线求得顶点。所以根据Voronoi可以求得该点所在的射线(障碍的中垂线)。但由于射线与线段的交点情况较难解决,所以选择了另一种方法:求直线与每一个边框所在直线的交点,然后求交点和该点与边框所在线段的情况。即将射线转换为线段交点的情况。最终求得交点。

1. 已知俩点求俩点之间的直线,俩点间的中垂线,俩条直线的交点

2. 直线射线线段的相交判断

第一个网址为求中垂线;第二个网址为直线、线段、射线两个分别相交的判断方法,由于较为复杂,没有看懂,仅仅作为参考。

4. 参考代码

由于本问题解决过程走了很多弯路,所以代码可能有很多冗余代码,仅限参考。

import numpy as np
from scipy.spatial import Voronoi
from scipy.spatial import voronoi_plot_2d
import matplotlib.pyplot as plt

# 忽略警告
import warnings
warnings.filterwarnings("ignore")

barr_center = np.load('../data/Python/barr_center.npy')
data = np.load('../data/Python/data.npy')

# 计算Voronoi图
vor = Voronoi(barr_center)

x_min = data[0, 0, 0]
x_max = data[-1,-1,0]
y_min = data[0, 0, 1]
y_max = data[-1, -1, 1]
# bound = [data[0, 0, 0], data[0, 0, 1], data[-1,-1,0], data[-1, -1, 1]]

# 判断是否在框内,若在返回ture
def judge_frame(point):

    x, y = point[0], point[1]
    flag = (x >= x_min and x <= x_max and y >= y_min and y <= y_max)
    return flag

# 判断两线段相交
def judge_inter(A, B, C, D):

    def vector(piont_1, point_2):   # 输入numpy格式
        return point_2 - piont_1

    def vector_product(vec_1, vec_2):   # 计算向量
        return vec_1[0] * vec_2[1] - vec_2[0] * vec_1[1]

    AC = vector(A, C)
    AD = vector(A, D)
    BC = vector(B, C)
    BD = vector(B, D)
    CA = vector(C, A)
    CB = vector(C, B)
    DA = vector(D, A)
    DB = vector(D, B)

    # 相交返回true
    return (vector_product(AC, AD) * vector_product(BC, BD) <= 0 ) \
           and (vector_product(CA, CB) * vector_product(DA, DB) <= 0)


# 判断得出两线段的交点
def intersection(point_1, point_2):
    point_bound = [[[x_min, y_min], [x_max, y_min]], [[x_max, y_min], [x_max, y_max]],
                   [[x_min, y_max], [x_max, y_max]], [[x_min, y_max], [x_min, y_min]]]

    for line in point_bound:
        if judge_inter(point_1, point_2, line[0], line[1]):
            x1, x2, x3, x4 = point_1[0], point_2[0], line[0][0], line[1][0]
            y1, y2, y3, y4 = point_1[1], point_2[1], line[0][1], line[1][1]
            px = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
            py = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
            return [px, py]

# 求射线方程
def ray(barr, piont):

    # 计算障碍的中垂线
    def medLine(x1, y1, x2, y2):
        A = 2 * (x2 - x1)
        B = 2 * (y2 - y1)
        C = x1 ** 2 - x2 ** 2 + y1 ** 2 - y2 ** 2
        return A, B, C
    A, B, C = medLine(barr[0][0], barr[0][1], barr[1][0], barr[1][1])
    return [A, B, C]

# 判断直线与线段相交并求出交点
def ray_inter_point(ray, point, barr):
    point_bound = [[[x_min, y_min], [x_max, y_min]], [[x_max, y_min], [x_max, y_max]],
                   [[x_min, y_max], [x_max, y_max]], [[x_min, y_max], [x_min, y_min]]]

    list_inter = []
    for bound in point_bound:
        sPoint, ePoint = bound[0], bound[1]
        line = [sPoint[1] - ePoint[1], ePoint[0] - sPoint[0], sPoint[0] * ePoint[1] - ePoint[0] * sPoint[1]]

        a0, b0, c0 = ray[0], ray[1], ray[2]
        a1, b1, c1 = line[0], line[1], line[2]

        D = a0 * b1 - a1 * b0
        x = (b0 * c1 - b1 * c0) / D
        y = (a1 * c0 - a0 * c1) / D

        list_inter.append([x, y])

    for i in list_inter:
        if judge_inter(point, np.array(i), barr[0].tolist(), barr[1].tolist()) and judge_frame(np.array(i)):
            return i

# 定义顶点和顶点连接的索引
vertices, ridge_vertices = [], []
new_dict = []
for k,v in vor.ridge_dict.items():
    if -1 in v:
        new_dict.append(list(k))

for ver_rela in vor.ridge_vertices:

    if -1 in ver_rela:
        temp = [0, 1]
        temp.pop(ver_rela.index(-1))
        if judge_frame(vor.vertices[ver_rela[temp[0]]]):
            # 与边界的交点
            barr_ind = new_dict.pop(0)
            barr = [vor.points[barr_ind[0]], vor.points[barr_ind[1]]]
            f_ray = ray(barr, vor.vertices[ver_rela[temp[0]]])  # 改为直线方程
            inter = ray_inter_point(f_ray, vor.vertices[ver_rela[temp[0]]], barr)

            if vor.vertices[ver_rela[temp[0]]].tolist() not in vertices:
                vertices.append(vor.vertices[ver_rela[temp[0]]].tolist())
            start = vertices.index(vor.vertices[ver_rela[temp[0]]].tolist())
            vertices.append(inter)
            end = vertices.index(vertices[-1])
            ridge_vertices.append([start, end])
        else:
            new_dict.pop(0)
            continue
    else:
        # 两个都在
        if judge_frame(vor.vertices[ver_rela[0]]) and judge_frame(vor.vertices[ver_rela[1]]):
            if vor.vertices[ver_rela[0]].tolist() not in vertices:
                vertices.append(vor.vertices[ver_rela[0]].tolist())
            if vor.vertices[ver_rela[1]].tolist() not in vertices:
                vertices.append(vor.vertices[ver_rela[1]].tolist())

            start = vertices.index(vor.vertices[ver_rela[0]].tolist())
            end = vertices.index(vor.vertices[ver_rela[1]].tolist())
            ridge_vertices.append([start, end])

        # 一个都不在
        elif not(judge_frame(vor.vertices[ver_rela[0]]) or judge_frame(vor.vertices[ver_rela[1]])):
            continue

        # 有一个在
        else:
            # 与边界的交点,即判断两线段的交点
            inter = intersection(vor.vertices[ver_rela[0]], vor.vertices[ver_rela[1]])

            # 判断那一点在框内
            if judge_frame(vor.vertices[ver_rela[0]]):
                if vor.vertices[ver_rela[0]].tolist() not in vertices:
                    vertices.append(vor.vertices[ver_rela[0]].tolist())
                    start = vertices.index(vor.vertices[ver_rela[0]].tolist())
                else:
                    start = vertices.index(vor.vertices[ver_rela[0]].tolist())

            else:
                if vor.vertices[ver_rela[1]].tolist() not in vertices:
                    vertices.append(vor.vertices[ver_rela[1]].tolist())
                    start = vertices.index(vor.vertices[ver_rela[1]].tolist())
                else:
                    start = vertices.index(vor.vertices[ver_rela[1]].tolist())
            vertices.append(inter)
            end = vertices.index(vertices[-1])
            ridge_vertices.append([start, end])

vertices = np.array(vertices)
ridge_vertices = np.array(ridge_vertices)

plt.scatter(vertices[:, 0], vertices[:, 1])
for point_ind in ridge_vertices:
    plt.plot([vertices[point_ind[0], 0], vertices[point_ind[1], 0]],
             [vertices[point_ind[0], 1], vertices[point_ind[1], 1]],
             color = 'k')
plt.show()

voronoi_plot_2d(vor)
plt.show()

对代码的输入做少量解释。Voronoi求解是只需要知道障碍坐标即可。输入的barr_center为障碍的坐标数据,data为全部数据,在这里只是求得边界坐标,并无他用。
结果如下图所示

在这里插入图片描述
根据Voronoi图求解的图下图所示。

在这里插入图片描述
可以看出,使用该方法求解的结果是可用的。

5. 参考文献

除了文中引用的网址外,我还找到了一个更优于的求解带边框的网址,但由于其注释为日语,以及引用包的原因,原理被没有特别清楚,仅做记录。

用Python计算并绘制有界(封闭)的Voronoi图

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值