1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
"""
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.tests import vistools
# %%
# 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!")
# %%
# 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
vistools.view_images([original_image], title="Original Test Image with Synthetic Blobs")
# %%
# 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
vistools.view_images_side_by_side(
[original_image, binned_image],
titles=["Original Image", "Binned Image (2x2)"],
title="Image Binning Comparison",
)
# %%
# 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
vistools.view_images_side_by_side(
[original_image, binned_image, filtered_image],
titles=["Original", "Binned", "Median Filtered"],
title="Image Preprocessing Pipeline",
)
# %%
# 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:
blob_param = sigima.proc.image.BlobOpenCVParam()
# %%
# Threshold parameters for blob detection:
blob_param.min_threshold = 10
blob_param.max_threshold = 200
# %%
# Minimum repeatability (how many times a blob center is detected):
blob_param.min_repeatability = 2
# %%
# Color filtering (not used for grayscale):
blob_param.filter_by_color = False
# %%
# 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:
blob_param.filter_by_inertia = False
blob_param.filter_by_convexity = False
# %%
# We finally print configured parameters:
print("\n✓ Blob detection parameters configured:" + "\n")
print(blob_param)
# %%
# 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}")
vistools.view_images(
[filtered_image],
title="Filtered Image with Blob Detection",
results=blobs,
colormap="gray",
)
# %%
# 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.")
|