提取matplotlib补丁包围的坐标。

问题描述 投票:3回答:3

我使用matplotlib.patches.ellipse创建了一个椭圆,如下所示:

patch = mpatches.Ellipse(center, major_ax, minor_ax, angle_deg, fc='none', ls='solid', ec='g', lw='3.')

我想要的是这个补丁中包含的所有整数坐标的列表。即如果我要将此椭圆与同一网格上的每个整数点一起绘制,那么椭圆中会包含多少个这些点?

我试过看看我是否可以提取椭圆的方程,这样我就可以遍历每个点,看看它是否属于直线,但我似乎无法找到一个明显的方法来做到这一点,它变得更加复杂,因为主要椭圆的轴可以以任何角度定向。执行此操作的信息必须存储在某个地方的补丁中,但我似乎无法找到它。

对此有任何建议将非常感激。

python matplotlib patch ellipse
3个回答
4
投票

Ellipse对象有一个方法contains_point,如果该点在椭圆中,则返回1,其他为0。

窃听@DrV的答案:

import matplotlib.pyplot as plt
import matplotlib.patches
import numpy as np

# create an ellipse
el = matplotlib.patches.Ellipse((50,-23), 10, 13.7, 30, facecolor=(1,0,0,.2), edgecolor='none')

# calculate the x and y points possibly within the ellipse
y_int = np.arange(-30, -15)
x_int = np.arange(40, 60)

# create a list of possible coordinates
g = np.meshgrid(x_int, y_int)
coords = list(zip(*(c.flat for c in g)))

# create the list of valid coordinates (from untransformed)
ellipsepoints = np.vstack([p for p in coords if el.contains_point(p, radius=0)])

# just to see if this works
fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_artist(el)
ep = np.array(ellipsepoints)
ax.plot(ellipsepoints[:,0], ellipsepoints[:,1], 'ko')
plt.show()

这将给你如下结果:

result image


2
投票

如果你真的想使用matplotlib提供的方法,那么:

import matplotlib.pyplot as plt
import matplotlib.patches
import numpy as np

# create an ellipse
el = matplotlib.patches.Ellipse((50,-23), 10, 13.7, 30, facecolor=(1,0,0,.2), edgecolor='none')

# find the bounding box of the ellipse
bb = el.get_window_extent()

# calculate the x and y points possibly within the ellipse
x_int = np.arange(np.ceil(bb.x0), np.floor(bb.x1) + 1, dtype='int')
y_int = np.arange(np.ceil(bb.y0), np.floor(bb.y1) + 1, dtype='int')

# create a list of possible coordinates
g = np.meshgrid(x_int, y_int)
coords = np.array(zip(*(c.flat for c in g)))

# create a list of transformed points (transformed so that the ellipse is a unit circle)
transcoords = el.get_transform().inverted().transform(coords)

# find the transformed coordinates which are within a unit circle
validcoords = transcoords[:,0]**2 + transcoords[:,1]**2 < 1.0

# create the list of valid coordinates (from untransformed)
ellipsepoints = coords[validcoords]

# just to see if this works
fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_artist(el)
ep = np.array(ellipsepoints)
ax.plot(ellipsepoints[:,0], ellipsepoints[:,1], 'ko')

似乎工作:

(放大显示,即使是悬挂在边缘的点也在里面。)

这里的要点是matplotlib处理椭圆作为变换圆(平移,旋转,缩放,任何仿射)。如果反向应用变换,则结果是原点的单位圆,并且检查点是否在该范围内非常简单。

只是一句警告:get_window_extent可能不是非常可靠,因为它似乎使用圆的样条近似。另外,请参阅tcaswell对渲染器依赖性的评论。

为了找到更可靠的边界框,您可以:

  • 在绘图坐标中创建一个水平和垂直向量(它们的位置不重要,([0,0],[1,0])和([0,0],[0,1])会这样做)
  • 将这些向量转换为椭圆坐标(get_transform等)
  • 在椭圆坐标系中找到(即椭圆是原点周围的单位圆的系统)圆的四个切线与这两个向量平行
  • 找到向量的交叉点(4个交叉点,但2个对角线就足够了)
  • 将交点转换回绘图坐标

这将给出准确的(但当然受数值精度限制)方形边界框。

但是,您可以使用简单的近似值:

  • 所有可能的点都在一个圆的范围内,该圆的中心与椭圆的中心相同,其直径与椭圆长轴的直径相同

换句话说,所有可能的点都在方形边界框内,该方框在x0 + -m / 2,y0 + -m / 2之间,其中(x0,y0)是椭圆的中心,m是长轴。


0
投票

我想提供另一个使用Path对象的contains_points()方法而不是contains_point()的解决方案:

首先获取椭圆的坐标并使其成为Path对象:

elpath=Path(el.get_verts())

(请注意,el.get_paths()由于某种原因不起作用。)

然后调用路径的contains_points()

validcoords=elpath.contains_points(coords)

下面我将比较@ tacaswell的解决方案(方法1),@ Drv's(方法2)和我自己的方法3(方法3)(我将椭圆放大了约5倍):

import numpy
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
from matplotlib.path import Path
import time

#----------------Create an ellipse----------------
el=Ellipse((50,-23),50,70,30,facecolor=(1,0,0,.2), edgecolor='none')

#---------------------Method 1---------------------
t1=time.time()

for ii in range(50):
    y=numpy.arange(-100,50)
    x=numpy.arange(-30,130)
    g=numpy.meshgrid(x,y)
    coords=numpy.array(zip(*(c.flat for c in g)))

    ellipsepoints = numpy.vstack([p for p in coords if el.contains_point(p, radius=0)])

t2=time.time()
print 'time of method 1',t2-t1

#---------------------Method 2---------------------
t2=time.time()

for ii in range(50):
    y=numpy.arange(-100,50)
    x=numpy.arange(-30,130)
    g=numpy.meshgrid(x,y)
    coords=numpy.array(zip(*(c.flat for c in g)))

    invtrans=el.get_transform().inverted()
    transcoords=invtrans.transform(coords)
    validcoords=transcoords[:,0]**2+transcoords[:,1]**2<=1.0
    ellipsepoints=coords[validcoords]

t3=time.time()
print 'time of method 2',t3-t2

#---------------------Method 3---------------------
t3=time.time()

for ii in range(50):
    y=numpy.arange(-100,50)
    x=numpy.arange(-30,130)
    g=numpy.meshgrid(x,y)
    coords=numpy.array(zip(*(c.flat for c in g)))

    #------Create a path from ellipse's vertices------
    elpath=Path(el.get_verts())
    # call contains_points()
    validcoords=elpath.contains_points(coords)
    ellipsepoints=coords[validcoords]

t4=time.time()
print 'time of method 3',t4-t3

#---------------------Plot it ---------------------
fig,ax=plt.subplots()
ax.add_artist(el)
ep=numpy.array(ellipsepoints)
ax.plot(ellipsepoints[:,0],ellipsepoints[:,1],'ko')
plt.show(block=False)

我得到了这些执行时间:

time of method 1 62.2502269745
time of method 2 0.488734006882
time of method 3 0.588987112045

所以contains_point()方法慢得多。坐标变换方法比我的快,但是当你获得不规则形状的轮廓/多边形时,这种方法仍然有效。

最后结果图:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.