
Image processing is primarily used to extract different features in an image. Since digital images contain different objects and information, it is evident that this kind of information is extracted from such images.
To do this, we can perform image processing techniques to single out and detect such features and objects. One of the most promising techniques is called Blob Detection.
A Blob, in a sense, is anything that is considered a large object or anything bright in a dark background, in images, we can generalize it as a group of pixel values that forms a somewhat colony or a large object that is distinguishable from its background. Using image processing, we can detect such blobs in an image.
Scikit-Image has different functions that can be used to show the different blob in an image. Let us go through each of them.
Let us first load the example image:
import numpy as np
from skimage.io import imshow, imread
from skimage.color import rgb2gray
import matplotlib.pyplot as plt
sample = imread('flowers2.png')
sample_g = rgb2gray(sample)
fig, ax = plt.subplots(1,2,figsize=(10,5))
ax[0].imshow(sample)
ax[1].imshow(sample_g,cmap='gray')
ax[0].set_title('Colored Image',fontsize=15)
ax[1].set_title('Grayscale Image',fontsize=15)
plt.show()

Now, let us binarize the image:
fig, ax = plt.subplots(1,3,figsize=(15,5))
sample_b = sample_g > 0.6
ax[0].set_title('Grayscale Image',fontsize=20)
ax[0].imshow(sample_g,cmap='gray')
ax[1].plot(sample_g[600])
ax[1].set_ylabel('Pixel Value')
ax[1].set_xlabel('Width of Picture')
ax[1].set_title('Plot of 1 Line',fontsize=15)
ax[2].set_title('Binarized Image',fontsize=15)
ax[2].imshow(sample_b,cmap='gray')

For binarizing, we used a threshold on the pixel value of 0.6 since we can see on the sample plotline that there is a separation within this value.
We usually binarize our image before blob detection since it is easier to work around the smaller dimensions and normalized values.
There are nifty functions in scikit-image where you can use different methods to detect the blobs in the image, some of them are as follows:
Laplacian of Gaussian (LOG)
Determines the blobs by using the Laplacian of Gaussian Method
from skimage.feature import blob_dog, blob_log, blob_doh
fig, ax = plt.subplots(1,2,figsize=(10,5))
ax[0].set_title('Binarized Image',fontsize=15)
ax[0].imshow(sample_g,cmap='gray')
blobs = blob_log(sample_b, max_sigma=30, threshold=0.01)
ax[1].imshow(sample_b, cmap='gray')
for blob in blobs:
y, x, area = blob
ax[1].add_patch(plt.Circle((x, y), area*np.sqrt(2), color='r',
fill=False))
ax[1].set_title('Using LOG',fontsize=15)
plt.tight_layout()
plt.show()

Notice that we were able to detect the circular blobs in the image, even the smallest blobs were detected also.
Difference of Gaussian(DOG)
Determines the blobs by using the difference of two gaussian smoothed image
The code will be the same as above with a variation on the blobs variable by using the direct function blob_dog from scikit-image.
blobs = blob_dog(sample_b, max_sigma=30, threshold=0.01)

Comparing the result of the DOG to LOG, we can see that the DOG method can detect much larger blobs, and also the center coordinates of the blobs are much more centered compared to LOG.
Determinant of Hessian (DOH)
Determines the bobs by using the maximum in the matrix of the Hessian determinant.
blobs = blob_doh(sample_b, max_sigma=30, threshold=0.01)

Comparing the results of DOH, to DOG and LOG, the DOH coordinates of the blobs are much more centered on the edges of the circular ground truth blobs unlike the DOG, and also, it detects way more small blobs compared to LOG.
Connected Components(Labelling)
Another approach in dealing with blob detection is by using the connected component in the image. Using this approach we can easily detect all shapes.
To do so, we need to make sure our binarized image is a little bit cleaned. We can perform a morphological operation on the binarized image to easily clean it. Sample below:
sample_c = im_cleaned = multi_ero(multi_dil(sample_b,5),5)

Notice that when we used a dilation effect, we were able to make the white roses more complete and whole. This will make our labeling much easier and minimized.
There is already a nifty function on the scikit-image library that you can use to label the morphed image. Sample code and graph are as follows:
sample_l = label(sample_c)

Notice on the labeled image figure that we were able to label each blob. All the small blobs are actually also labeled. We can check how many blobs were detected by doing:
sample_rp=regionprops(sample_l)
print('How many Blobs detected?:', len(sample_rp))
How many Blobs detected?: 1541
We were able to detect almost a thousand blobs, this is because the label function will also label even the smallest blob, because it only needs to satisfy one condition, if it is a different pixel value from its surrounding. If it has the same pixel values then it will connect it to the whole group making a much bigger blob.
We can use the function region props to further segment the blobs, some codes as an example follows:
list1 = []
for x in sample_rp:
list1.append(x.area)
list2 = sorted(list(enumerate(list1)),key=lambda x: x[1], reverse=True)[:7]
fig, ax = plt.subplots(1,4,figsize=(15,10))
ax[0].imshow(sample_l)
ax[0].set_title('Labelled Image',fontsize=15)
for x,y in enumerate(list2[:3]):
ax[x+1].imshow(sample_rp[y[0]].image)
ax[x+1].set_title('Biggest Blob'+str(x+1))

Notice that we were able to segment the biggest 3 blobs in the image by using the pixel area of the Connected Components.
SUMMARY
In this article, we were able to show different kinds of functions to use for circular blob detection which are LoG, DoG, and DoH. We were able also to see how connected components can simplify blob detection much easier and lastly, we were able to segment the blobs seen on the image by using region props.
Stay tuned for the next article!