Is it possible to patch an image in matplotlib? - python

I'm developing and automata in Python with matplotlib, and I would like to design it with a robot-look I picked on the web. I chose a file and I would like to place it in place of the black squares in the image below...
I have been looking for a way to do it on the web but I haven't found any answer.
FYI, I use the fig = plt.Figure() method and then the fig.add_subplot to create my subplot and I finally generate the black square by creating black patches.

I don't believe patches are meant for this purpose. However, since you undoubtedly know the location and bounding area of the black boxes, OffsetImage and AnnotationBbox is a viable alternative.
import math
import numpy as np
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
x = np.linspace(0,10, 10)
y = [math.sin(i) for i in x]
fig, ax = plt.subplots()
im = plt.imread('pacman.png')
oi = OffsetImage(im, zoom = 0.15)
a = []
for px, py in zip(x,y):
box = AnnotationBbox(oi, (px, py), frameon=False)
a.append(ax.add_artist(box))
ax.plot(x,y,'r--')
Hope this helps.

Related

Python: Delaunay triangle vertices don't "line up" when saved as .pdf or .png

I created a set of Delaunay triangles using scipy.spatial.
from matplotlib import pyplot as plt
import matplotlib
from skimage import io
from scipy.spatial import Delaunay
import numpy as np
from PIL import Image
from scipy.ndimage import rotate
h = 700
w = 700
npts = 500
pts = np.zeros((npts,2))
pts[:,0] = np.random.randint(0,w,npts)
pts[:,1] = np.random.randint(0,h,npts)
tri = Delaunay(pts)
centers = np.sum(pts[tri.simplices], axis=1, dtype='int')/3.0
#plt.figure()
fig, ax = plt.subplots()
plt.xlim(0, w)
plt.ylim(0, h)
for i in range(0,len(pts[tri.simplices])-1):
temp_tri = plt.Polygon(pts[tri.simplices][i], color = colors[i]/256) #colors variable is a numpy.ndarray variable that contains RGB values
plt.gca().add_patch(temp_tri)
plt.gca().set_aspect('equal')
plt.axis('off')
plt.savefig('test.pdf', bbox_inches = 'tight', dpi=fig.dpi)
plt.show()
On the screen, the output is as I intended. However, when I save it as pdf or png, the vertices of Delaunay triangles do not match (they do when I look at them through plt.show())
The picture below is part of the whole picture, just to highlight where the vertices don't match.
Few suggestions I found with issues regarding different images shown with plt.show() and fitsave() said I should match the dpi, which I have done.
Please advise on what I should try. Thank you so much in advance!
I can't tell the reason why it fails to produce the correct points for the triangles, but in general it's probably better to create a PolyCollection from the vertices, instead of individual triangles.
In that case the problem would not appear.
pc = PolyCollection(pts[tri.simplices],
facecolors=np.random.rand(len(pts[tri.simplices]),3),
edgecolor="face",linewidth=0.1)
plt.gca().add_collection(pc)
Here some remaining overlap is seen, which is due to the linewidth. Smaller linewidths would look better in the pdf, e.g. linewidth=0.01, but might result in "white" lines in the graphics on screen. You may play around with that parameter until you are happy with the result.

Image icons in a matplotlib plot? [duplicate]

I would like to utilize customer markers in both scatter and line charts. How can I make custom marker out of a PNG file?
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:
Following on from Mark's answer. I just thought I would add to this a bit because I tried to run this and it does what I want with the exception of actually displaying the icons on the graph. Maybe something has changed with matplotlib. It has been 4 years.
The line of code that reads:
ax.get_frame().set_alpha(0)
does not seem to work, however
ax.patch.set_alpha(0)
does work.
The other answer may lead to problems when resizing the figure. Here is a different approach, positionning the images inside annotation boxes, which are anchored in data coordinates.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
path = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Tango-example_icons.png"
image = plt.imread(path)[116:116+30, 236:236+30]
x = np.arange(10)
y = np.random.rand(10)
fig, ax = plt.subplots()
ax.plot(x,y)
def plot_images(x, y, image, ax=None):
ax = ax or plt.gca()
for xi, yi in zip(x,y):
im = OffsetImage(image, zoom=72/ax.figure.dpi)
im.image.axes = ax
ab = AnnotationBbox(im, (xi,yi), frameon=False, pad=0.0,)
ax.add_artist(ab)
plot_images(x, y, image, ax=ax)
plt.show()

Putting a small image on the corner of a plot with matplotlib

This question lend code from Joe Kington(how to insert a small image on the corner of a plot with matplotlib?):
import matplotlib.pyplot as plt
import Image
import numpy as np
im = Image.open('/home/jofer/logo.png')
height = im.size[1]
# We need a float array between 0-1, rather than
# a uint8 array between 0-255
im = np.array(im).astype(np.float) / 255
fig = plt.figure()
plt.plot(np.arange(10), 4 * np.arange(10))
# With newer (1.0) versions of matplotlib, you can
# use the "zorder" kwarg to make the image overlay
# the plot, rather than hide behind it... (e.g. zorder=10)
fig.figimage(im, 0, fig.bbox.ymax - height)
# (Saving with the same dpi as the screen default to
# avoid displacing the logo image)
fig.savefig('/home/jofer/temp.png', dpi=80)
plt.show()
I tried the following:
import matplotlib.pyplot as plt
import Image
import numpy as np
im = Image.open('/home/po/pic.jpg')
height = im.size[1]
im = np.array(im).astype(np.float) / 255
fig = plt.figure()
fig.subplots_adjust(top=0.80)
fig.patch.set_facecolor('black')
ax1 = fig.add_subplot(1, 1, 1, axisbg='white')
fig.figimage(im, 0, fig.bbox.ymax - height)
But my image is at the center rather than at the middle, is there a way to shift it up, i have tried read up on http://effbot.org/imagingbook/image.htm but to no avail
Thanks in advance:)
I don't see the need for an extra module import when matplotlib has the capabilities to do the same.
If you want to make an inset, simply add (and position) an extra axes object to the figure window. It has some advantages over the figimage method here, because figimage
Adds a non-resampled image to the figure
(matplotlib docs).
Here's an example:
from scipy import misc
face = misc.face()
import matplotlib.pyplot as plt
plt.plot(range(10), range(10))
ax = plt.axes([0.5,0.8, 0.1, 0.1], frameon=True) # Change the numbers in this array to position your image [left, bottom, width, height])
ax.imshow(face)
ax.axis('off') # get rid of the ticks and ticklabels
plt.show()
I used face as an image, to make sure anyone can run the code, but you can have matplotlib load your image by typing:
image = plt.imread('/home/po/pic.jpg')
This replaces your call to the Image module, making it obsolete. The variable image serves the same purpose as the variable face in the small script above.
Depending on your use-case, it may be far faster and cleaner to just resize the image in your photo editor and then use two lines of code:
img = image.imread("my_image.png")
plt.figimage(img, 100, 200, zorder=1, alpha=0.3)

draping texture over a 3D surface in python

my goal is to drap a texture (i.e. an image) over a surface and visualize it in 3D with python. The application is viewing an orthophotography over a DEM, I thence use gdal for importing my data (both image and DEM). I tried to use plot_surface from matplotlib but it seems that I can't add texture to the surface.
here is the current code:
from osgeo import gdal
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
ds = gdal.Open('MyDEM.cub')
dem = ds.ReadAsArray()
do = gdal.Open('MyOrtho.cub')
or = do.ReadAsArray()
xres = gt[1]
yres = gt[5]
X = np.arange(gt[0], gt[0] + dem.shape[1]*xres, xres)
Y = np.linspace(gt[3], gt[3] + dem.shape[0]*yres, ds.RasterYSize)
X, Y = np.meshgrid(X, Y)
fig, ax = plt.subplots(figsize=(16,8), subplot_kw={'projection': '3d'})
surf = ax.plot_surface(X,Y,dem,rstride=1, cstride=1,linewidth=0, antialiased=True,cmap=plt.cm.RdYlBu_r)
fig.colorbar(surf, shrink=0.4, aspect=20)
plt.show()
How can I use or array as a texture (e.g., or can have a different resolution/size than dem but I'll manage this later), I want first to have a stupid 3D surface with a texture. This is easy in Matlab, but how to do it with Python? Any idea ?
Yeah Mayavi will do this. You can open the DEM file in gdal and then pull the image into TVTK as a texture. Finally you can wrap it over the surface with the mlab.surf() commands. Here is a link to a good example of this.
Example
From the matplotlib docs:
Axes3D.plot_surface(X, Y, Z, *args, **kwargs)
Create a surface plot.
By default it will be colored in shades of a solid color, but it also supports color mapping by supplying the cmap argument.
It seems, therefore, that matplotlib does not support the use of an arbitrary image on a surface plot. You will need to identify another library that provides this feature (a quick search suggests that mayavi may do what you want).

Putting custom objects as datapoints in python [duplicate]

I would like to utilize customer markers in both scatter and line charts. How can I make custom marker out of a PNG file?
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:
Following on from Mark's answer. I just thought I would add to this a bit because I tried to run this and it does what I want with the exception of actually displaying the icons on the graph. Maybe something has changed with matplotlib. It has been 4 years.
The line of code that reads:
ax.get_frame().set_alpha(0)
does not seem to work, however
ax.patch.set_alpha(0)
does work.
The other answer may lead to problems when resizing the figure. Here is a different approach, positionning the images inside annotation boxes, which are anchored in data coordinates.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
path = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Tango-example_icons.png"
image = plt.imread(path)[116:116+30, 236:236+30]
x = np.arange(10)
y = np.random.rand(10)
fig, ax = plt.subplots()
ax.plot(x,y)
def plot_images(x, y, image, ax=None):
ax = ax or plt.gca()
for xi, yi in zip(x,y):
im = OffsetImage(image, zoom=72/ax.figure.dpi)
im.image.axes = ax
ab = AnnotationBbox(im, (xi,yi), frameon=False, pad=0.0,)
ax.add_artist(ab)
plot_images(x, y, image, ax=ax)
plt.show()

Categories

Resources