From 3c84888f72982d57318d7c0b450aac3acdad7e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Thu, 21 Nov 2013 23:37:06 +0100 Subject: [PATCH 01/25] Remove duplicate greycomatrix function definitions --- skimage/feature/_greycomatrix.py | 226 ------------------------------- 1 file changed, 226 deletions(-) delete mode 100644 skimage/feature/_greycomatrix.py diff --git a/skimage/feature/_greycomatrix.py b/skimage/feature/_greycomatrix.py deleted file mode 100644 index 45476d33..00000000 --- a/skimage/feature/_greycomatrix.py +++ /dev/null @@ -1,226 +0,0 @@ -""" -Compute grey level co-occurrence matrices (GLCMs) and associated -properties to characterize image textures. -""" - -import numpy as np - -from ._texture import _glcm_loop - - -def greycomatrix(image, distances, angles, levels=256, symmetric=False, - normed=False): - """Calculate the grey-level co-occurrence matrix. - - A grey level co-occurence matrix is a histogram of co-occuring - greyscale values at a given offset over an image. - - Parameters - ---------- - image : array_like of uint8 - Integer typed input image. The image will be cast to uint8, so - the maximum value must be less than 256. - distances : array_like - List of pixel pair distance offsets. - angles : array_like - List of pixel pair angles in radians. - levels : int, optional - The input image should contain integers in [0, levels-1], - where levels indicate the number of grey-levels counted - (typically 256 for an 8-bit image). The maximum value is - 256. - symmetric : bool, optional - If True, the output matrix `P[:, :, d, theta]` is symmetric. This - is accomplished by ignoring the order of value pairs, so both - (i, j) and (j, i) are accumulated when (i, j) is encountered - for a given offset. The default is False. - normed : bool, optional - If True, normalize each matrix `P[:, :, d, theta]` by dividing - by the total number of accumulated co-occurrences for the given - offset. The elements of the resulting matrix sum to 1. The - default is False. - - Returns - ------- - P : 4-D ndarray - The grey-level co-occurrence histogram. The value - `P[i,j,d,theta]` is the number of times that grey-level `j` - occurs at a distance `d` and at an angle `theta` from - grey-level `i`. If `normed` is `False`, the output is of - type uint32, otherwise it is float64. - - References - ---------- - .. [1] The GLCM Tutorial Home Page, - http://www.fp.ucalgary.ca/mhallbey/tutorial.htm - .. [2] Pattern Recognition Engineering, Morton Nadler & Eric P. - Smith - .. [3] Wikipedia, http://en.wikipedia.org/wiki/Co-occurrence_matrix - - - Examples - -------- - Compute 2 GLCMs: One for a 1-pixel offset to the right, and one - for a 1-pixel offset upwards. - - >>> image = np.array([[0, 0, 1, 1], - ... [0, 0, 1, 1], - ... [0, 2, 2, 2], - ... [2, 2, 3, 3]], dtype=np.uint8) - >>> result = greycomatrix(image, [1], [0, np.pi/2], levels=4) - >>> result[:, :, 0, 0] - array([[2, 2, 1, 0], - [0, 2, 0, 0], - [0, 0, 3, 1], - [0, 0, 0, 1]], dtype=uint32) - >>> result[:, :, 0, 1] - array([[3, 0, 2, 0], - [0, 2, 2, 0], - [0, 0, 1, 2], - [0, 0, 0, 0]], dtype=uint32) - - """ - - assert levels <= 256 - image = np.ascontiguousarray(image) - assert image.ndim == 2 - assert image.min() >= 0 - assert image.max() < levels - image = image.astype(np.uint8) - distances = np.ascontiguousarray(distances, dtype=np.float64) - angles = np.ascontiguousarray(angles, dtype=np.float64) - assert distances.ndim == 1 - assert angles.ndim == 1 - - P = np.zeros((levels, levels, len(distances), len(angles)), - dtype=np.uint32, order='C') - - # count co-occurences - _glcm_loop(image, distances, angles, levels, P) - - # make each GLMC symmetric - if symmetric: - Pt = np.transpose(P, (1, 0, 2, 3)) - P = P + Pt - - # normalize each GLMC - if normed: - P = P.astype(np.float64) - glcm_sums = np.apply_over_axes(np.sum, P, axes=(0, 1)) - glcm_sums[glcm_sums == 0] = 1 - P /= glcm_sums - - return P - - -def greycoprops(P, prop='contrast'): - """Calculate texture properties of a GLCM. - - Compute a feature of a grey level co-occurrence matrix to serve as - a compact summary of the matrix. The properties are computed as - follows: - - - 'contrast': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}(i-j)^2` - - 'dissimilarity': :math:`\\sum_{i,j=0}^{levels-1}P_{i,j}|i-j|` - - 'homogeneity': :math:`\\sum_{i,j=0}^{levels-1}\\frac{P_{i,j}}{1+(i-j)^2}` - - 'ASM': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}^2` - - 'energy': :math:`\\sqrt{ASM}` - - 'correlation': - .. math:: \\sum_{i,j=0}^{levels-1} P_{i,j}\\left[\\frac{(i-\\mu_i) \\ - (j-\\mu_j)}{\\sqrt{(\\sigma_i^2)(\\sigma_j^2)}}\\right] - - - Parameters - ---------- - P : ndarray - Input array. `P` is the grey-level co-occurrence histogram - for which to compute the specified property. The value - `P[i,j,d,theta]` is the number of times that grey-level j - occurs at a distance d and at an angle theta from - grey-level i. - - prop : {'contrast', 'dissimilarity', 'homogeneity', 'energy', \ - 'correlation', 'ASM'}, optional - The property of the GLCM to compute. The default is 'contrast'. - - Returns - ------- - results : 2-D ndarray - 2-dimensional array. `results[d, a]` is the property 'prop' for - the d'th distance and the a'th angle. - - References - ---------- - .. [1] The GLCM Tutorial Home Page, - http://www.fp.ucalgary.ca/mhallbey/tutorial.htm - - Examples - -------- - Compute the contrast for GLCMs with distances [1, 2] and angles - [0 degrees, 90 degrees] - - >>> image = np.array([[0, 0, 1, 1], - ... [0, 0, 1, 1], - ... [0, 2, 2, 2], - ... [2, 2, 3, 3]], dtype=np.uint8) - >>> g = greycomatrix(image, [1, 2], [0, np.pi/2], levels=4, - ... normed=True, symmetric=True) - >>> contrast = greycoprops(g, 'contrast') - >>> contrast - array([[ 0.58333333, 1. ], - [ 1.25 , 2.75 ]]) - - """ - - assert P.ndim == 4 - (num_level, num_level2, num_dist, num_angle) = P.shape - assert num_level == num_level2 - assert num_dist > 0 - assert num_angle > 0 - - # create weights for specified property - I, J = np.ogrid[0:num_level, 0:num_level] - if prop == 'contrast': - weights = (I - J)**2 - elif prop == 'dissimilarity': - weights = np.abs(I - J) - elif prop == 'homogeneity': - weights = 1. / (1. + (I - J)**2) - elif prop in ['ASM', 'energy', 'correlation']: - pass - else: - raise ValueError('%s is an invalid property' % (prop)) - - # compute property for each GLCM - if prop == 'energy': - asm = np.apply_over_axes(np.sum, (P**2), axes=(0, 1))[0, 0] - results = np.sqrt(asm) - elif prop == 'ASM': - results = np.apply_over_axes(np.sum, (P**2), axes=(0, 1))[0, 0] - elif prop == 'correlation': - results = np.zeros((num_dist, num_angle), dtype=np.float64) - I = np.array(range(num_level)).reshape((num_level, 1, 1, 1)) - J = np.array(range(num_level)).reshape((1, num_level, 1, 1)) - diff_i = I - np.apply_over_axes(np.sum, (I * P), axes=(0, 1))[0, 0] - diff_j = J - np.apply_over_axes(np.sum, (J * P), axes=(0, 1))[0, 0] - - std_i = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_i)**2), - axes=(0, 1))[0, 0]) - std_j = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_j)**2), - axes=(0, 1))[0, 0]) - cov = np.apply_over_axes(np.sum, (P * (diff_i * diff_j)), - axes=(0, 1))[0, 0] - - # handle the special case of standard deviations near zero - mask_0 = std_i < 1e-15 - mask_0[std_j < 1e-15] = True - results[mask_0] = 1 - - # handle the standard case - mask_1 = mask_0 == False - results[mask_1] = cov[mask_1] / (std_i[mask_1] * std_j[mask_1]) - elif prop in ['contrast', 'dissimilarity', 'homogeneity']: - weights = weights.reshape((num_level, num_level, 1, 1)) - results = np.apply_over_axes(np.sum, (P * weights), axes=(0, 1))[0, 0] - - return results From e74db662cbba56f8c192586ca4ac34d5e5330376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Thu, 21 Nov 2013 23:58:39 +0100 Subject: [PATCH 02/25] Add test case for *_boundaries functions --- skimage/segmentation/tests/test_boundaries.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 skimage/segmentation/tests/test_boundaries.py diff --git a/skimage/segmentation/tests/test_boundaries.py b/skimage/segmentation/tests/test_boundaries.py new file mode 100644 index 00000000..2fff52f8 --- /dev/null +++ b/skimage/segmentation/tests/test_boundaries.py @@ -0,0 +1,59 @@ +import numpy as np +from numpy.testing import assert_array_equal +from skimage.segmentation import find_boundaries, mark_boundaries + + +def test_find_boundaries(): + image = np.zeros((10, 10)) + image[2:7, 2:7] = 1 + + ref = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + + result = find_boundaries(image) + assert_array_equal(result, ref) + + +def test_mark_boundaries(): + image = np.zeros((10, 10)) + label_image = np.zeros((10, 10)) + label_image[2:7, 2:7] = 1 + + ref = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + result = mark_boundaries(image, label_image, color=(1, 1, 1)).mean(axis=2) + assert_array_equal(result, ref) + + ref = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 2, 0], + [0, 0, 1, 2, 2, 2, 2, 1, 2, 0], + [0, 0, 1, 2, 0, 0, 0, 1, 2, 0], + [0, 0, 1, 2, 0, 0, 0, 1, 2, 0], + [0, 0, 1, 2, 0, 0, 0, 1, 2, 0], + [0, 0, 1, 1, 1, 1, 1, 2, 2, 0], + [0, 0, 2, 2, 2, 2, 2, 2, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + result = mark_boundaries(image, label_image, color=(1, 1, 1), + outline_color=(2, 2, 2)).mean(axis=2) + assert_array_equal(result, ref) + + +if __name__ == "__main__": + np.testing.run_module_suite() From a2cde5ebb07f839b8191d6988aa680bcae3360fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 00:17:59 +0100 Subject: [PATCH 03/25] Test invalid inplace operation --- skimage/morphology/tests/test_grey.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/skimage/morphology/tests/test_grey.py b/skimage/morphology/tests/test_grey.py index e2a3928d..f0099ee5 100644 --- a/skimage/morphology/tests/test_grey.py +++ b/skimage/morphology/tests/test_grey.py @@ -155,5 +155,15 @@ class TestDTypes(): self._test_image(image) +def test_inplace(): + selem = np.ones((3, 3)) + image = np.zeros((5, 5)) + out = image + + for f in (grey.erosion, grey.dilation, + grey.white_tophat, grey.black_tophat): + testing.assert_raises(NotImplementedError, f, image, selem, out=out) + + if __name__ == '__main__': testing.run_module_suite() From 4d9ecf33f612be3be8efc24cc704db3e3cb33b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 00:22:47 +0100 Subject: [PATCH 04/25] Add missing deprecation removal to todo --- TODO.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.txt b/TODO.txt index b867bc73..07cb5777 100644 --- a/TODO.txt +++ b/TODO.txt @@ -14,3 +14,4 @@ Version 0.10 * Remove deprecated `skimage.color.is_gray` and `skimage.color.is_rgb` functions * Enable doctests of experimental `skimage.feature.brief` +* Remove deprecated `skimage.segmentation.visualize_boundaries` From de9e0fdfb36d912e0a6fda2c9c0a430d206fbfa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 00:51:55 +0100 Subject: [PATCH 05/25] Add missing deprecation removal to todo --- TODO.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.txt b/TODO.txt index 07cb5777..25070d05 100644 --- a/TODO.txt +++ b/TODO.txt @@ -15,3 +15,4 @@ Version 0.10 functions * Enable doctests of experimental `skimage.feature.brief` * Remove deprecated `skimage.segmentation.visualize_boundaries` +* Remove deprecated `skimage.morphology.greyscale_*` From 06a9c5da8a3206316b1b8e6941a6bac784e16a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:34:32 +0100 Subject: [PATCH 06/25] Add test case for union of heterogenous tform types --- skimage/transform/tests/test_geometric.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index c7f9f832..aa968380 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -169,11 +169,16 @@ def test_union(): tform1 = SimilarityTransform(scale=0.1, rotation=0.3) tform2 = SimilarityTransform(scale=0.1, rotation=0.9) tform3 = SimilarityTransform(scale=0.1 ** 2, rotation=0.3 + 0.9) - tform = tform1 + tform2 - assert_array_almost_equal(tform._matrix, tform3._matrix) + tform1 = AffineTransform(scale=(0.1, 0.1), rotation=0.3) + tform2 = SimilarityTransform(scale=0.1, rotation=0.9) + tform3 = SimilarityTransform(scale=0.1 ** 2, rotation=0.3 + 0.9) + tform = tform1 + tform2 + assert_array_almost_equal(tform._matrix, tform3._matrix) + assert tform.__class__ == ProjectiveTransform + if __name__ == "__main__": from numpy.testing import run_module_suite From 0fb48a6153c1523d27da17c40ad38de61f4f3098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:38:25 +0100 Subject: [PATCH 07/25] Add test cases for abstract geometric base class --- skimage/transform/tests/test_geometric.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index aa968380..125abe06 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -1,6 +1,8 @@ import numpy as np -from numpy.testing import assert_equal, assert_array_almost_equal +from numpy.testing import (assert_equal, assert_array_almost_equal, + assert_raises) from skimage.transform._geometric import _stackcopy +from skimage.transform._geometric import GeometricTransform from skimage.transform import (estimate_transform, SimilarityTransform, AffineTransform, ProjectiveTransform, PolynomialTransform, @@ -180,6 +182,13 @@ def test_union(): assert tform.__class__ == ProjectiveTransform +def test_geometric_tform(): + tform = GeometricTransform() + assert_raises(NotImplementedError, tform, 0) + assert_raises(NotImplementedError, tform.inverse, 0) + assert_raises(NotImplementedError, tform.__add__, 0) + + if __name__ == "__main__": from numpy.testing import run_module_suite run_module_suite() From 4ba9eaa98be4552d6c39eb95875e61891b01c5e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:42:41 +0100 Subject: [PATCH 08/25] Test invalid input --- skimage/transform/tests/test_geometric.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 125abe06..0af70fdd 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -189,6 +189,17 @@ def test_geometric_tform(): assert_raises(NotImplementedError, tform.__add__, 0) +def test_invalid_input(): + assert_raises(ValueError, ProjectiveTransform, np.zeros((2, 3))) + assert_raises(ValueError, AffineTransform, np.zeros((2, 3))) + assert_raises(ValueError, SimilarityTransform, np.zeros((2, 3))) + + assert_raises(ValueError, AffineTransform, + matrix=np.zeros((2, 3)), scale=1) + assert_raises(ValueError, SimilarityTransform, + matrix=np.zeros((2, 3)), scale=1) + + if __name__ == "__main__": from numpy.testing import run_module_suite run_module_suite() From bd240d9f92d0c5caced4b330fb9b169f8ce91b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:43:08 +0100 Subject: [PATCH 09/25] Test scale determination for 0 rotation --- skimage/transform/tests/test_geometric.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 0af70fdd..4315029d 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -76,6 +76,16 @@ def test_similarity_init(): assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.translation, translation) + # test special case for scale if rotation=0 + scale = 0.1 + rotation = 0 + 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 From 62f51e35a1fedb4bc44165892aae9e528d909214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:45:51 +0100 Subject: [PATCH 10/25] Add missing test coverage of polynomial tform --- 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 4315029d..73d3a0c4 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -177,6 +177,10 @@ def test_polynomial_default_order(): assert_array_almost_equal(tform2._params, tform._params) +def test_polynomial_inverse(): + assert_raises(Exception, PolynomialTransform().inverse, 0) + + def test_union(): tform1 = SimilarityTransform(scale=0.1, rotation=0.3) tform2 = SimilarityTransform(scale=0.1, rotation=0.9) @@ -209,6 +213,8 @@ def test_invalid_input(): assert_raises(ValueError, SimilarityTransform, matrix=np.zeros((2, 3)), scale=1) + assert_raises(ValueError, PolynomialTransform, np.zeros((3, 3))) + if __name__ == "__main__": from numpy.testing import run_module_suite From e159fa0e9d29cbc5e68dfc7b09ef95932c49902a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:48:32 +0100 Subject: [PATCH 11/25] Add test case for estimate_transform --- skimage/transform/tests/test_geometric.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 73d3a0c4..774d0888 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -40,6 +40,13 @@ def test_stackcopy(): assert_array_almost_equal(x[..., i], y) +def test_estimate_transform(): + for tform in ('similarity', 'affine', 'projective', 'polynomial'): + estimate_transform(tform, SRC[:2, :], DST[:2, :]) + assert_raises(ValueError, estimate_transform, 'foobar', + SRC[:2, :], DST[:2, :]) + + def test_similarity_estimation(): # exact solution tform = estimate_transform('similarity', SRC[:2, :], DST[:2, :]) From da42c46ab42010a0379fa42096b1eb1f88394ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:51:15 +0100 Subject: [PATCH 12/25] Add matrix_transform function to namespace --- skimage/transform/__init__.py | 2 ++ skimage/transform/tests/test_geometric.py | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/skimage/transform/__init__.py b/skimage/transform/__init__.py index 4f00a076..c6408309 100644 --- a/skimage/transform/__init__.py +++ b/skimage/transform/__init__.py @@ -5,6 +5,7 @@ from .radon_transform import radon, iradon, iradon_sart from .finite_radon_transform import frt2, ifrt2 from .integral import integral_image, integrate from ._geometric import (warp, warp_coords, estimate_transform, + matrix_transform, SimilarityTransform, AffineTransform, ProjectiveTransform, PolynomialTransform, PiecewiseAffineTransform) @@ -30,6 +31,7 @@ __all__ = ['hough_circle', 'warp', 'warp_coords', 'estimate_transform', + 'matrix_transform', 'SimilarityTransform', 'AffineTransform', 'ProjectiveTransform', diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 774d0888..06c4f652 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -3,7 +3,7 @@ from numpy.testing import (assert_equal, assert_array_almost_equal, assert_raises) from skimage.transform._geometric import _stackcopy from skimage.transform._geometric import GeometricTransform -from skimage.transform import (estimate_transform, +from skimage.transform import (estimate_transform, matrix_transform, SimilarityTransform, AffineTransform, ProjectiveTransform, PolynomialTransform, PiecewiseAffineTransform) @@ -47,6 +47,11 @@ def test_estimate_transform(): SRC[:2, :], DST[:2, :]) +def test_matrix_transform(): + tform = AffineTransform(scale=(0.1, 0.5), rotation=2) + assert_equal(tform(SRC), matrix_transform(SRC, tform._matrix)) + + def test_similarity_estimation(): # exact solution tform = estimate_transform('similarity', SRC[:2, :], DST[:2, :]) From d659cdc6b0df54838e8456a5cd90bdf8f1ec69b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:55:18 +0100 Subject: [PATCH 13/25] Deprecate reverse_map parameter and add to future removal list --- TODO.txt | 5 +++++ skimage/transform/_geometric.py | 2 ++ 2 files changed, 7 insertions(+) diff --git a/TODO.txt b/TODO.txt index 25070d05..742f853e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,8 @@ +Version 0.11 +------------ +* Remove deprecated `reverse_map` parameter of `skimage.transform.warp` + + Version 0.10 ------------ * Remove deprecated functions in `skimage.filter.rank.*` diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index eb89460e..b55078ef 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -1021,6 +1021,8 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, """ # Backward API compatibility if reverse_map is not None: + warnings.warn('`reverse_map` parameter is deprecated and replaced by ' + 'the `inverse_map` parameter.') inverse_map = reverse_map if image.ndim < 2: From 396b686bab9addf2a0ea09b6d7dde69ee8bf3565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 11:59:07 +0100 Subject: [PATCH 14/25] Add test case for invalid image dimensions --- skimage/transform/_geometric.py | 4 ++-- skimage/transform/tests/test_warps.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index b55078ef..af193173 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -1025,8 +1025,8 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, 'the `inverse_map` parameter.') inverse_map = reverse_map - if image.ndim < 2: - raise ValueError("Input must have more than 1 dimension.") + if image.ndim < 2 or image.ndim > 3: + raise ValueError("Input must have 2 or 3 dimensions.") orig_ndim = image.ndim image = np.atleast_3d(img_as_float(image)) diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py index 7f7ef47d..760d4696 100644 --- a/skimage/transform/tests/test_warps.py +++ b/skimage/transform/tests/test_warps.py @@ -1,4 +1,5 @@ -from numpy.testing import assert_array_almost_equal, run_module_suite, assert_array_equal +from numpy.testing import (assert_array_almost_equal, run_module_suite, + assert_array_equal, assert_raises) import numpy as np from scipy.ndimage import map_coordinates @@ -234,5 +235,10 @@ def test_downscale_local_mean(): assert_array_equal(expected2, out2) +def test_invalid(): + assert_raises(ValueError, warp, np.ones((4, )), SimilarityTransform()) + assert_raises(ValueError, warp, np.ones((4, 3, 3, 3)), SimilarityTransform()) + + if __name__ == "__main__": run_module_suite() From c53e98a3c62c6278f40c2c22b83a08b39f80e40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:07:36 +0100 Subject: [PATCH 15/25] Fix bug in inverse warping and add test case --- skimage/transform/_geometric.py | 4 ++-- skimage/transform/tests/test_warps.py | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index af193173..a4587c10 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -1051,8 +1051,8 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, # inverse_map is the inverse of a homography elif (hasattr(inverse_map, '__name__') and inverse_map.__name__ == 'inverse' - and isinstance(get_bound_method_class(inverse_map), - HOMOGRAPHY_TRANSFORMS)): + and get_bound_method_class(inverse_map) \ + in HOMOGRAPHY_TRANSFORMS): matrix = np.linalg.inv(six.get_method_self(inverse_map)._matrix) if matrix is not None: diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py index 760d4696..db936a3a 100644 --- a/skimage/transform/tests/test_warps.py +++ b/skimage/transform/tests/test_warps.py @@ -237,7 +237,15 @@ def test_downscale_local_mean(): def test_invalid(): assert_raises(ValueError, warp, np.ones((4, )), SimilarityTransform()) - assert_raises(ValueError, warp, np.ones((4, 3, 3, 3)), SimilarityTransform()) + assert_raises(ValueError, warp, np.ones((4, 3, 3, 3)), + SimilarityTransform()) + + +def test_inverse(): + tform = SimilarityTransform(scale=0.5, rotation=0.1) + inverse_tform = SimilarityTransform(matrix=np.linalg.inv(tform._matrix)) + image = np.arange(10 * 10).reshape(10, 10).astype(np.double) + assert_array_equal(warp(image, inverse_tform), warp(image, tform.inverse)) if __name__ == "__main__": From 2e8a36a28388e53037ee6f68fb0aa240071e5c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:11:47 +0100 Subject: [PATCH 16/25] Rename test to enable hidden line model test --- skimage/measure/tests/test_fit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/measure/tests/test_fit.py b/skimage/measure/tests/test_fit.py index bde1fcc7..62c543f4 100644 --- a/skimage/measure/tests/test_fit.py +++ b/skimage/measure/tests/test_fit.py @@ -127,7 +127,7 @@ def test_ellipse_model_estimate(): assert_almost_equal(model0._params, model_est._params, 0) -def test_line_model_residuals(): +def test_ellipse_model_residuals(): model = EllipseModel() # vertical line through origin model._params = (0, 0, 10, 5, 0) From 6e6505ecb6caf97ea3bea4d05734538c14355b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:16:44 +0100 Subject: [PATCH 17/25] Increase test coverage of regionprops --- skimage/measure/tests/test_regionprops.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/skimage/measure/tests/test_regionprops.py b/skimage/measure/tests/test_regionprops.py index 5a4dd117..42926499 100644 --- a/skimage/measure/tests/test_regionprops.py +++ b/skimage/measure/tests/test_regionprops.py @@ -345,6 +345,9 @@ def test_old_dict_interface(): np.array([list(props.values()) for props in feats], np.float) assert_equal(len(feats[0]), 8) + def assign(): + feats[0]['Area'] = 0 + assert_raises(RuntimeError, assign) def test_label_sequence(): @@ -361,6 +364,13 @@ def test_pure_background(): assert len(ps) == 0 +def test_invalid(): + ps = regionprops(SAMPLE) + def get_intensity_image(): + ps[0].intensity_image + assert_raises(AttributeError, get_intensity_image) + + if __name__ == "__main__": from numpy.testing import run_module_suite run_module_suite() From 4033566ae6ad419f147cd8925a3b83dd710fcde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:22:10 +0100 Subject: [PATCH 18/25] Add test for invalid parameter combination --- skimage/segmentation/tests/test_slic.py | 28 +++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/skimage/segmentation/tests/test_slic.py b/skimage/segmentation/tests/test_slic.py index a4657785..15d1fe6d 100644 --- a/skimage/segmentation/tests/test_slic.py +++ b/skimage/segmentation/tests/test_slic.py @@ -1,7 +1,7 @@ import itertools as it import warnings import numpy as np -from numpy.testing import assert_equal, assert_array_equal +from numpy.testing import assert_equal, assert_raises from skimage.segmentation import slic @@ -21,10 +21,10 @@ def test_color_2d(): # we expect 4 segments assert_equal(len(np.unique(seg)), 4) assert_equal(seg.shape, img.shape[:-1]) - assert_array_equal(seg[:10, :10], 0) - assert_array_equal(seg[10:, :10], 2) - assert_array_equal(seg[:10, 10:], 1) - assert_array_equal(seg[10:, 10:], 3) + assert_equal(seg[:10, :10], 0) + assert_equal(seg[10:, :10], 2) + assert_equal(seg[:10, 10:], 1) + assert_equal(seg[10:, 10:], 3) def test_gray_2d(): @@ -41,10 +41,10 @@ def test_gray_2d(): assert_equal(len(np.unique(seg)), 4) assert_equal(seg.shape, img.shape) - assert_array_equal(seg[:10, :10], 0) - assert_array_equal(seg[10:, :10], 2) - assert_array_equal(seg[:10, 10:], 1) - assert_array_equal(seg[10:, 10:], 3) + assert_equal(seg[:10, :10], 0) + assert_equal(seg[10:, :10], 2) + assert_equal(seg[:10, 10:], 1) + assert_equal(seg[10:, 10:], 3) def test_color_3d(): @@ -65,7 +65,7 @@ def test_color_3d(): assert_equal(len(np.unique(seg)), 8) for s, c in zip(slices, range(8)): - assert_array_equal(seg[s], c) + assert_equal(seg[s], c) def test_gray_3d(): @@ -87,7 +87,7 @@ def test_gray_3d(): assert_equal(len(np.unique(seg)), 8) for s, c in zip(slices, range(8)): - assert_array_equal(seg[s], c) + assert_equal(seg[s], c) def test_list_sigma(): @@ -118,6 +118,12 @@ def test_spacing(): assert_equal(seg_spaced, result_spaced) +def test_invalid_lab_conversion(): + img = np.array([[1, 1, 1, 0, 0], + [1, 1, 0, 0, 0]], np.float) + assert_raises(ValueError, slic, img, multichannel=True, convert2lab=True) + + if __name__ == '__main__': from numpy import testing From 335a74e11f27295df79b9e1807554ce256045f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:25:06 +0100 Subject: [PATCH 19/25] Add test case for eps corner_harris method --- skimage/feature/tests/test_corner.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py index 9c07c006..25d42efa 100644 --- a/skimage/feature/tests/test_corner.py +++ b/skimage/feature/tests/test_corner.py @@ -19,7 +19,11 @@ def test_square_image(): assert len(results) == 57 # Harris - results = peak_local_max(corner_harris(im)) + results = peak_local_max(corner_harris(im, method='k')) + # interest at corner + assert len(results) == 1 + + results = peak_local_max(corner_harris(im, method='eps')) # interest at corner assert len(results) == 1 @@ -41,7 +45,9 @@ def test_noisy_square_image(): assert results.any() # Harris - results = peak_local_max(corner_harris(im, sigma=1.5)) + results = peak_local_max(corner_harris(im, sigma=1.5, method='k')) + assert len(results) == 1 + results = peak_local_max(corner_harris(im, sigma=1.5, method='eps')) assert len(results) == 1 # Shi-Tomasi From e904454c2e57afdd1203bca438d3b8cd906f937d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:26:41 +0100 Subject: [PATCH 20/25] Add test for mask output of corner_peaks --- skimage/feature/tests/test_corner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py index 25d42efa..7ff34796 100644 --- a/skimage/feature/tests/test_corner.py +++ b/skimage/feature/tests/test_corner.py @@ -146,6 +146,10 @@ def test_corner_peaks(): corners = corner_peaks(response, exclude_border=False, min_distance=0) assert len(corners) == 4 + corners = corner_peaks(response, exclude_border=False, min_distance=0, + indices=False) + assert np.sum(corners) == 4 + def test_blank_image_nans(): """Some of the corner detectors had a weakness in terms of returning From 8c734eaccb58dd65c9011fdeb54c7c679140683f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:28:31 +0100 Subject: [PATCH 21/25] Add missing removal of deprecated function to todo --- TODO.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.txt b/TODO.txt index 742f853e..4fe8e90d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -21,3 +21,4 @@ Version 0.10 * Enable doctests of experimental `skimage.feature.brief` * Remove deprecated `skimage.segmentation.visualize_boundaries` * Remove deprecated `skimage.morphology.greyscale_*` +* Remove deprecated `skimage.exposure.equalize` From 0a2ed352538e1cb96c7cb39d69ecbbe6a71da854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:30:49 +0100 Subject: [PATCH 22/25] Add test for negative input image --- skimage/exposure/tests/test_exposure.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/skimage/exposure/tests/test_exposure.py b/skimage/exposure/tests/test_exposure.py index 2b696b32..f952d158 100644 --- a/skimage/exposure/tests/test_exposure.py +++ b/skimage/exposure/tests/test_exposure.py @@ -2,7 +2,7 @@ import warnings import numpy as np from numpy.testing import assert_array_almost_equal as assert_close -from numpy.testing import assert_array_equal +from numpy.testing import assert_array_equal, assert_raises import skimage from skimage import data from skimage import exposure @@ -336,3 +336,8 @@ def test_adjust_inv_sigmoid_cutoff_half(): result = exposure.adjust_sigmoid(image, 0.5, 10, True) assert_array_equal(result, expected) + + +def test_neggative(): + image = np.arange(-10, 245, 4).reshape(8, 8).astype(np.double) + assert_raises(ValueError, exposure.adjust_gamma, image) From 5b3c21a4d42b024edb4e28ef5c8d35262fed7499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:33:21 +0100 Subject: [PATCH 23/25] Add test for neggative gamma parameter --- skimage/exposure/exposure.py | 2 +- skimage/exposure/tests/test_exposure.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/skimage/exposure/exposure.py b/skimage/exposure/exposure.py index f703f846..31d9a57f 100644 --- a/skimage/exposure/exposure.py +++ b/skimage/exposure/exposure.py @@ -263,7 +263,7 @@ def adjust_gamma(image, gamma=1, gain=1): dtype = image.dtype.type if gamma < 0: - return "Gamma should be a non-negative real number" + raise ValueError("Gamma should be a non-negative real number.") scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0]) diff --git a/skimage/exposure/tests/test_exposure.py b/skimage/exposure/tests/test_exposure.py index f952d158..e6610874 100644 --- a/skimage/exposure/tests/test_exposure.py +++ b/skimage/exposure/tests/test_exposure.py @@ -230,6 +230,11 @@ def test_adjust_gamma_greater_one(): assert_array_equal(result, expected) +def test_adjust_gamma_neggative(): + image = np.arange(0, 255, 4, np.uint8).reshape(8,8) + assert_raises(ValueError, exposure.adjust_gamma, image, -1) + + # Test Logarithmic Correction # =========================== From 43f6ae9a2c948cd4fa16ad7556c6277eb3048dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 12:46:41 +0100 Subject: [PATCH 24/25] Add test case for explicit use of radii and sigmas parameters --- skimage/feature/_daisy.py | 8 ++++---- skimage/feature/tests/test_daisy.py | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/skimage/feature/_daisy.py b/skimage/feature/_daisy.py index 1a97de8f..3a55faf1 100644 --- a/skimage/feature/_daisy.py +++ b/skimage/feature/_daisy.py @@ -94,15 +94,15 @@ def daisy(img, step=4, radius=15, rings=3, histograms=8, orientations=8, ''' # Validate image format. - if img.ndim > 2: + if img.ndim != 2: raise ValueError('Only grey-level images are supported.') - if img.dtype.kind != 'f': - img = img_as_float(img) + + img = img_as_float(img) # Validate parameters. if sigmas is not None and ring_radii is not None \ and len(sigmas) - 1 != len(ring_radii): - raise ValueError('len(sigmas)-1 != len(ring_radii)') + raise ValueError('`len(sigmas)-1 != len(ring_radii)`') if ring_radii is not None: rings = len(ring_radii) radius = ring_radii[-1] diff --git a/skimage/feature/tests/test_daisy.py b/skimage/feature/tests/test_daisy.py index 7f4e85d7..32a7a5df 100644 --- a/skimage/feature/tests/test_daisy.py +++ b/skimage/feature/tests/test_daisy.py @@ -45,8 +45,15 @@ def test_descs_shape(): assert(descs.shape[1] == ceil((img.shape[1] - radius * 2) / float(step))) +def test_daisy_sigmas_and_radii(): + img = img_as_float(data.lena()[:64, :64].mean(axis=2)) + sigmas = [1, 2, 3] + radii = [1, 2] + daisy(img, sigmas=sigmas, ring_radii=radii) + + def test_daisy_incompatible_sigmas_and_radii(): - img = img_as_float(data.lena()[:128, :128].mean(axis=2)) + img = img_as_float(data.lena()[:64, :64].mean(axis=2)) sigmas = [1, 2] radii = [1, 2] assert_raises(ValueError, daisy, img, sigmas=sigmas, ring_radii=radii) From 4d6308c8108a2175d5b03708b4fc0120a33414d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 22 Nov 2013 13:28:53 +0100 Subject: [PATCH 25/25] Fix error in line model test --- skimage/measure/tests/test_fit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/measure/tests/test_fit.py b/skimage/measure/tests/test_fit.py index 62c543f4..253ac0ea 100644 --- a/skimage/measure/tests/test_fit.py +++ b/skimage/measure/tests/test_fit.py @@ -44,7 +44,7 @@ def test_line_model_residuals(): assert_equal(abs(model.residuals(np.array([[10, 0]]))), 10) model._params = (5, np.pi / 4) assert_equal(abs(model.residuals(np.array([[0, 0]]))), 5) - assert_equal(abs(model.residuals(np.array([[np.sqrt(50), 0]]))), 5) + assert_almost_equal(abs(model.residuals(np.array([[np.sqrt(50), 0]]))), 0) def test_line_model_under_determined():