コード例 #1
0
def isMelodicDir(path):
    """Returns ``True`` if the given path looks like it is a MELODIC directory,
    ``False`` otherwise. A MELODIC directory:

      - Must contain a file called ``melodic_IC.nii.gz`` or
        ``melodic_oIC.nii.gz``.
      - Must contain a file called ``melodic_mix``.
      - Must contain a file called ``melodic_FTmix``.
    """

    path = op.abspath(path)

    if not op.isdir(path):
        return False

    # Must contain an image file called
    # melodic_IC or melodic_oIC
    prefixes = ['melodic_IC', 'melodic_oIC']
    for p in prefixes:
        try:
            fslimage.addExt(op.join(path, p))
            break
        except fslimage.PathError:
            pass
    else:
        return False

    # Must contain files called
    # melodic_mix and melodic_FTmix
    if not op.exists(op.join(path, 'melodic_mix')): return False
    if not op.exists(op.join(path, 'melodic_FTmix')): return False

    return True
コード例 #2
0
def isMelodicDir(path):
    """Returns ``True`` if the given path looks like it is contained within
    a MELODIC directory, ``False`` otherwise. A melodic directory:

      - Must be named ``*.ica``.
      - Must contain a file called ``melodic_IC.nii.gz``.
      - Must contain a file called ``melodic_mix``.
      - Must contain a file called ``melodic_FTmix``.
    """

    path = op.abspath(path)

    if op.isdir(path): dirname = path
    else: dirname = op.dirname(path)

    sufs = ['.ica']

    if not any([dirname.endswith(suf) for suf in sufs]):
        return False

    # Must contain an image file called melodic_IC
    try:
        fslimage.addExt(op.join(dirname, 'melodic_IC'), mustExist=True)
    except fslimage.PathError:
        return False

    # Must contain files called
    # melodic_mix and melodic_FTmix
    if not op.exists(op.join(dirname, 'melodic_mix')): return False
    if not op.exists(op.join(dirname, 'melodic_FTmix')): return False

    return True
コード例 #3
0
def test_addExt():
    """Test the addExt function. """

    default = fslimage.defaultExt()
    testdir = tempfile.mkdtemp()

    toCreate = [
        'compressed.nii.gz', 'uncompressed.nii', 'img_hdr_pair.img',
        'compressed_img_hdr_pair.img.gz', 'ambiguous.nii', 'ambiguous.nii.gz',
        'ambiguous.img', 'ambiguous.img.gz'
    ]

    # (file, mustExist, expected)
    tests = [('blah', False, 'blah{}'.format(default)),
             ('blah.nii', False, 'blah.nii'),
             ('blah.nii.gz', False, 'blah.nii.gz'),
             ('blah.img', False, 'blah.img'), ('blah.hdr', False, 'blah.hdr'),
             ('blah.img.gz', False, 'blah.img.gz'),
             ('blah.hdr.gz', False, 'blah.hdr.gz'),
             ('compressed', True, 'compressed.nii.gz'),
             ('compressed.nii.gz', True, 'compressed.nii.gz'),
             ('uncompressed', True, 'uncompressed.nii'),
             ('uncompressed.nii', True, 'uncompressed.nii'),
             ('img_hdr_pair', True, 'img_hdr_pair.hdr'),
             ('img_hdr_pair.hdr', True, 'img_hdr_pair.hdr'),
             ('img_hdr_pair.img', True, 'img_hdr_pair.img'),
             ('compressed_img_hdr_pair', True,
              'compressed_img_hdr_pair.hdr.gz'),
             ('compressed_img_hdr_pair.img.gz', True,
              'compressed_img_hdr_pair.img.gz'),
             ('compressed_img_hdr_pair.hdr.gz', True,
              'compressed_img_hdr_pair.hdr.gz'),
             ('ambiguous.nii', True, 'ambiguous.nii'),
             ('ambiguous.nii.gz', True, 'ambiguous.nii.gz'),
             ('ambiguous.img', True, 'ambiguous.img'),
             ('ambiguous.hdr', True, 'ambiguous.hdr'),
             ('ambiguous.img.gz', True, 'ambiguous.img.gz'),
             ('ambiguous.hdr.gz', True, 'ambiguous.hdr.gz')]

    for path in toCreate:
        path = op.abspath(op.join(testdir, path))
        make_random_image(path)

    try:
        for path, mustExist, expected in tests:

            path = op.abspath(op.join(testdir, path))
            expected = op.abspath(op.join(testdir, expected))

            assert fslimage.addExt(path, mustExist) == expected

        # Make sure that an ambiguous path fails
        with pytest.raises(fslimage.PathError):
            path = op.join(testdir, 'ambiguous')
            fslimage.addExt(path, mustExist=True)
    finally:
        shutil.rmtree(testdir)
コード例 #4
0
def ensureIsImage(img):
    """Ensures that the given ``img`` is an in-memory ``nibabel`` object.
    """
    if isinstance(img, six.string_types):
        img = fslimage.addExt(img)
        img = nib.load(img)
    return img
コード例 #5
0
def getDataFile(meldir):
    """If the given melodic directory is contained within another analysis
    directory, the path to the data file is returned. Otherwise ``None`` is
    returned.
    """

    topDir = getTopLevelAnalysisDir(meldir)

    if topDir is None:
        return None

    # People often rename filtered_func_data.nii.gz
    # to something like filtered_func_data_clean.nii.gz,
    # because that is the recommended approach when
    # performing ICA-based denoising). So we try both.
    candidates = ['filtered_func_data', 'filtered_func_data_clean']

    for candidate in candidates:
        dataFile = op.join(topDir, candidate)
        try:
            return fslimage.addExt(dataFile)
        except fslimage.PathError:
            continue

    return None
コード例 #6
0
ファイル: ensure.py プロジェクト: pauldmccarthy/fslpy
def ensureIsImage(img):
    """Ensures that the given ``img`` is an in-memory ``nibabel`` object.
    """
    if isinstance(img, (str, pathlib.Path)):
        img = fslimage.addExt(img)
        img = nib.load(img)
    return img
コード例 #7
0
ファイル: vtk.py プロジェクト: physimals/fslpy
def findReferenceImage(modelfile):
    """Given a ``vtk`` file, attempts to find a corresponding ``NIFTI``
    image file. Return the path to the image, or ``None`` if no image was
    found.

    Currently this function will only return an image for ``vtk`` files
    generated by `FIRST <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FIRST>`_.
    """

    try:

        dirname = op.dirname(modelfile)
        prefixes = [getFIRSTPrefix(modelfile)]
    except ValueError:
        return None

    if prefixes[0].endswith('_first'):
        prefixes.append(prefixes[0][:-6])

    for p in prefixes:
        try:
            return fslimage.addExt(op.join(dirname, p), mustExist=True)
        except fslimage.PathError:
            continue

    return None
コード例 #8
0
ファイル: test_immv_imcp.py プロジェクト: physimals/fslpy
def checkFilesToExpect(files, outdir, outputType, datahashes):

    exts = {
        'NIFTI'      : ['.nii'],
        'NIFTI_PAIR' : ['.hdr', '.img'],
        'NIFTI_GZ'   : ['.nii.gz'],
        ''           : ['.nii.gz'],
    }.get(outputType, None)

    allFiles = []

    if isinstance(files, str):
        files = files.split()

    for f in files:

        f, fe = fslimage.splitExt(f)
        fexts = exts

        if fexts is None:
            fexts = {
                '.img'    : ['.hdr', '.img'],
                '.hdr'    : ['.hdr', '.img'],
                '.nii'    : ['.nii'],
                '.nii.gz' : ['.nii.gz']
            }.get(fe, [])
        # filename already has a different extension
        elif fe != '' and fe not in fexts:
            fexts = [fe]

        for e in fexts:

            expected = op.join(outdir, f + e)

            allFiles.append(expected)

            print('  ', expected)

            assert op.exists(expected)

    allThatExist = os.listdir(outdir)
    allThatExist = [f for f in allThatExist if op.isfile(op.join(outdir, f))]

    assert len(allThatExist) == len(allFiles)

    for i, f in enumerate(files):
        f = fslimage.addExt(op.join(outdir, f), mustExist=True)

        if isinstance(datahashes, list):
            if len(datahashes) > len(files):
                diff = len(datahashes) - len(files)
                h    = datahashes[i + diff]

            else:
                h = datahashes[i]
        else:
            h = datahashes[op.basename(f)]

        checkImageHash(f, h)
コード例 #9
0
ファイル: parse_data.py プロジェクト: physimals/fslpy
def ImageOut(basename):
    """
    Uses the FSL convention to create a complete image output filename

    :param basename: filename provided by the user
    :return: filename with extension
    """
    return image.addExt(basename, mustExist=False)
コード例 #10
0
def guessDataSourceType(path):
    """A convenience function which, given the name of a file or directory,
    figures out a suitable overlay type.

    Returns a tuple containing two values - a type which should be able to
    load the path, and the path itself, possibly adjusted. If the type
    is unrecognised, the first tuple value will be ``None``.
    """

    import fsl.data.mesh as fslmesh
    import fsl.data.gifti as fslgifti
    import fsl.data.featimage as featimage
    import fsl.data.melodicimage as melimage
    import fsl.data.dtifit as dtifit
    import fsl.data.melodicanalysis as melanalysis
    import fsl.data.featanalysis as featanalysis

    # Support files opened via fsleyes:// URL
    if path.startswith('fsleyes://'):
        path = path[10:]

    path = op.abspath(path)

    # VTK files are easy
    if path.endswith('.vtk'):
        return fslmesh.TriangleMesh, path

    # So are GIFTIS
    if path.endswith('.gii'):
        return fslgifti.GiftiSurface, path

    # Analysis directory?
    if op.isdir(path):
        if melanalysis.isMelodicDir(path):
            return melimage.MelodicImage, path

        elif featanalysis.isFEATDir(path):
            return featimage.FEATImage, path

        elif dtifit.isDTIFitPath(path):
            return dtifit.DTIFitTensor, path

    # Assume it's a NIFTI image
    try:
        path = fslimage.addExt(path, mustExist=True)
    except fslimage.PathError:
        return None, path

    if melanalysis.isMelodicImage(path): return melimage.MelodicImage, path
    elif featanalysis.isFEATImage(path): return featimage.FEATImage, path
    else: return fslimage.Image, path

    # Otherwise, I don't
    # know what to do
    return None, path
コード例 #11
0
    def getfmt(arg, fname, exist):
        ext = op.splitext(fname)[1]
        if ext in ('.mat', '.x5'):
            return ext[1:]

        fname = fslimage.addExt(fname, mustExist=exist)

        if fslimage.looksLikeImage(fname):
            return 'nii'
        parser.error('Could not infer format from '
                     'filename: {}'.format(args.input))
コード例 #12
0
def imglob(paths, output=None):
    """Given a list of file names, identifies and returns the unique
    NIFTI/ANALYZE image files that exist.

    :arg paths:  Sequence of paths/prefixes to glob.

    :arg output: One of ``'prefix'`` (the default), ``'all'``, or
                 ``'primary'``:

                  - ``'prefix'``:  Returns the files without extensions.
                  - ``'all'``:     Returns all files that match (e.g. both
                                   ``.img`` and ``.hdr`` files will be
                                   returned).
                  - ``'primary'``: Returns only the primary file of each
                                   matching file group, e.g. only the
                                   ``.hdr`` file would be returned from
                                   an ``.img``/``.hdr`` pair.

    :returns: A sequence of resolved path names, in the form specified
              by the ``output`` parameter.
    """

    if output is None:
        output = 'prefix'

    if output not in ('prefix', 'all', 'primary'):
        raise ValueError('Unsupported output format: {}'.format(output))

    imgfiles = []

    # Build a list of all image files (both
    # hdr and img and otherwise) that match
    for path in paths:
        try:
            path = fslimage.removeExt(path)
            imgfiles.extend(fslimage.addExt(path, unambiguous=False))
        except fslpath.PathError:
            continue

    if output == 'prefix':
        imgfiles = fslpath.removeDuplicates(imgfiles,
                                            allowedExts=exts,
                                            fileGroups=groups)
        imgfiles = [fslpath.removeExt(f, exts) for f in imgfiles]

    elif output == 'primary':
        imgfiles = fslpath.removeDuplicates(imgfiles,
                                            allowedExts=exts,
                                            fileGroups=groups)

    return list(sorted(set(imgfiles)))
コード例 #13
0
def isMelodicImage(path):
    """Returns ``True`` if the given path looks like it is a melodic
    component image file, ``False`` otherwise.
    """

    try:
        path = fslimage.addExt(path, mustExist=True)
    except fslimage.PathError:
        return False

    dirname = op.dirname(path)
    filename = op.basename(path)

    return filename.startswith('melodic_IC') and isMelodicDir(dirname)
コード例 #14
0
def flipdir(dirname):
    newdir = '{}_flipdir'.format(dirname.strip(op.sep))

    if op.exists(newdir):
        return newdir

    os.mkdir(newdir)
    for f in os.listdir(dirname):
        if not op.exists(op.join(dirname, f)):
            continue
        flipped = fliporient(op.join(dirname, f))
        flipped = fslimage.addExt(flipped)
        shutil.move(flipped, op.join(newdir, f))

    return newdir
コード例 #15
0
ファイル: parse_data.py プロジェクト: physimals/fslpy
def Image(filename, *args, **kwargs):
    """
    Reads in an image from a NIFTI or Analyze file.

    :arg filename: filename provided by the user
    :return: fsl.data.image.Image object

    All other arguments are passed through to the :class:`.Image` upon
    creation.
    """
    try:
        full_filename = image.addExt(filename)
    except path.PathError as e:
        raise argparse.ArgumentTypeError(*e.args)
    return image.Image(full_filename, *args, **kwargs)
コード例 #16
0
def getDataFile(meldir):
    """If the given melodic directory is contained within another analysis
    directory, the path to the data file is returned. Otherwise ``None`` is
    returned.
    """

    topDir = getTopLevelAnalysisDir(meldir)

    if topDir is None:
        return None

    dataFile = op.join(topDir, 'filtered_func_data')

    try:                       return fslimage.addExt(dataFile)
    except fslimage.PathError: return None
コード例 #17
0
    def asrt(path, cls):
        restype, respath = dutils.guessType(path)

        assert restype is cls

        if path.startswith('fsleyes://'):
            path = path[10:]

        # image path might not have an extension

        try:
            path = fslimage.addExt(path, mustExist=True)
        except fslimage.PathError:
            pass

        assert respath == op.abspath(path)
コード例 #18
0
ファイル: test_mghimage.py プロジェクト: physimals/fslpy
def test_MGHImage_save():

    testfile = op.join(datadir, 'example.mgz')

    with tempdir.tempdir():

        shutil.copy(testfile, 'example.mgz')

        testfile = 'example.mgz'

        img = fslmgh.MGHImage(testfile)

        img.save()

        expfile = op.abspath(fslimage.addExt('example', mustExist=False))

        assert img.dataSource == op.abspath(expfile)
コード例 #19
0
def isMelodicImage(path):
    """Returns ``True`` if the given path looks like it is a melodic
    component image file, ``False`` otherwise.
    """

    try:
        path = fslimage.addExt(path)
    except fslimage.PathError:
        return False

    dirname = op.dirname(path)
    filename = op.basename(path)
    filename = fslimage.removeExt(filename)

    prefixes = ['melodic_IC', 'melodic_oIC']

    return any([filename == p for p in prefixes]) and isMelodicDir(dirname)
コード例 #20
0
def run(job_id: int, njobs: int, mask_fn: str, command: Sequence[str]):
    """
    Runs part of the script

    :param job_id: job ID
    :param njobs: number of jobs
    :param mask_fn: mask filename
    :param command: script to run
    """
    logger.debug(f'loading original mask from {mask_fn}')
    mask_img = nib.load(addExt(mask_fn, mustExist=True, unambiguous=True))
    mask = mask_img.get_data() > 0
    voxels = np.where(mask)
    nvox = voxels[0].size
    boundaries = np.round(np.linspace(0, nvox, njobs + 1)).astype('int')

    use = tuple(vox[boundaries[job_id]:boundaries[job_id + 1]]
                for vox in voxels)
    logger.debug(
        f'creating new mask covering voxels {boundaries[job_id]} to ' +
        f'{boundaries[job_id + 1]} out of {nvox} voxels')
    mask[()] = False
    mask[use] = True
    marker = get_markers(njobs)[job_id]
    with tempfile.NamedTemporaryFile(prefix='mask' + marker,
                                     suffix='.nii.gz') as temp_mask:
        logger.debug(f'Storing new mask under {temp_mask}')
        nib.Nifti1Image(mask.astype('i4'), affine=None,
                        header=mask_img.header).to_filename(temp_mask.name)

        if not any('MASK' in part for part in command):
            raise ValueError('MASK not found')
        new_cmd = [
            part.replace('MASK', temp_mask.name).replace('JOBID', marker)
            for part in command
        ]
        logger.info(f'Running {new_cmd}')
        srun(new_cmd, check=True)
コード例 #21
0
def guessFlirtFiles(path):
    """Given a ``path`` to a NIFTI image file, tries to guess an appropriate
    FLIRT transformation matrix file and reference image. The guess is based
    on the path location (e.g. if it is a FEAT or MELODIC image).

    Returns a tuple containing paths to the matrix file and reference image,
    or ``(None, None)`` if a guess couldn't be made.
    """

    import fsl.data.featanalysis as featanalysis
    import fsl.data.melodicanalysis as melodicanalysis

    if path is None:
        return None, None

    filename = op.basename(path)
    dirname = op.dirname(path)
    regDir = None
    srcRefMap = {}

    func2struc = 'example_func2highres.mat'
    struc2std = 'highres2standard.mat'

    featDir = featanalysis.getAnalysisDir(dirname)
    melDir = melodicanalysis.getAnalysisDir(dirname)

    # TODO more heuristics
    if featDir is not None:

        regDir = op.join(featDir, 'reg')
        srcRefMap = {
            'example_func': ('highres', func2struc),
            'filtered_func_data': ('highres', func2struc),
            'mean_func': ('highres', func2struc),
            'thresh_zstat': ('highres', func2struc),
            op.join('stats', 'zstat'): ('highres', func2struc),
            op.join('stats', 'tstat'): ('highres', func2struc),
            op.join('stats', 'cope'): ('highres', func2struc),
            op.join('stats', 'pe'): ('highres', func2struc),
            'highres': ('standard', struc2std),
            'highres_head': ('standard', struc2std),
            'struc': ('standard', struc2std),
            'struct': ('standard', struc2std),
            'struct_brain': ('standard', struc2std),
            'struc_brain': ('standard', struc2std),
        }

    elif melodicanalysis.isMelodicDir(dirname):

        if melDir.startswith('filtered_func_data'):
            regDir = op.join(melDir, '..', 'reg')
        else:
            regDir = op.join(melDir, 'reg')

        srcRefMap = {
            'filtered_func_data': ('highres', func2struc),
            'melodic_IC': ('highres', func2struc),
            'example_func': ('highres', func2struc),
            'mean_func': ('highres', func2struc),
            'highres': ('standard', struc2std),
            'highres_head': ('standard', struc2std),
            'struc': ('standard', struc2std),
            'struct': ('standard', struc2std),
            'struct_brain': ('standard', struc2std),
            'struc_brain': ('standard', struc2std),
        }

    matFile = None
    refFile = None

    for src, (ref, mat) in srcRefMap.items():

        if not filename.startswith(src):
            continue

        mat = op.join(regDir, mat)
        ref = op.join(regDir, ref)

        try:
            ref = fslimage.addExt(ref)
        except Exception:
            continue

        if op.exists(mat):
            matFile = mat
            refFile = ref
            break

    return matFile, refFile
コード例 #22
0
def getICFile(meldir):
    """Returns the path to the melodic IC image. """
    try:
        return fslimage.addExt(op.join(meldir, 'melodic_IC'))
    except fslimage.PathError:
        return fslimage.addExt(op.join(meldir, 'melodic_oIC'))
コード例 #23
0
def getMeanFile(meldir):
    """Return a path to the mean image of the meloidic input data. """
    return fslimage.addExt(op.join(meldir, 'mean'))
コード例 #24
0
def getICFile(meldir):
    """Returns the path to the melodic IC image. """
    return fslimage.addExt(op.join(meldir, 'melodic_IC'))
コード例 #25
0
ファイル: overlay.py プロジェクト: sanjayankur31/fsleyes
def guessDataSourceType(path):
    """A convenience function which, given the name of a file or directory,
    figures out a suitable overlay type.

    Returns a tuple containing two values - a type which should be able to
    load the path, and the path itself, possibly adjusted. If the type
    is unrecognised, the first tuple value will be ``None``.
    """

    import fsl.data.vtk as fslvtk
    import fsl.data.gifti as fslgifti
    import fsl.data.freesurfer as fslfs
    import fsl.data.mghimage as fslmgh
    import fsl.data.featimage as featimage
    import fsl.data.melodicimage as melimage
    import fsl.data.dtifit as dtifit
    import fsl.data.melodicanalysis as melanalysis
    import fsl.data.featanalysis as featanalysis

    # Support files opened via fsleyes:// URL
    if path.startswith('fsleyes://'):
        path = path[10:]

    path = op.abspath(path)

    # Accept images sans-extension
    try:
        path = fslimage.addExt(path, mustExist=True)
    except fslimage.PathError:
        pass

    if op.isfile(path):

        # Some types are easy - just check the extensions
        if fslpath.hasExt(path, fslvtk.ALLOWED_EXTENSIONS):
            return fslvtk.VTKMesh, path
        elif fslpath.hasExt(path, fslgifti.ALLOWED_EXTENSIONS):
            return fslgifti.GiftiMesh, path
        elif fslfs.isGeometryFile(path):
            return fslfs.FreesurferMesh, path
        elif fslpath.hasExt(path, fslmgh.ALLOWED_EXTENSIONS):
            return fslmgh.MGHImage, path

        # Other specialised image types
        elif melanalysis.isMelodicImage(path):
            return melimage.MelodicImage, path
        elif featanalysis.isFEATImage(path):
            return featimage.FEATImage, path
        elif fslimage.looksLikeImage(path):
            return fslimage.Image, path

    # Analysis directory?
    elif op.isdir(path):
        if melanalysis.isMelodicDir(path):
            return melimage.MelodicImage, path
        elif featanalysis.isFEATDir(path):
            return featimage.FEATImage, path
        elif dtifit.isDTIFitPath(path):
            return dtifit.DTIFitTensor, path

    # Otherwise, I don't
    # know what to do
    return None, path
コード例 #26
0
ファイル: imcp.py プロジェクト: neurodebian/fslpy
def imcp(src,
         dest,
         overwrite=False,
         useDefaultExt=False,
         move=False):
    """Copy the given ``src`` file to destination ``dest``.

    A :class:`.fsl.utils.path.PathError` is raised if anything goes wrong.

    :arg src:           Path to copy. If ``allowedExts`` is provided,
                        the file extension can be omitted.

    :arg dest:          Destination path. Can be an incomplete file
                        specification (i.e. without the extension), or a
                        directory.

    :arg overwrite:     If ``True`` this function will overwrite files that
                        already exist. Defaults to ``False``.

    :arg useDefaultExt: Defaults to ``False``. If ``True``, the destination
                        file type will be set according to the default
                        extension, specified by
                        :func:`~fsl.data.image.defaultExt`. If the source
                        file does not have the same type as the default
                        extension, it will be converted. If ``False``, the
                        source file type is not changed.

    :arg move:          If ``True``, the files are moved, instead of being
                        copied. See :func:`immv`.
    """

    import nibabel as nib

    if op.isdir(dest):
        dest = op.join(dest, op.basename(src))

    srcBase,  srcExt  = fslimage.splitExt(src)
    destBase, destExt = fslimage.splitExt(dest)

    # src was specified without an
    # extension, or the specified
    # src does not have an allowed
    # extension.
    if srcExt == '':

        # Try to resolve the specified src
        # path - if src does not exist, or
        # does not have an allowed extension,
        # addExt will raise an error
        src = fslimage.addExt(src, mustExist=True)

        # We've resolved src to a
        # full filename - split it
        # again to get its extension
        srcBase, srcExt = fslimage.splitExt(src)

    if not op.exists(src):
        raise fslpath.PathError('imcp error - source path '
                                'does not exist: {}'.format(src))

    # Figure out the destination file
    # extension/type. If useDefaultExt
    # is True, we use the default
    # extension. Otherwise, if no
    # destination file extension is
    # provided, we use the source
    # extension.
    if   useDefaultExt: destExt = fslimage.defaultExt()
    elif destExt == '': destExt = srcExt

    # Resolve any file group differences
    # e.g. we don't care if the src is
    # specified as file.hdr, and the dest
    # is specified as file.img - if src
    # and dest are part of the same file
    # group, we replace the dest extension
    # with the src extension.
    if srcExt != destExt:
        for group in fslimage.FILE_GROUPS:
            if srcExt in group and destExt in group:
                destExt = srcExt
                break

    dest = destBase + destExt

    # Give up if we don't have permission.
    if          not os.access(op.dirname(dest), os.W_OK | os.X_OK):
        raise fslpath.PathError('imcp error - cannot write to {}'.format(dest))

    if move and not os.access(op.dirname(src),  os.W_OK | os.X_OK):
        raise fslpath.PathError('imcp error - cannot move from {}'.format(src))

    # If the source file type does not
    # match the destination file type,
    # we need to perform a conversion.
    #
    # This is more expensive in terms of
    # io and cpu, but programmatically
    # very easy - nibabel does all the
    # hard work.
    if srcExt != destExt:

        if not overwrite and op.exists(dest):
            raise fslpath.PathError('imcp error - destination already '
                                    'exists ({})'.format(dest))

        img = nib.load(src)
        nib.save(img, dest)

        if move:
            os.remove(src)

        return

    # Otherwise we do a file copy. This
    # is actually more complicated than
    # converting the file type due to
    # hdr/img pairs ...
    #
    # If the source is part of a file group,
    # e.g. src.img/src.hdr), we need to copy
    # the whole set of files. So here we
    # build a list of source files that need
    # to be copied/moved. The getFileGroup
    # function returns all other files that
    # are associated with this file (i.e.
    # part of the same group).
    #
    # We store the sources as separate
    # (base, ext) tuples, so we don't
    # have to re-split when creating
    # destination paths.
    #
    # The unambiguous flag tells getFileGroup
    # to raise an error if the source appears
    # to be part of an incopmlete file group
    # (e.g. file.hdr without an accompanying
    # file.img).
    copySrcs = fslpath.getFileGroup(src,
                                    fslimage.ALLOWED_EXTENSIONS,
                                    fslimage.FILE_GROUPS,
                                    fullPaths=False,
                                    unambiguous=True)
    copySrcs = [(srcBase, e) for e in copySrcs]

    # Build a list of destinations for each
    # copy source - we build this list in
    # advance, so we can fail if any of the
    # destinations already exist. We also
    # re-combine the source bases/extensions.
    copyDests = [destBase + e for (b, e) in copySrcs]
    copySrcs  = [b        + e for (b, e) in copySrcs]

    # Fail if any of the destination
    # paths already exist
    if not overwrite and any([op.exists(d) for d in copyDests]):
        raise fslpath.PathError('imcp error - a destination path already '
                                'exists ({})'.format(', '.join(copyDests)))

    # Do the copy/move
    for src, dest in zip(copySrcs, copyDests):
        if move: shutil.move(src, dest)
        else:    shutil.copy(src, dest)