Imshow: extensión y aspecto

85

Estoy escribiendo un sistema de software que visualiza cortes y proyecciones a través de un conjunto de datos 3D. Estoy usando matplotliby específicamente imshowpara visualizar los búferes de imágenes que obtengo de mi código de análisis.

Como me gustaría anotar las imágenes con ejes de trazado, utilizo la palabra clave de extensión que imshowproporciona para asignar las coordenadas de píxeles del búfer de imagen a un sistema de coordenadas del espacio de datos.

Desafortunadamente, matplotlibno sabe de unidades. Digamos (tomando un ejemplo artificial) que quiero trazar una imagen con dimensiones de 1000 m X 1 km. En ese caso, la extensión sería algo así como [0, 1000, 0, 1]. Aunque la matriz de imágenes es cuadrada, dado que la relación de aspecto implícita en la palabra clave de extensión es 1000, los ejes de trazado resultantes también tienen una relación de aspecto de 1000.

¿Es posible forzar la relación de aspecto del gráfico mientras se mantienen las marcas de graduación y etiquetas principales generadas automáticamente que obtengo al usar la palabra clave de extensión?

ngoldbaum
fuente

Respuestas:

141

Puede hacerlo configurando el aspecto de la imagen manualmente (o dejando que se escale automáticamente para completar la extensión de la figura).

De forma predeterminada, imshowestablece el aspecto del gráfico en 1, ya que esto suele ser lo que la gente quiere para los datos de imagen.

En su caso, puede hacer 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()

ingrese la descripción de la imagen aquí

Joe Kington
fuente
Gracias. Es curioso que los doctores no digan nada sobre la scalaropción. Parece escalar el y-axispor el escalar dado.
orodbhen
@JoeKington es posible obtener el tamaño de los píxeles individuales. Este tamaño depende del tamaño del conjunto de datos y puede ir desde un mosaico de mosaicos hasta una trama continua como en su caso.
Alexander Cska
4

Por plt.imshow()guía oficial, sabemos que el aspecto controla la relación de aspecto de los ejes. Bueno, en mis palabras, el aspecto es exactamente la relación entre la unidad x y la unidad y . La mayoría de las veces queremos mantenerlo como 1 ya que no queremos distorsionar las cifras sin querer. Sin embargo, hay casos en los que necesitamos especificar un aspecto con un valor distinto de 1. El interrogador proporcionó un buen ejemplo de que los ejes xey pueden tener diferentes unidades físicas. Supongamos que x está en km y y en m. Por lo tanto, para datos de 10x10, la extensión debe ser [0,10 km, 0,10 m] = [0, 10000 m, 0, 10 m]. En tal caso, si seguimos usando el aspecto predeterminado = 1, la calidad de la figura es realmente mala. Por lo tanto, podemos especificar el aspecto = 1000 para optimizar nuestra figura. Los siguientes códigos ilustran este 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)

ingrese la descripción de la imagen aquí

Sin embargo, creo que hay una alternativa que puede satisfacer la demanda del interrogador. Podemos simplemente establecer la extensión como [0,10,0,10] y agregar etiquetas de eje xy adicionales para denotar las unidades. Códigos de la siguiente manera.

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

ingrese la descripción de la imagen aquí

Para hacer una figura correcta , siempre debemos tener en cuenta eso x_max-x_min = x_res * data.shape[1]y y_max - y_min = y_res * data.shape[0]dónde extent = [x_min, x_max, y_min, y_max]. De forma predeterminada, aspect = 1lo que significa que la unidad de píxel es cuadrada. Este comportamiento predeterminado también funciona bien para x_res e y_res que tienen valores diferentes. Ampliando el ejemplo anterior, supongamos que x_res es 1,5 mientras que y_res es 1. Por tanto, la extensión debe ser igual a [0,15,0,10]. Usando el aspecto predeterminado, podemos tener píxeles de color rectangulares, ¡mientras que el píxel unitario sigue siendo cuadrado!

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

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

El aspecto del píxel de color es x_res / y_res. establecer su aspecto en el aspecto del píxel unitario (es decir aspect = x_res / y_res = ((x_max - x_min) / data.shape[1]) / ((y_max - y_min) / data.shape[0])) siempre daría un píxel de color cuadrado. Podemos cambiar el aspecto = 1,5 para que la unidad del eje x sea 1,5 veces la unidad del eje y, lo que da como resultado un píxel de color cuadrado y una figura completa cuadrada pero una unidad de píxel rectangular. Al parecer, normalmente no se acepta.

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

ingrese la descripción de la imagen aquí

El caso más indeseado es el de establecer un aspecto con un valor arbitrario, como 1.2, que no dará lugar a píxeles unitarios cuadrados ni píxeles de color cuadrados.

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

ingrese la descripción de la imagen aquí

En pocas palabras, siempre es suficiente establecer la extensión correcta y dejar que matplotlib haga las cosas restantes por nosotros (¡aunque x_res! = Y_res). Cambie de aspecto solo cuando sea necesario.

Fei Yao
fuente