Как сопоставить координаты в AxesImage с координатами в сохраненном файле изображения? - PullRequest
6 голосов
/ 12 января 2011

Я использую matplotlib , чтобы отобразить матрицу чисел в виде изображения, прикрепить метки вдоль осей и сохранить график в файл PNG. Для создания карты изображения в формате HTML мне нужно знать координаты пикселей в файле PNG для области изображения, отображаемой с помощью imshow.

Я нашел пример того, как сделать это с обычным графиком, но когда я пытаюсь сделать то же самое с imshow, отображение не корректно. Вот мой код, который сохраняет изображение и пытается напечатать пиксельные координаты центра каждого квадрата по диагонали:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
axim = ax.imshow(np.random.random((27,27)), interpolation='nearest')
for x, y in  axim.get_transform().transform(zip(range(28), range(28))):
    print int(x), int(fig.get_figheight() * fig.get_dpi() - y)
plt.savefig('foo.png', dpi=fig.get_dpi())

Вот результирующий файл foo.png, показанный в виде скриншота, чтобы включить линейки:

Screenshot of foo.png with rulers

Вывод сценария начинается и заканчивается следующим образом:

73 55
92 69
111 83
130 97
149 112
…
509 382
528 396
547 410
566 424
585 439

Как вы видите, y-координаты верны, но x-координаты растянуты: они варьируются от 73 до 585 вместо ожидаемых 135 до 506, и они расположены на расстоянии 19 пикселей o.c. вместо ожидаемого 14. Что я делаю не так?

Ответы [ 2 ]

12 голосов
/ 12 января 2011

Это одна из самых запутанных частей в попытке получить точные значения пикселей из matplotlib.Matplotlib отделяет средство визуализации, которое обрабатывает точные значения пикселей от холста, на котором нарисованы фигура и оси.

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

То, что вы делаете, правильно, но оно использует начальный рендер, а не тот, который используется при сохранении фигуры.

Чтобы проиллюстрировать это, вот несколько упрощенная версия вашего кода:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((27,27)), interpolation='nearest')

for i in range(28):
    x, y =  ax.transData.transform_point([i,i])
    print '%i, %i' % (x, fig.bbox.height - y)

fig.savefig('foo.png', dpi=fig.dpi)

Это дает результаты, аналогичные тем, что у вас есть выше: (Различия связаны с различными бэкэндами рендеринга между вашим компьютером.и мой)

89, 55
107, 69
125, 83
...
548, 410
566, 424
585, 439

Однако, если мы сделаем то же самое, но вместо этого нарисуем фигуру перед отображением координат, мы получим правильный ответ!

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((27,27)), interpolation='nearest')

fig.canvas.draw()

for i in range(28):
    x, y =  ax.transData.transform_point([i,i])
    print '%i, %i' % (x, fig.bbox.height - y)

fig.savefig('foo.png', dpi=fig.dpi)

Это дает:(Имейте в виду, что край фигуры находится в <-0.5, -0.5> в координатах данных, а не <0, 0>. (Т. Е. Координаты для построенного изображения центрированы по пикселям). Поэтому <0, 0> дает 143, 55, а не135, 48)

143, 55
157, 69
171, 83
...
498, 410
512, 424
527, 439

Конечно, рисование фигуры только для того, чтобы нарисовать ее снова, когда она сохранена, является избыточным и вычислительно дорогим.

Чтобы избежать рисования дважды, вы можете подключить обратный вызовфункция к событию рисования, и вывести свою карту изображения HTML внутри этой функции.В качестве быстрого примера:

import numpy as np
import matplotlib.pyplot as plt

def print_pixel_coords(event):
    fig = event.canvas.figure
    ax = fig.axes[0] # I'm assuming there's only one subplot here...
    for i in range(28):
        x, y = ax.transData.transform_point([i,i])
        print '%i, %i' % (x, fig.bbox.height - y)

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((27,27)), interpolation='nearest')

fig.canvas.mpl_connect('draw_event', print_pixel_coords)

fig.savefig('foo.png', dpi=fig.dpi)

, который дает правильный вывод, при этом рисуя фигуру только один раз, когда она сохраняется:

143, 55
157, 69
171, 83
...
498, 410
512, 424
527, 439

Еще одним преимуществом является то, что вы можете использовать любой dpiв вызове fig.savefig без необходимости предварительно вручную устанавливать dpi объекта fig.Следовательно, при использовании функции обратного вызова вы можете просто сделать fig.savefig('foo.png'), (или fig.savefig('foo.png', dpi=whatever)), и вы получите вывод, который соответствует сохраненному файлу .png.(Значение dpi по умолчанию при сохранении фигуры равно 100, а значение dpi по умолчанию для объекта фигуры равно 80, поэтому сначала нужно было указать значение dpi, равное fig.dpi))

Надеюсь, это хоть немного понятно!

1 голос
/ 12 января 2011

Я не знаю, правильно ли я понимаю, о чем вы просите, но если да, вам нужны координаты пикселей для нижнего левого угла графика (попробуйте, чтобы в единицах графика это было (-0,5, -0.5)), и оттуда вы можете вычислить координаты пикселей для каждого блока, просто со стороной блока в пикселях.

Чтобы получить их, выполните:

x = ax.get_xaxis().get_clip_box().x0
y = ax.get_yaxis().get_clip_box().y1

(я получилзначения 128, 432 для данного изображения)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...