def test_diffeomorphic_map_simplification_2d(): r""" Create an invertible deformation field, and define a DiffeomorphicMap using different voxel-to-space transforms for domain, codomain, and reference discretizations, also use a non-identity pre-aligning matrix. Warp a circle using the diffeomorphic map to obtain the expected warped circle. Now simplify the DiffeomorphicMap and warp the same circle using this simplified map. Verify that the two warped circles are equal up to numerical precision. """ #create a simple affine transformation dom_shape = (64, 64) cod_shape = (80, 80) nr = dom_shape[0] nc = dom_shape[1] s = 1.1 t = 0.25 trans = np.array([[1, 0, -t*nr], [0, 1, -t*nc], [0, 0, 1]]) trans_inv = np.linalg.inv(trans) scale = np.array([[1*s, 0, 0], [0, 1*s, 0], [0, 0, 1]]) gt_affine = trans_inv.dot(scale.dot(trans)) # Create the invertible displacement fields and the circle radius = 16 circle = vfu.create_circle(cod_shape[0], cod_shape[1], radius) d, dinv = vfu.create_harmonic_fields_2d(dom_shape[0], dom_shape[1], 0.3, 6) #Define different voxel-to-space transforms for domain, codomain and #reference grid, also, use a non-identity pre-align transform D = gt_affine C = imwarp.mult_aff(gt_affine, gt_affine) R = np.eye(3) P = gt_affine #Create the original diffeomorphic map diff_map = imwarp.DiffeomorphicMap(2, dom_shape, R, dom_shape, D, cod_shape, C, P) diff_map.forward = np.array(d, dtype = floating) diff_map.backward = np.array(dinv, dtype = floating) #Warp the circle to obtain the expected image expected = diff_map.transform(circle, 'linear') #Simplify simplified = diff_map.get_simplified_transform() #warp the circle warped = simplified.transform(circle, 'linear') #verify that the simplified map is equivalent to the #original one assert_array_almost_equal(warped, expected) #And of course, it must be simpler... assert_equal(simplified.domain_affine, None) assert_equal(simplified.codomain_affine, None) assert_equal(simplified.disp_affine, None) assert_equal(simplified.domain_affine_inv, None) assert_equal(simplified.codomain_affine_inv, None) assert_equal(simplified.disp_affine_inv, None)
def test_affine_map(): np.random.seed(2112927) dom_shape = np.array([64, 64, 64], dtype=np.int32) cod_shape = np.array([80, 80, 80], dtype=np.int32) nx = dom_shape[0] ny = dom_shape[1] nz = dom_shape[2] # Radius of the circle/sphere (testing image) radius = 16 # Rotation axis (used for 3D transforms only) rot_axis = np.array([.5, 2.0, 1.5]) # Arbitrary transform parameters t = 0.15 rotations = [-1 * np.pi / 10.0, 0.0, np.pi / 10.0] scales = [0.9, 1.0, 1.1] for dim in [2, 3]: # Setup current dimension if dim == 2: # Create image of a circle img = vf.create_circle(cod_shape[0], cod_shape[1], radius) oracle_linear = vf.transform_2d_affine oracle_nn = vf.transform_2d_affine_nn else: # Create image of a sphere img = vf.create_sphere(cod_shape[0], cod_shape[1], cod_shape[2], radius) oracle_linear = vf.transform_3d_affine oracle_nn = vf.transform_3d_affine_nn img = np.array(img) # Translation is the only parameter differing for 2D and 3D translations = [t * dom_shape[:dim]] # Generate affine transforms gt_affines = create_affine_transforms(dim, translations, rotations, scales, rot_axis) # Include the None case gt_affines.append(None) # testing str/format/repr for affine_mat in gt_affines: aff_map = AffineMap(affine_mat) assert_equal(str(aff_map), aff_map.__str__()) assert_equal(repr(aff_map), aff_map.__repr__()) for spec in ['f', 'r', 't', '']: assert_equal(format(aff_map, spec), aff_map.__format__(spec)) for affine in gt_affines: # make both domain point to the same physical region # It's ok to use the same transform, we just want to test # that this information is actually being considered domain_grid2world = affine codomain_grid2world = affine grid2grid_transform = affine # Evaluate the transform with vector_fields module (already tested) expected_linear = oracle_linear(img, dom_shape[:dim], grid2grid_transform) expected_nn = oracle_nn(img, dom_shape[:dim], grid2grid_transform) # Evaluate the transform with the implementation under test affine_map = imaffine.AffineMap(affine, dom_shape[:dim], domain_grid2world, cod_shape[:dim], codomain_grid2world) actual_linear = affine_map.transform(img, interp='linear') actual_nn = affine_map.transform(img, interp='nearest') assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Test set_affine with valid matrix affine_map.set_affine(affine) if affine is None: assert(affine_map.affine is None) assert(affine_map.affine_inv is None) else: # compatibility with previous versions assert_array_equal(affine, affine_map.affine) # new getter new_copy_affine = affine_map.get_affine() # value must be the same assert_array_equal(affine, new_copy_affine) # but not its reference assert id(affine) != id(new_copy_affine) actual = affine_map.affine.dot(affine_map.affine_inv) assert_array_almost_equal(actual, np.eye(dim + 1)) # Evaluate via the inverse transform # AffineMap will use the inverse of the input matrix when we call # `transform_inverse`. Since the inverse of the inverse of a matrix # is not exactly equal to the original matrix (numerical # limitations) we need to invert the matrix twice to make sure # the oracle and the implementation under test apply the same # transform aff_inv = None if affine is None else npl.inv(affine) aff_inv_inv = None if aff_inv is None else npl.inv(aff_inv) expected_linear = oracle_linear(img, dom_shape[:dim], aff_inv_inv) expected_nn = oracle_nn(img, dom_shape[:dim], aff_inv_inv) affine_map = imaffine.AffineMap(aff_inv, cod_shape[:dim], codomain_grid2world, dom_shape[:dim], domain_grid2world) actual_linear = affine_map.transform_inverse(img, interp='linear') actual_nn = affine_map.transform_inverse(img, interp='nearest') assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Verify AffineMap can not be created with non-square matrix non_square_shapes = [ np.zeros((dim, dim + 1), dtype=np.float64), np.zeros((dim + 1, dim), dtype=np.float64) ] for nsq in non_square_shapes: assert_raises(AffineInversionError, AffineMap, nsq) # Verify incorrect augmentations are caught for affine_mat in gt_affines: aff_map = AffineMap(affine_mat) if affine_mat is None: continue bad_aug = aff_map.get_affine() # no zeros in the first n-1 columns on last row bad_aug[-1,:] = 1 assert_raises(AffineInvalidValuesError, AffineMap, bad_aug) bad_aug = aff_map.get_affine() bad_aug[-1, -1] = 0 # lower right not 1 assert_raises(AffineInvalidValuesError, AffineMap, bad_aug) # Verify AffineMap cannot be created with a non-invertible matrix invalid_nan = np.zeros((dim + 1, dim + 1), dtype=np.float64) invalid_nan[1, 1] = np.nan invalid_zeros = np.zeros((dim + 1, dim + 1), dtype=np.float64) assert_raises( imaffine.AffineInvalidValuesError, imaffine.AffineMap, invalid_nan) assert_raises( AffineInvalidValuesError, imaffine.AffineMap, invalid_zeros) # Test exception is raised when the affine transform matrix is not # valid invalid_shape = np.eye(dim) affmap_invalid_shape = imaffine.AffineMap(invalid_shape, dom_shape[:dim], None, cod_shape[:dim], None) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when sampling info is not provided valid = np.eye(3) affmap_invalid_shape = imaffine.AffineMap(valid) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when requesting an invalid interpolation assert_raises(ValueError, affine_map.transform, img, 'invalid') assert_raises(ValueError, affine_map.transform_inverse, img, 'invalid') # Verify exception is raised when attempting to warp an image of # invalid dimension for dim in [2, 3]: affine_map = imaffine.AffineMap(np.eye(dim), cod_shape[:dim], None, dom_shape[:dim], None) for sh in [(2,), (2, 2, 2, 2)]: img = np.zeros(sh) assert_raises(ValueError, affine_map.transform, img) assert_raises(ValueError, affine_map.transform_inverse, img) aff_sing = np.zeros((dim + 1, dim + 1)) aff_nan = np.zeros((dim + 1, dim + 1)) aff_nan[...] = np.nan aff_inf = np.zeros((dim + 1, dim + 1)) aff_inf[...] = np.inf assert_raises( AffineInvalidValuesError, affine_map.set_affine, aff_sing) assert_raises(AffineInvalidValuesError, affine_map.set_affine, aff_nan) assert_raises(AffineInvalidValuesError, affine_map.set_affine, aff_inf) # Verify AffineMap can not be created with non-2D matrices : len(shape) != 2 for dim_not_2 in range(10): if dim_not_2 != _number_dim_affine_matrix: mat_large_dim = np.random.random([2]*dim_not_2) assert_raises(AffineInversionError, AffineMap, mat_large_dim)
def test_affine_map(): np.random.seed(2112927) dom_shape = np.array([64, 64, 64], dtype=np.int32) cod_shape = np.array([80, 80, 80], dtype=np.int32) nx = dom_shape[0] ny = dom_shape[1] nz = dom_shape[2] # Radius of the circle/sphere (testing image) radius = 16 # Rotation axis (used for 3D transforms only) rot_axis = np.array([0.5, 2.0, 1.5]) # Arbitrary transform parameters t = 0.15 rotations = [-1 * np.pi / 10.0, 0.0, np.pi / 10.0] scales = [0.9, 1.0, 1.1] for dim in [2, 3]: # Setup current dimension if dim == 2: # Create image of a circle img = vf.create_circle(cod_shape[0], cod_shape[1], radius) oracle_linear = vf.transform_2d_affine oracle_nn = vf.transform_2d_affine_nn else: # Create image of a sphere img = vf.create_sphere(cod_shape[0], cod_shape[1], cod_shape[2], radius) oracle_linear = vf.transform_3d_affine oracle_nn = vf.transform_3d_affine_nn img = np.array(img) # Translation is the only parameter differing for 2D and 3D translations = [t * dom_shape[:dim]] # Generate affine transforms gt_affines = create_affine_transforms(dim, translations, rotations, scales, rot_axis) # Include the None case gt_affines.append(None) for affine in gt_affines: # make both domain point to the same physical region # It's ok to use the same transform, we just want to test # that this information is actually being considered domain_grid2world = affine codomain_grid2world = affine grid2grid_transform = affine # Evaluate the transform with vector_fields module (already tested) expected_linear = oracle_linear(img, dom_shape[:dim], grid2grid_transform) expected_nn = oracle_nn(img, dom_shape[:dim], grid2grid_transform) # Evaluate the transform with the implementation under test affine_map = imaffine.AffineMap( affine, dom_shape[:dim], domain_grid2world, cod_shape[:dim], codomain_grid2world ) actual_linear = affine_map.transform(img, interp="linear") actual_nn = affine_map.transform(img, interp="nearest") assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Test set_affine with valid matrix affine_map.set_affine(affine) if affine is None: assert affine_map.affine is None assert affine_map.affine_inv is None else: assert_array_equal(affine, affine_map.affine) actual = affine_map.affine.dot(affine_map.affine_inv) assert_array_almost_equal(actual, np.eye(dim + 1)) # Evaluate via the inverse transform # AffineMap will use the inverse of the input matrix when we call # `transform_inverse`. Since the inverse of the inverse of a matrix # is not exactly equal to the original matrix (numerical # limitations) we need to invert the matrix twice to make sure # the oracle and the implementation under test apply the same # transform aff_inv = None if affine is None else npl.inv(affine) aff_inv_inv = None if aff_inv is None else npl.inv(aff_inv) expected_linear = oracle_linear(img, dom_shape[:dim], aff_inv_inv) expected_nn = oracle_nn(img, dom_shape[:dim], aff_inv_inv) affine_map = imaffine.AffineMap( aff_inv, cod_shape[:dim], codomain_grid2world, dom_shape[:dim], domain_grid2world ) actual_linear = affine_map.transform_inverse(img, interp="linear") actual_nn = affine_map.transform_inverse(img, interp="nearest") assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Verify AffineMap cannot be created with a non-invertible matrix invalid_nan = np.zeros((dim + 1, dim + 1), dtype=np.float64) invalid_nan[1, 1] = np.nan invalid_zeros = np.zeros((dim + 1, dim + 1), dtype=np.float64) assert_raises(imaffine.AffineInversionError, imaffine.AffineMap, invalid_nan) assert_raises(imaffine.AffineInversionError, imaffine.AffineMap, invalid_zeros) # Test exception is raised when the affine transform matrix is not # valid invalid_shape = np.eye(dim) affmap_invalid_shape = imaffine.AffineMap(invalid_shape, dom_shape[:dim], None, cod_shape[:dim], None) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when sampling info is not provided valid = np.eye(3) affmap_invalid_shape = imaffine.AffineMap(valid) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when requesting an invalid interpolation assert_raises(ValueError, affine_map.transform, img, "invalid") assert_raises(ValueError, affine_map.transform_inverse, img, "invalid") # Verify exception is raised when attempting to warp an image of # invalid dimension for dim in [2, 3]: affine_map = imaffine.AffineMap(np.eye(dim), cod_shape[:dim], None, dom_shape[:dim], None) for sh in [(2,), (2, 2, 2, 2)]: img = np.zeros(sh) assert_raises(ValueError, affine_map.transform, img) assert_raises(ValueError, affine_map.transform_inverse, img) aff_sing = np.zeros((dim + 1, dim + 1)) aff_nan = np.zeros((dim + 1, dim + 1)) aff_nan[...] = np.nan aff_inf = np.zeros((dim + 1, dim + 1)) aff_inf[...] = np.inf assert_raises(AffineInversionError, affine_map.set_affine, aff_sing) assert_raises(AffineInversionError, affine_map.set_affine, aff_nan) assert_raises(AffineInversionError, affine_map.set_affine, aff_inf)
def test_warp(shape): """Tests the cython implementation of the 3d warpings against scipy.""" ndim = len(shape) radius = shape[0] / 3 if ndim == 3: # Create an image of a sphere volume = vfu.create_sphere(*shape, radius) volume = np.array(volume, dtype=floating) # Create a displacement field for warping d, dinv = vfu.create_harmonic_fields_3d(*shape, 0.2, 8) else: # Create an image of a circle volume = vfu.create_circle(*shape, radius) volume = np.array(volume, dtype=floating) # Create a displacement field for warping d, dinv = vfu.create_harmonic_fields_2d(*shape, 0.2, 8) d = np.asarray(d).astype(floating) if ndim == 3: # Select an arbitrary rotation axis axis = np.array([0.5, 2.0, 1.5]) # Select an arbitrary translation matrix t = 0.1 trans = np.array([ [1, 0, 0, -t * shape[0]], [0, 1, 0, -t * shape[1]], [0, 0, 1, -t * shape[2]], [0, 0, 0, 1], ]) trans_inv = np.linalg.inv(trans) theta = np.pi / 5 s = 1.1 rot = np.zeros(shape=(4, 4)) rot[:3, :3] = geometry.rodrigues_axis_rotation(axis, theta) rot[3, 3] = 1.0 scale = np.array([[1 * s, 0, 0, 0], [0, 1 * s, 0, 0], [0, 0, 1 * s, 0], [0, 0, 0, 1]]) elif ndim == 2: # Select an arbitrary translation matrix t = 0.1 trans = np.array([[1, 0, -t * shape[0]], [0, 1, -t * shape[1]], [0, 0, 1]]) trans_inv = np.linalg.inv(trans) theta = -1 * np.pi / 6.0 s = 0.42 ct = np.cos(theta) st = np.sin(theta) rot = np.array([[ct, -st, 0], [st, ct, 0], [0, 0, 1]]) scale = np.array([[1 * s, 0, 0], [0, 1 * s, 0], [0, 0, 1]]) aff = trans_inv.dot(scale.dot(rot.dot(trans))) # Select arbitrary (but different) grid-to-space transforms sampling_grid2world = scale field_grid2world = aff field_world2grid = np.linalg.inv(field_grid2world) image_grid2world = aff.dot(scale) image_world2grid = np.linalg.inv(image_grid2world) A = field_world2grid.dot(sampling_grid2world) B = image_world2grid.dot(sampling_grid2world) C = image_world2grid # Reorient the displacement field according to its grid-to-space # transform dcopy = np.copy(d) if ndim == 3: vfu.reorient_vector_field_3d(dcopy, field_grid2world) expected = vfu.warp_3d(volume, dcopy, A, B, C, np.array(shape, dtype=np.int32)) elif ndim == 2: vfu.reorient_vector_field_2d(dcopy, field_grid2world) expected = vfu.warp_2d(volume, dcopy, A, B, C, np.array(shape, dtype=np.int32)) dcopyg = cupy.asarray(dcopy) volumeg = cupy.asarray(volume) Ag = cupy.asarray(A) Bg = cupy.asarray(B) Cg = cupy.asarray(C) warped = warp(volumeg, dcopyg, Ag, Bg, Cg, order=1, mode="constant") cupy.testing.assert_array_almost_equal(warped, expected, decimal=4)
def test_transform_affine(d_shape, codomain_shape, order): ndim = len(d_shape) theta = -1 * np.pi / 5.0 s = 0.5 ct = np.cos(theta) st = np.sin(theta) if ndim == 2: # Create an image of a circle radius = d_shape[0] // 4 volume = vfu.create_circle(*codomain_shape, radius) volume = np.array(volume, dtype=floating) # Generate affine transforms t = 0.3 trans = np.array([[1, 0, -t * d_shape[0]], [0, 1, -t * d_shape[1]], [0, 0, 1]]) trans_inv = np.linalg.inv(trans) rot = np.array([[ct, -st, 0], [st, ct, 0], [0, 0, 1]]) scale = np.array([[1 * s, 0, 0], [0, 1 * s, 0], [0, 0, 1]]) elif ndim == 3: # Create an image of a sphere radius = d_shape[0] // 4 volume = vfu.create_sphere(*codomain_shape, radius) volume = np.array(volume, dtype=floating) # Generate affine transforms # Select an arbitrary rotation axis axis = np.array([0.5, 2.0, 1.5]) t = 0.3 trans = np.array([ [1, 0, 0, -t * d_shape[0]], [0, 1, 0, -t * d_shape[1]], [0, 0, 1, -t * d_shape[2]], [0, 0, 0, 1], ]) trans_inv = np.linalg.inv(trans) rot = np.zeros(shape=(4, 4)) rot[:3, :3] = geometry.rodrigues_axis_rotation(axis, theta) rot[3, 3] = 1.0 scale = np.array([[1 * s, 0, 0, 0], [0, 1 * s, 0, 0], [0, 0, 1 * s, 0], [0, 0, 0, 1]]) gt_affine = trans_inv.dot(scale.dot(rot.dot(trans))) # # Apply the affine transform to the grid coordinates # Y = np.apply_along_axis(gt_affine.dot, 0, X)[0:2, ...] # expected = map_coordinates(volume, Y, order=1) if order == 1: if ndim == 2: dipy_func = vfu.transform_2d_affine elif ndim == 3: dipy_func = vfu.transform_3d_affine elif order == 0: if ndim == 2: dipy_func = vfu.transform_2d_affine_nn elif ndim == 3: dipy_func = vfu.transform_3d_affine_nn expected = dipy_func(volume, np.array(d_shape, dtype=np.int32), gt_affine) volumed = cupy.asarray(volume) warped = transform_affine(volumed, d_shape, gt_affine, order=order) cupy.testing.assert_array_almost_equal(warped, expected)
def test_affine_map(): np.random.seed(2112927) dom_shape = np.array([64, 64, 64], dtype=np.int32) cod_shape = np.array([80, 80, 80], dtype=np.int32) # Radius of the circle/sphere (testing image) radius = 16 # Rotation axis (used for 3D transforms only) rot_axis = np.array([.5, 2.0, 1.5]) # Arbitrary transform parameters t = 0.15 rotations = [-1 * np.pi / 10.0, 0.0, np.pi / 10.0] scales = [0.9, 1.0, 1.1] for dim in [2, 3]: # Setup current dimension if dim == 2: # Create image of a circle img = vf.create_circle(cod_shape[0], cod_shape[1], radius) oracle_linear = vf.transform_2d_affine oracle_nn = vf.transform_2d_affine_nn else: # Create image of a sphere img = vf.create_sphere(cod_shape[0], cod_shape[1], cod_shape[2], radius) oracle_linear = vf.transform_3d_affine oracle_nn = vf.transform_3d_affine_nn img = np.array(img) # Translation is the only parameter differing for 2D and 3D translations = [t * dom_shape[:dim]] # Generate affine transforms gt_affines = create_affine_transforms(dim, translations, rotations, scales, rot_axis) # Include the None case gt_affines.append(None) # testing str/format/repr for affine_mat in gt_affines: aff_map = AffineMap(affine_mat) assert_equal(str(aff_map), aff_map.__str__()) assert_equal(repr(aff_map), aff_map.__repr__()) for spec in ['f', 'r', 't', '']: assert_equal(format(aff_map, spec), aff_map.__format__(spec)) for affine in gt_affines: # make both domain point to the same physical region # It's ok to use the same transform, we just want to test # that this information is actually being considered domain_grid2world = affine codomain_grid2world = affine grid2grid_transform = affine # Evaluate the transform with vector_fields module (already tested) expected_linear = oracle_linear(img, dom_shape[:dim], grid2grid_transform) expected_nn = oracle_nn(img, dom_shape[:dim], grid2grid_transform) # Evaluate the transform with the implementation under test affine_map = imaffine.AffineMap(affine, dom_shape[:dim], domain_grid2world, cod_shape[:dim], codomain_grid2world) actual_linear = affine_map.transform(img, interp='linear') actual_nn = affine_map.transform(img, interp='nearest') assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Test set_affine with valid matrix affine_map.set_affine(affine) if affine is None: assert(affine_map.affine is None) assert(affine_map.affine_inv is None) else: # compatibility with previous versions assert_array_equal(affine, affine_map.affine) # new getter new_copy_affine = affine_map.get_affine() # value must be the same assert_array_equal(affine, new_copy_affine) # but not its reference assert id(affine) != id(new_copy_affine) actual = affine_map.affine.dot(affine_map.affine_inv) assert_array_almost_equal(actual, np.eye(dim + 1)) # Evaluate via the inverse transform # AffineMap will use the inverse of the input matrix when we call # `transform_inverse`. Since the inverse of the inverse of a matrix # is not exactly equal to the original matrix (numerical # limitations) we need to invert the matrix twice to make sure # the oracle and the implementation under test apply the same # transform aff_inv = None if affine is None else npl.inv(affine) aff_inv_inv = None if aff_inv is None else npl.inv(aff_inv) expected_linear = oracle_linear(img, dom_shape[:dim], aff_inv_inv) expected_nn = oracle_nn(img, dom_shape[:dim], aff_inv_inv) affine_map = imaffine.AffineMap(aff_inv, cod_shape[:dim], codomain_grid2world, dom_shape[:dim], domain_grid2world) actual_linear = affine_map.transform_inverse(img, interp='linear') actual_nn = affine_map.transform_inverse(img, interp='nearest') assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Verify AffineMap can not be created with non-square matrix non_square_shapes = [ np.zeros((dim, dim + 1), dtype=np.float64), np.zeros((dim + 1, dim), dtype=np.float64) ] for nsq in non_square_shapes: assert_raises(AffineInversionError, AffineMap, nsq) # Verify incorrect augmentations are caught for affine_mat in gt_affines: aff_map = AffineMap(affine_mat) if affine_mat is None: continue bad_aug = aff_map.get_affine() # no zeros in the first n-1 columns on last row bad_aug[-1,:] = 1 assert_raises(AffineInvalidValuesError, AffineMap, bad_aug) bad_aug = aff_map.get_affine() bad_aug[-1, -1] = 0 # lower right not 1 assert_raises(AffineInvalidValuesError, AffineMap, bad_aug) # Verify AffineMap cannot be created with a non-invertible matrix invalid_nan = np.zeros((dim + 1, dim + 1), dtype=np.float64) invalid_nan[1, 1] = np.nan invalid_zeros = np.zeros((dim + 1, dim + 1), dtype=np.float64) assert_raises( imaffine.AffineInvalidValuesError, imaffine.AffineMap, invalid_nan) assert_raises( AffineInvalidValuesError, imaffine.AffineMap, invalid_zeros) # Test exception is raised when the affine transform matrix is not # valid invalid_shape = np.eye(dim) affmap_invalid_shape = imaffine.AffineMap(invalid_shape, dom_shape[:dim], None, cod_shape[:dim], None) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when sampling info is not provided valid = np.eye(3) affmap_invalid_shape = imaffine.AffineMap(valid) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when requesting an invalid interpolation assert_raises(ValueError, affine_map.transform, img, 'invalid') assert_raises(ValueError, affine_map.transform_inverse, img, 'invalid') # Verify exception is raised when attempting to warp an image of # invalid dimension for dim in [2, 3]: affine_map = imaffine.AffineMap(np.eye(dim), cod_shape[:dim], None, dom_shape[:dim], None) for sh in [(2,), (2, 2, 2, 2)]: img = np.zeros(sh) assert_raises(ValueError, affine_map.transform, img) assert_raises(ValueError, affine_map.transform_inverse, img) aff_sing = np.zeros((dim + 1, dim + 1)) aff_nan = np.zeros((dim + 1, dim + 1)) aff_nan[...] = np.nan aff_inf = np.zeros((dim + 1, dim + 1)) aff_inf[...] = np.inf assert_raises( AffineInvalidValuesError, affine_map.set_affine, aff_sing) assert_raises(AffineInvalidValuesError, affine_map.set_affine, aff_nan) assert_raises(AffineInvalidValuesError, affine_map.set_affine, aff_inf) # Verify AffineMap can not be created with non-2D matrices : len(shape) != 2 for dim_not_2 in range(10): if dim_not_2 != _number_dim_affine_matrix: mat_large_dim = np.random.random([2]*dim_not_2) assert_raises(AffineInversionError, AffineMap, mat_large_dim)
def test_affine_map(): np.random.seed(2112927) dom_shape = np.array([64, 64, 64], dtype=np.int32) cod_shape = np.array([80, 80, 80], dtype=np.int32) nx = dom_shape[0] ny = dom_shape[1] nz = dom_shape[2] # Radius of the circle/sphere (testing image) radius = 16 # Rotation axis (used for 3D transforms only) rot_axis = np.array([.5, 2.0, 1.5]) # Arbitrary transform parameters t = 0.15 rotations = [-1 * np.pi / 10.0, 0.0, np.pi / 10.0] scales = [0.9, 1.0, 1.1] for dim in [2, 3]: # Setup current dimension if dim == 2: # Create image of a circle img = vf.create_circle(cod_shape[0], cod_shape[1], radius) oracle_linear = vf.transform_2d_affine oracle_nn = vf.transform_2d_affine_nn else: # Create image of a sphere img = vf.create_sphere(cod_shape[0], cod_shape[1], cod_shape[2], radius) oracle_linear = vf.transform_3d_affine oracle_nn = vf.transform_3d_affine_nn img = np.array(img) # Translation is the only parameter differing for 2D and 3D translations = [t * dom_shape[:dim]] # Generate affine transforms gt_affines = create_affine_transforms(dim, translations, rotations, scales, rot_axis) # Include the None case gt_affines.append(None) for affine in gt_affines: # make both domain point to the same physical region # It's ok to use the same transform, we just want to test # that this information is actually being considered domain_grid2world = affine codomain_grid2world = affine grid2grid_transform = affine # Evaluate the transform with vector_fields module (already tested) expected_linear = oracle_linear(img, dom_shape[:dim], grid2grid_transform) expected_nn = oracle_nn(img, dom_shape[:dim], grid2grid_transform) # Evaluate the transform with the implementation under test affine_map = imaffine.AffineMap(affine, dom_shape[:dim], domain_grid2world, cod_shape[:dim], codomain_grid2world) actual_linear = affine_map.transform(img, interp='linear') actual_nn = affine_map.transform(img, interp='nearest') assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Test set_affine with valid matrix affine_map.set_affine(affine) if affine is None: assert (affine_map.affine is None) assert (affine_map.affine_inv is None) else: assert_array_equal(affine, affine_map.affine) actual = affine_map.affine.dot(affine_map.affine_inv) assert_array_almost_equal(actual, np.eye(dim + 1)) # Evaluate via the inverse transform # AffineMap will use the inverse of the input matrix when we call # `transform_inverse`. Since the inverse of the inverse of a matrix # is not exactly equal to the original matrix (numerical limitations) # we need to invert the matrix twice to make sure the oracle and the # implementation under test apply the same transform aff_inv = None if affine is None else npl.inv(affine) aff_inv_inv = None if aff_inv is None else npl.inv(aff_inv) expected_linear = oracle_linear(img, dom_shape[:dim], aff_inv_inv) expected_nn = oracle_nn(img, dom_shape[:dim], aff_inv_inv) affine_map = imaffine.AffineMap(aff_inv, cod_shape[:dim], codomain_grid2world, dom_shape[:dim], domain_grid2world) actual_linear = affine_map.transform_inverse(img, interp='linear') actual_nn = affine_map.transform_inverse(img, interp='nearest') assert_array_almost_equal(actual_linear, expected_linear) assert_array_almost_equal(actual_nn, expected_nn) # Verify AffineMap cannot be created with a non-invertible matrix invalid_nan = np.zeros((dim + 1, dim + 1), dtype=np.float64) invalid_nan[1, 1] = np.nan invalid_zeros = np.zeros((dim + 1, dim + 1), dtype=np.float64) assert_raises(imaffine.AffineInversionError, imaffine.AffineMap, invalid_nan) assert_raises(imaffine.AffineInversionError, imaffine.AffineMap, invalid_zeros) # Test exception is raised when the affine transform matrix is not valid invalid_shape = np.eye(dim) affmap_invalid_shape = imaffine.AffineMap(invalid_shape, dom_shape[:dim], None, cod_shape[:dim], None) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when sampling info is not provided valid = np.eye(3) affmap_invalid_shape = imaffine.AffineMap(valid) assert_raises(ValueError, affmap_invalid_shape.transform, img) assert_raises(ValueError, affmap_invalid_shape.transform_inverse, img) # Verify exception is raised when requesting an invalid interpolation assert_raises(ValueError, affine_map.transform, img, 'invalid') assert_raises(ValueError, affine_map.transform_inverse, img, 'invalid') # Verify exception is raised when attempting to warp an image of # invalid dimension for dim in [2, 3]: affine_map = imaffine.AffineMap(np.eye(dim), cod_shape[:dim], None, dom_shape[:dim], None) for sh in [(2, ), (2, 2, 2, 2)]: img = np.zeros(sh) assert_raises(ValueError, affine_map.transform, img) assert_raises(ValueError, affine_map.transform_inverse, img) aff_sing = np.zeros((dim + 1, dim + 1)) aff_nan = np.zeros((dim + 1, dim + 1)) aff_nan[...] = np.nan aff_inf = np.zeros((dim + 1, dim + 1)) aff_inf[...] = np.inf assert_raises(AffineInversionError, affine_map.set_affine, aff_sing) assert_raises(AffineInversionError, affine_map.set_affine, aff_nan) assert_raises(AffineInversionError, affine_map.set_affine, aff_inf)