diff --git a/skimage/morphology/cmorph.pyx b/skimage/morphology/cmorph.pyx index 6870b500..5e5a88f9 100644 --- a/skimage/morphology/cmorph.pyx +++ b/skimage/morphology/cmorph.pyx @@ -1,116 +1,120 @@ -""" -:author: Damian Eads, 2009 -:license: modified BSD -""" +#cython: cdivision=True +#cython: boundscheck=False +#cython: nonecheck=False +#cython: wraparound=False -from __future__ import division import numpy as np - cimport numpy as np cimport cython from cpython cimport bool +from libc.stdlib cimport malloc, free -STREL_DTYPE = np.uint8 -ctypedef np.uint8_t STREL_DTYPE_t -IMAGE_DTYPE = np.uint8 -ctypedef np.uint8_t IMAGE_DTYPE_t +def dilate(np.ndarray[np.uint8_t, ndim=2] image, + np.ndarray[np.uint8_t, ndim=2] selem, + np.ndarray[np.uint8_t, ndim=2] out=None, + shift_x=False, shift_y=False): -cdef inline int int_max(int a, int b): return a if a >= b else b -cdef inline int int_min(int a, int b): return a if a <= b else b + cdef int rows = image.shape[0] + cdef int cols = image.shape[1] + cdef int srows = selem.shape[0] + cdef int scols = selem.shape[1] -@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, - bool shift_x, bool shift_y): - cdef int hw = selem.shape[0] // 2 - cdef int hh = selem.shape[1] // 2 - if shift_x: - hh -= 1 - if shift_y: - hw -= 1 + cdef int centre_r = int(selem.shape[0] / 2) - shift_y + cdef int centre_c = int(selem.shape[1] / 2) - shift_x - cdef int width = image.shape[0], height = image.shape[1] + image = np.ascontiguousarray(image) if out is None: - out = np.zeros([width, height], dtype=IMAGE_DTYPE) + out = np.zeros((rows, cols), dtype=np.uint8) + else: + out = np.ascontiguousarray(out) - assert out.shape[0] == image.shape[0] - assert out.shape[1] == image.shape[1] + cdef np.uint8_t* out_data = out.data + cdef np.uint8_t* image_data = image.data - cdef int x, y, ix, iy, cx, cy - cdef IMAGE_DTYPE_t max_so_far + cdef int r, c, rr, cc, s, value, local_max - cdef int sw = selem.shape[0], sh = selem.shape[1] + cdef int selem_num = np.sum(selem != 0) + cdef int* sr = malloc(selem_num * sizeof(int)) + cdef int* sc = malloc(selem_num * sizeof(int)) - cdef np.ndarray[np.int_t, ndim=2] xinc = np.zeros([sw, sh], dtype=np.int) - cdef np.ndarray[np.int_t, ndim=2] yinc = np.zeros([sw, sh], dtype=np.int) + s = 0 + for r in range(srows): + for c in range(scols): + if selem[r, c] != 0: + sr[s] = r - centre_r + sc[s] = c - centre_c + s += 1 - for x in range(sw): - for y in range(sh): - xinc[x, y] = (x - hw) - yinc[x, y] = (y - hh) + for r in range(rows): + for c in range(cols): + local_max = 0 + for s in range(selem_num): + rr = r + sr[s] + cc = c + sc[s] + if 0 <= rr < rows and 0 <= cc < cols: + value = image_data[rr * rows + cc] + if value > local_max: + local_max = value + out_data[r * cols + c] = local_max - for x in range(width): - for y in range(height): - max_so_far = 0 - for cx in range(0, sw): - for cy in range(0, sh): - ix = x + xinc[cx,cy] - iy = y + yinc[cx,cy] - if ix>=0 and iy>=0 and ix < width and iy < height \ - and selem[cx, cy] == 1 \ - and image[ix,iy] > max_so_far: - max_so_far = image[ix,iy] - out[x,y] = max_so_far + free(sr) + free(sc) return out -@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, - bool shift_x, bool shift_y): - cdef int hw = selem.shape[0] // 2 - cdef int hh = selem.shape[1] // 2 - if shift_x: - hh -= 1 - if shift_y: - hw -= 1 +def erode(np.ndarray[np.uint8_t, ndim=2] image, + np.ndarray[np.uint8_t, ndim=2] selem, + np.ndarray[np.uint8_t, ndim=2] out=None, + shift_x=False, shift_y=False): - cdef int width = image.shape[0], height = image.shape[1] + cdef int rows = image.shape[0] + cdef int cols = image.shape[1] + cdef int srows = selem.shape[0] + cdef int scols = selem.shape[1] + + cdef int centre_r = int(selem.shape[0] / 2) - shift_y + cdef int centre_c = int(selem.shape[1] / 2) - shift_x + + image = np.ascontiguousarray(image) if out is None: - out = np.zeros([width, height], dtype=IMAGE_DTYPE) + out = np.zeros((rows, cols), dtype=np.uint8) + else: + out = np.ascontiguousarray(out) - assert out.shape[0] == image.shape[0] - assert out.shape[1] == image.shape[1] + cdef np.uint8_t* out_data = out.data + cdef np.uint8_t* image_data = image.data - cdef int x, y, ix, iy, cx, cy - cdef IMAGE_DTYPE_t min_so_far + cdef int r, c, rr, cc, s, value, local_max - cdef int sw = selem.shape[0], sh = selem.shape[1] + cdef int selem_num = np.sum(selem != 0) + cdef int* sr = malloc(selem_num * sizeof(int)) + cdef int* sc = malloc(selem_num * sizeof(int)) - cdef np.ndarray[np.int_t, ndim=2] xinc = np.zeros([sw, sh], dtype=np.int) - cdef np.ndarray[np.int_t, ndim=2] yinc = np.zeros([sw, sh], dtype=np.int) + s = 0 + for r in range(srows): + for c in range(scols): + if selem[r, c] != 0: + sr[s] = r - centre_r + sc[s] = c - centre_c + s += 1 - for x in range(sw): - for y in range(sh): - xinc[x, y] = (x - hw) - yinc[x, y] = (y - hh) + for r in range(rows): + for c in range(cols): + local_min = 255 + for s in range(selem_num): + rr = r + sr[s] + cc = c + sc[s] + if 0 <= rr < rows and 0 <= cc < cols: + value = image_data[rr * rows + cc] + if value < local_min: + local_min = value - for x in range(width): - for y in range(height): - min_so_far = 255 - for cx in range(0, sw): - for cy in range(0, sh): - ix = x + xinc[cx,cy] - iy = y + yinc[cx,cy] - if ix>=0 and iy>=0 and ix < width \ - and iy < height and selem[cx, cy] == 1 \ - and image[ix,iy] < min_so_far: - min_so_far = image[ix,iy] - out[x,y] = min_so_far + out_data[r * cols + c] = local_min + + free(sr) + free(sc) return out diff --git a/skimage/morphology/grey.py b/skimage/morphology/grey.py index 309fb5be..7ce25475 100644 --- a/skimage/morphology/grey.py +++ b/skimage/morphology/grey.py @@ -6,11 +6,11 @@ __docformat__ = 'restructuredtext en' import warnings - import numpy as np - import skimage +from . import cmorph + __all__ = ['erosion', 'dilation', 'opening', 'closing', 'white_tophat', 'black_tophat', 'greyscale_erode', 'greyscale_dilate', @@ -66,14 +66,8 @@ def erosion(image, selem, out=None, shift_x=False, shift_y=False): if image is out: raise NotImplementedError("In-place erosion not supported!") image = skimage.img_as_ubyte(image) - - try: - import skimage.morphology.cmorph as cmorph - 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.") + return cmorph.erode(image, selem, out=out, + shift_x=shift_x, shift_y=shift_y) def dilation(image, selem, out=None, shift_x=False, shift_y=False): @@ -125,14 +119,8 @@ def dilation(image, selem, out=None, shift_x=False, shift_y=False): if image is out: raise NotImplementedError("In-place dilation not supported!") image = skimage.img_as_ubyte(image) - - try: - from . import cmorph - 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.") + return cmorph.dilate(image, selem, out=out, + shift_x=shift_x, shift_y=shift_y) def opening(image, selem, out=None):