Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
211
venv/Lib/site-packages/skimage/morphology/greyreconstruct.py
Normal file
211
venv/Lib/site-packages/skimage/morphology/greyreconstruct.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
"""
|
||||
This morphological reconstruction routine was adapted from CellProfiler, code
|
||||
licensed under both GPL and BSD licenses.
|
||||
|
||||
Website: http://www.cellprofiler.org
|
||||
Copyright (c) 2003-2009 Massachusetts Institute of Technology
|
||||
Copyright (c) 2009-2011 Broad Institute
|
||||
All rights reserved.
|
||||
Original author: Lee Kamentsky
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from ..filters._rank_order import rank_order
|
||||
|
||||
|
||||
def reconstruction(seed, mask, method='dilation', selem=None, offset=None):
|
||||
"""Perform a morphological reconstruction of an image.
|
||||
|
||||
Morphological reconstruction by dilation is similar to basic morphological
|
||||
dilation: high-intensity values will replace nearby low-intensity values.
|
||||
The basic dilation operator, however, uses a structuring element to
|
||||
determine how far a value in the input image can spread. In contrast,
|
||||
reconstruction uses two images: a "seed" image, which specifies the values
|
||||
that spread, and a "mask" image, which gives the maximum allowed value at
|
||||
each pixel. The mask image, like the structuring element, limits the spread
|
||||
of high-intensity values. Reconstruction by erosion is simply the inverse:
|
||||
low-intensity values spread from the seed image and are limited by the mask
|
||||
image, which represents the minimum allowed value.
|
||||
|
||||
Alternatively, you can think of reconstruction as a way to isolate the
|
||||
connected regions of an image. For dilation, reconstruction connects
|
||||
regions marked by local maxima in the seed image: neighboring pixels
|
||||
less-than-or-equal-to those seeds are connected to the seeded region.
|
||||
Local maxima with values larger than the seed image will get truncated to
|
||||
the seed value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
seed : ndarray
|
||||
The seed image (a.k.a. marker image), which specifies the values that
|
||||
are dilated or eroded.
|
||||
mask : ndarray
|
||||
The maximum (dilation) / minimum (erosion) allowed value at each pixel.
|
||||
method : {'dilation'|'erosion'}, optional
|
||||
Perform reconstruction by dilation or erosion. In dilation (or
|
||||
erosion), the seed image is dilated (or eroded) until limited by the
|
||||
mask image. For dilation, each seed value must be less than or equal
|
||||
to the corresponding mask value; for erosion, the reverse is true.
|
||||
Default is 'dilation'.
|
||||
selem : ndarray, optional
|
||||
The neighborhood expressed as an n-D array of 1's and 0's.
|
||||
Default is the n-D square of radius equal to 1 (i.e. a 3x3 square
|
||||
for 2D images, a 3x3x3 cube for 3D images, etc.)
|
||||
offset : ndarray, optional
|
||||
The coordinates of the center of the structuring element.
|
||||
Default is located on the geometrical center of the selem, in that case
|
||||
selem dimensions must be odd.
|
||||
|
||||
Returns
|
||||
-------
|
||||
reconstructed : ndarray
|
||||
The result of morphological reconstruction.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from skimage.morphology import reconstruction
|
||||
|
||||
First, we create a sinusoidal mask image with peaks at middle and ends.
|
||||
|
||||
>>> x = np.linspace(0, 4 * np.pi)
|
||||
>>> y_mask = np.cos(x)
|
||||
|
||||
Then, we create a seed image initialized to the minimum mask value (for
|
||||
reconstruction by dilation, min-intensity values don't spread) and add
|
||||
"seeds" to the left and right peak, but at a fraction of peak value (1).
|
||||
|
||||
>>> y_seed = y_mask.min() * np.ones_like(x)
|
||||
>>> y_seed[0] = 0.5
|
||||
>>> y_seed[-1] = 0
|
||||
>>> y_rec = reconstruction(y_seed, y_mask)
|
||||
|
||||
The reconstructed image (or curve, in this case) is exactly the same as the
|
||||
mask image, except that the peaks are truncated to 0.5 and 0. The middle
|
||||
peak disappears completely: Since there were no seed values in this peak
|
||||
region, its reconstructed value is truncated to the surrounding value (-1).
|
||||
|
||||
As a more practical example, we try to extract the bright features of an
|
||||
image by subtracting a background image created by reconstruction.
|
||||
|
||||
>>> y, x = np.mgrid[:20:0.5, :20:0.5]
|
||||
>>> bumps = np.sin(x) + np.sin(y)
|
||||
|
||||
To create the background image, set the mask image to the original image,
|
||||
and the seed image to the original image with an intensity offset, `h`.
|
||||
|
||||
>>> h = 0.3
|
||||
>>> seed = bumps - h
|
||||
>>> background = reconstruction(seed, bumps)
|
||||
|
||||
The resulting reconstructed image looks exactly like the original image,
|
||||
but with the peaks of the bumps cut off. Subtracting this reconstructed
|
||||
image from the original image leaves just the peaks of the bumps
|
||||
|
||||
>>> hdome = bumps - background
|
||||
|
||||
This operation is known as the h-dome of the image and leaves features
|
||||
of height `h` in the subtracted image.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm is taken from [1]_. Applications for greyscale reconstruction
|
||||
are discussed in [2]_ and [3]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Robinson, "Efficient morphological reconstruction: a downhill
|
||||
filter", Pattern Recognition Letters 25 (2004) 1759-1767.
|
||||
.. [2] Vincent, L., "Morphological Grayscale Reconstruction in Image
|
||||
Analysis: Applications and Efficient Algorithms", IEEE Transactions
|
||||
on Image Processing (1993)
|
||||
.. [3] Soille, P., "Morphological Image Analysis: Principles and
|
||||
Applications", Chapter 6, 2nd edition (2003), ISBN 3540429883.
|
||||
"""
|
||||
assert tuple(seed.shape) == tuple(mask.shape)
|
||||
if method == 'dilation' and np.any(seed > mask):
|
||||
raise ValueError("Intensity of seed image must be less than that "
|
||||
"of the mask image for reconstruction by dilation.")
|
||||
elif method == 'erosion' and np.any(seed < mask):
|
||||
raise ValueError("Intensity of seed image must be greater than that "
|
||||
"of the mask image for reconstruction by erosion.")
|
||||
try:
|
||||
from ._greyreconstruct import reconstruction_loop
|
||||
except ImportError:
|
||||
raise ImportError("_greyreconstruct extension not available.")
|
||||
|
||||
if selem is None:
|
||||
selem = np.ones([3] * seed.ndim, dtype=bool)
|
||||
else:
|
||||
selem = selem.astype(bool)
|
||||
|
||||
if offset is None:
|
||||
if not all([d % 2 == 1 for d in selem.shape]):
|
||||
raise ValueError("Footprint dimensions must all be odd")
|
||||
offset = np.array([d // 2 for d in selem.shape])
|
||||
else:
|
||||
if offset.ndim != selem.ndim:
|
||||
raise ValueError("Offset and selem ndims must be equal.")
|
||||
if not all([(0 <= o < d) for o, d in zip(offset, selem.shape)]):
|
||||
raise ValueError("Offset must be included inside selem")
|
||||
|
||||
# Cross out the center of the selem
|
||||
selem[tuple(slice(d, d + 1) for d in offset)] = False
|
||||
|
||||
# Make padding for edges of reconstructed image so we can ignore boundaries
|
||||
dims = np.zeros(seed.ndim + 1, dtype=int)
|
||||
dims[1:] = np.array(seed.shape) + (np.array(selem.shape) - 1)
|
||||
dims[0] = 2
|
||||
inside_slices = tuple(slice(o, o + s) for o, s in zip(offset, seed.shape))
|
||||
# Set padded region to minimum image intensity and mask along first axis so
|
||||
# we can interleave image and mask pixels when sorting.
|
||||
if method == 'dilation':
|
||||
pad_value = np.min(seed)
|
||||
elif method == 'erosion':
|
||||
pad_value = np.max(seed)
|
||||
else:
|
||||
raise ValueError("Reconstruction method can be one of 'erosion' "
|
||||
"or 'dilation'. Got '%s'." % method)
|
||||
images = np.full(dims, pad_value, dtype='float64')
|
||||
images[(0, *inside_slices)] = seed
|
||||
images[(1, *inside_slices)] = mask
|
||||
|
||||
# Create a list of strides across the array to get the neighbors within
|
||||
# a flattened array
|
||||
value_stride = np.array(images.strides[1:]) // images.dtype.itemsize
|
||||
image_stride = images.strides[0] // images.dtype.itemsize
|
||||
selem_mgrid = np.mgrid[[slice(-o, d - o)
|
||||
for d, o in zip(selem.shape, offset)]]
|
||||
selem_offsets = selem_mgrid[:, selem].transpose()
|
||||
nb_strides = np.array([np.sum(value_stride * selem_offset)
|
||||
for selem_offset in selem_offsets], np.int32)
|
||||
|
||||
images = images.flatten()
|
||||
|
||||
# Erosion goes smallest to largest; dilation goes largest to smallest.
|
||||
index_sorted = np.argsort(images).astype(np.int32)
|
||||
if method == 'dilation':
|
||||
index_sorted = index_sorted[::-1]
|
||||
|
||||
# Make a linked list of pixels sorted by value. -1 is the list terminator.
|
||||
prev = np.full(len(images), -1, np.int32)
|
||||
next = np.full(len(images), -1, np.int32)
|
||||
prev[index_sorted[1:]] = index_sorted[:-1]
|
||||
next[index_sorted[:-1]] = index_sorted[1:]
|
||||
|
||||
# Cython inner-loop compares the rank of pixel values.
|
||||
if method == 'dilation':
|
||||
value_rank, value_map = rank_order(images)
|
||||
elif method == 'erosion':
|
||||
value_rank, value_map = rank_order(-images)
|
||||
value_map = -value_map
|
||||
|
||||
start = index_sorted[0]
|
||||
reconstruction_loop(value_rank, prev, next, nb_strides, start,
|
||||
image_stride)
|
||||
|
||||
# Reshape reconstructed image to original image shape and remove padding.
|
||||
rec_img = value_map[value_rank[:image_stride]]
|
||||
rec_img.shape = np.array(seed.shape) + (np.array(selem.shape) - 1)
|
||||
return rec_img[inside_slices]
|
Loading…
Add table
Add a link
Reference in a new issue