【问题标题】:What's the fastest way of checking if a point is inside a polygon in python检查点是否在python中的多边形内的最快方法是什么
【发布时间】:2016-07-23 19:14:11
【问题描述】:

我发现了两种主要的方法来查看一个点是否属于多边形。一种是使用光线追踪方法here,这是最推荐的答案,另一种是使用matplotlib path.contains_points(这对我来说似乎有点晦涩难懂)。我将不得不连续检查很多点。有谁知道这两个是否比另一个更值得推荐,或者是否有更好的第三种选择?

更新:

我检查了这两种方法,matplotlib 看起来要快得多。

from time import time
import numpy as np
import matplotlib.path as mpltPath

# regular polygon for testing
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)[:-1]]

# random points set of points to test 
N = 10000
points = np.random.rand(N,2)


# Ray tracing
def ray_tracing_method(x,y,poly):

    n = len(poly)
    inside = False

    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside

start_time = time()
inside1 = [ray_tracing_method(point[0], point[1], polygon) for point in points]
print("Ray Tracing Elapsed time: " + str(time()-start_time))

# Matplotlib mplPath
start_time = time()
path = mpltPath.Path(polygon)
inside2 = path.contains_points(points)
print("Matplotlib contains_points Elapsed time: " + str(time()-start_time))

给出,

Ray Tracing Elapsed time: 0.441395998001
Matplotlib contains_points Elapsed time: 0.00994491577148

使用三角形而不是 100 边多边形获得了相同的相对差异。我也会检查一下,因为它看起来是专门解决这类问题的包

【问题讨论】:

  • 由于 matplotlib 的实现是 C++,您可能会期望它更快。考虑到 matplotlib 的使用非常广泛,而且这是一个非常基本的功能 - 假设它正常工作也可能是安全的(即使它看起来“晦涩难懂”)。最后但同样重要的是:为什么不简单地测试一下呢?
  • 我用测试更新了问题,正如你所预测的,matplotlib 更快。我很担心,因为 matplotlib 在我看过的不同地方并不是最著名的响应,我想知道我是否忽略了某些东西(或一些更好的包)。对于这样一个 simple 的问题,matplotlib 看起来也是个大人物。
  • 这个算法是错误的。它不适用于这种情况:polygon = np.array([[0, 0],[1, 0],[ 0, 1],[ 1, 1]])points = np.array([[0.5, 0.5]]) 只有 matplotlib.path 返回正确的结果。

标签: python matplotlib


【解决方案1】:

Even-odd rule 的纯 numpy 矢量化实现

其他答案要么是缓慢的 python 循环,要么需要外部依赖或 cython 处理。

import numpy as np
        
def points_in_polygon(polygon, pts):
    pts = np.asarray(pts,dtype='float32')
    polygon = np.asarray(polygon,dtype='float32')
    contour2 = np.vstack((polygon[1:], polygon[:1]))
    test_diff = contour2-polygon
    mask1 = (pts[:,None] == polygon).all(-1).any(-1)
    m1 = (polygon[:,1] > pts[:,None,1]) != (contour2[:,1] > pts[:,None,1])
    slope = ((pts[:,None,0]-polygon[:,0])*test_diff[:,1])-(test_diff[:,0]*(pts[:,None,1]-polygon[:,1]))
    m2 = slope == 0
    mask2 = (m1 & m2).any(-1)
    m3 = (slope < 0) != (contour2[:,1] < polygon[:,1])
    m4 = m1 & m3
    count = np.count_nonzero(m4,axis=-1)
    mask3 = ~(count%2==0)
    mask = mask1 | mask2 | mask3
    return mask

    
N = 1000000
lenpoly = 1000
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)]
polygon = np.array(polygon,dtype='float32')
points = np.random.uniform(-1.5, 1.5, size=(N, 2)).astype('float32')
mask = points_in_polygon(polygon, points)

100 万个多边形大小为 1000 的点耗时 44 秒。

它比其他实现慢几个数量级,但仍然比 python 循环快,并且只使用 numpy。

【讨论】:

  • 如果您可以使用库,请参阅opencv stackoverflow.com/a/50670359/11637415 的用法。
  • @GeneralCode python opencv 的实现对于测试单点来说速度很快(甚至比我的代码还快)。但是运行 100 万个点将永远处于一个循环中。我创建了这个算法来批量计算循环中的 bc opencv 太慢了,我无法测试 1000 个点
【解决方案2】:

如果您需要速度并且额外的依赖项不是问题,您可能会发现numba 非常有用(现在它很容易在任何平台上安装)。您提出的经典ray_tracing 方法可以通过使用numba @jit 装饰器并将多边形转换为numpy 数组轻松移植到numba。代码应如下所示:

@jit(nopython=True)
def ray_tracing(x,y,poly):
    n = len(poly)
    inside = False
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside

第一次执行将比任何后续调用花费一点时间:

%%time
polygon=np.array(polygon)
inside1 = [numba_ray_tracing_method(point[0], point[1], polygon) for 
point in points]

CPU times: user 129 ms, sys: 4.08 ms, total: 133 ms
Wall time: 132 ms

其中,编译后会减少到:

CPU times: user 18.7 ms, sys: 320 µs, total: 19.1 ms
Wall time: 18.4 ms

如果您在第一次调用函数时需要速度,您可以使用pycc 预编译模块中的代码。将函数存储在 src.py 中,例如:

from numba import jit
from numba.pycc import CC
cc = CC('nbspatial')


@cc.export('ray_tracing',  'b1(f8, f8, f8[:,:])')
@jit(nopython=True)
def ray_tracing(x,y,poly):
    n = len(poly)
    inside = False
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside


if __name__ == "__main__":
    cc.compile()

使用python src.py 构建它并运行:

import nbspatial

import numpy as np
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in 
np.linspace(0,2*np.pi,lenpoly)[:-1]]

# random points set of points to test 
N = 10000
# making a list instead of a generator to help debug
points = zip(np.random.random(N),np.random.random(N))

polygon = np.array(polygon)

%%time
result = [nbspatial.ray_tracing(point[0], point[1], polygon) for point in points]

CPU times: user 20.7 ms, sys: 64 µs, total: 20.8 ms
Wall time: 19.9 ms

在我使用的 numba 代码中: 'b1(f8, f8, f8[:,:])'

为了使用nopython=True进行编译,每个var都需要在for loop之前声明。

在 prebuild src 代码中的行:

@cc.export('ray_tracing' , 'b1(f8, f8, f8[:,:])')

用于声明函数名及其 I/O var 类型,一个布尔输出 b1 和两个浮点数 f8 和一个浮点数的二维数组 f8[:,:] 作为输入。

2021 年 1 月 4 日编辑

对于我的用例,我需要检查多个点是否在单个多边形内 - 在这种情况下,利用 numba 并行功能循环一系列点非常有用。上面的例子可以改成:

from numba import jit, njit
import numba
import numpy as np 

@jit(nopython=True)
def pointinpolygon(x,y,poly):
    n = len(poly)
    inside = False
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in numba.prange(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside


@njit(parallel=True)
def parallelpointinpolygon(points, polygon):
    D = np.empty(len(points), dtype=numba.boolean) 
    for i in numba.prange(0, len(D)):
        D[i] = pointinpolygon(points[i,0], points[i,1], polygon)
    return D    

注意:预编译以上代码不会启用numba的并行能力(pycc/AOT编译不支持并行CPU目标)见:https://github.com/numba/numba/issues/3336

测试:


import numpy as np
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)[:-1]]
polygon = np.array(polygon)
N = 10000
points = np.random.uniform(-1.5, 1.5, size=(N, 2))

对于 72 核机器上的 N=10000,返回:

%%timeit
parallelpointinpolygon(points, polygon)
# 480 µs ± 8.19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

21 年 2 月 17 日编辑:

  • 修复循环从0 开始而不是1(感谢@mehdi):

for i in numba.prange(0, len(D))

21 年 2 月 20 日编辑:

跟进@mehdi 的比较,我在下面添加了一个基于 GPU 的方法。它使用point_in_polygon 方法,来自cuspatial 库:

    import numpy as np
    import cudf
    import cuspatial

    N = 100000002
    lenpoly = 1000
    polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in 
    np.linspace(0,2*np.pi,lenpoly)]
    polygon = np.array(polygon)
    points = np.random.uniform(-1.5, 1.5, size=(N, 2))


    x_pnt = points[:,0]
    y_pnt = points[:,1]
    x_poly =polygon[:,0]
    y_poly = polygon[:,1]
    result = cuspatial.point_in_polygon(
        x_pnt,
        y_pnt,
        cudf.Series([0], index=['geom']),
        cudf.Series([0], name='r_pos', dtype='int32'), 
        x_poly, 
        y_poly,
    )

在@Mehdi 比较之后。对于N=100000002lenpoly=1000 - 我得到以下结果:

 time_parallelpointinpolygon:         161.54760098457336 
 time_mpltPath:                       307.1664695739746 
 time_ray_tracing_numpy_numba:        353.07356882095337 
 time_is_inside_sm_parallel:          37.45389246940613 
 time_is_inside_postgis_parallel:     127.13793849945068 
 time_is_inside_rapids:               4.246025562286377

硬件规格:

  • CPU 英特尔至强 E1240
  • GPU Nvidia GTX 1070

注意事项:

  • cuspatial.point_in_poligon 方法非常健壮和强大,它提供了处理多个复杂多边形的能力(我猜是以牺牲性能为代价的)

  • numba 方法也可以在 GPU 上“移植” - 看看比较会很有趣,其中包括移植到 @Mehdi 提到的最快方法 cuda(@ 987654360@).

【讨论】:

  • @epifanio,很好的实现,但您的代码并不总是返回正确的答案。来自 post#1 和 matplotlib.path 的原始 ray_tracing_method() 的结果始终匹配。
  • @Mehdi,感谢您的评论-答案中的代码应该复制问题中的代码(来自 post#1 的 ray_tracing_method())-您是否截取了代码以重现不匹配在我可以用来调试问题的两种方法之间?
  • @epifanio,区别在于第一点。使用 np.random.seed(2)。剩下的就是你的代码。这是代码:path = mpltPath.Path(polygon)inside1 = path.contains_points(points)inside2=parallelpointinpolygon(points, polygon)print('number of diffs:',len(inside1) - sum(inside2==inside1))
  • @epifanio,我发现了问题!您错过了 parallelpointinpolygon 中的 1sr 点:for i in numba.prange(1, len(D)):。它必须从零开始。 for i in numba.prange(0, len(D)):
  • 嗨,is_inside_rapids 是什么?我们在图表中看到它,但在文本/代码中没有。 Ty 用于比较。
【解决方案3】:

不同方法的比较

我找到了其他方法来检查一个点是否在多边形内 (here)。我只测试了其中两个(is_inside_sm 和 is_inside_postgis),结果与其他方法相同。

感谢@epifanio,我并行化了代码并将它们与@epifanio 和@user3274748 (ray_tracing_numpy) 方法进行了比较。请注意,这两种方法都有一个错误,所以我修复了它们,如下面的代码所示。

我发现的另一件事是为创建多边形提供的代码不会生成封闭路径np.linspace(0,2*np.pi,lenpoly)[:-1]。因此,上述 GitHub 存储库中提供的代码可能无法正常工作。所以最好创建一个封闭路径(第一点和最后一点应该相同)。

代码

方法一:parallelpointinpolygon

from numba import jit, njit
import numba
import numpy as np 

@jit(nopython=True)
def pointinpolygon(x,y,poly):
    n = len(poly)
    inside = False
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in numba.prange(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside


@njit(parallel=True)
def parallelpointinpolygon(points, polygon):
    D = np.empty(len(points), dtype=numba.boolean) 
    for i in numba.prange(0, len(D)):   #<-- Fixed here, must start from zero
        D[i] = pointinpolygon(points[i,0], points[i,1], polygon)
    return D  

方法二: ray_tracing_numpy_numba

@jit(nopython=True)
def ray_tracing_numpy_numba(points,poly):
    x,y = points[:,0], points[:,1]
    n = len(poly)
    inside = np.zeros(len(x),np.bool_)
    p2x = 0.0
    p2y = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        idx = np.nonzero((y > min(p1y,p2y)) & (y <= max(p1y,p2y)) & (x <= max(p1x,p2x)))[0]
        if len(idx):    # <-- Fixed here. If idx is null skip comparisons below.
            if p1y != p2y:
                xints = (y[idx]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
            if p1x == p2x:
                inside[idx] = ~inside[idx]
            else:
                idxx = idx[x[idx] <= xints]
                inside[idxx] = ~inside[idxx]    

        p1x,p1y = p2x,p2y
    return inside 

方法三: Matplotlib contains_points

path = mpltPath.Path(polygon,closed=True)  # <-- Very important to mention that the path 
                                           #     is closed (default is false)

方法四: is_inside_sm(从here得到)

@jit(nopython=True)
def is_inside_sm(polygon, point):
    length = len(polygon)-1
    dy2 = point[1] - polygon[0][1]
    intersections = 0
    ii = 0
    jj = 1

    while ii<length:
        dy  = dy2
        dy2 = point[1] - polygon[jj][1]

        # consider only lines which are not completely above/bellow/right from the point
        if dy*dy2 <= 0.0 and (point[0] >= polygon[ii][0] or point[0] >= polygon[jj][0]):

            # non-horizontal line
            if dy<0 or dy2<0:
                F = dy*(polygon[jj][0] - polygon[ii][0])/(dy-dy2) + polygon[ii][0]

                if point[0] > F: # if line is left from the point - the ray moving towards left, will intersect it
                    intersections += 1
                elif point[0] == F: # point on line
                    return 2

            # point on upper peak (dy2=dx2=0) or horizontal line (dy=dy2=0 and dx*dx2<=0)
            elif dy2==0 and (point[0]==polygon[jj][0] or (dy==0 and (point[0]-polygon[ii][0])*(point[0]-polygon[jj][0])<=0)):
                return 2

        ii = jj
        jj += 1

    #print 'intersections =', intersections
    return intersections & 1  


@njit(parallel=True)
def is_inside_sm_parallel(points, polygon):
    ln = len(points)
    D = np.empty(ln, dtype=numba.boolean) 
    for i in numba.prange(ln):
        D[i] = is_inside_sm(polygon,points[i])
    return D  

方法五: is_inside_postgis(来自here

@jit(nopython=True)
def is_inside_postgis(polygon, point):
    length = len(polygon)
    intersections = 0

    dx2 = point[0] - polygon[0][0]
    dy2 = point[1] - polygon[0][1]
    ii = 0
    jj = 1

    while jj<length:
        dx  = dx2
        dy  = dy2
        dx2 = point[0] - polygon[jj][0]
        dy2 = point[1] - polygon[jj][1]

        F =(dx-dx2)*dy - dx*(dy-dy2);
        if 0.0==F and dx*dx2<=0 and dy*dy2<=0:
            return 2;

        if (dy>=0 and dy2<0) or (dy2>=0 and dy<0):
            if F > 0:
                intersections += 1
            elif F < 0:
                intersections -= 1

        ii = jj
        jj += 1

    #print 'intersections =', intersections
    return intersections != 0  


@njit(parallel=True)
def is_inside_postgis_parallel(points, polygon):
    ln = len(points)
    D = np.empty(ln, dtype=numba.boolean) 
    for i in numba.prange(ln):
        D[i] = is_inside_postgis(polygon,points[i])
    return D  

基准测试

1000万点的时间:

parallelpointinpolygon Elapsed time:      4.0122294425964355
Matplotlib contains_points Elapsed time: 14.117807388305664
ray_tracing_numpy_numba Elapsed time:     7.908452272415161
sm_parallel Elapsed time:                 0.7710440158843994
is_inside_postgis_parallel Elapsed time:  2.131121873855591

这里是代码。

import matplotlib.pyplot as plt
import matplotlib.path as mpltPath
from time import time
import numpy as np

np.random.seed(2)

time_parallelpointinpolygon=[]
time_mpltPath=[]
time_ray_tracing_numpy_numba=[]
time_is_inside_sm_parallel=[]
time_is_inside_postgis_parallel=[]
n_points=[]

for i in range(1, 10000002, 1000000): 
    n_points.append(i)
    
    lenpoly = 100
    polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)]
    polygon = np.array(polygon)
    N = i
    points = np.random.uniform(-1.5, 1.5, size=(N, 2))
    
    
    #Method 1
    start_time = time()
    inside1=parallelpointinpolygon(points, polygon)
    time_parallelpointinpolygon.append(time()-start_time)

    # Method 2
    start_time = time()
    path = mpltPath.Path(polygon,closed=True)
    inside2 = path.contains_points(points)
    time_mpltPath.append(time()-start_time)

    # Method 3
    start_time = time()
    inside3=ray_tracing_numpy_numba(points,polygon)
    time_ray_tracing_numpy_numba.append(time()-start_time)

    # Method 4
    start_time = time()
    inside4=is_inside_sm_parallel(points,polygon)
    time_is_inside_sm_parallel.append(time()-start_time)

    # Method 5
    start_time = time()
    inside5=is_inside_postgis_parallel(points,polygon)
    time_is_inside_postgis_parallel.append(time()-start_time)


    
plt.plot(n_points,time_parallelpointinpolygon,label='parallelpointinpolygon')
plt.plot(n_points,time_mpltPath,label='mpltPath')
plt.plot(n_points,time_ray_tracing_numpy_numba,label='ray_tracing_numpy_numba')
plt.plot(n_points,time_is_inside_sm_parallel,label='is_inside_sm_parallel')
plt.plot(n_points,time_is_inside_postgis_parallel,label='is_inside_postgis_parallel')
plt.xlabel("N points")
plt.ylabel("time (sec)")
plt.legend(loc = 'best')
plt.show()

结论

最快的算法是:

1- is_inside_sm_parallel

2- is_inside_postgis_parallel

3-parallelpointinpolygon (@epifanio)

【讨论】:

  • 干得好@Mehdi,您也有兴趣测试 GPU 版本吗?我想在 cudf datfarme 中存储的一组点上应用 point_in_polygon 方法是否有任何加速。我还注意到来自rapidsaicuspatial 在多边形方法中有一个我尚未测试的点。
  • 我想测试它,但我目前无法访问 cuda gpu。
  • 一个免费的 google-colab 实例支持 GPU - 如果有兴趣,我们可以共享一个笔记本在那里进行比较。
  • 看起来很有趣。
  • 我从cuspatial 库中添加了point_in_polygon 方法。 (100000000 分) - 我将在答案中添加相关代码和图片。剩下的就是尝试numba-cuda 方法。
【解决方案4】:

我就放在这里吧,只是用numpy重写了上面的代码,也许有人觉得它有用:

def ray_tracing_numpy(x,y,poly):
    n = len(poly)
    inside = np.zeros(len(x),np.bool_)
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        idx = np.nonzero((y > min(p1y,p2y)) & (y <= max(p1y,p2y)) & (x <= max(p1x,p2x)))[0]
        if p1y != p2y:
            xints = (y[idx]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
        if p1x == p2x:
            inside[idx] = ~inside[idx]
        else:
            idxx = idx[x[idx] <= xints]
            inside[idxx] = ~inside[idxx]    

        p1x,p1y = p2x,p2y
    return inside    

将ray_tracing包装成

def ray_tracing_mult(x,y,poly):
    return [ray_tracing(xi, yi, poly[:-1,:]) for xi,yi in zip(x,y)]

在 100000 点上测试,结果:

ray_tracing_mult 0:00:00.850656
ray_tracing_numpy 0:00:00.003769

【讨论】:

  • 我怎样才能只返回一个 poly 和一个 x,y 的真或假?
  • 如果你只做一个多边形,我会使用@epifanio 解决方案。 NumPy 解决方案更适合大批量计算。
  • 如果提供工作示例会很棒。不想花 20 分钟来弄清楚您期望的形状。
【解决方案5】:

你的测试很好,但它只测量一些特定的情况: 我们有一个具有许多顶点的多边形,以及用于在多边形内检查它们的长数组。

此外,我想您测量的不是 matplotlib-inside-polygon-method vs ray-method, 但 matplotlib-somehow-optimized-iteration vs simple-list-iteration

让我们进行 N 次独立比较(N 对点和多边形)?

# ... your code...
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)[:-1]]

M = 10000
start_time = time()
# Ray tracing
for i in range(M):
    x,y = np.random.random(), np.random.random()
    inside1 = ray_tracing_method(x,y, polygon)
print "Ray Tracing Elapsed time: " + str(time()-start_time)

# Matplotlib mplPath
start_time = time()
for i in range(M):
    x,y = np.random.random(), np.random.random()
    inside2 = path.contains_points([[x,y]])
print "Matplotlib contains_points Elapsed time: " + str(time()-start_time)

结果:

Ray Tracing Elapsed time: 0.548588991165
Matplotlib contains_points Elapsed time: 0.103765010834

Matplotlib 仍然好很多,但不是好 100 倍。 现在让我们尝试更简单的多边形...

lenpoly = 5
# ... same code

结果:

Ray Tracing Elapsed time: 0.0727779865265
Matplotlib contains_points Elapsed time: 0.105288982391

【讨论】:

    【解决方案6】:

    可以考虑shapely

    from shapely.geometry import Point
    from shapely.geometry.polygon import Polygon
    
    point = Point(0.5, 0.5)
    polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0)])
    print(polygon.contains(point))
    

    根据您提到的方法,我只使用了第二种方法,path.contains_points,效果很好。在任何情况下,根据您测试所需的精度,我建议创建一个 numpy bool 网格,多边形内的所有节点都为 True(如果不是,则为 False)。如果您要对很多点进行测试,这可能会更快(请注意,这依赖于您在“像素”容差范围内进行测试):

    from matplotlib import path
    import matplotlib.pyplot as plt
    import numpy as np
    
    first = -3
    size  = (3-first)/100
    xv,yv = np.meshgrid(np.linspace(-3,3,100),np.linspace(-3,3,100))
    p = path.Path([(0,0), (0, 1), (1, 1), (1, 0)])  # square with legs length 1 and bottom left corner at the origin
    flags = p.contains_points(np.hstack((xv.flatten()[:,np.newaxis],yv.flatten()[:,np.newaxis])))
    grid = np.zeros((101,101),dtype='bool')
    grid[((xv.flatten()-first)/size).astype('int'),((yv.flatten()-first)/size).astype('int')] = flags
    
    xi,yi = np.random.randint(-300,300,100)/100,np.random.randint(-300,300,100)/100
    vflag = grid[((xi-first)/size).astype('int'),((yi-first)/size).astype('int')]
    plt.imshow(grid.T,origin='lower',interpolation='nearest',cmap='binary')
    plt.scatter(((xi-first)/size).astype('int'),((yi-first)/size).astype('int'),c=vflag,cmap='Greens',s=90)
    plt.show()
    

    ,结果是这样的:

    【讨论】:

      猜你喜欢
      • 2014-04-26
      • 1970-01-01
      • 2011-06-17
      • 2020-01-17
      • 1970-01-01
      相关资源
      最近更新 更多