예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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)
예제 #7
0
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)