Imshow: zakres i aspekt

85

Piszę system oprogramowania, który wizualizuje wycinki i projekcje za pomocą zestawu danych 3D. Używam matplotlibi specjalnie imshowdo wizualizacji buforów obrazu, które otrzymuję z mojego kodu analizy.

Ponieważ chciałbym dodać adnotacje do obrazów za pomocą osi wykresu, używam słowa kluczowego rozszerzonego, które imshowumożliwia odwzorowanie współrzędnych pikseli buforu obrazu na układ współrzędnych przestrzeni danych.

Niestety, matplotlibnie wie o jednostkach. Powiedz (na sztucznym przykładzie), że chcę wykreślić obraz o wymiarach 1000 m X 1 km. W takim przypadku zakres byłby podobny [0, 1000, 0, 1]. Mimo że tablica obrazów jest kwadratowa, ponieważ współczynnik proporcji wynikający ze słowa kluczowego rozszerzonego wynosi 1000, wynikowe osie wykresu również mają współczynnik kształtu 1000.

Czy można wymusić proporcje działki, jednocześnie zachowując automatycznie generowane główne znaczniki i etykiety, które otrzymuję, używając słowa kluczowego rozszerz?

ngoldbaum
źródło

Odpowiedzi:

141

Możesz to zrobić, ustawiając proporcje obrazu ręcznie (lub pozwalając na automatyczne skalowanie, aby wypełnić rozmiar figury).

Domyślnie imshowustawia aspekt wykresu na 1, ponieważ często tego oczekują ludzie w przypadku danych obrazu.

W twoim przypadku możesz zrobić coś takiego:

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

wprowadź opis obrazu tutaj

Joe Kington
źródło
Dzięki. Zabawne, że doktorzy nic nie mówią o scalaropcji. Wydaje się, że skaluje y-axisprzez podany skalar.
orodbhen
@JoeKington czy można uzyskać rozmiar poszczególnych pikseli. Rozmiar ten zależy od rozmiaru zestawu danych i może tworzyć mozaikę płytek w ciągły wykres, tak jak w twoim przypadku.
Alexander Cska
4

Z plt.imshow()oficjalnego przewodnika wiemy, że aspekt kontroluje proporcje osi. Dobrze w moich słowach, aspekt jest dokładnie relacja x jednostki i y jednostki . W większości przypadków chcemy, aby było to 1, ponieważ nie chcemy przypadkowo zniekształcać liczb. Jednak rzeczywiście są przypadki, w których musimy określić aspekt o wartości innej niż 1. Pytający podał dobry przykład, że oś x i y mogą mieć różne jednostki fizyczne. Załóżmy, że x jest w km, a y w m. Stąd dla danych 10x10 zakres powinien wynosić [0,10 km, 0,10 m] = [0, 10000 m, 0, 10 m]. W takim przypadku, jeśli nadal będziemy używać domyślnego aspektu = 1, jakość figury jest naprawdę zła. Możemy zatem określić aspekt = 1000, aby zoptymalizować naszą figurę. Poniższe kody ilustrują tę metodę.

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

wprowadź opis obrazu tutaj

Niemniej jednak myślę, że istnieje alternatywa, która może spełnić żądanie pytającego. Możemy po prostu ustawić zakres na [0,10,0,10] i dodać dodatkowe etykiety osi xy, aby oznaczyć jednostki. Kody w następujący sposób.

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

wprowadź opis obrazu tutaj

Aby uzyskać prawidłową figurę, zawsze powinniśmy pamiętać o tym x_max-x_min = x_res * data.shape[1]i y_max - y_min = y_res * data.shape[0]gdzie extent = [x_min, x_max, y_min, y_max]. Domyślnie aspect = 1oznacza to, że piksel jednostkowy jest kwadratowy. To domyślne zachowanie działa również dobrze w przypadku x_res i y_res, które mają różne wartości. Rozszerzając poprzedni przykład, przyjmijmy, że x_res wynosi 1,5, podczas gdy y_res wynosi 1. Stąd zakres powinien wynosić [0,15,0,10]. Używając domyślnego aspektu, możemy mieć prostokątne kolorowe piksele, podczas gdy piksel jednostkowy jest nadal kwadratowy!

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

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Aspekt kolorowego piksela to x_res / y_res. ustawienie jego aspektu na aspekt piksela jednostkowego (tj. aspect = x_res / y_res = ((x_max - x_min) / data.shape[1]) / ((y_max - y_min) / data.shape[0])) zawsze dawałoby kwadratowy piksel koloru. Możemy zmienić aspekt = 1,5 tak, aby jednostka osi x była 1,5 razy większa niż jednostka osi y, co prowadzi do kwadratowego koloru piksela i kwadratu całej figury, ale prostokątnej jednostki pikseli. Najwyraźniej nie jest to zwykle akceptowane.

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

wprowadź opis obrazu tutaj

Najbardziej niepożądanym przypadkiem jest ustawienie współczynnika na dowolną wartość, na przykład 1,2, co nie prowadzi do uzyskania ani kwadratowych pikseli, ani kwadratowych kolorowych pikseli.

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

wprowadź opis obrazu tutaj

Krótko mówiąc, zawsze wystarczy ustawić odpowiedni zakres i pozwolić matplotlib zrobić za nas pozostałe rzeczy (nawet jeśli x_res! = Y_res)! Zmieniaj aspekt tylko wtedy, gdy jest to konieczne.

Fei Yao
źródło