web-dev-qa-db-fra.com

Informations sur les pixels interactifs d'une image en Python?

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:

  1. Le imshow de matplotlib (et le imshow de scikit-image) Peut-il être contraint à afficher les indices de pixels corrects (entiers)?
  2. L'une de ces méthodes peut-elle être étendue pour afficher également les valeurs des pixels?
24
Alasdair

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

enter image description here

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

enter image description here

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

enter image description here

40
Joe Kington

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.

2
Roy Shilkrot

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)

0
shahar_m

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