#!/usr/bin/python3 # -*- coding: utf-8 -*- """ A short tutorial on plotting images with Matplotlib. This tutorial will use Matplotlib's imperative-style plotting interface, pyplot. This interface maintains global state, and is very useful for quickly and easily experimenting with various plot settings. The alternative is the object-oriented interface, which is also very powerful, and generally more suitable for large application development. If you'd like to learn about the object-oriented interface, a great place to start is our :doc:`Usage guide `. For now, let's get on with the imperative-style approach: """ # TODO fix the bad examples below... import matplotlib.pyplot as plt import matplotlib.image as mpimg ############################################################################### # .. _importing_data: # # Importing image data into Numpy arrays # ====================================== # # Matplotlib relies on the Pillow_ library to load image data. # # .. _Pillow: https://pillow.readthedocs.io/en/latest/ # # Here's the image we're going to play with. # It's a 24-bit RGB PNG image (8 bits for each of R, G, B). Depending # on where you get your data, the other kinds of image that you'll most # likely encounter are RGBA images, which allow for transparency, or # single-channel grayscale (luminosity) images. You can right click on # it and choose "Save image as" to download it to your computer for the # rest of this tutorial. # # And here we go... img = mpimg.imread("realpython.jpg") print(img) print(img.shape) ############################################################################### # Note the dtype there - float32. Matplotlib has rescaled the 8 bit # data from each channel to floating point data between 0.0 and 1.0. As # a side note, the only datatype that Pillow can work with is uint8. # Matplotlib plotting can handle float32 and uint8, but image # reading/writing for any format other than PNG is limited to uint8 # data. Why 8 bits? Most displays can only render 8 bits per channel # worth of color gradation. Why can they only render 8 bits/channel? # Because that's about all the human eye can see. More here (from a # photography standpoint): `Luminous Landscape bit depth tutorial # `_. # # Each inner list represents a pixel. Here, with an RGB image, there # are 3 values. Since it's a black and white image, R, G, and B are all # similar. An RGBA (where A is alpha, or transparency), has 4 values # per inner list, and a simple luminance image just has one value (and # is thus only a 2-D array, not a 3-D array). For RGB and RGBA images, # Matplotlib supports float32 and uint8 data types. For grayscale, # Matplotlib supports only float32. If your array data does not meet # one of these descriptions, you need to rescale it. # # Plotting numpy arrays as images # =================================== # # So, you have your data in a numpy array (either by importing it, or by # generating it). Let's render it. In Matplotlib, this is performed # using the :func:`~matplotlib.pyplot.imshow` function. Here we'll grab # the plot object. This object gives you an easy way to manipulate the # plot from the prompt. imgplot = plt.imshow(img) plt.show() ############################################################################### # You can also plot any numpy array. # # Applying pseudocolor schemes to image plots # ------------------------------------------------- # # Pseudocolor can be a useful tool for enhancing contrast and # visualizing your data more easily. This is especially useful when # making presentations of your data using projectors - their contrast is # typically quite poor. # # Pseudocolor is only relevant to single-channel, grayscale, luminosity # images. We currently have an RGB image. Since R, G, and B are all # similar (see for yourself above or in your data), we can just pick one # channel of our data: lum_img = img[:, :, 0] # This is array slicing. You can read more in the `Numpy tutorial # `_. plt.imshow(lum_img) plt.show() ############################################################################### # Now, with a luminosity (2D, no color) image, the default colormap (aka lookup table, # LUT), is applied. The default is called viridis. There are plenty of # others to choose from. plt.imshow(lum_img, cmap="hot") plt.show() ############################################################################### # Note that you can also change colormaps on existing plot objects using the # :meth:`~matplotlib.cm.ScalarMappable.set_cmap` method: imgplot = plt.imshow(lum_img) imgplot.set_cmap("nipy_spectral") plt.show() ############################################################################### # # However, remember that in the Jupyter Notebook with the inline backend, # you can't make changes to plots that have already been rendered. If you # create imgplot here in one cell, you cannot call set_cmap() on it in a later # cell and expect the earlier plot to change. Make sure that you enter these # commands together in one cell. plt commands will not change plots from earlier # cells. # # There are many other colormap schemes available. See the `list and # images of the colormaps # <../colors/colormaps.html>`_. # # Color scale reference # ------------------------ # # It's helpful to have an idea of what value a color represents. We can # do that by adding a color bar to your figure: imgplot = plt.imshow(lum_img) plt.colorbar() plt.show() ############################################################################### # Examining a specific data range # --------------------------------- # # Sometimes you want to enhance the contrast in your image, or expand # the contrast in a particular region while sacrificing the detail in # colors that don't vary much, or don't matter. A good tool to find # interesting regions is the histogram. To create a histogram of our # image data, we use the :func:`~matplotlib.pyplot.hist` function. plt.hist(lum_img.ravel(), bins=256, range=(0.0, 1.0), fc="k", ec="k") plt.show() ############################################################################### # Most often, the "interesting" part of the image is around the peak, # and you can get extra contrast by clipping the regions above and/or # below the peak. In our histogram, it looks like there's not much # useful information in the high end (not many white things in the # image). Let's adjust the upper limit, so that we effectively "zoom in # on" part of the histogram. We do this by passing the clim argument to # imshow. You could also do this by calling the # :meth:`~matplotlib.cm.ScalarMappable.set_clim` method of the image plot # object, but make sure that you do so in the same cell as your plot # command when working with the Jupyter Notebook - it will not change # plots from earlier cells. # # You can specify the clim in the call to ``plot``. imgplot = plt.imshow(lum_img, clim=(0.0, 0.7)) plt.show() ############################################################################### # You can also specify the clim using the returned object fig = plt.figure() ax = fig.add_subplot(1, 2, 1) imgplot = plt.imshow(lum_img) ax.set_title("Before") plt.colorbar(ticks=[0.1, 0.3, 0.5, 0.7], orientation="horizontal") ax = fig.add_subplot(1, 2, 2) imgplot = plt.imshow(lum_img) imgplot.set_clim(0.0, 0.7) ax.set_title("After") plt.colorbar(ticks=[0.1, 0.3, 0.5, 0.7], orientation="horizontal") plt.show()