Source code for cdl.computation.image.detection

# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.

"""
Blob detection computation module
---------------------------------

"""

# pylint: disable=invalid-name  # Allows short reference names like x, y, ...

# Note:
# ----
# All dataset classes must also be imported in the cdl.computation.param module.

from __future__ import annotations

import guidata.dataset as gds

import cdl.algorithms.image as alg
from cdl.computation.image import calc_resultshape
from cdl.config import _
from cdl.obj import ImageObj, ResultShape, ShapeTypes


[docs] class GenericDetectionParam(gds.DataSet): """Generic detection parameters""" threshold = gds.FloatItem( _("Relative threshold"), default=0.5, min=0.1, max=0.9, help=_( "Detection threshold, relative to difference between " "data maximum and minimum" ), )
[docs] class Peak2DDetectionParam(GenericDetectionParam): """Peak detection parameters""" size = gds.IntItem( _("Neighborhoods size"), default=10, min=1, unit="pixels", help=_( "Size of the sliding window used in maximum/minimum filtering algorithm" ), ) create_rois = gds.BoolItem(_("Create regions of interest"), default=True)
[docs] def compute_peak_detection( image: ImageObj, p: Peak2DDetectionParam ) -> ResultShape | None: """Compute 2D peak detection with :py:func:`cdl.algorithms.image.get_2d_peaks_coords` Args: imageOutput: input image p: parameters Returns: Peak coordinates """ return calc_resultshape( "peak", "point", image, alg.get_2d_peaks_coords, p.size, p.threshold )
[docs] class ContourShapeParam(GenericDetectionParam): """Contour shape parameters""" shapes = ( ("ellipse", _("Ellipse")), ("circle", _("Circle")), ("polygon", _("Polygon")), ) # The following item is used to store the 'shape type' and is implicitly accessed by # the `cdl.core.gui.processor.base.BaseProcessor.compute_10` method. The keys of the # item choices (i.e. the first element of each tuple of `shapes`) must match the # names of the `cdl.core.model.base.ShapeTypes` (when uppercased). assert {shape[0].upper() for shape in shapes}.issubset( set(ShapeTypes.__members__.keys()) ) shape = gds.ChoiceItem(_("Shape"), shapes, default="ellipse")
[docs] def compute_contour_shape(image: ImageObj, p: ContourShapeParam) -> ResultShape | None: """Compute contour shape fit with :py:func:`cdl.algorithms.image.get_contour_shapes`""" return calc_resultshape( "contour", p.shape, image, alg.get_contour_shapes, p.shape, p.threshold )
[docs] class BaseBlobParam(gds.DataSet): """Base class for blob detection parameters""" min_sigma = gds.FloatItem( "σ<sub>min</sub>", default=1.0, unit="pixels", min=0, nonzero=True, help=_( "The minimum standard deviation for Gaussian Kernel. " "Keep this low to detect smaller blobs." ), ) max_sigma = gds.FloatItem( "σ<sub>max</sub>", default=30.0, unit="pixels", min=0, nonzero=True, help=_( "The maximum standard deviation for Gaussian Kernel. " "Keep this high to detect larger blobs." ), ) threshold_rel = gds.FloatItem( _("Relative threshold"), default=0.2, min=0.0, max=1.0, help=_("Minimum intensity of blobs."), ) overlap = gds.FloatItem( _("Overlap"), default=0.5, min=0.0, max=1.0, help=_( "If two blobs overlap by a fraction greater than this value, the " "smaller blob is eliminated." ), )
[docs] class BlobDOGParam(BaseBlobParam): """Blob detection using Difference of Gaussian method""" exclude_border = gds.BoolItem( _("Exclude border"), default=True, help=_("If True, exclude blobs from the border of the image."), )
[docs] def compute_blob_dog(image: ImageObj, p: BlobDOGParam) -> ResultShape | None: """Compute blobs using Difference of Gaussian method with :py:func:`cdl.algorithms.image.find_blobs_dog` Args: imageOutput: input image p: parameters Returns: Blobs coordinates """ return calc_resultshape( "blob_dog", "circle", image, alg.find_blobs_dog, p.min_sigma, p.max_sigma, p.overlap, p.threshold_rel, p.exclude_border, )
[docs] class BlobDOHParam(BaseBlobParam): """Blob detection using Determinant of Hessian method""" log_scale = gds.BoolItem( _("Log scale"), default=False, help=_( "If set intermediate values of standard deviations are interpolated " "using a logarithmic scale to the base 10. " "If not, linear interpolation is used." ), )
[docs] def compute_blob_doh(image: ImageObj, p: BlobDOHParam) -> ResultShape | None: """Compute blobs using Determinant of Hessian method with :py:func:`cdl.algorithms.image.find_blobs_doh` Args: imageOutput: input image p: parameters Returns: Blobs coordinates """ return calc_resultshape( "blob_doh", "circle", image, alg.find_blobs_doh, p.min_sigma, p.max_sigma, p.overlap, p.log_scale, p.threshold_rel, )
[docs] class BlobLOGParam(BlobDOHParam): """Blob detection using Laplacian of Gaussian method""" exclude_border = gds.BoolItem( _("Exclude border"), default=True, help=_("If True, exclude blobs from the border of the image."), )
[docs] def compute_blob_log(image: ImageObj, p: BlobLOGParam) -> ResultShape | None: """Compute blobs using Laplacian of Gaussian method with :py:func:`cdl.algorithms.image.find_blobs_log` Args: imageOutput: input image p: parameters Returns: Blobs coordinates """ return calc_resultshape( "blob_log", "circle", image, alg.find_blobs_log, p.min_sigma, p.max_sigma, p.overlap, p.log_scale, p.threshold_rel, p.exclude_border, )
[docs] class BlobOpenCVParam(gds.DataSet): """Blob detection using OpenCV""" min_threshold = gds.FloatItem( _("Min. threshold"), default=10.0, min=0.0, help=_( "The minimum threshold between local maxima and minima. " "This parameter does not affect the quality of the blobs, " "only the quantity. Lower thresholds result in larger " "numbers of blobs." ), ) max_threshold = gds.FloatItem( _("Max. threshold"), default=200.0, min=0.0, help=_( "The maximum threshold between local maxima and minima. " "This parameter does not affect the quality of the blobs, " "only the quantity. Lower thresholds result in larger " "numbers of blobs." ), ) min_repeatability = gds.IntItem( _("Min. repeatability"), default=2, min=1, help=_( "The minimum number of times a blob needs to be detected " "in a sequence of images to be considered valid." ), ) min_dist_between_blobs = gds.FloatItem( _("Min. distance between blobs"), default=10.0, min=0.0, nonzero=True, help=_( "The minimum distance between two blobs. If blobs are found " "closer together than this distance, the smaller blob is removed." ), ) _prop_col = gds.ValueProp(False) filter_by_color = gds.BoolItem( _("Filter by color"), default=True, help=_("If true, the image is filtered by color instead of intensity."), ).set_prop("display", store=_prop_col) blob_color = gds.IntItem( _("Blob color"), default=0, help=_( "The color of the blobs to detect (0 for dark blobs, 255 for light blobs)." ), ).set_prop("display", active=_prop_col) _prop_area = gds.ValueProp(False) filter_by_area = gds.BoolItem( _("Filter by area"), default=True, help=_("If true, the image is filtered by blob area."), ).set_prop("display", store=_prop_area) min_area = gds.FloatItem( _("Min. area"), default=25.0, min=0.0, help=_("The minimum blob area."), ).set_prop("display", active=_prop_area) max_area = gds.FloatItem( _("Max. area"), default=500.0, min=0.0, help=_("The maximum blob area."), ).set_prop("display", active=_prop_area) _prop_circ = gds.ValueProp(False) filter_by_circularity = gds.BoolItem( _("Filter by circularity"), default=False, help=_("If true, the image is filtered by blob circularity."), ).set_prop("display", store=_prop_circ) min_circularity = gds.FloatItem( _("Min. circularity"), default=0.8, min=0.0, max=1.0, help=_("The minimum circularity of the blobs."), ).set_prop("display", active=_prop_circ) max_circularity = gds.FloatItem( _("Max. circularity"), default=1.0, min=0.0, max=1.0, help=_("The maximum circularity of the blobs."), ).set_prop("display", active=_prop_circ) _prop_iner = gds.ValueProp(False) filter_by_inertia = gds.BoolItem( _("Filter by inertia"), default=False, help=_("If true, the image is filtered by blob inertia."), ).set_prop("display", store=_prop_iner) min_inertia_ratio = gds.FloatItem( _("Min. inertia ratio"), default=0.6, min=0.0, max=1.0, help=_("The minimum inertia ratio of the blobs."), ).set_prop("display", active=_prop_iner) max_inertia_ratio = gds.FloatItem( _("Max. inertia ratio"), default=1.0, min=0.0, max=1.0, help=_("The maximum inertia ratio of the blobs."), ).set_prop("display", active=_prop_iner) _prop_conv = gds.ValueProp(False) filter_by_convexity = gds.BoolItem( _("Filter by convexity"), default=False, help=_("If true, the image is filtered by blob convexity."), ).set_prop("display", store=_prop_conv) min_convexity = gds.FloatItem( _("Min. convexity"), default=0.8, min=0.0, max=1.0, help=_("The minimum convexity of the blobs."), ).set_prop("display", active=_prop_conv) max_convexity = gds.FloatItem( _("Max. convexity"), default=1.0, min=0.0, max=1.0, help=_("The maximum convexity of the blobs."), ).set_prop("display", active=_prop_conv)
[docs] def compute_blob_opencv(image: ImageObj, p: BlobOpenCVParam) -> ResultShape | None: """Compute blobs using OpenCV with :py:func:`cdl.algorithms.image.find_blobs_opencv` Args: imageOutput: input image p: parameters Returns: Blobs coordinates """ return calc_resultshape( "blob_opencv", "circle", image, alg.find_blobs_opencv, p.min_threshold, p.max_threshold, p.min_repeatability, p.min_dist_between_blobs, p.filter_by_color, p.blob_color, p.filter_by_area, p.min_area, p.max_area, p.filter_by_circularity, p.min_circularity, p.max_circularity, p.filter_by_inertia, p.min_inertia_ratio, p.max_inertia_ratio, p.filter_by_convexity, p.min_convexity, p.max_convexity, )