From 69a0255a28df3c8c75f0977c2c93d2f6d55a9012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 09:07:01 -0400 Subject: [PATCH 1/9] Test invalid input for seed --- skimage/morphology/tests/test_reconstruction.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/skimage/morphology/tests/test_reconstruction.py b/skimage/morphology/tests/test_reconstruction.py index 8e40ac67..99ef39aa 100644 --- a/skimage/morphology/tests/test_reconstruction.py +++ b/skimage/morphology/tests/test_reconstruction.py @@ -8,7 +8,8 @@ All rights reserved. Original author: Lee Kamentsky """ import numpy as np -from numpy.testing import assert_array_almost_equal as assert_close +from numpy.testing import (assert_array_almost_equal as assert_close, + assert_raises) from skimage.morphology.greyreconstruct import reconstruction @@ -77,6 +78,15 @@ def test_fill_hole(): assert_close(result, np.array([0, 3, 6, 4, 4, 4, 4, 4, 2, 0])) +def test_invalid_seed(): + seed = np.ones((5, 5)) + mask = np.ones((5, 5)) + assert_raises(ValueError, reconstruction, seed * 2, mask, + method='dilation') + assert_raises(ValueError, reconstruction, seed * 0.5, mask, + method='erosion') + + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From e4a60e4bf2096a0304e6c0c093e16ad9bc1656d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 09:08:40 -0400 Subject: [PATCH 2/9] Test invalid input for selem --- skimage/morphology/tests/test_reconstruction.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/skimage/morphology/tests/test_reconstruction.py b/skimage/morphology/tests/test_reconstruction.py index 99ef39aa..5b2763e1 100644 --- a/skimage/morphology/tests/test_reconstruction.py +++ b/skimage/morphology/tests/test_reconstruction.py @@ -87,6 +87,14 @@ def test_invalid_seed(): method='erosion') +def test_invalid_selem(): + seed = np.ones((5, 5)) + mask = np.ones((5, 5)) + selem = + assert_raises(ValueError, reconstruction, seed, mask, np.ones((2, 2))) + reconstruction(seed, mask, np.ones((3, 3))) + + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From 7b86728498cbda478e393f1fb3b63d87ec4c10dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 10:12:41 -0400 Subject: [PATCH 3/9] Add full test coverage for corner_subpix and fix bug --- skimage/feature/corner.py | 25 +++++++++++++------ skimage/feature/tests/test_corner.py | 21 +++++++++++++++- skimage/morphology/greyreconstruct.py | 4 +-- .../morphology/tests/test_reconstruction.py | 8 +++--- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index e1dd519b..3b22bbb6 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -737,8 +737,12 @@ def corner_subpix(image, corners, window_size=11, alpha=0.99): b_edge[:] = byy_y + bxy_x, bxx_x + bxy_y # estimated positions - est_dot = np.linalg.solve(N_dot, b_dot) - est_edge = np.linalg.solve(N_edge, b_edge) + try: + est_dot = np.linalg.solve(N_dot, b_dot) + est_edge = np.linalg.solve(N_edge, b_edge) + except np.linalg.LinAlgError: + corners_subpix[i, :] = np.nan, np.nan + continue # residuals ry_dot = y - est_dot[0] @@ -759,12 +763,19 @@ def corner_subpix(image, corners, window_size=11, alpha=0.99): + winy_winy * rxx_dot) var_edge = np.sum(winy_winy * ryy_edge + 2 * winx_winy * rxy_edge \ + winx_winx * rxx_edge) - # test value (F-distributed) - t = var_edge / var_dot - # 1 for edge, -1 for dot, 0 for "not classified" - corner_class = (t < t_crit_edge) - (t > t_crit_dot) - if corner_class == - 1: + # test value (F-distributed) + if var_dot < np.spacing(1) and var_edge < np.spacing(1): + t = np.nan + elif var_dot == 0: + t = np.inf + else: + t = var_edge / var_dot + + # 1 for edge, -1 for dot, 0 for "not classified" + corner_class = int(t < t_crit_edge) - int(t > t_crit_dot) + + if corner_class == -1: corners_subpix[i, :] = y0 + est_dot[0], x0 + est_dot[1] elif corner_class == 0: corners_subpix[i, :] = np.nan, np.nan diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py index 7d2af0df..1c39dd2d 100644 --- a/skimage/feature/tests/test_corner.py +++ b/skimage/feature/tests/test_corner.py @@ -188,7 +188,7 @@ def test_rotated_lena(): assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all() -def test_subpix(): +def test_subpix_edge(): img = np.zeros((50, 50)) img[:25, :25] = 255 img[25:, 25:] = 255 @@ -197,6 +197,25 @@ def test_subpix(): assert_array_equal(subpix[0], (24.5, 24.5)) +def test_subpix_dot(): + img = np.zeros((50, 50)) + img[25, 25] = 255 + corner = peak_local_max(corner_harris(img), num_peaks=1) + subpix = corner_subpix(img, corner) + assert_array_equal(subpix[0], (25, 25)) + + +def test_subpix_no_class(): + img = np.zeros((50, 50)) + subpix = corner_subpix(img, np.array([[25, 25]])) + assert_array_equal(subpix[0], (np.nan, np.nan)) + + img[25, 25] = 1e-10 + corner = peak_local_max(corner_harris(img), num_peaks=1) + subpix = corner_subpix(img, np.array([[25, 25]])) + assert_array_equal(subpix[0], (np.nan, np.nan)) + + def test_subpix_border(): img = np.zeros((50, 50)) img[1:25,1:25] = 255 diff --git a/skimage/morphology/greyreconstruct.py b/skimage/morphology/greyreconstruct.py index 3fffd28e..d302c2c0 100644 --- a/skimage/morphology/greyreconstruct.py +++ b/skimage/morphology/greyreconstruct.py @@ -131,11 +131,11 @@ def reconstruction(seed, mask, method='dilation', selem=None, offset=None): if selem is None: selem = np.ones([3] * seed.ndim, dtype=bool) else: - selem = selem.copy() + selem = selem.astype(bool, copy=True) if offset is None: if not all([d % 2 == 1 for d in selem.shape]): - ValueError("Footprint dimensions must all be odd") + raise ValueError("Footprint dimensions must all be odd") offset = np.array([d // 2 for d in selem.shape]) # Cross out the center of the selem selem[[slice(d, d + 1) for d in offset]] = False diff --git a/skimage/morphology/tests/test_reconstruction.py b/skimage/morphology/tests/test_reconstruction.py index 5b2763e1..f5678c08 100644 --- a/skimage/morphology/tests/test_reconstruction.py +++ b/skimage/morphology/tests/test_reconstruction.py @@ -90,9 +90,11 @@ def test_invalid_seed(): def test_invalid_selem(): seed = np.ones((5, 5)) mask = np.ones((5, 5)) - selem = - assert_raises(ValueError, reconstruction, seed, mask, np.ones((2, 2))) - reconstruction(seed, mask, np.ones((3, 3))) + assert_raises(ValueError, reconstruction, seed, mask, + selem=np.ones((4, 4))) + assert_raises(ValueError, reconstruction, seed, mask, + selem=np.ones((3, 4))) + reconstruction(seed, mask, selem=np.ones((3, 3))) if __name__ == '__main__': From c3e13a975436a0f5087f9738acc9b539022998b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 10:16:38 -0400 Subject: [PATCH 4/9] Test union of differing geometric transforms --- skimage/transform/tests/test_geometric.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 2a6a5c7e..1165365e 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -208,6 +208,12 @@ def test_union(): assert tform.__class__ == ProjectiveTransform +def test_union_differing_types(): + tform1 = SimilarityTransform() + tform2 = PolynomialTransform() + assert_raises(TypeError, tform1.__add__, tform2) + + def test_geometric_tform(): tform = GeometricTransform() assert_raises(NotImplementedError, tform, 0) From 02e6ad6b71f263b28b717ea596ebcc25232cc8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 10:22:55 -0400 Subject: [PATCH 5/9] Test 90deg rotation for similarity transform --- skimage/transform/_geometric.py | 4 ++-- skimage/transform/tests/test_geometric.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index 6eb0f70a..a134c9fa 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -620,9 +620,9 @@ class SimilarityTransform(ProjectiveTransform): @property def scale(self): - if math.cos(self.rotation) == 0: + if abs(math.cos(self.rotation)) < np.spacing(1): # sin(self.rotation) == 1 - scale = self.params[0, 1] + scale = self.params[1, 0] else: scale = self.params[0, 0] / math.cos(self.rotation) return scale diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 1165365e..14a9305c 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -99,6 +99,17 @@ def test_similarity_init(): assert_array_almost_equal(tform.translation, translation) + # test special case for scale if rotation=90deg + scale = 0.1 + rotation = np.pi / 2 + translation = (1, 1) + tform = SimilarityTransform(scale=scale, rotation=rotation, + translation=translation) + assert_array_almost_equal(tform.scale, scale) + assert_array_almost_equal(tform.rotation, rotation) + assert_array_almost_equal(tform.translation, translation) + + def test_affine_estimation(): # exact solution tform = estimate_transform('affine', SRC[:3, :], DST[:3, :]) From ac1b10d1cc244b260cab01e8b30f7820ae953e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 10:38:48 -0400 Subject: [PATCH 6/9] Fix docstring of skimage.filter.rank.threshold_percentile --- skimage/filter/rank/_percentile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/filter/rank/_percentile.py b/skimage/filter/rank/_percentile.py index f8ace8a4..01dd0b49 100644 --- a/skimage/filter/rank/_percentile.py +++ b/skimage/filter/rank/_percentile.py @@ -384,10 +384,10 @@ def threshold_percentile(image, selem, out=None, mask=None, shift_x=False, p0 : float in [0, ..., 1] Set the percentile value. - out : 2-D array (same dtype as input image) + Returns + ------- + out : 2-D array (same dtype as input image) Output image. - local threshold : ndarray (same dtype as input) - The result of the local threshold. """ From e8ab5559d5b3972065ae4e4403cb4b3b3f53596b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sat, 10 May 2014 10:47:52 -0400 Subject: [PATCH 7/9] Test invalid input for unwrap_phase --- skimage/restoration/tests/test_unwrap.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/skimage/restoration/tests/test_unwrap.py b/skimage/restoration/tests/test_unwrap.py index 3d548fc3..fca63724 100644 --- a/skimage/restoration/tests/test_unwrap.py +++ b/skimage/restoration/tests/test_unwrap.py @@ -137,5 +137,12 @@ def test_mask(): assert_array_almost_equal(image_unwrapped_3d[:, :, -1], image[i, -1]) +def test_invalid_input(): + assert_raises(ValueError, unwrap_phase, np.zeros([])) + assert_raises(ValueError, unwrap_phase, np.zeros((1, 1, 1, 1))) + assert_raises(ValueError, unwrap_phase, np.zeros((1, 1)), 3 * [False]) + assert_raises(ValueError, unwrap_phase, np.zeros((1, 1)), 'False') + + if __name__ == "__main__": run_module_suite() From 7ec58dcaed7729cd1d34d18d204d5c7e39996def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 11 May 2014 18:20:56 -0400 Subject: [PATCH 8/9] Comment special case when local patch around is constant --- skimage/feature/corner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index 3b22bbb6..96952a09 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -741,6 +741,7 @@ def corner_subpix(image, corners, window_size=11, alpha=0.99): est_dot = np.linalg.solve(N_dot, b_dot) est_edge = np.linalg.solve(N_edge, b_edge) except np.linalg.LinAlgError: + # if image is constant the system is singular corners_subpix[i, :] = np.nan, np.nan continue From da2ddb61233e88bde3826b5b61d39dfc38881f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 12 May 2014 14:41:09 -0400 Subject: [PATCH 9/9] Improve description of subpixel corner localization --- skimage/feature/corner.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index 96952a09..f18e1407 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -624,6 +624,13 @@ def corner_fast(image, n=12, threshold=0.15): def corner_subpix(image, corners, window_size=11, alpha=0.99): """Determine subpixel position of corners. + A statistical test decides whether the corner is defined as the + intersection of two edges or a single peak. Depending on the classification + result, the subpixel corner location is determined based on the local + covariance of the grey-values. If the significance level for either + statistical test is not sufficient, the corner cannot be classified, and + the output subpixel position is set to NaN. + Parameters ---------- image : ndarray @@ -633,7 +640,7 @@ def corner_subpix(image, corners, window_size=11, alpha=0.99): window_size : int, optional Search window size for subpixel estimation. alpha : float, optional - Significance level for point classification. + Significance level for corner classification. Returns -------