Exemple #1
0
def test_exceptions():
    H = ParzenJointHistogram(32)
    valid = np.empty((2, 2, 2), dtype=np.float64)
    invalid = np.empty((2, 2, 2, 2), dtype=np.float64)

    # Test exception from `ParzenJointHistogram.update_pdfs_dense`
    assert_raises(ValueError, H.update_pdfs_dense, valid, invalid)
    assert_raises(ValueError, H.update_pdfs_dense, invalid, valid)
    assert_raises(ValueError, H.update_pdfs_dense, invalid, invalid)

    # Test exception from `ParzenJointHistogram.update_gradient_dense`
    for shape in [(5, 5), (5, 5, 5)]:
        dim = len(shape)
        grid2world = np.eye(dim + 1)
        transform = regtransforms[('ROTATION', dim)]
        theta = transform.get_identity_parameters()
        valid_img = np.empty(shape, dtype=np.float64)
        valid_grad = np.empty(shape + (dim, ), dtype=np.float64)

        invalid_img = np.empty((2, 2, 2, 2), dtype=np.float64)
        invalid_grad_type = valid_grad.astype(np.int32)
        invalid_grad_dim = np.empty(shape + (dim + 1, ), dtype=np.float64)

        for s, m, g in [(valid_img, valid_img, invalid_grad_type),
                        (valid_img, valid_img, invalid_grad_dim),
                        (invalid_img, valid_img, valid_grad),
                        (invalid_img, invalid_img, invalid_grad_type),
                        (invalid_img, invalid_img, invalid_grad_dim)]:
            assert_raises(ValueError, H.update_gradient_dense, theta,
                          transform, s, m, grid2world, g)

    # Test exception from `ParzenJointHistogram.update_gradient_dense`
    nsamples = 2
    for dim in [2, 3]:
        transform = regtransforms[('ROTATION', dim)]
        theta = transform.get_identity_parameters()
        valid_vals = np.empty((nsamples, ), dtype=np.float64)
        valid_grad = np.empty((nsamples, dim), dtype=np.float64)
        valid_points = np.empty((nsamples, dim), dtype=np.float64)

        invalid_grad_type = np.empty((nsamples, dim), dtype=np.int32)
        invalid_grad_dim = np.empty((nsamples, dim + 2), dtype=np.float64)
        invalid_grad_len = np.empty((nsamples + 1, dim), dtype=np.float64)
        invalid_vals = np.empty((nsamples + 1), dtype=np.float64)
        invalid_points_dim = np.empty((nsamples, dim + 2), dtype=np.float64)
        invalid_points_len = np.empty((nsamples + 1, dim), dtype=np.float64)

        C = [(invalid_vals, valid_vals, valid_points, valid_grad),
             (valid_vals, invalid_vals, valid_points, valid_grad),
             (valid_vals, valid_vals, invalid_points_dim, valid_grad),
             (valid_vals, valid_vals, invalid_points_dim, invalid_grad_dim),
             (valid_vals, valid_vals, invalid_points_len, valid_grad),
             (valid_vals, valid_vals, valid_points, invalid_grad_type),
             (valid_vals, valid_vals, valid_points, invalid_grad_dim),
             (valid_vals, valid_vals, valid_points, invalid_grad_len)]

        for s, m, p, g in C:
            assert_raises(ValueError, H.update_gradient_sparse, theta,
                          transform, s, m, p, g)
Exemple #2
0
ftype = moving.dtype.type
out = np.empty(tuple(out_shape) + (dim, ), dtype=ftype)
inside = np.empty(tuple(out_shape), dtype=np.int32)
_gradient_3d(moving, moving_world2grid, moving_spacing, static_grid2world, out,
             inside)

mgrad = np.asarray(out)

from dipy.align.imaffine import AffineMap
dim = len(static.shape)
starting_affine = np.eye(dim + 1)
affine_map = AffineMap(starting_affine, static.shape, static_grid2world,
                       moving.shape, moving_grid2world)

static_values = static
moving_values = affine_map.transform(moving)

from dipy.align.transforms import AffineTransform3D
transform = AffineTransform3D()
params = transform.get_identity_parameters()

from dipy.align.parzenhist import ParzenJointHistogram
nbins = 32
histogram = ParzenJointHistogram(nbins)

static2prealigned = static_grid2world
histogram.update_gradient_dense(params, transform, static_values,
                                moving_values, static2prealigned, mgrad)

np.save('sl_aff_par_jpdf_jgrad.npy', histogram.joint_grad)
Exemple #3
0
def test_joint_pdf_gradients_dense():
    # Compare the analytical and numerical (finite differences) gradient of
    # the joint distribution (i.e. derivatives of each histogram cell) w.r.t.
    # the transform parameters. Since the histograms are discrete partitions
    # of the image intensities, the finite difference approximation is
    # normally not very close to the analytical derivatives. Other sources of
    # error are the interpolation used when transforming the images and the
    # boundary intensities introduced when interpolating outside of the image
    # (i.e. some "zeros" are introduced at the boundary which affect the
    # numerical derivatives but is not taken into account by the analytical
    # derivatives). Thus, we need to relax the verification. Instead of
    # looking for the analytical and numerical gradients to be very close to
    # each other, we will verify that they approximately point in the same
    # direction by testing if the angle they form is close to zero.
    h = 1e-4

    # Make sure dictionary entries are processed in the same order regardless
    # of the platform. Otherwise any random numbers drawn within the loop
    # would make the test non-deterministic even if we fix the seed before
    # the loop. Right now, this test does not draw any samples, but we still
    # sort the entries to prevent future related failures.
    for ttype in sorted(factors):
        dim = ttype[1]
        if dim == 2:
            nslices = 1
            transform_method = vf.transform_2d_affine
        else:
            nslices = 45
            transform_method = vf.transform_3d_affine

        transform = regtransforms[ttype]
        factor = factors[ttype]
        theta = transform.get_identity_parameters()

        static, moving, static_g2w, moving_g2w, smask, mmask, M = \
            setup_random_transform(transform, factor, nslices, 5.0)
        parzen_hist = ParzenJointHistogram(32)
        parzen_hist.setup(static, moving, smask, mmask)

        # Compute the gradient at theta with the implementation under test
        M = transform.param_to_matrix(theta)
        shape = np.array(static.shape, dtype=np.int32)

        moved = transform_method(moving.astype(np.float32), shape, M)
        moved = np.array(moved)
        parzen_hist.update_pdfs_dense(static.astype(np.float64),
                                      moved.astype(np.float64))
        # Get the joint distribution evaluated at theta
        J0 = np.copy(parzen_hist.joint)
        grid_to_space = np.eye(dim + 1)
        spacing = np.ones(dim, dtype=np.float64)
        mgrad, inside = vf.gradient(moving.astype(np.float32), moving_g2w,
                                    spacing, shape, grid_to_space)
        id = transform.get_identity_parameters()
        parzen_hist.update_gradient_dense(id, transform,
                                          static.astype(np.float64),
                                          moved.astype(np.float64),
                                          grid_to_space, mgrad, smask, mmask)
        actual = np.copy(parzen_hist.joint_grad)
        # Now we have the gradient of the joint distribution w.r.t. the
        # transform parameters

        # Compute the gradient using finite-diferences
        n = transform.get_number_of_parameters()
        expected = np.empty_like(actual)
        for i in range(n):
            dtheta = theta.copy()
            dtheta[i] += h
            # Update the joint distribution with the transformed moving image
            M = transform.param_to_matrix(dtheta)
            shape = np.array(static.shape, dtype=np.int32)
            moved = transform_method(moving.astype(np.float32), shape, M)
            moved = np.array(moved)
            parzen_hist.update_pdfs_dense(static.astype(np.float64),
                                          moved.astype(np.float64))
            J1 = np.copy(parzen_hist.joint)
            expected[..., i] = (J1 - J0) / h

        # Dot product and norms of gradients of each joint histogram cell
        # i.e. the derivatives of each cell w.r.t. all parameters
        P = (expected * actual).sum(2)
        enorms = np.sqrt((expected**2).sum(2))
        anorms = np.sqrt((actual**2).sum(2))
        prodnorms = enorms * anorms
        # Cosine of angle between the expected and actual gradients.
        # Exclude very small gradients
        P[prodnorms > 1e-6] /= (prodnorms[prodnorms > 1e-6])
        P[prodnorms <= 1e-6] = 0
        # Verify that a large proportion of the gradients point almost in
        # the same direction. Disregard very small gradients
        mean_cosine = P[P != 0].mean()
        std_cosine = P[P != 0].std()
        assert (mean_cosine > 0.9)
        assert (std_cosine < 0.25)
Exemple #4
0
def test_joint_pdf_gradients_sparse():
    h = 1e-4

    # Make sure dictionary entries are processed in the same order regardless
    # of the platform. Otherwise any random numbers drawn within the loop
    # would make the test non-deterministic even if we fix the seed before
    # the loop.Right now, this test does not draw any samples, but we still
    # sort the entries to prevent future related failures.

    for ttype in sorted(factors):
        dim = ttype[1]
        if dim == 2:
            nslices = 1
            interp_method = interpolate_scalar_2d
        else:
            nslices = 45
            interp_method = interpolate_scalar_3d

        transform = regtransforms[ttype]
        factor = factors[ttype]
        theta = transform.get_identity_parameters()

        static, moving, static_g2w, moving_g2w, smask, mmask, M = \
            setup_random_transform(transform, factor, nslices, 5.0)
        parzen_hist = ParzenJointHistogram(32)
        parzen_hist.setup(static, moving, smask, mmask)

        # Sample the fixed-image domain
        k = 3
        sigma = 0.25
        seed = 1234
        shape = np.array(static.shape, dtype=np.int32)
        samples = sample_domain_regular(k, shape, static_g2w, sigma, seed)
        samples = np.array(samples)
        samples = np.hstack((samples, np.ones(samples.shape[0])[:, None]))
        sp_to_static = np.linalg.inv(static_g2w)
        samples_static_grid = (sp_to_static.dot(samples.T).T)[..., :dim]
        intensities_static, inside = interp_method(static.astype(np.float32),
                                                   samples_static_grid)
        # The routines in vector_fields operate, mostly, with float32 because
        # they were thought to be used for non-linear registration. We may need
        # to write some float64 counterparts for affine registration, where
        # memory is not so big issue
        intensities_static = np.array(intensities_static, dtype=np.float64)

        # Compute the gradient at theta with the implementation under test
        M = transform.param_to_matrix(theta)
        sp_to_moving = np.linalg.inv(moving_g2w).dot(M)
        samples_moving_grid = (sp_to_moving.dot(samples.T).T)[..., :dim]
        intensities_moving, inside = interp_method(moving.astype(np.float32),
                                                   samples_moving_grid)
        intensities_moving = np.array(intensities_moving, dtype=np.float64)
        parzen_hist.update_pdfs_sparse(intensities_static, intensities_moving)
        # Get the joint distribution evaluated at theta
        J0 = np.copy(parzen_hist.joint)

        spacing = np.ones(dim + 1, dtype=np.float64)
        mgrad, inside = vf.sparse_gradient(moving.astype(np.float32),
                                           sp_to_moving, spacing, samples)
        parzen_hist.update_gradient_sparse(theta, transform,
                                           intensities_static,
                                           intensities_moving,
                                           samples[..., :dim], mgrad)
        # Get the gradient of the joint distribution w.r.t. the transform
        # parameters
        actual = np.copy(parzen_hist.joint_grad)

        # Compute the gradient using finite-diferences
        n = transform.get_number_of_parameters()
        expected = np.empty_like(actual)
        for i in range(n):
            dtheta = theta.copy()
            dtheta[i] += h
            # Update the joint distribution with the transformed moving image
            M = transform.param_to_matrix(dtheta)
            sp_to_moving = np.linalg.inv(moving_g2w).dot(M)
            samples_moving_grid = sp_to_moving.dot(samples.T).T
            intensities_moving, inside = \
                interp_method(moving.astype(np.float32), samples_moving_grid)
            intensities_moving = np.array(intensities_moving, dtype=np.float64)
            parzen_hist.update_pdfs_sparse(intensities_static,
                                           intensities_moving)
            J1 = np.copy(parzen_hist.joint)
            expected[..., i] = (J1 - J0) / h

        # Dot product and norms of gradients of each joint histogram cell
        # i.e. the derivatives of each cell w.r.t. all parameters
        P = (expected * actual).sum(2)
        enorms = np.sqrt((expected**2).sum(2))
        anorms = np.sqrt((actual**2).sum(2))
        prodnorms = enorms * anorms
        # Cosine of angle between the expected and actual gradients.
        # Exclude very small gradients
        P[prodnorms > 1e-6] /= (prodnorms[prodnorms > 1e-6])
        P[prodnorms <= 1e-6] = 0
        # Verify that a large proportion of the gradients point almost in
        # the same direction. Disregard very small gradients
        mean_cosine = P[P != 0].mean()
        std_cosine = P[P != 0].std()
        assert (mean_cosine > 0.99)
        assert (std_cosine < 0.15)
Exemple #5
0
def test_parzen_joint_histogram():
    # Test the simple functionality of ParzenJointHistogram,
    # the gradients and computation of the joint intensity distribution
    # will be tested independently
    for nbins in [15, 30, 50]:
        for min_int in [-10.0, 0.0, 10.0]:
            for intensity_range in [0.1, 1.0, 10.0]:
                fact = 1
                max_int = min_int + intensity_range
                P = ParzenJointHistogram(nbins)
                # Make a pair of 4-pixel images, introduce +/- 1 values
                # that will be excluded using a mask
                static = np.array(
                    [min_int - 1.0, min_int, max_int, max_int + 1.0])
                # Multiply by an arbitrary value (make the ranges different)
                moving = fact * np.array(
                    [min_int, min_int - 1.0, max_int + 1.0, max_int])
                # Create a mask to exclude the invalid values (beyond min and
                # max computed above)
                static_mask = np.array([0, 1, 1, 0])
                moving_mask = np.array([1, 0, 0, 1])

                P.setup(static, moving, static_mask, moving_mask)

                # Test bin_normalize_static at the boundary
                normalized = P.bin_normalize_static(min_int)
                assert_almost_equal(normalized, P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, P.padding)
                normalized = P.bin_normalize_static(max_int)
                assert_almost_equal(normalized, nbins - P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, nbins - 1 - P.padding)

                # Test bin_normalize_moving at the boundary
                normalized = P.bin_normalize_moving(fact * min_int)
                assert_almost_equal(normalized, P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, P.padding)
                normalized = P.bin_normalize_moving(fact * max_int)
                assert_almost_equal(normalized, nbins - P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, nbins - 1 - P.padding)

                # Test bin_index not at the boundary
                delta_s = (max_int - min_int) / (nbins - 2 * P.padding)
                delta_m = fact * (max_int - min_int) / (nbins - 2 * P.padding)
                for i in range(nbins - 2 * P.padding):
                    normalized = P.bin_normalize_static(min_int +
                                                        (i + 0.5) * delta_s)
                    index = P.bin_index(normalized)
                    assert_equal(index, P.padding + i)

                    normalized = P.bin_normalize_moving(fact * min_int +
                                                        (i + 0.5) * delta_m)
                    index = P.bin_index(normalized)
                    assert_equal(index, P.padding + i)
Exemple #6
0
def test_parzen_densities():
    # Test the computation of the joint intensity distribution
    # using a dense and a sparse set of values
    seed = 1246592
    nbins = 32
    nr = 30
    nc = 35
    ns = 20
    nvals = 50

    for dim in [2, 3]:
        if dim == 2:
            shape = (nr, nc)
            static, moving = create_random_image_pair(shape, nvals, seed)
        else:
            shape = (ns, nr, nc)
            static, moving = create_random_image_pair(shape, nvals, seed)

        # Initialize
        parzen_hist = ParzenJointHistogram(nbins)
        parzen_hist.setup(static, moving)
        # Get distributions computed by dense sampling
        parzen_hist.update_pdfs_dense(static, moving)
        actual_joint_dense = parzen_hist.joint
        actual_mmarginal_dense = parzen_hist.mmarginal
        actual_smarginal_dense = parzen_hist.smarginal

        # Get distributions computed by sparse sampling
        sval = static.reshape(-1)
        mval = moving.reshape(-1)
        parzen_hist.update_pdfs_sparse(sval, mval)
        actual_joint_sparse = parzen_hist.joint
        actual_mmarginal_sparse = parzen_hist.mmarginal
        actual_smarginal_sparse = parzen_hist.smarginal

        # Compute the expected joint distribution with dense sampling
        expected_joint_dense = np.zeros(shape=(nbins, nbins))
        for index in ndindex(shape):
            sv = parzen_hist.bin_normalize_static(static[index])
            mv = parzen_hist.bin_normalize_moving(moving[index])
            sbin = parzen_hist.bin_index(sv)
            # The spline is centered at mv, will evaluate for all row
            spline_arg = np.array([i - mv for i in range(nbins)])
            contribution = cubic_spline(spline_arg)
            expected_joint_dense[sbin, :] += contribution

        # Compute the expected joint distribution with sparse sampling
        expected_joint_sparse = np.zeros(shape=(nbins, nbins))
        for index in range(sval.shape[0]):
            sv = parzen_hist.bin_normalize_static(sval[index])
            mv = parzen_hist.bin_normalize_moving(mval[index])
            sbin = parzen_hist.bin_index(sv)
            # The spline is centered at mv, will evaluate for all row
            spline_arg = np.array([i - mv for i in range(nbins)])
            contribution = cubic_spline(spline_arg)
            expected_joint_sparse[sbin, :] += contribution

        # Verify joint distributions
        expected_joint_dense /= expected_joint_dense.sum()
        expected_joint_sparse /= expected_joint_sparse.sum()
        assert_array_almost_equal(actual_joint_dense, expected_joint_dense)
        assert_array_almost_equal(actual_joint_sparse, expected_joint_sparse)

        # Verify moving marginals
        expected_mmarginal_dense = expected_joint_dense.sum(0)
        expected_mmarginal_dense /= expected_mmarginal_dense.sum()
        expected_mmarginal_sparse = expected_joint_sparse.sum(0)
        expected_mmarginal_sparse /= expected_mmarginal_sparse.sum()
        assert_array_almost_equal(actual_mmarginal_dense,
                                  expected_mmarginal_dense)
        assert_array_almost_equal(actual_mmarginal_sparse,
                                  expected_mmarginal_sparse)

        # Verify static marginals
        expected_smarginal_dense = expected_joint_dense.sum(1)
        expected_smarginal_dense /= expected_smarginal_dense.sum()
        expected_smarginal_sparse = expected_joint_sparse.sum(1)
        expected_smarginal_sparse /= expected_smarginal_sparse.sum()
        assert_array_almost_equal(actual_smarginal_dense,
                                  expected_smarginal_dense)
        assert_array_almost_equal(actual_smarginal_sparse,
                                  expected_smarginal_sparse)
def test_joint_pdf_gradients_sparse():
    h = 1e-4

    # Make sure dictionary entries are processed in the same order regardless of
    # the platform. Otherwise any random numbers drawn within the loop would make
    # the test non-deterministic even if we fix the seed before the loop.
    # Right now, this test does not draw any samples, but we still sort the entries
    # to prevent future related failures.

    for ttype in sorted(factors):
        dim = ttype[1]
        if dim == 2:
            nslices = 1
            interp_method = vf.interpolate_scalar_2d
        else:
            nslices = 45
            interp_method = vf.interpolate_scalar_3d

        transform = regtransforms[ttype]
        factor = factors[ttype]
        theta = transform.get_identity_parameters()

        static, moving, static_g2w, moving_g2w, smask, mmask, M = \
            setup_random_transform(transform, factor, nslices, 5.0)
        parzen_hist = ParzenJointHistogram(32)
        parzen_hist.setup(static, moving, smask, mmask)

        # Sample the fixed-image domain
        k = 3
        sigma = 0.25
        seed = 1234
        shape = np.array(static.shape, dtype=np.int32)
        samples = sample_domain_regular(k, shape, static_g2w, sigma, seed)
        samples = np.array(samples)
        samples = np.hstack((samples, np.ones(samples.shape[0])[:, None]))
        sp_to_static = np.linalg.inv(static_g2w)
        samples_static_grid = (sp_to_static.dot(samples.T).T)[..., :dim]
        intensities_static, inside = interp_method(static.astype(np.float32),
                                                   samples_static_grid)
        # The routines in vector_fields operate, mostly, with float32 because
        # they were thought to be used for non-linear registration. We may need
        # to write some float64 counterparts for affine registration, where
        # memory is not so big issue
        intensities_static = np.array(intensities_static, dtype=np.float64)

        # Compute the gradient at theta with the implementation under test
        M = transform.param_to_matrix(theta)
        sp_to_moving = np.linalg.inv(moving_g2w).dot(M)
        samples_moving_grid = (sp_to_moving.dot(samples.T).T)[..., :dim]
        intensities_moving, inside = interp_method(moving.astype(np.float32),
                                                   samples_moving_grid)
        intensities_moving = np.array(intensities_moving, dtype=np.float64)
        parzen_hist.update_pdfs_sparse(intensities_static, intensities_moving)
        # Get the joint distribution evaluated at theta
        J0 = np.copy(parzen_hist.joint)

        spacing = np.ones(dim + 1, dtype=np.float64)
        mgrad, inside = vf.sparse_gradient(moving.astype(np.float32),
                                           sp_to_moving, spacing, samples)
        parzen_hist.update_gradient_sparse(theta, transform, intensities_static,
                                      intensities_moving, samples[..., :dim],
                                      mgrad)
        # Get the gradient of the joint distribution w.r.t. the transform
        # parameters
        actual = np.copy(parzen_hist.joint_grad)

        # Compute the gradient using finite-diferences
        n = transform.get_number_of_parameters()
        expected = np.empty_like(actual)
        for i in range(n):
            dtheta = theta.copy()
            dtheta[i] += h
            # Update the joint distribution with the transformed moving image
            M = transform.param_to_matrix(dtheta)
            sp_to_moving = np.linalg.inv(moving_g2w).dot(M)
            samples_moving_grid = sp_to_moving.dot(samples.T).T
            intensities_moving, inside = \
                interp_method(moving.astype(np.float32), samples_moving_grid)
            intensities_moving = np.array(intensities_moving, dtype=np.float64)
            parzen_hist.update_pdfs_sparse(intensities_static, intensities_moving)
            J1 = np.copy(parzen_hist.joint)
            expected[..., i] = (J1 - J0) / h

        # Dot product and norms of gradients of each joint histogram cell
        # i.e. the derivatives of each cell w.r.t. all parameters
        P = (expected * actual).sum(2)
        enorms = np.sqrt((expected ** 2).sum(2))
        anorms = np.sqrt((actual ** 2).sum(2))
        prodnorms = enorms*anorms
        # Cosine of angle between the expected and actual gradients.
        # Exclude very small gradients
        P[prodnorms > 1e-6] /= (prodnorms[prodnorms > 1e-6])
        P[prodnorms <= 1e-6] = 0
        # Verify that a large proportion of the gradients point almost in
        # the same direction. Disregard very small gradients
        mean_cosine = P[P != 0].mean()
        std_cosine = P[P != 0].std()
        assert(mean_cosine > 0.99)
        assert(std_cosine < 0.15)
def test_joint_pdf_gradients_dense():
    # Compare the analytical and numerical (finite differences) gradient of the
    # joint distribution (i.e. derivatives of each histogram cell) w.r.t. the
    # transform parameters. Since the histograms are discrete partitions of the
    # image intensities, the finite difference approximation is normally not
    # very close to the analytical derivatives. Other sources of error are the
    # interpolation used when transforming the images and the boundary intensities
    # introduced when interpolating outside of the image (i.e. some "zeros" are
    # introduced at the boundary which affect the numerical derivatives but is
    # not taken into account by the analytical derivatives). Thus, we need to
    # relax the verification. Instead of looking for the analytical and
    # numerical gradients to be very close to each other, we will verify that
    # they approximately point in the same direction by testing if the angle
    # they form is close to zero.
    h = 1e-4

    # Make sure dictionary entries are processed in the same order regardless of
    # the platform. Otherwise any random numbers drawn within the loop would make
    # the test non-deterministic even if we fix the seed before the loop.
    # Right now, this test does not draw any samples, but we still sort the entries
    # to prevent future related failures.
    for ttype in sorted(factors):
        dim = ttype[1]
        if dim == 2:
            nslices = 1
            transform_method = vf.transform_2d_affine
        else:
            nslices = 45
            transform_method = vf.transform_3d_affine

        transform = regtransforms[ttype]
        factor = factors[ttype]
        theta = transform.get_identity_parameters()

        static, moving, static_g2w, moving_g2w, smask, mmask, M = \
            setup_random_transform(transform, factor, nslices, 5.0)
        parzen_hist = ParzenJointHistogram(32)
        parzen_hist.setup(static, moving, smask, mmask)

        # Compute the gradient at theta with the implementation under test
        M = transform.param_to_matrix(theta)
        shape = np.array(static.shape, dtype=np.int32)

        moved = transform_method(moving.astype(np.float32), shape, M)
        moved = np.array(moved)
        parzen_hist.update_pdfs_dense(static.astype(np.float64),
                                 moved.astype(np.float64))
        # Get the joint distribution evaluated at theta
        J0 = np.copy(parzen_hist.joint)
        grid_to_space = np.eye(dim + 1)
        spacing = np.ones(dim, dtype=np.float64)
        mgrad, inside = vf.gradient(moving.astype(np.float32), moving_g2w,
                                    spacing, shape, grid_to_space)
        id = transform.get_identity_parameters()
        parzen_hist.update_gradient_dense(id, transform, static.astype(np.float64),
                                     moved.astype(np.float64), grid_to_space,
                                     mgrad, smask, mmask)
        actual = np.copy(parzen_hist.joint_grad)
        # Now we have the gradient of the joint distribution w.r.t. the
        # transform parameters

        # Compute the gradient using finite-diferences
        n = transform.get_number_of_parameters()
        expected = np.empty_like(actual)
        for i in range(n):
            dtheta = theta.copy()
            dtheta[i] += h
            # Update the joint distribution with the transformed moving image
            M = transform.param_to_matrix(dtheta)
            shape = np.array(static.shape, dtype=np.int32)
            moved = transform_method(moving.astype(np.float32), shape, M)
            moved = np.array(moved)
            parzen_hist.update_pdfs_dense(static.astype(np.float64),
                                          moved.astype(np.float64))
            J1 = np.copy(parzen_hist.joint)
            expected[..., i] = (J1 - J0) / h

        # Dot product and norms of gradients of each joint histogram cell
        # i.e. the derivatives of each cell w.r.t. all parameters
        P = (expected * actual).sum(2)
        enorms = np.sqrt((expected ** 2).sum(2))
        anorms = np.sqrt((actual ** 2).sum(2))
        prodnorms = enorms * anorms
        # Cosine of angle between the expected and actual gradients.
        # Exclude very small gradients
        P[prodnorms > 1e-6] /= (prodnorms[prodnorms > 1e-6])
        P[prodnorms <= 1e-6] = 0
        # Verify that a large proportion of the gradients point almost in
        # the same direction. Disregard very small gradients
        mean_cosine = P[P != 0].mean()
        std_cosine = P[P != 0].std()
        assert(mean_cosine > 0.9)
        assert(std_cosine < 0.25)
def test_parzen_densities():
    # Test the computation of the joint intensity distribution
    # using a dense and a sparse set of values
    seed = 1246592
    nbins = 32
    nr = 30
    nc = 35
    ns = 20
    nvals = 50

    for dim in [2, 3]:
        if dim == 2:
            shape = (nr, nc)
            static, moving = create_random_image_pair(shape, nvals, seed)
        else:
            shape = (ns, nr, nc)
            static, moving = create_random_image_pair(shape, nvals, seed)

        # Initialize
        parzen_hist = ParzenJointHistogram(nbins)
        parzen_hist.setup(static, moving)
        # Get distributions computed by dense sampling
        parzen_hist.update_pdfs_dense(static, moving)
        actual_joint_dense = parzen_hist.joint
        actual_mmarginal_dense = parzen_hist.mmarginal
        actual_smarginal_dense = parzen_hist.smarginal

        # Get distributions computed by sparse sampling
        sval = static.reshape(-1)
        mval = moving.reshape(-1)
        parzen_hist.update_pdfs_sparse(sval, mval)
        actual_joint_sparse = parzen_hist.joint
        actual_mmarginal_sparse = parzen_hist.mmarginal
        actual_smarginal_sparse = parzen_hist.smarginal

        # Compute the expected joint distribution with dense sampling
        expected_joint_dense = np.zeros(shape=(nbins, nbins))
        for index in ndindex(shape):
            sv = parzen_hist.bin_normalize_static(static[index])
            mv = parzen_hist.bin_normalize_moving(moving[index])
            sbin = parzen_hist.bin_index(sv)
            # The spline is centered at mv, will evaluate for all row
            spline_arg = np.array([i - mv for i in range(nbins)])
            contribution = cubic_spline(spline_arg)
            expected_joint_dense[sbin, :] += contribution

        # Compute the expected joint distribution with sparse sampling
        expected_joint_sparse = np.zeros(shape=(nbins, nbins))
        for index in range(sval.shape[0]):
            sv = parzen_hist.bin_normalize_static(sval[index])
            mv = parzen_hist.bin_normalize_moving(mval[index])
            sbin = parzen_hist.bin_index(sv)
            # The spline is centered at mv, will evaluate for all row
            spline_arg = np.array([i - mv for i in range(nbins)])
            contribution = cubic_spline(spline_arg)
            expected_joint_sparse[sbin, :] += contribution

        # Verify joint distributions
        expected_joint_dense /= expected_joint_dense.sum()
        expected_joint_sparse /= expected_joint_sparse.sum()
        assert_array_almost_equal(actual_joint_dense, expected_joint_dense)
        assert_array_almost_equal(actual_joint_sparse, expected_joint_sparse)

        # Verify moving marginals
        expected_mmarginal_dense = expected_joint_dense.sum(0)
        expected_mmarginal_dense /= expected_mmarginal_dense.sum()
        expected_mmarginal_sparse = expected_joint_sparse.sum(0)
        expected_mmarginal_sparse /= expected_mmarginal_sparse.sum()
        assert_array_almost_equal(actual_mmarginal_dense,
                                  expected_mmarginal_dense)
        assert_array_almost_equal(actual_mmarginal_sparse,
                                  expected_mmarginal_sparse)

        # Verify static marginals
        expected_smarginal_dense = expected_joint_dense.sum(1)
        expected_smarginal_dense /= expected_smarginal_dense.sum()
        expected_smarginal_sparse = expected_joint_sparse.sum(1)
        expected_smarginal_sparse /= expected_smarginal_sparse.sum()
        assert_array_almost_equal(actual_smarginal_dense,
                                  expected_smarginal_dense)
        assert_array_almost_equal(actual_smarginal_sparse,
                                  expected_smarginal_sparse)
Exemple #10
0
def test_parzen_joint_histogram():
    # Test the simple functionality of ParzenJointHistogram,
    # the gradients and computation of the joint intensity distribution
    # will be tested independently
    for nbins in [15, 30, 50]:
        for min_int in [-10.0, 0.0, 10.0]:
            for intensity_range in [0.1, 1.0, 10.0]:
                fact = 1
                max_int = min_int + intensity_range
                P = ParzenJointHistogram(nbins)
                # Make a pair of 4-pixel images, introduce +/- 1 values
                # that will be excluded using a mask
                static = np.array([min_int - 1.0, min_int,
                                   max_int, max_int + 1.0])
                # Multiply by an arbitrary value (make the ranges different)
                moving = fact * np.array([min_int, min_int - 1.0,
                                          max_int + 1.0, max_int])
                # Create a mask to exclude the invalid values (beyond min and
                # max computed above)
                static_mask = np.array([0, 1, 1, 0])
                moving_mask = np.array([1, 0, 0, 1])

                P.setup(static, moving, static_mask, moving_mask)

                # Test bin_normalize_static at the boundary
                normalized = P.bin_normalize_static(min_int)
                assert_almost_equal(normalized, P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, P.padding)
                normalized = P.bin_normalize_static(max_int)
                assert_almost_equal(normalized, nbins - P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, nbins - 1 - P.padding)

                # Test bin_normalize_moving at the boundary
                normalized = P.bin_normalize_moving(fact * min_int)
                assert_almost_equal(normalized, P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, P.padding)
                normalized = P.bin_normalize_moving(fact * max_int)
                assert_almost_equal(normalized, nbins - P.padding)
                index = P.bin_index(normalized)
                assert_equal(index, nbins - 1 - P.padding)

                # Test bin_index not at the boundary
                delta_s = (max_int - min_int) / (nbins - 2 * P.padding)
                delta_m = fact * (max_int - min_int) / (nbins - 2 * P.padding)
                for i in range(nbins - 2 * P.padding):
                    normalized = P.bin_normalize_static(min_int +
                                                        (i + 0.5) * delta_s)
                    index = P.bin_index(normalized)
                    assert_equal(index, P.padding + i)

                    normalized = P.bin_normalize_moving(fact * min_int +
                                                        (i + 0.5) * delta_m)
                    index = P.bin_index(normalized)
                    assert_equal(index, P.padding + i)
Exemple #11
0
fetch_syn_data()
_, nib_syn_b0 = read_syn_data()
moving = np.array(nib_syn_b0.get_data())
moving_grid2world = nib_syn_b0.affine

static = ((static.astype(np.float64) - static.min()) /
          (static.max() - static.min()))
moving = ((moving.astype(np.float64) - moving.min()) /
          (moving.max() - moving.min()))

static = np.array(static).astype(np.float64)
moving = np.array(moving).astype(np.float64)

from dipy.align.imaffine import AffineMap
dim = len(static.shape)
starting_affine = np.eye(dim + 1)
affine_map = AffineMap(starting_affine, static.shape, static_grid2world,
                       moving.shape, moving_grid2world)

static_values = static
moving_values = affine_map.transform(moving)

from dipy.align.parzenhist import ParzenJointHistogram
nbins = 32
histogram = ParzenJointHistogram(nbins)
histogram.update_pdfs_dense(static_values, moving_values)

np.save('sl_aff_par_cpdf_joint', histogram.joint)
np.save('sl_aff_par_cpdf_smarg', histogram.smarginal)
np.save('sl_aff_par_cpdf_mmarg', histogram.mmarginal)