From 4697d66c85524de6255e6dc5ad0bc079f1d29d26 Mon Sep 17 00:00:00 2001 From: emmanuelle Date: Mon, 26 Jan 2015 22:06:38 +0100 Subject: [PATCH] [ENH] Removed eps parameter that was not needed. Added a test for the case when no denoising at all is performed when h is very small. Corrected a bug in looping range in the classical algorithm. --- skimage/restoration/_nl_means_denoising.pyx | 23 +++++++++------------ skimage/restoration/tests/test_denoise.py | 23 +++++++++++++++------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/skimage/restoration/_nl_means_denoising.pyx b/skimage/restoration/_nl_means_denoising.pyx index 832efa85..7d0c1fcf 100644 --- a/skimage/restoration/_nl_means_denoising.pyx +++ b/skimage/restoration/_nl_means_denoising.pyx @@ -6,8 +6,6 @@ from libc.math cimport exp ctypedef np.float32_t DTYPE_t -cdef eps = 1.e-8 - @cython.boundscheck(False) cdef inline float patch_distance_2d(DTYPE_t [:, :] p1, @@ -44,15 +42,15 @@ cdef inline float patch_distance_2d(DTYPE_t [:, :] p1, cdef float tmp_diff = p1[center, center] - p2[center, center] cdef float init = w[center, center] * tmp_diff * tmp_diff if init > 1: - return eps + return 0. cdef float distance = 0 for i in range(s): # exp of large negative numbers will be 0, so we'd better stop if distance > 5: - return eps + return 0. for j in range(s): tmp_diff = p1[i, j] - p2[i, j] - distance += w[i, j] * tmp_diff * tmp_diff + distance += (w[i, j] * tmp_diff * tmp_diff) distance = exp(- distance) return distance @@ -94,7 +92,7 @@ cdef inline float patch_distance_2drgb(DTYPE_t [:, :, :] p1, for i in range(s): # exp of large negative numbers will be 0, so we'd better stop if distance > 5: - return eps + return 0. for j in range(s): for color in range(3): tmp_diff = p1[i, j, color] - p2[i, j, color] @@ -138,7 +136,7 @@ cdef inline float patch_distance_3d(DTYPE_t [:, :, :] p1, for i in range(s): # exp of large negative numbers will be 0, so we'd better stop if distance > 5: - return eps + return 0. for j in range(s): for k in range(s): tmp_diff = p1[i, j, k] - p2[i, j, k] @@ -199,11 +197,11 @@ def _nl_means_denoising_2d(image, int s=7, int d=13, float h=0.1): x_col_end = x_col + offset + 1 # Coordinates of test pixel and patch bounds for i in range(max(- d, offset - x_row), - min(d + 1, n_row - x_row - 1)): + min(d + 1, n_row + offset - x_row)): x_row_start_i = x_row_start + i x_row_end_i = x_row_end + i for j in range(max(- d, offset - x_col), - min(d + 1, n_col - x_col - 1)): + min(d + 1, n_col + offset - x_col)): x_col_start_j = x_col_start + j x_col_end_j = x_col_end + j if n_ch == 1: @@ -213,7 +211,6 @@ def _nl_means_denoising_2d(image, int s=7, int d=13, float h=0.1): padded[x_row_start_i: x_row_end_i, x_col_start_j: x_col_end_j, 0], w, s) - else: weight = patch_distance_2drgb( padded[x_row_start: x_row_end, @@ -288,15 +285,15 @@ def _nl_means_denoising_3d(image, int s=7, weight_sum = 0 # Coordinates of test pixel and patch bounds for i in range(max(- d, offset - x_pln), - min(d + 1, n_pln - x_pln - 1)): + min(d + 1, n_pln + offset - x_pln)): x_pln_start_i = x_pln_start + i x_pln_end_i = x_pln_end + i for j in range(max(- d, offset - x_row), - min(d + 1, n_row - x_row - 1)): + min(d + 1, n_row + offset - x_row)): x_row_start_j = x_row_start + j x_row_end_j = x_row_end + j for k in range(max(- d, offset - x_col), - min(d + 1, n_col - x_col - 1)): + min(d + 1, n_col + offset - x_col)): x_col_start_k = x_col_start + k x_col_end_k = x_col_end + k weight = patch_distance_3d( diff --git a/skimage/restoration/tests/test_denoise.py b/skimage/restoration/tests/test_denoise.py index 934e10d4..e84e1c49 100644 --- a/skimage/restoration/tests/test_denoise.py +++ b/skimage/restoration/tests/test_denoise.py @@ -148,10 +148,10 @@ def test_nl_means_denoising_2d(): img = np.zeros((40, 40)) img[10:-10, 10:-10] = 1. img += 0.3*np.random.randn(*img.shape) - denoised = restoration.nl_means_denoising(img, 7, 5, 0.1, fast_mode=True) + denoised = restoration.nl_means_denoising(img, 7, 5, 0.2, fast_mode=True) # make sure noise is reduced assert img.std() > denoised.std() - denoised = restoration.nl_means_denoising(img, 7, 5, 0.1, fast_mode=False) + denoised = restoration.nl_means_denoising(img, 7, 5, 0.2, fast_mode=False) # make sure noise is reduced assert img.std() > denoised.std() @@ -162,10 +162,10 @@ def test_nl_means_denoising_2drgb(): # add some random noise img += 0.5 * img.std() * np.random.random(img.shape) img = np.clip(img, 0, 1) - denoised = restoration.nl_means_denoising(img, 7, 9, 0.08, fast_mode=True) + denoised = restoration.nl_means_denoising(img, 7, 9, 0.3, fast_mode=True) # make sure noise is reduced assert img.std() > denoised.std() - denoised = restoration.nl_means_denoising(img, 7, 9, 0.08, fast_mode=False) + denoised = restoration.nl_means_denoising(img, 7, 9, 0.3, fast_mode=False) # make sure noise is reduced assert img.std() > denoised.std() @@ -174,11 +174,11 @@ def test_nl_means_denoising_3d(): img = np.zeros((20, 20, 10)) img[5:-5, 5:-5, 3:-3] = 1. img += 0.3*np.random.randn(*img.shape) - denoised = restoration.nl_means_denoising(img, 5, 4, 0.1, fast_mode=True, + denoised = restoration.nl_means_denoising(img, 5, 4, 0.2, fast_mode=True, multichannel=False) # make sure noise is reduced assert img.std() > denoised.std() - denoised = restoration.nl_means_denoising(img, 5, 4, 0.1, fast_mode=False, + denoised = restoration.nl_means_denoising(img, 5, 4, 0.2, fast_mode=False, multichannel=False) # make sure noise is reduced assert img.std() > denoised.std() @@ -204,5 +204,16 @@ def test_nl_means_denoising_wrong_dimension(): assert_raises(NotImplementedError, restoration.nl_means_denoising, img) +def test_no_denoising_for_small_h(): + img = np.zeros((40, 40)) + img[10:-10, 10:-10] = 1. + img += 0.3*np.random.randn(*img.shape) + # very small h should result in no averaging with other patches + denoised = restoration.nl_means_denoising(img, 7, 5, 0.01, fast_mode=True) + assert np.allclose(denoised, img) + denoised = restoration.nl_means_denoising(img, 7, 5, 0.01, fast_mode=False) + assert np.allclose(denoised, img) + + if __name__ == "__main__": run_module_suite()