From 340d92c7117d69b0ea340e7eea17b11344f47926 Mon Sep 17 00:00:00 2001 From: Zach Pincus Date: Mon, 9 May 2011 10:02:42 -0400 Subject: [PATCH 1/6] Fix memory leak due to not properly using __cinit__ in heap classes --- scikits/image/graph/heap.pyx | 38 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/scikits/image/graph/heap.pyx b/scikits/image/graph/heap.pyx index 59822a26..1673d5fa 100644 --- a/scikits/image/graph/heap.pyx +++ b/scikits/image/graph/heap.pyx @@ -127,17 +127,7 @@ cdef class BinaryHeap: # # To calculate the capacity at a certain level: # 2**l - - - def __init__(self, int initial_capcity=128): - """__init__(initial_capacity=128) - - Class constructor. - - Takes an optional parameter 'initial_capacity' so that - if the required heap capacity is known or can be estimated in advance, - there will need to be fewer resize operations on the heap.""" - + def __cinit__(self, int initial_capcity=128, *args, **kws): # calc levels from the default capacity cdef int levels = 0 while 2**levels < initial_capcity: @@ -150,10 +140,19 @@ cdef class BinaryHeap: # allocate arrays cdef int number = 2**self.levels - cdef VALUE_T *values - values = self._values = malloc( 2*number * sizeof(VALUE_T)) + self._values = malloc( 2*number * sizeof(VALUE_T)) self._references = malloc(number * sizeof(REFERENCE_T)) + def __init__(self, int initial_capcity=128): + """__init__(initial_capacity=128) + + Class constructor. + + Takes an optional parameter 'initial_capacity' so that + if the required heap capacity is known or can be estimated in advance, + there will need to be fewer resize operations on the heap.""" + if self._values is NULL or self._references is NULL: + raise MemoryError() self.reset() def reset(self): @@ -512,19 +511,22 @@ cdef class FastUpdateBinaryHeap(BinaryHeap): is lower than the current value in the heap. This is again useful for pathfinding algorithms. """ + def __cinit__(self, int initial_capacity=128, max_reference=None): + if max_reference is None: + max_reference = initial_capacity - 1 + self.max_reference = max_reference + self._crossref = malloc((max_reference+1) * \ + sizeof(REFERENCE_T)) + def __init__(self, int initial_capacity=128, max_reference=None): """__init__(initial_capacity=128, max_reference=None) Class constructor. """ - if max_reference is None: - max_reference = initial_capacity - 1 - self.max_reference = max_reference - self._crossref = malloc((max_reference+1) * \ - sizeof(REFERENCE_T)) # below will call self.reset BinaryHeap.__init__(self, initial_capacity) + def __dealloc__(self): if self._crossref is not NULL: free(self._crossref) From c2664f087eb28dee6b218650fc8ce80dc6fd94d2 Mon Sep 17 00:00:00 2001 From: Zach Pincus Date: Mon, 9 May 2011 11:52:35 -0400 Subject: [PATCH 2/6] Fixed (?) segfaults on 64-bit platforms This bug was caused by the fact that ctypes silently converts returned c_void_p instances into python int values, even if the restype of a function is c_void_p. Then, without explicit argtypes (which this module doesn't use, out of laziness on my part), python ints passed to functions are treated as 32-bit ints. Solution: explicitly wrap returned pointers as c_void_p, so that they'll get treated right when passed back to C. No more segfaults! I also reverted the _wrap_bitmap_bits_in_array code to what I wrote, as I can vouch for that on 64-bit, whereas the other version had a "still crashes" note... --- scikits/image/io/_plugins/freeimage_plugin.py | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/scikits/image/io/_plugins/freeimage_plugin.py b/scikits/image/io/_plugins/freeimage_plugin.py index d165a855..44133c09 100644 --- a/scikits/image/io/_plugins/freeimage_plugin.py +++ b/scikits/image/io/_plugins/freeimage_plugin.py @@ -16,7 +16,7 @@ if 'HOME' in os.environ: lib_dirs.append(os.path.join(os.environ['HOME'], 'lib')) API = { - 'FreeImage_Load': (ctypes.c_voidp, + 'FreeImage_Load': (ctypes.c_void_p, [ctypes.c_int, ctypes.c_char_p, ctypes.c_int]), 'FreeImage_GetWidth': (ctypes.c_uint, [ctypes.c_void_p]), @@ -263,10 +263,12 @@ def read_multipage(filename, flags=0): if not multibitmap: raise ValueError('Could not open %s as multi-page image.' % filename) try: + multibitmap = ctypes.c_void_p(multibitmap) pages = _FI.FreeImage_GetPageCount(multibitmap) arrays = [] for i in range(pages): bitmap = _FI.FreeImage_LockPage(multibitmap, i) + bitmap = ctypes.c_void_p(bitmap) try: arrays.append(_array_from_bitmap(bitmap)) finally: @@ -284,36 +286,28 @@ def _read_bitmap(filename, flags): bitmap = _FI.FreeImage_Load(ftype, filename, flags) if not bitmap: raise ValueError('Could not load file %s' % filename) - return bitmap - + return ctypes.c_void_p(bitmap) + def _wrap_bitmap_bits_in_array(bitmap, shape, dtype): - """Return an ndarray view on the data in a FreeImage bitmap. Only - valid for as long as the bitmap is loaded (if single page) / locked - in memory (if multipage). + """Return an ndarray view on the data in a FreeImage bitmap. Only + valid for as long as the bitmap is loaded (if single page) / locked + in memory (if multipage). - """ - pitch = _FI.FreeImage_GetPitch(bitmap) - height = shape[-1] - itemsize = dtype.itemsize + """ + pitch = _FI.FreeImage_GetPitch(bitmap) + height = shape[-1] + byte_size = height * pitch + itemsize = dtype.itemsize - if len(shape) == 3: - strides = (itemsize, shape[0]*itemsize, pitch) - else: - strides = (itemsize, pitch) - bits = _FI.FreeImage_GetBits(bitmap) - - class DummyArray: - __array_interface__ = { - 'data': (bits, False), - 'strides': strides, - 'typestr': dtype.str, - 'shape': tuple(shape), - 'version' : 3, - } - - # Still segfaulting on 64-bit machine because of illegal memory access - - return numpy.array(DummyArray(), copy=False) + if len(shape) == 3: + strides = (itemsize, shape[0]*itemsize, pitch) + else: + strides = (itemsize, pitch) + bits = _FI.FreeImage_GetBits(bitmap) + array = numpy.ndarray(shape, dtype=dtype, + buffer=(ctypes.c_char*byte_size).from_address(bits), + strides=strides) + return array def _array_from_bitmap(bitmap): """Convert a FreeImage bitmap pointer to a numpy array @@ -399,8 +393,9 @@ def write_multipage(arrays, filename, flags=0): raise ValueError('Could not open %s for writing multi-page image.' % filename) try: + multibitmap = ctypes.c_void_p(multibitmap) for array in arrays: - bitmap = _array_to_bitmap(array) + bitmap, fi_type = _array_to_bitmap(array) _FI.FreeImage_AppendPage(multibitmap, bitmap) finally: _FI.FreeImage_CloseMultiBitmap(multibitmap, flags) @@ -436,6 +431,7 @@ def _array_to_bitmap(array): try: def n(arr): # normalise to freeimage's in-memory format return arr.T[:,::-1] + bitmap = ctypes.c_void_p(bitmap) wrapped_array = _wrap_bitmap_bits_in_array(bitmap, w_shape, dtype) # swizzle the color components and flip the scanlines to go to # FreeImage's BGR[A] and upside-down internal memory format From f09c1208e74e03aa0cb6455c5be2f9d695b91fb9 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Tue, 17 May 2011 19:24:05 +0200 Subject: [PATCH 3/6] ENH: graph: Fix typo. --- scikits/image/graph/heap.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scikits/image/graph/heap.pyx b/scikits/image/graph/heap.pyx index 1673d5fa..d6bd1f43 100644 --- a/scikits/image/graph/heap.pyx +++ b/scikits/image/graph/heap.pyx @@ -127,10 +127,10 @@ cdef class BinaryHeap: # # To calculate the capacity at a certain level: # 2**l - def __cinit__(self, int initial_capcity=128, *args, **kws): + def __cinit__(self, int initial_capacity=128, *args, **kws): # calc levels from the default capacity cdef int levels = 0 - while 2**levels < initial_capcity: + while 2**levels < initial_capacity: levels += 1 # set levels self.min_levels = self.levels = levels @@ -143,7 +143,7 @@ cdef class BinaryHeap: self._values = malloc( 2*number * sizeof(VALUE_T)) self._references = malloc(number * sizeof(REFERENCE_T)) - def __init__(self, int initial_capcity=128): + def __init__(self, int initial_capacity=128): """__init__(initial_capacity=128) Class constructor. From 13d1a3d111aa6cf04e42b5c20b824fa6d20119c4 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Sat, 28 May 2011 14:55:01 +0200 Subject: [PATCH 4/6] New module for total variation denoising, for 2D and 3D arrays. --- scikits/image/filter/__init__.py | 1 + scikits/image/filter/tv_denoise.py | 313 +++++++++++++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 scikits/image/filter/tv_denoise.py diff --git a/scikits/image/filter/__init__.py b/scikits/image/filter/__init__.py index ef33468d..bec0034d 100644 --- a/scikits/image/filter/__init__.py +++ b/scikits/image/filter/__init__.py @@ -2,3 +2,4 @@ from lpi_filter import * from ctmf import median_filter from canny import canny from edges import sobel, hsobel, vsobel, hprewitt, vprewitt, prewitt +from tv_denoise import tv_denoise diff --git a/scikits/image/filter/tv_denoise.py b/scikits/image/filter/tv_denoise.py new file mode 100644 index 00000000..da5b0903 --- /dev/null +++ b/scikits/image/filter/tv_denoise.py @@ -0,0 +1,313 @@ +import numpy as np + +def _tv_denoise_3d(im, eps=2.e-4, weight=100, keep_type=False, n_iter_max=200): + """ + Perform total-variation denoising on 3-D arrays + + Parameters + ---------- + im: ndarray + 3-D input data to be denoised + + eps: float, optional + relative difference of the value of the cost function that determines + the stop criterion. The algorithm stops when + (E_(n-1) - E_n) < eps * E_0 + + weight: float, optional + denoising weight. The greater ``weight``, the more denoising (at + the expense of fidelity to ``input``) + + keep_type: bool, optional (False) + whether the output has the same dtype as the input array. + keep_type is False by default, and the dtype of the output + is np.float + + n_iter_max: int, optional + maximal number of iterations used for the optimization. + + Returns + ------- + out: ndarray + denoised array + + Notes + ----- + Rudin, Osher and Fatemi algorithm + + Examples + --------- + First build synthetic noisy data + >>> x, y, z = np.ogrid[0:40, 0:40, 0:40] + >>> mask = (x -22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 + >>> mask = mask.astype(np.float) + >>> mask += 0.2*np.random.randn(*mask.shape) + >>> res = tv_denoise_3d(mask, weight=100) + """ + im_type = im.dtype + if im_type is not np.float: + im = im.astype(np.float) + px = np.zeros_like(im) + py = np.zeros_like(im) + pz = np.zeros_like(im) + gx = np.zeros_like(im) + gy = np.zeros_like(im) + gz = np.zeros_like(im) + d = np.zeros_like(im) + i = 0 + while i < n_iter_max: + d = - px - py - pz + d[1:] += px[:-1] + d[:, 1:] += py[:, :-1] + d[:, :, 1:] += pz[:, :, :-1] + + out = im + d + E = (d**2).sum() + + gx[:-1] = np.diff(out, axis=0) + gy[:, :-1] = np.diff(out, axis=1) + gz[:, :, :-1] = np.diff(out, axis=2) + norm = np.sqrt(gx**2 + gy**2 + gz**2) + E += weight * norm.sum() + norm *= 0.5 / weight + norm += 1. + px -= 1./6.*gx + px /= norm + py -= 1./6.*gy + py /= norm + pz -= 1/6.*gz + pz /= norm + E /= float(im.size) + print E + if i == 0: + E_init = E + E_previous = E + else: + if np.abs(E_previous - E) < eps * E_init: + print E_previous, E + break + else: + E_previous = E + i += 1 + if keep_type: + return out.astype(im_type) + else: + return out + +def _tv_denoise_2d(im, weight=50, eps=2.e-4, keep_type=False, n_iter_max=200): + """ + Perform total-variation denoising + + Parameters + ---------- + im: ndarray + input data to be denoised + + eps: float, optional + relative difference of the value of the cost function that determines + the stop criterion. The algorithm stops when + (E_(n-1) - E_n) < eps * E_0 + + weight: float, optional + denoising weight. The greater ``weight``, the more denoising (at + the expense of fidelity to ``input``) + + keep_type: bool, optional (False) + whether the output has the same dtype as the input array. + keep_type is False by default, and the dtype of the output + is np.float + + n_iter_max: int, optional + maximal number of iterations used for the optimization. + + Returns + ------- + out: ndarray + denoised array + + Notes + ----- + The principle of total variation denoising is explained in + http://en.wikipedia.org/wiki/Total_variation_denoising + + This code is an implementation of the algorithm of Rudin, Fatemi and Osher + that was proposed by Chambolle in [1]_. + + References + ---------- + + .. [1] A. Chambolle, An algorithm for total variation minimization and + applications, Journal of Mathematical Imaging and Vision, + Springer, 2004, 20, 89-97. + + Examples + --------- + >>> import scipy + >>> lena = scipy.lena() + >>> import scipy + >>> lena = scipy.lena().astype(np.float) + >>> lena += 0.5 * lena.std()*np.random.randn(*lena.shape) + >>> denoised_lena = tv_denoise(lena, weight=60.0) + """ + im_type = im.dtype + if im_type is not np.float: + im = im.astype(np.float) + px = np.zeros_like(im) + py = np.zeros_like(im) + gx = np.zeros_like(im) + gy = np.zeros_like(im) + d = np.zeros_like(im) + i = 0 + while i < n_iter_max: + d = -px -py + d[1:] += px[:-1] + d[:, 1:] += py[:, :-1] + + out = im + d + E = (d**2).sum() + gx[:-1] = np.diff(out, axis=0) + gy[:, :-1] = np.diff(out, axis=1) + norm = np.sqrt(gx**2 + gy**2) + E += weight * norm.sum() + norm *= 0.5 / weight + norm += 1 + px -= 0.25*gx + px /= norm + py -= 0.25*gy + py /= norm + E /= float(im.size) + print E + if i == 0: + E_init = E + E_previous = E + else: + if np.abs(E_previous - E) < eps * E_init: + break + else: + E_previous = E + i += 1 + print i + if keep_type: + return out.astype(im_type) + else: + return out + +def tv_denoise(im, eps=2.e-4, weight=50, keep_type=False, n_iter_max=200): + """ + Perform total-variation denoising on 2-d and 3-d images + + Parameters + ---------- + im: ndarray (2d or 3d) + input data to be denoised + + eps: float, optional + relative difference of the value of the cost function that determines + the stop criterion. The algorithm stops when + (E_(n-1) - E_n) < eps * E_0 + + weight: float, optional + denoising weight. The greater ``weight``, the more denoising (at + the expense of fidelity to ``input``) + + keep_type: bool, optional (False) + whether the output has the same dtype as the input array. + keep_type is False by default, and the dtype of the output + is np.float + + n_iter_max: int, optional + maximal number of iterations used for the optimization. + + Returns + ------- + out: ndarray + denoised array + + + Notes + ----- + The principle of total variation denoising is explained in + http://en.wikipedia.org/wiki/Total_variation_denoising + + The principle of total variation denoising is to minimize the + total variation of the image, which can be roughly described as + the integral of the norm of the image gradient. Total variation + denoising tends to produce "cartoon-like" images, that is, + piecewise-constant images. + + This code is an implementation of the algorithm of Rudin, Fatemi and Osher + that was proposed by Chambolle in [1]_. + + References + ---------- + + .. [1] A. Chambolle, An algorithm for total variation minimization and + applications, Journal of Mathematical Imaging and Vision, + Springer, 2004, 20, 89-97. + + Examples + --------- + >>> import scipy + >>> # 2D example using lena + >>> lena = scipy.lena() + >>> import scipy + >>> lena = scipy.lena().astype(np.float) + >>> lena += 0.5 * lena.std()*np.random.randn(*lena.shape) + >>> denoised_lena = tv_denoise(lena, weight=60) + >>> # 3D example on synthetic data + >>> x, y, z = np.ogrid[0:40, 0:40, 0:40] + >>> mask = (x -22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 + >>> mask = mask.astype(np.float) + >>> mask += 0.2*np.random.randn(*mask.shape) + >>> res = tv_denoise_3d(mask, weight=100) + """ + + if im.ndim == 2: + return _tv_denoise_2d(im, eps, weight, keep_type, n_iter_max) + elif im.ndim == 3: + return _tv_denoise_3d(im, eps, weight, keep_type, n_iter_max) + else: + raise ValueError('only 2-d and 3-d images may be denoised with this function') + +def test_tv_denoise(): + """ + Apply the TV denoising algorithm on the lena image provided + by scipy + """ + import scipy + lena = scipy.lena().astype(np.float) + lena += 0.5 * lena.std()*np.random.randn(*lena.shape) + denoised_lena = tv_denoise(lena, weight=60.0) + assert denoised_lena.dtype in [np.float, np.float32, np.float64] + from scipy import ndimage + grad = ndimage.morphological_gradient(lena, size=((3,3))) + grad_denoised = ndimage.morphological_gradient(denoised_lena, size=((3,3))) + assert np.sqrt((grad_denoised**2).sum()) < np.sqrt((grad**2).sum()) + denoised_lena_int = tv_denoise(lena.astype(np.int32), \ + weight=60.0, keep_type=True) + assert denoised_lena_int.dtype is np.dtype('int32') + +def test_tv_denoise_3d(): + """ + Apply the TV denoising algorithm on a 3D image representing + a sphere. + """ + x, y, z = np.ogrid[0:40, 0:40, 0:40] + mask = (x -22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 + mask = 100 * mask.astype(np.float) + mask += 60 + mask += 20*np.random.randn(*mask.shape) + mask[mask < 0] = 0 + mask[mask > 255] = 255 + res = tv_denoise(mask.astype(np.uint8), weight=100, keep_type=True) + assert res.std() < mask.std() + assert res.dtype is np.dtype('uint8') + res = tv_denoise(mask.astype(np.uint8), weight=100) + assert res.std() < mask.std() + assert res.dtype is not np.dtype('uint8') + # test wrong number of dimensions + a = np.random.random((8, 8, 8, 8)) + try: + res = tv_denoise(a) + except ValueError: + pass From edbb4dcc4834898565a5c6ec6d5885685f4fe445 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Mon, 30 May 2011 22:55:38 +0200 Subject: [PATCH 5/6] Moved the tests of tv_denoise to filter/tests --- scikits/image/filter/tests/test_tv_denoise.py | 58 +++++++++++++++++++ scikits/image/filter/tv_denoise.py | 42 -------------- 2 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 scikits/image/filter/tests/test_tv_denoise.py diff --git a/scikits/image/filter/tests/test_tv_denoise.py b/scikits/image/filter/tests/test_tv_denoise.py new file mode 100644 index 00000000..90325c80 --- /dev/null +++ b/scikits/image/filter/tests/test_tv_denoise.py @@ -0,0 +1,58 @@ +import numpy as np +from numpy.testing import run_module_suite +import scikits.image.filter as F + +class TestTvDenoise(): + + def test_tv_denoise_2d(self): + """ + Apply the TV denoising algorithm on the lena image provided + by scipy + """ + import scipy + # lena image + lena = scipy.lena().astype(np.float) + # add noise to lena + lena += 0.5 * lena.std()*np.random.randn(*lena.shape) + # denoise + denoised_lena = F.tv_denoise(lena, weight=60.0) + # which dtype? + assert denoised_lena.dtype in [np.float, np.float32, np.float64] + from scipy import ndimage + grad = ndimage.morphological_gradient(lena, size=((3,3))) + grad_denoised = ndimage.morphological_gradient(denoised_lena, size=((3,3))) + # test if the total variation has decreased + assert np.sqrt((grad_denoised**2).sum()) < np.sqrt((grad**2).sum()) + denoised_lena_int = F.tv_denoise(lena.astype(np.int32), \ + weight=60.0, keep_type=True) + assert denoised_lena_int.dtype is np.dtype('int32') + + + def test_tv_denoise_3d(self): + """ + Apply the TV denoising algorithm on a 3D image representing + a sphere. + """ + x, y, z = np.ogrid[0:40, 0:40, 0:40] + mask = (x -22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 + mask = 100 * mask.astype(np.float) + mask += 60 + mask += 20*np.random.randn(*mask.shape) + mask[mask < 0] = 0 + mask[mask > 255] = 255 + res = F.tv_denoise(mask.astype(np.uint8), weight=100, keep_type=True) + assert res.std() < mask.std() + assert res.dtype is np.dtype('uint8') + res = F.tv_denoise(mask.astype(np.uint8), weight=100) + assert res.std() < mask.std() + assert res.dtype is not np.dtype('uint8') + # test wrong number of dimensions + a = np.random.random((8, 8, 8, 8)) + try: + res = F.tv_denoise(a) + except ValueError: + pass + + +if __name__ == "__main__": + run_module_suite() diff --git a/scikits/image/filter/tv_denoise.py b/scikits/image/filter/tv_denoise.py index da5b0903..3b96f9e8 100644 --- a/scikits/image/filter/tv_denoise.py +++ b/scikits/image/filter/tv_denoise.py @@ -269,45 +269,3 @@ def tv_denoise(im, eps=2.e-4, weight=50, keep_type=False, n_iter_max=200): else: raise ValueError('only 2-d and 3-d images may be denoised with this function') -def test_tv_denoise(): - """ - Apply the TV denoising algorithm on the lena image provided - by scipy - """ - import scipy - lena = scipy.lena().astype(np.float) - lena += 0.5 * lena.std()*np.random.randn(*lena.shape) - denoised_lena = tv_denoise(lena, weight=60.0) - assert denoised_lena.dtype in [np.float, np.float32, np.float64] - from scipy import ndimage - grad = ndimage.morphological_gradient(lena, size=((3,3))) - grad_denoised = ndimage.morphological_gradient(denoised_lena, size=((3,3))) - assert np.sqrt((grad_denoised**2).sum()) < np.sqrt((grad**2).sum()) - denoised_lena_int = tv_denoise(lena.astype(np.int32), \ - weight=60.0, keep_type=True) - assert denoised_lena_int.dtype is np.dtype('int32') - -def test_tv_denoise_3d(): - """ - Apply the TV denoising algorithm on a 3D image representing - a sphere. - """ - x, y, z = np.ogrid[0:40, 0:40, 0:40] - mask = (x -22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 - mask = 100 * mask.astype(np.float) - mask += 60 - mask += 20*np.random.randn(*mask.shape) - mask[mask < 0] = 0 - mask[mask > 255] = 255 - res = tv_denoise(mask.astype(np.uint8), weight=100, keep_type=True) - assert res.std() < mask.std() - assert res.dtype is np.dtype('uint8') - res = tv_denoise(mask.astype(np.uint8), weight=100) - assert res.std() < mask.std() - assert res.dtype is not np.dtype('uint8') - # test wrong number of dimensions - a = np.random.random((8, 8, 8, 8)) - try: - res = tv_denoise(a) - except ValueError: - pass From a0f7eb51cd69096d240ee104ca51702395ff17a1 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Mon, 30 May 2011 23:11:04 +0200 Subject: [PATCH 6/6] documented the input dtype in tv_denoise --- scikits/image/filter/tv_denoise.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scikits/image/filter/tv_denoise.py b/scikits/image/filter/tv_denoise.py index 3b96f9e8..0536d57d 100644 --- a/scikits/image/filter/tv_denoise.py +++ b/scikits/image/filter/tv_denoise.py @@ -198,12 +198,14 @@ def tv_denoise(im, eps=2.e-4, weight=50, keep_type=False, n_iter_max=200): Parameters ---------- - im: ndarray (2d or 3d) - input data to be denoised + im: ndarray (2d or 3d) of ints, uints or floats + input data to be denoised. `im` can be of any numeric type, + but it is cast into an ndarray of floats for the computation + of the denoised image. eps: float, optional - relative difference of the value of the cost function that determines - the stop criterion. The algorithm stops when + relative difference of the value of the cost function that + determines the stop criterion. The algorithm stops when (E_(n-1) - E_n) < eps * E_0 weight: float, optional