|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import numpy as np |
|
from . import _ni_support |
|
from . import _ni_label |
|
from . import _nd_image |
|
from . import _morphology |
|
|
|
__all__ = ['label', 'find_objects', 'labeled_comprehension', 'sum', 'mean', |
|
'variance', 'standard_deviation', 'minimum', 'maximum', 'median', |
|
'minimum_position', 'maximum_position', 'extrema', 'center_of_mass', |
|
'histogram', 'watershed_ift', 'sum_labels', 'value_indices'] |
|
|
|
|
|
def label(input, structure=None, output=None): |
|
""" |
|
Label features in an array. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
An array-like object to be labeled. Any non-zero values in `input` are |
|
counted as features and zero values are considered the background. |
|
structure : array_like, optional |
|
A structuring element that defines feature connections. |
|
`structure` must be centrosymmetric |
|
(see Notes). |
|
If no structuring element is provided, |
|
one is automatically generated with a squared connectivity equal to |
|
one. That is, for a 2-D `input` array, the default structuring element |
|
is:: |
|
|
|
[[0,1,0], |
|
[1,1,1], |
|
[0,1,0]] |
|
|
|
output : (None, data-type, array_like), optional |
|
If `output` is a data type, it specifies the type of the resulting |
|
labeled feature array. |
|
If `output` is an array-like object, then `output` will be updated |
|
with the labeled features from this function. This function can |
|
operate in-place, by passing output=input. |
|
Note that the output must be able to store the largest label, or this |
|
function will raise an Exception. |
|
|
|
Returns |
|
------- |
|
label : ndarray or int |
|
An integer ndarray where each unique feature in `input` has a unique |
|
label in the returned array. |
|
num_features : int |
|
How many objects were found. |
|
|
|
If `output` is None, this function returns a tuple of |
|
(`labeled_array`, `num_features`). |
|
|
|
If `output` is a ndarray, then it will be updated with values in |
|
`labeled_array` and only `num_features` will be returned by this |
|
function. |
|
|
|
See Also |
|
-------- |
|
find_objects : generate a list of slices for the labeled features (or |
|
objects); useful for finding features' position or |
|
dimensions |
|
|
|
Notes |
|
----- |
|
A centrosymmetric matrix is a matrix that is symmetric about the center. |
|
See [1]_ for more information. |
|
|
|
The `structure` matrix must be centrosymmetric to ensure |
|
two-way connections. |
|
For instance, if the `structure` matrix is not centrosymmetric |
|
and is defined as:: |
|
|
|
[[0,1,0], |
|
[1,1,0], |
|
[0,0,0]] |
|
|
|
and the `input` is:: |
|
|
|
[[1,2], |
|
[0,3]] |
|
|
|
then the structure matrix would indicate the |
|
entry 2 in the input is connected to 1, |
|
but 1 is not connected to 2. |
|
|
|
References |
|
---------- |
|
.. [1] James R. Weaver, "Centrosymmetric (cross-symmetric) |
|
matrices, their basic properties, eigenvalues, and |
|
eigenvectors." The American Mathematical Monthly 92.10 |
|
(1985): 711-717. |
|
|
|
Examples |
|
-------- |
|
Create an image with some features, then label it using the default |
|
(cross-shaped) structuring element: |
|
|
|
>>> from scipy.ndimage import label, generate_binary_structure |
|
>>> import numpy as np |
|
>>> a = np.array([[0,0,1,1,0,0], |
|
... [0,0,0,1,0,0], |
|
... [1,1,0,0,1,0], |
|
... [0,0,0,1,0,0]]) |
|
>>> labeled_array, num_features = label(a) |
|
|
|
Each of the 4 features are labeled with a different integer: |
|
|
|
>>> num_features |
|
4 |
|
>>> labeled_array |
|
array([[0, 0, 1, 1, 0, 0], |
|
[0, 0, 0, 1, 0, 0], |
|
[2, 2, 0, 0, 3, 0], |
|
[0, 0, 0, 4, 0, 0]], dtype=int32) |
|
|
|
Generate a structuring element that will consider features connected even |
|
if they touch diagonally: |
|
|
|
>>> s = generate_binary_structure(2,2) |
|
|
|
or, |
|
|
|
>>> s = [[1,1,1], |
|
... [1,1,1], |
|
... [1,1,1]] |
|
|
|
Label the image using the new structuring element: |
|
|
|
>>> labeled_array, num_features = label(a, structure=s) |
|
|
|
Show the 2 labeled features (note that features 1, 3, and 4 from above are |
|
now considered a single feature): |
|
|
|
>>> num_features |
|
2 |
|
>>> labeled_array |
|
array([[0, 0, 1, 1, 0, 0], |
|
[0, 0, 0, 1, 0, 0], |
|
[2, 2, 0, 0, 1, 0], |
|
[0, 0, 0, 1, 0, 0]], dtype=int32) |
|
|
|
""" |
|
input = np.asarray(input) |
|
if np.iscomplexobj(input): |
|
raise TypeError('Complex type not supported') |
|
if structure is None: |
|
structure = _morphology.generate_binary_structure(input.ndim, 1) |
|
structure = np.asarray(structure, dtype=bool) |
|
if structure.ndim != input.ndim: |
|
raise RuntimeError('structure and input must have equal rank') |
|
for ii in structure.shape: |
|
if ii != 3: |
|
raise ValueError('structure dimensions must be equal to 3') |
|
|
|
|
|
|
|
|
|
need_64bits = input.size >= (2**31 - 2) |
|
|
|
if isinstance(output, np.ndarray): |
|
if output.shape != input.shape: |
|
raise ValueError("output shape not correct") |
|
caller_provided_output = True |
|
else: |
|
caller_provided_output = False |
|
if output is None: |
|
output = np.empty(input.shape, np.intp if need_64bits else np.int32) |
|
else: |
|
output = np.empty(input.shape, output) |
|
|
|
|
|
if input.ndim == 0 or input.size == 0: |
|
if input.ndim == 0: |
|
|
|
maxlabel = 1 if (input != 0) else 0 |
|
output[...] = maxlabel |
|
else: |
|
|
|
maxlabel = 0 |
|
if caller_provided_output: |
|
return maxlabel |
|
else: |
|
return output, maxlabel |
|
|
|
try: |
|
max_label = _ni_label._label(input, structure, output) |
|
except _ni_label.NeedMoreBits as e: |
|
|
|
|
|
tmp_output = np.empty(input.shape, np.intp if need_64bits else np.int32) |
|
max_label = _ni_label._label(input, structure, tmp_output) |
|
output[...] = tmp_output[...] |
|
if not np.all(output == tmp_output): |
|
|
|
raise RuntimeError( |
|
"insufficient bit-depth in requested output type" |
|
) from e |
|
|
|
if caller_provided_output: |
|
|
|
return max_label |
|
else: |
|
return output, max_label |
|
|
|
|
|
def find_objects(input, max_label=0): |
|
""" |
|
Find objects in a labeled array. |
|
|
|
Parameters |
|
---------- |
|
input : ndarray of ints |
|
Array containing objects defined by different labels. Labels with |
|
value 0 are ignored. |
|
max_label : int, optional |
|
Maximum label to be searched for in `input`. If max_label is not |
|
given, the positions of all objects are returned. |
|
|
|
Returns |
|
------- |
|
object_slices : list of tuples |
|
A list of tuples, with each tuple containing N slices (with N the |
|
dimension of the input array). Slices correspond to the minimal |
|
parallelepiped that contains the object. If a number is missing, |
|
None is returned instead of a slice. The label ``l`` corresponds to |
|
the index ``l-1`` in the returned list. |
|
|
|
See Also |
|
-------- |
|
label, center_of_mass |
|
|
|
Notes |
|
----- |
|
This function is very useful for isolating a volume of interest inside |
|
a 3-D array, that cannot be "seen through". |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> import numpy as np |
|
>>> a = np.zeros((6,6), dtype=int) |
|
>>> a[2:4, 2:4] = 1 |
|
>>> a[4, 4] = 1 |
|
>>> a[:2, :3] = 2 |
|
>>> a[0, 5] = 3 |
|
>>> a |
|
array([[2, 2, 2, 0, 0, 3], |
|
[2, 2, 2, 0, 0, 0], |
|
[0, 0, 1, 1, 0, 0], |
|
[0, 0, 1, 1, 0, 0], |
|
[0, 0, 0, 0, 1, 0], |
|
[0, 0, 0, 0, 0, 0]]) |
|
>>> ndimage.find_objects(a) |
|
[(slice(2, 5, None), slice(2, 5, None)), |
|
(slice(0, 2, None), slice(0, 3, None)), |
|
(slice(0, 1, None), slice(5, 6, None))] |
|
>>> ndimage.find_objects(a, max_label=2) |
|
[(slice(2, 5, None), slice(2, 5, None)), (slice(0, 2, None), slice(0, 3, None))] |
|
>>> ndimage.find_objects(a == 1, max_label=2) |
|
[(slice(2, 5, None), slice(2, 5, None)), None] |
|
|
|
>>> loc = ndimage.find_objects(a)[0] |
|
>>> a[loc] |
|
array([[1, 1, 0], |
|
[1, 1, 0], |
|
[0, 0, 1]]) |
|
|
|
""" |
|
input = np.asarray(input) |
|
if np.iscomplexobj(input): |
|
raise TypeError('Complex type not supported') |
|
|
|
if max_label < 1: |
|
max_label = input.max() |
|
|
|
return _nd_image.find_objects(input, max_label) |
|
|
|
|
|
def value_indices(arr, *, ignore_value=None): |
|
""" |
|
Find indices of each distinct value in given array. |
|
|
|
Parameters |
|
---------- |
|
arr : ndarray of ints |
|
Array containing integer values. |
|
ignore_value : int, optional |
|
This value will be ignored in searching the `arr` array. If not |
|
given, all values found will be included in output. Default |
|
is None. |
|
|
|
Returns |
|
------- |
|
indices : dictionary |
|
A Python dictionary of array indices for each distinct value. The |
|
dictionary is keyed by the distinct values, the entries are array |
|
index tuples covering all occurrences of the value within the |
|
array. |
|
|
|
This dictionary can occupy significant memory, usually several times |
|
the size of the input array. |
|
|
|
See Also |
|
-------- |
|
label, maximum, median, minimum_position, extrema, sum, mean, variance, |
|
standard_deviation, numpy.where, numpy.unique |
|
|
|
Notes |
|
----- |
|
For a small array with few distinct values, one might use |
|
`numpy.unique()` to find all possible values, and ``(arr == val)`` to |
|
locate each value within that array. However, for large arrays, |
|
with many distinct values, this can become extremely inefficient, |
|
as locating each value would require a new search through the entire |
|
array. Using this function, there is essentially one search, with |
|
the indices saved for all distinct values. |
|
|
|
This is useful when matching a categorical image (e.g. a segmentation |
|
or classification) to an associated image of other data, allowing |
|
any per-class statistic(s) to then be calculated. Provides a |
|
more flexible alternative to functions like ``scipy.ndimage.mean()`` |
|
and ``scipy.ndimage.variance()``. |
|
|
|
Some other closely related functionality, with different strengths and |
|
weaknesses, can also be found in ``scipy.stats.binned_statistic()`` and |
|
the `scikit-image <https://scikit-image.org/>`_ function |
|
``skimage.measure.regionprops()``. |
|
|
|
Note for IDL users: this provides functionality equivalent to IDL's |
|
REVERSE_INDICES option (as per the IDL documentation for the |
|
`HISTOGRAM <https://www.l3harrisgeospatial.com/docs/histogram.html>`_ |
|
function). |
|
|
|
.. versionadded:: 1.10.0 |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> from scipy import ndimage |
|
>>> a = np.zeros((6, 6), dtype=int) |
|
>>> a[2:4, 2:4] = 1 |
|
>>> a[4, 4] = 1 |
|
>>> a[:2, :3] = 2 |
|
>>> a[0, 5] = 3 |
|
>>> a |
|
array([[2, 2, 2, 0, 0, 3], |
|
[2, 2, 2, 0, 0, 0], |
|
[0, 0, 1, 1, 0, 0], |
|
[0, 0, 1, 1, 0, 0], |
|
[0, 0, 0, 0, 1, 0], |
|
[0, 0, 0, 0, 0, 0]]) |
|
>>> val_indices = ndimage.value_indices(a) |
|
|
|
The dictionary `val_indices` will have an entry for each distinct |
|
value in the input array. |
|
|
|
>>> val_indices.keys() |
|
dict_keys([np.int64(0), np.int64(1), np.int64(2), np.int64(3)]) |
|
|
|
The entry for each value is an index tuple, locating the elements |
|
with that value. |
|
|
|
>>> ndx1 = val_indices[1] |
|
>>> ndx1 |
|
(array([2, 2, 3, 3, 4]), array([2, 3, 2, 3, 4])) |
|
|
|
This can be used to index into the original array, or any other |
|
array with the same shape. |
|
|
|
>>> a[ndx1] |
|
array([1, 1, 1, 1, 1]) |
|
|
|
If the zeros were to be ignored, then the resulting dictionary |
|
would no longer have an entry for zero. |
|
|
|
>>> val_indices = ndimage.value_indices(a, ignore_value=0) |
|
>>> val_indices.keys() |
|
dict_keys([np.int64(1), np.int64(2), np.int64(3)]) |
|
|
|
""" |
|
|
|
|
|
|
|
arr = np.asarray(arr) |
|
ignore_value_arr = np.zeros((1,), dtype=arr.dtype) |
|
ignoreIsNone = (ignore_value is None) |
|
if not ignoreIsNone: |
|
ignore_value_arr[0] = ignore_value_arr.dtype.type(ignore_value) |
|
|
|
val_indices = _nd_image.value_indices(arr, ignoreIsNone, ignore_value_arr) |
|
return val_indices |
|
|
|
|
|
def labeled_comprehension(input, labels, index, func, out_dtype, default, |
|
pass_positions=False): |
|
""" |
|
Roughly equivalent to [func(input[labels == i]) for i in index]. |
|
|
|
Sequentially applies an arbitrary function (that works on array_like input) |
|
to subsets of an N-D image array specified by `labels` and `index`. |
|
The option exists to provide the function with positional parameters as the |
|
second argument. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Data from which to select `labels` to process. |
|
labels : array_like or None |
|
Labels to objects in `input`. |
|
If not None, array must be same shape as `input`. |
|
If None, `func` is applied to raveled `input`. |
|
index : int, sequence of ints or None |
|
Subset of `labels` to which to apply `func`. |
|
If a scalar, a single value is returned. |
|
If None, `func` is applied to all non-zero values of `labels`. |
|
func : callable |
|
Python function to apply to `labels` from `input`. |
|
out_dtype : dtype |
|
Dtype to use for `result`. |
|
default : int, float or None |
|
Default return value when a element of `index` does not exist |
|
in `labels`. |
|
pass_positions : bool, optional |
|
If True, pass linear indices to `func` as a second argument. |
|
Default is False. |
|
|
|
Returns |
|
------- |
|
result : ndarray |
|
Result of applying `func` to each of `labels` to `input` in `index`. |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> from scipy import ndimage |
|
>>> lbl, nlbl = ndimage.label(a) |
|
>>> lbls = np.arange(1, nlbl+1) |
|
>>> ndimage.labeled_comprehension(a, lbl, lbls, np.mean, float, 0) |
|
array([ 2.75, 5.5 , 6. ]) |
|
|
|
Falling back to `default`: |
|
|
|
>>> lbls = np.arange(1, nlbl+2) |
|
>>> ndimage.labeled_comprehension(a, lbl, lbls, np.mean, float, -1) |
|
array([ 2.75, 5.5 , 6. , -1. ]) |
|
|
|
Passing positions: |
|
|
|
>>> def fn(val, pos): |
|
... print("fn says: %s : %s" % (val, pos)) |
|
... return (val.sum()) if (pos.sum() % 2 == 0) else (-val.sum()) |
|
... |
|
>>> ndimage.labeled_comprehension(a, lbl, lbls, fn, float, 0, True) |
|
fn says: [1 2 5 3] : [0 1 4 5] |
|
fn says: [4 7] : [ 7 11] |
|
fn says: [9 3] : [12 13] |
|
array([ 11., 11., -12., 0.]) |
|
|
|
""" |
|
|
|
as_scalar = np.isscalar(index) |
|
input = np.asarray(input) |
|
|
|
if pass_positions: |
|
positions = np.arange(input.size).reshape(input.shape) |
|
|
|
if labels is None: |
|
if index is not None: |
|
raise ValueError("index without defined labels") |
|
if not pass_positions: |
|
return func(input.ravel()) |
|
else: |
|
return func(input.ravel(), positions.ravel()) |
|
|
|
labels = np.asarray(labels) |
|
|
|
try: |
|
input, labels = np.broadcast_arrays(input, labels) |
|
except ValueError as e: |
|
raise ValueError("input and labels must have the same shape " |
|
"(excepting dimensions with width 1)") from e |
|
|
|
if index is None: |
|
if not pass_positions: |
|
return func(input[labels > 0]) |
|
else: |
|
return func(input[labels > 0], positions[labels > 0]) |
|
|
|
index = np.atleast_1d(index) |
|
if np.any(index.astype(labels.dtype).astype(index.dtype) != index): |
|
raise ValueError(f"Cannot convert index values from <{index.dtype}> to " |
|
f"<{labels.dtype}> (labels' type) without loss of precision") |
|
|
|
index = index.astype(labels.dtype) |
|
|
|
|
|
|
|
lo = index.min() |
|
hi = index.max() |
|
mask = (labels >= lo) & (labels <= hi) |
|
|
|
|
|
labels = labels[mask] |
|
input = input[mask] |
|
if pass_positions: |
|
positions = positions[mask] |
|
|
|
|
|
label_order = labels.argsort() |
|
labels = labels[label_order] |
|
input = input[label_order] |
|
if pass_positions: |
|
positions = positions[label_order] |
|
|
|
index_order = index.argsort() |
|
sorted_index = index[index_order] |
|
|
|
def do_map(inputs, output): |
|
"""labels must be sorted""" |
|
nidx = sorted_index.size |
|
|
|
|
|
|
|
lo = np.searchsorted(labels, sorted_index, side='left') |
|
hi = np.searchsorted(labels, sorted_index, side='right') |
|
|
|
for i, l, h in zip(range(nidx), lo, hi): |
|
if l == h: |
|
continue |
|
output[i] = func(*[inp[l:h] for inp in inputs]) |
|
|
|
temp = np.empty(index.shape, out_dtype) |
|
temp[:] = default |
|
if not pass_positions: |
|
do_map([input], temp) |
|
else: |
|
do_map([input, positions], temp) |
|
|
|
output = np.zeros(index.shape, out_dtype) |
|
output[index_order] = temp |
|
if as_scalar: |
|
output = output[0] |
|
|
|
return output |
|
|
|
|
|
def _safely_castable_to_int(dt): |
|
"""Test whether the NumPy data type `dt` can be safely cast to an int.""" |
|
int_size = np.dtype(int).itemsize |
|
safe = ((np.issubdtype(dt, np.signedinteger) and dt.itemsize <= int_size) or |
|
(np.issubdtype(dt, np.unsignedinteger) and dt.itemsize < int_size)) |
|
return safe |
|
|
|
|
|
def _stats(input, labels=None, index=None, centered=False): |
|
"""Count, sum, and optionally compute (sum - centre)^2 of input by label |
|
|
|
Parameters |
|
---------- |
|
input : array_like, N-D |
|
The input data to be analyzed. |
|
labels : array_like (N-D), optional |
|
The labels of the data in `input`. This array must be broadcast |
|
compatible with `input`; typically, it is the same shape as `input`. |
|
If `labels` is None, all nonzero values in `input` are treated as |
|
the single labeled group. |
|
index : label or sequence of labels, optional |
|
These are the labels of the groups for which the stats are computed. |
|
If `index` is None, the stats are computed for the single group where |
|
`labels` is greater than 0. |
|
centered : bool, optional |
|
If True, the centered sum of squares for each labeled group is |
|
also returned. Default is False. |
|
|
|
Returns |
|
------- |
|
counts : int or ndarray of ints |
|
The number of elements in each labeled group. |
|
sums : scalar or ndarray of scalars |
|
The sums of the values in each labeled group. |
|
sums_c : scalar or ndarray of scalars, optional |
|
The sums of mean-centered squares of the values in each labeled group. |
|
This is only returned if `centered` is True. |
|
|
|
""" |
|
def single_group(vals): |
|
if centered: |
|
vals_c = vals - vals.mean() |
|
return vals.size, vals.sum(), (vals_c * vals_c.conjugate()).sum() |
|
else: |
|
return vals.size, vals.sum() |
|
|
|
input = np.asarray(input) |
|
if labels is None: |
|
return single_group(input) |
|
|
|
|
|
input, labels = np.broadcast_arrays(input, labels) |
|
|
|
if index is None: |
|
return single_group(input[labels > 0]) |
|
|
|
if np.isscalar(index): |
|
return single_group(input[labels == index]) |
|
|
|
def _sum_centered(labels): |
|
|
|
|
|
|
|
means = sums / counts |
|
centered_input = input - means[labels] |
|
|
|
bc = np.bincount(labels.ravel(), |
|
weights=(centered_input * |
|
centered_input.conjugate()).ravel()) |
|
return bc |
|
|
|
|
|
|
|
|
|
if (not _safely_castable_to_int(labels.dtype) or |
|
labels.min() < 0 or labels.max() > labels.size): |
|
|
|
|
|
|
|
unique_labels, new_labels = np.unique(labels, return_inverse=True) |
|
new_labels = np.reshape(new_labels, (-1,)) |
|
counts = np.bincount(new_labels) |
|
sums = np.bincount(new_labels, weights=input.ravel()) |
|
if centered: |
|
|
|
|
|
|
|
sums_c = _sum_centered(new_labels.reshape(labels.shape)) |
|
idxs = np.searchsorted(unique_labels, index) |
|
|
|
idxs[idxs >= unique_labels.size] = 0 |
|
found = (unique_labels[idxs] == index) |
|
else: |
|
|
|
|
|
counts = np.bincount(labels.ravel()) |
|
sums = np.bincount(labels.ravel(), weights=input.ravel()) |
|
if centered: |
|
sums_c = _sum_centered(labels) |
|
|
|
idxs = np.asanyarray(index, np.int_).copy() |
|
found = (idxs >= 0) & (idxs < counts.size) |
|
idxs[~found] = 0 |
|
|
|
counts = counts[idxs] |
|
counts[~found] = 0 |
|
sums = sums[idxs] |
|
sums[~found] = 0 |
|
|
|
if not centered: |
|
return (counts, sums) |
|
else: |
|
sums_c = sums_c[idxs] |
|
sums_c[~found] = 0 |
|
return (counts, sums, sums_c) |
|
|
|
|
|
def sum(input, labels=None, index=None): |
|
""" |
|
Calculate the sum of the values of the array. |
|
|
|
Notes |
|
----- |
|
This is an alias for `ndimage.sum_labels` kept for backwards compatibility |
|
reasons, for new code please prefer `sum_labels`. See the `sum_labels` |
|
docstring for more details. |
|
|
|
""" |
|
return sum_labels(input, labels, index) |
|
|
|
|
|
def sum_labels(input, labels=None, index=None): |
|
""" |
|
Calculate the sum of the values of the array. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Values of `input` inside the regions defined by `labels` |
|
are summed together. |
|
labels : array_like of ints, optional |
|
Assign labels to the values of the array. Has to have the same shape as |
|
`input`. |
|
index : array_like, optional |
|
A single label number or a sequence of label numbers of |
|
the objects to be measured. |
|
|
|
Returns |
|
------- |
|
sum : ndarray or scalar |
|
An array of the sums of values of `input` inside the regions defined |
|
by `labels` with the same shape as `index`. If 'index' is None or scalar, |
|
a scalar is returned. |
|
|
|
See Also |
|
-------- |
|
mean, median |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> input = [0,1,2,3] |
|
>>> labels = [1,1,2,2] |
|
>>> ndimage.sum_labels(input, labels, index=[1,2]) |
|
[1.0, 5.0] |
|
>>> ndimage.sum_labels(input, labels, index=1) |
|
1 |
|
>>> ndimage.sum_labels(input, labels) |
|
6 |
|
|
|
|
|
""" |
|
count, sum = _stats(input, labels, index) |
|
return sum |
|
|
|
|
|
def mean(input, labels=None, index=None): |
|
""" |
|
Calculate the mean of the values of an array at labels. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Array on which to compute the mean of elements over distinct |
|
regions. |
|
labels : array_like, optional |
|
Array of labels of same shape, or broadcastable to the same shape as |
|
`input`. All elements sharing the same label form one region over |
|
which the mean of the elements is computed. |
|
index : int or sequence of ints, optional |
|
Labels of the objects over which the mean is to be computed. |
|
Default is None, in which case the mean for all values where label is |
|
greater than 0 is calculated. |
|
|
|
Returns |
|
------- |
|
out : list |
|
Sequence of same length as `index`, with the mean of the different |
|
regions labeled by the labels in `index`. |
|
|
|
See Also |
|
-------- |
|
variance, standard_deviation, minimum, maximum, sum, label |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> import numpy as np |
|
>>> a = np.arange(25).reshape((5,5)) |
|
>>> labels = np.zeros_like(a) |
|
>>> labels[3:5,3:5] = 1 |
|
>>> index = np.unique(labels) |
|
>>> labels |
|
array([[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 1, 1], |
|
[0, 0, 0, 1, 1]]) |
|
>>> index |
|
array([0, 1]) |
|
>>> ndimage.mean(a, labels=labels, index=index) |
|
[10.285714285714286, 21.0] |
|
|
|
""" |
|
|
|
count, sum = _stats(input, labels, index) |
|
return sum / np.asanyarray(count).astype(np.float64) |
|
|
|
|
|
def variance(input, labels=None, index=None): |
|
""" |
|
Calculate the variance of the values of an N-D image array, optionally at |
|
specified sub-regions. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Nd-image data to process. |
|
labels : array_like, optional |
|
Labels defining sub-regions in `input`. |
|
If not None, must be same shape as `input`. |
|
index : int or sequence of ints, optional |
|
`labels` to include in output. If None (default), all values where |
|
`labels` is non-zero are used. |
|
|
|
Returns |
|
------- |
|
variance : float or ndarray |
|
Values of variance, for each sub-region if `labels` and `index` are |
|
specified. |
|
|
|
See Also |
|
-------- |
|
label, standard_deviation, maximum, minimum, extrema |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> from scipy import ndimage |
|
>>> ndimage.variance(a) |
|
7.609375 |
|
|
|
Features to process can be specified using `labels` and `index`: |
|
|
|
>>> lbl, nlbl = ndimage.label(a) |
|
>>> ndimage.variance(a, lbl, index=np.arange(1, nlbl+1)) |
|
array([ 2.1875, 2.25 , 9. ]) |
|
|
|
If no index is given, all non-zero `labels` are processed: |
|
|
|
>>> ndimage.variance(a, lbl) |
|
6.1875 |
|
|
|
""" |
|
count, sum, sum_c_sq = _stats(input, labels, index, centered=True) |
|
return sum_c_sq / np.asanyarray(count).astype(float) |
|
|
|
|
|
def standard_deviation(input, labels=None, index=None): |
|
""" |
|
Calculate the standard deviation of the values of an N-D image array, |
|
optionally at specified sub-regions. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
N-D image data to process. |
|
labels : array_like, optional |
|
Labels to identify sub-regions in `input`. |
|
If not None, must be same shape as `input`. |
|
index : int or sequence of ints, optional |
|
`labels` to include in output. If None (default), all values where |
|
`labels` is non-zero are used. |
|
|
|
Returns |
|
------- |
|
standard_deviation : float or ndarray |
|
Values of standard deviation, for each sub-region if `labels` and |
|
`index` are specified. |
|
|
|
See Also |
|
-------- |
|
label, variance, maximum, minimum, extrema |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> from scipy import ndimage |
|
>>> ndimage.standard_deviation(a) |
|
2.7585095613392387 |
|
|
|
Features to process can be specified using `labels` and `index`: |
|
|
|
>>> lbl, nlbl = ndimage.label(a) |
|
>>> ndimage.standard_deviation(a, lbl, index=np.arange(1, nlbl+1)) |
|
array([ 1.479, 1.5 , 3. ]) |
|
|
|
If no index is given, non-zero `labels` are processed: |
|
|
|
>>> ndimage.standard_deviation(a, lbl) |
|
2.4874685927665499 |
|
|
|
""" |
|
return np.sqrt(variance(input, labels, index)) |
|
|
|
|
|
def _select(input, labels=None, index=None, find_min=False, find_max=False, |
|
find_min_positions=False, find_max_positions=False, |
|
find_median=False): |
|
"""Returns min, max, or both, plus their positions (if requested), and |
|
median.""" |
|
|
|
input = np.asanyarray(input) |
|
|
|
find_positions = find_min_positions or find_max_positions |
|
positions = None |
|
if find_positions: |
|
positions = np.arange(input.size).reshape(input.shape) |
|
|
|
def single_group(vals, positions): |
|
result = [] |
|
if find_min: |
|
result += [vals.min()] |
|
if find_min_positions: |
|
result += [positions[vals == vals.min()][0]] |
|
if find_max: |
|
result += [vals.max()] |
|
if find_max_positions: |
|
result += [positions[vals == vals.max()][0]] |
|
if find_median: |
|
result += [np.median(vals)] |
|
return result |
|
|
|
if labels is None: |
|
return single_group(input, positions) |
|
|
|
|
|
input, labels = np.broadcast_arrays(input, labels) |
|
|
|
if index is None: |
|
mask = (labels > 0) |
|
masked_positions = None |
|
if find_positions: |
|
masked_positions = positions[mask] |
|
return single_group(input[mask], masked_positions) |
|
|
|
if np.isscalar(index): |
|
mask = (labels == index) |
|
masked_positions = None |
|
if find_positions: |
|
masked_positions = positions[mask] |
|
return single_group(input[mask], masked_positions) |
|
|
|
index = np.asarray(index) |
|
|
|
|
|
|
|
if (not _safely_castable_to_int(labels.dtype) or |
|
labels.min() < 0 or labels.max() > labels.size): |
|
|
|
unique_labels, labels = np.unique(labels, return_inverse=True) |
|
idxs = np.searchsorted(unique_labels, index) |
|
|
|
|
|
idxs[idxs >= unique_labels.size] = 0 |
|
found = (unique_labels[idxs] == index) |
|
else: |
|
|
|
idxs = np.asanyarray(index, np.int_).copy() |
|
found = (idxs >= 0) & (idxs <= labels.max()) |
|
|
|
idxs[~ found] = labels.max() + 1 |
|
|
|
if find_median: |
|
order = np.lexsort((input.ravel(), labels.ravel())) |
|
else: |
|
order = input.ravel().argsort() |
|
input = input.ravel()[order] |
|
labels = labels.ravel()[order] |
|
if find_positions: |
|
positions = positions.ravel()[order] |
|
|
|
result = [] |
|
if find_min: |
|
mins = np.zeros(labels.max() + 2, input.dtype) |
|
mins[labels[::-1]] = input[::-1] |
|
result += [mins[idxs]] |
|
if find_min_positions: |
|
minpos = np.zeros(labels.max() + 2, int) |
|
minpos[labels[::-1]] = positions[::-1] |
|
result += [minpos[idxs]] |
|
if find_max: |
|
maxs = np.zeros(labels.max() + 2, input.dtype) |
|
maxs[labels] = input |
|
result += [maxs[idxs]] |
|
if find_max_positions: |
|
maxpos = np.zeros(labels.max() + 2, int) |
|
maxpos[labels] = positions |
|
result += [maxpos[idxs]] |
|
if find_median: |
|
locs = np.arange(len(labels)) |
|
lo = np.zeros(labels.max() + 2, np.int_) |
|
lo[labels[::-1]] = locs[::-1] |
|
hi = np.zeros(labels.max() + 2, np.int_) |
|
hi[labels] = locs |
|
lo = lo[idxs] |
|
hi = hi[idxs] |
|
|
|
|
|
|
|
|
|
step = (hi - lo) // 2 |
|
lo += step |
|
hi -= step |
|
if (np.issubdtype(input.dtype, np.integer) |
|
or np.issubdtype(input.dtype, np.bool_)): |
|
|
|
result += [(input[lo].astype('d') + input[hi].astype('d')) / 2.0] |
|
else: |
|
result += [(input[lo] + input[hi]) / 2.0] |
|
|
|
return result |
|
|
|
|
|
def minimum(input, labels=None, index=None): |
|
""" |
|
Calculate the minimum of the values of an array over labeled regions. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Array_like of values. For each region specified by `labels`, the |
|
minimal values of `input` over the region is computed. |
|
labels : array_like, optional |
|
An array_like of integers marking different regions over which the |
|
minimum value of `input` is to be computed. `labels` must have the |
|
same shape as `input`. If `labels` is not specified, the minimum |
|
over the whole array is returned. |
|
index : array_like, optional |
|
A list of region labels that are taken into account for computing the |
|
minima. If index is None, the minimum over all elements where `labels` |
|
is non-zero is returned. |
|
|
|
Returns |
|
------- |
|
minimum : float or list of floats |
|
List of minima of `input` over the regions determined by `labels` and |
|
whose index is in `index`. If `index` or `labels` are not specified, a |
|
float is returned: the minimal value of `input` if `labels` is None, |
|
and the minimal value of elements where `labels` is greater than zero |
|
if `index` is None. |
|
|
|
See Also |
|
-------- |
|
label, maximum, median, minimum_position, extrema, sum, mean, variance, |
|
standard_deviation |
|
|
|
Notes |
|
----- |
|
The function returns a Python list and not a NumPy array, use |
|
`np.array` to convert the list to an array. |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> labels, labels_nb = ndimage.label(a) |
|
>>> labels |
|
array([[1, 1, 0, 0], |
|
[1, 1, 0, 2], |
|
[0, 0, 0, 2], |
|
[3, 3, 0, 0]], dtype=int32) |
|
>>> ndimage.minimum(a, labels=labels, index=np.arange(1, labels_nb + 1)) |
|
[1, 4, 3] |
|
>>> ndimage.minimum(a) |
|
0 |
|
>>> ndimage.minimum(a, labels=labels) |
|
1 |
|
|
|
""" |
|
return _select(input, labels, index, find_min=True)[0] |
|
|
|
|
|
def maximum(input, labels=None, index=None): |
|
""" |
|
Calculate the maximum of the values of an array over labeled regions. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Array_like of values. For each region specified by `labels`, the |
|
maximal values of `input` over the region is computed. |
|
labels : array_like, optional |
|
An array of integers marking different regions over which the |
|
maximum value of `input` is to be computed. `labels` must have the |
|
same shape as `input`. If `labels` is not specified, the maximum |
|
over the whole array is returned. |
|
index : array_like, optional |
|
A list of region labels that are taken into account for computing the |
|
maxima. If index is None, the maximum over all elements where `labels` |
|
is non-zero is returned. |
|
|
|
Returns |
|
------- |
|
output : float or list of floats |
|
List of maxima of `input` over the regions determined by `labels` and |
|
whose index is in `index`. If `index` or `labels` are not specified, a |
|
float is returned: the maximal value of `input` if `labels` is None, |
|
and the maximal value of elements where `labels` is greater than zero |
|
if `index` is None. |
|
|
|
See Also |
|
-------- |
|
label, minimum, median, maximum_position, extrema, sum, mean, variance, |
|
standard_deviation |
|
|
|
Notes |
|
----- |
|
The function returns a Python list and not a NumPy array, use |
|
`np.array` to convert the list to an array. |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.arange(16).reshape((4,4)) |
|
>>> a |
|
array([[ 0, 1, 2, 3], |
|
[ 4, 5, 6, 7], |
|
[ 8, 9, 10, 11], |
|
[12, 13, 14, 15]]) |
|
>>> labels = np.zeros_like(a) |
|
>>> labels[:2,:2] = 1 |
|
>>> labels[2:, 1:3] = 2 |
|
>>> labels |
|
array([[1, 1, 0, 0], |
|
[1, 1, 0, 0], |
|
[0, 2, 2, 0], |
|
[0, 2, 2, 0]]) |
|
>>> from scipy import ndimage |
|
>>> ndimage.maximum(a) |
|
15 |
|
>>> ndimage.maximum(a, labels=labels, index=[1,2]) |
|
[5, 14] |
|
>>> ndimage.maximum(a, labels=labels) |
|
14 |
|
|
|
>>> b = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> labels, labels_nb = ndimage.label(b) |
|
>>> labels |
|
array([[1, 1, 0, 0], |
|
[1, 1, 0, 2], |
|
[0, 0, 0, 2], |
|
[3, 3, 0, 0]], dtype=int32) |
|
>>> ndimage.maximum(b, labels=labels, index=np.arange(1, labels_nb + 1)) |
|
[5, 7, 9] |
|
|
|
""" |
|
return _select(input, labels, index, find_max=True)[0] |
|
|
|
|
|
def median(input, labels=None, index=None): |
|
""" |
|
Calculate the median of the values of an array over labeled regions. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Array_like of values. For each region specified by `labels`, the |
|
median value of `input` over the region is computed. |
|
labels : array_like, optional |
|
An array_like of integers marking different regions over which the |
|
median value of `input` is to be computed. `labels` must have the |
|
same shape as `input`. If `labels` is not specified, the median |
|
over the whole array is returned. |
|
index : array_like, optional |
|
A list of region labels that are taken into account for computing the |
|
medians. If index is None, the median over all elements where `labels` |
|
is non-zero is returned. |
|
|
|
Returns |
|
------- |
|
median : float or list of floats |
|
List of medians of `input` over the regions determined by `labels` and |
|
whose index is in `index`. If `index` or `labels` are not specified, a |
|
float is returned: the median value of `input` if `labels` is None, |
|
and the median value of elements where `labels` is greater than zero |
|
if `index` is None. |
|
|
|
See Also |
|
-------- |
|
label, minimum, maximum, extrema, sum, mean, variance, standard_deviation |
|
|
|
Notes |
|
----- |
|
The function returns a Python list and not a NumPy array, use |
|
`np.array` to convert the list to an array. |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 1], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> labels, labels_nb = ndimage.label(a) |
|
>>> labels |
|
array([[1, 1, 0, 2], |
|
[1, 1, 0, 2], |
|
[0, 0, 0, 2], |
|
[3, 3, 0, 0]], dtype=int32) |
|
>>> ndimage.median(a, labels=labels, index=np.arange(1, labels_nb + 1)) |
|
[2.5, 4.0, 6.0] |
|
>>> ndimage.median(a) |
|
1.0 |
|
>>> ndimage.median(a, labels=labels) |
|
3.0 |
|
|
|
""" |
|
return _select(input, labels, index, find_median=True)[0] |
|
|
|
|
|
def minimum_position(input, labels=None, index=None): |
|
""" |
|
Find the positions of the minimums of the values of an array at labels. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Array_like of values. |
|
labels : array_like, optional |
|
An array of integers marking different regions over which the |
|
position of the minimum value of `input` is to be computed. |
|
`labels` must have the same shape as `input`. If `labels` is not |
|
specified, the location of the first minimum over the whole |
|
array is returned. |
|
|
|
The `labels` argument only works when `index` is specified. |
|
index : array_like, optional |
|
A list of region labels that are taken into account for finding the |
|
location of the minima. If `index` is None, the ``first`` minimum |
|
over all elements where `labels` is non-zero is returned. |
|
|
|
The `index` argument only works when `labels` is specified. |
|
|
|
Returns |
|
------- |
|
output : list of tuples of ints |
|
Tuple of ints or list of tuples of ints that specify the location |
|
of minima of `input` over the regions determined by `labels` and |
|
whose index is in `index`. |
|
|
|
If `index` or `labels` are not specified, a tuple of ints is |
|
returned specifying the location of the first minimal value of `input`. |
|
|
|
See Also |
|
-------- |
|
label, minimum, median, maximum_position, extrema, sum, mean, variance, |
|
standard_deviation |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array([[10, 20, 30], |
|
... [40, 80, 100], |
|
... [1, 100, 200]]) |
|
>>> b = np.array([[1, 2, 0, 1], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
|
|
>>> from scipy import ndimage |
|
|
|
>>> ndimage.minimum_position(a) |
|
(2, 0) |
|
>>> ndimage.minimum_position(b) |
|
(0, 2) |
|
|
|
Features to process can be specified using `labels` and `index`: |
|
|
|
>>> label, pos = ndimage.label(a) |
|
>>> ndimage.minimum_position(a, label, index=np.arange(1, pos+1)) |
|
[(2, 0)] |
|
|
|
>>> label, pos = ndimage.label(b) |
|
>>> ndimage.minimum_position(b, label, index=np.arange(1, pos+1)) |
|
[(0, 0), (0, 3), (3, 1)] |
|
|
|
""" |
|
dims = np.array(np.asarray(input).shape) |
|
|
|
dim_prod = np.cumprod([1] + list(dims[:0:-1]))[::-1] |
|
|
|
result = _select(input, labels, index, find_min_positions=True)[0] |
|
|
|
if np.isscalar(result): |
|
return tuple((result // dim_prod) % dims) |
|
|
|
return [tuple(v) for v in (result.reshape(-1, 1) // dim_prod) % dims] |
|
|
|
|
|
def maximum_position(input, labels=None, index=None): |
|
""" |
|
Find the positions of the maximums of the values of an array at labels. |
|
|
|
For each region specified by `labels`, the position of the maximum |
|
value of `input` within the region is returned. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Array_like of values. |
|
labels : array_like, optional |
|
An array of integers marking different regions over which the |
|
position of the maximum value of `input` is to be computed. |
|
`labels` must have the same shape as `input`. If `labels` is not |
|
specified, the location of the first maximum over the whole |
|
array is returned. |
|
|
|
The `labels` argument only works when `index` is specified. |
|
index : array_like, optional |
|
A list of region labels that are taken into account for finding the |
|
location of the maxima. If `index` is None, the first maximum |
|
over all elements where `labels` is non-zero is returned. |
|
|
|
The `index` argument only works when `labels` is specified. |
|
|
|
Returns |
|
------- |
|
output : list of tuples of ints |
|
List of tuples of ints that specify the location of maxima of |
|
`input` over the regions determined by `labels` and whose index |
|
is in `index`. |
|
|
|
If `index` or `labels` are not specified, a tuple of ints is |
|
returned specifying the location of the ``first`` maximal value |
|
of `input`. |
|
|
|
See Also |
|
-------- |
|
label, minimum, median, maximum_position, extrema, sum, mean, variance, |
|
standard_deviation |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> ndimage.maximum_position(a) |
|
(3, 0) |
|
|
|
Features to process can be specified using `labels` and `index`: |
|
|
|
>>> lbl = np.array([[0, 1, 2, 3], |
|
... [0, 1, 2, 3], |
|
... [0, 1, 2, 3], |
|
... [0, 1, 2, 3]]) |
|
>>> ndimage.maximum_position(a, lbl, 1) |
|
(1, 1) |
|
|
|
If no index is given, non-zero `labels` are processed: |
|
|
|
>>> ndimage.maximum_position(a, lbl) |
|
(2, 3) |
|
|
|
If there are no maxima, the position of the first element is returned: |
|
|
|
>>> ndimage.maximum_position(a, lbl, 2) |
|
(0, 2) |
|
|
|
""" |
|
dims = np.array(np.asarray(input).shape) |
|
|
|
dim_prod = np.cumprod([1] + list(dims[:0:-1]))[::-1] |
|
|
|
result = _select(input, labels, index, find_max_positions=True)[0] |
|
|
|
if np.isscalar(result): |
|
return tuple((result // dim_prod) % dims) |
|
|
|
return [tuple(v) for v in (result.reshape(-1, 1) // dim_prod) % dims] |
|
|
|
|
|
def extrema(input, labels=None, index=None): |
|
""" |
|
Calculate the minimums and maximums of the values of an array |
|
at labels, along with their positions. |
|
|
|
Parameters |
|
---------- |
|
input : ndarray |
|
N-D image data to process. |
|
labels : ndarray, optional |
|
Labels of features in input. |
|
If not None, must be same shape as `input`. |
|
index : int or sequence of ints, optional |
|
Labels to include in output. If None (default), all values where |
|
non-zero `labels` are used. |
|
|
|
Returns |
|
------- |
|
minimums, maximums : int or ndarray |
|
Values of minimums and maximums in each feature. |
|
min_positions, max_positions : tuple or list of tuples |
|
Each tuple gives the N-D coordinates of the corresponding minimum |
|
or maximum. |
|
|
|
See Also |
|
-------- |
|
maximum, minimum, maximum_position, minimum_position, center_of_mass |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array([[1, 2, 0, 0], |
|
... [5, 3, 0, 4], |
|
... [0, 0, 0, 7], |
|
... [9, 3, 0, 0]]) |
|
>>> from scipy import ndimage |
|
>>> ndimage.extrema(a) |
|
(0, 9, (0, 2), (3, 0)) |
|
|
|
Features to process can be specified using `labels` and `index`: |
|
|
|
>>> lbl, nlbl = ndimage.label(a) |
|
>>> ndimage.extrema(a, lbl, index=np.arange(1, nlbl+1)) |
|
(array([1, 4, 3]), |
|
array([5, 7, 9]), |
|
[(0, 0), (1, 3), (3, 1)], |
|
[(1, 0), (2, 3), (3, 0)]) |
|
|
|
If no index is given, non-zero `labels` are processed: |
|
|
|
>>> ndimage.extrema(a, lbl) |
|
(1, 9, (0, 0), (3, 0)) |
|
|
|
""" |
|
dims = np.array(np.asarray(input).shape) |
|
|
|
dim_prod = np.cumprod([1] + list(dims[:0:-1]))[::-1] |
|
|
|
minimums, min_positions, maximums, max_positions = _select(input, labels, |
|
index, |
|
find_min=True, |
|
find_max=True, |
|
find_min_positions=True, |
|
find_max_positions=True) |
|
|
|
if np.isscalar(minimums): |
|
return (minimums, maximums, tuple((min_positions // dim_prod) % dims), |
|
tuple((max_positions // dim_prod) % dims)) |
|
|
|
min_positions = [ |
|
tuple(v) for v in (min_positions.reshape(-1, 1) // dim_prod) % dims |
|
] |
|
max_positions = [ |
|
tuple(v) for v in (max_positions.reshape(-1, 1) // dim_prod) % dims |
|
] |
|
|
|
return minimums, maximums, min_positions, max_positions |
|
|
|
|
|
def center_of_mass(input, labels=None, index=None): |
|
""" |
|
Calculate the center of mass of the values of an array at labels. |
|
|
|
Parameters |
|
---------- |
|
input : ndarray |
|
Data from which to calculate center-of-mass. The masses can either |
|
be positive or negative. |
|
labels : ndarray, optional |
|
Labels for objects in `input`, as generated by `ndimage.label`. |
|
Only used with `index`. Dimensions must be the same as `input`. |
|
index : int or sequence of ints, optional |
|
Labels for which to calculate centers-of-mass. If not specified, |
|
the combined center of mass of all labels greater than zero |
|
will be calculated. Only used with `labels`. |
|
|
|
Returns |
|
------- |
|
center_of_mass : tuple, or list of tuples |
|
Coordinates of centers-of-mass. |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array(([0,0,0,0], |
|
... [0,1,1,0], |
|
... [0,1,1,0], |
|
... [0,1,1,0])) |
|
>>> from scipy import ndimage |
|
>>> ndimage.center_of_mass(a) |
|
(2.0, 1.5) |
|
|
|
Calculation of multiple objects in an image |
|
|
|
>>> b = np.array(([0,1,1,0], |
|
... [0,1,0,0], |
|
... [0,0,0,0], |
|
... [0,0,1,1], |
|
... [0,0,1,1])) |
|
>>> lbl = ndimage.label(b)[0] |
|
>>> ndimage.center_of_mass(b, lbl, [1,2]) |
|
[(0.33333333333333331, 1.3333333333333333), (3.5, 2.5)] |
|
|
|
Negative masses are also accepted, which can occur for example when |
|
bias is removed from measured data due to random noise. |
|
|
|
>>> c = np.array(([-1,0,0,0], |
|
... [0,-1,-1,0], |
|
... [0,1,-1,0], |
|
... [0,1,1,0])) |
|
>>> ndimage.center_of_mass(c) |
|
(-4.0, 1.0) |
|
|
|
If there are division by zero issues, the function does not raise an |
|
error but rather issues a RuntimeWarning before returning inf and/or NaN. |
|
|
|
>>> d = np.array([-1, 1]) |
|
>>> ndimage.center_of_mass(d) |
|
(inf,) |
|
""" |
|
input = np.asarray(input) |
|
normalizer = sum_labels(input, labels, index) |
|
grids = np.ogrid[[slice(0, i) for i in input.shape]] |
|
|
|
results = [sum_labels(input * grids[dir].astype(float), labels, index) / normalizer |
|
for dir in range(input.ndim)] |
|
|
|
if np.isscalar(results[0]): |
|
return tuple(results) |
|
|
|
return [tuple(v) for v in np.array(results).T] |
|
|
|
|
|
def histogram(input, min, max, bins, labels=None, index=None): |
|
""" |
|
Calculate the histogram of the values of an array, optionally at labels. |
|
|
|
Histogram calculates the frequency of values in an array within bins |
|
determined by `min`, `max`, and `bins`. The `labels` and `index` |
|
keywords can limit the scope of the histogram to specified sub-regions |
|
within the array. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Data for which to calculate histogram. |
|
min, max : int |
|
Minimum and maximum values of range of histogram bins. |
|
bins : int |
|
Number of bins. |
|
labels : array_like, optional |
|
Labels for objects in `input`. |
|
If not None, must be same shape as `input`. |
|
index : int or sequence of ints, optional |
|
Label or labels for which to calculate histogram. If None, all values |
|
where label is greater than zero are used |
|
|
|
Returns |
|
------- |
|
hist : ndarray |
|
Histogram counts. |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> a = np.array([[ 0. , 0.2146, 0.5962, 0. ], |
|
... [ 0. , 0.7778, 0. , 0. ], |
|
... [ 0. , 0. , 0. , 0. ], |
|
... [ 0. , 0. , 0.7181, 0.2787], |
|
... [ 0. , 0. , 0.6573, 0.3094]]) |
|
>>> from scipy import ndimage |
|
>>> ndimage.histogram(a, 0, 1, 10) |
|
array([13, 0, 2, 1, 0, 1, 1, 2, 0, 0]) |
|
|
|
With labels and no indices, non-zero elements are counted: |
|
|
|
>>> lbl, nlbl = ndimage.label(a) |
|
>>> ndimage.histogram(a, 0, 1, 10, lbl) |
|
array([0, 0, 2, 1, 0, 1, 1, 2, 0, 0]) |
|
|
|
Indices can be used to count only certain objects: |
|
|
|
>>> ndimage.histogram(a, 0, 1, 10, lbl, 2) |
|
array([0, 0, 1, 1, 0, 0, 1, 1, 0, 0]) |
|
|
|
""" |
|
_bins = np.linspace(min, max, bins + 1) |
|
|
|
def _hist(vals): |
|
return np.histogram(vals, _bins)[0] |
|
|
|
return labeled_comprehension(input, labels, index, _hist, object, None, |
|
pass_positions=False) |
|
|
|
|
|
def watershed_ift(input, markers, structure=None, output=None): |
|
""" |
|
Apply watershed from markers using image foresting transform algorithm. |
|
|
|
Parameters |
|
---------- |
|
input : array_like |
|
Input. |
|
markers : array_like |
|
Markers are points within each watershed that form the beginning |
|
of the process. Negative markers are considered background markers |
|
which are processed after the other markers. |
|
structure : structure element, optional |
|
A structuring element defining the connectivity of the object can be |
|
provided. If None, an element is generated with a squared |
|
connectivity equal to one. |
|
output : ndarray, optional |
|
An output array can optionally be provided. The same shape as input. |
|
|
|
Returns |
|
------- |
|
watershed_ift : ndarray |
|
Output. Same shape as `input`. |
|
|
|
References |
|
---------- |
|
.. [1] A.X. Falcao, J. Stolfi and R. de Alencar Lotufo, "The image |
|
foresting transform: theory, algorithms, and applications", |
|
Pattern Analysis and Machine Intelligence, vol. 26, pp. 19-29, 2004. |
|
|
|
""" |
|
input = np.asarray(input) |
|
if input.dtype.type not in [np.uint8, np.uint16]: |
|
raise TypeError('only 8 and 16 unsigned inputs are supported') |
|
|
|
if structure is None: |
|
structure = _morphology.generate_binary_structure(input.ndim, 1) |
|
structure = np.asarray(structure, dtype=bool) |
|
if structure.ndim != input.ndim: |
|
raise RuntimeError('structure and input must have equal rank') |
|
for ii in structure.shape: |
|
if ii != 3: |
|
raise RuntimeError('structure dimensions must be equal to 3') |
|
|
|
if not structure.flags.contiguous: |
|
structure = structure.copy() |
|
markers = np.asarray(markers) |
|
if input.shape != markers.shape: |
|
raise RuntimeError('input and markers must have equal shape') |
|
|
|
integral_types = [np.int8, |
|
np.int16, |
|
np.int32, |
|
np.int64, |
|
np.intc, |
|
np.intp] |
|
|
|
if markers.dtype.type not in integral_types: |
|
raise RuntimeError('marker should be of integer type') |
|
|
|
if isinstance(output, np.ndarray): |
|
if output.dtype.type not in integral_types: |
|
raise RuntimeError('output should be of integer type') |
|
else: |
|
output = markers.dtype |
|
|
|
output = _ni_support._get_output(output, input) |
|
_nd_image.watershed_ift(input, markers, structure, output) |
|
return output |
|
|