Python: найдите контурные линии из matplotlib.pyplot.contour ()

Я пытаюсь найти (но не нарисовать!) Контурные линии для некоторых данных:

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 - это ContourSet, а cn.collections - это массив LineCollection. Я бы подумал, что LineCollection - это массив сегментов линии, но я не могу понять, как извлечь эти сегменты.

Моя конечная цель - создать файл KML, который отображает данные на карте мира, а также контуры для этих данных.

Однако, поскольку некоторые из моих точек данных расположены близко друг к другу, а другие находятся далеко, мне нужны фактические многоугольники (линии линий), составляющие контуры, а не просто растровое изображение контуров.

Я несколько удивлен, что qhull не делает ничего подобного.

Использование ListContourPlot в системе Mathematica и последующий экспорт в формате SVG, но я хочу использовать что-то с открытым исходным кодом.

Я не могу использовать хорошо известный алгоритм CONREC, потому что мои данные не находятся на сетке (не всегда есть несколько значений y для данного значения x, и наоборот).

Решение не обязательно на Python, но должно иметь открытый исходный код и запускаться в Linux.


person    schedule 18.08.2013    source источник


Ответы (4)


Вы можете вернуть вершины, перебирая коллекции и пути и используя метод iter_segments() из matplotlib.path.Path.

Вот функция, которая возвращает вершины как набор вложенных списков контурных линий, сечений контура и массивов вершин 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()

введите описание изображения здесь

person ali_m    schedule 19.08.2013
comment
Это сработало, спасибо! (первый из них; второй не тестировал, но я уверен, что он тоже сработает). Любопытно: для второго решения требуется сетка xy или оно будет работать с произвольными значениями x и y? - person ; 19.08.2013
comment
Вам нужно будет создать сетку, хотя вы всегда можете использовать что-то вроде scipy.interpolate.griddata, чтобы получить это - person ali_m; 19.08.2013
comment
Ожидаете ли вы, что cntr.Cntr () будет быстрее, чем matplotlib.pyplot.contour ()? - person Istopopoki; 06.10.2015
comment
@lstopopoki Это было бы моим наивным предположением, поскольку _cntr не требует дополнительных затрат на построение графика. Если производительность вызывает беспокойство, вам, вероятно, следует использовать оба метода. - person ali_m; 06.10.2015
comment
@ali_m Подход _cntr на моей машине рассчитан примерно на два порядка быстрее. Спасибо, что указали на эту замечательную недокументированную функцию - как вы узнали о ней? - person America; 04.02.2016
comment
У меня были проблемы с этим методом, когда iter_segments () не видел все вершины. Итак, мой простой обходной путь заключался в использовании cn.collections [0] .get_paths () [0] .vertices. Это нормально работало с моим кодом (только один контур, один непрерывный путь) - person Jeff; 10.03.2016
comment
matplotlib._cntr больше не доступен (начиная с matplotlib 2.2), рекомендуемым решением является использование scikit-image.org/docs/dev/auto_examples/edges/ - person Fabzi; 05.03.2018

Кажется, что данные контура находятся в атрибуте .allsegs объекта QuadContourSet, возвращаемого функцией plt.contour().

Атрибут .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. Вы получаете:

контурный график

извлеченные кривые

person Dneis    schedule 28.09.2017


Вершины всех путей могут быть возвращены как массив чисел типа float64 просто через:

cn.allsegs[i][j]  # for element j, in level i

Подробнее:

Просмотр коллекций и извлечение путей и вершин - не самое простое и быстрое занятие. Возвращенный объект 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

person RCCG    schedule 25.11.2019