This recipe will show how we can make simple yet effective usage of Python matplotlib library to process image channels and display the per-channel histogram of an external image.
We have provided some sample images, but the code is ready to load any image file, provided it is supported by matplotlib's imread
function.
In this recipe, you will learn how to combine different matplotlib plots to achieve functionality of a simple image viewer that displays an image histogram for red, green, and blue channels.
To show how to build an image histogram viewer, we are going to implement a simple class named ImageViewer
, and that class will contain helper methods to:
The following code shows how to build an image histogram viewer:
import matplotlib.pyplot as plt import matplotlib.image as mplimage import matplotlib as mpl import os class ImageViewer(object): def __init__(self, imfile): self._load_image(imfile) self._configure() self.figure = plt.gcf() t = "Image: {0}".format(os.path.basename(imfile)) self.figure.suptitle(t, fontsize=20) self.shape = (3, 2) def _configure(self): mpl.rcParams['font.size'] = 10 mpl.rcParams['figure.autolayout'] = False mpl.rcParams['figure.figsize'] = (9, 6) mpl.rcParams['figure.subplot.top'] = .9 def _load_image(self, imfile): self.im = mplimage.imread(imfile) @staticmethod def _get_chno(ch): chmap = {'R': 0, 'G': 1, 'B': 2} return chmap.get(ch, -1) def show_channel(self, ch): bins = 256 ec = 'none' chno = self._get_chno(ch) loc = (chno, 1) ax = plt.subplot2grid(self.shape, loc) ax.hist(self.im[:, :, chno].flatten(), bins, color=ch, ec=ec, label=ch, alpha=.7) ax.set_xlim(0, 255) plt.setp(ax.get_xticklabels(), visible=True) plt.setp(ax.get_yticklabels(), visible=False) plt.setp(ax.get_xticklines(), visible=True) plt.setp(ax.get_yticklines(), visible=False) plt.legend() plt.grid(True, axis='y') return ax def show(self): loc = (0, 0) axim = plt.subplot2grid(self.shape, loc, rowspan=3) axim.imshow(self.im) plt.setp(axim.get_xticklabels(), visible=False) plt.setp(axim.get_yticklabels(), visible=False) plt.setp(axim.get_xticklines(), visible=False) plt.setp(axim.get_yticklines(), visible=False) axr = self.show_channel('R') axg = self.show_channel('G') axb = self.show_channel('B') plt.show() if __name__ == '__main__': im = 'images/yellow_flowers.jpg' try: iv = ImageViewer(im) iv.show() except Exception as ex: print ex
Reading from the end of the code, we see hard-coded filenames. These can be swapped by loading the argument from the command line and parsing the given argument into the im
variable using the sys.argv
sequence.
We instantiate the ImageViewer
class with the provided path to an image file. During object instantiation, we try to load an image file into an array, configure the figure via the rcParams
dictionary, set the figure size and title, and define the object fields (self.shape) to be used inside the object's methods.
The main method here is show()
, which creates a layout for the figure and loads the image arrays into the main (left column) subplot. We hide any ticks and tick labels as this is the actual image, where we don't have to use the ticks.
We then call the private show_channel()
method for each of the red, green, and blue channels. This method also creates new subplot axes, this time in the right-hand side column, with each one in a separate row. We plot the histogram for each channel in a separate subplot.
We also set up a little plot to remove unnecessary x
ticks, and add a legend in case we want to print this figure in a non-color environment, in which case we can discern channel representation even in those environments.
After we run this code, we will get the following screenshot:
The use of the histogram plot type is just a choice for this image viewer example. We could have used any of the matplotlib supported plot types. Another real-world example would be to plot an EEG or similar medical records where we would want to display slice as an image, the time series of the EEG recorded as a line plot, and also additional meta information about the data shown, that would probably go into matplotlib.text.Text
artists.
Having the ability to interact with the user GUI event, matplotlib's figure allows us also to implement interaction where we would want to zoom into all plots if we manually zoom on one plot only. That would be another usage where we want to display an image and zoom into it while also zooming into other displayed plots in the currently active figure. An idea would be to use motion_notify_event
to call a function that will update x and y limits for all axes (subplots) in the current figure.
3.133.158.32