scikit-image
715 строк · 27.3 Кб
1import math
2
3import numpy as np
4import scipy.ndimage as ndi
5from scipy import spatial
6
7from .._shared.filters import gaussian
8from .._shared.utils import _supported_float_type, check_nD
9from ..transform import integral_image
10from ..util import img_as_float
11from ._hessian_det_appx import _hessian_matrix_det
12from .peak import peak_local_max
13
14# This basic blob detection algorithm is based on:
15# http://www.cs.utah.edu/~jfishbau/advimproc/project1/ (04.04.2013)
16# Theory behind: https://en.wikipedia.org/wiki/Blob_detection (04.04.2013)
17
18
19def _compute_disk_overlap(d, r1, r2):
20"""
21Compute fraction of surface overlap between two disks of radii
22``r1`` and ``r2``, with centers separated by a distance ``d``.
23
24Parameters
25----------
26d : float
27Distance between centers.
28r1 : float
29Radius of the first disk.
30r2 : float
31Radius of the second disk.
32
33Returns
34-------
35fraction: float
36Fraction of area of the overlap between the two disks.
37"""
38
39ratio1 = (d**2 + r1**2 - r2**2) / (2 * d * r1)
40ratio1 = np.clip(ratio1, -1, 1)
41acos1 = math.acos(ratio1)
42
43ratio2 = (d**2 + r2**2 - r1**2) / (2 * d * r2)
44ratio2 = np.clip(ratio2, -1, 1)
45acos2 = math.acos(ratio2)
46
47a = -d + r2 + r1
48b = d - r2 + r1
49c = d + r2 - r1
50d = d + r2 + r1
51area = r1**2 * acos1 + r2**2 * acos2 - 0.5 * math.sqrt(abs(a * b * c * d))
52return area / (math.pi * (min(r1, r2) ** 2))
53
54
55def _compute_sphere_overlap(d, r1, r2):
56"""
57Compute volume overlap fraction between two spheres of radii
58``r1`` and ``r2``, with centers separated by a distance ``d``.
59
60Parameters
61----------
62d : float
63Distance between centers.
64r1 : float
65Radius of the first sphere.
66r2 : float
67Radius of the second sphere.
68
69Returns
70-------
71fraction: float
72Fraction of volume of the overlap between the two spheres.
73
74Notes
75-----
76See for example http://mathworld.wolfram.com/Sphere-SphereIntersection.html
77for more details.
78"""
79vol = (
80math.pi
81/ (12 * d)
82* (r1 + r2 - d) ** 2
83* (d**2 + 2 * d * (r1 + r2) - 3 * (r1**2 + r2**2) + 6 * r1 * r2)
84)
85return vol / (4.0 / 3 * math.pi * min(r1, r2) ** 3)
86
87
88def _blob_overlap(blob1, blob2, *, sigma_dim=1):
89"""Finds the overlapping area fraction between two blobs.
90
91Returns a float representing fraction of overlapped area. Note that 0.0
92is *always* returned for dimension greater than 3.
93
94Parameters
95----------
96blob1 : sequence of arrays
97A sequence of ``(row, col, sigma)`` or ``(pln, row, col, sigma)``,
98where ``row, col`` (or ``(pln, row, col)``) are coordinates
99of blob and ``sigma`` is the standard deviation of the Gaussian kernel
100which detected the blob.
101blob2 : sequence of arrays
102A sequence of ``(row, col, sigma)`` or ``(pln, row, col, sigma)``,
103where ``row, col`` (or ``(pln, row, col)``) are coordinates
104of blob and ``sigma`` is the standard deviation of the Gaussian kernel
105which detected the blob.
106sigma_dim : int, optional
107The dimensionality of the sigma value. Can be 1 or the same as the
108dimensionality of the blob space (2 or 3).
109
110Returns
111-------
112f : float
113Fraction of overlapped area (or volume in 3D).
114"""
115ndim = len(blob1) - sigma_dim
116if ndim > 3:
117return 0.0
118root_ndim = math.sqrt(ndim)
119
120# we divide coordinates by sigma * sqrt(ndim) to rescale space to isotropy,
121# giving spheres of radius = 1 or < 1.
122if blob1[-1] == blob2[-1] == 0:
123return 0.0
124elif blob1[-1] > blob2[-1]:
125max_sigma = blob1[-sigma_dim:]
126r1 = 1
127r2 = blob2[-1] / blob1[-1]
128else:
129max_sigma = blob2[-sigma_dim:]
130r2 = 1
131r1 = blob1[-1] / blob2[-1]
132pos1 = blob1[:ndim] / (max_sigma * root_ndim)
133pos2 = blob2[:ndim] / (max_sigma * root_ndim)
134
135d = np.sqrt(np.sum((pos2 - pos1) ** 2))
136if d > r1 + r2: # centers farther than sum of radii, so no overlap
137return 0.0
138
139# one blob is inside the other
140if d <= abs(r1 - r2):
141return 1.0
142
143if ndim == 2:
144return _compute_disk_overlap(d, r1, r2)
145
146else: # ndim=3 http://mathworld.wolfram.com/Sphere-SphereIntersection.html
147return _compute_sphere_overlap(d, r1, r2)
148
149
150def _prune_blobs(blobs_array, overlap, *, sigma_dim=1):
151"""Eliminated blobs with area overlap.
152
153Parameters
154----------
155blobs_array : ndarray
156A 2d array with each row representing 3 (or 4) values,
157``(row, col, sigma)`` or ``(pln, row, col, sigma)`` in 3D,
158where ``(row, col)`` (``(pln, row, col)``) are coordinates of the blob
159and ``sigma`` is the standard deviation of the Gaussian kernel which
160detected the blob.
161This array must not have a dimension of size 0.
162overlap : float
163A value between 0 and 1. If the fraction of area overlapping for 2
164blobs is greater than `overlap` the smaller blob is eliminated.
165sigma_dim : int, optional
166The number of columns in ``blobs_array`` corresponding to sigmas rather
167than positions.
168
169Returns
170-------
171A : ndarray
172`array` with overlapping blobs removed.
173"""
174sigma = blobs_array[:, -sigma_dim:].max()
175distance = 2 * sigma * math.sqrt(blobs_array.shape[1] - sigma_dim)
176tree = spatial.cKDTree(blobs_array[:, :-sigma_dim])
177pairs = np.array(list(tree.query_pairs(distance)))
178if len(pairs) == 0:
179return blobs_array
180else:
181for i, j in pairs:
182blob1, blob2 = blobs_array[i], blobs_array[j]
183if _blob_overlap(blob1, blob2, sigma_dim=sigma_dim) > overlap:
184# note: this test works even in the anisotropic case because
185# all sigmas increase together.
186if blob1[-1] > blob2[-1]:
187blob2[-1] = 0
188else:
189blob1[-1] = 0
190
191return np.stack([b for b in blobs_array if b[-1] > 0])
192
193
194def _format_exclude_border(img_ndim, exclude_border):
195"""Format an ``exclude_border`` argument as a tuple of ints for calling
196``peak_local_max``.
197"""
198if isinstance(exclude_border, tuple):
199if len(exclude_border) != img_ndim:
200raise ValueError(
201"`exclude_border` should have the same length as the "
202"dimensionality of the image."
203)
204for exclude in exclude_border:
205if not isinstance(exclude, int):
206raise ValueError(
207"exclude border, when expressed as a tuple, must only "
208"contain ints."
209)
210return exclude_border + (0,)
211elif isinstance(exclude_border, int):
212return (exclude_border,) * img_ndim + (0,)
213elif exclude_border is True:
214raise ValueError("exclude_border cannot be True")
215elif exclude_border is False:
216return (0,) * (img_ndim + 1)
217else:
218raise ValueError(f'Unsupported value ({exclude_border}) for exclude_border')
219
220
221def blob_dog(
222image,
223min_sigma=1,
224max_sigma=50,
225sigma_ratio=1.6,
226threshold=0.5,
227overlap=0.5,
228*,
229threshold_rel=None,
230exclude_border=False,
231):
232r"""Finds blobs in the given grayscale image.
233
234Blobs are found using the Difference of Gaussian (DoG) method [1]_, [2]_.
235For each blob found, the method returns its coordinates and the standard
236deviation of the Gaussian kernel that detected the blob.
237
238Parameters
239----------
240image : ndarray
241Input grayscale image, blobs are assumed to be light on dark
242background (white on black).
243min_sigma : scalar or sequence of scalars, optional
244The minimum standard deviation for Gaussian kernel. Keep this low to
245detect smaller blobs. The standard deviations of the Gaussian filter
246are given for each axis as a sequence, or as a single number, in
247which case it is equal for all axes.
248max_sigma : scalar or sequence of scalars, optional
249The maximum standard deviation for Gaussian kernel. Keep this high to
250detect larger blobs. The standard deviations of the Gaussian filter
251are given for each axis as a sequence, or as a single number, in
252which case it is equal for all axes.
253sigma_ratio : float, optional
254The ratio between the standard deviation of Gaussian Kernels used for
255computing the Difference of Gaussians
256threshold : float or None, optional
257The absolute lower bound for scale space maxima. Local maxima smaller
258than `threshold` are ignored. Reduce this to detect blobs with lower
259intensities. If `threshold_rel` is also specified, whichever threshold
260is larger will be used. If None, `threshold_rel` is used instead.
261overlap : float, optional
262A value between 0 and 1. If the area of two blobs overlaps by a
263fraction greater than `threshold`, the smaller blob is eliminated.
264threshold_rel : float or None, optional
265Minimum intensity of peaks, calculated as
266``max(dog_space) * threshold_rel``, where ``dog_space`` refers to the
267stack of Difference-of-Gaussian (DoG) images computed internally. This
268should have a value between 0 and 1. If None, `threshold` is used
269instead.
270exclude_border : tuple of ints, int, or False, optional
271If tuple of ints, the length of the tuple must match the input array's
272dimensionality. Each element of the tuple will exclude peaks from
273within `exclude_border`-pixels of the border of the image along that
274dimension.
275If nonzero int, `exclude_border` excludes peaks from within
276`exclude_border`-pixels of the border of the image.
277If zero or False, peaks are identified regardless of their
278distance from the border.
279
280Returns
281-------
282A : (n, image.ndim + sigma) ndarray
283A 2d array with each row representing 2 coordinate values for a 2D
284image, or 3 coordinate values for a 3D image, plus the sigma(s) used.
285When a single sigma is passed, outputs are:
286``(r, c, sigma)`` or ``(p, r, c, sigma)`` where ``(r, c)`` or
287``(p, r, c)`` are coordinates of the blob and ``sigma`` is the standard
288deviation of the Gaussian kernel which detected the blob. When an
289anisotropic gaussian is used (sigmas per dimension), the detected sigma
290is returned for each dimension.
291
292See also
293--------
294skimage.filters.difference_of_gaussians
295
296References
297----------
298.. [1] https://en.wikipedia.org/wiki/Blob_detection#The_difference_of_Gaussians_approach
299.. [2] Lowe, D. G. "Distinctive Image Features from Scale-Invariant
300Keypoints." International Journal of Computer Vision 60, 91–110 (2004).
301https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf
302:DOI:`10.1023/B:VISI.0000029664.99615.94`
303
304Examples
305--------
306>>> from skimage import data, feature
307>>> coins = data.coins()
308>>> feature.blob_dog(coins, threshold=.05, min_sigma=10, max_sigma=40)
309array([[128., 155., 10.],
310[198., 155., 10.],
311[124., 338., 10.],
312[127., 102., 10.],
313[193., 281., 10.],
314[126., 208., 10.],
315[267., 115., 10.],
316[197., 102., 10.],
317[198., 215., 10.],
318[123., 279., 10.],
319[126., 46., 10.],
320[259., 247., 10.],
321[196., 43., 10.],
322[ 54., 276., 10.],
323[267., 358., 10.],
324[ 58., 100., 10.],
325[259., 305., 10.],
326[185., 347., 16.],
327[261., 174., 16.],
328[ 46., 336., 16.],
329[ 54., 217., 10.],
330[ 55., 157., 10.],
331[ 57., 41., 10.],
332[260., 47., 16.]])
333
334Notes
335-----
336The radius of each blob is approximately :math:`\sqrt{2}\sigma` for
337a 2-D image and :math:`\sqrt{3}\sigma` for a 3-D image.
338""" # noqa: E501
339image = img_as_float(image)
340float_dtype = _supported_float_type(image.dtype)
341image = image.astype(float_dtype, copy=False)
342
343# if both min and max sigma are scalar, function returns only one sigma
344scalar_sigma = np.isscalar(max_sigma) and np.isscalar(min_sigma)
345
346# Gaussian filter requires that sequence-type sigmas have same
347# dimensionality as image. This broadcasts scalar kernels
348if np.isscalar(max_sigma):
349max_sigma = np.full(image.ndim, max_sigma, dtype=float_dtype)
350if np.isscalar(min_sigma):
351min_sigma = np.full(image.ndim, min_sigma, dtype=float_dtype)
352
353# Convert sequence types to array
354min_sigma = np.asarray(min_sigma, dtype=float_dtype)
355max_sigma = np.asarray(max_sigma, dtype=float_dtype)
356
357if sigma_ratio <= 1.0:
358raise ValueError('sigma_ratio must be > 1.0')
359
360# k such that min_sigma*(sigma_ratio**k) > max_sigma
361k = int(np.mean(np.log(max_sigma / min_sigma) / np.log(sigma_ratio) + 1))
362
363# a geometric progression of standard deviations for gaussian kernels
364sigma_list = np.array([min_sigma * (sigma_ratio**i) for i in range(k + 1)])
365
366# computing difference between two successive Gaussian blurred images
367# to obtain an approximation of the scale invariant Laplacian of the
368# Gaussian operator
369dog_image_cube = np.empty(image.shape + (k,), dtype=float_dtype)
370gaussian_previous = gaussian(image, sigma=sigma_list[0], mode='reflect')
371for i, s in enumerate(sigma_list[1:]):
372gaussian_current = gaussian(image, sigma=s, mode='reflect')
373dog_image_cube[..., i] = gaussian_previous - gaussian_current
374gaussian_previous = gaussian_current
375
376# normalization factor for consistency in DoG magnitude
377sf = 1 / (sigma_ratio - 1)
378dog_image_cube *= sf
379
380exclude_border = _format_exclude_border(image.ndim, exclude_border)
381local_maxima = peak_local_max(
382dog_image_cube,
383threshold_abs=threshold,
384threshold_rel=threshold_rel,
385exclude_border=exclude_border,
386footprint=np.ones((3,) * (image.ndim + 1)),
387)
388
389# Catch no peaks
390if local_maxima.size == 0:
391return np.empty((0, image.ndim + (1 if scalar_sigma else image.ndim)))
392
393# Convert local_maxima to float64
394lm = local_maxima.astype(float_dtype)
395
396# translate final column of lm, which contains the index of the
397# sigma that produced the maximum intensity value, into the sigma
398sigmas_of_peaks = sigma_list[local_maxima[:, -1]]
399
400if scalar_sigma:
401# select one sigma column, keeping dimension
402sigmas_of_peaks = sigmas_of_peaks[:, 0:1]
403
404# Remove sigma index and replace with sigmas
405lm = np.hstack([lm[:, :-1], sigmas_of_peaks])
406
407sigma_dim = sigmas_of_peaks.shape[1]
408
409return _prune_blobs(lm, overlap, sigma_dim=sigma_dim)
410
411
412def blob_log(
413image,
414min_sigma=1,
415max_sigma=50,
416num_sigma=10,
417threshold=0.2,
418overlap=0.5,
419log_scale=False,
420*,
421threshold_rel=None,
422exclude_border=False,
423):
424r"""Finds blobs in the given grayscale image.
425
426Blobs are found using the Laplacian of Gaussian (LoG) method [1]_.
427For each blob found, the method returns its coordinates and the standard
428deviation of the Gaussian kernel that detected the blob.
429
430Parameters
431----------
432image : ndarray
433Input grayscale image, blobs are assumed to be light on dark
434background (white on black).
435min_sigma : scalar or sequence of scalars, optional
436the minimum standard deviation for Gaussian kernel. Keep this low to
437detect smaller blobs. The standard deviations of the Gaussian filter
438are given for each axis as a sequence, or as a single number, in
439which case it is equal for all axes.
440max_sigma : scalar or sequence of scalars, optional
441The maximum standard deviation for Gaussian kernel. Keep this high to
442detect larger blobs. The standard deviations of the Gaussian filter
443are given for each axis as a sequence, or as a single number, in
444which case it is equal for all axes.
445num_sigma : int, optional
446The number of intermediate values of standard deviations to consider
447between `min_sigma` and `max_sigma`.
448threshold : float or None, optional
449The absolute lower bound for scale space maxima. Local maxima smaller
450than `threshold` are ignored. Reduce this to detect blobs with lower
451intensities. If `threshold_rel` is also specified, whichever threshold
452is larger will be used. If None, `threshold_rel` is used instead.
453overlap : float, optional
454A value between 0 and 1. If the area of two blobs overlaps by a
455fraction greater than `threshold`, the smaller blob is eliminated.
456log_scale : bool, optional
457If set intermediate values of standard deviations are interpolated
458using a logarithmic scale to the base `10`. If not, linear
459interpolation is used.
460threshold_rel : float or None, optional
461Minimum intensity of peaks, calculated as
462``max(log_space) * threshold_rel``, where ``log_space`` refers to the
463stack of Laplacian-of-Gaussian (LoG) images computed internally. This
464should have a value between 0 and 1. If None, `threshold` is used
465instead.
466exclude_border : tuple of ints, int, or False, optional
467If tuple of ints, the length of the tuple must match the input array's
468dimensionality. Each element of the tuple will exclude peaks from
469within `exclude_border`-pixels of the border of the image along that
470dimension.
471If nonzero int, `exclude_border` excludes peaks from within
472`exclude_border`-pixels of the border of the image.
473If zero or False, peaks are identified regardless of their
474distance from the border.
475
476Returns
477-------
478A : (n, image.ndim + sigma) ndarray
479A 2d array with each row representing 2 coordinate values for a 2D
480image, or 3 coordinate values for a 3D image, plus the sigma(s) used.
481When a single sigma is passed, outputs are:
482``(r, c, sigma)`` or ``(p, r, c, sigma)`` where ``(r, c)`` or
483``(p, r, c)`` are coordinates of the blob and ``sigma`` is the standard
484deviation of the Gaussian kernel which detected the blob. When an
485anisotropic gaussian is used (sigmas per dimension), the detected sigma
486is returned for each dimension.
487
488References
489----------
490.. [1] https://en.wikipedia.org/wiki/Blob_detection#The_Laplacian_of_Gaussian
491
492Examples
493--------
494>>> from skimage import data, feature, exposure
495>>> img = data.coins()
496>>> img = exposure.equalize_hist(img) # improves detection
497>>> feature.blob_log(img, threshold = .3)
498array([[124. , 336. , 11.88888889],
499[198. , 155. , 11.88888889],
500[194. , 213. , 17.33333333],
501[121. , 272. , 17.33333333],
502[263. , 244. , 17.33333333],
503[194. , 276. , 17.33333333],
504[266. , 115. , 11.88888889],
505[128. , 154. , 11.88888889],
506[260. , 174. , 17.33333333],
507[198. , 103. , 11.88888889],
508[126. , 208. , 11.88888889],
509[127. , 102. , 11.88888889],
510[263. , 302. , 17.33333333],
511[197. , 44. , 11.88888889],
512[185. , 344. , 17.33333333],
513[126. , 46. , 11.88888889],
514[113. , 323. , 1. ]])
515
516Notes
517-----
518The radius of each blob is approximately :math:`\sqrt{2}\sigma` for
519a 2-D image and :math:`\sqrt{3}\sigma` for a 3-D image.
520""" # noqa: E501
521image = img_as_float(image)
522float_dtype = _supported_float_type(image.dtype)
523image = image.astype(float_dtype, copy=False)
524
525# if both min and max sigma are scalar, function returns only one sigma
526scalar_sigma = True if np.isscalar(max_sigma) and np.isscalar(min_sigma) else False
527
528# Gaussian filter requires that sequence-type sigmas have same
529# dimensionality as image. This broadcasts scalar kernels
530if np.isscalar(max_sigma):
531max_sigma = np.full(image.ndim, max_sigma, dtype=float_dtype)
532if np.isscalar(min_sigma):
533min_sigma = np.full(image.ndim, min_sigma, dtype=float_dtype)
534
535# Convert sequence types to array
536min_sigma = np.asarray(min_sigma, dtype=float_dtype)
537max_sigma = np.asarray(max_sigma, dtype=float_dtype)
538
539if log_scale:
540start = np.log10(min_sigma)
541stop = np.log10(max_sigma)
542sigma_list = np.logspace(start, stop, num_sigma)
543else:
544sigma_list = np.linspace(min_sigma, max_sigma, num_sigma)
545
546# computing gaussian laplace
547image_cube = np.empty(image.shape + (len(sigma_list),), dtype=float_dtype)
548for i, s in enumerate(sigma_list):
549# average s**2 provides scale invariance
550image_cube[..., i] = -ndi.gaussian_laplace(image, s) * np.mean(s) ** 2
551
552exclude_border = _format_exclude_border(image.ndim, exclude_border)
553local_maxima = peak_local_max(
554image_cube,
555threshold_abs=threshold,
556threshold_rel=threshold_rel,
557exclude_border=exclude_border,
558footprint=np.ones((3,) * (image.ndim + 1)),
559)
560
561# Catch no peaks
562if local_maxima.size == 0:
563return np.empty((0, image.ndim + (1 if scalar_sigma else image.ndim)))
564
565# Convert local_maxima to float64
566lm = local_maxima.astype(float_dtype)
567
568# translate final column of lm, which contains the index of the
569# sigma that produced the maximum intensity value, into the sigma
570sigmas_of_peaks = sigma_list[local_maxima[:, -1]]
571
572if scalar_sigma:
573# select one sigma column, keeping dimension
574sigmas_of_peaks = sigmas_of_peaks[:, 0:1]
575
576# Remove sigma index and replace with sigmas
577lm = np.hstack([lm[:, :-1], sigmas_of_peaks])
578
579sigma_dim = sigmas_of_peaks.shape[1]
580
581return _prune_blobs(lm, overlap, sigma_dim=sigma_dim)
582
583
584def blob_doh(
585image,
586min_sigma=1,
587max_sigma=30,
588num_sigma=10,
589threshold=0.01,
590overlap=0.5,
591log_scale=False,
592*,
593threshold_rel=None,
594):
595"""Finds blobs in the given grayscale image.
596
597Blobs are found using the Determinant of Hessian method [1]_. For each blob
598found, the method returns its coordinates and the standard deviation
599of the Gaussian Kernel used for the Hessian matrix whose determinant
600detected the blob. Determinant of Hessians is approximated using [2]_.
601
602Parameters
603----------
604image : 2D ndarray
605Input grayscale image.Blobs can either be light on dark or vice versa.
606min_sigma : float, optional
607The minimum standard deviation for Gaussian Kernel used to compute
608Hessian matrix. Keep this low to detect smaller blobs.
609max_sigma : float, optional
610The maximum standard deviation for Gaussian Kernel used to compute
611Hessian matrix. Keep this high to detect larger blobs.
612num_sigma : int, optional
613The number of intermediate values of standard deviations to consider
614between `min_sigma` and `max_sigma`.
615threshold : float or None, optional
616The absolute lower bound for scale space maxima. Local maxima smaller
617than `threshold` are ignored. Reduce this to detect blobs with lower
618intensities. If `threshold_rel` is also specified, whichever threshold
619is larger will be used. If None, `threshold_rel` is used instead.
620overlap : float, optional
621A value between 0 and 1. If the area of two blobs overlaps by a
622fraction greater than `threshold`, the smaller blob is eliminated.
623log_scale : bool, optional
624If set intermediate values of standard deviations are interpolated
625using a logarithmic scale to the base `10`. If not, linear
626interpolation is used.
627threshold_rel : float or None, optional
628Minimum intensity of peaks, calculated as
629``max(doh_space) * threshold_rel``, where ``doh_space`` refers to the
630stack of Determinant-of-Hessian (DoH) images computed internally. This
631should have a value between 0 and 1. If None, `threshold` is used
632instead.
633
634Returns
635-------
636A : (n, 3) ndarray
637A 2d array with each row representing 3 values, ``(y,x,sigma)``
638where ``(y,x)`` are coordinates of the blob and ``sigma`` is the
639standard deviation of the Gaussian kernel of the Hessian Matrix whose
640determinant detected the blob.
641
642References
643----------
644.. [1] https://en.wikipedia.org/wiki/Blob_detection#The_determinant_of_the_Hessian
645.. [2] Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool,
646"SURF: Speeded Up Robust Features"
647ftp://ftp.vision.ee.ethz.ch/publications/articles/eth_biwi_00517.pdf
648
649Examples
650--------
651>>> from skimage import data, feature
652>>> img = data.coins()
653>>> feature.blob_doh(img)
654array([[197. , 153. , 20.33333333],
655[124. , 336. , 20.33333333],
656[126. , 153. , 20.33333333],
657[195. , 100. , 23.55555556],
658[192. , 212. , 23.55555556],
659[121. , 271. , 30. ],
660[126. , 101. , 20.33333333],
661[193. , 275. , 23.55555556],
662[123. , 205. , 20.33333333],
663[270. , 363. , 30. ],
664[265. , 113. , 23.55555556],
665[262. , 243. , 23.55555556],
666[185. , 348. , 30. ],
667[156. , 302. , 30. ],
668[123. , 44. , 23.55555556],
669[260. , 173. , 30. ],
670[197. , 44. , 20.33333333]])
671
672Notes
673-----
674The radius of each blob is approximately `sigma`.
675Computation of Determinant of Hessians is independent of the standard
676deviation. Therefore detecting larger blobs won't take more time. In
677methods line :py:meth:`blob_dog` and :py:meth:`blob_log` the computation
678of Gaussians for larger `sigma` takes more time. The downside is that
679this method can't be used for detecting blobs of radius less than `3px`
680due to the box filters used in the approximation of Hessian Determinant.
681""" # noqa: E501
682check_nD(image, 2)
683
684image = img_as_float(image)
685float_dtype = _supported_float_type(image.dtype)
686image = image.astype(float_dtype, copy=False)
687
688image = integral_image(image)
689
690if log_scale:
691start, stop = math.log(min_sigma, 10), math.log(max_sigma, 10)
692sigma_list = np.logspace(start, stop, num_sigma)
693else:
694sigma_list = np.linspace(min_sigma, max_sigma, num_sigma)
695
696image_cube = np.empty(shape=image.shape + (len(sigma_list),), dtype=float_dtype)
697for j, s in enumerate(sigma_list):
698image_cube[..., j] = _hessian_matrix_det(image, s)
699
700local_maxima = peak_local_max(
701image_cube,
702threshold_abs=threshold,
703threshold_rel=threshold_rel,
704exclude_border=False,
705footprint=np.ones((3,) * image_cube.ndim),
706)
707
708# Catch no peaks
709if local_maxima.size == 0:
710return np.empty((0, 3))
711# Convert local_maxima to float64
712lm = local_maxima.astype(np.float64)
713# Convert the last index to its corresponding scale value
714lm[:, -1] = sigma_list[local_maxima[:, -1]]
715return _prune_blobs(lm, overlap)
716