From 9a17db19da341e4250d1168660db13b8fdb2075e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 30 Jun 2013 09:31:37 +0200 Subject: [PATCH] Refactor rank filter package for consistent naming --- skimage/filter/rank/__init__.py | 16 +- skimage/filter/rank/_rank.py | 773 ----------------- .../rank/{bilateral_rank.pyx => bilateral.py} | 8 +- ...ank16_bilateral.pyx => bilateral16_cy.pyx} | 2 +- .../rank/{_core16.pxd => core16_cy.pxd} | 0 .../rank/{_core16.pyx => core16_cy.pyx} | 2 +- .../filter/rank/{_core8.pxd => core8_cy.pxd} | 0 .../filter/rank/{_core8.pyx => core8_cy.pyx} | 0 skimage/filter/rank/generic.py | 776 ++++++++++++++++++ .../rank/{_crank16.pyx => generic16_cy.pyx} | 2 +- .../rank/{_crank8.pyx => generic8_cy.pyx} | 2 +- .../{percentile_rank.pyx => percentile.py} | 26 +- ...16_percentiles.pyx => percentile16_cy.pyx} | 2 +- ...nk8_percentiles.pyx => percentile8_cy.pyx} | 2 +- skimage/filter/setup.py | 37 +- 15 files changed, 822 insertions(+), 826 deletions(-) delete mode 100644 skimage/filter/rank/_rank.py rename skimage/filter/rank/{bilateral_rank.pyx => bilateral.py} (96%) rename skimage/filter/rank/{_crank16_bilateral.pyx => bilateral16_cy.pyx} (98%) rename skimage/filter/rank/{_core16.pxd => core16_cy.pxd} (100%) rename skimage/filter/rank/{_core16.pyx => core16_cy.pyx} (99%) rename skimage/filter/rank/{_core8.pxd => core8_cy.pxd} (100%) rename skimage/filter/rank/{_core8.pyx => core8_cy.pyx} (100%) rename skimage/filter/rank/{_crank16.pyx => generic16_cy.pyx} (99%) rename skimage/filter/rank/{_crank8.pyx => generic8_cy.pyx} (99%) rename skimage/filter/rank/{percentile_rank.pyx => percentile.py} (94%) rename skimage/filter/rank/{_crank16_percentiles.pyx => percentile16_cy.pyx} (99%) rename skimage/filter/rank/{_crank8_percentiles.pyx => percentile8_cy.pyx} (99%) diff --git a/skimage/filter/rank/__init__.py b/skimage/filter/rank/__init__.py index deceaade..9ad816ce 100644 --- a/skimage/filter/rank/__init__.py +++ b/skimage/filter/rank/__init__.py @@ -1,11 +1,11 @@ -from ._rank import (autolevel, bottomhat, equalize, gradient, maximum, mean, - meansubtraction, median, minimum, modal, morph_contr_enh, - pop, threshold, tophat, noise_filter, entropy, otsu) -from .percentile_rank import (percentile_autolevel, percentile_gradient, - percentile_mean, percentile_mean_subtraction, - percentile_morph_contr_enh, percentile, - percentile_pop, percentile_threshold) -from .bilateral_rank import bilateral_mean, bilateral_pop +from .generic import (autolevel, bottomhat, equalize, gradient, maximum, mean, + meansubtraction, median, minimum, modal, morph_contr_enh, + pop, threshold, tophat, noise_filter, entropy, otsu) +from .percentile import (percentile_autolevel, percentile_gradient, + percentile_mean, percentile_mean_subtraction, + percentile_morph_contr_enh, percentile, + percentile_pop, percentile_threshold) +from .bilateral import bilateral_mean, bilateral_pop __all__ = ['autolevel', diff --git a/skimage/filter/rank/_rank.py b/skimage/filter/rank/_rank.py deleted file mode 100644 index ea58ad86..00000000 --- a/skimage/filter/rank/_rank.py +++ /dev/null @@ -1,773 +0,0 @@ -"""The local histogram is computed using a sliding window similar to the method -described in [1]_. - -Input image can be 8-bit or 16-bit with a value < 4096 (i.e. 12 bit), for 16-bit -input images, the number of histogram bins is determined from the maximum value -present in the image. - -Result image is 8 or 16-bit with respect to the input image. - -References ----------- - -.. [1] Huang, T. ,Yang, G. ; Tang, G.. "A fast two-dimensional - median filtering algorithm", IEEE Transactions on Acoustics, Speech and - Signal Processing, Feb 1979. Volume: 27 , Issue: 1, Page(s): 13 - 18. - -""" - -import numpy as np -from skimage import img_as_ubyte, img_as_uint -from skimage.filter.rank import _crank8, _crank16 -from skimage.filter.rank.generic import find_bitdepth - - -__all__ = ['autolevel', 'bottomhat', 'equalize', 'gradient', 'maximum', 'mean', - 'meansubtraction', 'median', 'minimum', 'modal', 'morph_contr_enh', - 'pop', 'threshold', 'tophat', 'noise_filter', 'entropy', 'otsu'] - - -def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y): - selem = img_as_ubyte(selem > 0) - image = np.ascontiguousarray(image) - - if mask is None: - mask = np.ones(image.shape, dtype=np.uint8) - else: - mask = np.ascontiguousarray(mask) - mask = img_as_ubyte(mask) - - if image is out: - raise NotImplementedError("Cannot perform rank operation in place.") - - is_8bit = image.dtype in (np.uint8, np.int8) - - if func8 is not None and (is_8bit or func16 is None): - out = _apply8(func8, image, selem, out, mask, shift_x, shift_y) - else: - image = img_as_uint(image) - if out is None: - out = np.zeros(image.shape, dtype=np.uint16) - bitdepth = find_bitdepth(image) - if bitdepth > 11: - image = image >> 4 - bitdepth = find_bitdepth(image) - func16(image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, - bitdepth=bitdepth + 1, out=out) - - return out - - -def _apply8(func8, image, selem, out, mask, shift_x, shift_y): - if out is None: - out = np.zeros(image.shape, dtype=np.uint8) - image = img_as_ubyte(image) - func8(image, selem, shift_x=shift_x, shift_y=shift_y, - mask=mask, out=out) - return out - - -def autolevel(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Autolevel image using local histogram. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The result of the local autolevel. - - Examples - -------- - >>> from skimage import data - >>> from skimage.morphology import disk - >>> from skimage.filter.rank import autolevel - >>> # Load test image - >>> ima = data.camera() - >>> # Stretch image contrast locally - >>> auto = autolevel(ima, disk(20)) - - """ - - return _apply(_crank8.autolevel, _crank16.autolevel, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def bottomhat(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Returns greyscale local bottomhat of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - local bottomhat : uint8 array or uint16 array depending on input image - The result of the local bottomhat. - - """ - - return _apply(_crank8.bottomhat, _crank16.bottomhat, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def equalize(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Equalize image using local histogram. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The result of the local equalize. - - Examples - -------- - >>> from skimage import data - >>> from skimage.morphology import disk - >>> from skimage.filter.rank import equalize - >>> # Load test image - >>> ima = data.camera() - >>> # Local equalization - >>> equ = equalize(ima, disk(20)) - - """ - - return _apply(_crank8.equalize, _crank16.equalize, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def gradient(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local gradient of an image (i.e. local maximum - local - minimum). - - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The local gradient. - - """ - - return _apply(_crank8.gradient, _crank16.gradient, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def maximum(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local maximum of an image. - - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The local maximum. - - See also - -------- - skimage.morphology.dilation - - Note - ---- - * input image can be 8-bit or 16-bit with a value < 4096 (i.e. 12 bit) - * the lower algorithm complexity makes the rank.maximum() more efficient for - larger images and structuring elements - - """ - - return _apply(_crank8.maximum, _crank16.maximum, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local mean of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The local mean. - - Examples - -------- - >>> from skimage import data - >>> from skimage.morphology import disk - >>> from skimage.filter.rank import mean - >>> # Load test image - >>> ima = data.camera() - >>> # Local mean - >>> avg = mean(ima, disk(20)) - - """ - - return _apply(_crank8.mean, _crank16.mean, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def meansubtraction(image, selem, out=None, mask=None, shift_x=False, - shift_y=False): - """Return image subtracted from its local mean. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The result of the local meansubtraction. - - """ - - return _apply(_crank8.meansubtraction, _crank16.meansubtraction, image, - selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def median(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local median of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The local median. - - Examples - -------- - >>> from skimage import data - >>> from skimage.morphology import disk - >>> from skimage.filter.rank import median - >>> # Load test image - >>> ima = data.camera() - >>> # Local mean - >>> avg = median(ima, disk(20)) - - """ - - return _apply(_crank8.median, _crank16.median, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def minimum(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local minimum of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The local minimum. - - See also - -------- - skimage.morphology.erosion - - Note - ---- - * input image can be 8-bit or 16-bit with a value < 4096 (i.e. 12 bit) - * the lower algorithm complexity makes the rank.minimum() more efficient - for larger images and structuring elements - - """ - - return _apply(_crank8.minimum, _crank16.minimum, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def modal(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local mode of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The local modal. - - """ - - return _apply(_crank8.modal, _crank16.modal, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def morph_contr_enh(image, selem, out=None, mask=None, shift_x=False, - shift_y=False): - """Enhance an image replacing each pixel by the local maximum if pixel - greylevel is closest to maximimum than local minimum OR local minimum - otherwise. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The result of the local morph_contr_enh. - - Examples - -------- - >>> from skimage import data - >>> from skimage.morphology import disk - >>> from skimage.filter.rank import morph_contr_enh - >>> # Load test image - >>> ima = data.camera() - >>> # Local mean - >>> avg = morph_contr_enh(ima, disk(20)) - - """ - - return _apply(_crank8.morph_contr_enh, _crank16.morph_contr_enh, image, - selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def pop(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return the number (population) of pixels actually inside the - neighborhood. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The number of pixels belonging to the neighborhood. - - Examples - -------- - >>> # Local mean - >>> from skimage.morphology import square - >>> import skimage.filter.rank as rank - >>> ima = 255 * 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) - >>> rank.pop(ima, square(3)) - array([[4, 6, 6, 6, 4], - [6, 9, 9, 9, 6], - [6, 9, 9, 9, 6], - [6, 9, 9, 9, 6], - [4, 6, 6, 6, 4]], dtype=uint8) - - """ - - return _apply(_crank8.pop, _crank16.pop, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def threshold(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local threshold of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The result of the local threshold. - - Examples - -------- - >>> # Local threshold - >>> from skimage.morphology import square - >>> from skimage.filter.rank import threshold - >>> ima = 255 * 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) - >>> threshold(ima, square(3)) - array([[0, 0, 0, 0, 0], - [0, 1, 1, 1, 0], - [0, 1, 0, 1, 0], - [0, 1, 1, 1, 0], - [0, 0, 0, 0, 0]], dtype=uint8) - - """ - - return _apply(_crank8.threshold, _crank16.threshold, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def tophat(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Return greyscale local tophat of an image. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The image tophat. - - """ - - return _apply(_crank8.tophat, _crank16.tophat, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def noise_filter(image, selem, out=None, mask=None, shift_x=False, - shift_y=False): - """Returns the noise feature as described in [Hashimoto12]_ - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - References - ---------- - .. [Hashimoto12] N. Hashimoto et al. Referenceless image quality evaluation - for whole slide imaging. J Pathol Inform 2012;3:9. - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - The image noise. - - """ - - # ensure that the central pixel in the structuring element is empty - centre_r = int(selem.shape[0] / 2) + shift_y - centre_c = int(selem.shape[1] / 2) + shift_x - # make a local copy - selem_cpy = selem.copy() - selem_cpy[centre_r, centre_c] = 0 - - return _apply(_crank8.noise_filter, None, image, selem_cpy, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def entropy(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Returns the entropy [1]_ computed locally. Entropy is computed - using base 2 logarithm i.e. the filter returns the minimum number of - bits needed to encode local greylevel distribution. - - Parameters - ---------- - image : ndarray - Image array (uint8 array or uint16). If image is uint16, the algorithm - uses max. 12bit histogram, an exception will be raised if image has a - value > 4095. - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array or uint16 array (same as input image) - entropy x10 (uint8 images) and entropy x1000 (uint16 images) - - References - ---------- - .. [1] http://en.wikipedia.org/wiki/Entropy_(information_theory) - - Examples - -------- - >>> # Local entropy - >>> from skimage import data - >>> from skimage.filter.rank import entropy - >>> from skimage.morphology import disk - >>> # defining a 8- and a 16-bit test images - >>> a8 = data.camera() - >>> a16 = data.camera().astype(np.uint16) * 4 - >>> # pixel values contain 10x the local entropy - >>> ent8 = entropy(a8, disk(5)) - >>> # pixel values contain 1000x the local entropy - >>> ent16 = entropy(a16, disk(5)) - - """ - - return _apply(_crank8.entropy, _crank16.entropy, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) - - -def otsu(image, selem, out=None, mask=None, shift_x=False, shift_y=False): - """Returns the Otsu's threshold value for each pixel. - - Parameters - ---------- - image : ndarray - Image array (uint8 array). - selem : ndarray - The neighborhood expressed as a 2-D array of 1's and 0's. - out : ndarray - If None, a new array will be allocated. - mask : ndarray (uint8) - Mask array that defines (>0) area of the image included in the local - neighborhood. If None, the complete image is used (default). - shift_x, shift_y : int - Offset added to the structuring element center point. Shift is bounded - to the structuring element sizes (center must be inside the given - structuring element). - - Returns - ------- - out : uint8 array - Otsu's threshold values - - References - ---------- - .. [otsu] http://en.wikipedia.org/wiki/Otsu's_method - - Notes - ----- - * input image are 8-bit only - - Examples - -------- - >>> # Local entropy - >>> from skimage import data - >>> from skimage.filter.rank import otsu - >>> from skimage.morphology import disk - >>> # defining a 8-bit test images - >>> a8 = data.camera() - >>> loc_otsu = otsu(a8, disk(5)) - >>> thresh_image = a8 >= loc_otsu - - """ - - return _apply(_crank8.otsu, None, image, selem, out=out, - mask=mask, shift_x=shift_x, shift_y=shift_y) diff --git a/skimage/filter/rank/bilateral_rank.pyx b/skimage/filter/rank/bilateral.py similarity index 96% rename from skimage/filter/rank/bilateral_rank.pyx rename to skimage/filter/rank/bilateral.py index e2a1fcf3..a111dd7c 100644 --- a/skimage/filter/rank/bilateral_rank.pyx +++ b/skimage/filter/rank/bilateral.py @@ -28,8 +28,8 @@ References import numpy as np from skimage import img_as_ubyte -from skimage.filter.rank import _crank16_bilateral -from skimage.filter.rank.generic import find_bitdepth +from . import bilateral16_cy +from .generic import find_bitdepth __all__ = ['bilateral_mean', 'bilateral_pop'] @@ -130,7 +130,7 @@ def bilateral_mean(image, selem, out=None, mask=None, shift_x=False, >>> bilat_ima = bilateral_mean(ima, disk(20), s0=10,s1=10) """ - return _apply(None, _crank16_bilateral.mean, image, selem, out=out, + return _apply(None, _bilateral16_cy.mean, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, s0=s0, s1=s1) @@ -188,5 +188,5 @@ def bilateral_pop(image, selem, out=None, mask=None, shift_x=False, """ - return _apply(None, _crank16_bilateral.pop, image, selem, out=out, + return _apply(None, _bilateral16_cy.pop, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, s0=s0, s1=s1) diff --git a/skimage/filter/rank/_crank16_bilateral.pyx b/skimage/filter/rank/bilateral16_cy.pyx similarity index 98% rename from skimage/filter/rank/_crank16_bilateral.pyx rename to skimage/filter/rank/bilateral16_cy.pyx index e431e42b..d9f68f73 100644 --- a/skimage/filter/rank/_crank16_bilateral.pyx +++ b/skimage/filter/rank/bilateral16_cy.pyx @@ -4,7 +4,7 @@ #cython: wraparound=False cimport numpy as cnp -from skimage.filter.rank._core16 cimport _core16 +from .core16_cy cimport _core16 # ----------------------------------------------------------------- diff --git a/skimage/filter/rank/_core16.pxd b/skimage/filter/rank/core16_cy.pxd similarity index 100% rename from skimage/filter/rank/_core16.pxd rename to skimage/filter/rank/core16_cy.pxd diff --git a/skimage/filter/rank/_core16.pyx b/skimage/filter/rank/core16_cy.pyx similarity index 99% rename from skimage/filter/rank/_core16.pyx rename to skimage/filter/rank/core16_cy.pyx index 0c7a7a82..63bcdff1 100644 --- a/skimage/filter/rank/_core16.pyx +++ b/skimage/filter/rank/core16_cy.pyx @@ -7,7 +7,7 @@ import numpy as np cimport numpy as cnp from libc.stdlib cimport malloc, free -from _core8 cimport is_in_mask +from .core8_cy cimport is_in_mask cdef inline int int_max(int a, int b): diff --git a/skimage/filter/rank/_core8.pxd b/skimage/filter/rank/core8_cy.pxd similarity index 100% rename from skimage/filter/rank/_core8.pxd rename to skimage/filter/rank/core8_cy.pxd diff --git a/skimage/filter/rank/_core8.pyx b/skimage/filter/rank/core8_cy.pyx similarity index 100% rename from skimage/filter/rank/_core8.pyx rename to skimage/filter/rank/core8_cy.pyx diff --git a/skimage/filter/rank/generic.py b/skimage/filter/rank/generic.py index 94fc3130..124d4f25 100644 --- a/skimage/filter/rank/generic.py +++ b/skimage/filter/rank/generic.py @@ -1,3 +1,31 @@ +"""The local histogram is computed using a sliding window similar to the method +described in [1]_. + +Input image can be 8-bit or 16-bit with a value < 4096 (i.e. 12 bit), for 16-bit +input images, the number of histogram bins is determined from the maximum value +present in the image. + +Result image is 8 or 16-bit with respect to the input image. + +References +---------- + +.. [1] Huang, T. ,Yang, G. ; Tang, G.. "A fast two-dimensional + median filtering algorithm", IEEE Transactions on Acoustics, Speech and + Signal Processing, Feb 1979. Volume: 27 , Issue: 1, Page(s): 13 - 18. + +""" + +import numpy as np +from skimage import img_as_ubyte, img_as_uint +from . import generic8_cy, generic16_cy + + +__all__ = ['autolevel', 'bottomhat', 'equalize', 'gradient', 'maximum', 'mean', + 'meansubtraction', 'median', 'minimum', 'modal', 'morph_contr_enh', + 'pop', 'threshold', 'tophat', 'noise_filter', 'entropy', 'otsu'] + + import numpy as np @@ -9,3 +37,751 @@ def find_bitdepth(image): return int(np.log2(umax)) else: return 1 + + +def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y): + selem = img_as_ubyte(selem > 0) + image = np.ascontiguousarray(image) + + if mask is None: + mask = np.ones(image.shape, dtype=np.uint8) + else: + mask = np.ascontiguousarray(mask) + mask = img_as_ubyte(mask) + + if image is out: + raise NotImplementedError("Cannot perform rank operation in place.") + + is_8bit = image.dtype in (np.uint8, np.int8) + + if func8 is not None and (is_8bit or func16 is None): + out = _apply8(func8, image, selem, out, mask, shift_x, shift_y) + else: + image = img_as_uint(image) + if out is None: + out = np.zeros(image.shape, dtype=np.uint16) + bitdepth = find_bitdepth(image) + if bitdepth > 11: + image = image >> 4 + bitdepth = find_bitdepth(image) + func16(image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, + bitdepth=bitdepth + 1, out=out) + + return out + + +def _apply8(func8, image, selem, out, mask, shift_x, shift_y): + if out is None: + out = np.zeros(image.shape, dtype=np.uint8) + image = img_as_ubyte(image) + func8(image, selem, shift_x=shift_x, shift_y=shift_y, + mask=mask, out=out) + return out + + +def autolevel(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Autolevel image using local histogram. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The result of the local autolevel. + + Examples + -------- + >>> from skimage import data + >>> from skimage.morphology import disk + >>> from skimage.filter.rank import autolevel + >>> # Load test image + >>> ima = data.camera() + >>> # Stretch image contrast locally + >>> auto = autolevel(ima, disk(20)) + + """ + + return _apply(generic8_cy.autolevel, generic16_cy.autolevel, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def bottomhat(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Returns greyscale local bottomhat of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + local bottomhat : uint8 array or uint16 array depending on input image + The result of the local bottomhat. + + """ + + return _apply(generic8_cy.bottomhat, generic16_cy.bottomhat, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def equalize(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Equalize image using local histogram. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The result of the local equalize. + + Examples + -------- + >>> from skimage import data + >>> from skimage.morphology import disk + >>> from skimage.filter.rank import equalize + >>> # Load test image + >>> ima = data.camera() + >>> # Local equalization + >>> equ = equalize(ima, disk(20)) + + """ + + return _apply(generic8_cy.equalize, generic16_cy.equalize, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def gradient(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local gradient of an image (i.e. local maximum - local + minimum). + + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The local gradient. + + """ + + return _apply(generic8_cy.gradient, generic16_cy.gradient, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def maximum(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local maximum of an image. + + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The local maximum. + + See also + -------- + skimage.morphology.dilation + + Note + ---- + * input image can be 8-bit or 16-bit with a value < 4096 (i.e. 12 bit) + * the lower algorithm complexity makes the rank.maximum() more efficient for + larger images and structuring elements + + """ + + return _apply(generic8_cy.maximum, generic16_cy.maximum, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local mean of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The local mean. + + Examples + -------- + >>> from skimage import data + >>> from skimage.morphology import disk + >>> from skimage.filter.rank import mean + >>> # Load test image + >>> ima = data.camera() + >>> # Local mean + >>> avg = mean(ima, disk(20)) + + """ + + return _apply(generic8_cy.mean, generic16_cy.mean, image, selem, out=out, + mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def meansubtraction(image, selem, out=None, mask=None, shift_x=False, + shift_y=False): + """Return image subtracted from its local mean. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The result of the local meansubtraction. + + """ + + return _apply(generic8_cy.meansubtraction, generic16_cy.meansubtraction, + image, selem, out=out, mask=mask, shift_x=shift_x, + shift_y=shift_y) + + +def median(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local median of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The local median. + + Examples + -------- + >>> from skimage import data + >>> from skimage.morphology import disk + >>> from skimage.filter.rank import median + >>> # Load test image + >>> ima = data.camera() + >>> # Local mean + >>> avg = median(ima, disk(20)) + + """ + + return _apply(generic8_cy.median, generic16_cy.median, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def minimum(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local minimum of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The local minimum. + + See also + -------- + skimage.morphology.erosion + + Note + ---- + * input image can be 8-bit or 16-bit with a value < 4096 (i.e. 12 bit) + * the lower algorithm complexity makes the rank.minimum() more efficient + for larger images and structuring elements + + """ + + return _apply(generic8_cy.minimum, generic16_cy.minimum, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def modal(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local mode of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The local modal. + + """ + + return _apply(generic8_cy.modal, generic16_cy.modal, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def morph_contr_enh(image, selem, out=None, mask=None, shift_x=False, + shift_y=False): + """Enhance an image replacing each pixel by the local maximum if pixel + greylevel is closest to maximimum than local minimum OR local minimum + otherwise. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The result of the local morph_contr_enh. + + Examples + -------- + >>> from skimage import data + >>> from skimage.morphology import disk + >>> from skimage.filter.rank import morph_contr_enh + >>> # Load test image + >>> ima = data.camera() + >>> # Local mean + >>> avg = morph_contr_enh(ima, disk(20)) + + """ + + return _apply(generic8_cy.morph_contr_enh, generic16_cy.morph_contr_enh, + image, selem, out=out, mask=mask, shift_x=shift_x, + shift_y=shift_y) + + +def pop(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return the number (population) of pixels actually inside the + neighborhood. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The number of pixels belonging to the neighborhood. + + Examples + -------- + >>> # Local mean + >>> from skimage.morphology import square + >>> import skimage.filter.rank as rank + >>> ima = 255 * 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) + >>> rank.pop(ima, square(3)) + array([[4, 6, 6, 6, 4], + [6, 9, 9, 9, 6], + [6, 9, 9, 9, 6], + [6, 9, 9, 9, 6], + [4, 6, 6, 6, 4]], dtype=uint8) + + """ + + return _apply(generic8_cy.pop, generic16_cy.pop, image, selem, out=out, + mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def threshold(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local threshold of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The result of the local threshold. + + Examples + -------- + >>> # Local threshold + >>> from skimage.morphology import square + >>> from skimage.filter.rank import threshold + >>> ima = 255 * 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) + >>> threshold(ima, square(3)) + array([[0, 0, 0, 0, 0], + [0, 1, 1, 1, 0], + [0, 1, 0, 1, 0], + [0, 1, 1, 1, 0], + [0, 0, 0, 0, 0]], dtype=uint8) + + """ + + return _apply(generic8_cy.threshold, generic16_cy.threshold, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def tophat(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Return greyscale local tophat of an image. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The image tophat. + + """ + + return _apply(generic8_cy.tophat, generic16_cy.tophat, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def noise_filter(image, selem, out=None, mask=None, shift_x=False, + shift_y=False): + """Returns the noise feature as described in [Hashimoto12]_ + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + References + ---------- + .. [Hashimoto12] N. Hashimoto et al. Referenceless image quality evaluation + for whole slide imaging. J Pathol Inform 2012;3:9. + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + The image noise. + + """ + + # ensure that the central pixel in the structuring element is empty + centre_r = int(selem.shape[0] / 2) + shift_y + centre_c = int(selem.shape[1] / 2) + shift_x + # make a local copy + selem_cpy = selem.copy() + selem_cpy[centre_r, centre_c] = 0 + + return _apply(generic8_cy.noise_filter, None, image, selem_cpy, out=out, + mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def entropy(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Returns the entropy [1]_ computed locally. Entropy is computed + using base 2 logarithm i.e. the filter returns the minimum number of + bits needed to encode local greylevel distribution. + + Parameters + ---------- + image : ndarray + Image array (uint8 array or uint16). If image is uint16, the algorithm + uses max. 12bit histogram, an exception will be raised if image has a + value > 4095. + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array or uint16 array (same as input image) + entropy x10 (uint8 images) and entropy x1000 (uint16 images) + + References + ---------- + .. [1] http://en.wikipedia.org/wiki/Entropy_(information_theory) + + Examples + -------- + >>> # Local entropy + >>> from skimage import data + >>> from skimage.filter.rank import entropy + >>> from skimage.morphology import disk + >>> # defining a 8- and a 16-bit test images + >>> a8 = data.camera() + >>> a16 = data.camera().astype(np.uint16) * 4 + >>> # pixel values contain 10x the local entropy + >>> ent8 = entropy(a8, disk(5)) + >>> # pixel values contain 1000x the local entropy + >>> ent16 = entropy(a16, disk(5)) + + """ + + return _apply(generic8_cy.entropy, generic16_cy.entropy, image, selem, + out=out, mask=mask, shift_x=shift_x, shift_y=shift_y) + + +def otsu(image, selem, out=None, mask=None, shift_x=False, shift_y=False): + """Returns the Otsu's threshold value for each pixel. + + Parameters + ---------- + image : ndarray + Image array (uint8 array). + selem : ndarray + The neighborhood expressed as a 2-D array of 1's and 0's. + out : ndarray + If None, a new array will be allocated. + mask : ndarray (uint8) + Mask array that defines (>0) area of the image included in the local + neighborhood. If None, the complete image is used (default). + shift_x, shift_y : int + Offset added to the structuring element center point. Shift is bounded + to the structuring element sizes (center must be inside the given + structuring element). + + Returns + ------- + out : uint8 array + Otsu's threshold values + + References + ---------- + .. [otsu] http://en.wikipedia.org/wiki/Otsu's_method + + Notes + ----- + * input image are 8-bit only + + Examples + -------- + >>> # Local entropy + >>> from skimage import data + >>> from skimage.filter.rank import otsu + >>> from skimage.morphology import disk + >>> # defining a 8-bit test images + >>> a8 = data.camera() + >>> loc_otsu = otsu(a8, disk(5)) + >>> thresh_image = a8 >= loc_otsu + + """ + + return _apply(generic8_cy.otsu, None, image, selem, out=out, + mask=mask, shift_x=shift_x, shift_y=shift_y) diff --git a/skimage/filter/rank/_crank16.pyx b/skimage/filter/rank/generic16_cy.pyx similarity index 99% rename from skimage/filter/rank/_crank16.pyx rename to skimage/filter/rank/generic16_cy.pyx index e704afe0..eace0afa 100644 --- a/skimage/filter/rank/_crank16.pyx +++ b/skimage/filter/rank/generic16_cy.pyx @@ -5,7 +5,7 @@ cimport numpy as cnp from libc.math cimport log -from skimage.filter.rank._core16 cimport _core16 +from .core16_cy cimport _core16 # ----------------------------------------------------------------- diff --git a/skimage/filter/rank/_crank8.pyx b/skimage/filter/rank/generic8_cy.pyx similarity index 99% rename from skimage/filter/rank/_crank8.pyx rename to skimage/filter/rank/generic8_cy.pyx index da511790..1cccc21f 100644 --- a/skimage/filter/rank/_crank8.pyx +++ b/skimage/filter/rank/generic8_cy.pyx @@ -5,7 +5,7 @@ cimport numpy as cnp from libc.math cimport log -from skimage.filter.rank._core8 cimport _core8 +from .core8_cy cimport _core8 # ----------------------------------------------------------------- diff --git a/skimage/filter/rank/percentile_rank.pyx b/skimage/filter/rank/percentile.py similarity index 94% rename from skimage/filter/rank/percentile_rank.pyx rename to skimage/filter/rank/percentile.py index 704f53c2..25758115 100644 --- a/skimage/filter/rank/percentile_rank.pyx +++ b/skimage/filter/rank/percentile.py @@ -24,8 +24,8 @@ References import numpy as np from skimage import img_as_ubyte -from skimage.filter.rank.generic import find_bitdepth -from skimage.filter.rank import _crank16_percentiles, _crank8_percentiles +from . import percentile8_cy, percentile16_cy +from .generic import find_bitdepth __all__ = ['percentile_autolevel', 'percentile_gradient', @@ -106,7 +106,7 @@ def percentile_autolevel(image, selem, out=None, mask=None, shift_x=False, """ return _apply( - _crank8_percentiles.autolevel, _crank16_percentiles.autolevel, + percentile8_cy.autolevel, percentile16_cy.autolevel, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1) @@ -146,7 +146,7 @@ def percentile_gradient(image, selem, out=None, mask=None, shift_x=False, """ - return _apply(_crank8_percentiles.gradient, _crank16_percentiles.gradient, + return _apply(percentile8_cy.gradient, percentile16_cy.gradient, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1) @@ -186,7 +186,7 @@ def percentile_mean(image, selem, out=None, mask=None, shift_x=False, """ - return _apply(_crank8_percentiles.mean, _crank16_percentiles.mean, + return _apply(percentile8_cy.mean, percentile16_cy.mean, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1) @@ -226,8 +226,8 @@ def percentile_mean_subtraction(image, selem, out=None, mask=None, """ - return _apply(_crank8_percentiles.mean_subtraction, - _crank16_percentiles.mean_subtraction, + return _apply(percentile8_cy.mean_subtraction, + percentile16_cy.mean_subtraction, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1) @@ -268,8 +268,8 @@ def percentile_morph_contr_enh( """ - return _apply(_crank8_percentiles.morph_contr_enh, - _crank16_percentiles.morph_contr_enh, + return _apply(percentile8_cy.morph_contr_enh, + percentile16_cy.morph_contr_enh, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1) @@ -308,8 +308,8 @@ def percentile(image, selem, out=None, mask=None, shift_x=False, shift_y=False, """ - return _apply(_crank8_percentiles.percentile, - _crank16_percentiles.percentile, + return _apply(percentile8_cy.percentile, + percentile16_cy.percentile, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=0.) @@ -349,7 +349,7 @@ def percentile_pop(image, selem, out=None, mask=None, shift_x=False, """ - return _apply(_crank8_percentiles.pop, _crank16_percentiles.pop, + return _apply(percentile8_cy.pop, percentile16_cy.pop, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1) @@ -391,6 +391,6 @@ def percentile_threshold(image, selem, out=None, mask=None, shift_x=False, """ return _apply( - _crank8_percentiles.threshold, _crank16_percentiles.threshold, + percentile8_cy.threshold, percentile16_cy.threshold, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=0.) diff --git a/skimage/filter/rank/_crank16_percentiles.pyx b/skimage/filter/rank/percentile16_cy.pyx similarity index 99% rename from skimage/filter/rank/_crank16_percentiles.pyx rename to skimage/filter/rank/percentile16_cy.pyx index f4a4c9b2..368bec0e 100644 --- a/skimage/filter/rank/_crank16_percentiles.pyx +++ b/skimage/filter/rank/percentile16_cy.pyx @@ -4,7 +4,7 @@ #cython: wraparound=False cimport numpy as cnp -from skimage.filter.rank._core16 cimport _core16, int_min, int_max +from .core16_cy cimport _core16, int_min, int_max # ----------------------------------------------------------------- diff --git a/skimage/filter/rank/_crank8_percentiles.pyx b/skimage/filter/rank/percentile8_cy.pyx similarity index 99% rename from skimage/filter/rank/_crank8_percentiles.pyx rename to skimage/filter/rank/percentile8_cy.pyx index 8e5cee9c..7f514f89 100644 --- a/skimage/filter/rank/_crank8_percentiles.pyx +++ b/skimage/filter/rank/percentile8_cy.pyx @@ -4,7 +4,7 @@ #cython: wraparound=False cimport numpy as cnp -from skimage.filter.rank._core8 cimport _core8, uint8_max, uint8_min +from .core8_cy cimport _core8, uint8_max, uint8_min # ----------------------------------------------------------------- diff --git a/skimage/filter/setup.py b/skimage/filter/setup.py index c70730f0..35626114 100644 --- a/skimage/filter/setup.py +++ b/skimage/filter/setup.py @@ -14,46 +14,39 @@ def configuration(parent_package='', top_path=None): cython(['_ctmf.pyx'], working_path=base_path) cython(['_denoise_cy.pyx'], working_path=base_path) - cython(['rank/_core8.pyx'], working_path=base_path) - cython(['rank/_core16.pyx'], working_path=base_path) - cython(['rank/_crank8.pyx'], working_path=base_path) - cython(['rank/_crank8_percentiles.pyx'], working_path=base_path) - cython(['rank/_crank16.pyx'], working_path=base_path) - cython(['rank/_crank16_percentiles.pyx'], working_path=base_path) - cython(['rank/_crank16_bilateral.pyx'], working_path=base_path) - cython(['rank/percentile_rank.pyx'], working_path=base_path) - cython(['rank/bilateral_rank.pyx'], working_path=base_path) + cython(['rank/core8_cy.pyx'], working_path=base_path) + cython(['rank/core16_cy.pyx'], working_path=base_path) + cython(['rank/generic8_cy.pyx'], working_path=base_path) + cython(['rank/percentile8_cy.pyx'], working_path=base_path) + cython(['rank/generic16_cy.pyx'], working_path=base_path) + cython(['rank/percentile16_cy.pyx'], working_path=base_path) + cython(['rank/bilateral16_cy.pyx'], working_path=base_path) config.add_extension('_ctmf', sources=['_ctmf.c'], include_dirs=[get_numpy_include_dirs()]) config.add_extension('_denoise_cy', sources=['_denoise_cy.c'], include_dirs=[get_numpy_include_dirs(), '../_shared']) - config.add_extension('rank._core8', sources=['rank/_core8.c'], + config.add_extension('rank.core8_cy', sources=['rank/core8_cy.c'], include_dirs=[get_numpy_include_dirs()]) - config.add_extension('rank._core16', sources=['rank/_core16.c'], + config.add_extension('rank.core16_cy', sources=['rank/core16_cy.c'], include_dirs=[get_numpy_include_dirs()]) - config.add_extension('rank._crank8', sources=['rank/_crank8.c'], + config.add_extension('rank.generic8_cy', sources=['rank/generic8_cy.c'], include_dirs=[get_numpy_include_dirs()]) config.add_extension( - 'rank._crank8_percentiles', sources=['rank/_crank8_percentiles.c'], + 'rank.percentile8_cy', sources=['rank/percentile8_cy.c'], include_dirs=[get_numpy_include_dirs()]) - config.add_extension('rank._crank16', sources=['rank/_crank16.c'], + config.add_extension('rank.generic16_cy', sources=['rank/generic16_cy.c'], include_dirs=[get_numpy_include_dirs()]) config.add_extension( - 'rank._crank16_percentiles', sources=['rank/_crank16_percentiles.c'], + 'rank.percentile16_cy', sources=['rank/percentile16_cy.c'], include_dirs=[get_numpy_include_dirs()]) config.add_extension( - 'rank._crank16_bilateral', sources=['rank/_crank16_bilateral.c'], - include_dirs=[get_numpy_include_dirs()]) - config.add_extension( - 'rank.percentile_rank', sources=['rank/percentile_rank.c'], - include_dirs=[get_numpy_include_dirs()]) - config.add_extension( - 'rank.bilateral_rank', sources=['rank/bilateral_rank.c'], + 'rank.bilateral16_cy', sources=['rank/bilateral16_cy.c'], include_dirs=[get_numpy_include_dirs()]) return config + if __name__ == '__main__': from numpy.distutils.core import setup setup(maintainer='scikit-image Developers',