update
This commit is contained in:
630
.CondaPkg/env/Lib/site-packages/skimage/measure/_regionprops_utils.py
vendored
Normal file
630
.CondaPkg/env/Lib/site-packages/skimage/measure/_regionprops_utils.py
vendored
Normal file
@@ -0,0 +1,630 @@
|
||||
from math import sqrt
|
||||
from numbers import Real
|
||||
import numpy as np
|
||||
from scipy import ndimage as ndi
|
||||
|
||||
|
||||
STREL_4 = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.uint8)
|
||||
STREL_8 = np.ones((3, 3), dtype=np.uint8)
|
||||
|
||||
|
||||
# Coefficients from
|
||||
# Ohser J., Nagel W., Schladitz K. (2002) The Euler Number of Discretized Sets
|
||||
# - On the Choice of Adjacency in Homogeneous Lattices.
|
||||
# In: Mecke K., Stoyan D. (eds) Morphology of Condensed Matter. Lecture Notes
|
||||
# in Physics, vol 600. Springer, Berlin, Heidelberg.
|
||||
# The value of coefficients correspond to the contributions to the Euler number
|
||||
# of specific voxel configurations, which are themselves encoded thanks to a
|
||||
# LUT. Computing the Euler number from the addition of the contributions of
|
||||
# local configurations is possible thanks to an integral geometry formula
|
||||
# (see the paper by Ohser et al. for more details).
|
||||
EULER_COEFS2D_4 = [0, 1, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0]
|
||||
EULER_COEFS2D_8 = [0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, -1, 0]
|
||||
EULER_COEFS3D_26 = np.array(
|
||||
[
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
-2,
|
||||
-1,
|
||||
1,
|
||||
-2,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
-2,
|
||||
-1,
|
||||
-2,
|
||||
-1,
|
||||
-1,
|
||||
-2,
|
||||
-6,
|
||||
-3,
|
||||
-3,
|
||||
-2,
|
||||
-3,
|
||||
-2,
|
||||
0,
|
||||
-1,
|
||||
1,
|
||||
-2,
|
||||
0,
|
||||
-1,
|
||||
-6,
|
||||
-3,
|
||||
-3,
|
||||
-2,
|
||||
-2,
|
||||
-1,
|
||||
-1,
|
||||
-2,
|
||||
-3,
|
||||
0,
|
||||
-2,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
-3,
|
||||
-2,
|
||||
0,
|
||||
-1,
|
||||
-3,
|
||||
0,
|
||||
-2,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
-2,
|
||||
-6,
|
||||
-3,
|
||||
0,
|
||||
-1,
|
||||
-3,
|
||||
-2,
|
||||
-2,
|
||||
-1,
|
||||
-3,
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
-2,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-3,
|
||||
-2,
|
||||
-1,
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
-3,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
-2,
|
||||
-1,
|
||||
1,
|
||||
0,
|
||||
-2,
|
||||
-1,
|
||||
-3,
|
||||
0,
|
||||
-3,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
4,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
2,
|
||||
-1,
|
||||
-2,
|
||||
-2,
|
||||
-1,
|
||||
-2,
|
||||
-1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
-6,
|
||||
-2,
|
||||
-3,
|
||||
-2,
|
||||
-3,
|
||||
-1,
|
||||
0,
|
||||
0,
|
||||
-3,
|
||||
-1,
|
||||
-2,
|
||||
-1,
|
||||
-2,
|
||||
-2,
|
||||
-1,
|
||||
-2,
|
||||
-3,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
4,
|
||||
3,
|
||||
-3,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
-3,
|
||||
-1,
|
||||
-2,
|
||||
-3,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
-2,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
2,
|
||||
-2,
|
||||
1,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
-3,
|
||||
-3,
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
0,
|
||||
-2,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
0,
|
||||
1,
|
||||
-2,
|
||||
-1,
|
||||
3,
|
||||
2,
|
||||
-2,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
-1,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
-1,
|
||||
0,
|
||||
-2,
|
||||
1,
|
||||
-2,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
-2,
|
||||
3,
|
||||
-1,
|
||||
2,
|
||||
-1,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
-1,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def euler_number(image, connectivity=None):
|
||||
"""Calculate the Euler characteristic in binary image.
|
||||
|
||||
For 2D objects, the Euler number is the number of objects minus the number
|
||||
of holes. For 3D objects, the Euler number is obtained as the number of
|
||||
objects plus the number of holes, minus the number of tunnels, or loops.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image: (M, N[, P]) ndarray
|
||||
Input image. If image is not binary, all values greater than zero
|
||||
are considered as the object.
|
||||
connectivity : int, optional
|
||||
Maximum number of orthogonal hops to consider a pixel/voxel
|
||||
as a neighbor.
|
||||
Accepted values are ranging from 1 to input.ndim. If ``None``, a full
|
||||
connectivity of ``input.ndim`` is used.
|
||||
4 or 8 neighborhoods are defined for 2D images (connectivity 1 and 2,
|
||||
respectively).
|
||||
6 or 26 neighborhoods are defined for 3D images, (connectivity 1 and 3,
|
||||
respectively). Connectivity 2 is not defined.
|
||||
|
||||
Returns
|
||||
-------
|
||||
euler_number : int
|
||||
Euler characteristic of the set of all objects in the image.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The Euler characteristic is an integer number that describes the
|
||||
topology of the set of all objects in the input image. If object is
|
||||
4-connected, then background is 8-connected, and conversely.
|
||||
|
||||
The computation of the Euler characteristic is based on an integral
|
||||
geometry formula in discretized space. In practice, a neighborhood
|
||||
configuration is constructed, and a LUT is applied for each
|
||||
configuration. The coefficients used are the ones of Ohser et al.
|
||||
|
||||
It can be useful to compute the Euler characteristic for several
|
||||
connectivities. A large relative difference between results
|
||||
for different connectivities suggests that the image resolution
|
||||
(with respect to the size of objects and holes) is too low.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] S. Rivollier. Analyse d’image geometrique et morphometrique par
|
||||
diagrammes de forme et voisinages adaptatifs generaux. PhD thesis,
|
||||
2010. Ecole Nationale Superieure des Mines de Saint-Etienne.
|
||||
https://tel.archives-ouvertes.fr/tel-00560838
|
||||
.. [2] Ohser J., Nagel W., Schladitz K. (2002) The Euler Number of
|
||||
Discretized Sets - On the Choice of Adjacency in Homogeneous
|
||||
Lattices. In: Mecke K., Stoyan D. (eds) Morphology of Condensed
|
||||
Matter. Lecture Notes in Physics, vol 600. Springer, Berlin,
|
||||
Heidelberg.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> SAMPLE = np.zeros((100,100,100));
|
||||
>>> SAMPLE[40:60, 40:60, 40:60]=1
|
||||
>>> euler_number(SAMPLE) # doctest: +ELLIPSIS
|
||||
1...
|
||||
>>> SAMPLE[45:55,45:55,45:55] = 0;
|
||||
>>> euler_number(SAMPLE) # doctest: +ELLIPSIS
|
||||
2...
|
||||
>>> SAMPLE = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0],
|
||||
... [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
||||
... [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
... [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
... [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
||||
... [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
... [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
... [1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0],
|
||||
... [0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1],
|
||||
... [0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])
|
||||
>>> euler_number(SAMPLE) # doctest:
|
||||
0
|
||||
>>> euler_number(SAMPLE, connectivity=1) # doctest:
|
||||
2
|
||||
"""
|
||||
|
||||
# as image can be a label image, transform it to binary
|
||||
image = (image > 0).astype(int)
|
||||
image = np.pad(image, pad_width=1, mode='constant')
|
||||
|
||||
# check connectivity
|
||||
if connectivity is None:
|
||||
connectivity = image.ndim
|
||||
|
||||
# config variable is an adjacency configuration. A coefficient given by
|
||||
# variable coefs is attributed to each configuration in order to get
|
||||
# the Euler characteristic.
|
||||
if image.ndim == 2:
|
||||
config = np.array([[0, 0, 0], [0, 1, 4], [0, 2, 8]])
|
||||
if connectivity == 1:
|
||||
coefs = EULER_COEFS2D_4
|
||||
else:
|
||||
coefs = EULER_COEFS2D_8
|
||||
bins = 16
|
||||
else: # 3D images
|
||||
if connectivity == 2:
|
||||
raise NotImplementedError(
|
||||
'For 3D images, Euler number is implemented '
|
||||
'for connectivities 1 and 3 only'
|
||||
)
|
||||
|
||||
config = np.array(
|
||||
[
|
||||
[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
|
||||
[[0, 0, 0], [0, 1, 4], [0, 2, 8]],
|
||||
[[0, 0, 0], [0, 16, 64], [0, 32, 128]],
|
||||
]
|
||||
)
|
||||
if connectivity == 1:
|
||||
coefs = EULER_COEFS3D_26[::-1]
|
||||
else:
|
||||
coefs = EULER_COEFS3D_26
|
||||
bins = 256
|
||||
|
||||
# XF has values in the 0-255 range in 3D, and in the 0-15 range in 2D,
|
||||
# with one unique value for each binary configuration of the
|
||||
# 27-voxel cube in 3D / 8-pixel square in 2D, up to symmetries
|
||||
XF = ndi.convolve(image, config, mode='constant', cval=0)
|
||||
h = np.bincount(XF.ravel(), minlength=bins)
|
||||
|
||||
if image.ndim == 2:
|
||||
return coefs @ h
|
||||
else:
|
||||
return int(0.125 * coefs @ h)
|
||||
|
||||
|
||||
def perimeter(image, neighborhood=4):
|
||||
"""Calculate total perimeter of all objects in binary image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : (M, N) ndarray
|
||||
Binary input image.
|
||||
neighborhood : 4 or 8, optional
|
||||
Neighborhood connectivity for border pixel determination. It is used to
|
||||
compute the contour. A higher neighborhood widens the border on which
|
||||
the perimeter is computed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
perimeter : float
|
||||
Total perimeter of all objects in binary image.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] K. Benkrid, D. Crookes. Design and FPGA Implementation of
|
||||
a Perimeter Estimator. The Queen's University of Belfast.
|
||||
http://www.cs.qub.ac.uk/~d.crookes/webpubs/papers/perimeter.doc
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, util
|
||||
>>> from skimage.measure import label
|
||||
>>> # coins image (binary)
|
||||
>>> img_coins = data.coins() > 110
|
||||
>>> # total perimeter of all objects in the image
|
||||
>>> perimeter(img_coins, neighborhood=4) # doctest: +ELLIPSIS
|
||||
7796.867...
|
||||
>>> perimeter(img_coins, neighborhood=8) # doctest: +ELLIPSIS
|
||||
8806.268...
|
||||
|
||||
"""
|
||||
if image.ndim != 2:
|
||||
raise NotImplementedError('`perimeter` supports 2D images only')
|
||||
|
||||
if neighborhood == 4:
|
||||
strel = STREL_4
|
||||
else:
|
||||
strel = STREL_8
|
||||
image = image.astype(np.uint8)
|
||||
eroded_image = ndi.binary_erosion(image, strel, border_value=0)
|
||||
border_image = image - eroded_image
|
||||
|
||||
perimeter_weights = np.zeros(50, dtype=np.float64)
|
||||
perimeter_weights[[5, 7, 15, 17, 25, 27]] = 1
|
||||
perimeter_weights[[21, 33]] = sqrt(2)
|
||||
perimeter_weights[[13, 23]] = (1 + sqrt(2)) / 2
|
||||
|
||||
perimeter_image = ndi.convolve(
|
||||
border_image,
|
||||
np.array([[10, 2, 10], [2, 1, 2], [10, 2, 10]]),
|
||||
mode='constant',
|
||||
cval=0,
|
||||
)
|
||||
|
||||
# You can also write
|
||||
# return perimeter_weights[perimeter_image].sum()
|
||||
# but that was measured as taking much longer than bincount + np.dot (5x
|
||||
# as much time)
|
||||
perimeter_histogram = np.bincount(perimeter_image.ravel(), minlength=50)
|
||||
total_perimeter = perimeter_histogram @ perimeter_weights
|
||||
return total_perimeter
|
||||
|
||||
|
||||
def perimeter_crofton(image, directions=4):
|
||||
"""Calculate total Crofton perimeter of all objects in binary image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : (M, N) ndarray
|
||||
Input image. If image is not binary, all values greater than zero
|
||||
are considered as the object.
|
||||
directions : 2 or 4, optional
|
||||
Number of directions used to approximate the Crofton perimeter. By
|
||||
default, 4 is used: it should be more accurate than 2.
|
||||
Computation time is the same in both cases.
|
||||
|
||||
Returns
|
||||
-------
|
||||
perimeter : float
|
||||
Total perimeter of all objects in binary image.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This measure is based on Crofton formula [1], which is a measure from
|
||||
integral geometry. It is defined for general curve length evaluation via
|
||||
a double integral along all directions. In a discrete
|
||||
space, 2 or 4 directions give a quite good approximation, 4 being more
|
||||
accurate than 2 for more complex shapes.
|
||||
|
||||
Similar to :func:`~.measure.perimeter`, this function returns an
|
||||
approximation of the perimeter in continuous space.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Crofton_formula
|
||||
.. [2] S. Rivollier. Analyse d’image geometrique et morphometrique par
|
||||
diagrammes de forme et voisinages adaptatifs generaux. PhD thesis,
|
||||
2010.
|
||||
Ecole Nationale Superieure des Mines de Saint-Etienne.
|
||||
https://tel.archives-ouvertes.fr/tel-00560838
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, util
|
||||
>>> from skimage.measure import label
|
||||
>>> # coins image (binary)
|
||||
>>> img_coins = data.coins() > 110
|
||||
>>> # total perimeter of all objects in the image
|
||||
>>> perimeter_crofton(img_coins, directions=2) # doctest: +ELLIPSIS
|
||||
8144.578...
|
||||
>>> perimeter_crofton(img_coins, directions=4) # doctest: +ELLIPSIS
|
||||
7837.077...
|
||||
"""
|
||||
if image.ndim != 2:
|
||||
raise NotImplementedError('`perimeter_crofton` supports 2D images only')
|
||||
|
||||
# as image could be a label image, transform it to binary image
|
||||
image = (image > 0).astype(np.uint8)
|
||||
image = np.pad(image, pad_width=1, mode='constant')
|
||||
XF = ndi.convolve(
|
||||
image, np.array([[0, 0, 0], [0, 1, 4], [0, 2, 8]]), mode='constant', cval=0
|
||||
)
|
||||
|
||||
h = np.bincount(XF.ravel(), minlength=16)
|
||||
|
||||
# definition of the LUT
|
||||
if directions == 2:
|
||||
coefs = [
|
||||
0,
|
||||
np.pi / 2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
np.pi / 2,
|
||||
0,
|
||||
0,
|
||||
np.pi / 2,
|
||||
np.pi,
|
||||
0,
|
||||
0,
|
||||
np.pi / 2,
|
||||
np.pi,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
else:
|
||||
coefs = [
|
||||
0,
|
||||
np.pi / 4 * (1 + 1 / (np.sqrt(2))),
|
||||
np.pi / (4 * np.sqrt(2)),
|
||||
np.pi / (2 * np.sqrt(2)),
|
||||
0,
|
||||
np.pi / 4 * (1 + 1 / (np.sqrt(2))),
|
||||
0,
|
||||
np.pi / (4 * np.sqrt(2)),
|
||||
np.pi / 4,
|
||||
np.pi / 2,
|
||||
np.pi / (4 * np.sqrt(2)),
|
||||
np.pi / (4 * np.sqrt(2)),
|
||||
np.pi / 4,
|
||||
np.pi / 2,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
|
||||
total_perimeter = coefs @ h
|
||||
return total_perimeter
|
||||
|
||||
|
||||
def _normalize_spacing(spacing, ndims):
|
||||
"""Normalize spacing parameter.
|
||||
|
||||
The `spacing` parameter should be a sequence of numbers matching
|
||||
the image dimensions. If `spacing` is a scalar, assume equal
|
||||
spacing along all dimensions.
|
||||
|
||||
Parameters
|
||||
---------
|
||||
spacing : Any
|
||||
User-provided `spacing` keyword.
|
||||
ndims : int
|
||||
Number of image dimensions.
|
||||
|
||||
Returns
|
||||
-------
|
||||
spacing : array
|
||||
Corrected spacing.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `spacing` is invalid.
|
||||
|
||||
"""
|
||||
spacing = np.array(spacing)
|
||||
if spacing.shape == ():
|
||||
spacing = np.broadcast_to(spacing, shape=(ndims,))
|
||||
elif spacing.shape != (ndims,):
|
||||
raise ValueError(
|
||||
f"spacing isn't a scalar nor a sequence of shape {(ndims,)}, got {spacing}."
|
||||
)
|
||||
if not all(isinstance(s, Real) for s in spacing):
|
||||
raise TypeError(
|
||||
f"Element of spacing isn't float or integer type, got {spacing}."
|
||||
)
|
||||
if not all(np.isfinite(spacing)):
|
||||
raise ValueError(
|
||||
f"Invalid spacing parameter. All elements must be finite, got {spacing}."
|
||||
)
|
||||
return spacing
|
||||
Reference in New Issue
Block a user