Blob Detection and Connected Components — Image Processing
Images always tell story. Combining and complementing each individual component found in the image, a desired narrative can be drawn. And this is what people are after for — the big picture. However, what if you just wanted to examine each individual component independently on its own? Detaching it from the whole picture and creating a different story out if it. Fortunately, it is possible! Image components can be analyzed individually using blob detection techniques and the connected component algorithm.
Let us start with blob detection. Why is there a need for this?
Blob detection
It is among one of the final steps in preparing image data for model building. It is usually done after color detection and noise reduction to finally find the required object from the image for further analysis. Below is a compiled of reasons why blob detection is essential in image processing:
- Find distinctive features
- Describe region around feature
- Compare features to find matches
- Use these matches once compatible
There are three (3) methods to do blob detection — Laplacian of Gaussian (LoG), Difference of Gaussian (DoG), and Determinant of Hessian (DoH). We will not dig deep into the mathematics behind these algorithms rather an python implementation of these concepts will be discussed.
For the sake of illustration, we will be using this image in seeing blob detection in action.
But wait, how can we code if we do not import the necessary python libraries to make all of these work?
from skimage.io import imread, imshow
from skimage.color import rgb2gray
from skimage.feature import blob_dog, blob_log, blob_doh
from math import sqrt
import matplotlib.pyplot as plt
import numpy as np
Code implementation of the three blob detection methods:
blobs_log = blob_log(im_bw, max_sigma=30, num_sigma=10, threshold=.1)# Compute radii in the 3rd column.
blobs_log[:, 2] = blobs_log[:, 2] * sqrt(2) #normalizing and scaling parameter so that it matches theblobs_dog = blob_dog(im_bw, max_sigma=30, threshold=.1)blobs_dog[:, 2] = blobs_dog[:, 2] * sqrt(2)blobs_doh = blob_doh(im_bw, max_sigma=30, threshold=.01)blobs_list = [blobs_log, blobs_dog, blobs_doh]
colors = ['yellow', 'lime', 'red']
titles = ['Laplacian of Gaussian', 'Difference of Gaussian',
'Determinant of Hessian']
sequence = zip(blobs_list, colors, titles)fig, axes = plt.subplots(1, 3, figsize=(9, 3), sharex=True, sharey=True)
ax = axes.ravel()for idx, (blobs, color, title) in enumerate(sequence):
ax[idx].set_title(title)
ax[idx].imshow(im_bw, interpolation='nearest')
for blob in blobs:
y, x, r = blob
c = plt.Circle((x, y), r, color=color, linewidth=2, fill=False)
ax[idx].add_patch(c)
ax[idx].set_axis_off()plt.tight_layout()
plt.show()
From left to right, it can be observed that the detection of the objects becomes more sensitive. As for LoG, it clumps different objects into one detection at some circumstances. While for DoG and DoH, it is the opposite, detection is way more granular more specifically for DoH. Objects that are suppoesed to be detected only once, got detected multiple times.
While we are successful in implementing blob detection to a certain image, in real-life for most of the time, we are not only concerned of blobs that are circular in nature like what is performed above.
Connected Components
Instead, we take a look at connected components as the focal point of interest in the analysis. An evident drawback of this approach is that it is heavily reliant on how clean the data is. Hence, the application of tweaking color spaces and the morphological operations would do the trick. See previous article about morphological operations if you’re new to that term.
Let’s go back to our image.
Before we can use the label and region_properties function of skimage of our connected components, thorough image cleaning must be performed first. Below are user-defined functions to do this:
def multi_dil(im,num):
for i in range(num):
im = dilation(im)
return imdef multi_ero(im,num):
for i in range(num):
im = erosion(im)
return im
We use this chain operation to clean the image:
im_cleaned = multi_ero(multi_dil(im_bw,5),5)
imshow(im_cleaned)
Now that this is relatively clean, let us get the labels and properties of this image!
label_im = label(im_cleaned)
imshow(label_im)
Note: There are connected candies (meaning they will be considered as a single object). Hence, they will have the same label. Morphological operations must be performed to separate the images. However, please note that you will be affecting other objects (meaning you will removing/adding information). So, be creative!
There are a lot of features that can be extracted from this labelled image. To mention a few we have:
Now, let’s use regionprops and look at the following properties:
- area
- perimeter
- bbox — bounding box dimensions
- bbox_area — area of bounding box
- centroid — coordinate of centroid
- convex_image — convex hull of the blob
- convex_area — area of the convex hull
- eccentricity — measure how it fits into an ellipse (0) for circles (how elongated is your object)
- major_axis_length — length of the major moment of the ellipse fitted
- minor_axis_length — length of the minor moment of the ellipse fitted
Let us try to take the area of the first candy:
props=regionprops(label_im)
props[0].area #area (zero) 0th object in the image
Output: 4453
How about bounding box coordinate of the 5th candy?
props[5].bbox #bounding box of 5th image
Output: (118, 473, 209, 521)
There are plenty more that can be done using the label and region_props functions. Have an in-depth understanding on this topic for it will come in handy on the more advanced topics in image processing.
Alas! You now know how to detect blobs and obtain image properties of connected components using skimage functions. Stay tuned for the next article on image segmentation! That would be a treat. So, see you next time.