105 votes

Imshow : étendue et aspect

J'écris un logiciel qui permet de visualiser des coupes et des projections dans un ensemble de données 3D. J'utilise matplotlib et plus particulièrement imshow pour visualiser les tampons d'images que je reçois de mon code d'analyse.

Puisque je souhaite annoter les images avec des axes de tracé, j'utilise le mot-clé extent que imshow pour faire correspondre les coordonnées des pixels du tampon d'image à un système de coordonnées de l'espace de données.

Malheureusement, matplotlib ne connaît pas les unités. Disons (en prenant un exemple artificiel) que je veux tracer une image avec des dimensions de 1000 m X 1 km . Dans ce cas, l'étendue serait quelque chose comme [0, 1000, 0, 1] . Même si le tableau d'images est carré, puisque le rapport d'aspect impliqué par le mot-clé extent est de 1000, les axes du graphique résultant ont également un rapport d'aspect de 1000.

Est-il possible de forcer le rapport d'aspect du tracé tout en conservant les marques de tic-tac et les étiquettes principales générées automatiquement par l'utilisation du mot-clé extent ?

163voto

Joe Kington Points 68089

Vous pouvez le faire en réglant manuellement l'aspect de l'image (ou en la laissant s'auto-échelonner pour remplir l'étendue de la figure).

Par défaut, imshow définit l'aspect du tracé à 1, car c'est souvent ce que les gens veulent pour les données d'image.

Dans votre cas, vous pouvez faire quelque chose comme :

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

enter image description here

9voto

Fei Yao Points 884

De plt.imshow() guide officiel, nous savons que l'aspect contrôle le rapport d'aspect des axes. Eh bien, dans mes mots, l'aspect est exactement le rapport de x unité et y unité . La plupart du temps, nous souhaitons conserver la valeur 1, car nous ne voulons pas déformer les chiffres de manière involontaire. Cependant, il y a effectivement des cas où nous devons spécifier un aspect d'une valeur autre que 1. L'auteur de la question a fourni un bon exemple : les axes x et y peuvent avoir des unités physiques différentes. Supposons que x soit en km et y en m. Ainsi, pour une donnée 10x10, l'étendue devrait être [0,10km,0,10m] = [0, 10000m, 0, 10m]. Dans ce cas, si nous continuons à utiliser l'aspect=1 par défaut, la qualité de la figure est vraiment mauvaise. Nous pouvons donc spécifier aspect = 1000 pour optimiser notre figure. Les codes suivants illustrent cette méthode.

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

enter image description here

Néanmoins, je pense qu'il existe une alternative qui peut répondre à la demande de l'auteur de la question. Nous pouvons simplement définir l'étendue comme [0,10,0,10] et ajouter des étiquettes supplémentaires sur l'axe xy pour indiquer les unités. Les codes sont les suivants.

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

enter image description here

Pour faire un correct figure, nous devons toujours garder à l'esprit que x_max-x_min = x_res * data.shape[1] y y_max - y_min = y_res * data.shape[0] , donde extent = [x_min, x_max, y_min, y_max] . Par défaut, aspect = 1 ce qui signifie que le pixel unitaire est carré. Ce comportement par défaut fonctionne également très bien pour x_res et y_res qui ont des valeurs différentes. En prolongeant l'exemple précédent, supposons que x_res vaut 1,5 alors que y_res vaut 1. L'étendue devrait donc être égale à [0,15,0,10]. En utilisant l'aspect par défaut, nous pouvons avoir des pixels de couleur rectangulaires, alors que le pixel unitaire est toujours carré !

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

enter image description here enter image description here

L'aspect du pixel de couleur est x_res / y_res . en fixant son aspect à l'aspect du pixel de l'unité (c.-à-d. aspect = x_res / y_res = ((x_max - x_min) / data.shape[1]) / ((y_max - y_min) / data.shape[0]) ) donnerait toujours un pixel de couleur carré. Nous pouvons modifier l'aspect = 1,5 de sorte que l'unité de l'axe des x soit 1,5 fois l'unité de l'axe des y, ce qui donne un pixel de couleur carré et une figure entière carrée mais une unité de pixel rectangulaire. Apparemment, cela n'est pas accepté normalement.

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

enter image description here

Le cas le plus indésirable est celui de l'aspect fixé une valeur arbitraire, comme 1,2, qui ne conduira ni à des pixels carrés d'unité ni à des pixels carrés de couleur.

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

enter image description here

Pour faire court, il est toujours suffisant de définir l'aspect correct et de laisser matplotlib faire le reste pour nous (même si x_res!=y_res) ! Ne modifiez l'aspect que lorsque c'est indispensable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X