Version courte: existe-t-il une méthode Python pour afficher une image qui montre, en temps réel, les indices et les intensités des pixels? De sorte que lorsque je déplace le curseur sur image, j'ai un affichage constamment mis à jour tel que pixel[103,214] = 198
(pour les niveaux de gris) ou pixel[103,214] = (138,24,211)
pour rgb?
version longue:
Supposons que j'ouvre une image en niveaux de gris enregistrée en tant que ndarray im
et que je l'affiche avec imshow
de matplotlib:
im = plt.imread('image.png')
plt.imshow(im,cm.gray)
Ce que j'obtiens est l'image, et en bas à droite du cadre de la fenêtre, un affichage interactif des indices de pixels. Sauf qu'ils ne le sont pas tout à fait, car les valeurs ne sont pas des entiers: x=134.64 y=129.169
Par exemple.
Si je règle l'affichage avec une résolution correcte:
plt.axis('equal')
les valeurs x et y ne sont toujours pas des entiers.
La méthode imshow
du package spectral
fait un meilleur travail:
import spectral as spc
spc.imshow(im)
Ensuite, en bas à droite, j'ai maintenant pixel=[103,152]
Par exemple.
Cependant, aucune de ces méthodes n'affiche également les valeurs des pixels. J'ai donc deux questions:
imshow
de matplotlib
(et le imshow
de scikit-image
) Peut-il être contraint à afficher les indices de pixels corrects (entiers)?Il y a plusieurs façons de procéder.
Vous pouvez monkey-patch ax.format_coord
, Similaire à cet exemple officiel . Je vais utiliser ici une approche un peu plus "Pythonic" qui ne repose pas sur des variables globales. (Notez que je suppose qu'aucun extent
kwarg n'a été spécifié, similaire à l'exemple de matplotlib. Pour être complètement général, vous devez faire n peu plus de travail .)
import numpy as np
import matplotlib.pyplot as plt
class Formatter(object):
def __init__(self, im):
self.im = im
def __call__(self, x, y):
z = self.im.get_array()[int(y), int(x)]
return 'x={:.01f}, y={:.01f}, z={:.01f}'.format(x, y, z)
data = np.random.random((10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, interpolation='none')
ax.format_coord = Formatter(im)
plt.show()
Alternativement, juste pour brancher un de mes propres projets, vous pouvez utiliser mpldatacursor
pour cela. Si vous spécifiez hover=True
, La boîte apparaîtra chaque fois que vous survolez un artiste activé. (Par défaut, il n'apparaît que lorsque vous cliquez dessus.) Notez que mpldatacursor
gère correctement les kwargs extent
et Origin
vers imshow
correctement.
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none')
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'))
plt.show()
De plus, j'ai oublié de mentionner comment afficher les indices de pixels. Dans le premier exemple, cela suppose simplement que i, j = int(y), int(x)
. Vous pouvez ajouter ceux à la place de x
et y
, si vous préférez.
Avec mpldatacursor
, vous pouvez les spécifier avec un formateur personnalisé. Les arguments i
et j
sont les indices de pixels corrects, quels que soient les extent
et Origin
de l'image tracée.
Par exemple (notez le extent
de l'image par rapport aux coordonnées i,j
Affichées):
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none', extent=[0, 1.5*np.pi, 0, np.pi])
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'),
formatter='i, j = {i}, {j}\nz = {z:.02g}'.format)
plt.show()
Un "one-liner" absolu pour cela: (sans compter sur datacursor
)
def val_shower(im):
return lambda x,y: '%dx%d = %d' % (x,y,im[int(y+.5),int(x+.5)])
plt.imshow(image)
plt.gca().format_coord = val_shower(ims)
Il met l'image en fermeture donc s'assure que si vous avez plusieurs images, chacune affichera ses propres valeurs.
avec Jupyter vous pouvez le faire soit avec datacursor(myax)
soit avec ax.format_coord
.
Exemple de code:
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
X = 10*np.random.Rand(5,3)
fig,ax = plt.subplots()
myax = ax.imshow(X, cmap=cm.jet,interpolation='nearest')
ax.set_title('hover over the image')
datacursor(myax)
plt.show()
la datacursor(myax)
peut également être remplacée par ax.format_coord = lambda x,y : "x=%g y=%g" % (x, y)
Tous les exemples que j'ai vus ne fonctionnent que si vos extensions x et y commencent à 0. Voici du code qui utilise vos extensions d'image pour trouver la valeur z.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
d = np.array([[i+j for i in range(-5, 6)] for j in range(-5, 6)])
im = ax.imshow(d)
im.set_extent((-5, 5, -5, 5))
def format_coord(x, y):
"""Format the x and y string display."""
imgs = ax.get_images()
if len(imgs) > 0:
for img in imgs:
try:
array = img.get_array()
extent = img.get_extent()
# Get the x and y index spacing
x_space = np.linspace(extent[0], extent[1], array.shape[1])
y_space = np.linspace(extent[3], extent[2], array.shape[0])
# Find the closest index
x_idx= (np.abs(x_space - x)).argmin()
y_idx= (np.abs(y_space - y)).argmin()
# Grab z
z = array[y_idx, x_idx]
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, z)
except (TypeError, ValueError):
pass
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, 0)
return 'x={:1.4f}, y={:1.4f}'.format(x, y)
# end format_coord
ax.format_coord = format_coord
Si vous utilisez PySide/PyQT, voici un exemple pour avoir une info-bulle de survol de la souris pour les données
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Mouse tooltip
from PySide import QtGui, QtCore
mouse_tooltip = QtGui.QLabel()
mouse_tooltip.setFrameShape(QtGui.QFrame.StyledPanel)
mouse_tooltip.setWindowFlags(QtCore.Qt.ToolTip)
mouse_tooltip.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
mouse_tooltip.show()
def show_tooltip(msg):
msg = msg.replace(', ', '\n')
mouse_tooltip.setText(msg)
pos = QtGui.QCursor.pos()
mouse_tooltip.move(pos.x()+20, pos.y()+15)
mouse_tooltip.adjustSize()
fig.canvas.toolbar.message.connect(show_tooltip)
# Show the plot
plt.show()