コード例 #1
0
def apply_mask(brain_img, mask_img):
    """ Load brain image, matching mask; apply and return masked image

    Parameters
    ----------
    brain_img : str or image
        string giving image filename or image object
    mask_img : str or image
        string giving mask image filename or image object.  Mask must match
        `brain_img` after mapping with image and mask affine.

    Returns
    -------
    mask_img : image object
        image object where data is data from `brain_img` multiplied elementwise
        by the data of `mask_img`, where the mask data has[ been resampled into
        the voxel space of `brain_img` if necessary.
    """
    brain_img = as_image(brain_img)
    b_aff = brain_img.affine
    b_hdr = brain_img.header
    mask_img = as_image(mask_img)
    brain_data = brain_img.get_data()
    mask_data = mask_img.get_data()
    if not np.allclose(b_aff, mask_img.affine):
        # Mask and brain have different affines - we need to resample
        brain2mask = npl.inv(mask_img.affine).dot(brain_img.affine)
        mat, vec = to_matvec(brain2mask)
        mask_data = affine_transform(mask_data,
                                     mat,
                                     vec,
                                     output_shape=brain_data.shape,
                                     order=0)  # nearest neighbor
    return nib.Nifti1Image(brain_data * mask_data, b_aff, b_hdr)
コード例 #2
0
def resample_img2img(img_to, img_from, order=1, out_class=nib.Nifti1Image):
    vox2vox = npl.inv(img_from.affine).dot(img_to.affine)
    rzs, trans = to_matvec(vox2vox)
    data = spnd.affine_transform(img_from.get_data(),
                                 rzs,
                                 trans,
                                 img_to.shape,
                                 order = order)
    return out_class(data, img_to.affine)
コード例 #3
0
def resample_img2img(img_to, img_from, order=1, out_class=nib.Nifti1Image):
    if not have_scipy:
        raise Exception('Scipy must be installed to run resample_img2img.')

    from scipy import ndimage as spnd
    vox2vox = npl.inv(img_from.affine).dot(img_to.affine)
    rzs, trans = to_matvec(vox2vox)
    data = spnd.affine_transform(img_from.get_data(),
                                 rzs,
                                 trans,
                                 img_to.shape,
                                 order=order)
    return out_class(data, img_to.affine)
コード例 #4
0
def resample_img2img(img_to, img_from, order=1, out_class=nib.Nifti1Image):
    if not have_scipy:
        raise Exception('Scipy must be installed to run resample_img2img.')

    from scipy import ndimage as spnd
    vox2vox = npl.inv(img_from.affine).dot(img_to.affine)
    rzs, trans = to_matvec(vox2vox)
    data = spnd.affine_transform(img_from.get_data(),
                                 rzs,
                                 trans,
                                 img_to.shape,
                                 order=order)
    return out_class(data, img_to.affine)
コード例 #5
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)
コード例 #6
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)
コード例 #7
0
ファイル: resample.py プロジェクト: sashka/nipy
def resample(image, target, mapping, shape, order=3):
    """ 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`

    Returns
    -------
    output : Image instance
       with 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)
        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)
        else:  # not affine anymore
            interp = ImageInterpolator(image, order=order)
            grid = ArrayCoordMap.from_shape(TV2IV, shape)
            idata = interp.evaluate(grid.values)
            del (interp)
    return Image(idata, copy.copy(target))
コード例 #8
0
ファイル: parrec2nii.py プロジェクト: neurodata/aloftus
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.])
        intercept = np.array([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.])
        intercept = np.array([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)
コード例 #9
0
ファイル: resample.py プロジェクト: Naereen/nipy
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))
コード例 #10
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))
    with pytest.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 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 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 resample_to_output(img_ni2).__class__ == Nifti1Image
    # Can be overriden
    assert resample_to_output(img_ni1, out_class=Nifti2Image).__class__ == Nifti2Image
    # None specifies out_class from input
    assert resample_to_output(img_ni2, out_class=None).__class__ == Nifti2Image
コード例 #11
0
ファイル: test_processing.py プロジェクト: arokem/nibabel
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)
コード例 #12
0
ファイル: parrec2nii_cmd.py プロジェクト: nipy/nibabel
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)
コード例 #13
0
ファイル: nifti_ref.py プロジェクト: pombredanne/nipy
def nipy2nifti(img, data_dtype=None, strict=None, fix0=False):
    """ Return NIFTI image from nipy image `img`

    Parameters
    ----------
    img : object
         An object, usually a NIPY ``Image``,  having attributes `coordmap` and
         `shape`
    data_dtype : None or dtype specifier
        None means try and use header dtype, otherwise try and use data dtype,
        otherwise use np.float32.  A dtype specifier means set the header output
        data dtype using ``np.dtype(data_dtype)``.
    strict : bool, optional
        Whether to use strict checking of input image for creating NIFTI
    fix0: bool, optional
        Whether to fix potential 0 column / row in affine. This option only used
        when trying to find time etc axes in the coordmap output names.  In
        order to find matching input names, we need to use the corresponding
        rows and columns in the affine.  Sometimes time, in particular, has 0
        scaling, and thus all 0 in the corresponding row / column.  In that case
        it's hard to work out which input corresponds. If `fix0` is True, and
        there is only one all zero (matrix part of the) affine row, and only one
        all zero (matrix part of the) affine column, fix scaling for that
        combination to zero, assuming this a zero scaling for time.

    Returns
    -------
    ni_img : ``nibabel.Nifti1Image``
        NIFTI image

    Raises
    ------
    NiftiError: if space axes not orthogonal to non-space axes
    NiftiError: if non-space axes not orthogonal to each other
    NiftiError: if `img` output space does not match named spaces in NIFTI
    NiftiError: if input image has more than 7 dimensions
    NiftiError: if input image has 7 dimensions, but no time dimension, because
        we need to add an extra 1 length axis at position 3
    NiftiError: if we find a time-like input axis but the matching output axis
        is a different time-like.
    NiftiError: if we find a time-like output axis but the matching input axis
        is a different time-like.
    NiftiError: if we find a time output axis and there are non-zero non-spatial
        offsets in the affine, but we can't find a corresponding input axis.

    Notes
    -----
    First, we need to create a valid XYZ Affine.  We check if this can be done
    by checking if there are recognizable X, Y, Z output axes and corresponding
    input (voxel) axes.  This requires the input image to be at least 3D. If we
    find these requirements, we reorder the image axes to have XYZ output axes
    and 3 spatial input axes first, and get the corresponding XYZ affine.

    If the spatial dimensions are not orthogonal to the non-spatial dimensions,
    raise a NiftiError.

    If the non-spatial dimensions are not orthogonal to each other, raise a
    NiftiError.

    We check if the XYZ output fits with the NIFTI named spaces of scanner,
    aligned, Talairach, MNI.  If so, set the NIFTI code and qform, sform
    accordingly.  If the space corresponds to 'unknown' then we must set the
    NIFTI transform codes to 0, and the affine must match the affine we will get
    from loading the NIFTI with no qform, sform.  If not, we're going to lose
    information in the affine, and raise an error.

    If any of the first three input axes are named ('slice', 'freq', 'phase')
    set the ``dim_info`` field accordingly.

    Set the ``xyzt_units`` field to indicate millimeters and seconds, if there
    is a 't' axis, otherwise millimeters and 0 (unknown).

    We look to see if we have a time-like axis in the inputs or the outputs. A
    time-like axis has labels 't', 'hz', 'ppm', 'rads'.  If we have an axis 't'
    in the inputs *and* the outputs, check they either correspond, or both
    inputs and output correspond with no other axis, otherwise raise NiftiError.
    Do the same check for 'hz', then 'ppm', then 'rads'.

    If we do have a time-like axis, roll that axis to be the 4th axis.  If this
    axis is actually time, take the ``affine[3, -1]`` and put into the
    ``toffset`` field.  If there's no time-like axis, but there are other
    non-spatial axes, make a length 1 4th array axis to indicate this.

    If the resulting NIFTI image has more than 7 dimensions, raise a NiftiError.

    Set ``pixdim`` for axes >= 3 using vector length of corresponding affine
    columns.

    We don't set the intent-related fields for now.
    """
    strict_none = strict is None
    if strict_none:
        warnings.warn('Default `strict` currently False; this will change to '
                      'True in a future version of nipy',
                      FutureWarning,
                      stacklevel = 2)
        strict = False
    known_names = ncrs.known_names
    if not strict: # add simple 'xyz' to acceptable spatial names
        known_names = copy(known_names) # copy module global dict
        for c in 'xyz':
            known_names[c] = c
    try:
        img = as_xyz_image(img, known_names)
    except (ncrs.AxesError, ncrs.AffineError):
        # Python 2.5 / 3 compatibility
        e = sys.exc_info()[1]
        raise NiftiError('Image cannot be reordered to XYZ because: "%s"'
                         % e)
    coordmap = img.coordmap
    # Get useful information from old header
    in_hdr = img.metadata.get('header', None)
    hdr = nib.Nifti1Header.from_header(in_hdr)
    # Default behavior is to take datatype from old header, unless there was no
    # header, in which case we try to use the data dtype.
    data = None
    if data_dtype is None:
        if in_hdr is None:
            data = img.get_data()
            data_dtype = data.dtype
        else:
            data_dtype = in_hdr.get_data_dtype()
    else:
        data_dtype = np.dtype(data_dtype)
    hdr.set_data_dtype(data_dtype)
    # Remaining axes orthogonal?
    rzs, trans = to_matvec(coordmap.affine)
    if (not np.allclose(rzs[3:, :3], 0) or
        not np.allclose(rzs[:3, 3:], 0)):
        raise NiftiError('Non space axes not orthogonal to space')
    # And to each other?
    nsp_affine = rzs[3:,3:]
    nsp_nzs = np.abs(nsp_affine) > TINY
    n_in_col = np.sum(nsp_nzs, axis=0)
    n_in_row = np.sum(nsp_nzs, axis=1)
    if np.any(n_in_col > 1) or np.any(n_in_row > 1):
        raise NiftiError('Non space axes not orthogonal to each other')
    # Affine seems OK, check for space
    xyz_affine = ncrs.xyz_affine(coordmap, known_names)
    spatial_output_names = coordmap.function_range.coord_names[:3]
    out_space = CS(spatial_output_names)
    for name, space in XFORM2SPACE.items():
        if out_space in space:
            hdr.set_sform(xyz_affine, name)
            hdr.set_qform(xyz_affine, name)
            break
    else:
        if not strict and spatial_output_names == ('x', 'y', 'z'):
            warnings.warn('Default `strict` currently False; '
                          'this will change to True in a future version of '
                          'nipy; output names of "x", "y", "z" will raise '
                          'an error.  Please use canonical output names from '
                          'nipy.core.reference.spaces',
                          FutureWarning,
                          stacklevel = 2)
            hdr.set_sform(xyz_affine, 'scanner')
            hdr.set_qform(xyz_affine, 'scanner')
        elif not out_space in ncrs.unknown_space: # no space we recognize
            raise NiftiError('Image world not a NIFTI world')
        else: # unknown space requires affine that matches
            if not np.allclose(xyz_affine, hdr.get_base_affine()):
                raise NiftiError("Image world is 'unknown' but affine not "
                                 "compatible; please reset image world or "
                                 "affine")
            hdr.set_qform(None)
            hdr.set_sform(None)
    # Use list() to get .index method for python < 2.6
    input_names = list(coordmap.function_domain.coord_names)
    spatial_names = input_names[:3]
    dim_infos = []
    for fps in 'freq', 'phase', 'slice':
        dim_infos.append(
            spatial_names.index(fps) if fps in spatial_names else None)
    hdr.set_dim_info(*dim_infos)
    # Set units without knowing time
    hdr.set_xyzt_units(xyz='mm')
    # Done if we only have 3 input dimensions
    n_ns = coordmap.ndims[0] - 3
    if n_ns == 0: # No non-spatial dimensions
        return nib.Nifti1Image(img.get_data(), xyz_affine, hdr)
    elif n_ns > 4:
        raise NiftiError("Too many dimensions to convert")
    # Go now to data, pixdims
    if data is None:
        data = img.get_data()
    rzs, trans = to_matvec(img.coordmap.affine)
    ns_pixdims = list(np.sqrt(np.sum(rzs[3:, 3:] ** 2, axis=0)))
    in_ax, out_ax, tl_name = _find_time_like(coordmap, fix0)
    if in_ax is None: # No time-like axes
        # add new 1-length axis
        if n_ns == 4:
            raise NiftiError("Too many dimensions to convert")
        n_ns += 1
        data = data[:, :, :, None, ...]
        # xyzt_units
        hdr.set_xyzt_units(xyz='mm')
        # shift pixdims
        ns_pixdims.insert(0, 0)
    else: # Time-like
        hdr.set_xyzt_units(xyz='mm', t=TIME_LIKE_AXES[tl_name]['units'])
        # If this is really time, set toffset
        if tl_name == 't' and np.any(trans[3:]):
            # Which output axis corresponds to time?
            if out_ax is None:
                raise NiftiError('Time input and output do not match')
            hdr['toffset'] = trans[out_ax]
        # Make sure this time-like axis is first non-space axis
        if in_ax != 3:
            data = np.rollaxis(data, in_ax, 3)
            order = range(n_ns)
            order.pop(in_ax - 3)
            order.insert(0, in_ax - 3)
            ns_pixdims = [ns_pixdims[i] for i in order]
    hdr['pixdim'][4:(4 + n_ns)] = ns_pixdims
    return nib.Nifti1Image(data, xyz_affine, hdr)
コード例 #14
0
def nipy2nifti(img, data_dtype=None, strict=None, fix0=True):
    """ Return NIFTI image from nipy image `img`

    Parameters
    ----------
    img : object
         An object, usually a NIPY ``Image``,  having attributes `coordmap` and
         `shape`
    data_dtype : None or dtype specifier
        None means try and use header dtype, otherwise try and use data dtype,
        otherwise use np.float32.  A dtype specifier means set the header output
        data dtype using ``np.dtype(data_dtype)``.
    strict : bool, optional
        Whether to use strict checking of input image for creating NIFTI
    fix0: bool, optional
        Whether to fix potential 0 column / row in affine. This option only used
        when trying to find time etc axes in the coordmap output names.  In
        order to find matching input names, we need to use the corresponding
        rows and columns in the affine.  Sometimes time, in particular, has 0
        scaling, and thus all 0 in the corresponding row / column.  In that case
        it's hard to work out which input corresponds. If `fix0` is True, and
        there is only one all zero (matrix part of the) affine row, and only one
        all zero (matrix part of the) affine column, fix scaling for that
        combination to zero, assuming this a zero scaling for time.

    Returns
    -------
    ni_img : ``nibabel.Nifti1Image``
        NIFTI image

    Raises
    ------
    NiftiError: if space axes not orthogonal to non-space axes
    NiftiError: if non-space axes not orthogonal to each other
    NiftiError: if `img` output space does not match named spaces in NIFTI
    NiftiError: if input image has more than 7 dimensions
    NiftiError: if input image has 7 dimensions, but no time dimension, because
        we need to add an extra 1 length axis at position 3
    NiftiError: if we find a time-like input axis but the matching output axis
        is a different time-like.
    NiftiError: if we find a time-like output axis but the matching input axis
        is a different time-like.
    NiftiError: if we find a time output axis and there are non-zero non-spatial
        offsets in the affine, but we can't find a corresponding input axis.

    Notes
    -----
    First, we need to create a valid XYZ Affine.  We check if this can be done
    by checking if there are recognizable X, Y, Z output axes and corresponding
    input (voxel) axes.  This requires the input image to be at least 3D. If we
    find these requirements, we reorder the image axes to have XYZ output axes
    and 3 spatial input axes first, and get the corresponding XYZ affine.

    If the spatial dimensions are not orthogonal to the non-spatial dimensions,
    raise a NiftiError.

    If the non-spatial dimensions are not orthogonal to each other, raise a
    NiftiError.

    We check if the XYZ output fits with the NIFTI named spaces of scanner,
    aligned, Talairach, MNI.  If so, set the NIFTI code and qform, sform
    accordingly.  If the space corresponds to 'unknown' then we must set the
    NIFTI transform codes to 0, and the affine must match the affine we will get
    from loading the NIFTI with no qform, sform.  If not, we're going to lose
    information in the affine, and raise an error.

    If any of the first three input axes are named ('slice', 'freq', 'phase')
    set the ``dim_info`` field accordingly.

    Set the ``xyzt_units`` field to indicate millimeters and seconds, if there
    is a 't' axis, otherwise millimeters and 0 (unknown).

    We look to see if we have a time-like axis in the inputs or the outputs. A
    time-like axis has labels 't', 'hz', 'ppm', 'rads'.  If we have an axis 't'
    in the inputs *and* the outputs, check they either correspond, or both
    inputs and output correspond with no other axis, otherwise raise NiftiError.
    Do the same check for 'hz', then 'ppm', then 'rads'.

    If we do have a time-like axis, roll that axis to be the 4th axis.  If this
    axis is actually time, take the ``affine[3, -1]`` and put into the
    ``toffset`` field.  If there's no time-like axis, but there are other
    non-spatial axes, make a length 1 4th array axis to indicate this.

    If the resulting NIFTI image has more than 7 dimensions, raise a NiftiError.

    Set ``pixdim`` for axes >= 3 using vector length of corresponding affine
    columns.

    We don't set the intent-related fields for now.
    """
    strict_none = strict is None
    if strict_none:
        warnings.warn('Default `strict` currently False; this will change to '
                      'True in a future version of nipy',
                      FutureWarning,
                      stacklevel = 2)
        strict = False
    known_names = ncrs.known_names
    if not strict: # add simple 'xyz' to acceptable spatial names
        known_names = copy(known_names) # copy module global dict
        for c in 'xyz':
            known_names[c] = c
    try:
        img = as_xyz_image(img, known_names)
    except (ncrs.AxesError, ncrs.AffineError):
        # Python 2.5 / 3 compatibility
        e = sys.exc_info()[1]
        raise NiftiError('Image cannot be reordered to XYZ because: "%s"'
                         % e)
    coordmap = img.coordmap
    # Get useful information from old header
    in_hdr = img.metadata.get('header', None)
    hdr = nib.Nifti1Header.from_header(in_hdr)
    # Default behavior is to take datatype from old header, unless there was no
    # header, in which case we try to use the data dtype.
    data = None
    if data_dtype is None:
        if in_hdr is None:
            data = img.get_data()
            data_dtype = data.dtype
        else:
            data_dtype = in_hdr.get_data_dtype()
    else:
        data_dtype = np.dtype(data_dtype)
    hdr.set_data_dtype(data_dtype)
    # Remaining axes orthogonal?
    rzs, trans = to_matvec(coordmap.affine)
    if (not np.allclose(rzs[3:, :3], 0) or
        not np.allclose(rzs[:3, 3:], 0)):
        raise NiftiError('Non space axes not orthogonal to space')
    # And to each other?
    nsp_affine = rzs[3:,3:]
    nsp_nzs = np.abs(nsp_affine) > TINY
    n_in_col = np.sum(nsp_nzs, axis=0)
    n_in_row = np.sum(nsp_nzs, axis=1)
    if np.any(n_in_col > 1) or np.any(n_in_row > 1):
        raise NiftiError('Non space axes not orthogonal to each other')
    # Affine seems OK, check for space
    xyz_affine = ncrs.xyz_affine(coordmap, known_names)
    spatial_output_names = coordmap.function_range.coord_names[:3]
    out_space = CS(spatial_output_names)
    for name, space in XFORM2SPACE.items():
        if out_space in space:
            hdr.set_sform(xyz_affine, name)
            hdr.set_qform(xyz_affine, name)
            break
    else:
        if not strict and spatial_output_names == ('x', 'y', 'z'):
            warnings.warn('Default `strict` currently False; '
                          'this will change to True in a future version of '
                          'nipy; output names of "x", "y", "z" will raise '
                          'an error.  Please use canonical output names from '
                          'nipy.core.reference.spaces',
                          FutureWarning,
                          stacklevel = 2)
            hdr.set_sform(xyz_affine, 'scanner')
            hdr.set_qform(xyz_affine, 'scanner')
        elif not out_space in ncrs.unknown_space: # no space we recognize
            raise NiftiError('Image world not a NIFTI world')
        else: # unknown space requires affine that matches
            if not np.allclose(xyz_affine, hdr.get_base_affine()):
                raise NiftiError("Image world is 'unknown' but affine not "
                                 "compatible; please reset image world or "
                                 "affine")
            hdr.set_qform(None)
            hdr.set_sform(None)
    # Use list() to get .index method for python < 2.6
    input_names = list(coordmap.function_domain.coord_names)
    spatial_names = input_names[:3]
    dim_infos = []
    for fps in 'freq', 'phase', 'slice':
        dim_infos.append(
            spatial_names.index(fps) if fps in spatial_names else None)
    hdr.set_dim_info(*dim_infos)
    # Set units without knowing time
    hdr.set_xyzt_units(xyz='mm')
    # Done if we only have 3 input dimensions
    n_ns = coordmap.ndims[0] - 3
    if n_ns == 0: # No non-spatial dimensions
        return nib.Nifti1Image(img.get_data(), xyz_affine, hdr)
    elif n_ns > 4:
        raise NiftiError("Too many dimensions to convert")
    # Go now to data, pixdims
    if data is None:
        data = img.get_data()
    rzs, trans = to_matvec(img.coordmap.affine)
    ns_pixdims = list(np.sqrt(np.sum(rzs[3:, 3:] ** 2, axis=0)))
    in_ax, out_ax, tl_name = _find_time_like(coordmap, fix0)
    if in_ax is None: # No time-like axes
        # add new 1-length axis
        if n_ns == 4:
            raise NiftiError("Too many dimensions to convert")
        n_ns += 1
        data = data[:, :, :, None, ...]
        # xyzt_units
        hdr.set_xyzt_units(xyz='mm')
        # shift pixdims
        ns_pixdims.insert(0, 0)
    else: # Time-like
        hdr.set_xyzt_units(xyz='mm', t=TIME_LIKE_AXES[tl_name]['units'])
        # If this is really time, set toffset
        if tl_name == 't' and np.any(trans[3:]):
            # Which output axis corresponds to time?
            if out_ax is None:
                raise NiftiError('Time input and output do not match')
            hdr['toffset'] = trans[out_ax]
        # Make sure this time-like axis is first non-space axis
        if in_ax != 3:
            data = np.rollaxis(data, in_ax, 3)
            order = range(n_ns)
            order.pop(in_ax - 3)
            order.insert(0, in_ax - 3)
            ns_pixdims = [ns_pixdims[i] for i in order]
    hdr['pixdim'][4:(4 + n_ns)] = ns_pixdims
    return nib.Nifti1Image(data, xyz_affine, hdr)