We have seen in the previous notebook that images are simply matrices of numbers that can easily be handled as Numpy arrays. To visualize some operations, we plotted the result using Matplotlib but didn't dive in how exactly that worked. Here we review the basics that will allow you to plot one or multiple images, and make basic adjustments to the output.
There are many great libraries such as Altair, Plotly or Bokeh that allow one to easily create beautiful plots with little efforts (often less that with Matplotlib). However when specifically dealing with (2D) images, Matplotlib remains a reference, in particular in the perspective of creating "publish-ready" figures. It indeed allows one to customize every detail of a figure, in a way difficult to achieve with other tools. It also serves as a basis for other plotting libraries, and it is therefore also helpful to be familiar with it.
pyplot
¶All the necessary plotting functions reside in the pyplot
module of Matplotlib. Except for the color module that we use to create colormaps, it is the only module used throughout this course. plt
contains for example all the functions for various plot types:
plt.imshow()
plt.plot
plt.hist()
Let's import it with it's standard abbreviation plt
:
import matplotlib.pyplot as plt
We also import again skimage to read an image that we will use in the plotting demos (see next chapter to learn about importing images) and numpy for general calculations:
import numpy as np
import skimage.io
image = skimage.io.imread('../Data/neuron.tif')
Now we can directly use the functions from plt
to disaply e.g. our image:
plt.imshow(image);
As any other function, plt.imshow
can take optional arguments allowing you to change the rendering of the image. For example you can choose a color map to replace the default viridis:
plt.imshow(image, cmap = 'inferno');
What is actually happening when we plot this image? Matplotlib does a lot of things in the background: it creates a figure for us, adds axis to the figure and finally displays the image. It's great that we didn't have to care about all these things, but as soon as we want to create more complex plots, we'll need access to all these elements.
While it is possible to customize a plot to some level using simply plt.
calls, it makes it rather difficult to clearly "control" the plot.
In order to gain more control on the plot, we need to gain control on the elements that constitute it. Those are:
Figure
object which contains all elements of the figureAxes
object, the actual plots that belong to a figure objectWe can gain this control by explicity creating these objects via the subplots()
function:
fig, ax = plt.subplots()
We see that we just get an empty figure with axes that we should now fill. For example the ax
object can create an image plot on its own:
fig, ax = plt.subplots()
ax.imshow(image, cmap = 'inferno');
We can go further and customize other elements of the plot. Again, this is only possible because we have reference to the "image-objects". For example we can add labels:
fig, ax = plt.subplots()
ax.imshow(image, cmap = 'inferno');
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis');
ax.set_title('Neuron');
We can also superpose multiple plots. As we want all of them to share the same axis, we use the same ax
reference. For example we can add a line plot:
fig, ax = plt.subplots()
ax.imshow(image, cmap = 'inferno');
ax.plot(np.random.randint(0,1200,10),np.random.randint(0,1000,10))
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis');
ax.set_title('Neuron');
And finally we can export our image as an independent picture using the fig
reference:
fig.savefig('My_first_plot.png')
fig, ax = plt.subplots(figsize=(2,2))
ax.imshow(image, cmap = 'inferno', vmin = 0, vmax = 100);
figsize
in the subplot
function allows us to specify the figure dimensionscmap
specifies the colormap (or lookup table). A complete list can be found herevmin
and vmax
specify the intensity range to displaypoints = np.random.randint(0,1000,(100,100))
fig, ax = plt.subplots()
ax.plot(points[:,0],points[:,1],'r-o', linewidth = 0.2, markersize = 4);
'r-o'
is a code to specify the look of the plot. E.g. here 'r' stands for red, '-' stand for line, and 'o' stand for dot. See here for all options.linewidth
and markersize
are self-explanatorydata = np.random.normal(10,3, 10000)
plt.hist(data, bins = np.arange(0,25,0.2),color = 'r', alpha = 0.5, density=True);
bins
specifies the bins to use and can have various formats. In this course it is always used as a list of positions corresponding to bin edges.color
specifies the bin coloralpha
sets the transparency e.g. when superposing histogramsdensity
specifies whether the bin hight should be normalized to sum up to 1.There are multiple ways of creating figures with a grid of plots. We show here two used throughout this course.
In the first case, we specify the size of the grid when creating the figure with plt.subplots()
. This provides a list of Axes
objects, each corresponding to one element of the grid:
fig, ax = plt.subplots(2,2)
ax.shape
(2, 2)
We access each element of the ax
array like a regular list and use them to plot:
# create figure with 2x2 subplots
fig, ax = plt.subplots(2,2, figsize=(10,10))
# fill each subplot
ax[0,0].imshow(image)
ax[0,1].hist(data, bins = np.arange(0,25,0.2),color = 'r', alpha = 0.5, density=True);
ax[1,0].plot(points[:,0],points[:,1],'r-o', linewidth = 0.2, markersize = 4);
# here we add two plots to the last subplot
ax[1,1].imshow(image)
ax[1,1].plot(points[:,0],points[:,1],'r-o', linewidth = 0.2, markersize = 4);
# we can add titles to subplots
ax[0,0].set_title('image')
ax[0,1].set_title('histogram')
ax[1,0].set_title('lineplot')
ax[1,1].set_title('combination');
add_subplot
¶Here we only create a figure, and progressively add new subplots in a pre-determined grid. This variant is useful when programmatically creating a figure, as it easily allows to create plots in a loop:
# create a figure
fig = plt.figure(figsize=(7,7))
for x in range(1,5):
# add subplot and create an axis
ax = fig.add_subplot(2,2,x)
# plot the histogram in the axis
ax.hist(np.random.normal(10,x,10000),bins = np.arange(0,20,0.1))
# customize axis
ax.set_title(f'Width: {x}')