Matplotlib: How to plot images instead of points?
There are two ways to do this.
- Plot the image using
imshow
with theextent
kwarg set based on the location you want the image at. - Use an
OffsetImage
inside anAnnotationBbox
.
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()
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:
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
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:
Related Topics
Get Relative Path from Comparing Two Absolute Paths
A Very Simple Multithreading Parallel Url Fetching (Without Queue)
Load CSV Data into MySQL in Python
How to Equalize the Scales of X-Axis and Y-Axis in Matplotlib
Add Column to Dataframe with Constant Value
What Is the Fastest Way to Parse Large Xml Docs in Python
Splitting a Semicolon-Separated String to a Dictionary, in Python
Process to Convert Simple Python Script into Windows Executable
How to Get a Raw, Compiled SQL Query from a SQLalchemy Expression
Search for "Does-Not-Contain" on a Dataframe in Pandas
Solving "Dll Load Failed: %1 Is Not a Valid Win32 Application." for Pygame
Hiding a Password in a Python Script (Insecure Obfuscation Only)