从matplotlib.pyplot.contour()函数中找到等高线

25
我正在尝试找到(但不绘制!)一些数据的等高线。
from pprint import pprint 
import matplotlib.pyplot 
z = [[0.350087, 0.0590954, 0.002165], [0.144522, 0.885409, 0.378515], 
     [0.027956, 0.777996, 0.602663], [0.138367, 0.182499, 0.460879], 
     [0.357434, 0.297271, 0.587715]] 
cn = matplotlib.pyplot.contour(z) 

我知道cn中包含了我想要的等高线,但是我似乎无法找到它们。我尝试了几种方法:
print dir(cn) 
pprint(cn.collections[0]) 
print dir(cn.collections[0]) 
pprint(cn.collections[0].figure) 
print dir(cn.collections[0].figure) 

无济于事。我知道cn是一个ContourSetcn.collections是一个LineCollection数组。我认为LineCollection是一个线段数组,但我无法弄清楚如何提取这些线段。
我的最终目标是创建一个KML文件,在世界地图上绘制数据和相应的等高线。
然而,由于我的数据点有些靠得很近,有些则相距较远,我需要实际构成等高线的多边形(线串),而不仅仅是等高线的栅格化图像。
我对qhull没有像这样的功能感到有些惊讶。
使用Mathematica的ListContourPlot然后导出为SVG可以实现,但我想使用一些开源工具。
我不能使用众所周知的CONREC算法,因为我的数据不在网格上(对于给定的x值,不总是有多个y值,反之亦然)。
解决方案不一定要使用Python,但必须是开源的,并且可以在Linux上运行。
4个回答

26
你可以通过遍历集合和路径并使用matplotlib.path.Pathiter_segments()方法来获取顶点。

这是一个返回顶点作为轮廓线、轮廓部分和x,y顶点数组嵌套列表集合的函数:

import numpy as np

def get_contour_verts(cn):
    contours = []
    # for each contour line
    for cc in cn.collections:
        paths = []
        # for each separate section of the contour line
        for pp in cc.get_paths():
            xy = []
            # for each segment of that section
            for vv in pp.iter_segments():
                xy.append(vv[0])
            paths.append(np.vstack(xy))
        contours.append(paths)

    return contours

编辑:

你也可以使用未记录在案的matplotlib._cntr C模块计算轮廓,而无需绘制任何内容:


from matplotlib import pyplot as plt
from matplotlib import _cntr as cntr

z = np.array([[0.350087, 0.0590954, 0.002165],
              [0.144522,  0.885409, 0.378515],
              [0.027956,  0.777996, 0.602663],
              [0.138367,  0.182499, 0.460879], 
              [0.357434,  0.297271, 0.587715]])

x, y = np.mgrid[:z.shape[0], :z.shape[1]]
c = cntr.Cntr(x, y, z)

# trace a contour at z == 0.5
res = c.trace(0.5)

# result is a list of arrays of vertices and path codes
# (see docs for matplotlib.path.Path)
nseg = len(res) // 2
segments, codes = res[:nseg], res[nseg:]

fig, ax = plt.subplots(1, 1)
img = ax.imshow(z.T, origin='lower')
plt.colorbar(img)
ax.hold(True)
p = plt.Polygon(segments[0], fill=False, color='w')
ax.add_artist(p)
plt.show()

在此输入图片描述


这个方法很管用,谢谢!(第一个方法;第二个还没测试,但我相信它也会有效)。好奇:第二种解决方案是否需要 xy 网格,或者它可以使用任意的 x 和 y 值? - user354134
你认为 cntr.Cntr() 比 matplotlib.pyplot.contour() 更快吗? - Istopopoki
@lstopopoki 这将是我的天真假设,因为“_cntr”不涉及任何绘图开销。如果性能是一个问题,那么你应该比较一下两种方法的时间。 - ali_m
1
仅通过查看contourmatplotlib.contour.QuadContourSet的源代码,您可以使用?? IPython 魔法来检查函数或对象的源代码。请注意,未记录的组件可能会在未来版本的matplotlib中发生不可预知的更改。 - ali_m
4
matplotlib._cntr自matplotlib 2.2版本以来已不再可用,推荐的解决方案是使用http://scikit-image.org/docs/dev/auto_examples/edges/plot_contours.html。 - Fabzi
显示剩余4条评论

4

我建议使用scikit-image的find_contours函数。

该函数返回给定等级的轮廓列表。

matplotlib._cntr从matplotlib v2.2开始已被移除(请参见此处)。


3
似乎轮廓数据在由plt.contour()函数返回的QuadContourSet对象的.allsegs属性中。 .allseg属性是所有水平线(可以在调用plt.contour(X,Y,Z,V)时指定)的列表。对于每个水平线,您都会得到一个n x 2的NumPy数组列表。
plt.figure()
C = plt.contour(X, Y, Z, [0], colors='r')

plt.figure()
for ii, seg in enumerate(C.allsegs[0]):
    plt.plot(seg[:,0], seg[:,1], '.-', label=ii)
plt.legend(fontsize=9, loc='best')

在上面的示例中,只给出了一个等级,因此len(C.allsegs) = 1。你会得到以下内容:
等高线图 提取的曲线

1
所有路径的顶点可以通过以下方式简单地作为float64的numpy数组返回:
vertices = cn.allsegs[i][j]  # for element j, in level i

在原始问题中,cn 定义为:

import matplotlib.pyplot as plt
z = [[0.350087, 0.0590954, 0.002165], [0.144522, 0.885409, 0.378515], 
     [0.027956, 0.777996, 0.602663], [0.138367, 0.182499, 0.460879], 
     [0.357434, 0.297271, 0.587715]] 
cn = plt.contour(z) 

更详细的说明:

浏览集合并提取路径和顶点并不是最直接或最快的事情。返回的Contour对象实际上具有通过cs.allsegs获取段的属性,它返回一个嵌套列表,形状为[level][element][vertex_coord]:

num_levels = len(cn.allsegs)
num_element = len(cn.allsegs[0])  # in level 0
num_vertices = len(cn.allsegs[0][0])  # of element 0, in level 0
num_coord = len(cn.allsegs[0][0][0])  # of vertex 0, in element 0, in level 0

请参考: https://matplotlib.org/3.1.1/api/contour_api.html

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接