From 9adbca13e774849751405c4fc804af08b2470eed Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Fri, 14 Oct 2011 11:00:10 -0400 Subject: [PATCH 01/13] Add parameter to flip structuring element about its origin This parameter only affects structuring elements (selem) with even-numbered sides, since an odd-numbered selem is centered about its origin. Note the selem isn't actually flipped, but shifted (i.e. I assume a selem that is symmetric in *shape*) --- scikits/image/morphology/cmorph.pyx | 15 +++++++++++++-- scikits/image/morphology/grey.py | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/scikits/image/morphology/cmorph.pyx b/scikits/image/morphology/cmorph.pyx index 5572102c..8ca24752 100644 --- a/scikits/image/morphology/cmorph.pyx +++ b/scikits/image/morphology/cmorph.pyx @@ -8,6 +8,7 @@ import numpy as np cimport numpy as np cimport cython +from cpython cimport bool STREL_DTYPE = np.uint8 ctypedef np.uint8_t STREL_DTYPE_t @@ -21,9 +22,14 @@ cdef inline int int_min(int a, int b): return a if a <= b else b @cython.boundscheck(False) def dilate(np.ndarray[IMAGE_DTYPE_t, ndim=2] image not None, np.ndarray[IMAGE_DTYPE_t, ndim=2] selem not None, - np.ndarray[IMAGE_DTYPE_t, ndim=2] out): + np.ndarray[IMAGE_DTYPE_t, ndim=2] out, + bool flip): cdef int hw = selem.shape[0] // 2 cdef int hh = selem.shape[1] // 2 + if flip: + hw -= 1 + hh -= 1 + cdef int width = image.shape[0], height = image.shape[1] if out is None: out = np.zeros([width, height], dtype=IMAGE_DTYPE) @@ -64,9 +70,14 @@ def dilate(np.ndarray[IMAGE_DTYPE_t, ndim=2] image not None, @cython.boundscheck(False) def erode(np.ndarray[IMAGE_DTYPE_t, ndim=2] image not None, np.ndarray[IMAGE_DTYPE_t, ndim=2] selem not None, - np.ndarray[IMAGE_DTYPE_t, ndim=2] out): + np.ndarray[IMAGE_DTYPE_t, ndim=2] out, + bool flip): cdef int hw = selem.shape[0] // 2 cdef int hh = selem.shape[1] // 2 + if flip: + hw -= 1 + hh -= 1 + cdef int width = image.shape[0], height = image.shape[1] if out is None: out = np.zeros([width, height], dtype=IMAGE_DTYPE) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index 2eb94024..5592850f 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -9,7 +9,7 @@ import numpy as np eps = np.finfo(float).eps -def greyscale_erode(image, selem, out=None): +def greyscale_erode(image, selem, out=None, flip=False): """ Performs a greyscale morphological erosion on an image given a flat structuring element. The eroded pixel at (i,j) is the minimum over all @@ -27,6 +27,10 @@ def greyscale_erode(image, selem, out=None): The array to store the result of the morphology. If None is passed, a new array will be allocated. + flip : bool + Flip structuring element about center point. This only affects + eccentric structuring elements (i.e. selem with even numbered sides). + Returns ------- eroded : ndarray @@ -36,12 +40,12 @@ def greyscale_erode(image, selem, out=None): raise NotImplementedError("In-place erosion not supported!") try: import scikits.image.morphology.cmorph as cmorph - out = cmorph.erode(image, selem, out=out) + out = cmorph.erode(image, selem, out=out, flip=flip) return out; except ImportError: raise ImportError("cmorph extension not available.") -def greyscale_dilate(image, selem, out=None): +def greyscale_dilate(image, selem, out=None, flip=False): """ Performs a greyscale morphological dilation on an image given a flat structuring element. The dilated pixel at (i,j) is the maximum over all @@ -60,6 +64,10 @@ def greyscale_dilate(image, selem, out=None): The array to store the result of the morphology. If None, is passed, a new array will be allocated. + flip : bool + Flip structuring element about center point. This only affects + eccentric structuring elements (i.e. selem with even numbered sides). + Returns ------- dilated : ndarray @@ -69,7 +77,7 @@ def greyscale_dilate(image, selem, out=None): raise NotImplementedError("In-place dilation not supported!") try: from . import cmorph - out = cmorph.dilate(image, selem, out=out) + out = cmorph.dilate(image, selem, out=out, flip=flip) return out; except ImportError: raise ImportError("cmorph extension not available.") @@ -79,6 +87,7 @@ def greyscale_open(image, selem, out=None): Performs a greyscale morphological opening on an image given a flat structuring element defined as a erosion followed by a dilation. + Parameters ---------- image : ndarray From b6e470dbd15f388819626d5943524d427e9f7ce4 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 02:57:32 -0400 Subject: [PATCH 02/13] Remove unnecessary if-block. This looks like it was a copy-paste error. --- scikits/image/morphology/grey.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index 5592850f..ac41c4f1 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -189,8 +189,5 @@ def greyscale_black_top_hat(image, selem, out=None): raise NotImplementedError("Cannot perform white top hat in place.") dilated = greyscale_dilate(image, selem) out = greyscale_erode(dilated, selem, out=out) - out = out - image - if image is out: - raise NotImplementedError("Cannot perform black top hat in place.") return out From 4f4f219788843bea2bd6d9ee2f811a1a87abf57c Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 03:01:27 -0400 Subject: [PATCH 03/13] Reuse open/close operations for white/black tophat --- scikits/image/morphology/grey.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index ac41c4f1..0b8c49b8 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -159,8 +159,7 @@ def greyscale_white_top_hat(image, selem, out=None): if image is out: raise NotImplementedError("Cannot perform white top hat in place.") - eroded = greyscale_erode(image, selem) - out = greyscale_dilate(eroded, selem, out=out) + out = greyscale_open(image, selem, out=out) out = image - out return out @@ -187,7 +186,6 @@ def greyscale_black_top_hat(image, selem, out=None): """ if image is out: raise NotImplementedError("Cannot perform white top hat in place.") - dilated = greyscale_dilate(image, selem) - out = greyscale_erode(dilated, selem, out=out) + out = greyscale_close(image, selem, out=out) out = out - image return out From fb92bfcf715c81f7343eef81518f4379bfdc88aa Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 03:32:59 -0400 Subject: [PATCH 04/13] Add tests for correct behavior of square structuring elements --- .../image/morphology/tests/test_morphology.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/scikits/image/morphology/tests/test_morphology.py b/scikits/image/morphology/tests/test_morphology.py index ff22c458..7cea1e07 100644 --- a/scikits/image/morphology/tests/test_morphology.py +++ b/scikits/image/morphology/tests/test_morphology.py @@ -61,3 +61,48 @@ class TestMorphology(): def test_close_disk(self): self.morph_worker(lena, "disk-close-matlab-output.npz", greyscale_close, disk) + + +BLACK_PIXEL = 255 * np.ones((4, 4), dtype=np.uint8) +BLACK_PIXEL[1, 1] = 0 +WHITE_PIXEL = 255 - BLACK_PIXEL +SELEM = square(2) + +def test_dilate_erode_symmetry(): + c = greyscale_erode(BLACK_PIXEL, SELEM) + d = greyscale_dilate(WHITE_PIXEL, SELEM) + assert np.all(c == (255 - d)) + + +def test_open_dark_pixel(): + assert np.all(greyscale_open(BLACK_PIXEL, SELEM) == BLACK_PIXEL) + +def test_close_white_pixel(): + assert np.all(greyscale_close(WHITE_PIXEL, SELEM) == WHITE_PIXEL) + + +def test_open_white_pixel(): + assert np.all(greyscale_open(WHITE_PIXEL, SELEM) == 0) + +def test_close_dark_pixel(): + assert np.all(greyscale_close(BLACK_PIXEL, SELEM) == 255) + + +def test_white_tophat_white_pixel(): + tophat = greyscale_white_top_hat(WHITE_PIXEL, SELEM) + assert np.all(tophat == WHITE_PIXEL) + +def test_black_tophat_black_pixel(): + tophat = greyscale_black_top_hat(BLACK_PIXEL, SELEM) + assert np.all(tophat == (255 - BLACK_PIXEL)) + + +def test_white_tophat_black_pixel(): + tophat = greyscale_white_top_hat(BLACK_PIXEL, SELEM) + assert np.all(tophat == 0) + +def test_black_tophat_white_pixel(): + tophat = greyscale_black_top_hat(WHITE_PIXEL, SELEM) + assert np.all(tophat == 0) + + From ac2714d3c8e690e1de4e89cee1b8bac3191acfc8 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 03:34:30 -0400 Subject: [PATCH 05/13] Fix morphological open/close for square structuring elements with even sides. This also fixes rectangular selems with even sides, but not rectangular selems with one odd and one even side. --- scikits/image/morphology/grey.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index 0b8c49b8..463e55b2 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -105,8 +105,13 @@ def greyscale_open(image, selem, out=None): opening : ndarray The result of the morphological opening. """ + h, w = selem.shape + if (h % 2) == 0 and (w % 2) == 0: + flip = True + else: + flip = False eroded = greyscale_erode(image, selem) - out = greyscale_dilate(eroded, selem, out=out) + out = greyscale_dilate(eroded, selem, out=out, flip=flip) return out def greyscale_close(image, selem, out=None): @@ -131,8 +136,13 @@ def greyscale_close(image, selem, out=None): opening : ndarray The result of the morphological opening. """ + h, w = selem.shape + if (h % 2) == 0 and (w % 2) == 0: + flip = True + else: + flip = False dilated = greyscale_dilate(image, selem) - out = greyscale_erode(dilated, selem, out=out) + out = greyscale_erode(dilated, selem, out=out, flip=flip) return out def greyscale_white_top_hat(image, selem, out=None): From 9ce5ee7d71fbfcc7e0cec9d91fbd91e6edcfb999 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 03:59:41 -0400 Subject: [PATCH 06/13] Move test functions to test class --- .../image/morphology/tests/test_morphology.py | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/scikits/image/morphology/tests/test_morphology.py b/scikits/image/morphology/tests/test_morphology.py index 7cea1e07..93606848 100644 --- a/scikits/image/morphology/tests/test_morphology.py +++ b/scikits/image/morphology/tests/test_morphology.py @@ -63,46 +63,47 @@ class TestMorphology(): greyscale_close, disk) -BLACK_PIXEL = 255 * np.ones((4, 4), dtype=np.uint8) -BLACK_PIXEL[1, 1] = 0 -WHITE_PIXEL = 255 - BLACK_PIXEL -SELEM = square(2) +class TestEccentricStructuringElements(): -def test_dilate_erode_symmetry(): - c = greyscale_erode(BLACK_PIXEL, SELEM) - d = greyscale_dilate(WHITE_PIXEL, SELEM) - assert np.all(c == (255 - d)) - - -def test_open_dark_pixel(): - assert np.all(greyscale_open(BLACK_PIXEL, SELEM) == BLACK_PIXEL) - -def test_close_white_pixel(): - assert np.all(greyscale_close(WHITE_PIXEL, SELEM) == WHITE_PIXEL) - - -def test_open_white_pixel(): - assert np.all(greyscale_open(WHITE_PIXEL, SELEM) == 0) - -def test_close_dark_pixel(): - assert np.all(greyscale_close(BLACK_PIXEL, SELEM) == 255) - - -def test_white_tophat_white_pixel(): - tophat = greyscale_white_top_hat(WHITE_PIXEL, SELEM) - assert np.all(tophat == WHITE_PIXEL) - -def test_black_tophat_black_pixel(): - tophat = greyscale_black_top_hat(BLACK_PIXEL, SELEM) - assert np.all(tophat == (255 - BLACK_PIXEL)) - - -def test_white_tophat_black_pixel(): - tophat = greyscale_white_top_hat(BLACK_PIXEL, SELEM) - assert np.all(tophat == 0) - -def test_black_tophat_white_pixel(): - tophat = greyscale_black_top_hat(WHITE_PIXEL, SELEM) - assert np.all(tophat == 0) + def setUp(self): + self.black_pixel = 255 * np.ones((4, 4), dtype=np.uint8) + self.black_pixel[1, 1] = 0 + self.white_pixel = 255 - self.black_pixel + self.selem = square(2) + + def test_dilate_erode_symmetry(self): + c = greyscale_erode(self.black_pixel, self.selem) + d = greyscale_dilate(self.white_pixel, self.selem) + assert np.all(c == (255 - d)) + + def test_open_dark_pixel(self): + grey_open = greyscale_open(self.black_pixel, self.selem) + assert np.all(grey_open == self.black_pixel) + + def test_close_white_pixel(self): + grey_close = greyscale_close(self.white_pixel, self.selem) + assert np.all(grey_close == self.white_pixel) + + def test_open_white_pixel(self): + assert np.all(greyscale_open(self.white_pixel, self.selem) == 0) + + def test_close_dark_pixel(self): + assert np.all(greyscale_close(self.black_pixel, self.selem) == 255) + + def test_white_tophat_white_pixel(self): + tophat = greyscale_white_top_hat(self.white_pixel, self.selem) + assert np.all(tophat == self.white_pixel) + + def test_black_tophat_black_pixel(self): + tophat = greyscale_black_top_hat(self.black_pixel, self.selem) + assert np.all(tophat == (255 - self.black_pixel)) + + def test_white_tophat_black_pixel(self): + tophat = greyscale_white_top_hat(self.black_pixel, self.selem) + assert np.all(tophat == 0) + + def test_black_tophat_white_pixel(self): + tophat = greyscale_black_top_hat(self.white_pixel, self.selem) + assert np.all(tophat == 0) From 6366da861a3fb5f16bdb15244cb850163cec8055 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 04:19:04 -0400 Subject: [PATCH 07/13] Add tests for rectangular structuring elements These tests fail. --- .../image/morphology/tests/test_morphology.py | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/scikits/image/morphology/tests/test_morphology.py b/scikits/image/morphology/tests/test_morphology.py index 93606848..330518c2 100644 --- a/scikits/image/morphology/tests/test_morphology.py +++ b/scikits/image/morphology/tests/test_morphology.py @@ -69,41 +69,50 @@ class TestEccentricStructuringElements(): self.black_pixel = 255 * np.ones((4, 4), dtype=np.uint8) self.black_pixel[1, 1] = 0 self.white_pixel = 255 - self.black_pixel - self.selem = square(2) + self.selems = [square(2), rectangle(2, 2), + rectangle(2, 1), rectangle(1, 2)] def test_dilate_erode_symmetry(self): - c = greyscale_erode(self.black_pixel, self.selem) - d = greyscale_dilate(self.white_pixel, self.selem) - assert np.all(c == (255 - d)) + for s in self.selems: + c = greyscale_erode(self.black_pixel, s) + d = greyscale_dilate(self.white_pixel, s) + assert np.all(c == (255 - d)) - def test_open_dark_pixel(self): - grey_open = greyscale_open(self.black_pixel, self.selem) - assert np.all(grey_open == self.black_pixel) + def test_open_black_pixel(self): + for s in self.selems: + grey_open = greyscale_open(self.black_pixel, s) + assert np.all(grey_open == self.black_pixel) def test_close_white_pixel(self): - grey_close = greyscale_close(self.white_pixel, self.selem) - assert np.all(grey_close == self.white_pixel) + for s in self.selems: + grey_close = greyscale_close(self.white_pixel, s) + assert np.all(grey_close == self.white_pixel) def test_open_white_pixel(self): - assert np.all(greyscale_open(self.white_pixel, self.selem) == 0) + for s in self.selems: + assert np.all(greyscale_open(self.white_pixel, s) == 0) - def test_close_dark_pixel(self): - assert np.all(greyscale_close(self.black_pixel, self.selem) == 255) + def test_close_black_pixel(self): + for s in self.selems: + assert np.all(greyscale_close(self.black_pixel, s) == 255) def test_white_tophat_white_pixel(self): - tophat = greyscale_white_top_hat(self.white_pixel, self.selem) - assert np.all(tophat == self.white_pixel) + for s in self.selems: + tophat = greyscale_white_top_hat(self.white_pixel, s) + assert np.all(tophat == self.white_pixel) def test_black_tophat_black_pixel(self): - tophat = greyscale_black_top_hat(self.black_pixel, self.selem) - assert np.all(tophat == (255 - self.black_pixel)) + for s in self.selems: + tophat = greyscale_black_top_hat(self.black_pixel, s) + assert np.all(tophat == (255 - self.black_pixel)) def test_white_tophat_black_pixel(self): - tophat = greyscale_white_top_hat(self.black_pixel, self.selem) - assert np.all(tophat == 0) + for s in self.selems: + tophat = greyscale_white_top_hat(self.black_pixel, s) + assert np.all(tophat == 0) def test_black_tophat_white_pixel(self): - tophat = greyscale_black_top_hat(self.white_pixel, self.selem) - assert np.all(tophat == 0) - + for s in self.selems: + tophat = greyscale_black_top_hat(self.white_pixel, s) + assert np.all(tophat == 0) From b0376721ebfede6904a351b69ae6a7c745a8ba67 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 04:23:37 -0400 Subject: [PATCH 08/13] Replace flip parameter in erode/dilate with shift_x and shift_y This change allows morphological open/close and white/black tophat to correctly use rectangular structuring element that has one even-numbered dimension. Note: dilate and erode functions flip the definitions of x/y and width/height, but it does so consistently so it doesn't really matter. --- scikits/image/morphology/cmorph.pyx | 14 ++++++----- scikits/image/morphology/grey.py | 38 +++++++++++++++-------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/scikits/image/morphology/cmorph.pyx b/scikits/image/morphology/cmorph.pyx index 8ca24752..6870b500 100644 --- a/scikits/image/morphology/cmorph.pyx +++ b/scikits/image/morphology/cmorph.pyx @@ -23,12 +23,13 @@ cdef inline int int_min(int a, int b): return a if a <= b else b def dilate(np.ndarray[IMAGE_DTYPE_t, ndim=2] image not None, np.ndarray[IMAGE_DTYPE_t, ndim=2] selem not None, np.ndarray[IMAGE_DTYPE_t, ndim=2] out, - bool flip): + bool shift_x, bool shift_y): cdef int hw = selem.shape[0] // 2 cdef int hh = selem.shape[1] // 2 - if flip: - hw -= 1 + if shift_x: hh -= 1 + if shift_y: + hw -= 1 cdef int width = image.shape[0], height = image.shape[1] if out is None: @@ -71,12 +72,13 @@ def dilate(np.ndarray[IMAGE_DTYPE_t, ndim=2] image not None, def erode(np.ndarray[IMAGE_DTYPE_t, ndim=2] image not None, np.ndarray[IMAGE_DTYPE_t, ndim=2] selem not None, np.ndarray[IMAGE_DTYPE_t, ndim=2] out, - bool flip): + bool shift_x, bool shift_y): cdef int hw = selem.shape[0] // 2 cdef int hh = selem.shape[1] // 2 - if flip: - hw -= 1 + if shift_x: hh -= 1 + if shift_y: + hw -= 1 cdef int width = image.shape[0], height = image.shape[1] if out is None: diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index 463e55b2..dec12100 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -9,7 +9,7 @@ import numpy as np eps = np.finfo(float).eps -def greyscale_erode(image, selem, out=None, flip=False): +def greyscale_erode(image, selem, out=None, shift_x=False, shift_y=False): """ Performs a greyscale morphological erosion on an image given a flat structuring element. The eroded pixel at (i,j) is the minimum over all @@ -27,8 +27,8 @@ def greyscale_erode(image, selem, out=None, flip=False): The array to store the result of the morphology. If None is passed, a new array will be allocated. - flip : bool - Flip structuring element about center point. This only affects + shift_x, shift_y : bool + shift structuring element about center point. This only affects eccentric structuring elements (i.e. selem with even numbered sides). Returns @@ -40,12 +40,13 @@ def greyscale_erode(image, selem, out=None, flip=False): raise NotImplementedError("In-place erosion not supported!") try: import scikits.image.morphology.cmorph as cmorph - out = cmorph.erode(image, selem, out=out, flip=flip) + out = cmorph.erode(image, selem, out=out, + shift_x=shift_x, shift_y=shift_y) return out; except ImportError: raise ImportError("cmorph extension not available.") -def greyscale_dilate(image, selem, out=None, flip=False): +def greyscale_dilate(image, selem, out=None, shift_x=False, shift_y=False): """ Performs a greyscale morphological dilation on an image given a flat structuring element. The dilated pixel at (i,j) is the maximum over all @@ -64,8 +65,8 @@ def greyscale_dilate(image, selem, out=None, flip=False): The array to store the result of the morphology. If None, is passed, a new array will be allocated. - flip : bool - Flip structuring element about center point. This only affects + shift_x, shift_y : bool + shift structuring element about center point. This only affects eccentric structuring elements (i.e. selem with even numbered sides). Returns @@ -77,7 +78,8 @@ def greyscale_dilate(image, selem, out=None, flip=False): raise NotImplementedError("In-place dilation not supported!") try: from . import cmorph - out = cmorph.dilate(image, selem, out=out, flip=flip) + out = cmorph.dilate(image, selem, out=out, + shift_x=shift_x, shift_y=shift_y) return out; except ImportError: raise ImportError("cmorph extension not available.") @@ -106,12 +108,12 @@ def greyscale_open(image, selem, out=None): The result of the morphological opening. """ h, w = selem.shape - if (h % 2) == 0 and (w % 2) == 0: - flip = True - else: - flip = False + shift_x = True if (w % 2) == 0 else False + shift_y = True if (h % 2) == 0 else False + eroded = greyscale_erode(image, selem) - out = greyscale_dilate(eroded, selem, out=out, flip=flip) + out = greyscale_dilate(eroded, selem, out=out, + shift_x=shift_x, shift_y=shift_y) return out def greyscale_close(image, selem, out=None): @@ -137,12 +139,12 @@ def greyscale_close(image, selem, out=None): The result of the morphological opening. """ h, w = selem.shape - if (h % 2) == 0 and (w % 2) == 0: - flip = True - else: - flip = False + shift_x = True if (w % 2) == 0 else False + shift_y = True if (h % 2) == 0 else False + dilated = greyscale_dilate(image, selem) - out = greyscale_erode(dilated, selem, out=out, flip=flip) + out = greyscale_erode(dilated, selem, out=out, + shift_x=shift_x, shift_y=shift_y) return out def greyscale_white_top_hat(image, selem, out=None): From 65b6b6bed7aabd0c392d021dfac0ca811914ea60 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 04:27:35 -0400 Subject: [PATCH 09/13] Minor fix to docstring --- scikits/image/morphology/selem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scikits/image/morphology/selem.py b/scikits/image/morphology/selem.py index cf786d6d..4a5ee0bd 100644 --- a/scikits/image/morphology/selem.py +++ b/scikits/image/morphology/selem.py @@ -69,7 +69,7 @@ def diamond(radius, dtype=np.uint8): Parameters ---------- radius : string - The radius of the disk-shaped structuring element. + The radius of the diamond-shaped structuring element. dtype : data-type The data type of the structuring element. From 873520456531a7c4bedbf0b0b5a06de223bfe785 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 11:57:01 -0400 Subject: [PATCH 10/13] Improve docstrings in grey morphology module --- scikits/image/morphology/grey.py | 50 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index dec12100..ca452eec 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -10,10 +10,11 @@ import numpy as np eps = np.finfo(float).eps def greyscale_erode(image, selem, out=None, shift_x=False, shift_y=False): - """ - Performs a greyscale morphological erosion on an image given a flat - structuring element. The eroded pixel at (i,j) is the minimum over all - pixels in the neighborhood centered at (i,j). + """Return greyscale morphological erosion of an image. + + 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 ---------- @@ -47,10 +48,11 @@ def greyscale_erode(image, selem, out=None, shift_x=False, shift_y=False): raise ImportError("cmorph extension not available.") def greyscale_dilate(image, selem, out=None, shift_x=False, shift_y=False): - """ - Performs a greyscale morphological dilation on an image given a flat - structuring element. The dilated pixel at (i,j) is the maximum over all - pixels in the neighborhood centered at (i,j). + """Return greyscale morphological dilation of an image. + + 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 ---------- @@ -85,10 +87,12 @@ def greyscale_dilate(image, selem, out=None, shift_x=False, shift_y=False): raise ImportError("cmorph extension not available.") def greyscale_open(image, selem, out=None): - """ - Performs a greyscale morphological opening on an image given a flat - structuring element defined as a erosion followed by a dilation. + """Return greyscale morphological opening of an image. + The morphological opening on an image is defined as an erosion followed by + a dilation. Opening can remove small bright spots (i.e. "salt") and connect + small dark cracks. This tends to "open" up (dark) gaps between (bright) + features. Parameters ---------- @@ -117,9 +121,12 @@ def greyscale_open(image, selem, out=None): return out def greyscale_close(image, selem, out=None): - """ - Performs a greyscale morphological closing on an image given a flat - structuring element defined as a dilation followed by an erosion. + """Return greyscale morphological closing of an image. + + The morphological closing on an image is defined as a dilation followed by + an erosion. Closing can remove small dark spots (i.e. "pepper") and connect + small bright cracks. This tends to "close" up (dark) gaps between (bright) + features. Parameters ---------- @@ -148,8 +155,11 @@ def greyscale_close(image, selem, out=None): return out def greyscale_white_top_hat(image, selem, out=None): - """ - Applies a white top hat on an image given a flat structuring element. + """Return white top hat of an image. + + The white top hat of an image is defined as the image minus its + morphological opening. This operation returns the bright spots of the image + that are smaller than the structuring element. Parameters ---------- @@ -176,8 +186,12 @@ def greyscale_white_top_hat(image, selem, out=None): return out def greyscale_black_top_hat(image, selem, out=None): - """ - Applies a black top hat on an image given a flat structuring element. + """Return black top hat of an image. + + The black top hat of an image is defined as its morphological closing minus + the original image. This operation returns the dark spots of the image that + are smaller than the structuring element. Note that dark spots in the + original image are bright spots after the black top hat. Parameters ---------- From c7c0798d1c89600688a6585fcef22b1533bec414 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 12:37:25 -0400 Subject: [PATCH 11/13] Add examples to grey morphology docstrings --- scikits/image/morphology/grey.py | 105 ++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index ca452eec..12b74d14 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -36,6 +36,23 @@ def greyscale_erode(image, selem, out=None, shift_x=False, shift_y=False): ------- eroded : ndarray The result of the morphological erosion. + + Examples + -------- + >>> # Erosion shrinks bright regions + >>> from scikits.image.morphology import square + >>> bright_square = np.array([[0, 0, 0, 0, 0], + ... [0, 1, 1, 1, 0], + ... [0, 1, 1, 1, 0], + ... [0, 1, 1, 1, 0], + ... [0, 0, 0, 0, 0]], dtype=np.uint8) + >>> greyscale_erode(bright_square, square(3)) + array([[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]], dtype='uint8') + """ if image is out: raise NotImplementedError("In-place erosion not supported!") @@ -75,6 +92,23 @@ def greyscale_dilate(image, selem, out=None, shift_x=False, shift_y=False): ------- dilated : ndarray The result of the morphological dilation. + + Examples + -------- + >>> # Dilation enlarges bright regions + >>> from scikits.image.morphology import square + >>> bright_pixel = np.array([[0, 0, 0, 0, 0], + ... [0, 0, 0, 0, 0], + ... [0, 0, 1, 0, 0], + ... [0, 0, 0, 0, 0], + ... [0, 0, 0, 0, 0]], dtype=np.uint8) + >>> greyscale_dilate(bright_pixel, square(3)) + array([[0, 0, 0, 0, 0], + [0, 1, 1, 1, 0], + [0, 1, 1, 1, 0], + [0, 1, 1, 1, 0], + [0, 0, 0, 0, 0]], dtype='uint8') + """ if image is out: raise NotImplementedError("In-place dilation not supported!") @@ -110,6 +144,23 @@ def greyscale_open(image, selem, out=None): ------- opening : ndarray The result of the morphological opening. + + Examples + -------- + >>> # Open up gap between two bright regions (but also shrink regions) + >>> from scikits.image.morphology import square + >>> bad_connection = np.array([[1, 0, 0, 0, 1], + ... [1, 1, 0, 1, 1], + ... [1, 1, 1, 1, 1], + ... [1, 1, 0, 1, 1], + ... [1, 0, 0, 0, 1]], dtype=np.uint8) + >>> greyscale_open(bad_connection, square(3)) + array([[0, 0, 0, 0, 0], + [1, 1, 0, 1, 1], + [1, 1, 0, 1, 1], + [1, 1, 0, 1, 1], + [0, 0, 0, 0, 0]], dtype='uint8') + """ h, w = selem.shape shift_x = True if (w % 2) == 0 else False @@ -144,6 +195,23 @@ def greyscale_close(image, selem, out=None): ------- opening : ndarray The result of the morphological opening. + + Examples + -------- + >>> # Close a gap between two bright lines + >>> from scikits.image.morphology import square + >>> broken_line = np.array([[0, 0, 0, 0, 0], + ... [0, 0, 0, 0, 0], + ... [1, 1, 0, 1, 1], + ... [0, 0, 0, 0, 0], + ... [0, 0, 0, 0, 0]], dtype=np.uint8) + >>> greyscale_close(broken_line, square(3)) + array([[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]], dtype='uint8') + """ h, w = selem.shape shift_x = True if (w % 2) == 0 else False @@ -177,7 +245,24 @@ def greyscale_white_top_hat(image, selem, out=None): ------- opening : ndarray The result of the morphological white top hat. - """ + + Examples + -------- + >>> # Subtract grey background from bright peak + >>> from scikits.image.morphology import square + >>> bright_on_grey = np.array([[2, 3, 3, 3, 2], + ... [3, 4, 5, 4, 3], + ... [3, 5, 9, 5, 3], + ... [3, 4, 5, 4, 3], + ... [2, 3, 3, 3, 2]], dtype=np.uint8) + >>> greyscale_white_top_hat(bright_on_grey, square(3)) + array([[0, 0, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 1, 5, 1, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0]], dtype='uint8') + + """ if image is out: raise NotImplementedError("Cannot perform white top hat in place.") @@ -209,9 +294,27 @@ def greyscale_black_top_hat(image, selem, out=None): ------- opening : ndarray The result of the black top filter. + + Examples + -------- + >>> # Change dark peak to bright peak and subtract background + >>> from scikits.image.morphology import square + >>> dark_on_grey = np.array([[7, 6, 6, 6, 7], + ... [6, 5, 4, 5, 6], + ... [6, 4, 0, 4, 6], + ... [6, 5, 4, 5, 6], + ... [7, 6, 6, 6, 7]], dtype=np.uint8) + >>> greyscale_black_top_hat(dark_on_grey, square(3)) + array([[0, 0, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 1, 5, 1, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0]], dtype='uint8') + """ if image is out: raise NotImplementedError("Cannot perform white top hat in place.") out = greyscale_close(image, selem, out=out) out = out - image return out + From 9e81cecbb6aa340c9d38d5293066fe42b2b98351 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 12:40:06 -0400 Subject: [PATCH 12/13] Specify that input arrays to grey morphology filters must be uint8 --- scikits/image/morphology/grey.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scikits/image/morphology/grey.py b/scikits/image/morphology/grey.py index 12b74d14..1b5552ff 100644 --- a/scikits/image/morphology/grey.py +++ b/scikits/image/morphology/grey.py @@ -19,7 +19,7 @@ def greyscale_erode(image, selem, out=None, shift_x=False, shift_y=False): Parameters ---------- image : ndarray - The image as an ndarray. + The image as a uint8 ndarray. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. @@ -75,7 +75,7 @@ def greyscale_dilate(image, selem, out=None, shift_x=False, shift_y=False): ---------- image : ndarray - The image as an ndarray. + The image as a uint8 ndarray. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. @@ -131,7 +131,7 @@ def greyscale_open(image, selem, out=None): Parameters ---------- image : ndarray - The image as an ndarray. + The image as a uint8 ndarray. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. @@ -182,7 +182,7 @@ def greyscale_close(image, selem, out=None): Parameters ---------- image : ndarray - The image as an ndarray. + The image as a uint8 ndarray. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. @@ -232,7 +232,7 @@ def greyscale_white_top_hat(image, selem, out=None): Parameters ---------- image : ndarray - The image as an ndarray. + The image as a uint8 ndarray. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. @@ -281,7 +281,7 @@ def greyscale_black_top_hat(image, selem, out=None): Parameters ---------- image : ndarray - The image as an ndarray. + The image as a uint8 ndarray. selem : ndarray The neighborhood expressed as a 2-D array of 1's and 0's. From 1cc0f36d1568d1be49f846f7ecd115ea1fbdfa74 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 15 Oct 2011 12:44:26 -0400 Subject: [PATCH 13/13] Reformat doctests so that tests pass I believe the dtype print out was changed to a string in a recent version of numpy, so this change may actually break the doctest for older versions. --- scikits/image/morphology/watershed.py | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/scikits/image/morphology/watershed.py b/scikits/image/morphology/watershed.py index 1065dbd3..5c74374a 100644 --- a/scikits/image/morphology/watershed.py +++ b/scikits/image/morphology/watershed.py @@ -249,32 +249,32 @@ def is_local_maximum(image, labels=None, footprint=None): >>> image[3, 3] = 1 >>> image array([[ 0., 0., 0., 0.], - [ 0., 0., 2., 0.], - [ 0., 0., 0., 0.], - [ 0., 0., 0., 1.]]) + [ 0., 0., 2., 0.], + [ 0., 0., 0., 0.], + [ 0., 0., 0., 1.]]) >>> is_local_maximum(image) array([[ True, False, False, False], - [ True, False, True, False], - [ True, False, False, False], - [ True, True, False, True]], dtype=bool) + [ True, False, True, False], + [ True, False, False, False], + [ True, True, False, True]], dtype='bool') >>> image = np.arange(16).reshape((4, 4)) >>> labels = np.array([[1, 2], [3, 4]]) >>> labels = np.repeat(np.repeat(labels, 2, axis=0), 2, axis=1) >>> labels array([[1, 1, 2, 2], - [1, 1, 2, 2], - [3, 3, 4, 4], - [3, 3, 4, 4]]) + [1, 1, 2, 2], + [3, 3, 4, 4], + [3, 3, 4, 4]]) >>> image array([[ 0, 1, 2, 3], - [ 4, 5, 6, 7], - [ 8, 9, 10, 11], - [12, 13, 14, 15]]) + [ 4, 5, 6, 7], + [ 8, 9, 10, 11], + [12, 13, 14, 15]]) >>> is_local_maximum(image, labels=labels) array([[False, False, False, False], - [False, True, False, True], - [False, False, False, False], - [False, True, False, True]], dtype=bool) + [False, True, False, True], + [False, False, False, False], + [False, True, False, True]], dtype='bool') """ if labels is None: labels = np.ones(image.shape, dtype=np.uint8)