def get_data(): #dname = '/home/eleftherios/Data/Test_data_Jasmeen/Elef_Test_RecoBundles/' dname = '/home/eleftherios/Data/Elef_Test_RecoBundles/' fname = dname + 'tracts.trk' fname_npz = dname + 'tracts.npz' streamlines = nib.streamlines.compact_list.load_compact_list(fname_npz) streamlines = streamlines[::10].copy() streamlines._data -= np.mean(streamlines._data, axis=0) # Rotate brain to see a sagital view. R1 = np.eye(4) R1[:3, :3] = rodrigues_axis_rotation((0, 1, 0), theta=90) R2 = np.eye(4) R2[:3, :3] = rodrigues_axis_rotation((0, 0, 1), theta=90) R = np.dot(R2, R1) streamlines._data = apply_affine(R, streamlines._data) # renderer = window.Renderer() # bundle_actor = actor.line(streamlines) # renderer.add(bundle_actor) # window.show(renderer) return streamlines
def calc_euler(evecs): """ Calculate the Euler angles that rotate from the canonical coordinate frame to a coordinate frame defined by a set of eigenvectors. Parameters ---------- evecs : 3-by-3 array """ rot0 = np.eye(4) # What is the rotation from the first eigenvector to eye(3)? rot0[:3, :3] = vec2vec_rotmat(evecs[0], np.eye(3)[0]) # Decompose (we only need the angles) scale, shear, angles0, translate, perspective = decompose_matrix(rot0) # Convert angles to Euler matrix: em = euler_matrix(*angles0) # Now, we need another rotation to bring the second eigenvector to the right # direction ang1 = np.arccos( np.dot(evecs[1], em[1, :3]) / (np.linalg.norm(evecs[1]) * np.linalg.norm(em[1, :3]))) rar = np.eye(4) # The rar is a matrix that rotates for a given angle around a given # vector: rar[:3, :3] = rodrigues_axis_rotation(evecs[0], np.rad2deg(ang1)) # We combine these two rotations and decompose the combined matrix to give # us three Euler angles, which will be our parameters scale, shear, angles, translate, perspective = decompose_matrix(em @ rar) return angles
def create_affine_transforms(dim, translations, rotations, scales, rot_axis=None): r""" Creates a list of affine transforms with all combinations of params This function is intended to be used for testing only. It generates affine transforms for all combinations of the input parameters in the following order: let T be a translation, R a rotation and S a scale. The generated affine will be: A = T.dot(S).dot(R).dot(T^{-1}) Translation is handled this way because it is convenient to provide the translation parameters in terms of the center of rotation we wish to generate. Parameters ---------- dim: int (either dim=2 or dim=3) dimension of the affine transforms translations: sequence of dim-tuples each dim-tuple represents a translation parameter rotations: sequence of floats each number represents a rotation angle in radians scales: sequence of floats each number represents a scale rot_axis: rotation axis (used for dim=3 only) Returns ------- transforms: sequence of (dim + 1)x(dim + 1) matrices each matrix correspond to an affine transform with a combination of the input parameters """ transforms = [] for t in translations: trans_inv = np.eye(dim + 1) trans_inv[:dim, dim] = -t[:dim] trans = npl.inv(trans_inv) for theta in rotations: # rotation angle if dim == 2: ct = np.cos(theta) st = np.sin(theta) rot = np.array([[ct, -st, 0], [st, ct, 0], [0, 0, 1]]) else: rot = np.eye(dim + 1) rot[:3, :3] = geometry.rodrigues_axis_rotation(rot_axis, theta) for s in scales: # scale scale = np.eye(dim + 1) * s scale[dim, dim] = 1 affine = trans.dot(scale.dot(rot.dot(trans_inv))) transforms.append(affine) return transforms
def create_affine_transforms( dim, translations, rotations, scales, rot_axis=None): r""" Creates a list of affine transforms with all combinations of params This function is intended to be used for testing only. It generates affine transforms for all combinations of the input parameters in the following order: let T be a translation, R a rotation and S a scale. The generated affine will be: A = T.dot(S).dot(R).dot(T^{-1}) Translation is handled this way because it is convenient to provide the translation parameters in terms of the center of rotation we wish to generate. Parameters ---------- dim: int (either dim=2 or dim=3) dimension of the affine transforms translations: sequence of dim-tuples each dim-tuple represents a translation parameter rotations: sequence of floats each number represents a rotation angle in radians scales: sequence of floats each number represents a scale rot_axis: rotation axis (used for dim=3 only) Returns ------- transforms: sequence of (dim + 1)x(dim + 1) matrices each matrix correspond to an affine transform with a combination of the input parameters """ transforms = [] for t in translations: trans_inv = np.eye(dim + 1) trans_inv[:dim, dim] = -t[:dim] trans = npl.inv(trans_inv) for theta in rotations: # rotation angle if dim == 2: ct = np.cos(theta) st = np.sin(theta) rot = np.array([[ct, -st, 0], [st, ct, 0], [0, 0, 1]]) else: rot = np.eye(dim + 1) rot[:3, :3] = geometry.rodrigues_axis_rotation(rot_axis, theta) for s in scales: # scale scale = np.eye(dim + 1) * s scale[dim, dim] = 1 affine = trans.dot(scale.dot(rot.dot(trans_inv))) transforms.append(affine) return transforms
def test_transform_centers_of_mass_3d(): np.random.seed(1246592) shape = (64, 64, 64) rm = 8 sp = vf.create_sphere(shape[0] // 2, shape[1] // 2, shape[2] // 2, rm) moving = np.zeros(shape) # The center of mass will be (16, 16, 16), in image coordinates moving[:shape[0] // 2, :shape[1] // 2, :shape[2] // 2] = sp[...] rs = 16 # The center of mass will be (32, 32, 32), in image coordinates static = vf.create_sphere(shape[0], shape[1], shape[2], rs) # Create arbitrary image-to-space transforms axis = np.array([.5, 2.0, 1.5]) t = 0.15 # translation factor 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 = npl.inv(trans) for rotation_angle in [-1 * np.pi / 6.0, 0.0, np.pi / 5.0]: for scale_factor in [0.83, 1.3, 2.07]: # scale rot = np.zeros(shape=(4, 4)) rot[:3, :3] = geometry.rodrigues_axis_rotation(axis, rotation_angle) rot[3, 3] = 1.0 scale = np.array([[1 * scale_factor, 0, 0, 0], [0, 1 * scale_factor, 0, 0], [0, 0, 1 * scale_factor, 0], [0, 0, 0, 1]]) static_grid2world = trans_inv.dot(scale.dot(rot.dot(trans))) moving_grid2world = npl.inv(static_grid2world) # Expected translation c_static = static_grid2world.dot((32, 32, 32, 1))[:3] c_moving = moving_grid2world.dot((16, 16, 16, 1))[:3] expected = np.eye(4) expected[:3, 3] = c_moving - c_static # Implementation under test actual = imaffine.transform_centers_of_mass(static, static_grid2world, moving, moving_grid2world) assert_array_almost_equal(actual.affine, expected)
def test_transform_geometric_centers_3d(): # Create arbitrary image-to-space transforms axis = np.array([.5, 2.0, 1.5]) t = 0.15 # translation factor for theta in [-1 * np.pi / 6.0, 0.0, np.pi / 5.0]: # rotation angle for s in [0.83, 1.3, 2.07]: # scale m_shapes = [(256, 256, 128), (255, 255, 127), (64, 127, 142)] for shape_moving in m_shapes: s_shapes = [(256, 256, 128), (255, 255, 127), (64, 127, 142)] for shape_static in s_shapes: moving = np.ndarray(shape=shape_moving) static = np.ndarray(shape=shape_static) trans = np.array([[1, 0, 0, -t * shape_static[0]], [0, 1, 0, -t * shape_static[1]], [0, 0, 1, -t * shape_static[2]], [0, 0, 0, 1]]) trans_inv = npl.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]]) static_grid2world = trans_inv.dot( scale.dot(rot.dot(trans))) moving_grid2world = npl.inv(static_grid2world) # Expected translation c_static = np.array(shape_static, dtype=np.float64) * 0.5 c_static = tuple(c_static) c_static = static_grid2world.dot(c_static + (1,))[:3] c_moving = np.array(shape_moving, dtype=np.float64) * 0.5 c_moving = tuple(c_moving) c_moving = moving_grid2world.dot(c_moving + (1,))[:3] expected = np.eye(4) expected[:3, 3] = c_moving - c_static # Implementation under test actual = imaffine.transform_geometric_centers( static, static_grid2world, moving, moving_grid2world) assert_array_almost_equal(actual.affine, expected)
def test_metric_minimum_average_direct_flip(): feature = dipymetric.IdentityFeature() class MinimumAverageDirectFlipMetric(dipymetric.Metric): def __init__(self, feature): super(MinimumAverageDirectFlipMetric, self).__init__(feature=feature) @property def is_order_invariant(self): return True # Ordering is handled in the distance computation def are_compatible(self, shape1, shape2): return shape1[0] == shape2[0] def dist(self, v1, v2): average_euclidean = lambda x, y: np.mean(norm(x-y, axis=1)) dist_direct = average_euclidean(v1, v2) dist_flipped = average_euclidean(v1, v2[::-1]) return min(dist_direct, dist_flipped) for metric in [MinimumAverageDirectFlipMetric(feature), dipymetric.MinimumAverageDirectFlipMetric(feature)]: # Test special cases of the MDF distance. assert_equal(metric.dist(s, s), 0.) assert_equal(metric.dist(s, s[::-1]), 0.) # Translation offset = np.array([0.8, 1.3, 5], dtype=dtype) assert_almost_equal(metric.dist(s, s+offset), norm(offset), 5) # Scaling M_scaling = np.diag([1.2, 2.8, 3]).astype(dtype) s_mean = np.mean(s, axis=0) s_zero_mean = s - s_mean s_scaled = np.dot(M_scaling, s_zero_mean.T).T + s_mean d = np.mean(norm((np.diag(M_scaling)-1)*s_zero_mean, axis=1)) assert_almost_equal(metric.dist(s, s_scaled), d, 5) # Rotation from dipy.core.geometry import rodrigues_axis_rotation rot_axis = np.array([1, 2, 3], dtype=dtype) M_rotation = rodrigues_axis_rotation(rot_axis, 60.).astype(dtype) s_mean = np.mean(s, axis=0) s_zero_mean = s - s_mean s_rotated = np.dot(M_rotation, s_zero_mean.T).T + s_mean opposite = norm(np.cross(rot_axis, s_zero_mean), axis=1) / norm(rot_axis) distances = np.sqrt(2*opposite**2 * (1 - np.cos(60.*np.pi/180.))).astype(dtype) d = np.mean(distances) assert_almost_equal(metric.dist(s, s_rotated), d, 5) for s1, s2 in itertools.product(*[streamlines]*2): # All possible pairs # Extract features since metric doesn't work directly on streamlines f1 = metric.feature.extract(s1) f2 = metric.feature.extract(s2) # Test method are_compatible same_nb_points = f1.shape[0] == f2.shape[0] assert_equal(metric.are_compatible(f1.shape, f2.shape), same_nb_points) # Test method dist if features are compatible if metric.are_compatible(f1.shape, f2.shape): distance = metric.dist(f1, f2) if np.all(f1 == f2): assert_equal(distance, 0.) assert_almost_equal(distance, dipymetric.dist(metric, s1, s2)) assert_almost_equal(distance, dipymetric.mdf(s1, s2)) assert_true(distance >= 0.) # This metric type is order invariant assert_true(metric.is_order_invariant) for s1, s2 in itertools.product(*[streamlines]*2): # All possible pairs f1 = metric.feature.extract(s1) f2 = metric.feature.extract(s2) if not metric.are_compatible(f1.shape, f2.shape): continue f1_flip = metric.feature.extract(s1[::-1]) f2_flip = metric.feature.extract(s2[::-1]) distance = metric.dist(f1, f2) assert_almost_equal(metric.dist(f1_flip, f2_flip), distance) if not np.all(f1_flip == f2_flip): assert_true(np.allclose(metric.dist(f1, f2_flip), distance)) assert_true(np.allclose(metric.dist(f1_flip, f2), distance))
def test_metric_minimum_average_direct_flip(): feature = dipymetric.IdentityFeature() class MinimumAverageDirectFlipMetric(dipymetric.Metric): def __init__(self, feature): super(MinimumAverageDirectFlipMetric, self).__init__(feature=feature) @property def is_order_invariant(self): return True # Ordering is handled in the distance computation def are_compatible(self, shape1, shape2): return shape1[0] == shape2[0] def dist(self, v1, v2): def average_euclidean(x, y): return np.mean(norm(x - y, axis=1)) dist_direct = average_euclidean(v1, v2) dist_flipped = average_euclidean(v1, v2[::-1]) return min(dist_direct, dist_flipped) for metric in [ MinimumAverageDirectFlipMetric(feature), dipymetric.MinimumAverageDirectFlipMetric(feature) ]: # Test special cases of the MDF distance. assert_equal(metric.dist(s, s), 0.) assert_equal(metric.dist(s, s[::-1]), 0.) # Translation offset = np.array([0.8, 1.3, 5], dtype=dtype) assert_almost_equal(metric.dist(s, s + offset), norm(offset), 5) # Scaling M_scaling = np.diag([1.2, 2.8, 3]).astype(dtype) s_mean = np.mean(s, axis=0) s_zero_mean = s - s_mean s_scaled = np.dot(M_scaling, s_zero_mean.T).T + s_mean d = np.mean(norm((np.diag(M_scaling) - 1) * s_zero_mean, axis=1)) assert_almost_equal(metric.dist(s, s_scaled), d, 5) # Rotation from dipy.core.geometry import rodrigues_axis_rotation rot_axis = np.array([1, 2, 3], dtype=dtype) M_rotation = rodrigues_axis_rotation(rot_axis, 60.).astype(dtype) s_mean = np.mean(s, axis=0) s_zero_mean = s - s_mean s_rotated = np.dot(M_rotation, s_zero_mean.T).T + s_mean opposite = norm(np.cross(rot_axis, s_zero_mean), axis=1) / norm(rot_axis) distances = np.sqrt(2 * opposite**2 * (1 - np.cos(60. * np.pi / 180.))).astype(dtype) d = np.mean(distances) assert_almost_equal(metric.dist(s, s_rotated), d, 5) # All possible pairs for s1, s2 in itertools.product(*[streamlines] * 2): # Extract features since metric doesn't work # directly on streamlines f1 = metric.feature.extract(s1) f2 = metric.feature.extract(s2) # Test method are_compatible same_nb_points = f1.shape[0] == f2.shape[0] assert_equal(metric.are_compatible(f1.shape, f2.shape), same_nb_points) # Test method dist if features are compatible if metric.are_compatible(f1.shape, f2.shape): distance = metric.dist(f1, f2) if np.all(f1 == f2): assert_equal(distance, 0.) assert_almost_equal(distance, dipymetric.dist(metric, s1, s2)) assert_almost_equal(distance, dipymetric.mdf(s1, s2)) assert_true(distance >= 0.) # This metric type is order invariant assert_true(metric.is_order_invariant) # All possible pairs for s1, s2 in itertools.product(*[streamlines] * 2): f1 = metric.feature.extract(s1) f2 = metric.feature.extract(s2) if not metric.are_compatible(f1.shape, f2.shape): continue f1_flip = metric.feature.extract(s1[::-1]) f2_flip = metric.feature.extract(s2[::-1]) distance = metric.dist(f1, f2) assert_almost_equal(metric.dist(f1_flip, f2_flip), distance) if not np.all(f1_flip == f2_flip): assert_true(np.allclose(metric.dist(f1, f2_flip), distance)) assert_true(np.allclose(metric.dist(f1_flip, f2), distance))
X[2, ...] = x_2[None, None, :] * O X[3, ...] = 1 # Select an arbitrary rotation axis axis = np.array([.5, 2.0, 1.5]) # Select an arbitrary translation matrix t = 0.1 trans = np.array([[1, 0, 0, -t * ns], [0, 1, 0, -t * nr], [0, 0, 1, -t * nc], [0, 0, 0, 1]]) trans_inv = np.linalg.inv(trans) # Select arbitrary rotation and scaling matrices theta = np.pi / 5.0 # rotation angle s = 1.1 # scale 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]]) 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)
def test_invert_vector_field(shape): r""" Inverts a synthetic, analytically invertible, displacement field """ ndim = len(shape) if ndim == 3: ns = shape[0] nr = shape[1] nc = shape[2] # Create an arbitrary image-to-space transform # Select an arbitrary rotation axis axis = np.array([2.0, 0.5, 1.0]) t = 2.5 # translation factor trans = np.array([ [1, 0, 0, -t * ns], [0, 1, 0, -t * nr], [0, 0, 1, -t * nc], [0, 0, 0, 1], ]) dipy_create_func = vfu.create_harmonic_fields_3d dipy_reorient_func = vfu.reorient_vector_field_3d dipy_invert_func = vfu.invert_vector_field_fixed_point_3d elif ndim == 2: nr = shape[0] nc = shape[1] # Create an arbitrary image-to-space transform t = 2.5 # translation factor trans = np.array([[1, 0, -t * nr], [0, 1, -t * nc], [0, 0, 1]]) dipy_create_func = vfu.create_harmonic_fields_2d dipy_reorient_func = vfu.reorient_vector_field_2d dipy_invert_func = vfu.invert_vector_field_fixed_point_2d trans_inv = np.linalg.inv(trans) d, _ = dipy_create_func(*shape, 0.2, 8) d = np.asarray(d).astype(floating) for theta in [-1 * np.pi / 5.0, 0.0, np.pi / 5.0]: # rotation angle for s in [0.5, 1.0, 2.0]: # scale if ndim == 3: 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: 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]]) gt_affine = trans_inv.dot(scale.dot(rot.dot(trans))) gt_affine_inv = np.linalg.inv(gt_affine) dcopy = np.copy(d) dcopyd = cupy.asarray(dcopy) gt_affined = cupy.asarray(gt_affine) gt_affine_invd = cupy.asarray(gt_affine_inv) # make sure the field remains invertible after the re-mapping dipy_reorient_func(dcopy, gt_affine) # TODO: can't do in-place computation unless out= is supplied and # dcopy has the dimensions axis first instead of last dcopyd = reorient_vector_field(dcopyd, gt_affined) cupy.testing.assert_array_almost_equal(dcopyd, dcopy, decimal=4) # Note: the spacings are used just to check convergence, so they # don't need to be very accurate. Here we are passing (0.5 * s) to # force the algorithm to make more iterations: in ANTS, there is a # hard-coded bound on the maximum residual, that's why we cannot # force more iteration by changing the parameters. # We will investigate this issue with more detail in the future. if False: from cupyx.time import repeat perf = repeat( invert_vector_field_fixed_point, ( dcopyd, gt_affine_invd, cupy.asarray([s, s, s]) * 0.5, 40, 1e-7, ), n_warmup=20, n_repeat=80, ) print(perf) perf = repeat( dipy_invert_func, ( dcopy, gt_affine_inv, np.asarray([s, s, s]) * 0.5, 40, 1e-7, ), n_warmup=0, n_repeat=8, ) print(perf) # if False: # from pyvolplot import volshow # from matplotlib import pyplot as plt # inv_approx, q, norms, tmp1, tmp2, epsilon, maxlen = vfu.invert_vector_field_fixed_point_3d_debug( # dcopy, gt_affine_inv, np.array([s, s, s]) * 0.5, max_iter=1, tol=1e-7 # ) # inv_approxd, qd, normsd, tmp1d, tmp2d, epsilond, maxlend = invert_vector_field_fixed_point( # dcopyd, gt_affine_invd, cupy.asarray([s, s, s]) * 0.5, max_iter=1, tol=1e-7 # ) inv_approxd = invert_vector_field_fixed_point( dcopyd, gt_affine_invd, cupy.asarray([s, s, s]) * 0.5, 40, 1e-7) if False: inv_approx = dipy_invert_func(dcopy, gt_affine_inv, np.array([s, s, s]) * 0.5, 40, 1e-7) cupy.testing.assert_allclose(inv_approx, inv_approxd, rtol=1e-2, atol=1e-2) # TODO: use GPU-based imwarp here once implemented mapping = imwarp.DiffeomorphicMap(ndim, shape, gt_affine) mapping.forward = dcopy mapping.backward = inv_approxd.get() residual, stats = mapping.compute_inversion_error() assert_almost_equal(stats[1], 0, decimal=3) assert_almost_equal(stats[2], 0, decimal=3)
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)