Source code for medpy.filter.smoothing

# Copyright (C) 2013 Oskar Maier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# author Oskar Maier
# version r0.3.0
# since 2013-08-23
# status Release

# build-in modules

# third-party modules
import numpy
from scipy.ndimage.filters import gaussian_filter

# path changes

# own modules
from .utilities import xminus1d


# code
[docs]def gauss_xminus1d(img, sigma, dim=2): r""" Applies a X-1D gauss to a copy of a XD image, slicing it along dim. Essentially uses `scipy.ndimage.filters.gaussian_filter`, but applies it to a dimension less than the image has. Parameters ---------- img : array_like The image to smooth. sigma : integer The sigma i.e. gaussian kernel size in pixel dim : integer The dimension along which to apply the filter. Returns ------- gauss_xminus1d : ndarray The input image ``img`` smoothed by a gaussian kernel along dimension ``dim``. """ img = numpy.array(img, copy=False) return xminus1d(img, gaussian_filter, dim, sigma=sigma)
[docs]def anisotropic_diffusion(img, niter=1, kappa=50, gamma=0.1, voxelspacing=None, option=1): r""" Edge-preserving, XD Anisotropic diffusion. Parameters ---------- img : array_like Input image (will be cast to numpy.float). niter : integer Number of iterations. kappa : integer Conduction coefficient, e.g. 20-100. ``kappa`` controls conduction as a function of the gradient. If ``kappa`` is low small intensity gradients are able to block conduction and hence diffusion across steep edges. A large value reduces the influence of intensity gradients on conduction. gamma : float Controls the speed of diffusion. Pick a value :math:`<= .25` for stability. voxelspacing : tuple of floats or array_like The distance between adjacent pixels in all img.ndim directions option : {1, 2, 3} Whether to use the Perona Malik diffusion equation No. 1 or No. 2, or Tukey's biweight function. Equation 1 favours high contrast edges over low contrast ones, while equation 2 favours wide regions over smaller ones. See [1]_ for details. Equation 3 preserves sharper boundaries than previous formulations and improves the automatic stopping of the diffusion. See [2]_ for details. Returns ------- anisotropic_diffusion : ndarray Diffused image. Notes ----- Original MATLAB code by Peter Kovesi, School of Computer Science & Software Engineering, The University of Western Australia, pk @ csse uwa edu au, <http://www.csse.uwa.edu.au> Translated to Python and optimised by Alistair Muldal, Department of Pharmacology, University of Oxford, <alistair.muldal@pharm.ox.ac.uk> Adapted to arbitrary dimensionality and added to the MedPy library Oskar Maier, Institute for Medical Informatics, Universitaet Luebeck, <oskar.maier@googlemail.com> June 2000 original version. - March 2002 corrected diffusion eqn No 2. - July 2012 translated to Python - August 2013 incorporated into MedPy, arbitrary dimensionality - References ---------- .. [1] P. Perona and J. Malik. Scale-space and edge detection using ansotropic diffusion. IEEE Transactions on Pattern Analysis and Machine Intelligence, 12(7):629-639, July 1990. .. [2] M.J. Black, G. Sapiro, D. Marimont, D. Heeger Robust anisotropic diffusion. IEEE Transactions on Image Processing, 7(3):421-432, March 1998. """ # define conduction gradients functions if option == 1: def condgradient(delta, spacing): return numpy.exp(-(delta/kappa)**2.)/float(spacing) elif option == 2: def condgradient(delta, spacing): return 1./(1.+(delta/kappa)**2.)/float(spacing) elif option == 3: kappa_s = kappa * (2**0.5) def condgradient(delta, spacing): top = 0.5*((1.-(delta/kappa_s)**2.)**2.)/float(spacing) return numpy.where(numpy.abs(delta) <= kappa_s, top, 0) # initialize output array out = numpy.array(img, dtype=numpy.float32, copy=True) # set default voxel spacing if not supplied if voxelspacing is None: voxelspacing = tuple([1.] * img.ndim) # initialize some internal variables deltas = [numpy.zeros_like(out) for _ in range(out.ndim)] for _ in range(niter): # calculate the diffs for i in range(out.ndim): slicer = [slice(None, -1) if j == i else slice(None) for j in range(out.ndim)] deltas[i][slicer] = numpy.diff(out, axis=i) # update matrices matrices = [condgradient(delta, spacing) * delta for delta, spacing in zip(deltas, voxelspacing)] # subtract a copy that has been shifted ('Up/North/West' in 3D case) by one # pixel. Don't as questions. just do it. trust me. for i in range(out.ndim): slicer = [slice(1, None) if j == i else slice(None) for j in range(out.ndim)] matrices[i][slicer] = numpy.diff(matrices[i], axis=i) # update the image out += gamma * (numpy.sum(matrices, axis=0)) return out