예제 #1
0
def diagnose(args):
    """ Calculate, write results from diagnostic screen

    Parameters
    ----------
    args : object
        object with attributes:

        * filename : str - 4D image filename
        * time_axis : str - name or number of time axis in `filename`
        * slice_axis : str - name or number of slice axis in `filename`
        * out_path : None or str - path to which to write results
        * out_fname_label : None or filename - suffix of output results files
        * ncomponents : int - number of PCA components to write images for

    Returns
    -------
    res : dict
        Results of running :func:`screen` on `filename`
    """
    img, time_axis, slice_axis = parse_fname_axes(args.filename,
                                                  args.time_axis,
                                                  args.slice_axis)
    res = screen(img, args.ncomponents, time_axis, slice_axis)
    froot, ext, addext = splitext_addext(args.filename)
    fpath, fbase = psplit(froot)
    fpath = fpath if args.out_path is None else args.out_path
    fbase = fbase if args.out_fname_label is None else args.out_fname_label
    write_screen_res(res, fpath, fbase, ext + addext)
    return res
예제 #2
0
파일: commands.py 프로젝트: Naereen/nipy
def diagnose(args):
    """ Calculate, write results from diagnostic screen

    Parameters
    ----------
    args : object
        object with attributes:

        * filename : str - 4D image filename
        * time_axis : str - name or number of time axis in `filename`
        * slice_axis : str - name or number of slice axis in `filename`
        * out_path : None or str - path to which to write results
        * out_fname_label : None or filename - suffix of output results files
        * ncomponents : int - number of PCA components to write images for

    Returns
    -------
    res : dict
        Results of running :func:`screen` on `filename`
    """
    img, time_axis, slice_axis = parse_fname_axes(args.filename,
                                                  args.time_axis,
                                                  args.slice_axis)
    res = screen(img, args.ncomponents, time_axis, slice_axis)
    froot, ext, addext = splitext_addext(args.filename)
    fpath, fbase = psplit(froot)
    fpath = fpath if args.out_path is None else args.out_path
    fbase = fbase if args.out_fname_label is None else args.out_fname_label
    write_screen_res(res, fpath, fbase, ext + addext)
    return res
예제 #3
0
파일: utils.py 프로젝트: TomMaullin/tedana
def get_dtype(data):
    """
    Determines neuroimaging format of `data`

    Parameters
    ----------
    data : list-of-str or str or img_like
        Data to determine format of

    Returns
    -------
    dtype : {'NIFTI', 'GIFTI', 'OTHER'} str
        Format of input data
    """

    if isinstance(data, list):
        dtypes = np.unique([get_dtype(d) for d in data])
        if dtypes.size > 1:
            raise ValueError('Provided data detected to have varying formats: '
                             '{}'.format(dtypes))
        return dtypes[0]
    elif isinstance(data, str):
        dtype = splitext_addext(data)[1]
    else:  # img_like?
        if not hasattr(data, 'valid_exts'):
            raise TypeError('Input data format cannot be detected.')
        dtype = data.valid_exts[0]

    return FORMATS.get(dtype, 'OTHER')
예제 #4
0
파일: utils.py 프로젝트: NBCLab/tedana
def get_dtype(data):
    """
    Determines neuroimaging format of `data`

    Parameters
    ----------
    data : :obj:`list` of :obj:`str` or :obj:`str` or img_like
        Data to determine format of

    Returns
    -------
    dtype : {'NIFTI', 'OTHER'} str
        Format of input data
    """

    if isinstance(data, list):
        dtypes = np.unique([get_dtype(d) for d in data])
        if dtypes.size > 1:
            raise ValueError('Provided data detected to have varying formats: '
                             '{}'.format(dtypes))
        return dtypes[0]
    elif isinstance(data, str):
        dtype = splitext_addext(data)[1]
    else:  # img_like?
        if not hasattr(data, 'valid_exts'):
            raise TypeError('Input data format cannot be detected.')
        dtype = data.valid_exts[0]

    return FORMATS.get(dtype, 'OTHER')
예제 #5
0
def napari_get_reader(path):
    """A basic implementation of the napari_get_reader hook specification.

    Parameters
    ----------
    path : str or list of str
        Path to file, or list of paths.

    Returns
    -------
    function or None
        If the path is a recognized format, return a function that accepts the
        same path or list of paths, and returns a list of layer data tuples.
    """
    if isinstance(path, list):
        # reader plugins may be handed single path, or a list of paths.
        # if it is a list, it is assumed to be an image stack...
        # so we are only going to look at the first file.
        path = path[0]

    froot, ext, addext = splitext_addext(path)

    # if we know we cannot read the file, we immediately return None.
    if not ext.lower() in all_valid_exts:
        return None

    # otherwise we return the *function* that can read ``path``.
    return reader_function
예제 #6
0
파일: utils.py 프로젝트: TomMaullin/tedana
def filewrite(data, filename, ref_img, gzip=False, copy_header=True,
              copy_meta=False):
    """
    Writes `data` to `filename` in format of `ref_img`

    If `ref_img` dtype is GIFTI, then `data` is assumed to be stacked L/R
    hemispheric and will be split and saved as two files

    Parameters
    ----------
    data : (S [x T]) array_like
        Data to be saved
    filename : str
        Filepath where data should be saved to
    ref_img : str or img_like
        Reference image
    gzip : bool, optional
        Whether to gzip output (if not specified in `filename`). Only applies
        if output dtype is NIFTI. Default: False
    copy_header : bool, optional
        Whether to copy header from `ref_img` to new image. Default: True
    copy_meta : bool, optional
        Whether to copy meta from `ref_img` to new image. Only applies if
        output dtype is GIFTI. Default: False

    Returns
    -------
    name : str
        Path of saved image (with added extensions, as appropriate)
    """

    # get datatype and reference image for comparison
    dtype = get_dtype(ref_img)
    if isinstance(ref_img, list):
        ref_img = ref_img[0]

    # ensure that desired output type (from name) is compatible with `dtype`
    root, ext, add = splitext_addext(filename)
    if ext != '' and FORMATS[ext] != dtype:
        raise ValueError('Cannot write {} data to {} file. Please ensure file'
                         'formats are compatible'.format(dtype, FORMATS[ext]))

    if dtype == 'NIFTI':
        out = new_nii_like(ref_img, data, copy_header=copy_header)
        name = '{}.{}'.format(root, 'nii.gz' if gzip else 'nii')
        out.to_filename(name)
    elif dtype == 'GIFTI':
        # remove possible hemispheric denotations from root
        root = op.join(op.dirname(root), op.basename(root).split('.')[0])
        # save hemispheres separately
        for n, (hdata, hemi) in enumerate(zip(np.split(data, 2, axis=0),
                                              ['L', 'R'])):
            out = new_gii_like(ref_img[n], hdata,
                               copy_header=copy_header,
                               copy_meta=copy_meta)
            name = '{}.{}.func.gii'.format(root, hemi)
            out.to_filename(name)

    return name
예제 #7
0
def filewrite(data, filename, ref_img, gzip=False, copy_header=True,
              copy_meta=False):
    """
    Writes `data` to `filename` in format of `ref_img`

    If `ref_img` dtype is GIFTI, then `data` is assumed to be stacked L/R
    hemispheric and will be split and saved as two files

    Parameters
    ----------
    data : (S [x T]) array_like
        Data to be saved
    filename : str
        Filepath where data should be saved to
    ref_img : str or img_like
        Reference image
    gzip : bool, optional
        Whether to gzip output (if not specified in `filename`). Only applies
        if output dtype is NIFTI. Default: False
    copy_header : bool, optional
        Whether to copy header from `ref_img` to new image. Default: True
    copy_meta : bool, optional
        Whether to copy meta from `ref_img` to new image. Only applies if
        output dtype is GIFTI. Default: False

    Returns
    -------
    name : str
        Path of saved image (with added extensions, as appropriate)
    """

    # get datatype and reference image for comparison
    dtype = get_dtype(ref_img)
    if isinstance(ref_img, list):
        ref_img = ref_img[0]

    # ensure that desired output type (from name) is compatible with `dtype`
    root, ext, add = splitext_addext(filename)
    if ext != '' and FORMATS[ext] != dtype:
        raise ValueError('Cannot write {} data to {} file. Please ensure file'
                         'formats are compatible'.format(dtype, FORMATS[ext]))

    if dtype == 'NIFTI':
        out = new_nii_like(ref_img, data, copy_header=copy_header)
        name = '{}.{}'.format(root, 'nii.gz' if gzip else 'nii')
        out.to_filename(name)
    elif dtype == 'GIFTI':
        # remove possible hemispheric denotations from root
        root = op.join(op.dirname(root), op.basename(root).split('.')[0])
        # save hemispheres separately
        for n, (hdata, hemi) in enumerate(zip(np.split(data, 2, axis=0),
                                              ['L', 'R'])):
            out = new_gii_like(ref_img, hdata,
                               copy_header=copy_header,
                               copy_meta=copy_meta)
            name = '{}.{}.func.gii'.format(root, hemi)
            out.to_filename(name)

    return name
예제 #8
0
def guessed_image_type(filename):
    
    froot, ext, trailing = splitext_addext(filename, ('.gz', '.bz2'))
    lext = ext.lower()
    
    if lext == '.img' and os.path.isfile(os.path.join(froot,'.hdr')):
        return nibabel_guess(filename)
    else:
        
        return WashUImage
예제 #9
0
def tsdiffana(args):
    """ Generate tsdiffana plots from command line params `args`

    Parameters
    ----------
    args : object
        object with attributes

        * filename : str - 4D image filename
        * out_file : str - graphics file to write to instead of leaving
          graphics on screen
        * time_axis : str - name or number of time axis in `filename`
        * slice_axis : str - name or number of slice axis in `filename`
        * write_results : bool - if True, write images and plots to files
        * out_path : None or str - path to which to write results
        * out_fname_label : None or filename - suffix of output results files

    Returns
    -------
    axes : Matplotlib axes
       Axes on which we have done the plots.
    """
    if args.out_file is not None and args.write_results:
        raise ValueError("Cannot have OUT_FILE and WRITE_RESULTS options "
                         "together")
    img, time_axis, slice_axis = parse_fname_axes(args.filename,
                                                  args.time_axis,
                                                  args.slice_axis)
    results = time_slice_diffs_image(img, time_axis, slice_axis)
    axes = plot_tsdiffs(results)
    if args.out_file is None and not args.write_results:
        # interactive mode
        return axes
    if args.out_file is not None:
        # plot only mode
        axes[0].figure.savefig(args.out_file)
        return axes
    # plot and images mode
    froot, ext, addext = splitext_addext(args.filename)
    fpath, fbase = psplit(froot)
    fpath = fpath if args.out_path is None else args.out_path
    fbase = fbase if args.out_fname_label is None else args.out_fname_label
    axes[0].figure.savefig(pjoin(fpath, 'tsdiff_' + fbase + '.png'))
    # Save image volumes
    for key, prefix in (('slice_diff2_max_vol', 'dv2_max_'), ('diff2_mean_vol',
                                                              'dv2_mean_')):
        fname = pjoin(fpath, prefix + fbase + ext + addext)
        nipy.save_image(results[key], fname)
    # Save time courses into npz
    np.savez(
        pjoin(fpath, 'tsdiff_' + fbase + '.npz'),
        volume_means=results['volume_means'],
        slice_mean_diff2=results['slice_mean_diff2'],
    )
    return axes
예제 #10
0
파일: commands.py 프로젝트: Naereen/nipy
def tsdiffana(args):
    """ Generate tsdiffana plots from command line params `args`

    Parameters
    ----------
    args : object
        object with attributes

        * filename : str - 4D image filename
        * out_file : str - graphics file to write to instead of leaving
          graphics on screen
        * time_axis : str - name or number of time axis in `filename`
        * slice_axis : str - name or number of slice axis in `filename`
        * write_results : bool - if True, write images and plots to files
        * out_path : None or str - path to which to write results
        * out_fname_label : None or filename - suffix of output results files

    Returns
    -------
    axes : Matplotlib axes
       Axes on which we have done the plots.
    """
    if args.out_file is not None and args.write_results:
        raise ValueError("Cannot have OUT_FILE and WRITE_RESULTS options "
                         "together")
    img, time_axis, slice_axis = parse_fname_axes(args.filename,
                                                  args.time_axis,
                                                  args.slice_axis)
    results = time_slice_diffs_image(img, time_axis, slice_axis)
    axes = plot_tsdiffs(results)
    if args.out_file is None and not args.write_results:
        # interactive mode
        return axes
    if args.out_file is not None:
        # plot only mode
        axes[0].figure.savefig(args.out_file)
        return axes
    # plot and images mode
    froot, ext, addext = splitext_addext(args.filename)
    fpath, fbase = psplit(froot)
    fpath = fpath if args.out_path is None else args.out_path
    fbase = fbase if args.out_fname_label is None else args.out_fname_label
    axes[0].figure.savefig(pjoin(fpath, 'tsdiff_' + fbase + '.png'))
    # Save image volumes
    for key, prefix in (('slice_diff2_max_vol', 'dv2_max_'),
                        ('diff2_mean_vol', 'dv2_mean_')):
        fname = pjoin(fpath, prefix + fbase + ext + addext)
        nipy.save_image(results[key], fname)
    # Save time courses into npz
    np.savez(pjoin(fpath, 'tsdiff_' + fbase + '.npz'),
             volume_means=results['volume_means'],
             slice_mean_diff2=results['slice_mean_diff2'],
            )
    return axes
예제 #11
0
    def _list_outputs(self):
        outputs = self._outputs().get()
        filename = splitext_addext(os.path.basename(self.inputs.in_files[0]))[0]
        out_dir = os.path.abspath('TED.{}'.format(filename))

        outputs['t2star_map'] = os.path.join(out_dir, 't2sv.nii')
        outputs['s0_map'] = os.path.join(out_dir, 's0v.nii')
        outputs['t2star_adaptive_map'] = os.path.join(out_dir, 't2svG.nii')
        outputs['s0_adaptive_map'] = os.path.join(out_dir, 's0vG.nii')
        outputs['optimal_comb'] = os.path.join(out_dir, 'ts_OC.nii')

        return outputs
예제 #12
0
    def _list_outputs(self):
        outputs = self._outputs().get()
        filename = splitext_addext(os.path.basename(
            self.inputs.in_files[0]))[0]
        out_dir = os.path.abspath('TED.{}'.format(filename))

        outputs['t2star_map'] = os.path.join(out_dir, 't2sv.nii')
        outputs['s0_map'] = os.path.join(out_dir, 's0v.nii')
        outputs['t2star_adaptive_map'] = os.path.join(out_dir, 't2svG.nii')
        outputs['s0_adaptive_map'] = os.path.join(out_dir, 's0vG.nii')
        outputs['optimal_comb'] = os.path.join(out_dir, 'ts_OC.nii')

        return outputs
예제 #13
0
파일: utils.py 프로젝트: NBCLab/tedana
def filewrite(data, filename, ref_img, gzip=False, copy_header=True):
    """
    Writes `data` to `filename` in format of `ref_img`

    Parameters
    ----------
    data : (S [x T]) array_like
        Data to be saved
    filename : :obj:`str`
        Filepath where data should be saved to
    ref_img : :obj:`str` or img_like
        Reference image
    gzip : :obj:`bool`, optional
        Whether to gzip output (if not specified in `filename`). Only applies
        if output dtype is NIFTI. Default: False
    copy_header : :obj:`bool`, optional
        Whether to copy header from `ref_img` to new image. Default: True

    Returns
    -------
    name : :obj:`str`
        Path of saved image (with added extensions, as appropriate)
    """

    # get datatype and reference image for comparison
    dtype = get_dtype(ref_img)
    if isinstance(ref_img, list):
        ref_img = ref_img[0]

    # ensure that desired output type (from name) is compatible with `dtype`
    root, ext, add = splitext_addext(filename)
    if ext != '' and FORMATS[ext] != dtype:
        raise ValueError('Cannot write {} data to {} file. Please ensure file'
                         'formats are compatible'.format(dtype, FORMATS[ext]))

    if dtype == 'NIFTI':
        out = new_nii_like(ref_img, data, copy_header=copy_header)
        name = '{}.{}'.format(root, 'nii.gz' if gzip else 'nii')
        out.to_filename(name)

    return name
예제 #14
0
파일: io.py 프로젝트: schudds/tedana
def filewrite(data, filename, ref_img, gzip=True, copy_header=True):
    """
    Writes `data` to `filename` in format of `ref_img`

    Parameters
    ----------
    data : (S [x T]) array_like
        Data to be saved
    filename : :obj:`str`
        Filepath where data should be saved to
    ref_img : :obj:`str` or img_like
        Reference image
    gzip : :obj:`bool`, optional
        Whether to gzip output (if not specified in `filename`). Only applies
        if output dtype is NIFTI. Default: True
    copy_header : :obj:`bool`, optional
        Whether to copy header from `ref_img` to new image. Default: True

    Returns
    -------
    name : :obj:`str`
        Path of saved image (with added extensions, as appropriate)
    """

    # get reference image for comparison
    if isinstance(ref_img, list):
        ref_img = ref_img[0]

    # generate out file for saving
    out = new_nii_like(ref_img, data, copy_header=copy_header)

    # FIXME: we only handle writing to nifti right now
    # get root of desired output file and save as nifti image
    root = op.dirname(filename)
    base = op.basename(filename)
    base, ext, add = splitext_addext(base)
    root = op.join(root, base)
    name = '{}.{}'.format(root, 'nii.gz' if gzip else 'nii')
    out.to_filename(name)

    return name
예제 #15
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.])
        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)
예제 #16
0
def space_time_realign(
    input, tr, slice_order="descending", slice_dim=2, slice_dir=1, apply=True, make_figure=False, out_name=None
):
    """
    This is a scripting interface to `nipy.algorithms.registration.SpaceTimeRealign` 

    Parameters
    ----------
    input : str or list
        A full path to a file-name (4D nifti time-series) , or to a directory
        containing 4D nifti time-series, or a list of full-paths to files.
    tr : float
        The repetition time
    slice_order : str (optional)
        This is the order of slice-times in the acquisition. This is used as a
        key into the ``SLICETIME_FUNCTIONS`` dictionary from
        :mod:`nipy.algorithms.slicetiming.timefuncs`. Default: 'descending'.
    slice_dim : int (optional) 
        Denotes the axis in `images` that is the slice axis.  In a 4D image,
        this will often be axis = 2 (default).
    slice_dir : int (optional)
        1 if the slices were acquired slice 0 first (default), slice -1 last,
        or -1 if acquire slice -1 first, slice 0 last.
    apply : bool (optional)
        Whether to apply the transformation and produce an output. Default:
        True. 
    make_figure : bool (optional)
        Whether to generate a .png figure with the parameters across scans.
    out_name : bool (optional)
        Specify an output location (full path) for the files that are
        generated. Default: generate files in the path of the inputs (with an
        `_mc` suffix added to the file-names.

    Returns
    -------
    transforms : ndarray
        An (n_times_points,) shaped array containing 
       `nipy.algorithms.registration.affine.Rigid` class instances for each time
        point in the time-series. These can be used as affine transforms by
        referring to their `.as_affine` attribute.
    """
    if make_figure:
        if not HAVE_MPL:
            e_s = "You need to have matplotlib installed to run this function"
            e_s += " with `make_figure` set to `True`"
            raise RuntimeError(e_s)

    # If we got only a single file, we motion correct that one:
    if op.isfile(input):
        if not (input.endswith(".nii") or input.endswith(".nii.gz")):
            e_s = "Input needs to be a nifti file ('.nii' or '.nii.gz'"
            raise ValueError(e_s)
        fnames = [input]
        input = nib.load(input)
    # If this is a full-path to a directory containing files, it's still a
    # string:
    elif isinstance(input, str):
        list_of_files = os.listdir(input)
        fnames = [op.join(input, f) for f in np.sort(list_of_files) if (f.endswith(".nii") or f.endswith(".nii.gz"))]
        input = [nib.load(x) for x in fnames]
    # Assume that it's a list of full-paths to files:
    else:
        input = [nib.load(x) for x in input]

    slice_times = timefuncs[slice_order]
    slice_info = [slice_dim, slice_dir]

    reggy = SpaceTimeRealign(input, tr, slice_times, slice_info)

    reggy.estimate(align_runs=True)

    # We now have the transformation parameters in here:
    transforms = np.squeeze(np.array(reggy._transforms))
    rot = np.array([t.rotation for t in transforms])
    trans = np.array([t.translation for t in transforms])

    if apply:
        new_reggy = reggy.resample(align_runs=True)
        for run_idx, new_im in enumerate(new_reggy):
            # Fix output TR - it was probably lost in the image realign step
            assert new_im.affine.shape == (5, 5)
            new_im.affine[:] = new_im.affine.dot(np.diag([1, 1, 1, tr, 1]))
            # Save it out to a '.nii.gz' file:
            froot, ext, trail_ext = splitext_addext(fnames[run_idx])
            path, fname = op.split(froot)
            # We retain the file-name adding '_mc' regardless of where it's
            # saved
            new_path = path if out_name is None else out_name
            save_image(new_im, op.join(new_path, fname + "_mc.nii.gz"))

    if make_figure:
        # Delay MPL plotting import to latest moment to avoid errors trying
        # import the default MPL backend (such as tkinter, which may not be
        # installed). See: https://github.com/nipy/nipy/issues/414
        import matplotlib.pyplot as plt

        figure, ax = plt.subplots(2)
        figure.set_size_inches([8, 6])
        ax[0].plot(rot)
        ax[0].set_xlabel("Time (TR)")
        ax[0].set_ylabel("Translation (mm)")
        ax[1].plot(trans)
        ax[1].set_xlabel("Time (TR)")
        ax[1].set_ylabel("Rotation (radians)")
        figure.savefig(op.join(os.path.split(fnames[0])[0], "mc_params.png"))

    return transforms
예제 #17
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)
예제 #18
0
def space_time_realign(input,
                       tr,
                       slice_order='descending',
                       slice_dim=2,
                       slice_dir=1,
                       apply=True,
                       make_figure=False,
                       out_name=None):
    """
    This is a scripting interface to `nipy.algorithms.registration.SpaceTimeRealign` 

    Parameters
    ----------
    input : str or list
        A full path to a file-name (4D nifti time-series) , or to a directory
        containing 4D nifti time-series, or a list of full-paths to files.
    tr : float
        The repetition time
    slice_order : str (optional)
        This is the order of slice-times in the acquisition. This is used as a
        key into the ``SLICETIME_FUNCTIONS`` dictionary from
        :mod:`nipy.algorithms.slicetiming.timefuncs`. Default: 'descending'.
    slice_dim : int (optional) 
        Denotes the axis in `images` that is the slice axis.  In a 4D image,
        this will often be axis = 2 (default).
    slice_dir : int (optional)
        1 if the slices were acquired slice 0 first (default), slice -1 last,
        or -1 if acquire slice -1 first, slice 0 last.
    apply : bool (optional)
        Whether to apply the transformation and produce an output. Default:
        True. 
    make_figure : bool (optional)
        Whether to generate a .png figure with the parameters across scans.
    out_name : bool (optional)
        Specify an output location (full path) for the files that are
        generated. Default: generate files in the path of the inputs (with an
        `_mc` suffix added to the file-names.
    

    Returns
    -------
    transforms : ndarray
        An (n_times_points,) shaped array containing 
       `nipy.algorithms.registration.affine.Rigid` class instances for each time
        point in the time-series. These can be used as affine transforms by
        referring to their `.as_affine` attribute.
    """
    if make_figure:
        if not HAVE_MPL:
            e_s = "You need to have matplotlib installed to run this function"
            e_s += " with `make_figure` set to `True`"
            raise RuntimeError(e_s)

    # If we got only a single file, we motion correct that one:
    if op.isfile(input):
        if not (input.endswith('.nii') or input.endswith('.nii.gz')):
            e_s = "Input needs to be a nifti file ('.nii' or '.nii.gz'"
            raise ValueError(e_s)
        fnames = [input]
        input = nib.load(input)
    # If this is a full-path to a directory containing files, it's still a
    # string:
    elif isinstance(input, str):
        list_of_files = os.listdir(input)
        fnames = [
            op.join(input, f) for f in np.sort(list_of_files)
            if (f.endswith('.nii') or f.endswith('.nii.gz'))
        ]
        input = [nib.load(x) for x in fnames]
    # Assume that it's a list of full-paths to files:
    else:
        input = [nib.load(x) for x in input]

    slice_times = timefuncs[slice_order]
    slice_info = [slice_dim, slice_dir]

    reggy = SpaceTimeRealign(input, tr, slice_times, slice_info)

    reggy.estimate(align_runs=True)

    # We now have the transformation parameters in here:
    transforms = np.squeeze(np.array(reggy._transforms))
    rot = np.array([t.rotation for t in transforms])
    trans = np.array([t.translation for t in transforms])

    if apply:
        new_reggy = reggy.resample(align_runs=True)
        for run_idx, new_im in enumerate(new_reggy):
            # Fix output TR - it was probably lost in the image realign step
            assert new_im.affine.shape == (5, 5)
            new_im.affine[:] = new_im.affine.dot(np.diag([1, 1, 1, tr, 1]))
            # Save it out to a '.nii.gz' file:
            froot, ext, trail_ext = splitext_addext(fnames[run_idx])
            path, fname = op.split(froot)
            # We retain the file-name adding '_mc' regardless of where it's
            # saved
            new_path = path if out_name is None else out_name
            save_image(new_im, op.join(new_path, fname + '_mc.nii.gz'))

    if make_figure:
        figure, ax = plt.subplots(2)
        figure.set_size_inches([8, 6])
        ax[0].plot(rot)
        ax[0].set_xlabel('Time (TR)')
        ax[0].set_ylabel('Translation (mm)')
        ax[1].plot(trans)
        ax[1].set_xlabel('Time (TR)')
        ax[1].set_ylabel('Rotation (radians)')
        figure.savefig(op.join(os.path.split(fnames[0])[0], 'mc_params.png'))

    return transforms