Ejemplo n.º 1
0
def par2referenceFSL (par_file, session, par_cycle):
    rigids_euler = np.array(pd.read_csv(par_file, header=None, delimiter='\s+'))
    number_of_cycles = rigids_euler.shape[0]
    rigids = np.empty((number_of_cycles, 4, 4))
    for i in range(number_of_cycles):
        mat = euler2mat(
            -rigids_euler[i,0], # gieren / yaw   / z
            -rigids_euler[i,1], # rollen / roll  / y
            rigids_euler[i,2]) # nicken / pitch / x
        vec = rigids_euler[i,3:]
        rigids[i] = from_matvec(
            matrix=mat,
            vector=np.array((-vec[1], vec[0], -vec[2])))
    reference_maps = ReferenceMaps(session.name)
    reference_maps.temporal_resolution = session.temporal_resolution
    reference_maps.slice_timing = session.slice_timing
    reference_maps.set_acquisition_maps(Affines(rigids))
    reference_maps.shape = (session.numob, session.shape[session.ep])

    # The realignment parameters (Euler angles and transformations)
    # saved by McFLIRT are given with respect to the centre of mass of
    # the reference scan cycle (Why do you have to make *everything* so
    # complicated, FSL?!). We need to apply a translation from the
    # centre of mass of the reference cycle to the coordinate system of
    # the session.

    n,x,y,z = session.data.shape
    indices = ((slice(0,x), slice(0,y), slice(0,z)))
    lattice = session.reference.apply_to_indices(indices)
    lattice = np.moveaxis(lattice, -1 ,0)
    com = (session.raw[par_cycle] * lattice).sum(axis=(1,2,3)) / session.raw[par_cycle].sum()

    translation_from_com = Affine(from_matvec(np.eye(3), -com))
    reference_maps.reset_reference_space(x=translation_from_com)
    return reference_maps
Ejemplo n.º 2
0
def test_adapt_affine():
    # Adapt affine to missing or extra input dimensions
    aff_3d = from_matvec(np.arange(9).reshape((3, 3)), [11, 12, 13])
    # For 4x4 affine, 3D image, no-op
    assert_array_equal(adapt_affine(aff_3d, 3), aff_3d)
    # For 4x4 affine, 4D image, add extra identity dimension
    assert_array_equal(adapt_affine(aff_3d, 4),
                       [[ 0,  1,  2,  0,  11],
                        [ 3,  4,  5,  0,  12],
                        [ 6,  7,  8,  0,  13],
                        [ 0,  0,  0,  1,   0],
                        [ 0,  0,  0,  0,   1]])
    # For 5x5 affine, 4D image, identity
    aff_4d = from_matvec(np.arange(16).reshape((4, 4)), [11, 12, 13, 14])
    assert_array_equal(adapt_affine(aff_4d, 4), aff_4d)
    # For 4x4 affine, 2D image, dropped column
    assert_array_equal(adapt_affine(aff_3d, 2),
                       [[ 0,  1,  11],
                        [ 3,  4,  12],
                        [ 6,  7,  13],
                        [ 0,  0,   1]])
    # For 4x4 affine, 1D image, 2 dropped columnn
    assert_array_equal(adapt_affine(aff_3d, 1),
                       [[ 0,  11],
                        [ 3,  12],
                        [ 6,  13],
                        [ 0,   1]])
    # For 3x3 affine, 2D image, identity
    aff_2d = from_matvec(np.arange(4).reshape((2, 2)), [11, 12])
    assert_array_equal(adapt_affine(aff_2d, 2), aff_2d)
Ejemplo n.º 3
0
def test_load_spaces():
    # Test spaces get read correctly
    shape = np.array((6, 5, 4, 3, 2))
    zooms = np.array((2, 3, 4, 5, 6))
    data = np.random.normal(size=shape)
    # Default with no affine in header, or in image
    ni_img = nib.Nifti1Image(data, None)
    hdr = get_header(ni_img)
    hdr.set_zooms(zooms)
    # Expected affine is from the pixdims and the center of the image.  Default
    # is also flipped X.
    offsets = (1 - shape[:3]) / 2. * zooms[:3] * (-1, 1, 1)
    exp_aff = from_matvec(np.diag([-2, 3, 4, 5, 6]),
                          list(offsets) + [0, 0])
    in_cs = CS('ijktu', name='voxels')
    exp_cmap = AT(in_cs, unknown_csm(5), exp_aff)
    assert_equal(nifti2nipy(ni_img).coordmap, exp_cmap)
    an_aff = from_matvec(np.diag([1.1, 2.2, 3.3]), [10, 11, 12])
    exp_aff = from_matvec(np.diag([1.1, 2.2, 3.3, 5, 6]), [10, 11, 12, 0, 0])
    for label, csm in (('scanner', scanner_csm),
                       ('aligned', aligned_csm),
                       ('talairach', talairach_csm),
                       ('mni', mni_csm)):
        hdr.set_sform(an_aff, label)
        assert_equal(nifti2nipy(ni_img).coordmap, AT(in_cs, csm(5), exp_aff))
Ejemplo n.º 4
0
def test_adapt_affine():
    # Adapt affine to missing or extra input dimensions
    aff_3d = from_matvec(np.arange(9).reshape((3, 3)), [11, 12, 13])
    # For 4x4 affine, 3D image, no-op
    assert_array_equal(adapt_affine(aff_3d, 3), aff_3d)
    # For 4x4 affine, 4D image, add extra identity dimension
    assert_array_equal(adapt_affine(aff_3d, 4),
                       [[ 0,  1,  2,  0,  11],
                        [ 3,  4,  5,  0,  12],
                        [ 6,  7,  8,  0,  13],
                        [ 0,  0,  0,  1,   0],
                        [ 0,  0,  0,  0,   1]])
    # For 5x5 affine, 4D image, identity
    aff_4d = from_matvec(np.arange(16).reshape((4, 4)), [11, 12, 13, 14])
    assert_array_equal(adapt_affine(aff_4d, 4), aff_4d)
    # For 4x4 affine, 2D image, dropped column
    assert_array_equal(adapt_affine(aff_3d, 2),
                       [[ 0,  1,  11],
                        [ 3,  4,  12],
                        [ 6,  7,  13],
                        [ 0,  0,   1]])
    # For 4x4 affine, 1D image, 2 dropped columnn
    assert_array_equal(adapt_affine(aff_3d, 1),
                       [[ 0,  11],
                        [ 3,  12],
                        [ 6,  13],
                        [ 0,   1]])
    # For 3x3 affine, 2D image, identity
    aff_2d = from_matvec(np.arange(4).reshape((2, 2)), [11, 12])
    assert_array_equal(adapt_affine(aff_2d, 2), aff_2d)
Ejemplo n.º 5
0
def test_mm_scaling():
    # Test the micron and meter scale the affine right
    data = np.random.normal(size=list(range(4)))
    xyz_aff = from_matvec(np.diag([2, 3, 4]), [11, 12, 13])
    exp_aff = from_matvec(np.diag([2, 3, 4, 1]), [11, 12, 13, 0])
    in_cs = CS('ijkt', name='voxels')
    out_cs = aligned_csm(4)
    # No space scaling
    ni_img = nib.Nifti1Image(data, xyz_aff)
    hdr = get_header(ni_img)
    assert_equal(hdr.get_xyzt_units(), ('unknown', 'unknown'))
    assert_equal(nifti2nipy(ni_img).coordmap, AT(in_cs, out_cs, exp_aff))
    # mm is assumed
    hdr.set_xyzt_units('mm')
    assert_equal(nifti2nipy(ni_img).coordmap, AT(in_cs, out_cs, exp_aff))
    # microns !
    hdr.set_xyzt_units('micron')
    scaler = np.diag([1 / 1000., 1 / 1000., 1 / 1000., 1, 1])
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(in_cs, out_cs, np.dot(scaler, exp_aff)))
    # mm again !  This test implicitly asserts that the nifti image affine is
    # not being changed by the conversion routine, otherwise we'd pick up the
    # microns scaling above.
    hdr.set_xyzt_units('mm')
    assert_equal(nifti2nipy(ni_img).coordmap, AT(in_cs, out_cs, exp_aff))
    # meters !
    hdr.set_xyzt_units('meter')
    scaler = np.diag([1000., 1000., 1000., 1, 1])
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(in_cs, out_cs, np.dot(scaler, exp_aff)))
Ejemplo n.º 6
0
 def to_ras(self, moving=None, reference=None):
     """Return a nitransforms internal RAS+ matrix."""
     sa = self.structarr
     matrix = sa['parameters']
     offset = sa['offset']
     c_neg = from_matvec(np.eye(3), offset * -1.0)
     c_pos = from_matvec(np.eye(3), offset)
     return LPS.dot(c_pos.dot(matrix.dot(c_neg.dot(LPS))))
Ejemplo n.º 7
0
def test_save_toffset():
    # Check toffset only gets set for time
    shape = (2, 3, 4, 5, 6, 7)
    data = np.random.normal(size = shape)
    aff = from_matvec(np.diag([2., 3, 4, 5, 6, 7]),
                              [11, 12, 13, 14, 15, 16])
    xyz_names = talairach_csm(3).coord_names
    in_cs = CS('ijklmn')
    for t_name in 't', 'time':
        cmap = AT(in_cs, CS(xyz_names + (t_name, 'q', 'r')), aff)
        ni_img = nipy2nifti(Image(data, cmap))
        assert_equal(get_header(ni_img)['toffset'], 14)
    for time_like in ('hz', 'ppm', 'rads'):
        cmap = AT(in_cs, CS(xyz_names + (time_like, 'q', 'r')), aff)
        ni_img = nipy2nifti(Image(data, cmap))
        assert_equal(get_header(ni_img)['toffset'], 0)
    # Check that non-matching time causes a nifti error when toffset !=0
    shape_shifted = (2, 3, 4, 6, 5, 7)
    for t_name in 't', 'time':
        # No toffset, this is OK
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  np.diag([3, 4, 5, 6, 0, 7, 1]))
        assert_equal(nipy2nifti(Image(data, cmap)).shape, shape_shifted)
        # toffset with 0 on TR (time) diagonal
        aff_z1 = from_matvec(np.diag([2., 3, 4, 5, 0, 7]),
                             [11, 12, 13, 14, 15, 16])
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff_z1)
        # Default is to fix the zero
        assert_equal(nipy2nifti(Image(data, cmap)).shape,
                     shape_shifted)
        assert_equal(nipy2nifti(Image(data, cmap), fix0=True).shape,
                     shape_shifted)
        # Unless fix0 is False
        assert_raises(NiftiError, nipy2nifti, Image(data, cmap), fix0=False)
        # Fix doesn't work if there is more than one zero row and column
        aff_z2 = from_matvec(np.diag([2., 3, 4, 0, 0, 7]),
                             [11, 12, 13, 14, 15, 16])
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff_z2)
        assert_raises(NiftiError, nipy2nifti, Image(data, cmap), fix0=True)
    # zeros on the diagonal are not a problem for non-time, with toffset,
    # because we don't need to set the 'time' part of the translation vector,
    # and therefore we don't need to know which *output axis* is time-like
    for t_name in 'hz', 'ppm', 'rads':
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff_z1)
        assert_equal(nipy2nifti(Image(data, cmap), fix0=False).shape,
                     shape_shifted)
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff_z2)
        assert_equal(nipy2nifti(Image(data, cmap), fix0=False).shape,
                     shape_shifted)
Ejemplo n.º 8
0
def test_affines_save(image_orientation):
    """Check implementation of exporting affines to formats."""
    # Generate test transform
    img = loadimg(SOMEONES_ANATOMY)
    imgaff = img.affine

    if image_orientation == 'LAS':
        newaff = imgaff.copy()
        newaff[0, 0] *= -1.0
        newaff[0, 3] = imgaff.dot(np.hstack(
            (np.array(img.shape[:3]) - 1, 1.0)))[0]
        img = Nifti1Image(np.flip(img.get_fdata(), 0), newaff, img.header)
    elif image_orientation == 'LPS':
        newaff = imgaff.copy()
        newaff[0, 0] *= -1.0
        newaff[1, 1] *= -1.0
        newaff[:2,
               3] = imgaff.dot(np.hstack(
                   (np.array(img.shape[:3]) - 1, 1.0)))[:2]
        img = Nifti1Image(np.flip(np.flip(img.get_fdata(), 0), 1), newaff,
                          img.header)
    elif image_orientation == 'oblique':
        A = shape_zoom_affine(img.shape, img.header.get_zooms(), x_flip=False)
        R = from_matvec(euler2mat(x=0.09, y=0.001, z=0.001))
        newaff = R.dot(A)
        img = Nifti1Image(img.get_fdata(), newaff, img.header)
        img.header.set_qform(newaff, 1)
        img.header.set_sform(newaff, 1)

    T = from_matvec(euler2mat(x=0.9, y=0.001, z=0.001), [4.0, 2.0, -1.0])

    xfm = nbl.Affine(T)
    xfm.reference = img

    itk = nbl.load(os.path.join(data_path,
                                'affine-%s-itk.tfm' % image_orientation),
                   fmt='itk')
    fsl = np.loadtxt(
        os.path.join(data_path, 'affine-%s.fsl' % image_orientation))
    afni = np.loadtxt(
        os.path.join(data_path, 'affine-%s.afni' % image_orientation))

    with InTemporaryDirectory():
        xfm.to_filename('M.tfm', fmt='itk')
        xfm.to_filename('M.fsl', fmt='fsl')
        xfm.to_filename('M.afni', fmt='afni')

        nb_itk = nbl.load('M.tfm', fmt='itk')
        nb_fsl = np.loadtxt('M.fsl')
        nb_afni = np.loadtxt('M.afni')

    assert_equal(itk, nb_itk)
    assert_almost_equal(fsl, nb_fsl)
    assert_almost_equal(afni, nb_afni)


# Create version not aligned to canonical
Ejemplo n.º 9
0
def test_save_toffset():
    # Check toffset only gets set for time
    shape = (2, 3, 4, 5, 6, 7)
    data = np.random.normal(size = shape)
    aff = from_matvec(np.diag([2., 3, 4, 5, 6, 7]),
                              [11, 12, 13, 14, 15, 16])
    xyz_names = talairach_csm(3).coord_names
    in_cs = CS('ijklmn')
    for t_name in 't', 'time':
        cmap = AT(in_cs, CS(xyz_names + (t_name, 'q', 'r')), aff)
        ni_img = nipy2nifti(Image(data, cmap))
        assert_equal(ni_img.get_header()['toffset'], 14)
    for time_like in ('hz', 'ppm', 'rads'):
        cmap = AT(in_cs, CS(xyz_names + (time_like, 'q', 'r')), aff)
        ni_img = nipy2nifti(Image(data, cmap))
        assert_equal(ni_img.get_header()['toffset'], 0)
    # Check that non-matching time causes a nifti error when toffset !=0
    shape_shifted = (2, 3, 4, 6, 5, 7)
    for t_name in 't', 'time':
        # No toffset, this is OK
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  np.diag([3, 4, 5, 6, 0, 7, 1]))
        assert_equal(nipy2nifti(Image(data, cmap)).shape, shape_shifted)
        # toffset, non-matching error
        aff_z1 = from_matvec(np.diag([2., 3, 4, 5, 0, 7]),
                             [11, 12, 13, 14, 15, 16])
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff_z1)
        assert_raises(NiftiError, nipy2nifti, Image(data, cmap))
        # Unless fix0 set
        assert_equal(nipy2nifti(Image(data, cmap), fix0=True).shape,
                     shape_shifted)
        # Even this doesn't work if there is more than one zero row and column
        aff_z2 = from_matvec(np.diag([2., 3, 4, 0, 0, 7]),
                             [11, 12, 13, 14, 15, 16])
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff_z2)
        assert_raises(NiftiError, nipy2nifti, Image(data, cmap), fix0=True)
    # No problem for non-time
    for t_name in 'hz', 'ppm', 'rads':
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  aff)
        assert_equal(nipy2nifti(Image(data, cmap), fix0=True).shape,
                     shape_shifted)
Ejemplo n.º 10
0
def test_LinearList_common(tmpdir, data_path, sw, image_orientation,
                           get_testdata):
    tmpdir.chdir()

    angles = np.random.uniform(low=-3.14, high=3.14, size=(5, 3))
    translation = np.random.uniform(low=-5., high=5., size=(5, 3))
    mats = [from_matvec(euler2mat(*a), t)
            for a, t in zip(angles, translation)]

    ext = ''
    if sw == 'afni':
        factory = afni.AFNILinearTransformArray
    elif sw == 'fsl':
        factory = fsl.FSLLinearTransformArray
    elif sw == 'itk':
        ext = '.tfm'
        factory = itk.ITKLinearTransformArray

    tflist1 = factory(mats)

    fname = 'affine-%s.%s%s' % (image_orientation, sw, ext)

    with pytest.raises(FileNotFoundError):
        factory.from_filename(fname)

    tmpdir.join('singlemat.%s' % ext).write('')
    with pytest.raises(TransformFileError):
        factory.from_filename('singlemat.%s' % ext)

    tflist1.to_filename(fname)
    tflist2 = factory.from_filename(fname)

    assert tflist1['nxforms'] == tflist2['nxforms']
    assert all([np.allclose(x1['parameters'], x2['parameters'])
                for x1, x2 in zip(tflist1.xforms, tflist2.xforms)])
Ejemplo n.º 11
0
def test_ITKLinearTransform(tmpdir, data_path):
    tmpdir.chdir()

    matlabfile = data_path / 'ds-005_sub-01_from-T1_to-OASIS_affine.mat'
    mat = loadmat(str(matlabfile))
    with open(str(matlabfile), 'rb') as f:
        itkxfm = itk.ITKLinearTransform.from_fileobj(f)
    assert np.allclose(itkxfm['parameters'][:3, :3].flatten(),
                       mat['AffineTransform_float_3_3'][:-3].flatten())
    assert np.allclose(itkxfm['offset'], mat['fixed'].reshape((3, )))

    itkxfm = itk.ITKLinearTransform.from_filename(matlabfile)
    assert np.allclose(itkxfm['parameters'][:3, :3].flatten(),
                       mat['AffineTransform_float_3_3'][:-3].flatten())
    assert np.allclose(itkxfm['offset'], mat['fixed'].reshape((3, )))

    # Test to_filename(textfiles)
    itkxfm.to_filename('textfile.tfm')
    with open('textfile.tfm', 'r') as f:
        itkxfm2 = itk.ITKLinearTransform.from_fileobj(f)
    assert np.allclose(itkxfm['parameters'], itkxfm2['parameters'])

    # Test to_filename(matlab)
    itkxfm.to_filename('copy.mat')
    with open('copy.mat', 'rb') as f:
        itkxfm3 = itk.ITKLinearTransform.from_fileobj(f)
    assert np.all(itkxfm['parameters'] == itkxfm3['parameters'])

    rasmat = from_matvec(euler2mat(x=0.9, y=0.001, z=0.001), [4.0, 2.0, -1.0])
    itkxfm = itk.ITKLinearTransform.from_ras(rasmat)
    assert np.allclose(itkxfm['parameters'], ITK_MAT * rasmat)
    assert np.allclose(itkxfm.to_ras(), rasmat)
Ejemplo n.º 12
0
def test_against_spm_resample():
    # Test resampling against images resampled with SPM12
    # anatomical.nii has a diagonal -2, 2 2 affine;
    # functional.nii has a diagonal -4, 4 4 affine;
    # These are a bit boring, so first add some rotations and translations to
    # the anatomical image affine, and then resample to the first volume in the
    # functional, and compare to the same thing in SPM.
    # See ``make_moved_anat.py`` script in this directory for input to SPM.
    anat = nib.load(pjoin(DATA_DIR, 'anatomical.nii'))
    func = nib.load(pjoin(DATA_DIR, 'functional.nii'))
    some_rotations = euler2mat(0.1, 0.2, 0.3)
    extra_affine = from_matvec(some_rotations, [3, 4, 5])
    moved_anat = nib.Nifti1Image(anat.get_fdata(),
                                 extra_affine.dot(anat.affine),
                                 anat.header)
    one_func = nib.Nifti1Image(func.dataobj[..., 0],
                               func.affine,
                               func.header)
    moved2func = resample_from_to(moved_anat, one_func, order=1, cval=np.nan)
    spm_moved = nib.load(pjoin(DATA_DIR, 'resampled_anat_moved.nii'))
    assert_spm_resampling_close(moved_anat, moved2func, spm_moved)
    # Next we resample the rotated anatomical image to output space, and compare
    # to the same operation done with SPM (our own version of 'reorient.m' by
    # John Ashburner).
    moved2output = resample_to_output(moved_anat, 4, order=1, cval=np.nan)
    spm2output = nib.load(pjoin(DATA_DIR, 'reoriented_anat_moved.nii'))
    assert_spm_resampling_close(moved_anat, moved2output, spm2output);
Ejemplo n.º 13
0
def test_load_dim_info():
    # Test freq, phase, slice get set correctly on load
    data = np.random.normal(size=list(range(3)))
    xyz_aff = from_matvec(np.diag([2, 3, 4]), [11, 12, 13])
    in_cs = CS('ijk', name='voxels')
    out_cs = aligned_csm(3)
    # Just confirm that the default leads to no axis renaming
    ni_img = nib.Nifti1Image(data, xyz_aff)
    hdr = get_header(ni_img)
    assert_equal(hdr.get_dim_info(), (None, None, None))
    assert_equal(nifti2nipy(ni_img).coordmap, AT(in_cs, out_cs, xyz_aff))
    # But now...
    hdr.set_dim_info(freq=1)
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(CS(('i', 'freq', 'k'), "voxels"), out_cs, xyz_aff))
    hdr.set_dim_info(freq=2)
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(CS(('i', 'j', 'freq'), "voxels"), out_cs, xyz_aff))
    hdr.set_dim_info(phase=1)
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(CS(('i', 'phase', 'k'), "voxels"), out_cs, xyz_aff))
    hdr.set_dim_info(slice=0)
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(CS(('slice', 'j', 'k'), "voxels"), out_cs, xyz_aff))
    hdr.set_dim_info(freq=1, phase=0, slice=2)
    assert_equal(nifti2nipy(ni_img).coordmap,
                 AT(CS(('phase', 'freq', 'slice'), "voxels"), out_cs, xyz_aff))
Ejemplo n.º 14
0
def test_save3():
    # A test to ensure that when a file is saved, the affine
    # and the data agree. In this case, things don't agree:
    # i) the pixdim is off
    # ii) makes the affine off
    step = np.array([3.45,2.3,4.5,6.9])
    shape = (13,5,7,3)
    mni_xyz = mni_csm(3).coord_names
    cmap = AT(CS('jkli'),
              CS(('t',) + mni_xyz[::-1]),
              from_matvec(np.diag([0,3,5,1]), step))
    data = np.random.standard_normal(shape)
    img = api.Image(data, cmap)
    # with InTemporaryDirectory():
    with InTemporaryDirectory():
        save_image(img, TMP_FNAME)
        tmp = load_image(TMP_FNAME)
        # Detach image from file so we can delete it
        data = tmp.get_data().copy()
        img2 = api.Image(data, tmp.coordmap, tmp.metadata)
        del tmp
    assert_equal(tuple([img.shape[l] for l in [3,2,1,0]]), img2.shape)
    a = np.transpose(np.asarray(img), [3,2,1,0])
    assert_false(np.allclose(img.affine, img2.affine))
    assert_true(np.allclose(a, img2.get_data()))
Ejemplo n.º 15
0
def test_orthogonal_dims():
    # Test whether conversion to nifti raises an error for non-orthogonal
    # non-spatial dimensions
    # This affine is all nicely diagonal
    aff = from_matvec(np.diag([2., 3, 4, 5, 6]), [10, 11, 12, 13, 14])
    data = np.random.normal(size=(3, 4, 5, 6, 7))
    img = Image(data, vox2mni(aff))
    def as3d(aff):
        return from_matvec(aff[:3, :3], aff[:3, -1])
    assert_array_equal(get_affine(nipy2nifti(img)), as3d(aff))
    # Non-orthogonal spatial dimensions OK
    aff[:3, :3] = np.random.normal(size=(3, 3))
    img = Image(data, vox2mni(aff))
    assert_array_equal(get_affine(nipy2nifti(img)), as3d(aff))
    # Space must be orthogonal to time etc
    aff[0, 3] = 0.1
    assert_raises(NiftiError, nipy2nifti, img)
    aff[0, 3] = 0
    assert_array_equal(get_affine(nipy2nifti(img)), as3d(aff))
    aff[3, 0] = 0.1
    assert_raises(NiftiError, nipy2nifti, img)
    aff[3, 0] = 0
    assert_array_equal(get_affine(nipy2nifti(img)), as3d(aff))
    aff[4, 0] = 0.1
    assert_raises(NiftiError, nipy2nifti, img)
Ejemplo n.º 16
0
def test_against_spm_resample():
    # Test resampling against images resampled with SPM12
    # anatomical.nii has a diagonal -2, 2 2 affine;
    # functional.nii has a diagonal -4, 4 4 affine;
    # These are a bit boring, so first add some rotations and translations to
    # the anatomical image affine, and then resample to the first volume in the
    # functional, and compare to the same thing in SPM.
    # See ``make_moved_anat.py`` script in this directory for input to SPM.
    anat = nib.load(pjoin(DATA_DIR, 'anatomical.nii'))
    func = nib.load(pjoin(DATA_DIR, 'functional.nii'))
    some_rotations = euler2mat(0.1, 0.2, 0.3)
    extra_affine = from_matvec(some_rotations, [3, 4, 5])
    moved_anat = nib.Nifti1Image(anat.get_data().astype(float),
                                 extra_affine.dot(anat.affine),
                                 anat.header)
    one_func = nib.Nifti1Image(func.dataobj[..., 0],
                               func.affine,
                               func.header)
    moved2func = resample_from_to(moved_anat, one_func, order=1, cval=np.nan)
    spm_moved = nib.load(pjoin(DATA_DIR, 'resampled_anat_moved.nii'))
    assert_spm_resampling_close(moved_anat, moved2func, spm_moved)
    # Next we resample the rotated anatomical image to output space, and compare
    # to the same operation done with SPM (our own version of 'reorient.m' by
    # John Ashburner).
    moved2output = resample_to_output(moved_anat, 4, order=1, cval=np.nan)
    spm2output = nib.load(pjoin(DATA_DIR, 'reoriented_anat_moved.nii'))
    assert_spm_resampling_close(moved_anat, moved2output, spm2output);
Ejemplo n.º 17
0
def xslice(x, y_spec, z_spec, world):
    """
    Return an LPS slice through a 3d box with x fixed.

    Parameters
    ----------
    x : float
       The value at which x is fixed.
    y_spec : sequence
       A sequence with 2 values of form ((float, float), int). The
       (float, float) components are the min and max y values; the int
       is the number of points.
    z_spec : sequence
       As for `y_spec` but for z
    world : str or CoordinateSystem CoordSysMaker or XYZSpace
        World 3D space to which resulting coordmap refers

    Returns
    -------
    affine_transform : AffineTransform
       An affine transform that describes an plane in
       LPS coordinates with x fixed.

    Examples
    --------
    >>> y_spec = ([-114,114], 115) # voxels of size 2 in y, starting at -114, ending at 114
    >>> z_spec = ([-70,100], 86) # voxels of size 2 in z, starting at -70, ending at 100
    >>> x30 = xslice(30, y_spec, z_spec, 'scanner')
    >>> x30([0,0])
    array([  30., -114.,  -70.])
    >>> x30([114,85])
    array([  30.,  114.,  100.])
    >>> x30
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i_y', 'i_z'), name='slice', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('scanner-x=L->R', 'scanner-y=P->A', 'scanner-z=I->S'), name='scanner', coord_dtype=float64),
       affine=array([[   0.,    0.,   30.],
                     [   2.,    0., -114.],
                     [   0.,    2.,  -70.],
                     [   0.,    0.,    1.]])
    )
    >>> bounding_box(x30, (y_spec[1], z_spec[1]))
    ((30.0, 30.0), (-114.0, 114.0), (-70.0, 100.0))
    """
    affine_range = get_world_cs(world)
    (ymin, ymax), yno = y_spec
    y_tick = (ymax-ymin) / (yno - 1.0)
    (zmin, zmax), zno = z_spec
    z_tick = (zmax-zmin) / (zno - 1.0)
    origin = [x, ymin, zmin]
    colvectors = np.asarray([[0, 0],
                             [y_tick, 0],
                             [0, z_tick]])
    T = from_matvec(colvectors, origin)
    affine_domain = CoordinateSystem(['i_y', 'i_z'], 'slice')
    return AffineTransform(affine_domain,
                           affine_range,
                           T)
Ejemplo n.º 18
0
    def from_h5obj(cls, fileobj, check=True):
        """Read the struct from a file object."""
        xfm_list = []
        h5group = fileobj["TransformGroup"]
        typo_fallback = "Transform"
        try:
            h5group["1"][f"{typo_fallback}Parameters"]
        except KeyError:
            typo_fallback = "Tranform"

        for xfm in list(h5group.values())[1:]:
            if xfm["TransformType"][0].startswith(b"AffineTransform"):
                _params = np.asanyarray(xfm[f"{typo_fallback}Parameters"])
                xfm_list.append(
                    ITKLinearTransform(
                        parameters=from_matvec(_params[:-3].reshape(3, 3),
                                               _params[-3:]),
                        offset=np.asanyarray(
                            xfm[f"{typo_fallback}FixedParameters"]),
                    ))
                continue
            if xfm["TransformType"][0].startswith(
                    b"DisplacementFieldTransform"):
                _fixed = np.asanyarray(xfm[f"{typo_fallback}FixedParameters"])
                shape = _fixed[:3].astype("uint16").tolist()
                offset = _fixed[3:6].astype("uint16")
                zooms = _fixed[6:9].astype("float")
                directions = _fixed[9:].astype("float").reshape((3, 3))
                affine = from_matvec(directions * zooms, offset)
                field = np.asanyarray(
                    xfm[f"{typo_fallback}Parameters"]).reshape(
                        tuple(shape + [1, -1]))
                hdr = Nifti1Header()
                hdr.set_intent("vector")
                hdr.set_data_dtype("float")

                xfm_list.append(
                    ITKDisplacementsField.from_image(
                        Nifti1Image(field.astype("float"), affine, hdr)))
                continue

            raise NotImplementedError(
                f"Unsupported transform type {xfm['TransformType'][0]}")

        return xfm_list
Ejemplo n.º 19
0
def yslice(y, x_spec, z_spec, world):
    """ Return a slice through a 3d box with y fixed.

    Parameters
    ----------
    y : float
       The value at which y is fixed.
    x_spec : sequence
       A sequence with 2 values of form ((float, float), int). The
       (float, float) components are the min and max x values; the int
       is the number of points.
    z_spec : sequence
       As for `x_spec` but for z
    world : str or CoordinateSystem CoordSysMaker or XYZSpace
        World 3D space to which resulting coordmap refers

    Returns
    -------
    affine_transform : AffineTransform
       An affine transform that describes an plane in
       LPS coordinates with y fixed.

    Examples
    --------
    >>> x_spec = ([-92,92], 93) # voxels of size 2 in x, starting at -92, ending at 92
    >>> z_spec = ([-70,100], 86) # voxels of size 2 in z, starting at -70, ending at 100
    >>> y70 = yslice(70, x_spec, z_spec, 'mni')
    >>> y70
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i_x', 'i_z'), name='slice', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S'), name='mni', coord_dtype=float64),
       affine=array([[  2.,   0., -92.],
                     [  0.,   0.,  70.],
                     [  0.,   2., -70.],
                     [  0.,   0.,   1.]])
    )
    >>> y70([0,0])
    array([-92.,  70., -70.])
    >>> y70([92,85])
    array([  92.,   70.,  100.])
    >>> bounding_box(y70, (x_spec[1], z_spec[1]))
    ((-92.0, 92.0), (70.0, 70.0), (-70.0, 100.0))
    """
    affine_range = get_world_cs(world)
    (xmin, xmax), xno = x_spec
    x_tick = (xmax-xmin) / (xno - 1.0)
    (zmin, zmax), zno = z_spec
    z_tick = (zmax-zmin) / (zno - 1.0)
    origin = [xmin, y, zmin]
    colvectors = np.asarray([[x_tick, 0],
                             [0, 0],
                             [0, z_tick]])
    T = from_matvec(colvectors, origin)
    affine_domain = CoordinateSystem(['i_x', 'i_z'], 'slice')
    return AffineTransform(affine_domain,
                           affine_range,
                           T)
Ejemplo n.º 20
0
def zslice(z, x_spec, y_spec, world):
    """ Return a slice through a 3d box with z fixed.

    Parameters
    ----------
    z : float
       The value at which z is fixed.
    x_spec : sequence
       A sequence with 2 values of form ((float, float), int). The
       (float, float) components are the min and max x values; the int
       is the number of points.
    y_spec : sequence
       As for `x_spec` but for y
    world : str or CoordinateSystem CoordSysMaker or XYZSpace
        World 3D space to which resulting coordmap refers

    Returns
    -------
    affine_transform : AffineTransform
       An affine transform that describes a plane in LPS coordinates with z
       fixed.

    Examples
    --------
    >>> x_spec = ([-92,92], 93) # voxels of size 2 in x, starting at -92, ending at 92
    >>> y_spec = ([-114,114], 115) # voxels of size 2 in y, starting at -114, ending at 114
    >>> z40 = zslice(40, x_spec, y_spec, 'unknown')
    >>> z40
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i_x', 'i_y'), name='slice', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('unknown-x=L->R', 'unknown-y=P->A', 'unknown-z=I->S'), name='unknown', coord_dtype=float64),
       affine=array([[   2.,    0.,  -92.],
                     [   0.,    2., -114.],
                     [   0.,    0.,   40.],
                     [   0.,    0.,    1.]])
    )
    >>> z40([0,0])
    array([ -92., -114.,   40.])
    >>> z40([92,114])
    array([  92.,  114.,   40.])
    >>> bounding_box(z40, (x_spec[1], y_spec[1]))
    ((-92.0, 92.0), (-114.0, 114.0), (40.0, 40.0))
    """
    affine_range = get_world_cs(world)
    (xmin, xmax), xno = x_spec
    x_tick = (xmax-xmin) / (xno - 1.0)
    (ymin, ymax), yno = y_spec
    y_tick = (ymax-ymin) / (yno - 1.0)
    origin = [xmin, ymin, z]
    colvectors = np.asarray([[x_tick, 0],
                             [0, y_tick],
                             [0, 0]])
    T = from_matvec(colvectors, origin)
    affine_domain = CoordinateSystem(['i_x', 'i_y'], 'slice')
    return AffineTransform(affine_domain,
                           affine_range,
                           T)
Ejemplo n.º 21
0
def test_save_toffset():
    # Check toffset only gets set for time
    shape = (2, 3, 4, 5, 6, 7)
    data = np.random.normal(size=shape)
    aff = from_matvec(np.diag([2., 3, 4, 5, 6, 7]), [11, 12, 13, 14, 15, 16])
    xyz_names = talairach_csm(3).coord_names
    in_cs = CS('ijklmn')
    for t_name in 't', 'time':
        cmap = AT(in_cs, CS(xyz_names + (t_name, 'q', 'r')), aff)
        ni_img = nipy2nifti(Image(data, cmap))
        assert_equal(ni_img.get_header()['toffset'], 14)
    for time_like in ('hz', 'ppm', 'rads'):
        cmap = AT(in_cs, CS(xyz_names + (time_like, 'q', 'r')), aff)
        ni_img = nipy2nifti(Image(data, cmap))
        assert_equal(ni_img.get_header()['toffset'], 0)
    # Check that non-matching time causes a nifti error when toffset !=0
    shape_shifted = (2, 3, 4, 6, 5, 7)
    for t_name in 't', 'time':
        # No toffset, this is OK
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')),
                  np.diag([3, 4, 5, 6, 0, 7, 1]))
        assert_equal(nipy2nifti(Image(data, cmap)).shape, shape_shifted)
        # toffset, non-matching error
        aff_z1 = from_matvec(np.diag([2., 3, 4, 5, 0, 7]),
                             [11, 12, 13, 14, 15, 16])
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')), aff_z1)
        assert_raises(NiftiError, nipy2nifti, Image(data, cmap))
        # Unless fix0 set
        assert_equal(
            nipy2nifti(Image(data, cmap), fix0=True).shape, shape_shifted)
        # Even this doesn't work if there is more than one zero row and column
        aff_z2 = from_matvec(np.diag([2., 3, 4, 0, 0, 7]),
                             [11, 12, 13, 14, 15, 16])
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')), aff_z2)
        assert_raises(NiftiError, nipy2nifti, Image(data, cmap), fix0=True)
    # No problem for non-time
    for t_name in 'hz', 'ppm', 'rads':
        cmap = AT(CS(('i', 'j', 'k', 'u', t_name, 'v')),
                  CS(xyz_names + ('u', t_name, 'v')), aff)
        assert_equal(
            nipy2nifti(Image(data, cmap), fix0=True).shape, shape_shifted)
Ejemplo n.º 22
0
def test_obliquity():
    """Check the calculation of inclination of an affine axes."""
    from math import pi
    aligned = np.diag([2.0, 2.0, 2.3, 1.0])
    aligned[:-1, -1] = [-10, -10, -7]
    R = from_matvec(euler2mat(x=0.09, y=0.001, z=0.001), [0.0, 0.0, 0.0])
    oblique = R.dot(aligned)
    np.testing.assert_almost_equal(obliquity(aligned), [0.0, 0.0, 0.0])
    np.testing.assert_almost_equal(obliquity(oblique) * 180 / pi,
                                   [0.0810285, 5.1569949, 5.1569376])
Ejemplo n.º 23
0
def xslice(x, y_spec, z_spec, world):
    """
    Return an LPS slice through a 3d box with x fixed.

    Parameters
    ----------
    x : float
       The value at which x is fixed.
    y_spec : sequence
       A sequence with 2 values of form ((float, float), int). The
       (float, float) components are the min and max y values; the int
       is the number of points.
    z_spec : sequence
       As for `y_spec` but for z
    world : str or CoordinateSystem CoordSysMaker or XYZSpace
        World 3D space to which resulting coordmap refers

    Returns
    -------
    affine_transform : AffineTransform
       An affine transform that describes an plane in
       LPS coordinates with x fixed.

    Examples
    --------
    >>> y_spec = ([-114,114], 115) # voxels of size 2 in y, starting at -114, ending at 114
    >>> z_spec = ([-70,100], 86) # voxels of size 2 in z, starting at -70, ending at 100
    >>> x30 = xslice(30, y_spec, z_spec, 'scanner')
    >>> x30([0,0])
    array([  30., -114.,  -70.])
    >>> x30([114,85])
    array([  30.,  114.,  100.])
    >>> x30
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i_y', 'i_z'), name='slice', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('scanner-x=L->R', 'scanner-y=P->A', 'scanner-z=I->S'), name='scanner', coord_dtype=float64),
       affine=array([[   0.,    0.,   30.],
                     [   2.,    0., -114.],
                     [   0.,    2.,  -70.],
                     [   0.,    0.,    1.]])
    )
    >>> bounding_box(x30, (y_spec[1], z_spec[1]))
    ((30.0, 30.0), (-114.0, 114.0), (-70.0, 100.0))
    """
    affine_range = get_world_cs(world)
    (ymin, ymax), yno = y_spec
    y_tick = (ymax - ymin) / (yno - 1.0)
    (zmin, zmax), zno = z_spec
    z_tick = (zmax - zmin) / (zno - 1.0)
    origin = [x, ymin, zmin]
    colvectors = np.asarray([[0, 0], [y_tick, 0], [0, z_tick]])
    T = from_matvec(colvectors, origin)
    affine_domain = CoordinateSystem(['i_y', 'i_z'], 'slice')
    return AffineTransform(affine_domain, affine_range, T)
Ejemplo n.º 24
0
def zslice(z, x_spec, y_spec, world):
    """ Return a slice through a 3d box with z fixed.

    Parameters
    ----------
    z : float
       The value at which z is fixed.
    x_spec : sequence
       A sequence with 2 values of form ((float, float), int). The
       (float, float) components are the min and max x values; the int
       is the number of points.
    y_spec : sequence
       As for `x_spec` but for y
    world : str or CoordinateSystem CoordSysMaker or XYZSpace
        World 3D space to which resulting coordmap refers

    Returns
    -------
    affine_transform : AffineTransform
       An affine transform that describes a plane in LPS coordinates with z
       fixed.

    Examples
    --------
    >>> x_spec = ([-92,92], 93) # voxels of size 2 in x, starting at -92, ending at 92
    >>> y_spec = ([-114,114], 115) # voxels of size 2 in y, starting at -114, ending at 114
    >>> z40 = zslice(40, x_spec, y_spec, 'unknown')
    >>> z40
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i_x', 'i_y'), name='slice', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('unknown-x=L->R', 'unknown-y=P->A', 'unknown-z=I->S'), name='unknown', coord_dtype=float64),
       affine=array([[   2.,    0.,  -92.],
                     [   0.,    2., -114.],
                     [   0.,    0.,   40.],
                     [   0.,    0.,    1.]])
    )
    >>> z40([0,0])
    array([ -92., -114.,   40.])
    >>> z40([92,114])
    array([  92.,  114.,   40.])
    >>> bounding_box(z40, (x_spec[1], y_spec[1]))
    ((-92.0, 92.0), (-114.0, 114.0), (40.0, 40.0))
    """
    affine_range = get_world_cs(world)
    (xmin, xmax), xno = x_spec
    x_tick = (xmax - xmin) / (xno - 1.0)
    (ymin, ymax), yno = y_spec
    y_tick = (ymax - ymin) / (yno - 1.0)
    origin = [xmin, ymin, z]
    colvectors = np.asarray([[x_tick, 0], [0, y_tick], [0, 0]])
    T = from_matvec(colvectors, origin)
    affine_domain = CoordinateSystem(['i_x', 'i_y'], 'slice')
    return AffineTransform(affine_domain, affine_range, T)
Ejemplo n.º 25
0
def yslice(y, x_spec, z_spec, world):
    """ Return a slice through a 3d box with y fixed.

    Parameters
    ----------
    y : float
       The value at which y is fixed.
    x_spec : sequence
       A sequence with 2 values of form ((float, float), int). The
       (float, float) components are the min and max x values; the int
       is the number of points.
    z_spec : sequence
       As for `x_spec` but for z
    world : str or CoordinateSystem CoordSysMaker or XYZSpace
        World 3D space to which resulting coordmap refers

    Returns
    -------
    affine_transform : AffineTransform
       An affine transform that describes an plane in
       LPS coordinates with y fixed.

    Examples
    --------
    >>> x_spec = ([-92,92], 93) # voxels of size 2 in x, starting at -92, ending at 92
    >>> z_spec = ([-70,100], 86) # voxels of size 2 in z, starting at -70, ending at 100
    >>> y70 = yslice(70, x_spec, z_spec, 'mni')
    >>> y70
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i_x', 'i_z'), name='slice', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S'), name='mni', coord_dtype=float64),
       affine=array([[  2.,   0., -92.],
                     [  0.,   0.,  70.],
                     [  0.,   2., -70.],
                     [  0.,   0.,   1.]])
    )
    >>> y70([0,0])
    array([-92.,  70., -70.])
    >>> y70([92,85])
    array([  92.,   70.,  100.])
    >>> bounding_box(y70, (x_spec[1], z_spec[1]))
    ((-92.0, 92.0), (70.0, 70.0), (-70.0, 100.0))
    """
    affine_range = get_world_cs(world)
    (xmin, xmax), xno = x_spec
    x_tick = (xmax - xmin) / (xno - 1.0)
    (zmin, zmax), zno = z_spec
    z_tick = (zmax - zmin) / (zno - 1.0)
    origin = [xmin, y, zmin]
    colvectors = np.asarray([[x_tick, 0], [0, 0], [0, z_tick]])
    T = from_matvec(colvectors, origin)
    affine_domain = CoordinateSystem(['i_x', 'i_z'], 'slice')
    return AffineTransform(affine_domain, affine_range, T)
Ejemplo n.º 26
0
def load(filename, fmt='X5', reference=None):
    """Load a linear transform."""
    if fmt.lower() in ['itk', 'ants', 'elastix', 'nifty']:
        with open(filename) as itkfile:
            itkxfm = itkfile.read().splitlines()

        if 'Insight Transform File' not in itkxfm[0]:
            raise ValueError(
                "File %s does not look like an ITK transform text file." %
                filename)

        matlist = []
        for nxfm in range(len(itkxfm[1:]) // 4):
            parameters = np.fromstring(itkxfm[nxfm * 4 +
                                              3].split(':')[-1].strip(),
                                       dtype=float,
                                       sep=' ')
            offset = np.fromstring(itkxfm[nxfm * 4 + 4].split(':')[-1].strip(),
                                   dtype=float,
                                   sep=' ')
            if len(parameters) == 12:
                matrix = from_matvec(parameters[:9].reshape((3, 3)),
                                     parameters[9:])
                c_neg = from_matvec(np.eye(3), offset * -1.0)
                c_pos = from_matvec(np.eye(3), offset)
                matlist.append(LPS.dot(c_pos.dot(matrix.dot(c_neg.dot(LPS)))))
        matrix = np.stack(matlist)
    # elif fmt.lower() == 'afni':
    #     parameters = LPS.dot(self.matrix.dot(LPS))
    #     parameters = parameters[:3, :].reshape(-1).tolist()
    elif fmt.lower() in ('x5', 'bids'):
        raise NotImplementedError
    else:
        raise NotImplementedError

    if reference and isinstance(reference, str):
        reference = loadimg(reference)
    return Affine(matrix, reference)
Ejemplo n.º 27
0
def test_mulmat_operator(testdata_path):
    """Check the @ operator."""
    ref = testdata_path / "someones_anatomy.nii.gz"
    mat1 = np.diag([2.0, 2.0, 2.0, 1.0])
    mat2 = from_matvec(np.eye(3), (4, 2, -1))
    aff = nitl.Affine(mat1, reference=ref)

    composed = aff @ mat2
    assert composed.reference is None
    assert composed == nitl.Affine(mat1.dot(mat2))

    composed = nitl.Affine(mat2) @ aff
    assert composed.reference == aff.reference
    assert composed == nitl.Affine(mat2.dot(mat1), reference=ref)
Ejemplo n.º 28
0
def plot_rigid_body_transformation(trans_x=0,
                                   trans_y=0,
                                   trans_z=0,
                                   rot_x=0,
                                   rot_y=0,
                                   rot_z=0):
    '''This plot creates an interactive demo to illustrate the parameters of a rigid body transformation'''
    fov = 30
    radius = 10
    x, y, z = np.indices((fov, fov, fov))
    cube = ((x > fov // 2 - radius // 2) & (x < fov // 2 + radius // 2)) & (
        (y > fov // 2 - radius // 2) &
        (y < fov // 2 + radius // 2)) & ((z > fov // 2 - radius // 2) &
                                         (z < fov // 2 + radius // 2))
    cube = cube.astype(int)

    vec = np.array([trans_x, trans_y, trans_z])

    rot_x = np.radians(rot_x)
    rot_y = np.radians(rot_y)
    rot_z = np.radians(rot_z)
    rot_axis1 = np.array([[1, 0, 0], [0, np.cos(rot_x), -np.sin(rot_x)],
                          [0, np.sin(rot_x), np.cos(rot_x)]])

    rot_axis2 = np.array([[np.cos(rot_y), 0, np.sin(rot_y)], [0, 1, 0],
                          [-np.sin(rot_y), 0, np.cos(rot_y)]])

    rot_axis3 = np.array([[np.cos(rot_z), -np.sin(rot_z), 0],
                          [np.sin(rot_z), np.cos(rot_z), 0], [0, 0, 1]])

    rotation = rot_axis1 @ rot_axis2 @ rot_axis3

    affine = from_matvec(rotation, vec)

    i_coords, j_coords, k_coords = np.meshgrid(range(cube.shape[0]),
                                               range(cube.shape[1]),
                                               range(cube.shape[2]),
                                               indexing='ij')
    coordinate_grid = np.array([i_coords, j_coords, k_coords])
    coords_last = coordinate_grid.transpose(1, 2, 3, 0)
    transformed = apply_affine(affine, coords_last)
    coords_first = transformed.transpose(3, 0, 1, 2)

    fig = plt.figure(figsize=(15, 12))
    ax = plt.axes(projection='3d')
    ax.voxels(map_coordinates(cube, coords_first))
    ax.set_xlabel('x', fontsize=16)
    ax.set_ylabel('y', fontsize=16)
    ax.set_zlabel('z', fontsize=16)
Ejemplo n.º 29
0
def apply_rotate(matrix, rad_x=0, rad_y=0, rad_z=0):
    ''' axis = x or y or z '''
    rmat = dict(x=np.array([[1, 0, 0], [0, np.cos(rad_x), -np.sin(rad_x)],
                            [0, np.sin(rad_x),
                             np.cos(rad_x)]]).astype('float'),
                y=np.array([[np.cos(rad_y), 0, np.sin(rad_y)], [0, 1, 0],
                            [-np.sin(rad_y), 0,
                             np.cos(rad_y)]]).astype('float'),
                z=np.array([[np.cos(rad_z), -np.sin(rad_z), 0],
                            [np.sin(rad_z), np.cos(rad_z), 0],
                            [0, 0, 1]]).astype('float'))
    af_mat, af_vec = to_matvec(matrix)
    rotated_mat = rmat['z'].dot(rmat['y'].dot(rmat['x'].dot(af_mat)))
    rotated_vec = rmat['z'].dot(rmat['y'].dot(rmat['x'].dot(af_vec)))
    return from_matvec(rotated_mat, rotated_vec)
Ejemplo n.º 30
0
def test_is_xyz_affable():
    # Whether there exists an xyz affine for this coordmap
    affine = np.diag([2, 4, 5, 6, 1])
    cmap = AffineTransform(VARS['d_cs_r4'], VARS['r_cs_r4'], affine)
    assert_true(is_xyz_affable(cmap))
    assert_false(is_xyz_affable(cmap.reordered_range([3, 0, 1, 2])))
    assert_false(is_xyz_affable(cmap.reordered_domain([3, 0, 1, 2])))
    # Can pass in own validator
    my_valtor = dict(blind='x', leading='y', ditch='z')
    r_cs = CS(('blind', 'leading', 'ditch'), 'fall')
    affine = from_matvec(np.arange(9).reshape((3, 3)), [11, 12, 13])
    cmap = AffineTransform(VARS['d_cs_r3'], r_cs, affine)
    # No xyz affine if we don't use our custom dictionary
    assert_false(is_xyz_affable(cmap))
    # Is if we do
    assert_true(is_xyz_affable(cmap, my_valtor))
Ejemplo n.º 31
0
def test_is_xyz_affable():
    # Whether there exists an xyz affine for this coordmap
    affine = np.diag([2,4,5,6,1])
    cmap = AffineTransform(VARS['d_cs_r4'], VARS['r_cs_r4'], affine)
    assert_true(is_xyz_affable(cmap))
    assert_false(is_xyz_affable(cmap.reordered_range([3,0,1,2])))
    assert_false(is_xyz_affable(cmap.reordered_domain([3,0,1,2])))
    # Can pass in own validator
    my_valtor = dict(blind='x', leading='y', ditch='z')
    r_cs = CS(('blind', 'leading', 'ditch'), 'fall')
    affine = from_matvec(np.arange(9).reshape((3, 3)), [11, 12, 13])
    cmap = AffineTransform(VARS['d_cs_r3'], r_cs, affine)
    # No xyz affine if we don't use our custom dictionary
    assert_false(is_xyz_affable(cmap))
    # Is if we do
    assert_true(is_xyz_affable(cmap, my_valtor))
Ejemplo n.º 32
0
def test_save4():
    # Same as test_save3 except we have reordered the 'ijk' input axes.
    shape = (13,5,7,3)
    step = np.array([3.45,2.3,4.5,6.9])
    # When the input coords are in the 'ljki' order, the affines get
    # rearranged.  Note that the 'start' below, must be 0 for
    # non-spatial dimensions, because we have no way to store them in
    # most cases.  For example, a 'start' of [1,5,3,1] would be lost on
    # reload
    mni_xyz = mni_csm(3).coord_names
    cmap = AT(CS('tkji'),
              CS((('t',) + mni_xyz[::-1])),
              from_matvec(np.diag([2., 3, 5, 1]), step))
    data = np.random.standard_normal(shape)
    img = api.Image(data, cmap)
    with InTemporaryDirectory():
        save_image(img, TMP_FNAME)
        tmp = load_image(TMP_FNAME)
        data = tmp.get_data().copy()
        # Detach image from file so we can delete it
        img2 = api.Image(data, tmp.coordmap, tmp.metadata)
        del tmp
    P = np.array([[0,0,0,1,0],
                  [0,0,1,0,0],
                  [0,1,0,0,0],
                  [1,0,0,0,0],
                  [0,0,0,0,1]])
    res = np.dot(P, np.dot(img.affine, P.T))
    # the step part of the affine should be set correctly
    assert_array_almost_equal(res[:4,:4], img2.affine[:4,:4])
    # start in the spatial dimensions should be set correctly
    assert_array_almost_equal(res[:3,-1], img2.affine[:3,-1])
    # start in the time dimension should be 3.45 as in img, because NIFTI stores
    # the time offset in hdr[``toffset``]
    assert_not_equal(res[3,-1], img2.affine[3,-1])
    assert_equal(res[3,-1], 3.45)
    # shapes should be reversed because img has coordinates reversed
    assert_equal(img.shape[::-1], img2.shape)
    # data should be transposed because coordinates are reversed
    assert_array_almost_equal(
           np.transpose(np.asarray(img2),[3,2,1,0]),
           np.asarray(img))
    # coordinate names should be reversed as well
    assert_equal(img2.coordmap.function_domain.coord_names,
                 img.coordmap.function_domain.coord_names[::-1])
    assert_equal(img2.coordmap.function_domain.coord_names,
                 ('i', 'j', 'k', 't'))
Ejemplo n.º 33
0
def plot_affine_cost(trans_x=0, trans_y=0):
    '''This function creates an interactive demo to highlight how a cost function works in image registration.'''
    fov = 30
    radius = 15
    x, y = np.indices((fov, fov))
    square1 = (x < radius - 2) & (y < radius - 2)
    square2 = ((x > fov // 2 - radius // 2) &
               (x < fov // 2 + radius // 2)) & ((y > fov // 2 - radius // 2) &
                                                (y < fov // 2 + radius // 2))
    square1 = square1.astype(float)
    square2 = square2.astype(float)

    vec = np.array([trans_y, trans_x])

    affine = from_matvec(np.eye(2), vec)

    i_coords, j_coords = np.meshgrid(range(square1.shape[0]),
                                     range(square1.shape[1]),
                                     indexing='ij')
    coordinate_grid = np.array([i_coords, j_coords])
    coords_last = coordinate_grid.transpose(1, 2, 0)
    transformed = apply_affine(affine, coords_last)
    coords_first = transformed.transpose(2, 0, 1)

    transformed_square = map_coordinates(square1, coords_first)
    f, a = plt.subplots(ncols=3, figsize=(15, 5))
    a[0].imshow(transformed_square)
    a[0].set_xlabel('x', fontsize=16)
    a[0].set_ylabel('y', fontsize=16)
    a[0].set_title('Target Image', fontsize=18)

    a[1].imshow(square2)
    a[1].set_xlabel('x', fontsize=16)
    a[1].set_ylabel('y', fontsize=16)
    a[1].set_title('Reference Image', fontsize=18)

    point_x = deepcopy(trans_x)
    point_y = deepcopy(trans_y)
    sse = np.sum((transformed_square - square2)**2)
    a[2].bar(0, sse)
    a[2].set_ylim([0, 350])
    a[2].set_ylabel('SSE', fontsize=18)
    a[2].set_xlabel('Cost Function', fontsize=18)
    a[2].set_xticks([])
    a[2].set_title(f'Parameters: ({int(trans_x)},{int(trans_y)})', fontsize=20)
    plt.tight_layout()
Ejemplo n.º 34
0
def test_other_axes():
    # With a diagonal affine, we can do PCA on any axis
    ncomp = 5
    img = data_dict['fmridata']
    in_coords = list(img.axes.coord_names)
    img_data = img.get_data()
    for axis_no, axis_name in enumerate('ijkt'):
        p = pca_image(img, axis_name, ncomp=ncomp)
        n = img.shape[axis_no]
        bv_key = 'basis_vectors over ' + axis_name
        assert_equal(_rank(p), n - 1)
        assert_equal(p[bv_key].shape, (n, n - 1))
        # We get the expected data back
        dp = pca_array(img_data, axis_no, ncomp=ncomp)
        # We have to make sure the signs are the same; on Windows it seems the
        # signs can flip even between two runs on the same data
        pos_p = img_res2pos1(p, bv_key)
        pos_dp = res2pos1(dp)
        img_bps = pos_p['basis_projections']
        assert_almost_equal(pos_dp['basis_vectors'], pos_p[bv_key])
        assert_almost_equal(pos_dp['basis_projections'], img_bps.get_data())
        # And we've replaced the expected axis
        exp_coords = in_coords[:]
        exp_coords[exp_coords.index(axis_name)] = 'PCA components'
        assert_equal(img_bps.axes.coord_names, exp_coords)
    # If the affine is not diagonal, we'll get an error
    aff = from_matvec(np.arange(16).reshape(4,4))
    nd_cmap = AffineTransform(img.axes, img.reference, aff)
    nd_img = Image(img_data, nd_cmap)
    for axis_name in 'ijkt':
        assert_raises(AxisError, pca_image, nd_img, axis_name)
    # Only for the non-diagonal parts
    aff = np.array([[1, 2, 0, 0, 10],
                    [2, 1, 0, 0, 11],
                    [0, 0, 3, 0, 12],
                    [0, 0, 0, 4, 13],
                    [0, 0, 0, 0, 1]])
    nd_cmap = AffineTransform(img.axes, img.reference, aff)
    nd_img = Image(img_data, nd_cmap)
    for axis_name in 'ij':
        assert_raises(AxisError, pca_image, nd_img, axis_name)
    for axis_name in 'kt':
        p = pca_image(img, axis_name, ncomp=ncomp)
        exp_coords = in_coords[:]
        exp_coords[exp_coords.index(axis_name)] = 'PCA components'
        assert_equal(p['basis_projections'].axes.coord_names, exp_coords)
Ejemplo n.º 35
0
def test_other_axes():
    # With a diagonal affine, we can do PCA on any axis
    ncomp = 5
    img = data_dict['fmridata']
    in_coords = list(img.axes.coord_names)
    img_data = img.get_data()
    for axis_no, axis_name in enumerate('ijkt'):
        p = pca_image(img, axis_name, ncomp=ncomp)
        n = img.shape[axis_no]
        bv_key = 'basis_vectors over ' + axis_name
        assert_equal(_rank(p), n - 1)
        assert_equal(p[bv_key].shape, (n, n - 1))
        # We get the expected data back
        dp = pca_array(img_data, axis_no, ncomp=ncomp)
        # We have to make sure the signs are the same; on Windows it seems the
        # signs can flip even between two runs on the same data
        pos_p = img_res2pos1(p, bv_key)
        pos_dp = res2pos1(dp)
        img_bps = pos_p['basis_projections']
        assert_almost_equal(pos_dp['basis_vectors'], pos_p[bv_key])
        assert_almost_equal(pos_dp['basis_projections'], img_bps.get_data())
        # And we've replaced the expected axis
        exp_coords = in_coords[:]
        exp_coords[exp_coords.index(axis_name)] = 'PCA components'
        assert_equal(img_bps.axes.coord_names, exp_coords)
    # If the affine is not diagonal, we'll get an error
    aff = from_matvec(np.arange(16).reshape(4,4))
    nd_cmap = AffineTransform(img.axes, img.reference, aff)
    nd_img = Image(img_data, nd_cmap)
    for axis_name in 'ijkt':
        assert_raises(AxisError, pca_image, nd_img, axis_name)
    # Only for the non-diagonal parts
    aff = np.array([[1, 2, 0, 0, 10],
                    [2, 1, 0, 0, 11],
                    [0, 0, 3, 0, 12],
                    [0, 0, 0, 4, 13],
                    [0, 0, 0, 0, 1]])
    nd_cmap = AffineTransform(img.axes, img.reference, aff)
    nd_img = Image(img_data, nd_cmap)
    for axis_name in 'ij':
        assert_raises(AxisError, pca_image, nd_img, axis_name)
    for axis_name in 'kt':
        p = pca_image(img, axis_name, ncomp=ncomp)
        exp_coords = in_coords[:]
        exp_coords[exp_coords.index(axis_name)] = 'PCA components'
        assert_equal(p['basis_projections'].axes.coord_names, exp_coords)
Ejemplo n.º 36
0
def apply_flip(matrix, axis, mat=True, vec=True):
    '''axis = x or y or z'''
    flip_idx = dict(x=0, y=1, z=2)
    orig_mat, orig_vec = to_matvec(matrix)

    aff_mat = np.ones(3)
    aff_mat[flip_idx[axis]] = -1
    aff_mat = np.diag(aff_mat)
    if mat:
        flip_mat = aff_mat.dot(orig_mat)
    else:
        flip_mat = orig_mat
    if vec:
        flip_vec = aff_mat.dot(orig_vec)
    else:
        flip_vec = orig_vec
    return from_matvec(flip_mat, flip_vec)
Ejemplo n.º 37
0
def test_apply_linear_transform(tmpdir, get_testdata, image_orientation,
                                sw_tool):
    """Check implementation of exporting affines to formats."""
    tmpdir.chdir()

    img = get_testdata[image_orientation]
    # Generate test transform
    T = from_matvec(euler2mat(x=0.9, y=0.001, z=0.001), [4.0, 2.0, -1.0])
    xfm = nitl.Affine(T)
    xfm.reference = img

    ext = ""
    if sw_tool == "itk":
        ext = ".tfm"
    elif sw_tool == "fs":
        ext = ".lta"

    img.to_filename("img.nii.gz")
    xfm_fname = "M.%s%s" % (sw_tool, ext)
    xfm.to_filename(xfm_fname, fmt=sw_tool)

    cmd = APPLY_LINEAR_CMD[sw_tool](
        transform=os.path.abspath(xfm_fname),
        reference=os.path.abspath("img.nii.gz"),
        moving=os.path.abspath("img.nii.gz"),
    )

    # skip test if command is not available on host
    exe = cmd.split(" ", 1)[0]
    if not shutil.which(exe):
        pytest.skip("Command {} not found on host".format(exe))

    exit_code = check_call([cmd], shell=True)
    assert exit_code == 0
    sw_moved = nb.load("resampled.nii.gz")

    nt_moved = xfm.apply(img, order=0)
    diff = sw_moved.get_fdata() - nt_moved.get_fdata()
    # A certain tolerance is necessary because of resampling at borders
    assert (np.abs(diff) > 1e-3).sum() / diff.size < TESTS_BORDER_TOLERANCE

    nt_moved = xfm.apply("img.nii.gz", order=0)
    diff = sw_moved.get_fdata() - nt_moved.get_fdata()
    # A certain tolerance is necessary because of resampling at borders
    assert (np.abs(diff) > 1e-3).sum() / diff.size < TESTS_BORDER_TOLERANCE
Ejemplo n.º 38
0
def test_Linear_common(tmpdir, data_path, sw, image_orientation,
                       get_testdata):
    tmpdir.chdir()

    moving = get_testdata[image_orientation]
    reference = get_testdata[image_orientation]

    ext = ''
    if sw == 'afni':
        factory = afni.AFNILinearTransform
    elif sw == 'fsl':
        factory = fsl.FSLLinearTransform
    elif sw == 'itk':
        reference = None
        moving = None
        ext = '.tfm'
        factory = itk.ITKLinearTransform
    elif sw == 'fs':
        ext = '.lta'
        factory = fs.LinearTransformArray

    with pytest.raises(TransformFileError):
        factory.from_string('')

    fname = 'affine-%s.%s%s' % (image_orientation, sw, ext)

    # Test the transform loaders are implemented
    xfm = factory.from_filename(data_path / fname)

    with open(str(data_path / fname)) as f:
        text = f.read()
        f.seek(0)
        xfm = factory.from_fileobj(f)

    # Test to_string
    assert text == xfm.to_string()

    xfm.to_filename(fname)
    assert filecmp.cmp(fname, str((data_path / fname).resolve()))

    # Test from_ras
    RAS = from_matvec(euler2mat(x=0.9, y=0.001, z=0.001), [4.0, 2.0, -1.0])
    xfm = factory.from_ras(RAS, reference=reference, moving=moving)
    assert np.allclose(xfm.to_ras(reference=reference, moving=moving), RAS)
Ejemplo n.º 39
0
def par2referenceSPM(par_file, session):
    rigids_euler = np.array(pd.read_csv(par_file, header=None,
                                        delimiter='\s+'))
    number_of_cycles = rigids_euler.shape[0]
    rigids = np.empty((number_of_cycles, 4, 4))
    for i in range(number_of_cycles):
        mat = euler2mat(
            -rigids_euler[i, 3],  # gieren / yaw   / z
            -rigids_euler[i, 4],  # rollen / roll  / y
            -rigids_euler[i, 5])  # nicken / pitch / x
        vec = rigids_euler[i, :3]
        rigids[i] = from_matvec(matrix=mat,
                                vector=np.array((-vec[1], -vec[0], -vec[2])))
    reference_maps = ReferenceMaps(session.name)
    reference_maps.temporal_resolution = session.temporal_resolution
    reference_maps.slice_timing = session.slice_timing
    reference_maps.set_acquisition_maps(Affines(rigids))
    reference_maps.shape = (session.numob, session.shape[session.ep])
    return reference_maps
Ejemplo n.º 40
0
def test_save4():
    # Same as test_save3 except we have reordered the 'ijk' input axes.
    shape = (13, 5, 7, 3)
    step = np.array([3.45, 2.3, 4.5, 6.9])
    # When the input coords are in the 'ljki' order, the affines get
    # rearranged.  Note that the 'start' below, must be 0 for
    # non-spatial dimensions, because we have no way to store them in
    # most cases.  For example, a 'start' of [1,5,3,1] would be lost on
    # reload
    mni_xyz = mni_csm(3).coord_names
    cmap = AT(CS('tkji'), CS((('t', ) + mni_xyz[::-1])),
              from_matvec(np.diag([2., 3, 5, 1]), step))
    data = np.random.standard_normal(shape)
    img = api.Image(data, cmap)
    with InTemporaryDirectory():
        save_image(img, TMP_FNAME)
        tmp = load_image(TMP_FNAME)
        data = tmp.get_data().copy()
        # Detach image from file so we can delete it
        img2 = api.Image(data, tmp.coordmap, tmp.metadata)
        del tmp
    P = np.array([[0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 0, 0, 0],
                  [1, 0, 0, 0, 0], [0, 0, 0, 0, 1]])
    res = np.dot(P, np.dot(img.affine, P.T))
    # the step part of the affine should be set correctly
    assert_array_almost_equal(res[:4, :4], img2.affine[:4, :4])
    # start in the spatial dimensions should be set correctly
    assert_array_almost_equal(res[:3, -1], img2.affine[:3, -1])
    # start in the time dimension should be 3.45 as in img, because NIFTI stores
    # the time offset in hdr[``toffset``]
    assert_not_equal(res[3, -1], img2.affine[3, -1])
    assert_equal(res[3, -1], 3.45)
    # shapes should be reversed because img has coordinates reversed
    assert_equal(img.shape[::-1], img2.shape)
    # data should be transposed because coordinates are reversed
    assert_array_almost_equal(np.transpose(img2.get_data(), [3, 2, 1, 0]),
                              img.get_data())
    # coordinate names should be reversed as well
    assert_equal(img2.coordmap.function_domain.coord_names,
                 img.coordmap.function_domain.coord_names[::-1])
    assert_equal(img2.coordmap.function_domain.coord_names,
                 ('i', 'j', 'k', 't'))
Ejemplo n.º 41
0
def test_xyz_affine():
    # Getting an xyz affine from coordmaps
    aff3d = from_matvec(np.arange(9).reshape((3,3)), [15,16,17])
    cmap3d = AffineTransform(VARS['d_cs_r3'], VARS['r_cs_r3'], aff3d)
    rzs = np.c_[np.arange(12).reshape((4,3)), [0,0,0,12]]
    aff4d = from_matvec(rzs, [15,16,17,18])
    cmap4d = AffineTransform(VARS['d_cs_r4'], VARS['r_cs_r4'], aff4d)
    # Simplest case of 3D affine -> affine unchanged
    assert_array_equal(xyz_affine(cmap3d), aff3d)
    # 4D (5, 5) affine -> 3D equivalent
    assert_array_equal(xyz_affine(cmap4d), aff3d)
    # Any dimensions not spatial, AxesError
    r_cs = CS(('mni-x', 'mni-y', 'mni-q'), 'mni')
    funny_cmap = AffineTransform(VARS['d_cs_r3'],r_cs, aff3d)
    assert_raises(AxesError, xyz_affine, funny_cmap)
    r_cs = CS(('mni-x', 'mni-q', 'mni-z'), 'mni')
    funny_cmap = AffineTransform(VARS['d_cs_r3'],r_cs, aff3d)
    assert_raises(AxesError, xyz_affine, funny_cmap)
    # We insist that the coordmap is in output xyz order
    permutations = (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)
    for perm in permutations:
        assert_raises(AxesError, xyz_affine, cmap3d.reordered_range(perm))
    # The input order doesn't matter, as long as the xyz axes map to the first
    # three input axes
    for perm in permutations:
        assert_array_equal(xyz_affine(
            cmap3d.reordered_domain(perm)), aff3d[:, perm + (-1,)])
    # But if the corresponding input axes not in the first three, an axis error
    wrong_inputs = cmap4d.reordered_domain([0, 1, 3, 2])
    assert_raises(AxesError, xyz_affine, wrong_inputs)
    # xyzs must be orthogonal to dropped axis
    for i in range(3):
        aff = aff4d.copy()
        aff[i,3] = 1
        cmap = AffineTransform(VARS['d_cs_r4'], VARS['r_cs_r4'], aff)
        assert_raises(AffineError, xyz_affine, cmap)
        # And if reordered
        assert_raises(AxesError, xyz_affine, cmap.reordered_range([2,0,1,3]))
    # Non-square goes to square
    aff54 = np.array([[0, 1, 2, 15],
                      [3, 4, 5, 16],
                      [6, 7, 8, 17],
                      [0, 0, 0, 18],
                      [0, 0, 0, 1]])
    cmap = AffineTransform(VARS['d_cs_r3'], VARS['r_cs_r4'], aff54)
    assert_array_equal(xyz_affine(cmap), aff3d)
    aff57 = np.array([[0, 1, 2, 0, 0, 0, 15],
                      [3, 4, 5, 0, 0, 0, 16],
                      [6, 7, 8, 0, 0, 0, 17],
                      [0, 0, 0, 0, 0, 0, 18],
                      [0, 0, 0, 0, 0, 0, 1]])
    d_cs_r6 = CS('ijklmn', 'voxels')
    cmap = AffineTransform(d_cs_r6, VARS['r_cs_r4'], aff57)
    assert_array_equal(xyz_affine(cmap), aff3d)
    # Non-affine raises SpaceTypeError
    cmap_cmap = CoordinateMap(VARS['d_cs_r4'], VARS['r_cs_r4'], lambda x:x*3)
    assert_raises(SpaceTypeError, xyz_affine, cmap_cmap)
    # Not enough dimensions - SpaceTypeError
    d_cs_r2 = CS('ij', 'voxels')
    r_cs_r2 = CS(VARS['r_names'][:2], 'mni')
    cmap = AffineTransform(d_cs_r2, r_cs_r2,
                           np.array([[2,0,10],[0,3,11],[0,0,1]]))
    assert_raises(AxesError, xyz_affine, cmap)
    # Can pass in own validator
    my_valtor = dict(blind='x', leading='y', ditch='z')
    r_cs = CS(('blind', 'leading', 'ditch'), 'fall')
    cmap = AffineTransform(VARS['d_cs_r3'],r_cs, aff3d)
    assert_raises(AxesError, xyz_affine, cmap)
    assert_array_equal(xyz_affine(cmap, my_valtor), aff3d)
    # Slices in x, y, z coordmaps raise error because of missing spatial
    # dimensions
    arr = np.arange(120).reshape((2, 3, 4, 5))
    aff = np.diag([2, 3, 4, 5, 1])
    img = Image(arr, vox2mni(aff))
    assert_raises(AxesError, xyz_affine, img[1].coordmap)
    assert_raises(AxesError, xyz_affine, img[:,1].coordmap)
    assert_raises(AxesError, xyz_affine, img[:,:,1].coordmap)
Ejemplo n.º 42
0
""" Make anatomical image with altered affine

* Add some rotations and translations to affine;
* Save as ``.nii`` file so SPM can read it.

See ``resample_using_spm.m`` for processing of this generated image by SPM.
"""

import numpy as np

import nibabel as nib
from nibabel.eulerangles import euler2mat
from nibabel.affines import from_matvec

img = nib.load('anatomical.nii')
some_rotations = euler2mat(0.1, 0.2, 0.3)
extra_affine = from_matvec(some_rotations, [3, 4, 5])
moved_anat = nib.Nifti1Image(img.dataobj,
                             extra_affine.dot(img.affine),
                             img.header)
moved_anat.set_data_dtype(np.float32)
nib.save(moved_anat, 'anat_moved.nii')
Ejemplo n.º 43
0
def proc_file(infile, opts):
    # figure out the output filename, and see if it exists
    basefilename = splitext_addext(os.path.basename(infile))[0]
    if opts.outdir is not None:
        # set output path
        basefilename = os.path.join(opts.outdir, basefilename)

    # prep a file
    if opts.compressed:
        verbose("Using gzip compression")
        outfilename = basefilename + ".nii.gz"
    else:
        outfilename = basefilename + ".nii"
    if os.path.isfile(outfilename) and not opts.overwrite:
        raise IOError('Output file "%s" exists, use --overwrite to ' "overwrite it" % outfilename)

    # load the PAR header and data
    scaling = "dv" if opts.scaling == "off" else opts.scaling
    infile = fname_ext_ul_case(infile)
    pr_img = pr.load(infile, permit_truncated=opts.permit_truncated, scaling=scaling, strict_sort=opts.strict_sort)
    pr_hdr = pr_img.header
    affine = pr_hdr.get_affine(origin=opts.origin)
    slope, intercept = pr_hdr.get_data_scaling(scaling)
    if opts.scaling != "off":
        verbose('Using data scaling "%s"' % opts.scaling)
    # get original scaling, and decide if we scale in-place or not
    if opts.scaling == "off":
        slope = np.array([1.0])
        intercept = np.array([0.0])
        in_data = pr_img.dataobj.get_unscaled()
        out_dtype = pr_hdr.get_data_dtype()
    elif not np.any(np.diff(slope)) and not np.any(np.diff(intercept)):
        # Single scalefactor case
        slope = slope.ravel()[0]
        intercept = intercept.ravel()[0]
        in_data = pr_img.dataobj.get_unscaled()
        out_dtype = pr_hdr.get_data_dtype()
    else:
        # Multi scalefactor case
        slope = np.array([1.0])
        intercept = np.array([0.0])
        in_data = np.array(pr_img.dataobj)
        out_dtype = np.float64
    # Reorient data block to LAS+ if necessary
    ornt = io_orientation(np.diag([-1, 1, 1, 1]).dot(affine))
    if np.all(ornt == [[0, 1], [1, 1], [2, 1]]):  # already in LAS+
        t_aff = np.eye(4)
    else:  # Not in LAS+
        t_aff = inv_ornt_aff(ornt, pr_img.shape)
        affine = np.dot(affine, t_aff)
        in_data = apply_orientation(in_data, ornt)

    bvals, bvecs = pr_hdr.get_bvals_bvecs()
    if not opts.keep_trace:  # discard Philips DTI trace if present
        if bvecs is not None:
            bad_mask = np.logical_and(bvals != 0, (bvecs == 0).all(axis=1))
            if bad_mask.sum() > 0:
                pl = "s" if bad_mask.sum() != 1 else ""
                verbose("Removing %s DTI trace volume%s" % (bad_mask.sum(), pl))
                good_mask = ~bad_mask
                in_data = in_data[..., good_mask]
                bvals = bvals[good_mask]
                bvecs = bvecs[good_mask]

    # Make corresponding NIfTI image
    nimg = nifti1.Nifti1Image(in_data, affine, pr_hdr)
    nhdr = nimg.header
    nhdr.set_data_dtype(out_dtype)
    nhdr.set_slope_inter(slope, intercept)
    nhdr.set_sform(affine, code=1)
    nhdr.set_qform(affine, code=1)

    if "parse" in opts.minmax:
        # need to get the scaled data
        verbose("Loading (and scaling) the data to determine value range")
    if opts.minmax[0] == "parse":
        nhdr["cal_min"] = in_data.min() * slope + intercept
    else:
        nhdr["cal_min"] = float(opts.minmax[0])
    if opts.minmax[1] == "parse":
        nhdr["cal_max"] = in_data.max() * slope + intercept
    else:
        nhdr["cal_max"] = float(opts.minmax[1])

    # container for potential NIfTI1 header extensions
    if opts.store_header:
        # dump the full PAR header content into an extension
        with open(infile, "rb") as fobj:  # contents must be bytes
            hdr_dump = fobj.read()
            dump_ext = nifti1.Nifti1Extension("comment", hdr_dump)
        nhdr.extensions.append(dump_ext)

    verbose("Writing %s" % outfilename)
    nibabel.save(nimg, outfilename)

    # write out bvals/bvecs if requested
    if opts.bvs:
        if bvals is None and bvecs is None:
            verbose("No DTI volumes detected, bvals and bvecs not written")
        elif bvecs is None:
            verbose("DTI volumes detected, but no diffusion direction info was" "found.  Writing .bvals file only.")
            with open(basefilename + ".bvals", "w") as fid:
                # np.savetxt could do this, but it's just a loop anyway
                for val in bvals:
                    fid.write("%s " % val)
                fid.write("\n")
        else:
            verbose("Writing .bvals and .bvecs files")
            # Transform bvecs with reorientation affine
            orig2new = npl.inv(t_aff)
            bv_reorient = from_matvec(to_matvec(orig2new)[0], [0, 0, 0])
            bvecs = apply_affine(bv_reorient, bvecs)
            with open(basefilename + ".bvals", "w") as fid:
                # np.savetxt could do this, but it's just a loop anyway
                for val in bvals:
                    fid.write("%s " % val)
                fid.write("\n")
            with open(basefilename + ".bvecs", "w") as fid:
                for row in bvecs.T:
                    for val in row:
                        fid.write("%s " % val)
                    fid.write("\n")

    # export data labels varying along the 4th dimensions if requested
    if opts.vol_info:
        labels = pr_img.header.get_volume_labels()
        if len(labels) > 0:
            vol_keys = list(labels.keys())
            with open(basefilename + ".ordering.csv", "w") as csvfile:
                csvwriter = csv.writer(csvfile, delimiter=",")
                csvwriter.writerow(vol_keys)
                for vals in zip(*[labels[k] for k in vol_keys]):
                    csvwriter.writerow(vals)

    # write out dwell time if requested
    if opts.dwell_time:
        try:
            dwell_time = calculate_dwell_time(
                pr_hdr.get_water_fat_shift(), pr_hdr.get_echo_train_length(), opts.field_strength
            )
        except MRIError:
            verbose("No EPI factors, dwell time not written")
        else:
            verbose("Writing dwell time (%r sec) calculated assuming %sT " "magnet" % (dwell_time, opts.field_strength))
            with open(basefilename + ".dwell_time", "w") as fid:
                fid.write("%r\n" % dwell_time)
Ejemplo n.º 44
0
def test_resample_to_output():
    # Test routine to sample iamges to output space
    # Image aligned to output axes - no-op
    data = np.arange(24).reshape((2, 3, 4))
    img = Nifti1Image(data, np.eye(4))
    # Check default resampling
    img2 = resample_to_output(img)
    assert_array_equal(img2.shape, (2, 3, 4))
    assert_array_equal(img2.affine, np.eye(4))
    assert_array_equal(img2.dataobj, data)
    # Check resampling with different voxel size specifications
    for vox_sizes in (None, 1, [1, 1, 1]):
        img2 = resample_to_output(img, vox_sizes)
        assert_array_equal(img2.shape, (2, 3, 4))
        assert_array_equal(img2.affine, np.eye(4))
        assert_array_equal(img2.dataobj, data)
    img2 = resample_to_output(img, vox_sizes)
    # Check 2D works
    img_2d = Nifti1Image(data[0], np.eye(4))
    for vox_sizes in (None, 1, (1, 1), (1, 1, 1)):
        img3 = resample_to_output(img_2d, vox_sizes)
        assert_array_equal(img3.shape, (3, 4, 1))
        assert_array_equal(img3.affine, np.eye(4))
        assert_array_equal(img3.dataobj, data[0][..., None])
    # Even 1D
    img_1d = Nifti1Image(data[0, 0], np.eye(4))
    img3 = resample_to_output(img_1d)
    assert_array_equal(img3.shape, (4, 1, 1))
    assert_array_equal(img3.affine, np.eye(4))
    assert_array_equal(img3.dataobj, data[0, 0][..., None, None])
    # But 4D does not
    img_4d = Nifti1Image(data.reshape(2, 3, 2, 2), np.eye(4))
    assert_raises(ValueError, resample_to_output, img_4d)
    # Run vox2vox_out tests, checking output shape, coordinate transform
    for in_shape, in_aff, vox, out_shape, out_aff in get_outspace_params():
        # Allow for expansion of image shape from < 3D
        in_n_dim = len(in_shape)
        if in_n_dim < 3:
            in_shape = in_shape + (1,) * (3 - in_n_dim)
            if not vox is None:
                vox = vox + (1,) * (3 - in_n_dim)
            assert len(out_shape) == in_n_dim
            out_shape = out_shape + (1,) * (3 - in_n_dim)
        img = Nifti1Image(np.ones(in_shape), in_aff)
        out_img = resample_to_output(img, vox)
        assert_all_in(in_shape, in_aff, out_img.shape, out_img.affine)
        assert_equal(out_img.shape, out_shape)
        assert_almost_equal(out_img.affine, out_aff)
    # Check data is as expected with some transforms
    # Flip first axis
    out_img = resample_to_output(Nifti1Image(data, np.diag([-1, 1, 1, 1])))
    assert_array_equal(out_img.dataobj, np.flipud(data))
    # Subsample voxels
    out_img = resample_to_output(Nifti1Image(data, np.diag([4, 5, 6, 1])))
    exp_out = spnd.affine_transform(data,
                                    [1/4, 1/5, 1/6],
                                    output_shape = (5, 11, 19))
    assert_array_equal(out_img.dataobj, exp_out)
    # Unsubsample with voxel sizes
    out_img = resample_to_output(Nifti1Image(data, np.diag([4, 5, 6, 1])),
                                 [4, 5, 6])
    assert_array_equal(out_img.dataobj, data)
    # A rotation to test nearest, order, cval
    rot_3 = from_matvec(euler2mat(np.pi / 4), [0, 0, 0])
    rot_3_img = Nifti1Image(data, rot_3)
    out_img = resample_to_output(rot_3_img)
    exp_shape = (4, 4, 4)
    assert_equal(out_img.shape, exp_shape)
    exp_aff = np.array([[1, 0, 0, -2 * np.cos(np.pi / 4)],
                        [0, 1, 0, 0],
                        [0, 0, 1, 0],
                        [0, 0, 0, 1]])
    assert_almost_equal(out_img.affine, exp_aff)
    rzs, trans = to_matvec(np.dot(npl.inv(rot_3), exp_aff))
    exp_out = spnd.affine_transform(data, rzs, trans, exp_shape)
    assert_almost_equal(out_img.dataobj, exp_out)
    # Order
    assert_almost_equal(
        resample_to_output(rot_3_img, order=0).dataobj,
        spnd.affine_transform(data, rzs, trans, exp_shape, order=0))
    # Cval
    assert_almost_equal(
        resample_to_output(rot_3_img, cval=99).dataobj,
        spnd.affine_transform(data, rzs, trans, exp_shape, cval=99))
    # Mode
    assert_almost_equal(
        resample_to_output(rot_3_img, mode='nearest').dataobj,
        spnd.affine_transform(data, rzs, trans, exp_shape, mode='nearest'))
    # out_class
    img_ni1 = Nifti2Image(data, np.eye(4))
    img_ni2 = Nifti2Image(data, np.eye(4))
    # Default is Nifti1Image
    assert_equal(
        resample_to_output(img_ni2).__class__,
        Nifti1Image)
    # Can be overriden
    assert_equal(
        resample_to_output(img_ni1, out_class=Nifti2Image).__class__,
        Nifti2Image)
    # None specifies out_class from input
    assert_equal(
        resample_to_output(img_ni2, out_class=None).__class__,
        Nifti2Image)
Ejemplo n.º 45
0
 def as3d(aff):
     return from_matvec(aff[:3, :3], aff[:3, -1])
Ejemplo n.º 46
0
def test_resample_from_to():
    # Test resampling from image to image / image space
    data = np.arange(24).reshape((2, 3, 4))
    affine = np.diag([-4, 5, 6, 1])
    img = Nifti1Image(data, affine)
    img.header['descrip'] = 'red shirt image'
    out = resample_from_to(img, img)
    assert_almost_equal(img.dataobj, out.dataobj)
    assert_array_equal(img.affine, out.affine)
    # Check resampling reverses effect of flipping axes
    # This will also test translations
    flip_ornt = np.array([[0, 1], [1, 1], [2, 1]])
    for axis in (0, 1, 2):
        ax_flip_ornt = flip_ornt.copy()
        ax_flip_ornt[axis, 1] = -1
        aff_flip_i = inv_ornt_aff(ax_flip_ornt, (2, 3, 4))
        flipped_img = Nifti1Image(flip_axis(data, axis),
                                  np.dot(affine, aff_flip_i))
        out = resample_from_to(flipped_img, ((2, 3, 4), affine))
        assert_almost_equal(img.dataobj, out.dataobj)
        assert_array_equal(img.affine, out.affine)
    # A translation of one voxel on each axis
    trans_aff = from_matvec(np.diag([-4, 5, 6]), [4, -5, -6])
    trans_img = Nifti1Image(data, trans_aff)
    out = resample_from_to(trans_img, img)
    exp_out = np.zeros_like(data)
    exp_out[:-1, :-1, :-1] = data[1:, 1:, 1:]
    assert_almost_equal(out.dataobj, exp_out)
    out = resample_from_to(img, trans_img)
    trans_exp_out = np.zeros_like(data)
    trans_exp_out[1:, 1:, 1:] = data[:-1, :-1, :-1]
    assert_almost_equal(out.dataobj, trans_exp_out)
    # Test mode with translation of first axis only
    # Default 'constant' mode first
    trans1_aff = from_matvec(np.diag([-4, 5, 6]), [4, 0, 0])
    trans1_img = Nifti1Image(data, trans1_aff)
    out = resample_from_to(img, trans1_img)
    exp_out = np.zeros_like(data)
    exp_out[1:, :, :] = data[:-1, :, :]
    assert_almost_equal(out.dataobj, exp_out)
    # Then 'nearest' mode
    out = resample_from_to(img, trans1_img, mode='nearest')
    exp_out[0, :, :] = exp_out[1, :, :]
    assert_almost_equal(out.dataobj, exp_out)
    # Test order
    trans_p_25_aff = from_matvec(np.diag([-4, 5, 6]), [1, 0, 0])
    trans_p_25_img = Nifti1Image(data, trans_p_25_aff)
    # Suprising to me, but all points outside are set to 0, even with NN
    out = resample_from_to(img, trans_p_25_img, order=0)
    exp_out = np.zeros_like(data)
    exp_out[1:, :, :] = data[1, :, :]
    assert_almost_equal(out.dataobj, exp_out)
    out = resample_from_to(img, trans_p_25_img)
    exp_out = spnd.affine_transform(data, [1, 1, 1], [-0.25, 0, 0], order=3)
    assert_almost_equal(out.dataobj, exp_out)
    # Test cval
    out = resample_from_to(img, trans_img, cval=99)
    exp_out = np.zeros_like(data) + 99
    exp_out[1:, 1:, 1:] = data[:-1, :-1, :-1]
    assert_almost_equal(out.dataobj, exp_out)
    # Out class
    out = resample_from_to(img, trans_img)
    assert_equal(out.__class__, Nifti1Image)
    # By default, type of from_img makes no difference
    n1_img = Nifti2Image(data, affine)
    out = resample_from_to(n1_img, trans_img)
    assert_equal(out.__class__, Nifti1Image)
    # Passed as keyword arg
    out = resample_from_to(img, trans_img, out_class=Nifti2Image)
    assert_equal(out.__class__, Nifti2Image)
    # If keyword arg is None, use type of from_img
    out = resample_from_to(n1_img, trans_img, out_class=None)
    assert_equal(out.__class__, Nifti2Image)
    # to_img type irrelevant in all cases
    n1_trans_img = Nifti2Image(data, trans_aff)
    out = resample_from_to(img, n1_trans_img, out_class=None)
    assert_equal(out.__class__, Nifti1Image)
    # From 2D to 3D, error, the fixed affine is not invertible
    img_2d = Nifti1Image(data[:, :, 0], affine)
    assert_raises(AffineError, resample_from_to, img_2d, img)
    # 3D to 2D, we don't need to invert the fixed matrix
    out = resample_from_to(img, img_2d)
    assert_array_equal(out.dataobj, data[:, :, 0])
    # Same for tuple as to_img imput
    out = resample_from_to(img, (img_2d.shape, img_2d.affine))
    assert_array_equal(out.dataobj, data[:, :, 0])
    # 4D input and output also OK
    data_4d = np.arange(24 * 5).reshape((2, 3, 4, 5))
    img_4d = Nifti1Image(data_4d, affine)
    out = resample_from_to(img_4d, img_4d)
    assert_almost_equal(data_4d, out.dataobj)
    assert_array_equal(img_4d.affine, out.affine)
    # Errors trying to match 3D to 4D
    assert_raises(ValueError, resample_from_to, img_4d, img)
    assert_raises(ValueError, resample_from_to, img, img_4d)
Ejemplo n.º 47
0
def nifti2nipy(ni_img):
    """ Return NIPY image from NIFTI image `ni_image`

    Parameters
    ----------
    ni_img : nibabel.Nifti1Image
        NIFTI image

    Returns
    -------
    img : :class:`Image`
        nipy image

    Raises
    ------
    NiftiError : if image is < 3D

    Notes
    -----
    Lacking any other information, we take the input coordinate names for
    axes 0:7 to be  ('i', 'j', 'k', 't', 'u', 'v', 'w').

    If the image is 1D or 2D then we have a problem.  If there's a defined
    (sform, qform) affine, this has 3 input dimensions, and we have to guess
    what the extra input dimensions are.  If we don't have a defined affine, we
    don't know what the output dimensions are.  For example, if the image is 2D,
    and we don't have an affine, are these X and Y or X and Z or Y and Z?
    In the presence of ambiguity, resist the temptation to guess - raise a
    NiftiError.

    If there is a time-like axis, name the input and corresponding output axis
    for the type of axis ('t', 'hz', 'ppm', 'rads').

    Otherwise remove the 't' axis from both input and output names, and squeeze
    the length 1 dimension from the input data.

    If there's a 't' axis get ``toffset`` and put into affine at position [3,
    -1].

    If ``dim_info`` is set coherently, set input axis names to 'slice', 'freq',
    'phase' from ``dim_info``.

    Get the output spatial coordinate names from the 'scanner', 'aligned',
    'talairach', 'mni' XYZ spaces (see :mod:`nipy.core.reference.spaces`).

    We construct the N-D affine by taking the XYZ affine and adding scaling
    diagonal elements from ``pixdim``.

    If the space units in NIFTI ``xyzt_units`` are 'microns' or 'meters' we
    adjust the affine to mm units, but warn because this might be a mistake.

    If the time units in NIFTI `xyzt_units` are 'msec' or 'usec', scale the time
    axis ``pixdim`` values accordingly.

    Ignore the intent-related fields for now, but warn that we are doing so if
    there appears to be specific information in there.
    """
    hdr = ni_img.get_header()
    affine = ni_img.get_affine()
    # Affine will not be None from a loaded image, but just in case
    if affine is None:
        affine = hdr.get_best_affine()
    else:
        affine = affine.copy()
    data = ni_img.get_data()
    shape = list(ni_img.shape)
    ndim = len(shape)
    if ndim < 3:
        raise NiftiError("With less than 3 dimensions we cannot be sure "
                         "which input and output dimensions you intend for "
                         "the coordinate map.  Please fix this image with "
                         "nibabel or some other tool")
    # For now we only warn if intent is set to an unexpected value
    intent, _, _ = hdr.get_intent()
    if intent != 'none':
        warnings.warn('Ignoring intent field meaning "%s"' % intent,
                        UserWarning)
    # Which space?
    world_label = hdr.get_value_label('sform_code')
    if world_label == 'unknown':
        world_label = hdr.get_value_label('qform_code')
    world_space = XFORM2SPACE.get(world_label, ncrs.unknown_space)
    # Get information from dim_info
    input_names3 = list('ijk')
    freq, phase, slice = hdr.get_dim_info()
    if not freq is None:
        input_names3[freq] = 'freq'
    if not phase is None:
        input_names3[phase] = 'phase'
    if not slice is None:
        input_names3[slice] = 'slice'
    # Add to mm scaling, with warning
    space_units, time_like_units = hdr.get_xyzt_units()
    if space_units in ('micron', 'meter'):
        warnings.warn('"%s" space scaling in NIFTI ``xyt_units field; '
                      'applying scaling to affine, but this may not be what '
                      'you want' % space_units, UserWarning)
        if space_units == 'micron':
            affine[:3] /= 1000.
        elif space_units == 'meter':
            affine[:3] *= 1000.
    input_cs3 = CS(input_names3, name='voxels')
    output_cs3 = world_space.to_coordsys_maker()(3)
    cmap3 = AT(input_cs3, output_cs3, affine)
    if ndim == 3:
        return Image(data, cmap3, {'header': hdr})
    space_units, time_like_units = hdr.get_xyzt_units()
    units_info = TIME_LIKE_UNITS.get(time_like_units, None)
    n_ns = ndim - 3
    ns_zooms = list(hdr.get_zooms()[3:])
    ns_trans = [0] * n_ns
    ns_names = tuple('uvw')
    # Have we got a time axis?
    if (shape[3] == 1 and ndim > 4 and units_info is None):
        # Squeeze length 1 no-time axis
        shape.pop(3)
        ns_zooms.pop(0)
        ns_trans.pop(0)
        data = data.reshape(shape)
        n_ns -= 1
    else: # have time-like
        if units_info is None:
            units_info = TIME_LIKE_UNITS['sec']
        time_name = units_info['name']
        if units_info['scaling'] != 1:
            ns_zooms[0] *= units_info['scaling']
        if time_name == 't':
            # Get time offset
            ns_trans[0] = hdr['toffset']
        ns_names = (time_name,) + ns_names
    output_cs = CS(ns_names[:n_ns])
    input_cs = CS(ns_names[:n_ns])
    aff = from_matvec(np.diag(ns_zooms), ns_trans)
    ns_cmap = AT(input_cs, output_cs, aff)
    cmap = cm_product(cmap3, ns_cmap,
                      input_name=cmap3.function_domain.name,
                      output_name=cmap3.function_range.name)
    return Image(data, cmap, {'header': hdr})
Ejemplo n.º 48
0
def resample(image, target, mapping, shape, order=3, mode='constant',
             cval=0.0):
    """ Resample `image` to `target` CoordinateMap

    Use a "world-to-world" mapping `mapping` and spline interpolation of a 
    `order`.

    Here, "world-to-world" refers to the fact that mapping should be a
    callable that takes a physical coordinate in "target" and gives a
    physical coordinate in "image".

    Parameters
    ----------
    image : Image instance
       image that is to be resampled.
    target : CoordinateMap
       coordinate map for output image.
    mapping : callable or tuple or array
       transformation from target.function_range to
       image.coordmap.function_range, i.e. 'world-to-world mapping'. Can
       be specified in three ways: a callable, a tuple (A, b)
       representing the mapping y=dot(A,x)+b or a representation of this
       mapping as an affine array, in homogeneous coordinates.
    shape : sequence of int
       shape of output array, in target.function_domain.
    order : int, optional
       what order of interpolation to use in ``scipy.ndimage``.
    mode : str, optional
        Points outside the boundaries of the input are filled according to the
        given mode ('constant', 'nearest', 'reflect' or 'wrap'). Default is
        'constant'.
    cval : scalar, optional
        Value used for points outside the boundaries of the input if
        mode='constant'. Default is 0.0.

    Returns
    -------
    output : Image instance
       Image has interpolated data and output.coordmap == target.
    """
    if not callable(mapping):
        if type(mapping) is type(()):
            mapping = from_matvec(*mapping)
        # image world to target world mapping
        TW2IW = AffineTransform(target.function_range,
                                image.coordmap.function_range,
                                mapping)
    else:
        if isinstance(mapping, AffineTransform):
            TW2IW = mapping
        else:
            TW2IW = CoordinateMap(target.function_range,
                                  image.coordmap.function_range,
                                  mapping)
    # target voxel to image world mapping
    TV2IW = compose(TW2IW, target)
    # CoordinateMap describing mapping from target voxel to
    # image world coordinates
    if not isinstance(TV2IW, AffineTransform):
        # interpolator evaluates image at values image.coordmap.function_range,
        # i.e. physical coordinates rather than voxel coordinates
        grid = ArrayCoordMap.from_shape(TV2IW, shape)
        interp = ImageInterpolator(image, order=order, mode=mode, cval=cval)
        idata = interp.evaluate(grid.transposed_values)
        del(interp)
    else: # it is an affine transform, but, what if we compose?
        TV2IV = compose(image.coordmap.inverse(), TV2IW)
        if isinstance(TV2IV, AffineTransform): # still affine
            A, b = to_matvec(TV2IV.affine)
            idata = affine_transform(image.get_data(), A,
                                     offset=b,
                                     output_shape=shape,
                                     order=order,
                                     mode=mode,
                                     cval=cval)
        else: # not affine anymore
            interp = ImageInterpolator(image, order=order, mode=mode, cval=cval)
            grid = ArrayCoordMap.from_shape(TV2IV, shape)
            idata = interp.evaluate(grid.values)
            del(interp)
    return Image(idata, copy.copy(target))
Ejemplo n.º 49
0
def xyz_affine(coordmap, name2xyz=None):
    """ Return (4, 4) affine mapping voxel coordinates to XYZ from `coordmap`

    If no (4, 4) affine "makes sense"(TM) for this `coordmap` then raise errors
    listed below.  A (4, 4) affine makes sense if the first three output axes
    are recognizably X, Y, and Z in that order AND they there are corresponding
    input dimensions, AND the corresponding input dimensions are the first three
    input dimension (in any order).  Thus the input axes have to be 3D.

    Parameters
    ----------
    coordmap : ``CoordinateMap`` instance
    name2xyz : None or mapping, optional
        Object such that ``name2xyz[ax_name]`` returns 'x', or 'y' or 'z' or
        raises a KeyError for a str ``ax_name``.  None means use module default.

    Returns
    -------
    xyz_aff : (4,4) array
        voxel to X, Y, Z affine mapping

    Raises
    ------
    SpaceTypeError : if this is not an affine coordinate map
    AxesError : if not all of x, y, z recognized in `coordmap` output, or they
    are in the wrong order, or the x, y, z axes do not correspond to the first
    three input axes.
    AffineError : if axes dropped from the affine contribute to x, y, z
    coordinates.

    Notes
    -----
    We could also try and "make sense" (TM) of a coordmap that had X, Y and Z
    outputs, but not in that order, nor all in the first three axes.  In that
    case we could just permute the affine to get the output order we need.  But,
    that could become confusing if the returned affine has different output
    coordinates than the passed `coordmap`.  And it's more complicated.  So,
    let's not do that for now.

    Examples
    --------
    >>> cmap = vox2mni(np.diag([2,3,4,5,1]))
    >>> cmap
    AffineTransform(
       function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='voxels', coord_dtype=float64),
       function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S', 't'), name='mni', coord_dtype=float64),
       affine=array([[ 2.,  0.,  0.,  0.,  0.],
                     [ 0.,  3.,  0.,  0.,  0.],
                     [ 0.,  0.,  4.,  0.,  0.],
                     [ 0.,  0.,  0.,  5.,  0.],
                     [ 0.,  0.,  0.,  0.,  1.]])
    )
    >>> xyz_affine(cmap)
    array([[ 2.,  0.,  0.,  0.],
           [ 0.,  3.,  0.,  0.],
           [ 0.,  0.,  4.,  0.],
           [ 0.,  0.,  0.,  1.]])
    """
    if name2xyz is None:
        name2xyz = known_names
    try:
        affine = coordmap.affine
    except AttributeError:
        raise SpaceTypeError('Need affine coordinate map')
    order = xyz_order(coordmap.function_range, name2xyz)
    if order[:3] != [0, 1, 2]:
        raise AxesError('First 3 output axes must be X, Y, Z')
    # Check equivalent input axes
    ornt = io_orientation(affine)
    if set(ornt[:3, 0]) != set((0, 1, 2)):
        raise AxesError('First 3 input axes must correspond to X, Y, Z')
    # Check that dropped dimensions don't provide xyz coordinate info
    extra_cols = affine[:3,3:-1]
    if not np.allclose(extra_cols, 0):
        raise AffineError('Dropped dimensions not orthogonal to xyz')
    return from_matvec(affine[:3,:3], affine[:3,-1])