Imshow: extensão e aspecto

86

Estou escrevendo um sistema de software que visualiza fatias e projeções por meio de um conjunto de dados 3D. Estou usando matplotlibe especificamente imshowpara visualizar os buffers de imagem que recebo do meu código de análise.

Como gostaria de fazer anotações nas imagens com eixos de plotagem, uso a palavra-chave de extensão que imshowfornece para mapear as coordenadas de pixel do buffer de imagem para um sistema de coordenadas de espaço de dados.

Infelizmente, matplotlibnão sabe sobre unidades. Digamos (tomando um exemplo artificial) que desejo traçar uma imagem com dimensões de 1000 m X 1 km. Nesse caso, a extensão seria algo parecido [0, 1000, 0, 1]. Mesmo que a matriz de imagem seja quadrada, uma vez que a proporção implícita pela palavra-chave de extensão é 1000, os eixos de plotagem resultantes também têm uma proporção de 1000.

É possível forçar a proporção do gráfico e ainda manter as principais marcas de escala e rótulos gerados automaticamente que recebo usando a palavra-chave extensão?

Ngoldbaum
fonte

Respostas:

144

Você pode fazer isso definindo o aspecto da imagem manualmente (ou permitindo que ela seja dimensionada automaticamente para preencher a extensão da figura).

Por padrão, imshowdefine o aspecto do gráfico como 1, pois é isso que as pessoas geralmente desejam para os dados de imagem.

No seu caso, você pode fazer algo como:

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()

insira a descrição da imagem aqui

Joe Kington
fonte
Obrigado. Engraçado, os médicos não falam nada sobre scalaropção. Parece escalar y-axispelo escalar fornecido.
orodbhen
@JoeKington é possível obter o tamanho dos pixels individuais. Esse tamanho depende do tamanho do conjunto de dados e pode ir de uma colcha de retalhos de ladrilhos a um gráfico contínuo como no seu caso.
Alexander Cska
4

Pelo plt.imshow()guia oficial, sabemos que esse aspecto controla a proporção dos eixos. Bem, em minhas palavras, o aspecto é exatamente a proporção da unidade xey unidade . Na maioria das vezes, queremos mantê-lo como 1, pois não queremos distorcer os números involuntariamente. No entanto, há de fato casos em que precisamos especificar o aspecto com um valor diferente de 1. O questionador forneceu um bom exemplo de que os eixos xey podem ter unidades físicas diferentes. Vamos supor que x esteja em km ey em m. Portanto, para dados de 10x10, a extensão deve ser [0,10km, 0,10m] = [0, 10000m, 0, 10m]. Nesse caso, se continuarmos a usar o aspecto padrão = 1, a qualidade da figura é muito ruim. Podemos, portanto, especificar aspect = 1000 para otimizar nossa figura. Os códigos a seguir ilustram esse método.

%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)

insira a descrição da imagem aqui

No entanto, acho que existe uma alternativa que pode atender à demanda do questionador. Podemos apenas definir a extensão como [0,10,0,10] e adicionar rótulos adicionais ao eixo xy para denotar as unidades. Códigos da seguinte maneira.

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

insira a descrição da imagem aqui

Para fazer uma figura correta , devemos sempre ter em mente que x_max-x_min = x_res * data.shape[1]e y_max - y_min = y_res * data.shape[0]onde extent = [x_min, x_max, y_min, y_max]. Por padrão, aspect = 1o que significa que o pixel unitário é quadrado. Este comportamento padrão também funciona bem para x_res e y_res que têm valores diferentes. Estendendo o exemplo anterior, vamos assumir que x_res é 1,5 enquanto y_res é 1. Portanto, a extensão deve ser igual a [0,15,0,10]. Usando o aspecto padrão, podemos ter pixels de cor retangulares, enquanto o pixel unitário ainda é quadrado!

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])

insira a descrição da imagem aqui insira a descrição da imagem aqui

O aspecto do pixel de cor é x_res / y_res. definir seu aspecto para o aspecto de pixel unitário (ou seja aspect = x_res / y_res = ((x_max - x_min) / data.shape[1]) / ((y_max - y_min) / data.shape[0])) sempre forneceria pixel de cor quadrada. Podemos alterar o aspecto = 1,5 para que a unidade do eixo x seja 1,5 vezes a unidade do eixo y, levando a um pixel quadrado colorido e uma figura quadrada inteira, mas unidade de pixel retangular. Aparentemente, não é normalmente aceito.

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

insira a descrição da imagem aqui

O caso mais indesejado é aquele conjunto de aspecto com um valor arbitrário, como 1,2, que não levará a pixels unitários quadrados nem pixels coloridos quadrados.

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

insira a descrição da imagem aqui

Resumindo, é sempre suficiente definir a extensão correta e deixar que o matplotlib faça o restante por nós (mesmo que x_res! = Y_res)! Mude o aspecto apenas quando for necessário.

Fei Yao
fonte