From c262ad2d441439ad991cc72fa8f34fbf9f15c5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 2 Dec 2013 11:17:55 +0100 Subject: [PATCH 01/10] Use public attribute for parameter values --- TODO.txt | 1 + skimage/measure/fit.py | 40 ++++++++++++++++++------------- skimage/measure/tests/test_fit.py | 38 +++++++++++++++++------------ 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/TODO.txt b/TODO.txt index bbf248be..16a3f85f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -3,6 +3,7 @@ Version 0.11 * Remove deprecated `reverse_map` parameter of `skimage.transform.warp` * Change depecrated `enforce_connectivity=False`on skimage.segmentation.slic and set it to True as default +* Remove deprecated `skimage.measure.fit.BaseModel._params` attribute Version 0.10 ------------ diff --git a/skimage/measure/fit.py b/skimage/measure/fit.py index e16cd218..ce0faf10 100644 --- a/skimage/measure/fit.py +++ b/skimage/measure/fit.py @@ -1,4 +1,5 @@ import math +import warnings import numpy as np from scipy import optimize @@ -11,7 +12,14 @@ def _check_data_dim(data, dim): class BaseModel(object): def __init__(self): - self._params = None + # keep _params for backwards compatibility + self.params_ = None + + @property + def _params(self): + warnings.warn('`_params` attribute is deprecated, ' + 'use `params_` instead.') + return self.params_ class LineModel(BaseModel): @@ -30,7 +38,7 @@ class LineModel(BaseModel): min{ sum((dist - x_i * cos(theta) + y_i * sin(theta))**2) } - The ``_params`` attribute contains the parameters in the following order:: + The ``params_`` attribute contains the parameters in the following order:: dist, theta @@ -68,7 +76,7 @@ class LineModel(BaseModel): # line always passes through mean dist = X0[0] * math.cos(theta) + X0[1] * math.sin(theta) - self._params = (dist, theta) + self.params_ = (dist, theta) def residuals(self, data): """Determine residuals of data to model. @@ -89,7 +97,7 @@ class LineModel(BaseModel): _check_data_dim(data, dim=2) - dist, theta = self._params + dist, theta = self.params_ x = data[:, 0] y = data[:, 1] @@ -114,7 +122,7 @@ class LineModel(BaseModel): """ if params is None: - params = self._params + params = self.params_ dist, theta = params return (dist - y * math.sin(theta)) / math.cos(theta) @@ -136,7 +144,7 @@ class LineModel(BaseModel): """ if params is None: - params = self._params + params = self.params_ dist, theta = params return (dist - x * math.cos(theta)) / math.sin(theta) @@ -154,7 +162,7 @@ class CircleModel(BaseModel): min{ sum((r - sqrt((x_i - xc)**2 + (y_i - yc)**2))**2) } - The ``_params`` attribute contains the parameters in the following order:: + The ``params_`` attribute contains the parameters in the following order:: xc, yc, r @@ -203,7 +211,7 @@ class CircleModel(BaseModel): params0 = (xc0, yc0, r0) params, _ = optimize.leastsq(fun, params0, Dfun=Dfun, col_deriv=True) - self._params = params + self.params_ = params def residuals(self, data): """Determine residuals of data to model. @@ -224,7 +232,7 @@ class CircleModel(BaseModel): _check_data_dim(data, dim=2) - xc, yc, r = self._params + xc, yc, r = self.params_ x = data[:, 0] y = data[:, 1] @@ -249,7 +257,7 @@ class CircleModel(BaseModel): """ if params is None: - params = self._params + params = self.params_ xc, yc, r = params x = xc + r * np.cos(t) @@ -279,7 +287,7 @@ class EllipseModel(BaseModel): Thus you have ``2 * N`` equations (x_i, y_i) for ``N + 5`` unknowns (t_i, xc, yc, a, b, theta), which gives you an effective redundancy of ``N - 5``. - The ``_params`` attribute contains the parameters in the following order:: + The ``params_`` attribute contains the parameters in the following order:: xc, yc, a, b, theta @@ -353,7 +361,7 @@ class EllipseModel(BaseModel): params, _ = optimize.leastsq(fun, params0, Dfun=Dfun, col_deriv=True) - self._params = params[:5] + self.params_ = params[:5] def residuals(self, data): """Determine residuals of data to model. @@ -374,7 +382,7 @@ class EllipseModel(BaseModel): _check_data_dim(data, dim=2) - xc, yc, a, b, theta = self._params + xc, yc, a, b, theta = self.params_ ctheta = math.cos(theta) stheta = math.sin(theta) @@ -436,7 +444,7 @@ class EllipseModel(BaseModel): """ if params is None: - params = self._params + params = self.params_ xc, yc, a, b, theta = params ct = np.cos(t) @@ -550,7 +558,7 @@ def ransac(data, model_class, min_samples, residual_threshold, >>> model = EllipseModel() >>> model.estimate(data) - >>> model._params # doctest: +SKIP + >>> model.params_ # doctest: +SKIP array([ -3.30354146e+03, -2.87791160e+03, 5.59062118e+03, 7.84365066e+00, 7.19203152e-01]) @@ -558,7 +566,7 @@ def ransac(data, model_class, min_samples, residual_threshold, Estimate ellipse model using RANSAC: >>> ransac_model, inliers = ransac(data, EllipseModel, 5, 3, max_trials=50) - >>> ransac_model._params + >>> ransac_model.params_ array([ 20.12762373, 29.73563063, 4.81499637, 10.4743584 , 0.05217117]) >>> inliers array([False, False, False, False, True, True, True, True, True, diff --git a/skimage/measure/tests/test_fit.py b/skimage/measure/tests/test_fit.py index 253ac0ea..6d191a19 100644 --- a/skimage/measure/tests/test_fit.py +++ b/skimage/measure/tests/test_fit.py @@ -10,7 +10,7 @@ def test_line_model_invalid_input(): def test_line_model_predict(): model = LineModel() - model._params = (10, 1) + model.params_ = (10, 1) x = np.arange(-10, 10) y = model.predict_y(x) assert_almost_equal(x, model.predict_x(y)) @@ -19,7 +19,7 @@ def test_line_model_predict(): def test_line_model_estimate(): # generate original data without noise model0 = LineModel() - model0._params = (10, 1) + model0.params_ = (10, 1) x0 = np.arange(-100, 100) y0 = model0.predict_y(x0) data0 = np.column_stack([x0, y0]) @@ -33,16 +33,16 @@ def test_line_model_estimate(): model_est.estimate(data) # test whether estimated parameters almost equal original parameters - assert_almost_equal(model0._params, model_est._params, 1) + assert_almost_equal(model0.params_, model_est.params_, 1) def test_line_model_residuals(): model = LineModel() - model._params = (0, 0) + model.params_ = (0, 0) assert_equal(abs(model.residuals(np.array([[0, 0]]))), 0) assert_equal(abs(model.residuals(np.array([[0, 10]]))), 0) assert_equal(abs(model.residuals(np.array([[10, 0]]))), 10) - model._params = (5, np.pi / 4) + model.params_ = (5, np.pi / 4) assert_equal(abs(model.residuals(np.array([[0, 0]]))), 5) assert_almost_equal(abs(model.residuals(np.array([[np.sqrt(50), 0]]))), 0) @@ -59,7 +59,7 @@ def test_circle_model_invalid_input(): def test_circle_model_predict(): model = CircleModel() r = 5 - model._params = (0, 0, r) + model.params_ = (0, 0, r) t = np.arange(0, 2 * np.pi, np.pi / 2) xy = np.array(((5, 0), (0, 5), (-5, 0), (0, -5))) @@ -69,7 +69,7 @@ def test_circle_model_predict(): def test_circle_model_estimate(): # generate original data without noise model0 = CircleModel() - model0._params = (10, 12, 3) + model0.params_ = (10, 12, 3) t = np.linspace(0, 2 * np.pi, 1000) data0 = model0.predict_xy(t) @@ -82,12 +82,12 @@ def test_circle_model_estimate(): model_est.estimate(data) # test whether estimated parameters almost equal original parameters - assert_almost_equal(model0._params, model_est._params, 1) + assert_almost_equal(model0.params_, model_est.params_, 1) def test_circle_model_residuals(): model = CircleModel() - model._params = (0, 0, 5) + model.params_ = (0, 0, 5) assert_almost_equal(abs(model.residuals(np.array([[5, 0]]))), 0) assert_almost_equal(abs(model.residuals(np.array([[6, 6]]))), np.sqrt(2 * 6**2) - 5) @@ -101,7 +101,7 @@ def test_ellipse_model_invalid_input(): def test_ellipse_model_predict(): model = EllipseModel() r = 5 - model._params = (0, 0, 5, 10, 0) + model.params_ = (0, 0, 5, 10, 0) t = np.arange(0, 2 * np.pi, np.pi / 2) xy = np.array(((5, 0), (0, 10), (-5, 0), (0, -10))) @@ -111,7 +111,7 @@ def test_ellipse_model_predict(): def test_ellipse_model_estimate(): # generate original data without noise model0 = EllipseModel() - model0._params = (10, 20, 15, 25, 0) + model0.params_ = (10, 20, 15, 25, 0) t = np.linspace(0, 2 * np.pi, 100) data0 = model0.predict_xy(t) @@ -124,13 +124,13 @@ def test_ellipse_model_estimate(): model_est.estimate(data) # test whether estimated parameters almost equal original parameters - assert_almost_equal(model0._params, model_est._params, 0) + assert_almost_equal(model0.params_, model_est.params_, 0) def test_ellipse_model_residuals(): model = EllipseModel() # vertical line through origin - model._params = (0, 0, 10, 5, 0) + model.params_ = (0, 0, 10, 5, 0) assert_almost_equal(abs(model.residuals(np.array([[10, 0]]))), 0) assert_almost_equal(abs(model.residuals(np.array([[0, 5]]))), 0) assert_almost_equal(abs(model.residuals(np.array([[0, 10]]))), 5) @@ -141,7 +141,7 @@ def test_ransac_shape(): # generate original data without noise model0 = CircleModel() - model0._params = (10, 12, 3) + model0.params_ = (10, 12, 3) t = np.linspace(0, 2 * np.pi, 1000) data0 = model0.predict_xy(t) @@ -155,7 +155,7 @@ def test_ransac_shape(): model_est, inliers = ransac(data0, CircleModel, 3, 5) # test whether estimated parameters equal original parameters - assert_equal(model0._params, model_est._params) + assert_equal(model0.params_, model_est.params_) for outlier in outliers: assert outlier not in inliers @@ -204,5 +204,13 @@ def test_ransac_is_model_valid(): assert_equal(inliers, None) +def test_deprecated_params_attribute(): + model = LineModel() + model.params_ = (10, 1) + x = np.arange(-10, 10) + y = model.predict_y(x) + assert_equal(model.params_, model._params) + + if __name__ == "__main__": np.testing.run_module_suite() From b3d62afa28ac0b95fb19a5bbe95f20dd7a47b9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 2 Dec 2013 11:50:33 +0100 Subject: [PATCH 02/10] Use public attribute for parameter values --- TODO.txt | 4 + doc/examples/applications/plot_geometric.py | 4 +- skimage/transform/_geometric.py | 101 ++++++++++++-------- skimage/transform/tests/test_geometric.py | 30 +++--- 4 files changed, 84 insertions(+), 55 deletions(-) diff --git a/TODO.txt b/TODO.txt index 16a3f85f..52699085 100644 --- a/TODO.txt +++ b/TODO.txt @@ -4,6 +4,10 @@ Version 0.11 * Change depecrated `enforce_connectivity=False`on skimage.segmentation.slic and set it to True as default * Remove deprecated `skimage.measure.fit.BaseModel._params` attribute +* Remove deprecated `skimage.measure.fit.BaseModel._params`, + `skimage.transform.ProjectiveTransform._matrx`, + `skimage.transform.PolynomialTransform._params`, + `skimage.transform.PiecewiseAffineTransform.affines_*` attributes Version 0.10 ------------ diff --git a/doc/examples/applications/plot_geometric.py b/doc/examples/applications/plot_geometric.py index 141ef51f..843faa1f 100644 --- a/doc/examples/applications/plot_geometric.py +++ b/doc/examples/applications/plot_geometric.py @@ -33,14 +33,14 @@ First we create a transformation using explicit parameters: tform = tf.SimilarityTransform(scale=1, rotation=math.pi / 2, translation=(0, 1)) -print(tform._matrix) +print(tform.matrix_) """ Alternatively you can define a transformation by the transformation matrix itself: """ -matrix = tform._matrix.copy() +matrix = tform.matrix_.copy() matrix[1, 2] = 2 tform2 = tf.SimilarityTransform(matrix) diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index a4587c10..611f9ab2 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -1,5 +1,6 @@ import six import math +import warnings import numpy as np from scipy import ndimage, spatial @@ -113,11 +114,17 @@ class ProjectiveTransform(GeometricTransform): matrix = np.eye(3) if matrix.shape != (3, 3): raise ValueError("invalid shape of transformation matrix") - self._matrix = matrix + self.matrix_ = matrix + + @property + def _matrix(self): + warnings.warn('`_matrix` attribute is deprecated, ' + 'use `matrix_` instead.') + return self.matrix_ @property def _inv_matrix(self): - return np.linalg.inv(self._matrix) + return np.linalg.inv(self.matrix_) def _apply_mat(self, coords, matrix): coords = np.array(coords, copy=False, ndmin=2) @@ -133,7 +140,7 @@ class ProjectiveTransform(GeometricTransform): return dst[:, :2] def __call__(self, coords): - return self._apply_mat(coords, self._matrix) + return self._apply_mat(coords, self.matrix_) def inverse(self, coords): """Apply inverse transformation. @@ -235,7 +242,7 @@ class ProjectiveTransform(GeometricTransform): H.flat[list(self._coeffs) + [8]] = - V[-1, :-1] / V[-1, -1] H[2, 2] = 1 - self._matrix = H + self.matrix_ = H def __add__(self, other): """Combine this transformation with another. @@ -248,7 +255,7 @@ class ProjectiveTransform(GeometricTransform): tform = self.__class__ else: tform = ProjectiveTransform - return tform(other._matrix.dot(self._matrix)) + return tform(other.matrix_.dot(self.matrix_)) else: raise TypeError("Cannot combine transformations of differing " "types.") @@ -299,7 +306,7 @@ class AffineTransform(ProjectiveTransform): elif matrix is not None: if matrix.shape != (3, 3): raise ValueError("Invalid shape of transformation matrix.") - self._matrix = matrix + self.matrix_ = matrix elif params: if scale is None: scale = (1, 1) @@ -311,34 +318,34 @@ class AffineTransform(ProjectiveTransform): translation = (0, 0) sx, sy = scale - self._matrix = np.array([ + self.matrix_ = np.array([ [sx * math.cos(rotation), -sy * math.sin(rotation + shear), 0], [sx * math.sin(rotation), sy * math.cos(rotation + shear), 0], [ 0, 0, 1] ]) - self._matrix[0:2, 2] = translation + self.matrix_[0:2, 2] = translation else: # default to an identity transform - self._matrix = np.eye(3) + self.matrix_ = np.eye(3) @property def scale(self): - sx = math.sqrt(self._matrix[0, 0] ** 2 + self._matrix[1, 0] ** 2) - sy = math.sqrt(self._matrix[0, 1] ** 2 + self._matrix[1, 1] ** 2) + sx = math.sqrt(self.matrix_[0, 0] ** 2 + self.matrix_[1, 0] ** 2) + sy = math.sqrt(self.matrix_[0, 1] ** 2 + self.matrix_[1, 1] ** 2) return sx, sy @property def rotation(self): - return math.atan2(self._matrix[1, 0], self._matrix[0, 0]) + return math.atan2(self.matrix_[1, 0], self.matrix_[0, 0]) @property def shear(self): - beta = math.atan2(- self._matrix[0, 1], self._matrix[1, 1]) + beta = math.atan2(- self.matrix_[0, 1], self.matrix_[1, 1]) return beta - self.rotation @property def translation(self): - return self._matrix[0:2, 2] + return self.matrix_[0:2, 2] class PiecewiseAffineTransform(GeometricTransform): @@ -354,8 +361,20 @@ class PiecewiseAffineTransform(GeometricTransform): def __init__(self): self._tesselation = None self._inverse_tesselation = None - self.affines = [] - self.inverse_affines = [] + self.affines_ = [] + self.inverse_affines_ = [] + + @property + def affines(self): + warnings.warn('`affines` attribute is deprecated, ' + 'use `affines_` instead.') + return self.affines_ + + @property + def inverse_affines(self): + warnings.warn('`inverse_affines` attribute is deprecated, ' + 'use `inverse_affines_` instead.') + return self.inverse_affines_ def estimate(self, src, dst): """Set the control points with which to perform the piecewise mapping. @@ -375,21 +394,21 @@ class PiecewiseAffineTransform(GeometricTransform): # triangulate input positions into mesh self._tesselation = spatial.Delaunay(src) # find affine mapping from source positions to destination - self.affines = [] + self.affines_ = [] for tri in self._tesselation.vertices: affine = AffineTransform() affine.estimate(src[tri, :], dst[tri, :]) - self.affines.append(affine) + self.affines_.append(affine) # inverse piecewise affine # triangulate input positions into mesh self._inverse_tesselation = spatial.Delaunay(dst) # find affine mapping from source positions to destination - self.inverse_affines = [] + self.inverse_affines_ = [] for tri in self._inverse_tesselation.vertices: affine = AffineTransform() affine.estimate(dst[tri, :], src[tri, :]) - self.inverse_affines.append(affine) + self.inverse_affines_.append(affine) def __call__(self, coords): """Apply forward transformation. @@ -418,7 +437,7 @@ class PiecewiseAffineTransform(GeometricTransform): for index in range(len(self._tesselation.vertices)): # affine transform for triangle - affine = self.affines[index] + affine = self.affines_[index] # all coordinates within triangle index_mask = simplex == index @@ -453,7 +472,7 @@ class PiecewiseAffineTransform(GeometricTransform): for index in range(len(self._inverse_tesselation.vertices)): # affine transform for triangle - affine = self.inverse_affines[index] + affine = self.inverse_affines_[index] # all coordinates within triangle index_mask = simplex == index @@ -501,7 +520,7 @@ class SimilarityTransform(ProjectiveTransform): elif matrix is not None: if matrix.shape != (3, 3): raise ValueError("Invalid shape of transformation matrix.") - self._matrix = matrix + self.matrix_ = matrix elif params: if scale is None: scale = 1 @@ -510,16 +529,16 @@ class SimilarityTransform(ProjectiveTransform): if translation is None: translation = (0, 0) - self._matrix = np.array([ + self.matrix_ = np.array([ [math.cos(rotation), - math.sin(rotation), 0], [math.sin(rotation), math.cos(rotation), 0], [ 0, 0, 1] ]) - self._matrix[0:2, 0:2] *= scale - self._matrix[0:2, 2] = translation + self.matrix_[0:2, 0:2] *= scale + self.matrix_[0:2, 2] = translation else: # default to an identity transform - self._matrix = np.eye(3) + self.matrix_ = np.eye(3) def estimate(self, src, dst): """Set the transformation matrix with the explicit parameters. @@ -585,7 +604,7 @@ class SimilarityTransform(ProjectiveTransform): # singular value a0, a1, b0, b1 = - V[-1, :-1] / V[-1, -1] - self._matrix = np.array([[a0, -b0, a1], + self.matrix_ = np.array([[a0, -b0, a1], [b0, a0, b1], [ 0, 0, 1]]) @@ -593,18 +612,18 @@ class SimilarityTransform(ProjectiveTransform): def scale(self): if math.cos(self.rotation) == 0: # sin(self.rotation) == 1 - scale = self._matrix[0, 1] + scale = self.matrix_[0, 1] else: - scale = self._matrix[0, 0] / math.cos(self.rotation) + scale = self.matrix_[0, 0] / math.cos(self.rotation) return scale @property def rotation(self): - return math.atan2(self._matrix[1, 0], self._matrix[1, 1]) + return math.atan2(self.matrix_[1, 0], self.matrix_[1, 1]) @property def translation(self): - return self._matrix[0:2, 2] + return self.matrix_[0:2, 2] class PolynomialTransform(GeometricTransform): @@ -627,7 +646,13 @@ class PolynomialTransform(GeometricTransform): params = np.array([[0, 1, 0], [0, 0, 1]]) if params.shape[0] != 2: raise ValueError("invalid shape of transformation parameters") - self._params = params + self.params_ = params + + @property + def _params(self): + warnings.warn('`_params` attribute is deprecated, ' + 'use `params_` instead.') + return self.params_ def estimate(self, src, dst, order=2): """Set the transformation matrix with the explicit transformation @@ -700,7 +725,7 @@ class PolynomialTransform(GeometricTransform): # singular value params = - V[-1, :-1] / V[-1, -1] - self._params = params.reshape((2, u / 2)) + self.params_ = params.reshape((2, u / 2)) def __call__(self, coords): """Apply forward transformation. @@ -718,7 +743,7 @@ class PolynomialTransform(GeometricTransform): """ x = coords[:, 0] y = coords[:, 1] - u = len(self._params.ravel()) + u = len(self.params_.ravel()) # number of coefficients -> u = (order + 1) * (order + 2) order = int((- 3 + math.sqrt(9 - 4 * (2 - u))) / 2) dst = np.zeros(coords.shape) @@ -726,8 +751,8 @@ class PolynomialTransform(GeometricTransform): pidx = 0 for j in range(order + 1): for i in range(j + 1): - dst[:, 0] += self._params[0, pidx] * x ** (j - i) * y ** i - dst[:, 1] += self._params[1, pidx] * x ** (j - i) * y ** i + dst[:, 0] += self.params_[0, pidx] * x ** (j - i) * y ** i + dst[:, 1] += self.params_[1, pidx] * x ** (j - i) * y ** i pidx += 1 return dst @@ -1046,7 +1071,7 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, # inverse_map is a homography elif isinstance(inverse_map, HOMOGRAPHY_TRANSFORMS): - matrix = inverse_map._matrix + matrix = inverse_map.matrix_ # inverse_map is the inverse of a homography elif (hasattr(inverse_map, '__name__') diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 06c4f652..e6a2fcfc 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -56,19 +56,19 @@ def test_similarity_estimation(): # exact solution tform = estimate_transform('similarity', SRC[:2, :], DST[:2, :]) assert_array_almost_equal(tform(SRC[:2, :]), DST[:2, :]) - assert_equal(tform._matrix[0, 0], tform._matrix[1, 1]) - assert_equal(tform._matrix[0, 1], - tform._matrix[1, 0]) + assert_equal(tform.matrix_[0, 0], tform.matrix_[1, 1]) + assert_equal(tform.matrix_[0, 1], - tform.matrix_[1, 0]) # over-determined tform2 = estimate_transform('similarity', SRC, DST) assert_array_almost_equal(tform2.inverse(tform2(SRC)), SRC) - assert_equal(tform2._matrix[0, 0], tform2._matrix[1, 1]) - assert_equal(tform2._matrix[0, 1], - tform2._matrix[1, 0]) + assert_equal(tform2.matrix_[0, 0], tform2.matrix_[1, 1]) + assert_equal(tform2.matrix_[0, 1], - tform2.matrix_[1, 0]) # via estimate method tform3 = SimilarityTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3._matrix, tform2._matrix) + assert_array_almost_equal(tform3.matrix_, tform2.matrix_) def test_similarity_init(): @@ -83,7 +83,7 @@ def test_similarity_init(): assert_array_almost_equal(tform.translation, translation) # init with transformation matrix - tform2 = SimilarityTransform(tform._matrix) + tform2 = SimilarityTransform(tform.matrix_) assert_array_almost_equal(tform2.scale, scale) assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.translation, translation) @@ -111,7 +111,7 @@ def test_affine_estimation(): # via estimate method tform3 = AffineTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3._matrix, tform2._matrix) + assert_array_almost_equal(tform3.matrix_, tform2.matrix_) def test_affine_init(): @@ -128,7 +128,7 @@ def test_affine_init(): assert_array_almost_equal(tform.translation, translation) # init with transformation matrix - tform2 = AffineTransform(tform._matrix) + tform2 = AffineTransform(tform.matrix_) assert_array_almost_equal(tform2.scale, scale) assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.shear, shear) @@ -155,14 +155,14 @@ def test_projective_estimation(): # via estimate method tform3 = ProjectiveTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3._matrix, tform2._matrix) + assert_array_almost_equal(tform3.matrix_, tform2.matrix_) def test_projective_init(): tform = estimate_transform('projective', SRC, DST) # init with transformation matrix - tform2 = ProjectiveTransform(tform._matrix) - assert_array_almost_equal(tform2._matrix, tform._matrix) + tform2 = ProjectiveTransform(tform.matrix_) + assert_array_almost_equal(tform2.matrix_, tform.matrix_) def test_polynomial_estimation(): @@ -173,20 +173,20 @@ def test_polynomial_estimation(): # via estimate method tform2 = PolynomialTransform() tform2.estimate(SRC, DST, order=10) - assert_array_almost_equal(tform2._params, tform._params) + assert_array_almost_equal(tform2.params_, tform.params_) def test_polynomial_init(): tform = estimate_transform('polynomial', SRC, DST, order=10) # init with transformation parameters - tform2 = PolynomialTransform(tform._params) - assert_array_almost_equal(tform2._params, tform._params) + tform2 = PolynomialTransform(tform.params_) + assert_array_almost_equal(tform2.params_, tform.params_) def test_polynomial_default_order(): tform = estimate_transform('polynomial', SRC, DST) tform2 = estimate_transform('polynomial', SRC, DST, order=2) - assert_array_almost_equal(tform2._params, tform._params) + assert_array_almost_equal(tform2.params_, tform.params_) def test_polynomial_inverse(): From 0f2076fa61e9755b12a097daae40631f2bb8482c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 2 Dec 2013 12:00:37 +0100 Subject: [PATCH 03/10] Add tests for deprecated attributes --- skimage/transform/tests/test_geometric.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index e6a2fcfc..fffc3ae9 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -228,6 +228,19 @@ def test_invalid_input(): assert_raises(ValueError, PolynomialTransform, np.zeros((3, 3))) +def test_deprecated_params_attributes(): + for t in ('projective', 'affine', 'similarity'): + tform = estimate_transform(t, SRC, DST) + assert_equal(tform._matrix, tform.matrix_) + + tform = estimate_transform('polynomial', SRC, DST, order=3) + assert_equal(tform._params, tform.params_) + + tform = estimate_transform('piecewise-affine', SRC, DST) + assert_equal(tform.affines, tform.affines_) + assert_equal(tform.inverse_affines, tform.inverse_affines_) + + if __name__ == "__main__": from numpy.testing import run_module_suite run_module_suite() From 9963a25cde23625b9e9fb79177df4015892bd569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Tue, 3 Dec 2013 11:23:20 +0100 Subject: [PATCH 04/10] Remove wrong comment location --- skimage/measure/fit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/measure/fit.py b/skimage/measure/fit.py index ce0faf10..a91784ad 100644 --- a/skimage/measure/fit.py +++ b/skimage/measure/fit.py @@ -12,7 +12,6 @@ def _check_data_dim(data, dim): class BaseModel(object): def __init__(self): - # keep _params for backwards compatibility self.params_ = None @property From c089ddfd01d128d8109b0c3bb70b244002ceba65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Tue, 3 Dec 2013 11:23:45 +0100 Subject: [PATCH 05/10] Fix typo --- TODO.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.txt b/TODO.txt index 52699085..ab5021a7 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,7 +5,7 @@ Version 0.11 and set it to True as default * Remove deprecated `skimage.measure.fit.BaseModel._params` attribute * Remove deprecated `skimage.measure.fit.BaseModel._params`, - `skimage.transform.ProjectiveTransform._matrx`, + `skimage.transform.ProjectiveTransform._matrix`, `skimage.transform.PolynomialTransform._params`, `skimage.transform.PiecewiseAffineTransform.affines_*` attributes From fc7b770ba9a7a7d4f7a40737889dd87b235559d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Tue, 3 Dec 2013 11:24:39 +0100 Subject: [PATCH 06/10] Use None as default for estimated params --- skimage/transform/_geometric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index 611f9ab2..3e4f50f8 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -361,8 +361,8 @@ class PiecewiseAffineTransform(GeometricTransform): def __init__(self): self._tesselation = None self._inverse_tesselation = None - self.affines_ = [] - self.inverse_affines_ = [] + self.affines_ = None + self.inverse_affines_ = None @property def affines(self): From c96088170b700746ef9f31c401e94f87d265372b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 19 Jan 2014 08:41:07 -0500 Subject: [PATCH 07/10] Use consistent params_ attribute across all estimators --- skimage/transform/_geometric.py | 56 +++++++++++------------ skimage/transform/tests/test_geometric.py | 24 +++++----- skimage/transform/tests/test_warps.py | 2 +- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index 3e4f50f8..bb27eb31 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -114,17 +114,17 @@ class ProjectiveTransform(GeometricTransform): matrix = np.eye(3) if matrix.shape != (3, 3): raise ValueError("invalid shape of transformation matrix") - self.matrix_ = matrix + self.params_ = matrix @property def _matrix(self): warnings.warn('`_matrix` attribute is deprecated, ' - 'use `matrix_` instead.') - return self.matrix_ + 'use `params_` instead.') + return self.params_ @property def _inv_matrix(self): - return np.linalg.inv(self.matrix_) + return np.linalg.inv(self.params_) def _apply_mat(self, coords, matrix): coords = np.array(coords, copy=False, ndmin=2) @@ -140,7 +140,7 @@ class ProjectiveTransform(GeometricTransform): return dst[:, :2] def __call__(self, coords): - return self._apply_mat(coords, self.matrix_) + return self._apply_mat(coords, self.params_) def inverse(self, coords): """Apply inverse transformation. @@ -242,7 +242,7 @@ class ProjectiveTransform(GeometricTransform): H.flat[list(self._coeffs) + [8]] = - V[-1, :-1] / V[-1, -1] H[2, 2] = 1 - self.matrix_ = H + self.params_ = H def __add__(self, other): """Combine this transformation with another. @@ -255,7 +255,7 @@ class ProjectiveTransform(GeometricTransform): tform = self.__class__ else: tform = ProjectiveTransform - return tform(other.matrix_.dot(self.matrix_)) + return tform(other.params_.dot(self.params_)) else: raise TypeError("Cannot combine transformations of differing " "types.") @@ -306,7 +306,7 @@ class AffineTransform(ProjectiveTransform): elif matrix is not None: if matrix.shape != (3, 3): raise ValueError("Invalid shape of transformation matrix.") - self.matrix_ = matrix + self.params_ = matrix elif params: if scale is None: scale = (1, 1) @@ -318,34 +318,34 @@ class AffineTransform(ProjectiveTransform): translation = (0, 0) sx, sy = scale - self.matrix_ = np.array([ + self.params_ = np.array([ [sx * math.cos(rotation), -sy * math.sin(rotation + shear), 0], [sx * math.sin(rotation), sy * math.cos(rotation + shear), 0], [ 0, 0, 1] ]) - self.matrix_[0:2, 2] = translation + self.params_[0:2, 2] = translation else: # default to an identity transform - self.matrix_ = np.eye(3) + self.params_ = np.eye(3) @property def scale(self): - sx = math.sqrt(self.matrix_[0, 0] ** 2 + self.matrix_[1, 0] ** 2) - sy = math.sqrt(self.matrix_[0, 1] ** 2 + self.matrix_[1, 1] ** 2) + sx = math.sqrt(self.params_[0, 0] ** 2 + self.params_[1, 0] ** 2) + sy = math.sqrt(self.params_[0, 1] ** 2 + self.params_[1, 1] ** 2) return sx, sy @property def rotation(self): - return math.atan2(self.matrix_[1, 0], self.matrix_[0, 0]) + return math.atan2(self.params_[1, 0], self.params_[0, 0]) @property def shear(self): - beta = math.atan2(- self.matrix_[0, 1], self.matrix_[1, 1]) + beta = math.atan2(- self.params_[0, 1], self.params_[1, 1]) return beta - self.rotation @property def translation(self): - return self.matrix_[0:2, 2] + return self.params_[0:2, 2] class PiecewiseAffineTransform(GeometricTransform): @@ -520,7 +520,7 @@ class SimilarityTransform(ProjectiveTransform): elif matrix is not None: if matrix.shape != (3, 3): raise ValueError("Invalid shape of transformation matrix.") - self.matrix_ = matrix + self.params_ = matrix elif params: if scale is None: scale = 1 @@ -529,16 +529,16 @@ class SimilarityTransform(ProjectiveTransform): if translation is None: translation = (0, 0) - self.matrix_ = np.array([ + self.params_ = np.array([ [math.cos(rotation), - math.sin(rotation), 0], [math.sin(rotation), math.cos(rotation), 0], [ 0, 0, 1] ]) - self.matrix_[0:2, 0:2] *= scale - self.matrix_[0:2, 2] = translation + self.params_[0:2, 0:2] *= scale + self.params_[0:2, 2] = translation else: # default to an identity transform - self.matrix_ = np.eye(3) + self.params_ = np.eye(3) def estimate(self, src, dst): """Set the transformation matrix with the explicit parameters. @@ -604,7 +604,7 @@ class SimilarityTransform(ProjectiveTransform): # singular value a0, a1, b0, b1 = - V[-1, :-1] / V[-1, -1] - self.matrix_ = np.array([[a0, -b0, a1], + self.params_ = np.array([[a0, -b0, a1], [b0, a0, b1], [ 0, 0, 1]]) @@ -612,18 +612,18 @@ class SimilarityTransform(ProjectiveTransform): def scale(self): if math.cos(self.rotation) == 0: # sin(self.rotation) == 1 - scale = self.matrix_[0, 1] + scale = self.params_[0, 1] else: - scale = self.matrix_[0, 0] / math.cos(self.rotation) + scale = self.params_[0, 0] / math.cos(self.rotation) return scale @property def rotation(self): - return math.atan2(self.matrix_[1, 0], self.matrix_[1, 1]) + return math.atan2(self.params_[1, 0], self.params_[1, 1]) @property def translation(self): - return self.matrix_[0:2, 2] + return self.params_[0:2, 2] class PolynomialTransform(GeometricTransform): @@ -1071,14 +1071,14 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, # inverse_map is a homography elif isinstance(inverse_map, HOMOGRAPHY_TRANSFORMS): - matrix = inverse_map.matrix_ + matrix = inverse_map.params_ # inverse_map is the inverse of a homography elif (hasattr(inverse_map, '__name__') and inverse_map.__name__ == 'inverse' and get_bound_method_class(inverse_map) \ in HOMOGRAPHY_TRANSFORMS): - matrix = np.linalg.inv(six.get_method_self(inverse_map)._matrix) + matrix = np.linalg.inv(six.get_method_self(inverse_map).params_) if matrix is not None: matrix = matrix.astype(np.double) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index fffc3ae9..ed4cd405 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -56,19 +56,19 @@ def test_similarity_estimation(): # exact solution tform = estimate_transform('similarity', SRC[:2, :], DST[:2, :]) assert_array_almost_equal(tform(SRC[:2, :]), DST[:2, :]) - assert_equal(tform.matrix_[0, 0], tform.matrix_[1, 1]) - assert_equal(tform.matrix_[0, 1], - tform.matrix_[1, 0]) + assert_equal(tform.params_[0, 0], tform.params_[1, 1]) + assert_equal(tform.params_[0, 1], - tform.params_[1, 0]) # over-determined tform2 = estimate_transform('similarity', SRC, DST) assert_array_almost_equal(tform2.inverse(tform2(SRC)), SRC) - assert_equal(tform2.matrix_[0, 0], tform2.matrix_[1, 1]) - assert_equal(tform2.matrix_[0, 1], - tform2.matrix_[1, 0]) + assert_equal(tform2.params_[0, 0], tform2.params_[1, 1]) + assert_equal(tform2.params_[0, 1], - tform2.params_[1, 0]) # via estimate method tform3 = SimilarityTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3.matrix_, tform2.matrix_) + assert_array_almost_equal(tform3.params_, tform2.params_) def test_similarity_init(): @@ -83,7 +83,7 @@ def test_similarity_init(): assert_array_almost_equal(tform.translation, translation) # init with transformation matrix - tform2 = SimilarityTransform(tform.matrix_) + tform2 = SimilarityTransform(tform.params_) assert_array_almost_equal(tform2.scale, scale) assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.translation, translation) @@ -111,7 +111,7 @@ def test_affine_estimation(): # via estimate method tform3 = AffineTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3.matrix_, tform2.matrix_) + assert_array_almost_equal(tform3.params_, tform2.params_) def test_affine_init(): @@ -128,7 +128,7 @@ def test_affine_init(): assert_array_almost_equal(tform.translation, translation) # init with transformation matrix - tform2 = AffineTransform(tform.matrix_) + tform2 = AffineTransform(tform.params_) assert_array_almost_equal(tform2.scale, scale) assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.shear, shear) @@ -155,14 +155,14 @@ def test_projective_estimation(): # via estimate method tform3 = ProjectiveTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3.matrix_, tform2.matrix_) + assert_array_almost_equal(tform3.params_, tform2.params_) def test_projective_init(): tform = estimate_transform('projective', SRC, DST) # init with transformation matrix - tform2 = ProjectiveTransform(tform.matrix_) - assert_array_almost_equal(tform2.matrix_, tform.matrix_) + tform2 = ProjectiveTransform(tform.params_) + assert_array_almost_equal(tform2.params_, tform.params_) def test_polynomial_estimation(): @@ -231,7 +231,7 @@ def test_invalid_input(): def test_deprecated_params_attributes(): for t in ('projective', 'affine', 'similarity'): tform = estimate_transform(t, SRC, DST) - assert_equal(tform._matrix, tform.matrix_) + assert_equal(tform._matrix, tform.params_) tform = estimate_transform('polynomial', SRC, DST, order=3) assert_equal(tform._params, tform.params_) diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py index db936a3a..416991e8 100644 --- a/skimage/transform/tests/test_warps.py +++ b/skimage/transform/tests/test_warps.py @@ -243,7 +243,7 @@ def test_invalid(): def test_inverse(): tform = SimilarityTransform(scale=0.5, rotation=0.1) - inverse_tform = SimilarityTransform(matrix=np.linalg.inv(tform._matrix)) + inverse_tform = SimilarityTransform(matrix=np.linalg.inv(tform.params_)) image = np.arange(10 * 10).reshape(10, 10).astype(np.double) assert_array_equal(warp(image, inverse_tform), warp(image, tform.inverse)) From a1f7d4eb73cb0befe6b485a93696691fe61454ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 19 Jan 2014 22:51:09 -0500 Subject: [PATCH 08/10] Fix geometric example --- doc/examples/applications/plot_geometric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/examples/applications/plot_geometric.py b/doc/examples/applications/plot_geometric.py index 843faa1f..6ef94762 100644 --- a/doc/examples/applications/plot_geometric.py +++ b/doc/examples/applications/plot_geometric.py @@ -33,14 +33,14 @@ First we create a transformation using explicit parameters: tform = tf.SimilarityTransform(scale=1, rotation=math.pi / 2, translation=(0, 1)) -print(tform.matrix_) +print(tform.params_) """ Alternatively you can define a transformation by the transformation matrix itself: """ -matrix = tform.matrix_.copy() +matrix = tform.params_.copy() matrix[1, 2] = 2 tform2 = tf.SimilarityTransform(matrix) From 9905ff218f83c4ceba821e26159feba25552fb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Wed, 22 Jan 2014 18:39:35 -0500 Subject: [PATCH 09/10] Get rid of trailing underscore for params attribute --- doc/examples/applications/plot_geometric.py | 4 +- skimage/measure/fit.py | 36 +++++------ skimage/measure/tests/test_fit.py | 34 +++++----- skimage/transform/_geometric.py | 70 ++++++++++----------- skimage/transform/tests/test_geometric.py | 34 +++++----- skimage/transform/tests/test_warps.py | 2 +- 6 files changed, 90 insertions(+), 90 deletions(-) diff --git a/doc/examples/applications/plot_geometric.py b/doc/examples/applications/plot_geometric.py index 6ef94762..86dd5aaa 100644 --- a/doc/examples/applications/plot_geometric.py +++ b/doc/examples/applications/plot_geometric.py @@ -33,14 +33,14 @@ First we create a transformation using explicit parameters: tform = tf.SimilarityTransform(scale=1, rotation=math.pi / 2, translation=(0, 1)) -print(tform.params_) +print(tform.params) """ Alternatively you can define a transformation by the transformation matrix itself: """ -matrix = tform.params_.copy() +matrix = tform.params.copy() matrix[1, 2] = 2 tform2 = tf.SimilarityTransform(matrix) diff --git a/skimage/measure/fit.py b/skimage/measure/fit.py index a91784ad..53e98c92 100644 --- a/skimage/measure/fit.py +++ b/skimage/measure/fit.py @@ -12,13 +12,13 @@ def _check_data_dim(data, dim): class BaseModel(object): def __init__(self): - self.params_ = None + self.params = None @property def _params(self): warnings.warn('`_params` attribute is deprecated, ' - 'use `params_` instead.') - return self.params_ + 'use `params` instead.') + return self.params class LineModel(BaseModel): @@ -37,7 +37,7 @@ class LineModel(BaseModel): min{ sum((dist - x_i * cos(theta) + y_i * sin(theta))**2) } - The ``params_`` attribute contains the parameters in the following order:: + The ``params`` attribute contains the parameters in the following order:: dist, theta @@ -75,7 +75,7 @@ class LineModel(BaseModel): # line always passes through mean dist = X0[0] * math.cos(theta) + X0[1] * math.sin(theta) - self.params_ = (dist, theta) + self.params = (dist, theta) def residuals(self, data): """Determine residuals of data to model. @@ -96,7 +96,7 @@ class LineModel(BaseModel): _check_data_dim(data, dim=2) - dist, theta = self.params_ + dist, theta = self.params x = data[:, 0] y = data[:, 1] @@ -121,7 +121,7 @@ class LineModel(BaseModel): """ if params is None: - params = self.params_ + params = self.params dist, theta = params return (dist - y * math.sin(theta)) / math.cos(theta) @@ -143,7 +143,7 @@ class LineModel(BaseModel): """ if params is None: - params = self.params_ + params = self.params dist, theta = params return (dist - x * math.cos(theta)) / math.sin(theta) @@ -161,7 +161,7 @@ class CircleModel(BaseModel): min{ sum((r - sqrt((x_i - xc)**2 + (y_i - yc)**2))**2) } - The ``params_`` attribute contains the parameters in the following order:: + The ``params`` attribute contains the parameters in the following order:: xc, yc, r @@ -210,7 +210,7 @@ class CircleModel(BaseModel): params0 = (xc0, yc0, r0) params, _ = optimize.leastsq(fun, params0, Dfun=Dfun, col_deriv=True) - self.params_ = params + self.params = params def residuals(self, data): """Determine residuals of data to model. @@ -231,7 +231,7 @@ class CircleModel(BaseModel): _check_data_dim(data, dim=2) - xc, yc, r = self.params_ + xc, yc, r = self.params x = data[:, 0] y = data[:, 1] @@ -256,7 +256,7 @@ class CircleModel(BaseModel): """ if params is None: - params = self.params_ + params = self.params xc, yc, r = params x = xc + r * np.cos(t) @@ -286,7 +286,7 @@ class EllipseModel(BaseModel): Thus you have ``2 * N`` equations (x_i, y_i) for ``N + 5`` unknowns (t_i, xc, yc, a, b, theta), which gives you an effective redundancy of ``N - 5``. - The ``params_`` attribute contains the parameters in the following order:: + The ``params`` attribute contains the parameters in the following order:: xc, yc, a, b, theta @@ -360,7 +360,7 @@ class EllipseModel(BaseModel): params, _ = optimize.leastsq(fun, params0, Dfun=Dfun, col_deriv=True) - self.params_ = params[:5] + self.params = params[:5] def residuals(self, data): """Determine residuals of data to model. @@ -381,7 +381,7 @@ class EllipseModel(BaseModel): _check_data_dim(data, dim=2) - xc, yc, a, b, theta = self.params_ + xc, yc, a, b, theta = self.params ctheta = math.cos(theta) stheta = math.sin(theta) @@ -443,7 +443,7 @@ class EllipseModel(BaseModel): """ if params is None: - params = self.params_ + params = self.params xc, yc, a, b, theta = params ct = np.cos(t) @@ -557,7 +557,7 @@ def ransac(data, model_class, min_samples, residual_threshold, >>> model = EllipseModel() >>> model.estimate(data) - >>> model.params_ # doctest: +SKIP + >>> model.params # doctest: +SKIP array([ -3.30354146e+03, -2.87791160e+03, 5.59062118e+03, 7.84365066e+00, 7.19203152e-01]) @@ -565,7 +565,7 @@ def ransac(data, model_class, min_samples, residual_threshold, Estimate ellipse model using RANSAC: >>> ransac_model, inliers = ransac(data, EllipseModel, 5, 3, max_trials=50) - >>> ransac_model.params_ + >>> ransac_model.params array([ 20.12762373, 29.73563063, 4.81499637, 10.4743584 , 0.05217117]) >>> inliers array([False, False, False, False, True, True, True, True, True, diff --git a/skimage/measure/tests/test_fit.py b/skimage/measure/tests/test_fit.py index 6d191a19..3ef7a0f0 100644 --- a/skimage/measure/tests/test_fit.py +++ b/skimage/measure/tests/test_fit.py @@ -10,7 +10,7 @@ def test_line_model_invalid_input(): def test_line_model_predict(): model = LineModel() - model.params_ = (10, 1) + model.params = (10, 1) x = np.arange(-10, 10) y = model.predict_y(x) assert_almost_equal(x, model.predict_x(y)) @@ -19,7 +19,7 @@ def test_line_model_predict(): def test_line_model_estimate(): # generate original data without noise model0 = LineModel() - model0.params_ = (10, 1) + model0.params = (10, 1) x0 = np.arange(-100, 100) y0 = model0.predict_y(x0) data0 = np.column_stack([x0, y0]) @@ -33,16 +33,16 @@ def test_line_model_estimate(): model_est.estimate(data) # test whether estimated parameters almost equal original parameters - assert_almost_equal(model0.params_, model_est.params_, 1) + assert_almost_equal(model0.params, model_est.params, 1) def test_line_model_residuals(): model = LineModel() - model.params_ = (0, 0) + model.params = (0, 0) assert_equal(abs(model.residuals(np.array([[0, 0]]))), 0) assert_equal(abs(model.residuals(np.array([[0, 10]]))), 0) assert_equal(abs(model.residuals(np.array([[10, 0]]))), 10) - model.params_ = (5, np.pi / 4) + model.params = (5, np.pi / 4) assert_equal(abs(model.residuals(np.array([[0, 0]]))), 5) assert_almost_equal(abs(model.residuals(np.array([[np.sqrt(50), 0]]))), 0) @@ -59,7 +59,7 @@ def test_circle_model_invalid_input(): def test_circle_model_predict(): model = CircleModel() r = 5 - model.params_ = (0, 0, r) + model.params = (0, 0, r) t = np.arange(0, 2 * np.pi, np.pi / 2) xy = np.array(((5, 0), (0, 5), (-5, 0), (0, -5))) @@ -69,7 +69,7 @@ def test_circle_model_predict(): def test_circle_model_estimate(): # generate original data without noise model0 = CircleModel() - model0.params_ = (10, 12, 3) + model0.params = (10, 12, 3) t = np.linspace(0, 2 * np.pi, 1000) data0 = model0.predict_xy(t) @@ -82,12 +82,12 @@ def test_circle_model_estimate(): model_est.estimate(data) # test whether estimated parameters almost equal original parameters - assert_almost_equal(model0.params_, model_est.params_, 1) + assert_almost_equal(model0.params, model_est.params, 1) def test_circle_model_residuals(): model = CircleModel() - model.params_ = (0, 0, 5) + model.params = (0, 0, 5) assert_almost_equal(abs(model.residuals(np.array([[5, 0]]))), 0) assert_almost_equal(abs(model.residuals(np.array([[6, 6]]))), np.sqrt(2 * 6**2) - 5) @@ -101,7 +101,7 @@ def test_ellipse_model_invalid_input(): def test_ellipse_model_predict(): model = EllipseModel() r = 5 - model.params_ = (0, 0, 5, 10, 0) + model.params = (0, 0, 5, 10, 0) t = np.arange(0, 2 * np.pi, np.pi / 2) xy = np.array(((5, 0), (0, 10), (-5, 0), (0, -10))) @@ -111,7 +111,7 @@ def test_ellipse_model_predict(): def test_ellipse_model_estimate(): # generate original data without noise model0 = EllipseModel() - model0.params_ = (10, 20, 15, 25, 0) + model0.params = (10, 20, 15, 25, 0) t = np.linspace(0, 2 * np.pi, 100) data0 = model0.predict_xy(t) @@ -124,13 +124,13 @@ def test_ellipse_model_estimate(): model_est.estimate(data) # test whether estimated parameters almost equal original parameters - assert_almost_equal(model0.params_, model_est.params_, 0) + assert_almost_equal(model0.params, model_est.params, 0) def test_ellipse_model_residuals(): model = EllipseModel() # vertical line through origin - model.params_ = (0, 0, 10, 5, 0) + model.params = (0, 0, 10, 5, 0) assert_almost_equal(abs(model.residuals(np.array([[10, 0]]))), 0) assert_almost_equal(abs(model.residuals(np.array([[0, 5]]))), 0) assert_almost_equal(abs(model.residuals(np.array([[0, 10]]))), 5) @@ -141,7 +141,7 @@ def test_ransac_shape(): # generate original data without noise model0 = CircleModel() - model0.params_ = (10, 12, 3) + model0.params = (10, 12, 3) t = np.linspace(0, 2 * np.pi, 1000) data0 = model0.predict_xy(t) @@ -155,7 +155,7 @@ def test_ransac_shape(): model_est, inliers = ransac(data0, CircleModel, 3, 5) # test whether estimated parameters equal original parameters - assert_equal(model0.params_, model_est.params_) + assert_equal(model0.params, model_est.params) for outlier in outliers: assert outlier not in inliers @@ -206,10 +206,10 @@ def test_ransac_is_model_valid(): def test_deprecated_params_attribute(): model = LineModel() - model.params_ = (10, 1) + model.params = (10, 1) x = np.arange(-10, 10) y = model.predict_y(x) - assert_equal(model.params_, model._params) + assert_equal(model.params, model._params) if __name__ == "__main__": diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index bb27eb31..bf34352b 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -114,17 +114,17 @@ class ProjectiveTransform(GeometricTransform): matrix = np.eye(3) if matrix.shape != (3, 3): raise ValueError("invalid shape of transformation matrix") - self.params_ = matrix + self.params = matrix @property def _matrix(self): warnings.warn('`_matrix` attribute is deprecated, ' - 'use `params_` instead.') - return self.params_ + 'use `params` instead.') + return self.params @property def _inv_matrix(self): - return np.linalg.inv(self.params_) + return np.linalg.inv(self.params) def _apply_mat(self, coords, matrix): coords = np.array(coords, copy=False, ndmin=2) @@ -140,7 +140,7 @@ class ProjectiveTransform(GeometricTransform): return dst[:, :2] def __call__(self, coords): - return self._apply_mat(coords, self.params_) + return self._apply_mat(coords, self.params) def inverse(self, coords): """Apply inverse transformation. @@ -242,7 +242,7 @@ class ProjectiveTransform(GeometricTransform): H.flat[list(self._coeffs) + [8]] = - V[-1, :-1] / V[-1, -1] H[2, 2] = 1 - self.params_ = H + self.params = H def __add__(self, other): """Combine this transformation with another. @@ -255,7 +255,7 @@ class ProjectiveTransform(GeometricTransform): tform = self.__class__ else: tform = ProjectiveTransform - return tform(other.params_.dot(self.params_)) + return tform(other.params.dot(self.params)) else: raise TypeError("Cannot combine transformations of differing " "types.") @@ -306,7 +306,7 @@ class AffineTransform(ProjectiveTransform): elif matrix is not None: if matrix.shape != (3, 3): raise ValueError("Invalid shape of transformation matrix.") - self.params_ = matrix + self.params = matrix elif params: if scale is None: scale = (1, 1) @@ -318,34 +318,34 @@ class AffineTransform(ProjectiveTransform): translation = (0, 0) sx, sy = scale - self.params_ = np.array([ + self.params = np.array([ [sx * math.cos(rotation), -sy * math.sin(rotation + shear), 0], [sx * math.sin(rotation), sy * math.cos(rotation + shear), 0], [ 0, 0, 1] ]) - self.params_[0:2, 2] = translation + self.params[0:2, 2] = translation else: # default to an identity transform - self.params_ = np.eye(3) + self.params = np.eye(3) @property def scale(self): - sx = math.sqrt(self.params_[0, 0] ** 2 + self.params_[1, 0] ** 2) - sy = math.sqrt(self.params_[0, 1] ** 2 + self.params_[1, 1] ** 2) + sx = math.sqrt(self.params[0, 0] ** 2 + self.params[1, 0] ** 2) + sy = math.sqrt(self.params[0, 1] ** 2 + self.params[1, 1] ** 2) return sx, sy @property def rotation(self): - return math.atan2(self.params_[1, 0], self.params_[0, 0]) + return math.atan2(self.params[1, 0], self.params[0, 0]) @property def shear(self): - beta = math.atan2(- self.params_[0, 1], self.params_[1, 1]) + beta = math.atan2(- self.params[0, 1], self.params[1, 1]) return beta - self.rotation @property def translation(self): - return self.params_[0:2, 2] + return self.params[0:2, 2] class PiecewiseAffineTransform(GeometricTransform): @@ -520,7 +520,7 @@ class SimilarityTransform(ProjectiveTransform): elif matrix is not None: if matrix.shape != (3, 3): raise ValueError("Invalid shape of transformation matrix.") - self.params_ = matrix + self.params = matrix elif params: if scale is None: scale = 1 @@ -529,16 +529,16 @@ class SimilarityTransform(ProjectiveTransform): if translation is None: translation = (0, 0) - self.params_ = np.array([ + self.params = np.array([ [math.cos(rotation), - math.sin(rotation), 0], [math.sin(rotation), math.cos(rotation), 0], [ 0, 0, 1] ]) - self.params_[0:2, 0:2] *= scale - self.params_[0:2, 2] = translation + self.params[0:2, 0:2] *= scale + self.params[0:2, 2] = translation else: # default to an identity transform - self.params_ = np.eye(3) + self.params = np.eye(3) def estimate(self, src, dst): """Set the transformation matrix with the explicit parameters. @@ -604,7 +604,7 @@ class SimilarityTransform(ProjectiveTransform): # singular value a0, a1, b0, b1 = - V[-1, :-1] / V[-1, -1] - self.params_ = np.array([[a0, -b0, a1], + self.params = np.array([[a0, -b0, a1], [b0, a0, b1], [ 0, 0, 1]]) @@ -612,18 +612,18 @@ class SimilarityTransform(ProjectiveTransform): def scale(self): if math.cos(self.rotation) == 0: # sin(self.rotation) == 1 - scale = self.params_[0, 1] + scale = self.params[0, 1] else: - scale = self.params_[0, 0] / math.cos(self.rotation) + scale = self.params[0, 0] / math.cos(self.rotation) return scale @property def rotation(self): - return math.atan2(self.params_[1, 0], self.params_[1, 1]) + return math.atan2(self.params[1, 0], self.params[1, 1]) @property def translation(self): - return self.params_[0:2, 2] + return self.params[0:2, 2] class PolynomialTransform(GeometricTransform): @@ -646,13 +646,13 @@ class PolynomialTransform(GeometricTransform): params = np.array([[0, 1, 0], [0, 0, 1]]) if params.shape[0] != 2: raise ValueError("invalid shape of transformation parameters") - self.params_ = params + self.params = params @property def _params(self): warnings.warn('`_params` attribute is deprecated, ' - 'use `params_` instead.') - return self.params_ + 'use `params` instead.') + return self.params def estimate(self, src, dst, order=2): """Set the transformation matrix with the explicit transformation @@ -725,7 +725,7 @@ class PolynomialTransform(GeometricTransform): # singular value params = - V[-1, :-1] / V[-1, -1] - self.params_ = params.reshape((2, u / 2)) + self.params = params.reshape((2, u / 2)) def __call__(self, coords): """Apply forward transformation. @@ -743,7 +743,7 @@ class PolynomialTransform(GeometricTransform): """ x = coords[:, 0] y = coords[:, 1] - u = len(self.params_.ravel()) + u = len(self.params.ravel()) # number of coefficients -> u = (order + 1) * (order + 2) order = int((- 3 + math.sqrt(9 - 4 * (2 - u))) / 2) dst = np.zeros(coords.shape) @@ -751,8 +751,8 @@ class PolynomialTransform(GeometricTransform): pidx = 0 for j in range(order + 1): for i in range(j + 1): - dst[:, 0] += self.params_[0, pidx] * x ** (j - i) * y ** i - dst[:, 1] += self.params_[1, pidx] * x ** (j - i) * y ** i + dst[:, 0] += self.params[0, pidx] * x ** (j - i) * y ** i + dst[:, 1] += self.params[1, pidx] * x ** (j - i) * y ** i pidx += 1 return dst @@ -1071,14 +1071,14 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, # inverse_map is a homography elif isinstance(inverse_map, HOMOGRAPHY_TRANSFORMS): - matrix = inverse_map.params_ + matrix = inverse_map.params # inverse_map is the inverse of a homography elif (hasattr(inverse_map, '__name__') and inverse_map.__name__ == 'inverse' and get_bound_method_class(inverse_map) \ in HOMOGRAPHY_TRANSFORMS): - matrix = np.linalg.inv(six.get_method_self(inverse_map).params_) + matrix = np.linalg.inv(six.get_method_self(inverse_map).params) if matrix is not None: matrix = matrix.astype(np.double) diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index ed4cd405..74997789 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -56,19 +56,19 @@ def test_similarity_estimation(): # exact solution tform = estimate_transform('similarity', SRC[:2, :], DST[:2, :]) assert_array_almost_equal(tform(SRC[:2, :]), DST[:2, :]) - assert_equal(tform.params_[0, 0], tform.params_[1, 1]) - assert_equal(tform.params_[0, 1], - tform.params_[1, 0]) + assert_equal(tform.params[0, 0], tform.params[1, 1]) + assert_equal(tform.params[0, 1], - tform.params[1, 0]) # over-determined tform2 = estimate_transform('similarity', SRC, DST) assert_array_almost_equal(tform2.inverse(tform2(SRC)), SRC) - assert_equal(tform2.params_[0, 0], tform2.params_[1, 1]) - assert_equal(tform2.params_[0, 1], - tform2.params_[1, 0]) + assert_equal(tform2.params[0, 0], tform2.params[1, 1]) + assert_equal(tform2.params[0, 1], - tform2.params[1, 0]) # via estimate method tform3 = SimilarityTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3.params_, tform2.params_) + assert_array_almost_equal(tform3.params, tform2.params) def test_similarity_init(): @@ -83,7 +83,7 @@ def test_similarity_init(): assert_array_almost_equal(tform.translation, translation) # init with transformation matrix - tform2 = SimilarityTransform(tform.params_) + tform2 = SimilarityTransform(tform.params) assert_array_almost_equal(tform2.scale, scale) assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.translation, translation) @@ -111,7 +111,7 @@ def test_affine_estimation(): # via estimate method tform3 = AffineTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3.params_, tform2.params_) + assert_array_almost_equal(tform3.params, tform2.params) def test_affine_init(): @@ -128,7 +128,7 @@ def test_affine_init(): assert_array_almost_equal(tform.translation, translation) # init with transformation matrix - tform2 = AffineTransform(tform.params_) + tform2 = AffineTransform(tform.params) assert_array_almost_equal(tform2.scale, scale) assert_array_almost_equal(tform2.rotation, rotation) assert_array_almost_equal(tform2.shear, shear) @@ -155,14 +155,14 @@ def test_projective_estimation(): # via estimate method tform3 = ProjectiveTransform() tform3.estimate(SRC, DST) - assert_array_almost_equal(tform3.params_, tform2.params_) + assert_array_almost_equal(tform3.params, tform2.params) def test_projective_init(): tform = estimate_transform('projective', SRC, DST) # init with transformation matrix - tform2 = ProjectiveTransform(tform.params_) - assert_array_almost_equal(tform2.params_, tform.params_) + tform2 = ProjectiveTransform(tform.params) + assert_array_almost_equal(tform2.params, tform.params) def test_polynomial_estimation(): @@ -173,20 +173,20 @@ def test_polynomial_estimation(): # via estimate method tform2 = PolynomialTransform() tform2.estimate(SRC, DST, order=10) - assert_array_almost_equal(tform2.params_, tform.params_) + assert_array_almost_equal(tform2.params, tform.params) def test_polynomial_init(): tform = estimate_transform('polynomial', SRC, DST, order=10) # init with transformation parameters - tform2 = PolynomialTransform(tform.params_) - assert_array_almost_equal(tform2.params_, tform.params_) + tform2 = PolynomialTransform(tform.params) + assert_array_almost_equal(tform2.params, tform.params) def test_polynomial_default_order(): tform = estimate_transform('polynomial', SRC, DST) tform2 = estimate_transform('polynomial', SRC, DST, order=2) - assert_array_almost_equal(tform2.params_, tform.params_) + assert_array_almost_equal(tform2.params, tform.params) def test_polynomial_inverse(): @@ -231,10 +231,10 @@ def test_invalid_input(): def test_deprecated_params_attributes(): for t in ('projective', 'affine', 'similarity'): tform = estimate_transform(t, SRC, DST) - assert_equal(tform._matrix, tform.params_) + assert_equal(tform._matrix, tform.params) tform = estimate_transform('polynomial', SRC, DST, order=3) - assert_equal(tform._params, tform.params_) + assert_equal(tform._params, tform.params) tform = estimate_transform('piecewise-affine', SRC, DST) assert_equal(tform.affines, tform.affines_) diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py index 416991e8..e49ab098 100644 --- a/skimage/transform/tests/test_warps.py +++ b/skimage/transform/tests/test_warps.py @@ -243,7 +243,7 @@ def test_invalid(): def test_inverse(): tform = SimilarityTransform(scale=0.5, rotation=0.1) - inverse_tform = SimilarityTransform(matrix=np.linalg.inv(tform.params_)) + inverse_tform = SimilarityTransform(matrix=np.linalg.inv(tform.params)) image = np.arange(10 * 10).reshape(10, 10).astype(np.double) assert_array_equal(warp(image, inverse_tform), warp(image, tform.inverse)) From 21a99a39c35bced4e257b2caef1a18a6770144d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Wed, 22 Jan 2014 18:46:27 -0500 Subject: [PATCH 10/10] Document params attribute for each class --- skimage/measure/fit.py | 24 ++++++---- skimage/transform/_geometric.py | 56 +++++++++++++++-------- skimage/transform/tests/test_geometric.py | 4 -- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/skimage/measure/fit.py b/skimage/measure/fit.py index 53e98c92..8f9ceb9c 100644 --- a/skimage/measure/fit.py +++ b/skimage/measure/fit.py @@ -37,12 +37,13 @@ class LineModel(BaseModel): min{ sum((dist - x_i * cos(theta) + y_i * sin(theta))**2) } - The ``params`` attribute contains the parameters in the following order:: - - dist, theta - A minimum number of 2 points is required to solve for the parameters. + Attributes + ---------- + params : tuple + Line model parameters in the following order `dist`, `theta`. + """ def estimate(self, data): @@ -161,12 +162,13 @@ class CircleModel(BaseModel): min{ sum((r - sqrt((x_i - xc)**2 + (y_i - yc)**2))**2) } - The ``params`` attribute contains the parameters in the following order:: - - xc, yc, r - A minimum number of 3 points is required to solve for the parameters. + Attributes + ---------- + params : tuple + Circle model parameters in the following order `xc`, `yc`, `r`. + """ def estimate(self, data): @@ -292,6 +294,12 @@ class EllipseModel(BaseModel): A minimum number of 5 points is required to solve for the parameters. + Attributes + ---------- + params : tuple + Ellipse model parameters in the following order `xc`, `yc`, `a`, + `b`, `theta`. + """ def estimate(self, data): diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index bf34352b..092738bc 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -104,6 +104,11 @@ class ProjectiveTransform(GeometricTransform): matrix : (3, 3) array, optional Homogeneous transformation matrix. + Attributes + ---------- + params : (3, 3) array + Homogeneous transformation matrix. + """ _coeffs = range(8) @@ -291,6 +296,11 @@ class AffineTransform(ProjectiveTransform): translation : (tx, ty) as array, list or tuple, optional Translation parameters. + Attributes + ---------- + params : (3, 3) array + Homogeneous transformation matrix. + """ _coeffs = range(6) @@ -356,25 +366,20 @@ class PiecewiseAffineTransform(GeometricTransform): a Delaunay triangulation of the points to form a mesh. Each triangle is used to find a local affine transform. + Attributes + ---------- + affines : list of AffineTransform objects + Affine transformations for each triangle in the mesh. + inverse_affines : list of AffineTransform objects + Inverse affine transformations for each triangle in the mesh. + """ def __init__(self): self._tesselation = None self._inverse_tesselation = None - self.affines_ = None - self.inverse_affines_ = None - - @property - def affines(self): - warnings.warn('`affines` attribute is deprecated, ' - 'use `affines_` instead.') - return self.affines_ - - @property - def inverse_affines(self): - warnings.warn('`inverse_affines` attribute is deprecated, ' - 'use `inverse_affines_` instead.') - return self.inverse_affines_ + self.affines = None + self.inverse_affines = None def estimate(self, src, dst): """Set the control points with which to perform the piecewise mapping. @@ -394,21 +399,21 @@ class PiecewiseAffineTransform(GeometricTransform): # triangulate input positions into mesh self._tesselation = spatial.Delaunay(src) # find affine mapping from source positions to destination - self.affines_ = [] + self.affines = [] for tri in self._tesselation.vertices: affine = AffineTransform() affine.estimate(src[tri, :], dst[tri, :]) - self.affines_.append(affine) + self.affines.append(affine) # inverse piecewise affine # triangulate input positions into mesh self._inverse_tesselation = spatial.Delaunay(dst) # find affine mapping from source positions to destination - self.inverse_affines_ = [] + self.inverse_affines = [] for tri in self._inverse_tesselation.vertices: affine = AffineTransform() affine.estimate(dst[tri, :], src[tri, :]) - self.inverse_affines_.append(affine) + self.inverse_affines.append(affine) def __call__(self, coords): """Apply forward transformation. @@ -437,7 +442,7 @@ class PiecewiseAffineTransform(GeometricTransform): for index in range(len(self._tesselation.vertices)): # affine transform for triangle - affine = self.affines_[index] + affine = self.affines[index] # all coordinates within triangle index_mask = simplex == index @@ -472,7 +477,7 @@ class PiecewiseAffineTransform(GeometricTransform): for index in range(len(self._inverse_tesselation.vertices)): # affine transform for triangle - affine = self.inverse_affines_[index] + affine = self.inverse_affines[index] # all coordinates within triangle index_mask = simplex == index @@ -507,6 +512,11 @@ class SimilarityTransform(ProjectiveTransform): translation : (tx, ty) as array, list or tuple, optional x, y translation parameters. + Attributes + ---------- + params : (3, 3) array + Homogeneous transformation matrix. + """ def __init__(self, matrix=None, scale=None, rotation=None, @@ -638,6 +648,12 @@ class PolynomialTransform(GeometricTransform): Polynomial coefficients where `N * 2 = (order + 1) * (order + 2)`. So, a_ji is defined in `params[0, :]` and b_ji in `params[1, :]`. + Attributes + ---------- + params : (2, N) array + Polynomial coefficients where `N * 2 = (order + 1) * (order + 2)`. So, + a_ji is defined in `params[0, :]` and b_ji in `params[1, :]`. + """ def __init__(self, params=None): diff --git a/skimage/transform/tests/test_geometric.py b/skimage/transform/tests/test_geometric.py index 74997789..2a6a5c7e 100644 --- a/skimage/transform/tests/test_geometric.py +++ b/skimage/transform/tests/test_geometric.py @@ -236,10 +236,6 @@ def test_deprecated_params_attributes(): tform = estimate_transform('polynomial', SRC, DST, order=3) assert_equal(tform._params, tform.params) - tform = estimate_transform('piecewise-affine', SRC, DST) - assert_equal(tform.affines, tform.affines_) - assert_equal(tform.inverse_affines, tform.inverse_affines_) - if __name__ == "__main__": from numpy.testing import run_module_suite