diff --git a/skimage/morphology/binary.py b/skimage/morphology/binary.py index 11a14d49..89ae8057 100644 --- a/skimage/morphology/binary.py +++ b/skimage/morphology/binary.py @@ -3,67 +3,46 @@ import numpy as np from scipy import ndimage -def _convolve(image, selem, out, cval): - - # determine the smallest integer dtype which does not overflow - selem = selem != 0 - selem_sum = np.sum(selem) - if selem_sum < 2 ** 8: - out_dtype = np.uint8 - else: - out_dtype = np.intp - - if out is None: - out = np.zeros_like(image, dtype=out_dtype) - else: - warnings.warn('Parameter `out` is deprecated and it does not equal ' - 'the output image if the sum of the structuring element ' - 'overflows the dtype of `out`.') - iinfo = np.iinfo(out.dtype) - if iinfo.max - iinfo.min < selem_sum: - raise ValueError('Sum of structuring (=%d) element results in ' - 'overflow for dtype of `out`. You must raise the ' - 'bit-depth.') - - conv = ndimage.convolve(image > 0, selem, output=out, - mode='constant', cval=cval) - - if conv is not None: - out = conv - - return out, selem_sum - - def binary_erosion(image, selem, out=None): """Return fast binary morphological erosion of an image. This function returns the same result as greyscale erosion but performs faster for binary images. - Morphological erosion sets a pixel at (i,j) to the minimum over all pixels - in the neighborhood centered at (i,j). Erosion shrinks bright regions and - enlarges dark regions. + Morphological erosion sets a pixel at ``(i,j)`` to the minimum over all + pixels in the neighborhood centered at ``(i,j)``. Erosion shrinks bright + regions and enlarges dark regions. Parameters ---------- image : ndarray - Image array. + Binary input image. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray + out : ndarray of bool The array to store the result of the morphology. If None is passed, a new array will be allocated. Returns ------- - eroded : bool array - The result of the morphological erosion. + eroded : ndarray of bool or intp + The result of the morphological erosion with values in ``[0, 1]``. """ + selem = (selem != 0) + selem_sum = np.sum(selem) - out, selem_sum = _convolve(image, selem, out, 1) - return np.array(np.equal(out, selem_sum, out=out), dtype=np.bool, - copy=False) + if selem_sum <= 255: + conv = np.empty_like(image, dtype=np.uint8) + else: + conv = np.empty_like(image, dtype=np.intp) + + binary = (image > 0).view(np.uint8) + ndimage.convolve(binary, selem, mode='constant', cval=1, output=conv) + + if out is None: + out = conv + return np.equal(conv, selem_sum, out=out) def binary_dilation(image, selem, out=None): @@ -72,30 +51,40 @@ def binary_dilation(image, selem, out=None): This function returns the same result as greyscale dilation but performs faster for binary images. - Morphological dilation sets a pixel at (i,j) to the maximum over all pixels - in the neighborhood centered at (i,j). Dilation enlarges bright regions - and shrinks dark regions. + Morphological dilation sets a pixel at ``(i,j)`` to the maximum over all + pixels in the neighborhood centered at ``(i,j)``. Dilation enlarges bright + regions and shrinks dark regions. Parameters ---------- image : ndarray - Image array. + Binary input image. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray + out : ndarray of bool The array to store the result of the morphology. If None, is passed, a new array will be allocated. Returns ------- - dilated : bool array - The result of the morphological dilation. + dilated : ndarray of bool or intp + The result of the morphological dilation with values in ``[0, 1]``. """ + selem = (selem != 0) - out, _ = _convolve(image, selem, out, 0) - return np.array(np.not_equal(out, 0, out=out), dtype=np.bool, copy=False) + if np.sum(selem) <= 255: + conv = np.empty_like(image, dtype=np.uint8) + else: + conv = np.empty_like(image, dtype=np.intp) + + binary = (image > 0).view(np.uint8) + ndimage.convolve(binary, selem, mode='constant', cval=0, output=conv) + + if out is None: + out = conv + return np.not_equal(conv, 0, out=out) def binary_opening(image, selem, out=None): @@ -112,20 +101,19 @@ def binary_opening(image, selem, out=None): Parameters ---------- image : ndarray - Image array. + Binary input image. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray + out : ndarray of bool The array to store the result of the morphology. If None is passed, a new array will be allocated. Returns ------- - opening : bool array + opening : ndarray of bool The result of the morphological opening. """ - eroded = binary_erosion(image, selem) out = binary_dilation(eroded, selem, out=out) return out @@ -145,16 +133,16 @@ def binary_closing(image, selem, out=None): Parameters ---------- image : ndarray - Image array. + Binary input image. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray + out : ndarray of bool The array to store the result of the morphology. If None, is passed, a new array will be allocated. Returns ------- - closing : bool array + closing : ndarray of bool The result of the morphological closing. """ diff --git a/skimage/morphology/tests/test_binary.py b/skimage/morphology/tests/test_binary.py index 5f831669..deab3d82 100644 --- a/skimage/morphology/tests/test_binary.py +++ b/skimage/morphology/tests/test_binary.py @@ -17,27 +17,6 @@ def test_non_square_image(): testing.assert_array_equal(binary_res, grey_res) -def test_selem_overflow(): - strel = np.ones((17, 17), dtype=np.uint8) - img = np.zeros((20, 20)) - img[2:19, 2:19] = 1 - binary_res = binary.binary_erosion(img, strel) - grey_res = img_as_bool(grey.erosion(img, strel)) - testing.assert_array_equal(binary_res, grey_res) - - -def test_selem_overflow_exception(): - strel = np.ones((17, 17), dtype=np.uint8) - img = np.zeros((20, 20)) - img[2:19, 2:19] = 1 - out = np.zeros((20, 20), dtype=np.uint8) - testing.assert_raises(ValueError, binary.binary_erosion, img, strel, out) - out = np.zeros((20, 20), dtype=np.uint16) - binary_res = binary.binary_erosion(img, strel, out=out) - grey_res = img_as_bool(grey.erosion(img, strel)) - testing.assert_array_equal(binary_res, grey_res) - - def test_binary_erosion(): strel = selem.square(3) binary_res = binary.binary_erosion(bw_lena, strel) @@ -66,5 +45,24 @@ def test_binary_opening(): testing.assert_array_equal(binary_res, grey_res) +def test_selem_overflow(): + strel = np.ones((17, 17), dtype=np.uint8) + img = np.zeros((20, 20)) + img[2:19, 2:19] = 1 + binary_res = binary.binary_erosion(img, strel) + grey_res = img_as_bool(grey.erosion(img, strel)) + testing.assert_array_equal(binary_res, grey_res) + + +def test_out_argument(): + for func in (binary.binary_erosion, binary.binary_dilation): + strel = np.ones((3, 3), dtype=np.uint8) + img = np.ones((10, 10)) + out = np.zeros_like(img) + out_saved = out.copy() + func(img, strel, out=out) + testing.assert_(np.any(out != out_saved)) + testing.assert_array_equal(out, func(img, strel)) + if __name__ == '__main__': testing.run_module_suite()