Ejemplo n.º 1
0
def _handle_pipeline_inputs(moving,
                            static,
                            static_affine=None,
                            moving_affine=None,
                            starting_affine=None):
    """
    Helper function to prepare inputs for pipeline functions

    Parameters
    ----------
    moving, static: Either as a 3D/4D array or as a nifti image object, or as
        a string containing the full path to a nifti file.

    static_affine, moving_affine: 2D arrays.
        The array associated with the static/moving images.

    starting_affine : 2D array, optional.
        This is the registration matrix that is inherited from previous steps
        in the pipeline. Default: 4-by-4 identity matrix.
    """
    static, static_affine = read_img_arr_or_path(static, affine=static_affine)
    moving, moving_affine = read_img_arr_or_path(moving, affine=moving_affine)
    if starting_affine is None:
        starting_affine = np.eye(4)

    return static, static_affine, moving, moving_affine, starting_affine
Ejemplo n.º 2
0
def test_read_img_arr_or_path():
    data = np.random.rand(4, 4, 4, 3)
    aff = np.eye(4)
    aff[:3, :] = np.random.randn(3, 4)
    img = Nifti1Image(data, aff)
    path = tempfile.NamedTemporaryFile().name + '.nii.gz'
    save(img, path)
    for this in [data, img, path]:
        dd, aa = read_img_arr_or_path(this, affine=aff)
        assert np.allclose(dd, data)
        assert np.allclose(aa, aff)

    # Tests that if an array is provided, but no affine, an error is raised:
    with pytest.raises(ValueError):
        read_img_arr_or_path(data)

    # Tests that the affine is recovered correctly from path:
    dd, aa = read_img_arr_or_path(path)
    assert np.allclose(dd, data)
    assert np.allclose(aa, aff)
Ejemplo n.º 3
0
def hmc(data, gtab, mask=None, b0_ref=0, affine=None):
    data, affine = read_img_arr_or_path(data, affine=affine)
    if isinstance(gtab, collections.Sequence):
        gtab = dpg.gradient_table(*gtab)

    # We fix b0 to be one volume, registered to one of the
    # b0 volumes (first, per default):
    if np.sum(gtab.b0s_mask) > 1:
        b0_img = nib.Nifti1Image(data[..., gtab.b0s_mask], affine)
        trans_b0, b0_affines = register_series(b0_img, ref=b0_ref)
        ref_data = np.mean(trans_b0, -1)[..., np.newaxis]
    else:
        # There's only one b0 and we register everything to it
        trans_b0 = ref_data = data[..., gtab.b0s_mask]
        b0_affines = np.eye(4)[..., np.newaxis]

    moving_data = data[..., ~gtab.b0s_mask]
    moving_bvals = gtab.bvals[~gtab.b0s_mask]
    moving_bvecs = gtab.bvecs[~gtab.b0s_mask]

    clf = FRR4SFM(fracs=0.5)
    for loo in range(moving_data.shape[-1]):
        loo_idxer = np.ones(moving_data.shape[-1]).astype(bool)
        loo_idxer[loo] = False

        in_data = np.concatenate([ref_data, moving_data[..., loo_idxer]], -1)
        in_gtab = dpg.gradient_table(
            np.concatenate([np.array([0]), moving_bvals[loo_idxer]]),
            np.concatenate([np.array([[0, 0, 0]]), moving_bvecs[loo_idxer]]))

        in_sfm = SFM4HMC(
            in_gtab,
            #isotropic=ExponentialIsotropicModel,
            solver=clf)

        in_sff = in_sfm.fit(in_data)#, mask=np.ones(in_data.shape[:3]))

        # out_data = moving_data[..., ~loo_idxer]
        out_gtab = dpg.gradient_table(moving_bvals[~loo_idxer],
                                      moving_bvecs[~loo_idxer])
        out_pred = in_sff.predict(out_gtab, S0=ref_data[..., 0])
        nib.save(nib.Nifti1Image(out_pred, affine),
                 '/Users/arokem/tmp/target.nii.gz')
        1/0.
Ejemplo n.º 4
0
def register_dwi_series(data, gtab, affine=None, b0_ref=0, pipeline=None):
    """
    Register a DWI series to the mean of the B0 images in that series (all
    first registered to the first B0 volume)

    Parameters
    ----------
    data : 4D array or nibabel Nifti1Image class instance or str
        Diffusion data. Either as a 4D array or as a nifti image object, or as
        a string containing the full path to a nifti file.

    gtab : a GradientTable class instance or tuple of strings
        If provided as a tuple of strings, these are assumed to be full paths
        to the bvals and bvecs files (in that order).

    affine : 4x4 array, optional.
        Must be provided for `data` provided as an array. If provided together
        with Nifti1Image or str `data`, this input will over-ride the affine
        that is stored in the `data` input. Default: use the affine stored
        in `data`.

    b0_ref : int, optional.
        Which b0 volume to use as reference. Default: 0

    pipeline : list of callables, optional.
        The transformations to perform in sequence (from left to right):
        Default: `[center_of_mass, translation, rigid, affine]`


    Returns
    -------
    xform_img, affine_array: a Nifti1Image containing the registered data and
    using the affine of the original data and a list containing the affine
    transforms associated with each of the

    """
    if pipeline is None:
        [center_of_mass, translation, rigid, affine]

    data, affine = read_img_arr_or_path(data, affine=affine)
    if isinstance(gtab, collections.Sequence):
        gtab = dpg.gradient_table(*gtab)

    if np.sum(gtab.b0s_mask) > 1:
        # First, register the b0s into one image and average:
        b0_img = nib.Nifti1Image(data[..., gtab.b0s_mask], affine)
        trans_b0, b0_affines = register_series(b0_img,
                                               ref=b0_ref,
                                               pipeline=pipeline)
        ref_data = np.mean(trans_b0, -1)
    else:
        # There's only one b0 and we register everything to it
        trans_b0 = ref_data = data[..., gtab.b0s_mask]
        b0_affines = np.eye(4)[..., np.newaxis]

    # Construct a series out of the DWI and the registered mean B0:
    moving_data = data[..., ~gtab.b0s_mask]
    series_arr = np.concatenate([ref_data, moving_data], -1)
    series = nib.Nifti1Image(series_arr, affine)

    xformed, affines = register_series(series, ref=0, pipeline=pipeline)
    # Cut out the part pertaining to that first volume:
    affines = affines[..., 1:]
    xformed = xformed[..., 1:]
    affine_array = np.zeros((4, 4, data.shape[-1]))
    affine_array[..., gtab.b0s_mask] = b0_affines
    affine_array[..., ~gtab.b0s_mask] = affines

    data_array = np.zeros(data.shape)
    data_array[..., gtab.b0s_mask] = trans_b0
    data_array[..., ~gtab.b0s_mask] = xformed

    return nib.Nifti1Image(data_array, affine), affine_array
Ejemplo n.º 5
0
def register_series(series,
                    ref,
                    pipeline=None,
                    series_affine=None,
                    ref_affine=None):
    """Register a series to a reference image.

    Parameters
    ----------
    series : 4D array or nib.Nifti1Image class instance or str
        The data is 4D with the last dimension separating different 3D volumes

    ref : int or 3D array or nib.Nifti1Image class instance or str
        If this is an int, this is the index of the reference image within the
        series. Otherwise it is an array of data to register with (associated
        with a `ref_affine` required) or a nifti img or full path to a file
        containing one.

    pipeline : sequence, optional
        Sequence of transforms to do for each volume in the series.
        Default: (executed from left to right):
        `[center_of_mass, translation, rigid, affine]`

    series_affine, ref_affine : 4x4 arrays, optional.
        The affine. If provided, this input will over-ride the affine provided
        together with the nifti img or file.

    Returns
    -------
    xformed, affines : 4D array with transformed data and a (4,4,n) array
    with 4x4 matrices associated with each of the volumes of the input moving
    data that was used to transform it into register with the static data.
    """
    pipeline = pipeline or [center_of_mass, translation, rigid, affine]

    series, series_affine = read_img_arr_or_path(series, affine=series_affine)
    if isinstance(ref, numbers.Number):
        ref_as_idx = ref
        idxer = np.zeros(series.shape[-1]).astype(bool)
        idxer[ref] = True
        ref = series[..., idxer].squeeze()
        ref_affine = series_affine
    else:
        ref_as_idx = False
        ref, ref_affine = read_img_arr_or_path(ref, affine=ref_affine)
        if len(ref.shape) != 3:
            raise ValueError("The reference image should be a single volume",
                             " or the index of one or more volumes")

    xformed = np.zeros(series.shape)
    affines = np.zeros((4, 4, series.shape[-1]))
    for ii in range(series.shape[-1]):
        this_moving = series[..., ii]
        if isinstance(ref_as_idx, numbers.Number) and ii == ref_as_idx:
            # This is the reference! No need to move and the xform is I(4):
            xformed[..., ii] = this_moving
            affines[..., ii] = np.eye(4)
        else:
            transformed, reg_affine = affine_registration(
                this_moving,
                ref,
                moving_affine=series_affine,
                static_affine=ref_affine,
                pipeline=pipeline)
            xformed[..., ii] = transformed
            affines[..., ii] = reg_affine

    return xformed, affines
Ejemplo n.º 6
0
def register_dwi_to_template(dwi,
                             gtab,
                             dwi_affine=None,
                             template=None,
                             template_affine=None,
                             reg_method="syn",
                             **reg_kwargs):
    """
    Register DWI data to a template through the B0 volumes.

    Parameters
    -----------
    dwi : 4D array, nifti image or str
        Containing the DWI data, or full path to a nifti file with DWI.
    gtab : GradientTable or sequence of strings
        The gradients associated with the DWI data, or a sequence with
        (fbval, fbvec), full paths to bvals and bvecs files.
    dwi_affine : 4x4 array, optional
        An affine transformation associated with the DWI. Required if data
        is provided as an array. If provided together with nifti/path,
        will over-ride the affine that is in the nifti.
    template : 3D array, nifti image or str
        Containing the data for the template, or full path to a nifti file
        with the template data.
    template_affine : 4x4 array, optional
        An affine transformation associated with the template. Required if data
        is provided as an array. If provided together with nifti/path,
        will over-ride the affine that is in the nifti.

    reg_method : str,
        One of "syn" or "aff", which designates which registration method is
        used. Either syn, which uses the :func:`syn_registration` function
        or :func:`affine_registration` function. Default: "syn".
    reg_kwargs : key-word arguments for :func:`syn_registration` or
        :func:`affine_registration`

    Returns
    -------
    warped_b0, mapping: The fist is an array with the b0 volume warped to the
    template. If reg_method is "syn", the second is a DiffeomorphicMap class
    instance that can be used to transform between the two spaces. Otherwise,
    if reg_method is "aff", this is a 4x4 matrix encoding the affine transform.

    Notes
    -----
    This function assumes that the DWI data is already internally registered.
    See :func:`register_dwi_series`.

    """
    dwi_data, dwi_affine = read_img_arr_or_path(dwi, affine=dwi_affine)

    if template is None:
        template = dpd.read_mni_template()

    template_data, template_affine = read_img_arr_or_path(
        template, affine=template_affine)

    if not isinstance(gtab, dpg.GradientTable):
        gtab = dpg.gradient_table(*gtab)

    mean_b0 = np.mean(dwi_data[..., gtab.b0s_mask], -1)
    if reg_method.lower() == "syn":
        warped_b0, mapping = syn_registration(mean_b0,
                                              template_data,
                                              moving_affine=dwi_affine,
                                              static_affine=template_affine,
                                              **reg_kwargs)
    elif reg_method.lower() == "aff":
        warped_b0, mapping = affine_registration(mean_b0,
                                                 template_data,
                                                 moving_affine=dwi_affine,
                                                 static_affine=template_affine,
                                                 **reg_kwargs)
    else:
        raise ValueError("reg_method should be one of 'aff' or 'syn', but you"
                         " provided %s" % reg_method)

    return warped_b0, mapping
Ejemplo n.º 7
0
Archivo: core.py Proyecto: nrdg/hmc
def hmc(data, gtab, mask=None, b0_ref=0, affine=None):
    data, affine = read_img_arr_or_path(data, affine=affine)
    if isinstance(gtab, collections.Sequence):
        gtab = dpg.gradient_table(*gtab)

    # We fix b0 to be one volume, registered to one of the
    # b0 volumes (first, per default):
    if np.sum(gtab.b0s_mask) > 1:
        b0_img = nib.Nifti1Image(data[..., gtab.b0s_mask], affine)
        trans_b0, b0_affines = dpa.register_series(b0_img, ref=b0_ref)
        ref_data = np.mean(trans_b0, -1)[..., np.newaxis]
    else:
        # There's only one b0 and we register everything to it
        trans_b0 = ref_data = data[..., gtab.b0s_mask]

    moving_data = data[..., ~gtab.b0s_mask]
    moving_bvals = gtab.bvals[~gtab.b0s_mask]
    moving_bvecs = gtab.bvecs[~gtab.b0s_mask]
    mask = np.ones(ref_data.shape[:3], dtype=bool)
    mask[np.where(ref_data[..., 0] == 0)] = False
    moved = []
    affines = []

    # We fit the isotropic prediction once for all the data:
    sfm_all = SFM4HMC(
        gtab,
        isotropic=ExponentialIsotropicModel)

    sff_all, iso_params = sfm_all.fit(data, alpha=10e-10, mask=mask, tol=10e-10, iso_params=None)

    for loo in range(moving_data.shape[-1]):
        print(loo)
        loo_idxer = np.ones(moving_data.shape[-1]).astype(bool)
        loo_idxer[loo] = False

        in_data = np.concatenate([ref_data, moving_data[..., loo_idxer]], -1)
        in_gtab = dpg.gradient_table(
            np.concatenate([np.array([0]), moving_bvals[loo_idxer]]),
            np.concatenate([np.array([[0, 0, 0]]), moving_bvecs[loo_idxer]]))

        sfm = SFM4HMC(
            in_gtab,
            isotropic=ExponentialIsotropicModel)

        t1 = time.time()
        sff, _ = sfm.fit(in_data, mask=mask, alpha=10e-10, iso_params=iso_params)
        t2 = time.time()
        print(t2 - t1)
        out_data = moving_data[..., ~loo_idxer]
        out_gtab = dpg.gradient_table(moving_bvals[~loo_idxer],
                                      moving_bvecs[~loo_idxer])

        out_pred = sff.predict(out_gtab, S0=ref_data[..., 0])
        t1 = time.time()
        resampled, out_affine = dpa.affine_registration(
            out_data[..., 0],
            out_pred,
            moving_affine=affine,
            static_affine=affine,
            pipeline=[dpa.affine],
            level_iters=[1000, 100, 10])
        t2 = time.time()
        print(t2 - t1)
        moved.append(resampled)
        affines.append(out_affine)
        in_data[..., loo] = resampled
        # XXX Also rotate the b-vector here
        new_out_gtab = dpg.reorient_bvecs(out_gtab, [out_affine])
        moving_bvals[~loo_idxer] = new_out_gtab.bvals
        moving_bvecs[~loo_idxer] = new_out_gtab.bvecs

    return moved, affines

# Reuse USV from a single SVD decomposition of X at the beginning of each
# loop through all of the volumes. Should speed up the RR fit in every volume.
# <= We can't do that, because the directions are different each time

# Use a sliding window to fit only to n nearest neighbors.

# Fit isotropic component once per voxel and be done with it