| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- import math
- import numpy as np
- import scipy.ndimage as ndi
- from scipy import spatial
- from .._shared.filters import gaussian
- from .._shared.utils import _supported_float_type, check_nD
- from ..transform import integral_image
- from ..util import img_as_float
- from ._hessian_det_appx import _hessian_matrix_det
- from .peak import peak_local_max
- # This basic blob detection algorithm is based on:
- # http://www.cs.utah.edu/~jfishbau/advimproc/project1/ (04.04.2013)
- # Theory behind: https://en.wikipedia.org/wiki/Blob_detection (04.04.2013)
- def _compute_disk_overlap(d, r1, r2):
- """
- Compute fraction of surface overlap between two disks of radii
- ``r1`` and ``r2``, with centers separated by a distance ``d``.
- Parameters
- ----------
- d : float
- Distance between centers.
- r1 : float
- Radius of the first disk.
- r2 : float
- Radius of the second disk.
- Returns
- -------
- fraction: float
- Fraction of area of the overlap between the two disks.
- """
- ratio1 = (d**2 + r1**2 - r2**2) / (2 * d * r1)
- ratio1 = np.clip(ratio1, -1, 1)
- acos1 = math.acos(ratio1)
- ratio2 = (d**2 + r2**2 - r1**2) / (2 * d * r2)
- ratio2 = np.clip(ratio2, -1, 1)
- acos2 = math.acos(ratio2)
- a = -d + r2 + r1
- b = d - r2 + r1
- c = d + r2 - r1
- d = d + r2 + r1
- area = r1**2 * acos1 + r2**2 * acos2 - 0.5 * math.sqrt(abs(a * b * c * d))
- return area / (math.pi * (min(r1, r2) ** 2))
- def _compute_sphere_overlap(d, r1, r2):
- """
- Compute volume overlap fraction between two spheres of radii
- ``r1`` and ``r2``, with centers separated by a distance ``d``.
- Parameters
- ----------
- d : float
- Distance between centers.
- r1 : float
- Radius of the first sphere.
- r2 : float
- Radius of the second sphere.
- Returns
- -------
- fraction: float
- Fraction of volume of the overlap between the two spheres.
- Notes
- -----
- See for example http://mathworld.wolfram.com/Sphere-SphereIntersection.html
- for more details.
- """
- vol = (
- math.pi
- / (12 * d)
- * (r1 + r2 - d) ** 2
- * (d**2 + 2 * d * (r1 + r2) - 3 * (r1**2 + r2**2) + 6 * r1 * r2)
- )
- return vol / (4.0 / 3 * math.pi * min(r1, r2) ** 3)
- def _blob_overlap(blob1, blob2, *, sigma_dim=1):
- """Finds the overlapping area fraction between two blobs.
- Returns a float representing fraction of overlapped area. Note that 0.0
- is *always* returned for dimension greater than 3.
- Parameters
- ----------
- blob1 : sequence of arrays
- A sequence of ``(row, col, sigma)`` or ``(pln, row, col, sigma)``,
- where ``row, col`` (or ``(pln, row, col)``) are coordinates
- of blob and ``sigma`` is the standard deviation of the Gaussian kernel
- which detected the blob.
- blob2 : sequence of arrays
- A sequence of ``(row, col, sigma)`` or ``(pln, row, col, sigma)``,
- where ``row, col`` (or ``(pln, row, col)``) are coordinates
- of blob and ``sigma`` is the standard deviation of the Gaussian kernel
- which detected the blob.
- sigma_dim : int, optional
- The dimensionality of the sigma value. Can be 1 or the same as the
- dimensionality of the blob space (2 or 3).
- Returns
- -------
- f : float
- Fraction of overlapped area (or volume in 3D).
- """
- ndim = len(blob1) - sigma_dim
- if ndim > 3:
- return 0.0
- root_ndim = math.sqrt(ndim)
- # we divide coordinates by sigma * sqrt(ndim) to rescale space to isotropy,
- # giving spheres of radius = 1 or < 1.
- if blob1[-1] == blob2[-1] == 0:
- return 0.0
- elif blob1[-1] > blob2[-1]:
- max_sigma = blob1[-sigma_dim:]
- r1 = 1
- r2 = blob2[-1] / blob1[-1]
- else:
- max_sigma = blob2[-sigma_dim:]
- r2 = 1
- r1 = blob1[-1] / blob2[-1]
- pos1 = blob1[:ndim] / (max_sigma * root_ndim)
- pos2 = blob2[:ndim] / (max_sigma * root_ndim)
- d = np.sqrt(np.sum((pos2 - pos1) ** 2))
- if d > r1 + r2: # centers farther than sum of radii, so no overlap
- return 0.0
- # one blob is inside the other
- if d <= abs(r1 - r2):
- return 1.0
- if ndim == 2:
- return _compute_disk_overlap(d, r1, r2)
- else: # ndim=3 http://mathworld.wolfram.com/Sphere-SphereIntersection.html
- return _compute_sphere_overlap(d, r1, r2)
- def _prune_blobs(blobs_array, overlap, *, sigma_dim=1):
- """Eliminated blobs with area overlap.
- Parameters
- ----------
- blobs_array : ndarray
- A 2d array with each row representing 3 (or 4) values,
- ``(row, col, sigma)`` or ``(pln, row, col, sigma)`` in 3D,
- where ``(row, col)`` (``(pln, row, col)``) are coordinates of the blob
- and ``sigma`` is the standard deviation of the Gaussian kernel which
- detected the blob.
- This array must not have a dimension of size 0.
- overlap : float
- A value between 0 and 1. If the fraction of area overlapping for 2
- blobs is greater than `overlap` the smaller blob is eliminated.
- sigma_dim : int, optional
- The number of columns in ``blobs_array`` corresponding to sigmas rather
- than positions.
- Returns
- -------
- A : ndarray
- `array` with overlapping blobs removed.
- """
- sigma = blobs_array[:, -sigma_dim:].max()
- distance = 2 * sigma * math.sqrt(blobs_array.shape[1] - sigma_dim)
- tree = spatial.cKDTree(blobs_array[:, :-sigma_dim])
- pairs = np.array(list(tree.query_pairs(distance)))
- if len(pairs) == 0:
- return blobs_array
- else:
- for i, j in pairs:
- blob1, blob2 = blobs_array[i], blobs_array[j]
- if _blob_overlap(blob1, blob2, sigma_dim=sigma_dim) > overlap:
- # note: this test works even in the anisotropic case because
- # all sigmas increase together.
- if blob1[-1] > blob2[-1]:
- blob2[-1] = 0
- else:
- blob1[-1] = 0
- return np.stack([b for b in blobs_array if b[-1] > 0])
- def _format_exclude_border(img_ndim, exclude_border):
- """Format an ``exclude_border`` argument as a tuple of ints for calling
- ``peak_local_max``.
- """
- if isinstance(exclude_border, tuple):
- if len(exclude_border) != img_ndim:
- raise ValueError(
- "`exclude_border` should have the same length as the "
- "dimensionality of the image."
- )
- for exclude in exclude_border:
- if not isinstance(exclude, int):
- raise ValueError(
- "exclude border, when expressed as a tuple, must only "
- "contain ints."
- )
- return exclude_border + (0,)
- elif isinstance(exclude_border, int):
- return (exclude_border,) * img_ndim + (0,)
- elif exclude_border is True:
- raise ValueError("exclude_border cannot be True")
- elif exclude_border is False:
- return (0,) * (img_ndim + 1)
- else:
- raise ValueError(f'Unsupported value ({exclude_border}) for exclude_border')
- def blob_dog(
- image,
- min_sigma=1,
- max_sigma=50,
- sigma_ratio=1.6,
- threshold=0.5,
- overlap=0.5,
- *,
- threshold_rel=None,
- exclude_border=False,
- ):
- r"""Finds blobs in the given grayscale image.
- Blobs are found using the Difference of Gaussian (DoG) method [1]_, [2]_.
- For each blob found, the method returns its coordinates and the standard
- deviation of the Gaussian kernel that detected the blob.
- Parameters
- ----------
- image : ndarray
- Input grayscale image, blobs are assumed to be light on dark
- background (white on black).
- min_sigma : scalar or sequence of scalars, optional
- Minimum standard deviation for Gaussian kernel. Keep this value low to
- detect smaller blobs. The standard deviation of the Gaussian kernel
- is given either as a sequence for each axis, or as a single number, in
- which case it is equal for all axes.
- max_sigma : scalar or sequence of scalars, optional
- The maximum standard deviation for Gaussian kernel. Keep this high to
- detect larger blobs. The standard deviation of the Gaussian kernel
- is given either as a sequence for each axis, or as a single number, in
- which case it is equal for all axes.
- sigma_ratio : float, optional
- The ratio between the standard deviation of Gaussian Kernels used for
- computing the Difference of Gaussians
- threshold : float or None, optional
- The absolute lower bound for scale space maxima. Local maxima smaller
- than `threshold` are ignored. Reduce this to detect blobs with lower
- intensities. If `threshold_rel` is also specified, whichever threshold
- is larger will be used. If None, `threshold_rel` is used instead.
- overlap : float, optional
- A value between 0 and 1. If the area of two blobs overlaps by a
- fraction greater than `threshold`, the smaller blob is eliminated.
- threshold_rel : float or None, optional
- Minimum intensity of peaks, calculated as
- ``max(dog_space) * threshold_rel``, where ``dog_space`` refers to the
- stack of Difference-of-Gaussian (DoG) images computed internally. This
- should have a value between 0 and 1. If None, `threshold` is used
- instead.
- exclude_border : tuple of ints, int, or False, optional
- If tuple of ints, the length of the tuple must match the input array's
- dimensionality. Each element of the tuple will exclude peaks from
- within `exclude_border`-pixels of the border of the image along that
- dimension.
- If nonzero int, `exclude_border` excludes peaks from within
- `exclude_border`-pixels of the border of the image.
- If zero or False, peaks are identified regardless of their
- distance from the border.
- Returns
- -------
- A : (n, image.ndim + sigma) ndarray
- A 2d array with each row representing 2 coordinate values for a 2D
- image, or 3 coordinate values for a 3D image, plus the sigma(s) used.
- When a single sigma is passed, outputs are:
- ``(r, c, sigma)`` or ``(p, r, c, sigma)`` where ``(r, c)`` or
- ``(p, r, c)`` are coordinates of the blob and ``sigma`` is the standard
- deviation of the Gaussian kernel which detected the blob. When an
- anisotropic gaussian is used (sigmas per dimension), the detected sigma
- is returned for each dimension.
- See also
- --------
- skimage.filters.difference_of_gaussians
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Blob_detection#The_difference_of_Gaussians_approach
- .. [2] Lowe, D. G. "Distinctive Image Features from Scale-Invariant
- Keypoints." International Journal of Computer Vision 60, 91–110 (2004).
- https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf
- :DOI:`10.1023/B:VISI.0000029664.99615.94`
- Examples
- --------
- >>> from skimage import data, feature
- >>> coins = data.coins()
- >>> feature.blob_dog(coins, threshold=.05, min_sigma=10, max_sigma=40)
- array([[128., 155., 10.],
- [198., 155., 10.],
- [124., 338., 10.],
- [127., 102., 10.],
- [193., 281., 10.],
- [126., 208., 10.],
- [267., 115., 10.],
- [197., 102., 10.],
- [198., 215., 10.],
- [123., 279., 10.],
- [126., 46., 10.],
- [259., 247., 10.],
- [196., 43., 10.],
- [ 54., 276., 10.],
- [267., 358., 10.],
- [ 58., 100., 10.],
- [259., 305., 10.],
- [185., 347., 16.],
- [261., 174., 16.],
- [ 46., 336., 16.],
- [ 54., 217., 10.],
- [ 55., 157., 10.],
- [ 57., 41., 10.],
- [260., 47., 16.]])
- Notes
- -----
- The radius of each blob is approximately :math:`\sqrt{2}\sigma` for
- a 2-D image and :math:`\sqrt{3}\sigma` for a 3-D image.
- """
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- # if both min and max sigma are scalar, function returns only one sigma
- scalar_sigma = np.isscalar(max_sigma) and np.isscalar(min_sigma)
- # Gaussian filter requires that sequence-type sigmas have same
- # dimensionality as image. This broadcasts scalar kernels
- if np.isscalar(max_sigma):
- max_sigma = np.full(image.ndim, max_sigma, dtype=float_dtype)
- if np.isscalar(min_sigma):
- min_sigma = np.full(image.ndim, min_sigma, dtype=float_dtype)
- # Convert sequence types to array
- min_sigma = np.asarray(min_sigma, dtype=float_dtype)
- max_sigma = np.asarray(max_sigma, dtype=float_dtype)
- if sigma_ratio <= 1.0:
- raise ValueError('sigma_ratio must be > 1.0')
- # k such that min_sigma*(sigma_ratio**k) > max_sigma
- k = int(np.mean(np.log(max_sigma / min_sigma) / np.log(sigma_ratio) + 1))
- # a geometric progression of standard deviations for gaussian kernels
- sigma_list = np.array([min_sigma * (sigma_ratio**i) for i in range(k + 1)])
- # computing difference between two successive Gaussian blurred images
- # to obtain an approximation of the scale invariant Laplacian of the
- # Gaussian operator
- dog_image_cube = np.empty(image.shape + (k,), dtype=float_dtype)
- gaussian_previous = gaussian(image, sigma=sigma_list[0], mode='reflect')
- for i, s in enumerate(sigma_list[1:]):
- gaussian_current = gaussian(image, sigma=s, mode='reflect')
- dog_image_cube[..., i] = gaussian_previous - gaussian_current
- gaussian_previous = gaussian_current
- # normalization factor for consistency in DoG magnitude
- sf = 1 / (sigma_ratio - 1)
- dog_image_cube *= sf
- exclude_border = _format_exclude_border(image.ndim, exclude_border)
- local_maxima = peak_local_max(
- dog_image_cube,
- threshold_abs=threshold,
- threshold_rel=threshold_rel,
- exclude_border=exclude_border,
- footprint=np.ones((3,) * (image.ndim + 1)),
- )
- # Catch no peaks
- if local_maxima.size == 0:
- return np.empty((0, image.ndim + (1 if scalar_sigma else image.ndim)))
- # Convert local_maxima to float64
- lm = local_maxima.astype(float_dtype)
- # translate final column of lm, which contains the index of the
- # sigma that produced the maximum intensity value, into the sigma
- sigmas_of_peaks = sigma_list[local_maxima[:, -1]]
- if scalar_sigma:
- # select one sigma column, keeping dimension
- sigmas_of_peaks = sigmas_of_peaks[:, 0:1]
- # Remove sigma index and replace with sigmas
- lm = np.hstack([lm[:, :-1], sigmas_of_peaks])
- sigma_dim = sigmas_of_peaks.shape[1]
- return _prune_blobs(lm, overlap, sigma_dim=sigma_dim)
- def blob_log(
- image,
- min_sigma=1,
- max_sigma=50,
- num_sigma=10,
- threshold=0.2,
- overlap=0.5,
- log_scale=False,
- *,
- threshold_rel=None,
- exclude_border=False,
- ):
- r"""Finds blobs in the given grayscale image.
- Blobs are found using the Laplacian of Gaussian (LoG) method [1]_.
- For each blob found, the method returns its coordinates and the standard
- deviation of the Gaussian kernel that detected the blob.
- Parameters
- ----------
- image : ndarray
- Input grayscale image, blobs are assumed to be light on dark
- background (white on black).
- min_sigma : scalar or sequence of scalars, optional
- Minimum standard deviation for Gaussian kernel. Keep this value low to
- detect smaller blobs. The standard deviation of the Gaussian kernel
- is given either as a sequence for each axis, or as a single number, in
- which case it is equal for all axes.
- max_sigma : scalar or sequence of scalars, optional
- The maximum standard deviation for Gaussian kernel. Keep this high to
- detect larger blobs. The standard deviation of the Gaussian kernel
- is given either as a sequence for each axis, or as a single number, in
- which case it is equal for all axes.
- num_sigma : int, optional
- The number of evenly spaced values for standard deviation of the
- Gaussian kernel to consider on the closed interval
- ``[min_sigma, max_sigma]``.
- threshold : float or None, optional
- The absolute lower bound for scale space maxima. Local maxima smaller
- than `threshold` are ignored. Reduce this to detect blobs with lower
- intensities. If `threshold_rel` is also specified, whichever threshold
- is larger will be used. If None, `threshold_rel` is used instead.
- overlap : float, optional
- A value between 0 and 1. If the area of two blobs overlaps by a
- fraction greater than `threshold`, the smaller blob is eliminated.
- log_scale : bool, optional
- If set intermediate values of standard deviations are interpolated
- using a logarithmic scale to the base `10`. If not, linear
- interpolation is used.
- threshold_rel : float or None, optional
- Minimum intensity of peaks, calculated as
- ``max(log_space) * threshold_rel``, where ``log_space`` refers to the
- stack of Laplacian-of-Gaussian (LoG) images computed internally. This
- should have a value between 0 and 1. If None, `threshold` is used
- instead.
- exclude_border : tuple of ints, int, or False, optional
- If tuple of ints, the length of the tuple must match the input array's
- dimensionality. Each element of the tuple will exclude peaks from
- within `exclude_border`-pixels of the border of the image along that
- dimension.
- If nonzero int, `exclude_border` excludes peaks from within
- `exclude_border`-pixels of the border of the image.
- If zero or False, peaks are identified regardless of their
- distance from the border.
- Returns
- -------
- A : (n, image.ndim + sigma) ndarray
- A 2d array with each row representing 2 coordinate values for a 2D
- image, or 3 coordinate values for a 3D image, plus the sigma(s) used.
- When a single sigma is passed, outputs are:
- ``(r, c, sigma)`` or ``(p, r, c, sigma)`` where ``(r, c)`` or
- ``(p, r, c)`` are coordinates of the blob and ``sigma`` is the standard
- deviation of the Gaussian kernel which detected the blob. When an
- anisotropic gaussian is used (sigmas per dimension), the detected sigma
- is returned for each dimension.
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Blob_detection#The_Laplacian_of_Gaussian
- Examples
- --------
- >>> from skimage import data, feature, exposure
- >>> img = data.coins()
- >>> img = exposure.equalize_hist(img) # improves detection
- >>> feature.blob_log(img, threshold = .3)
- array([[124. , 336. , 11.88888889],
- [198. , 155. , 11.88888889],
- [194. , 213. , 17.33333333],
- [121. , 272. , 17.33333333],
- [263. , 244. , 17.33333333],
- [194. , 276. , 17.33333333],
- [266. , 115. , 11.88888889],
- [128. , 154. , 11.88888889],
- [260. , 174. , 17.33333333],
- [198. , 103. , 11.88888889],
- [126. , 208. , 11.88888889],
- [127. , 102. , 11.88888889],
- [263. , 302. , 17.33333333],
- [197. , 44. , 11.88888889],
- [185. , 344. , 17.33333333],
- [126. , 46. , 11.88888889],
- [113. , 323. , 1. ]])
- Notes
- -----
- The radius of each blob is approximately :math:`\sqrt{2}\sigma` for
- a 2-D image and :math:`\sqrt{3}\sigma` for a 3-D image.
- """
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- # if both min and max sigma are scalar, function returns only one sigma
- scalar_sigma = True if np.isscalar(max_sigma) and np.isscalar(min_sigma) else False
- # Gaussian filter requires that sequence-type sigmas have same
- # dimensionality as image. This broadcasts scalar kernels
- if np.isscalar(max_sigma):
- max_sigma = np.full(image.ndim, max_sigma, dtype=float_dtype)
- if np.isscalar(min_sigma):
- min_sigma = np.full(image.ndim, min_sigma, dtype=float_dtype)
- # Convert sequence types to array
- min_sigma = np.asarray(min_sigma, dtype=float_dtype)
- max_sigma = np.asarray(max_sigma, dtype=float_dtype)
- if log_scale:
- start = np.log10(min_sigma)
- stop = np.log10(max_sigma)
- sigma_list = np.logspace(start, stop, num_sigma)
- else:
- sigma_list = np.linspace(min_sigma, max_sigma, num_sigma)
- # computing gaussian laplace
- image_cube = np.empty(image.shape + (len(sigma_list),), dtype=float_dtype)
- for i, s in enumerate(sigma_list):
- # average s**2 provides scale invariance
- image_cube[..., i] = -ndi.gaussian_laplace(image, s) * np.mean(s) ** 2
- exclude_border = _format_exclude_border(image.ndim, exclude_border)
- local_maxima = peak_local_max(
- image_cube,
- threshold_abs=threshold,
- threshold_rel=threshold_rel,
- exclude_border=exclude_border,
- footprint=np.ones((3,) * (image.ndim + 1)),
- )
- # Catch no peaks
- if local_maxima.size == 0:
- return np.empty((0, image.ndim + (1 if scalar_sigma else image.ndim)))
- # Convert local_maxima to float64
- lm = local_maxima.astype(float_dtype)
- # translate final column of lm, which contains the index of the
- # sigma that produced the maximum intensity value, into the sigma
- sigmas_of_peaks = sigma_list[local_maxima[:, -1]]
- if scalar_sigma:
- # select one sigma column, keeping dimension
- sigmas_of_peaks = sigmas_of_peaks[:, 0:1]
- # Remove sigma index and replace with sigmas
- lm = np.hstack([lm[:, :-1], sigmas_of_peaks])
- sigma_dim = sigmas_of_peaks.shape[1]
- return _prune_blobs(lm, overlap, sigma_dim=sigma_dim)
- def blob_doh(
- image,
- min_sigma=1,
- max_sigma=30,
- num_sigma=10,
- threshold=0.01,
- overlap=0.5,
- log_scale=False,
- *,
- threshold_rel=None,
- ):
- """Finds blobs in the given grayscale image.
- Blobs are found using the Determinant of Hessian method [1]_. For each blob
- found, the method returns its coordinates and the standard deviation
- of the Gaussian Kernel used for the Hessian matrix whose determinant
- detected the blob. Determinant of Hessians is approximated using [2]_.
- Parameters
- ----------
- image : 2D ndarray
- Input grayscale image. Blobs can either be light on dark or vice versa.
- min_sigma : float, optional
- The minimum standard deviation for Gaussian Kernel used to compute
- Hessian matrix. Keep this value low to detect smaller blobs.
- The standard deviation of the Gaussian kernel is given either as a
- sequence for each axis, or as a single number, in which case it is
- equal for all axes.
- max_sigma : float, optional
- The maximum standard deviation for Gaussian Kernel used to compute
- Hessian matrix. Keep this value high to detect larger blobs.
- The standard deviation of the Gaussian kernel is given either as a
- sequence for each axis, or as a single number, in which case it is
- equal for all axes.
- num_sigma : int, optional
- The number of evenly spaced values for standard deviation of the
- Gaussian kernel to consider on the closed interval
- ``[min_sigma, max_sigma]``.
- threshold : float or None, optional
- The absolute lower bound for scale space maxima. Local maxima smaller
- than `threshold` are ignored. Reduce this to detect blobs with lower
- intensities. If `threshold_rel` is also specified, whichever threshold
- is larger will be used. If None, `threshold_rel` is used instead.
- overlap : float, optional
- A value between 0 and 1. If the area of two blobs overlaps by a
- fraction greater than `threshold`, the smaller blob is eliminated.
- log_scale : bool, optional
- If set intermediate values of standard deviations are interpolated
- using a logarithmic scale to the base `10`. If not, linear
- interpolation is used.
- threshold_rel : float or None, optional
- Minimum intensity of peaks, calculated as
- ``max(doh_space) * threshold_rel``, where ``doh_space`` refers to the
- stack of Determinant-of-Hessian (DoH) images computed internally. This
- should have a value between 0 and 1. If None, `threshold` is used
- instead.
- Returns
- -------
- A : (n, 3) ndarray
- A 2d array with each row representing 3 values, ``(y,x,sigma)``
- where ``(y,x)`` are coordinates of the blob and ``sigma`` is the
- standard deviation of the Gaussian kernel of the Hessian Matrix whose
- determinant detected the blob.
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Blob_detection#The_determinant_of_the_Hessian
- .. [2] Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool,
- "SURF: Speeded Up Robust Features"
- ftp://ftp.vision.ee.ethz.ch/publications/articles/eth_biwi_00517.pdf
- Examples
- --------
- >>> from skimage import data, feature
- >>> img = data.coins()
- >>> feature.blob_doh(img)
- array([[197. , 153. , 20.33333333],
- [124. , 336. , 20.33333333],
- [126. , 153. , 20.33333333],
- [195. , 100. , 23.55555556],
- [192. , 212. , 23.55555556],
- [121. , 271. , 30. ],
- [126. , 101. , 20.33333333],
- [193. , 275. , 23.55555556],
- [123. , 205. , 20.33333333],
- [270. , 363. , 30. ],
- [265. , 113. , 23.55555556],
- [262. , 243. , 23.55555556],
- [185. , 348. , 30. ],
- [156. , 302. , 30. ],
- [123. , 44. , 23.55555556],
- [260. , 173. , 30. ],
- [197. , 44. , 20.33333333]])
- Notes
- -----
- The radius of each blob is approximately `sigma`.
- Computation of Determinant of Hessians is independent of the standard
- deviation. Therefore detecting larger blobs won't take more time. In
- methods line :py:meth:`blob_dog` and :py:meth:`blob_log` the computation
- of Gaussians for larger `sigma` takes more time. The downside is that
- this method can't be used for detecting blobs of radius less than `3px`
- due to the box filters used in the approximation of Hessian Determinant.
- """
- check_nD(image, 2)
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- image = integral_image(image)
- if log_scale:
- start, stop = math.log(min_sigma, 10), math.log(max_sigma, 10)
- sigma_list = np.logspace(start, stop, num_sigma)
- else:
- sigma_list = np.linspace(min_sigma, max_sigma, num_sigma)
- image_cube = np.empty(shape=image.shape + (len(sigma_list),), dtype=float_dtype)
- for j, s in enumerate(sigma_list):
- image_cube[..., j] = _hessian_matrix_det(image, s)
- local_maxima = peak_local_max(
- image_cube,
- threshold_abs=threshold,
- threshold_rel=threshold_rel,
- exclude_border=False,
- footprint=np.ones((3,) * image_cube.ndim),
- )
- # Catch no peaks
- if local_maxima.size == 0:
- return np.empty((0, 3))
- # Convert local_maxima to float64
- lm = local_maxima.astype(np.float64)
- # Convert the last index to its corresponding scale value
- lm[:, -1] = sigma_list[local_maxima[:, -1]]
- return _prune_blobs(lm, overlap)
|