Introduction
- Simple Python and scikit-image techniques can be used to solve genuine image analysis problems.
- Morphometric problems involve the number, shape, and / or size of the objects in an image.
Image Basics
- Digital images are represented as rectangular arrays of square pixels.
- Digital images use a left-hand coordinate system, with the origin in the upper left corner, the x-axis running to the right, and the y-axis running down. Some learners may prefer to think in terms of counting down rows for the y-axis and across columns for the x-axis. Thus, we will make an effort to allow for both approaches in our lesson presentation.
- Most frequently, digital images use an additive RGB model, with eight bits for the red, green, and blue channels.
- scikit-image images are stored as multi-dimensional NumPy arrays.
- In scikit-image images, the red channel is specified first, then the green, then the blue, i.e., RGB.
- Lossless compression retains all the details in an image, but lossy compression results in loss of some of the original image detail.
- BMP images are uncompressed, meaning they have high quality but also that their file sizes are large.
- JPEG images use lossy compression, meaning that their file sizes are smaller, but image quality may suffer.
- TIFF images can be uncompressed or compressed with lossy or lossless compression.
- Depending on the camera or sensor, various useful pieces of information may be stored in an image file, in the image metadata.
Working with scikit-image
- Images are read from disk with the
iio.imread()function. - We create a window that automatically scales the displayed image
with Matplotlib and calling
imshow()on the global figure object. - Colour images can be transformed to grayscale using
ski.color.rgb2gray()or, in many cases, be read as grayscale directly by passing the argumentmode="L"toiio.imread(). - We can resize images with the
ski.transform.resize()function. - NumPy array commands, such as
image[image < 128] = 0, can be used to manipulate the pixels of an image. - Array slicing can be used to extract sub-images or modify areas of
images, e.g.,
clip = image[60:150, 135:480, :]. - Metadata is not retained when images are loaded as NumPy arrays
using
iio.imread().
Drawing and Bitwise Operations
- We can use the NumPy
zeros()function to create a blank, black image. - We can draw on scikit-image images with functions such as
ski.draw.rectangle(),ski.draw.disk(),ski.draw.line(), and more. - The drawing functions return indices to pixels that can be set directly.
Creating Histograms
- In many cases, we can load images in grayscale by passing the
mode="L"argument to theiio.imread()function. - We can create histograms of images with the
np.histogramfunction. - We can display histograms using
ax.plot()with thebin_edgesandhistogramvalues returned bynp.histogram(). - The plot can be customised using
ax.set_xlabel(),ax.set_ylabel(),ax.set_xlim(),ax.set_ylim(), andax.set_title(). - We can separate the colour channels of an RGB image using slicing operations and create histograms for each colour channel separately.
Blurring Images
- Applying a low-pass blurring filter smooths edges and removes noise from an image.
- Blurring is often used as a first step before we perform thresholding or edge detection.
- The Gaussian blur can be applied to an image with the
ski.filters.gaussian()function. - Larger sigma values may remove more noise, but they will also remove detail from an image.
Thresholding
- Thresholding produces a binary image, where all pixels with intensities above (or below) a threshold value are turned on, while all other pixels are turned off.
- The binary images produced by thresholding are held in two-dimensional NumPy arrays, since they have only one colour value channel. They are boolean, hence they contain the values 0 (off) and 1 (on).
- Thresholding can be used to create masks that select only the interesting parts of an image, or as the first step before edge detection or finding contours.
Connected Component Analysis
- We can use
ski.measure.labelto find and label connected objects in an image. - We can use
ski.measure.regionpropsto measure properties of labeled objects. - We can use
ski.morphology.remove_small_objectsto mask small objects and remove artifacts from an image. - We can display the labeled image to view the objects coloured by label.
Capstone Challenge
- Using thresholding, connected component analysis and other tools we can automatically segment images of bacterial colonies.
- These methods are useful for many scientific problems, especially those involving morphometrics.
Multidimensional data
- We can access open a Napari n-dimensional image viewer with
viewer = napari.Viewer(). -
ImageandLabelLayers can be added to a viewer withviewer.add_image()andviewer.add_labels()respectively. - Many scikit-image functions such as
ski.filters.gaussian(),ski.threshold.threshold_otsu(),ski.measure.label()andski.measure.regionprops()work with 3D image data. - We can iterate through time-points to analyse timelapse movies.