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.histogram
function. - We can display histograms using
ax.plot()
with thebin_edges
andhistogram
values 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.label
to find and label connected objects in an image. - We can use
ski.measure.regionprops
to measure properties of labeled objects. - We can use
ski.morphology.remove_small_objects
to 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()
. -
Image
andLabel
Layers 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.