Blob Detection#

This example demonstrates blob detection techniques available in Sigima for analyzing circular or blob-like features in images. It shows how to generate apply preprocessing filters and detect blobs using OpenCV-based algorithms with fine-tuned parameters for optimal results.

The script demonstrates image processing workflows commonly used in microscopy, particle analysis, and feature detection applications.

Importing necessary modules#

We start by importing all the required modules for image processing and visualization. To run this example, ensure you have all the required dependencies installed.

import numpy as np
import skimage.draw

import sigima.objects
import sigima.proc.image
from sigima import viz

Generate synthetic test image with known blobs#

We create a synthetic image with a noisy background and several circular blobs of varying sizes and intensities. This allows us to validate the blob detection algorithms effectively. To perform this task, we use a function: the detailes of this is out of the scope of this tutorial: once you have learned how to use Sigima, you will be able to use the library on your own data.

def generate_test_image() -> None:
    """Generate test image with randomly placed blobs."""
    rng = np.random.default_rng(0)
    arr = rng.normal(10000, 1000, (2048, 2048))
    for _ in range(10):
        row = rng.integers(0, arr.shape[0])
        col = rng.integers(0, arr.shape[1])
        rr, cc = skimage.draw.disk((row, col), 40, shape=arr.shape)
        arr[rr, cc] -= rng.integers(5000, 6000)
    icenter = arr.shape[0] // 2
    rr, cc = skimage.draw.disk((icenter, icenter), 200, shape=arr.shape)
    arr[rr, cc] -= rng.integers(5000, 8000)
    data = np.clip(arr, 0, 65535).astype(np.uint16)

    # Create a new image object
    image = sigima.objects.create_image("Test image", data, units=("mm", "mm", "lsb"))
    return image


original_image = generate_test_image()

print("✓ Test image created successfully!")
✓ Test image created successfully!

Visualize the original image#

We visualize the original synthetic image to understand its characteristics before applying any processing or blob detection. This can be done with your preferred image viewer (i.e. plotpy, matplotlib, …). The preference of Sigima developers is on plotpy, a library developed with performance in mind.

We wrapped a simple function to do perform visualizations tasks required for this and other tutorials, to help reducing the impact of GUI code in documentation and let you concentrate in the analysis

viz.view_images([original_image], title="Original Test Image with Synthetic Blobs")
Qt widget 1

Image preprocessing - Binning#

Looking to our image, we can see that the blobs we look for are large respect to the pixel size. A binning process can help to reduce the importance of the noise.

binning_factor = 2
binned_image = sigima.proc.image.binning(original_image, binning_factor)

print(f"\n✓ Binning applied with factor {binning_factor}")
print(f"Original size: {original_image.data.shape}")
print(f"Binned size: {binned_image.data.shape}")
print("Binning reduces computational load and can improve blob detection")

# Compare original and binned images
viz.view_images_side_by_side(
    [original_image, binned_image],
    titles=["Original Image", "Binned Image (2x2)"],
    title="Image Binning Comparison",
)
Qt widget 1
✓ Binning applied with factor 2
Original size: (2048, 2048)
Binned size: (1024, 1024)
Binning reduces computational load and can improve blob detection

Additional preprocessing - Moving median filter#

The result of the binning is good, but we can imagine to not be happy with that. A different approach we can take is to apply a moving median filter, to reduce the importance of the spikes. We do it with a window of 5, of curse in practice different window sizes can be tested to find the good compromise between noise reduction and resolution. Lets see how to do that.

filter_size = 5
filtered_image = sigima.proc.image.moving_median(binned_image, n=filter_size)

print(f"\n✓ Moving median filter applied (window size: {filter_size})")

# Show progression of preprocessing steps
viz.view_images_side_by_side(
    [original_image, binned_image, filtered_image],
    titles=["Original", "Binned", "Median Filtered"],
    title="Image Preprocessing Pipeline",
)
Qt widget 1
✓ Moving median filter applied (window size: 5)

Configure blob detection parameters#

We are happy with the filtered image, we can now proceed to the blob detection. First of all we need to configure the parameters of the detection algorithm: this is very important to get good results. In this example, you find an overview of the parameters that can be tuned.

Create blob detection parameter object:

Threshold parameters for blob detection:

Minimum repeatability (how many times a blob center is detected):

Color filtering (not used for grayscale):

Area filtering to select appropriate blob sizes:

blob_param.filter_by_area = True
blob_param.min_area = 600.0  # Minimum area in pixels
blob_param.max_area = 6000.0  # Maximum area in pixels

Circularity filtering to prefer round objects:

blob_param.filter_by_circularity = True
blob_param.min_circularity = 0.8  # 0 = not circular, 1 = perfect circle
blob_param.max_circularity = 1.0

Disable inertia and convexity filtering for this example:

We finally print configured parameters:

print("\n✓ Blob detection parameters configured:" + "\n")
print(blob_param)
✓ Blob detection parameters configured:

Blob detection using OpenCV:
    _roi_g:
      Create regions of interest: ☐
      ROI geometry: Rectangle
    Min. threshold: 10.0
    Max. threshold: 200.0
    Min. repeatability: 2
    Min. distance between blobs: 10.0
    Filter by color: ☐
    Blob color: 0
    Filter by area: ☑
    Min. area: 600.0
    Max. area: 6000.0
    Filter by circularity: ☑
    Min. circularity: 0.8
    Max. circularity: 1.0
    Filter by inertia: ☐
    Min. inertia ratio: 0.6
    Max. inertia ratio: 1.0
    Filter by convexity: ☐
    Min. convexity: 0.8
    Max. convexity: 1.0

Perform blob detection#

We can now perform the blob detection on the preprocessed image using the configured parameters.

# Detect blobs in the preprocessed image
blobs = sigima.proc.image.blob_opencv(filtered_image, blob_param)

print("\n✓ Blob detection completed!")
print(f"  Number of blobs detected: {len(blobs.coords) if blobs else 0}")

viz.view_images(
    [filtered_image],
    title="Filtered Image with Blob Detection",
    results=blobs,
    colormap="gray",
)
Qt widget 1
✓ Blob detection completed!
  Number of blobs detected: 8

We print the detected blobs and their properties:

if blobs and len(blobs.coords) > 0:
    blobs_df = blobs.to_dataframe()
    print("\nDetected blobs data frame:")
    print(blobs_df)

else:
    print("No blobs detected. Consider adjusting detection parameters.")
Detected blobs data frame:
   roi_index            x            y          r
0         -1  1929.505005  1629.516724  40.535240
1         -1  1550.487915  1070.406860  40.601971
2         -1  1444.442627   626.465759  40.596977
3         -1   197.523972   320.457184  40.515938
4         -1   749.518250   159.622253  40.518841
5         -1   368.359985   520.453979  40.556187
6         -1  1456.451782   227.557236  40.577053
7         -1   682.408081  1821.558105  40.533581

Gallery generated by Sphinx-Gallery