Information interactive en pixel d'une image en Python?
version courte: existe-t-il une méthode Python pour afficher une image qui affiche, en temps réel, les indices et les intensités des pixels? Donc, en déplaçant le curseur sur l'image, j'ai un affichage continuellement mis à jour tel que pixel[103,214] = 198
(pour l'échelle de gris) ou pixel[103,214] = (138,24,211)
pour rgb?
version longue:
supposons que j'ouvre une image en échelle de gris enregistrée en tant que im
et que je l'affiche avec imshow
de matplotlib:
im = plt.imread('image.png')
plt.imshow(im,cm.gray)
ce que j'obtiens c'est l'image, et en bas à droite du cadre de la fenêtre, un affichage interactif des indices pixels. Sauf qu'ils ne 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 la bonne résolution:
plt.axis('equal')
les valeurs x et y ne sont toujours pas des entiers.
le imshow
méthode du paquet spectral
fait un meilleur travail:
import spectral as spc
spc.imshow(im)
Puis en bas à droite j'ai maintenant pixel=[103,152]
par exemple.
cependant, aucune de ces méthodes n'affiche les valeurs des pixels. J'ai donc deux questions:
- le
imshow
dematplotlib
(et leimshow
descikit-image
) peut-il être forcé à montrer les bons indices (entiers) de pixels? - Pouvez une de ces méthodes, être étendu pour afficher les valeurs des pixels?
4 réponses
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" pythonique " 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 tout à fait général, vous devez faire un 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()
Sinon, juste brancher l'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 survolerez un artiste activé. (Par défaut, il ne s'affiche lorsque l'utilisateur clique dessus.) Notez que mpldatacursor
manipule correctement les extent
et origin
kwargs à imshow
.
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()
aussi, j'ai oublié de mentionner comment afficher les indices de pixels. Dans le premier exemple, c'est juste supposer que i, j = int(y), int(x)
. Vous pouvez les ajouter à la place de x
et y
, si vous préférez.
avec mpldatacursor
, vous pouvez les spécifier avec un formatteur personnalisé. Les arguments i
et j
sont les indices pixels corrects, indépendamment des extent
et origin
de l'image tracer.
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()
absolue " bare bones "one-liner" pour ce faire: (sans compter sur les 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 dans la fermeture donc s'assure que si vous avez plusieurs images chacune affichera ses propres valeurs.
tous les exemples que j'ai vu ne fonctionnent que si vos X et y sont à partir de 0. Voici le code qui utilise vos extensions d'image pour trouver la valeur de 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 un tooltip de mouse hover 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()
avec Jupyter vous pouvez le faire soit avec datacursor(myax)
ou par ax.format_coord
.
code échantillon:
%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()
le datacursor(myax)
peut également être remplacé par ax.format_coord = lambda x,y : "x=%g y=%g" % (x, y)