Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python show image upon hovering over a point

Tags:

I have a 2-d scatter plot of points, that correspond to images. I was wondering if there's an easy way to display the corresponding image (as a popup or tooltip) when you hover your mouse over each point? I tried plotly but found out you need to manually edit javascript to get the hover event to work. Is there a simple solution just with matplotlib or some other common package?

like image 246
Alex R. Avatar asked Mar 17 '17 21:03

Alex R.


2 Answers

Find here a complete solution on how to display an image on hover events. It uses a 'motion_notify_event' to detect when the mouse is over a scatter point (hovering). If this is the case, it displays an image annotation with a corresponding image next to the hovered scatter point.

import matplotlib.pyplot as plt from matplotlib.offsetbox import OffsetImage, AnnotationBbox import numpy as np; np.random.seed(42)  # Generate data x, y for scatter and an array of images. x = np.arange(20) y = np.random.rand(len(x)) arr = np.empty((len(x),10,10)) for i in range(len(x)):     f = np.random.rand(5,5)     arr[i, 0:5,0:5] = f     arr[i, 5:,0:5] =np.flipud(f)     arr[i, 5:,5:] =np.fliplr(np.flipud(f))     arr[i, 0:5:,5:] = np.fliplr(f)  # create figure and plot scatter fig = plt.figure() ax = fig.add_subplot(111) line, = ax.plot(x,y, ls="", marker="o")  # create the annotations box im = OffsetImage(arr[0,:,:], zoom=5) xybox=(50., 50.) ab = AnnotationBbox(im, (0,0), xybox=xybox, xycoords='data',         boxcoords="offset points",  pad=0.3,  arrowprops=dict(arrowstyle="->")) # add it to the axes and make it invisible ax.add_artist(ab) ab.set_visible(False)  def hover(event):     # if the mouse is over the scatter points     if line.contains(event)[0]:         # find out the index within the array from the event         ind, = line.contains(event)[1]["ind"]         # get the figure size         w,h = fig.get_size_inches()*fig.dpi         ws = (event.x > w/2.)*-1 + (event.x <= w/2.)          hs = (event.y > h/2.)*-1 + (event.y <= h/2.)         # if event occurs in the top or right quadrant of the figure,         # change the annotation box position relative to mouse.         ab.xybox = (xybox[0]*ws, xybox[1]*hs)         # make annotation box visible         ab.set_visible(True)         # place it at the position of the hovered scatter point         ab.xy =(x[ind], y[ind])         # set the image corresponding to that point         im.set_data(arr[ind,:,:])     else:         #if the mouse is not over a scatter point         ab.set_visible(False)     fig.canvas.draw_idle()  # add callback for mouse moves fig.canvas.mpl_connect('motion_notify_event', hover)            plt.show() 

enter image description here

like image 163
ImportanceOfBeingErnest Avatar answered Sep 23 '22 03:09

ImportanceOfBeingErnest


If you want your images to be displayed in RGB, you have to slightly adjust the code. For this example the images need to be at your disk. Instead of using an 3darray for the images where the first dimension only represents the index you need to read the image with the plt.imread and then set_data to the corresponding position in your array containing the image names.

import matplotlib.pyplot as plt from matplotlib.offsetbox import OffsetImage, AnnotationBbox import numpy as np; np.random.seed(42) import os  os.chdir('Path/to/your/images')  # Generate data x, y for scatter and an array of images. x = np.arange(3) y = np.random.rand(len(x)) jpg_name_np = np.array(['904646.jpg', '903825.jpg', '905722.jpg']).astype('<U12') # names of your images files  cmap = plt.cm.RdYlGn  # create figure and plot scatter fig = plt.figure() ax = fig.add_subplot(111) #line, = ax.plot(x,y, ls="", marker="o") line = plt.scatter(x,y,c=heat, s=10, cmap=cmap) image_path = np.asarray(jpg_name_np)  # create the annotations box image = plt.imread(image_path[0]) im = OffsetImage(image, zoom=0.1) xybox=(50., 50.) ab = AnnotationBbox(im, (0,0), xybox=xybox, xycoords='data',         boxcoords="offset points",  pad=0.3,  arrowprops=dict(arrowstyle="->")) # add it to the axes and make it invisible ax.add_artist(ab) ab.set_visible(False)  def hover(event):     # if the mouse is over the scatter points     if line.contains(event)[0]:         # find out the index within the array from the event         ind, = line.contains(event)[1]["ind"]         # get the figure size         w,h = fig.get_size_inches()*fig.dpi         ws = (event.x > w/2.)*-1 + (event.x <= w/2.)          hs = (event.y > h/2.)*-1 + (event.y <= h/2.)         # if event occurs in the top or right quadrant of the figure,         # change the annotation box position relative to mouse.         ab.xybox = (xybox[0]*ws, xybox[1]*hs)         # make annotation box visible         ab.set_visible(True)         # place it at the position of the hovered scatter point         ab.xy =(x[ind], y[ind])         # set the image corresponding to that point         im.set_data(plt.imread(image_path[ind]))     else:         #if the mouse is not over a scatter point         ab.set_visible(False)     fig.canvas.draw_idle()  # add callback for mouse moves fig.canvas.mpl_connect('motion_notify_event', hover)    fig = plt.gcf() fig.set_size_inches(10.5, 9.5)  plt.show() 

Plot when hovering over scatterpoint

like image 42
lschmidt90 Avatar answered Sep 23 '22 03:09

lschmidt90