mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-23 13:10:18 +08:00
Wrap scipy.ndimage grey morphology functions
This commit is contained in:
+84
-57
@@ -1,17 +1,53 @@
|
||||
"""
|
||||
Grayscale morphological operations
|
||||
"""
|
||||
from skimage import img_as_ubyte
|
||||
from .misc import default_fallback
|
||||
|
||||
from . import cmorph
|
||||
|
||||
import numpy as np
|
||||
from scipy import ndimage as nd
|
||||
from .misc import default_selem
|
||||
|
||||
__all__ = ['erosion', 'dilation', 'opening', 'closing', 'white_tophat',
|
||||
'black_tophat']
|
||||
|
||||
|
||||
@default_fallback
|
||||
def _shift_selem(selem, shift_x, shift_y):
|
||||
"""Shift the binary image `selem` in the left and/or up.
|
||||
|
||||
This only affects structuring elements with even number of rows or
|
||||
columns.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
selem : 2D array, shape (M, N)
|
||||
The input structuring element.
|
||||
shift_x, shift_y : bool
|
||||
Whether to move `selem` along each axis.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : 2D array, shape (M + int(shift_x), N + int(shift_y))
|
||||
The shifted structuring element.
|
||||
"""
|
||||
if selem.ndim > 2:
|
||||
# do nothing for 3D or higher structuring elements
|
||||
return selem
|
||||
m, n = selem.shape
|
||||
if m % 2 == 0:
|
||||
extra_row = np.zeros((1, n), selem.dtype)
|
||||
if shift_x:
|
||||
selem = np.vstack((selem, extra_row))
|
||||
else:
|
||||
selem = np.vstack((extra_row, selem))
|
||||
m += 1
|
||||
if n % 2 == 0:
|
||||
extra_col = np.zeros((m, 1), selem.dtype)
|
||||
if shift_y:
|
||||
selem = np.hstack((selem, extra_col))
|
||||
else:
|
||||
selem = np.hstack((extra_col, selem))
|
||||
return selem
|
||||
|
||||
|
||||
@default_selem
|
||||
def erosion(image, selem=None, out=None, shift_x=False, shift_y=False):
|
||||
"""Return greyscale morphological erosion of an image.
|
||||
|
||||
@@ -24,7 +60,7 @@ def erosion(image, selem=None, out=None, shift_x=False, shift_y=False):
|
||||
image : ndarray
|
||||
Image array.
|
||||
selem : ndarray, optional
|
||||
The neighborhood expressed as a 2-D array of 1's and 0's.
|
||||
The neighborhood expressed as an array of 1's and 0's.
|
||||
If None, use cross-shaped structuring element (connectivity=1).
|
||||
out : ndarrays, optional
|
||||
The array to store the result of the morphology. If None is
|
||||
@@ -35,7 +71,7 @@ def erosion(image, selem=None, out=None, shift_x=False, shift_y=False):
|
||||
|
||||
Returns
|
||||
-------
|
||||
eroded : uint8 array
|
||||
eroded : array, same shape as `image`
|
||||
The result of the morphological erosion.
|
||||
|
||||
Notes
|
||||
@@ -62,16 +98,15 @@ def erosion(image, selem=None, out=None, shift_x=False, shift_y=False):
|
||||
[0, 0, 0, 0, 0]], dtype=uint8)
|
||||
|
||||
"""
|
||||
|
||||
if image is out:
|
||||
raise NotImplementedError("In-place erosion not supported!")
|
||||
image = img_as_ubyte(image)
|
||||
selem = img_as_ubyte(selem)
|
||||
return cmorph._erode(image, selem, out=out,
|
||||
shift_x=shift_x, shift_y=shift_y)
|
||||
selem = np.array(selem)
|
||||
selem = _shift_selem(selem, shift_x, shift_y)
|
||||
if out is None:
|
||||
out = np.empty_like(image)
|
||||
nd.grey_erosion(image, footprint=selem, output=out)
|
||||
return out
|
||||
|
||||
|
||||
@default_fallback
|
||||
@default_selem
|
||||
def dilation(image, selem=None, out=None, shift_x=False, shift_y=False):
|
||||
"""Return greyscale morphological dilation of an image.
|
||||
|
||||
@@ -123,17 +158,15 @@ def dilation(image, selem=None, out=None, shift_x=False, shift_y=False):
|
||||
[0, 0, 0, 0, 0]], dtype=uint8)
|
||||
|
||||
"""
|
||||
|
||||
if image is out:
|
||||
raise NotImplementedError("In-place dilation not supported!")
|
||||
|
||||
image = img_as_ubyte(image)
|
||||
selem = img_as_ubyte(selem)
|
||||
return cmorph._dilate(image, selem, out=out,
|
||||
shift_x=shift_x, shift_y=shift_y)
|
||||
selem = np.array(selem)
|
||||
selem = _shift_selem(selem, shift_x, shift_y)
|
||||
if out is None:
|
||||
out = np.empty_like(image)
|
||||
nd.grey_dilation(image, footprint=selem, output=out)
|
||||
return out
|
||||
|
||||
|
||||
@default_fallback
|
||||
@default_selem
|
||||
def opening(image, selem=None, out=None):
|
||||
"""Return greyscale morphological opening of an image.
|
||||
|
||||
@@ -147,7 +180,7 @@ def opening(image, selem=None, out=None):
|
||||
image : ndarray
|
||||
Image array.
|
||||
selem : ndarray, optional
|
||||
The neighborhood expressed as a 2-D array of 1's and 0's.
|
||||
The neighborhood expressed as an array of 1's and 0's.
|
||||
If None, use cross-shaped structuring element (connectivity=1).
|
||||
out : ndarray, optional
|
||||
The array to store the result of the morphology. If None
|
||||
@@ -155,7 +188,7 @@ def opening(image, selem=None, out=None):
|
||||
|
||||
Returns
|
||||
-------
|
||||
opening : uint8 array
|
||||
opening : array
|
||||
The result of the morphological opening.
|
||||
|
||||
Examples
|
||||
@@ -176,17 +209,13 @@ def opening(image, selem=None, out=None):
|
||||
[0, 0, 0, 0, 0]], dtype=uint8)
|
||||
|
||||
"""
|
||||
|
||||
h, w = selem.shape
|
||||
shift_x = True if (w % 2) == 0 else False
|
||||
shift_y = True if (h % 2) == 0 else False
|
||||
|
||||
eroded = erosion(image, selem)
|
||||
out = dilation(eroded, selem, out=out, shift_x=shift_x, shift_y=shift_y)
|
||||
# note: shift_x, shift_y do nothing if selem side length is odd
|
||||
out = dilation(eroded, selem, out=out, shift_x=True, shift_y=True)
|
||||
return out
|
||||
|
||||
|
||||
@default_fallback
|
||||
@default_selem
|
||||
def closing(image, selem=None, out=None):
|
||||
"""Return greyscale morphological closing of an image.
|
||||
|
||||
@@ -200,7 +229,7 @@ def closing(image, selem=None, out=None):
|
||||
image : ndarray
|
||||
Image array.
|
||||
selem : ndarray, optional
|
||||
The neighborhood expressed as a 2-D array of 1's and 0's.
|
||||
The neighborhood expressed as an array of 1's and 0's.
|
||||
If None, use cross-shaped structuring element (connectivity=1).
|
||||
out : ndarray, optional
|
||||
The array to store the result of the morphology. If None,
|
||||
@@ -208,7 +237,7 @@ def closing(image, selem=None, out=None):
|
||||
|
||||
Returns
|
||||
-------
|
||||
closing : uint8 array
|
||||
closing : array
|
||||
The result of the morphological closing.
|
||||
|
||||
Examples
|
||||
@@ -229,17 +258,13 @@ def closing(image, selem=None, out=None):
|
||||
[0, 0, 0, 0, 0]], dtype=uint8)
|
||||
|
||||
"""
|
||||
|
||||
h, w = selem.shape
|
||||
shift_x = True if (w % 2) == 0 else False
|
||||
shift_y = True if (h % 2) == 0 else False
|
||||
|
||||
dilated = dilation(image, selem)
|
||||
out = erosion(dilated, selem, out=out, shift_x=shift_x, shift_y=shift_y)
|
||||
# note: shift_x, shift_y do nothing if selem side length is odd
|
||||
out = erosion(dilated, selem, out=out, shift_x=True, shift_y=True)
|
||||
return out
|
||||
|
||||
|
||||
@default_fallback
|
||||
@default_selem
|
||||
def white_tophat(image, selem=None, out=None):
|
||||
"""Return white top hat of an image.
|
||||
|
||||
@@ -252,7 +277,7 @@ def white_tophat(image, selem=None, out=None):
|
||||
image : ndarray
|
||||
Image array.
|
||||
selem : ndarray, optional
|
||||
The neighborhood expressed as a 2-D array of 1's and 0's.
|
||||
The neighborhood expressed as an array of 1's and 0's.
|
||||
If None, use cross-shaped structuring element (connectivity=1).
|
||||
out : ndarray, optional
|
||||
The array to store the result of the morphology. If None
|
||||
@@ -260,7 +285,7 @@ def white_tophat(image, selem=None, out=None):
|
||||
|
||||
Returns
|
||||
-------
|
||||
opening : uint8 array
|
||||
out : array
|
||||
The result of the morphological white top hat.
|
||||
|
||||
Examples
|
||||
@@ -281,16 +306,18 @@ def white_tophat(image, selem=None, out=None):
|
||||
[0, 0, 0, 0, 0]], dtype=uint8)
|
||||
|
||||
"""
|
||||
|
||||
if image is out:
|
||||
raise NotImplementedError("Cannot perform white top hat in place.")
|
||||
|
||||
out = opening(image, selem, out=out)
|
||||
out = image - out
|
||||
selem = np.array(selem)
|
||||
if out is image:
|
||||
opened = opening(image, selem)
|
||||
out -= opened
|
||||
return out
|
||||
elif out is None:
|
||||
out = np.empty_like(image)
|
||||
out = nd.white_tophat(image, footprint=selem, output=out)
|
||||
return out
|
||||
|
||||
|
||||
@default_fallback
|
||||
@default_selem
|
||||
def black_tophat(image, selem=None, out=None):
|
||||
"""Return black top hat of an image.
|
||||
|
||||
@@ -333,10 +360,10 @@ def black_tophat(image, selem=None, out=None):
|
||||
[0, 0, 0, 0, 0]], dtype=uint8)
|
||||
|
||||
"""
|
||||
|
||||
if image is out:
|
||||
raise NotImplementedError("Cannot perform white top hat in place.")
|
||||
|
||||
if out is image:
|
||||
original = image.copy()
|
||||
else:
|
||||
original = image
|
||||
out = closing(image, selem, out=out)
|
||||
out = out - image
|
||||
out -= original
|
||||
return out
|
||||
|
||||
@@ -15,11 +15,8 @@ funcs = ('binary_erosion', 'binary_dilation', 'binary_opening',
|
||||
skimage2ndimage.update(dict((x, x) for x in funcs))
|
||||
|
||||
|
||||
def default_fallback(func):
|
||||
"""Decorator to fall back on ndimage for images with more than 2 dimensions
|
||||
|
||||
Decorator also provides a default structuring element, `selem`, with the
|
||||
appropriate dimensionality if none is specified.
|
||||
def default_selem(func):
|
||||
"""Decorator to add a default structuring element to morphology functions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -30,25 +27,14 @@ def default_fallback(func):
|
||||
Returns
|
||||
-------
|
||||
func_out : function
|
||||
If the image dimensionality is greater than 2D, the ndimage
|
||||
function is returned, otherwise skimage function is used.
|
||||
The function, using a default structuring element of same dimension
|
||||
as the input image with connectivity 1.
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def func_out(image, selem=None, out=None, **kwargs):
|
||||
# Default structure element
|
||||
def func_out(image, selem=None, *args, **kwargs):
|
||||
if selem is None:
|
||||
selem = _default_selem(image.ndim)
|
||||
|
||||
# If image has more than 2 dimensions, use scipy.ndimage
|
||||
if image.ndim > 2:
|
||||
function = getattr(nd, skimage2ndimage[func.__name__])
|
||||
try:
|
||||
return function(image, footprint=selem, output=out, **kwargs)
|
||||
except TypeError:
|
||||
# nd.binary_* take structure instead of footprint
|
||||
return function(image, structure=selem, output=out, **kwargs)
|
||||
else:
|
||||
return func(image, selem=selem, out=out, **kwargs)
|
||||
return func(image, selem=selem, *args, **kwargs)
|
||||
|
||||
return func_out
|
||||
|
||||
|
||||
Reference in New Issue
Block a user