Имшоу: масштабы и аспект

86

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

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

К сожалению, matplotlibнасчет единиц не знает. Скажем (взяв искусственный пример), что я хочу построить изображение размером 1000 m X 1 km. В этом случае степень будет примерно такой [0, 1000, 0, 1]. Несмотря на то, что массив изображений является квадратным, поскольку соотношение сторон, подразумеваемое ключевым словом extension, равно 1000, итоговые оси графика также имеют соотношение сторон 1000.

Можно ли изменить соотношение сторон графика, сохраняя при этом автоматически сгенерированные основные отметки и метки, которые я получаю с помощью ключевого слова экстента?

нгольдбаум
источник

Ответы:

144

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

По умолчанию imshowустанавливает аспект графика на 1, так как это часто то, что люди хотят для данных изображения.

В вашем случае вы можете сделать что-то вроде:

import matplotlib.pyplot as plt
import numpy as np

grid = np.random.random((10,10))

fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, figsize=(6,10))

ax1.imshow(grid, extent=[0,100,0,1])
ax1.set_title('Default')

ax2.imshow(grid, extent=[0,100,0,1], aspect='auto')
ax2.set_title('Auto-scaled Aspect')

ax3.imshow(grid, extent=[0,100,0,1], aspect=100)
ax3.set_title('Manually Set Aspect')

plt.tight_layout()
plt.show()

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

Джо Кингтон
источник
Благодарю. Забавно, что документация ничего не говорит о scalarварианте. Кажется, масштабируется y-axisзаданным скаляром.
orodbhen
@JoeKington можно ли получить размер отдельных пикселей. Этот размер зависит от размера набора данных и может образовывать лоскутное одеяло из плиток в непрерывный график, как в вашем случае.
Александр Цска
4

Из plt.imshow()официального руководства мы знаем, что аспект определяет соотношение сторон осей. Ну в моих словах, аспект именно отношение х единиц и у блока . В большинстве случаев мы хотим оставить его равным 1, так как мы не хотим непреднамеренно искажать цифры. Однако действительно есть случаи, когда нам нужно указать аспектное значение, отличное от 1. Автор вопроса привел хороший пример того, что оси x и y могут иметь разные физические единицы. Предположим, что x указано в км, а y - в метрах. Следовательно, для данных 10x10 протяженность должна быть [0,10 км, 0,10 м] = [0, 10000 м, 0, 10 м]. В таком случае, если мы продолжим использовать аспект по умолчанию = 1, качество рисунка будет действительно плохим. Следовательно, мы можем указать аспект = 1000, чтобы оптимизировать нашу фигуру. Следующие коды иллюстрируют этот метод.

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
rng=np.random.RandomState(0)
data=rng.randn(10,10)
plt.imshow(data, origin = 'lower',  extent = [0, 10000, 0, 10], aspect = 1000)

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

Тем не менее, я думаю, что есть альтернатива, способная удовлетворить требования вопрошающего. Мы можем просто установить размер как [0,10,0,10] и добавить дополнительные метки оси xy для обозначения единиц. Коды следующие.

plt.imshow(data, origin = 'lower',  extent = [0, 10, 0, 10])
plt.xlabel('km')
plt.ylabel('m')

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

Чтобы составить правильную цифру, всегда нужно иметь в виду то x_max-x_min = x_res * data.shape[1]и y_max - y_min = y_res * data.shape[0], где extent = [x_min, x_max, y_min, y_max]. По умолчанию это aspect = 1означает, что пиксель единицы квадратный. Это поведение по умолчанию также отлично работает для x_res и y_res, которые имеют разные значения. Расширяя предыдущий пример, предположим, что x_res равно 1,5, а y_res равно 1. Следовательно, размер должен быть равен [0,15,0,10]. Используя формат по умолчанию, мы можем иметь прямоугольные цветные пиксели, тогда как единичный пиксель по-прежнему квадратный!

plt.imshow(data, origin = 'lower',  extent = [0, 15, 0, 10])
# Or we have similar x_max and y_max but different data.shape, leading to different color pixel res.
data=rng.randn(10,5)
plt.imshow(data, origin = 'lower',  extent = [0, 5, 0, 5])

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

Аспект цветного пикселя x_res / y_res. установка его формата на единичный пиксель (т.е. aspect = x_res / y_res = ((x_max - x_min) / data.shape[1]) / ((y_max - y_min) / data.shape[0])) всегда будет давать квадратный цветной пиксель. Мы можем изменить аспект = 1.5 так, чтобы единица измерения по оси x была в 1,5 раза больше единицы по оси y, что привело бы к квадратному цветному пикселю и квадратной целой фигуре, но прямоугольной единице пикселей. Видимо, это обычно не принято.

data=rng.randn(10,10)
plt.imshow(data, origin = 'lower',  extent = [0, 15, 0, 10], aspect = 1.5)

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

Самым нежелательным случаем является то, что аспекту задается произвольное значение, например 1,2, что не приведет ни к квадратным единичным пикселям, ни к квадратным цветным пикселям.

plt.imshow(data, origin = 'lower',  extent = [0, 15, 0, 10], aspect = 1.2)

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

Короче говоря, всегда достаточно установить правильный экстент и позволить matplotlib делать остальное за нас (даже если x_res! = Y_res)! Меняйте вид только тогда, когда это необходимо.

Фэй Яо
источник