Matplotlib: How to plot images instead of points? - python

I want to read a list of images into Python/Matplotlib and then plot this images instead of other markers (like points) in a graph. I have tried with imshow but I didn't succeed, because I cannot shift the image to another position and scale it appropriately. Maybe somebody has a good idea : )

There are two ways to do this.
Plot the image using imshow with the extent kwarg set based on the location you want the image at.
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()

If you want different images:
This is now the first reply when googling "matplotlib scatter with images". If you're like me and actually need to plot different images on each image, try this minimalied example instead. Just be sure to input your own images.
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
def getImage(path, zoom=1):
return OffsetImage(plt.imread(path), zoom=zoom)
paths = [
'a.jpg',
'b.jpg',
'c.jpg',
'd.jpg',
'e.jpg']
x = [0,1,2,3,4]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.scatter(x, y)
for x0, y0, path in zip(x, y,paths):
ab = AnnotationBbox(getImage(path), (x0, y0), frameon=False)
ax.add_artist(ab)

Related

Getting viridis colorbar for all cmaps in matplotlib

I am trying to visualize the features obtained from TSNE. I have two questions:
Why am I getting viridis in color bar even though I have specified another cmap?
I want to use the cmap colors to plot the features for all images in a 3d plot. I want to use a single color for all 256 coordinates of a single image. Is the below code correctly doing so?
Below is the code I am using:
import numpy as np
import matplotlib.pyplot as plt
cmap = plt.cm.get_cmap('plasma', 1000)
i = 0
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
features = np.random.rand(1000, 256, 2)
for fvec in features:
if(i%50 == 0):
x_coord, y_coord = fvec.T
p = ax.scatter(i, x_coord, y_coord, color=cmap(i))
i = i+1
fig.colorbar(p)
plt.show()
This is the output that I am getting.
EDIT: First question is solved with help from comments.
Clarification on second question:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
features = np.random.rand(1, 100, 2)
i = 0
for fvec in features:
x_coord, y_coord = fvec.T
p = ax.scatter(i, x_coord, y_coord, c=i*np.ones(len(x_coord)), norm=plt.Normalize(0,1), cmap='plasma')
i = i+1
fig.colorbar(p)
plt.show()
In this, among the 100 points plotted, some are of a darker color and some lighter. Are all points not using the same color?
See the output here

Wireframing from an image using matplotlib

I'm trying to make a 3D representation of an image as a surface using wireframes with matplotlib.
ig= mpimg.imread('testIMG.png');
X = np.linspace(0,len(ig[0]),len(ig[0])); #List of discrete x values
Y = np.linspace(0,len(ig[1]),len(ig[1])); #List of discrete y values
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#Plot the wireframe
#I want to plot the image as f(x,y) and I can't understand why wireframe won't let me
ax.plot_wireframe(X, Y, ig[:,:,2], rstride=10, cstride=10)
plt.show()
The imread function gives me an MxNx3 array of M rows, N columns, and an RGB value for each point in the matrix. I don't understand how to use wireframe to plot that data properly. These z values aren't plotting what I expected (a checkerboard pattern), but instead a y=x line alternating between 0 and 1.
What do I need to do here? I want a series of cuboids in a 3D checkerboard pattern.
Image of what I have currently
You may use np.meshgrid(), so:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import mpl_toolkits.mplot3d
ig = mpimg.imread('testIMG.png')
x = np.linspace(0, ig.shape[1], ig.shape[1]) #List of discrete x values
y = np.linspace(0, ig.shape[0], ig.shape[0]) #List of discrete y values
X, Y = np.meshgrid(x, y)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#Plot the wireframe
#I want to plot the image as f(x,y) and I can't understand why wireframe won't let me
ax.plot_wireframe(X, Y, ig[:,:,2], rstride=10, cstride=10)
plt.show()

matplotlib plot_surface 3D plot with non-linear color map

I have this following python code, which displays the following 3D plot.
My code is:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
# Generate data example
X,Y = np.meshgrid(np.arange(-99,-90), np.arange(-200,250,50))
Z = np.zeros_like(X)
Z[:,0] = 100.
Z[4][7] = 10
# Normalize to [0,1]
Z = (Z-Z.min())/(Z.max()-Z.min())
colors = cm.viridis(Z)
rcount, ccount, _ = colors.shape
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount,
facecolors=colors, shade=False)
surf.set_facecolor((0,0,0,0))
plt.show()
I want to color the irregularities on the XY plane in a different color. I want to be able to highlight the bumps on the XY plane.
How do I do that?
The problem is that the grid is not very dense. The bump consist of a single pixel. So there are 4 cells in the grid, 3 of which have their lower left corner at 0, and would hence not receive a different color according to their value. Only the one pixel which actually is the bump gets colorized.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
X,Y = np.meshgrid(np.arange(-99,-90), np.arange(-200,250,50))
Z = np.zeros_like(X)
Z[:,0] = 100.
Z[4][7] = 10
norm = plt.Normalize(Z.min(),Z.min()+10 )
colors = cm.viridis(norm(Z))
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, facecolors=colors, shade=False)
surf.set_facecolor((0,0,0,0))
plt.show()
Now you may expand the colorized part of the plot, e.g. using scipy.ndimage.grey_dilation, such that all pixels that are adjacent also become yellow.
from scipy import ndimage
C = ndimage.grey_dilation(Z, size=(2,2), structure=np.ones((2, 2)))
norm = plt.Normalize(Z.min(),Z.min()+10 )
colors = cm.viridis(norm(C))

How to rotate a 3d plot in python? (or as a animation) Rotate 3-D view using mouse

I have this code which contains a 3D plot. I run the code in Spyder; I want to know if it is possible to make this plot a rotating one (360 degrees) and save it.
Thanks!
P.s. Sorry if it is a silly question, but I am a newby in Python.
import matplotlib.pyplot as plt
import numpy as np
from scipy import array
jet = plt.get_cmap('jet')
from matplotlib import animation
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.linspace(70,40,4)
Y = np.linspace(5,2,4)
X,Y= np.meshgrid(X, Y)
Z = array ([
[1223.539555, 1428.075086,1714.479425, 2144.053223],
[1567.26647,1829.056119,2990.416079,2745.320067],
[2135.163957,2491.534201, 2990.416079,3738.761638],
[3257.280827, 3800.655101, 4561.372117, 5702.458776],
])
surf = ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1, cmap = jet,linewidth = 0,alpha= 1)
ax.set_zlim3d(0, Z.max())
fig.colorbar(surf, shrink=0.8, aspect=5)
ax.set_xlabel('Axial Length [mm]')
ax.set_ylabel('nbTurns')
ax.set_zlabel('RPM')
plt.show()
You need to define a function in order to get a specific animation. In your case it is a simple rotation:
def rotate(angle):
ax.view_init(azim=angle)
Then use the matplotlib animation:
rot_animation = animation.FuncAnimation(fig, rotate, frames=np.arange(0,362,2),interval=100)
This will call the rotate function with the frames argument as angles and with an interval of 100ms, so this will result in a rotation over 360° with a 2° step each 100ms. To save the animation as a gif file:
rot_animation.save('path/rotation.gif', dpi=80, writer='imagemagick')

Jupyter Notebook: Output image in previous line

I want to plot some image side by side in my jupyter notebook. So it can save some space for display. For example
This is done through
fig = plt.figure(figsize=(14,3))
ax1 = fig.add_subplot(1,3,1,projection = '3d')
ax2 = fig.add_subplot(1,3,2)
ax3 = fig.add_subplot(1,3,3)
And this makes them in one .png file. However, later on in writing the paper, I may only want part of the image. For example, the 2nd or the 3rd in previous plot. And this requires me to crop the image manually.
One way I can think of, is to make each subplot seperately, but display them in same line. In Python/Jupyter Notebook, the string output can achieve this by adding a comma at the end of previous line:
print 5,
print 6
# returns 5, 6
# instead of
# 5
# 6
I'm wondering if there is anything similar in Jupyter Nobebook, that can do something like
plot fig1,
plot fig2
# Out put [fig1],[fig2]
# instead of
# fig1
# fig2
Output fig1, fig2 in the same line, but in seperate .png file?
use the following align_figures():
def align_figures():
import matplotlib
from matplotlib._pylab_helpers import Gcf
from IPython.display import display_html
import base64
from ipykernel.pylab.backend_inline import show
images = []
for figure_manager in Gcf.get_all_fig_managers():
fig = figure_manager.canvas.figure
png = get_ipython().display_formatter.format(fig)[0]['image/png']
src = base64.encodebytes(png).decode()
images.append('<img style="margin:0" align="left" src="data:image/png;base64,{}"/>'.format(src))
html = "<div>{}</div>".format("".join(images))
show._draw_called = False
matplotlib.pyplot.close('all')
display_html(html, raw=True)
Here is a test:
fig1, ax1 = pl.subplots(figsize=(4, 3))
fig2, ax2 = pl.subplots(figsize=(4, 3))
fig3, ax3 = pl.subplots(figsize=(4, 3))
align_figures()
The code assumes that the output format is PNG image.
first let me recommend you use a colormap other than the jet colormap for the reasons detailed in A better colormap for matplotlib.
As to what you want to do you can achieve this with a modified code from: https://stackoverflow.com/a/26432947/835607
I've extended that function to handle the zaxis of 3d plots as well as the colorbars you are using.
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.transforms import Bbox
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.ticker import LinearLocator, FormatStrFormatter
def full_extent(ax, xpad=0.0, ypad=0.0, cbar=None):
"""Modified from https://stackoverflow.com/a/26432947/835607
Get the full extent of an axes, including axes labels, tick labels, and
titles.
You may need to pad the x or y dimension in order to not get slightly chopped off labels
For text objects, we need to draw the figure first, otherwise the extents
are undefined. These draws can be eliminated by calling plt.show() prior
to calling this function."""
ax.figure.canvas.draw()
items = ax.get_xticklabels() + ax.get_yticklabels()
items += [ax, ax.title, ax.xaxis.label, ax.yaxis.label]
if '3D' in str(type(ax)):
items += ax.get_zticklabels() +[ax.zaxis.label]
if cbar:
items+=cbar.ax.get_yticklabels()
bbox = Bbox.union([cbar.ax.get_window_extent()]+[item.get_window_extent() for item in items])
else:
bbox = Bbox.union([item.get_window_extent() for item in items])
return bbox.expanded(1.0 + xpad, 1.0 + ypad)
Now for an example I plot 3 subplots and save them all to separate files. Note that the full_extent function has cbar, xpad, and ypad as arguments. For the plots that have colorbars make sure to pass the colorbar axes object to the function. You may also need to play around with the padding to get the best results.
# Make an example plot with 3 subplots...
fig = plt.figure(figsize=(9,4))
#3D Plot
ax1 = fig.add_subplot(1,3,1,projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax1.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis',
linewidth=0, antialiased=False)
ax1.set_zlim(-1.01, 1.01)
ax1.zaxis.set_major_locator(LinearLocator(10))
ax1.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# This plot has a colorbar that we'll need to pass to extent
ax2 = fig.add_subplot(1,3,2)
data = np.clip(np.random.randn(250, 250), -1, 1)
cax = ax2.imshow(data, interpolation='nearest', cmap='viridis')
ax2.set_title('Gaussian noise')
cbar = fig.colorbar(cax)
ax2.set_xlabel('asdf')
ax2.set_ylabel('Some Cool Data')
#3rd plot for fun
ax3 = fig.add_subplot(1,3,3)
ax3.plot([1,4,5,7,7],[3,5,7,8,3],'ko--')
ax3.set_ylabel('adsf')
ax3.set_title('a title')
plt.tight_layout() #no overlapping labels
plt.show() #show in notebook also give text an extent
fig.savefig('full_figure.png') #just in case
# Save just the portion _inside_ the boundaries of each axis
extent1 = full_extent(ax1).transformed(fig.dpi_scale_trans.inverted())
fig.savefig('ax1_figure.png', bbox_inches=extent1)
extent2 = full_extent(ax2,.05,.1,cbar).transformed(fig.dpi_scale_trans.inverted())
fig.savefig('ax2_figure.png', bbox_inches=extent2)
extent3 = full_extent(ax3).transformed(fig.dpi_scale_trans.inverted())
fig.savefig('ax3_figure.png', bbox_inches=extent3)
This plots the three plots on one line as you wanted and creates cropped output images such as this one:

Categories

Resources