Pyplot imshow не показывает квадратные пиксели при настройке соотношения сторон

У меня возникли проблемы с использованием Pyplot imshow для построения изображения из numpy ndarray под названием data, сохраняя как соотношение сторон, так и пиксели квадратной формы. Форма ndarray - (112, 2182). Вот код, который я использую:

import matplotlib as mpl
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np

mpl.use('Agg')

import matplotlib.pyplot as plt

data = np.random.random_sample((112, 2182))
plt.figure(figsize=(25, 3.5))
plt.subplots_adjust(left=0.1, right=0.9)

ax = plt.subplot(111)
plt.axis('off')
plt.title('Title', fontweight='bold', y=1.15)

im = ax.imshow(data)

# Add same-width colorbar to the bottom of the image
divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="25%", pad=0.3)
cbar = plt.colorbar(im, orientation="horizontal", cax=cax)

# Save image
plt.savefig('image.pdf', dpi=1000, format=pdf)

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

увеличенный график
весь сюжет

Эти результаты такие же, как у меня, когда я использовал im = ax.imshow(data) без аргумента aspect. Я вручную посчитал пиксели по оси Y всего графика, и их 112, так что дело не в группировке пикселей. Любые идеи о том, как решить эту проблему?

РЕДАКТИРОВАТЬ: изображение, которое я рисую, также будет иметь цветную полосу и заголовок. Кроме того, мне нужно иметь возможность сохранить его в файл PDF. Увеличенный график, который я загрузил, представляет собой снимок экрана, увеличенный в программе просмотра PDF.


person Jorge    schedule 16.01.2019    source источник


Ответы (3)


Если вы используете ax.imshow(data), вы автоматически получите квадратные пиксели. Если вместо этого вы установите аспект, отличный от 1/"equal", вы не получите квадратных пикселей. Это, однако, не зависит от эффекта, наблюдаемого в вопросе.

Проблема в «увеличенном изображении» заключается в том, что размер пикселей данных порядка фактических пикселей экрана. Следовательно, размер пикселей изображения может варьироваться на столько же, сколько пиксель экрана. Решение этой проблемы состоит в том, чтобы сэкономить на фигуре, так что один пиксель изображения равен ровно одному экранному пикселю или любому кратному ему.

В этом случае это будет так же просто, как

data = np.random.random_sample((2182, 112))
plt.imsave("filename.png", data)
person ImportanceOfBeingErnest    schedule 16.01.2019
comment
Я не упомянул, что у графика будет заголовок и цветная полоса, и его нужно сохранить в pdf-файл. Думаю, тогда этот ответ не сработает, я прав? Пожалуйста, смотрите мое редактирование. - person Jorge; 16.01.2019
comment
Хорошо, не могли бы вы предоставить фактический код, который вас интересует? В вопросе у вас есть фигура шириной 25 дюймов, но в массиве гораздо больше строк, чем столбцов. Также plt.axis("off") не означает, что вам нужен заголовок. - person ImportanceOfBeingErnest; 16.01.2019
comment
Я исправил форму случайных данных, так как строки и столбцы менялись местами. Я прочитал ссылку, а затем ваш ответьте здесь ссылка, но я не совсем понимаю, как figsize, dpi и ppi работа в imshow и с pdf. Параметр dpi=1000 сделал пиксели более квадратными, но все же не идеальными квадратами. Любая идея о том, как вычислить figsize и dpi для решения проблемы? - person Jorge; 17.01.2019
comment
Присутствующая цветовая полоса делает это действительно сложным. Итак, вам нужно leftmargin+axessize+space+colorbar+rightmargin = figuresizedpi и axessize = narrayshape, где n должно быть целым числом. То же самое для вертикального направления. Проблема в том, что поля указаны в единицах, отличных от размера фигуры. Так что будет громоздко. Не уверен, что найду время и мотивацию, чтобы записать это в коде. - person ImportanceOfBeingErnest; 17.01.2019

Вам необходимо масштабировать пиксели с использованием параметров imshow: форма и экстент:

import numpy as np
import matplotlib.pyplot as plt

shape = (112, 2182)
extent = [0, 112, 0, 2182]

data = np.random.random_sample(shape)

plt.figure(figsize=(5, 3.5))

ax = plt.subplot(111)
plt.axis('off')

dx = (extent[1] - extent[0]) / shape[1]
dy = (extent[3] - extent[2]) / shape[0]
dx_dy = dx/dy

im = ax.imshow(data, extent=extent, aspect=dx_dy)
plt.show()
person Community    schedule 16.01.2019
comment
Если я хорошо понимаю ваш код, в конце dx_dy == 1, что является значением по умолчанию для aspect. Таким образом, мы могли бы сказать, что вы «всего лишь» добавляете аргумент extent к imshow. Я попробовал ваш код, но это не повлияло на вывод, он остался прежним. - person Jorge; 16.01.2019
comment
В вашем случае dx_dy = 0,002634671980817975. Если вы увеличите созданное изображение, вы получите квадраты красивой формы. - person ; 16.01.2019

В документации сказано, что параметр aspect может быть числом с плавающей запятой или

'equal': обеспечивает соотношение сторон, равное 1. Пиксели будут квадратными (если размеры пикселей явно не сделаны неквадратными в координатах данных с использованием экстента).

«авто»: оси остаются фиксированными, а соотношение сторон регулируется таким образом, чтобы данные соответствовали осям. Как правило, это приводит к неквадратным пикселям.

Таким образом, соотношение сторон 1,0 — это то, что вам нужно. Не ожидается, что вы получите вывод, показанный выше, с учетом значения, которое вы вычислили из формы массива. Как указывает @ImportanceOfBeingErnest, aspect='equal' используется по умолчанию, так что это так же просто, как

im = ax.imshow(data)
person Christian K.    schedule 16.01.2019