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 argument mode="L" to iio.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 the iio.imread() function.
  • We can create histograms of images with the np.histogram function.
  • We can display histograms using ax.plot() with the bin_edges and histogram values returned by np.histogram().
  • The plot can be customised using ax.set_xlabel(), ax.set_ylabel(), ax.set_xlim(), ax.set_ylim(), and ax.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 and Label Layers can be added to a viewer with viewer.add_image() and viewer.add_labels() respectively.
  • Many scikit-image functions such as ski.filters.gaussian(), ski.threshold.threshold_otsu(), ski.measure.label() and ski.measure.regionprops() work with 3D image data.
  • We can iterate through time-points to analyse timelapse movies.