Matplotlib: How to Plot Images Instead of Points

Matplotlib: How to plot images instead of points?

There are two ways to do this.

  1. Plot the image using imshow with the extent kwarg set based on the location you want the image at.
  2. Use an OffsetImage inside an AnnotationBbox.

The first way is the easiest to understand, but the second has a large advantage. The annotation box approach will allow the image to stay at a constant size as you zoom in. Using imshow will tie the size of the image to the data coordinates of the plot.

Here's an example of the second option:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from matplotlib.cbook import get_sample_data

def main():
x = np.linspace(0, 10, 20)
y = np.cos(x)
image_path = get_sample_data('ada.png')
fig, ax = plt.subplots()
imscatter(x, y, image_path, zoom=0.1, ax=ax)
ax.plot(x, y)
plt.show()

def imscatter(x, y, image, ax=None, zoom=1):
if ax is None:
ax = plt.gca()
try:
image = plt.imread(image)
except TypeError:
# Likely already an array...
pass
im = OffsetImage(image, zoom=zoom)
x, y = np.atleast_1d(x, y)
artists = []
for x0, y0 in zip(x, y):
ab = AnnotationBbox(im, (x0, y0), xycoords='data', frameon=False)
artists.append(ax.add_artist(ab))
ax.update_datalim(np.column_stack([x, y]))
ax.autoscale()
return artists

main()

Sample Image

matplotlib plot numpy array of images as markers

You need to set the limits of the axis accordingly, or they would default to just (0,1):

from matplotlib.offsetbox import OffsetImage, AnnotationBbox
fig, ax = plt.subplots()
for x0, y0, img in zip(x, y, images):
ab = AnnotationBbox(OffsetImage(img, zoom=5, cmap='gray'), (x0, y0), frameon=False)
ax.add_artist(ab)
plt.xlim(x.min(), x.max()+1)
plt.ylim(y.min(), y.max()+1)
plt.show()

Output:

Sample Image

How to add images instead of dots in a plotly scatter plot (python)

  • a few points on answer
    • no reference was provided to images to use, so downloaded some from kaggle. Couldn't find emojis. This bulks up code to do this...
    • Plotly Express is used without a data frame in question. So have kept to this. This then means loop through x&y values in trace to get position of images
    • effectively image used is random, could be selected based on features of data

Sample Image

import plotly.express as px
import kaggle.cli
import sys, requests
from pathlib import Path
from zipfile import ZipFile
import urllib
from PIL import Image

# fmt: off
# download some images to demonstrate
url = "https://www.kaggle.com/anzhemeng/nfl-team-logos"
sys.argv = [sys.argv[0]] + f"datasets download {urllib.parse.urlparse(url).path[1:]}".split(" ")
kaggle.cli.main()
zfile = ZipFile(f'{urllib.parse.urlparse(url).path.split("/")[-1]}.zip')
# fmt: on
zfile.extractall("nfl-logos")

fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16])

# question did not use a dataframe, so will use x & y from the figure trace
# just a selection of images, used NFL images given don't have emojis
for x,y, png in zip(fig.data[0].x, fig.data[0].y, Path.cwd().joinpath("nfl-logos").glob("*.png")):
fig.add_layout_image(
x=x,
y=y,
source=Image.open(png),
xref="x",
yref="y",
sizex=2,
sizey=2,
xanchor="center",
yanchor="middle",
)

fig

How do you directly overlay a scatter plot on top of a jpg image in matplotlib / Python?

The pyplot.scatter() function was tailor made for this reason:

import matplotlib.pyplot as plt
im = plt.imread(image_name)
implot = plt.imshow(im)

# put a blue dot at (10, 20)
plt.scatter([10], [20])

# put a red dot, size 40, at 2 locations:
plt.scatter(x=[30, 40], y=[50, 60], c='r', s=40)

plt.show()

See the documentation for more info.

How to use custom png image marker with plot?

I don't believe matplotlib can customize markers like that. See here for the level of customization, which falls way short of what you need.

As an alternative, I've coded up this kludge which uses matplotlib.image to place images at the line point locations.

import matplotlib.pyplot as plt
from matplotlib import image

# constant
dpi = 72
path = 'smile.png'
# read in our png file
im = image.imread(path)
image_size = im.shape[1], im.shape[0]

fig = plt.figure(dpi=dpi)
ax = fig.add_subplot(111)
# plot our line with transparent markers, and markersize the size of our image
line, = ax.plot((1,2,3,4),(1,2,3,4),"bo",mfc="None",mec="None",markersize=image_size[0] * (dpi/ 96))
# we need to make the frame transparent so the image can be seen
# only in trunk can you put the image on top of the plot, see this link:
# http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg14534.html
ax.patch.set_alpha(0)
ax.set_xlim((0,5))
ax.set_ylim((0,5))

# translate point positions to pixel positions
# figimage needs pixels not points
line._transform_path()
path, affine = line._transformed_path.get_transformed_points_and_affine()
path = affine.transform_path(path)
for pixelPoint in path.vertices:
# place image at point, centering it
fig.figimage(im,pixelPoint[0]-image_size[0]/2,pixelPoint[1]-image_size[1]/2,origin="upper")

plt.show()

Produces:

Sample Image



Related Topics



Leave a reply



Submit