Commit 5bf216bc authored by Paul McCarthy's avatar Paul McCarthy
Browse files

imcp/immv moved into their own module. They now have an option to honour

the default file extensions (i.e. the FSLOUTPUTTYPE) - this is used by
the fslpy_immv/imcp command line scripts.
parent 4ea48533
......@@ -11,20 +11,8 @@ from __future__ import print_function
import os.path as op
import sys
import fsl.utils.path as fslpath
SUPPORTED_EXTENSIONS = [
'.nii', '.nii.gz',
'.hdr', '.hdr.gz',
'.img', '.img.gz',
'.mnc', '.mnc.gz'
]
FILE_GROUPS = [
('.img', '.hdr'),
('.img.gz', '.hdr.gz')
]
import fsl.utils.imcp as imcp
import fsl.data.image as fslimage
usage = """Usage:
......@@ -37,35 +25,32 @@ Copy images from <file1> to <file2>, or copy all <file>s to <directory>
NB: filenames can be basenames or include an extension.
Recognised file extensions: {}
""".format(', '.join(SUPPORTED_EXTENSIONS))
""".format(', '.join(fslimage.ALLOWED_EXTENSIONS))
def main():
def main(argv=None):
"""Parses CLI arguments (see the usage string), and calls the
fsl.utils.path.imcp function on each input.
:func:`fsl.utils.imcp.imcp` function on each input.
"""
if len(sys.argv) < 2:
if len(argv) < 2:
print(usage)
sys.exit(1)
srcs = sys.argv[1:-1]
dest = sys.argv[ -1]
srcs = argv[:-1]
dest = argv[ -1]
if len(srcs) > 1 and not op.isdir(dest):
print(usage)
sys.exit(1)
srcs = fslpath.removeDuplicates(srcs,
allowedExts=SUPPORTED_EXTENSIONS,
fileGroups=FILE_GROUPS)
allowedExts=fslimage.ALLOWED_EXTENSIONS,
fileGroups=fslimage.FILE_GROUPS)
for src in srcs:
try:
fslpath.imcp(src,
dest,
allowedExts=SUPPORTED_EXTENSIONS,
fileGroups=FILE_GROUPS)
imcp.imcp(src, dest, useDefaultExt=True)
except Exception as e:
print(e)
......
......@@ -11,21 +11,8 @@ from __future__ import print_function
import os.path as op
import sys
import fsl.utils.path as fslpath
SUPPORTED_EXTENSIONS = [
'.nii', '.nii.gz',
'.hdr', '.hdr.gz',
'.img', '.img.gz',
'.mnc', '.mnc.gz'
]
FILE_GROUPS = [
('.img', '.hdr'),
('.img.gz', '.hdr.gz')
]
import fsl.utils.imcp as imcp
import fsl.data.image as fslimage
usage = """Usage:
......@@ -38,35 +25,35 @@ Moves images from <file1> to <file2>, or move all <file>s to <directory>
NB: filenames can be basenames or include an extension.
Recognised file extensions: {}
""".format(', '.join(SUPPORTED_EXTENSIONS))
""".format(', '.join(fslimage.ALLOWED_EXTENSIONS))
def main():
def main(argv=None):
"""Parses CLI arguments (see the usage string), and calls the
fsl.utils.path.immv function on each input.
"""
fsl.utils.imcp.immv function on each input.
"""
if argv is None:
argv = sys.argv[1:]
if len(sys.argv) < 2:
if len(argv) < 2:
print(usage)
sys.exit(1)
srcs = sys.argv[1:-1]
dest = sys.argv[ -1]
srcs = argv[:-1]
dest = argv[ -1]
if len(srcs) > 1 and not op.isdir(dest):
print(usage)
sys.exit(1)
srcs = fslpath.removeDuplicates(srcs,
allowedExts=SUPPORTED_EXTENSIONS,
fileGroups=FILE_GROUPS)
allowedExts=fslimage.ALLOWED_EXTENSIONS,
fileGroups=fslimage.FILE_GROUPS)
for src in srcs:
try:
fslpath.immv(src,
dest,
allowedExts=SUPPORTED_EXTENSIONS,
fileGroups=FILE_GROUPS)
imcp.immv(src, dest, useDefaultExt=True, move=True)
except Exception as e:
print(e)
......
......@@ -25,9 +25,10 @@ and file names:
:nosignatures:
looksLikeImage
removeExt
addExt
splitExt
addExt
getExt
removeExt
defaultExt
loadIndexedImageFile
"""
......@@ -789,36 +790,36 @@ def looksLikeImage(filename, allowedExts=None):
return any([filename.endswith(ext) for ext in allowedExts])
def removeExt(filename):
"""Removes the extension from the given file name. Returns the filename
unmodified if it does not have a supported extension.
See :func:`~fsl.utils.path.removeExt`.
:arg filename: The file name to strip.
def addExt(prefix, mustExist=True):
"""Adds a file extension to the given file ``prefix``. See
:func:`~fsl.utils.path.addExt`.
"""
return fslpath.removeExt(filename, ALLOWED_EXTENSIONS)
return fslpath.addExt(prefix,
ALLOWED_EXTENSIONS,
mustExist,
defaultExt(),
fileGroups=FILE_GROUPS)
def splitExt(filename):
"""Splits the base name and extension for the given ``filename``.
See :func:`~fsl.utils.path.splitExt`.
"""Splits the base name and extension for the given ``filename``. See
:func:`~fsl.utils.path.splitExt`.
"""
return fslpath.splitExt(filename, ALLOWED_EXTENSIONS)
def addExt(prefix, mustExist=True):
"""Adds a file extension to the given file ``prefix``.
def getExt(filename):
"""Gets the extension for the given file name. See
:func:`~fsl.utils.path.getExt`.
"""
return fslpath.getExt(filename, ALLOWED_EXTENSIONS)
See :func:`~fsl.utils.path.addExt`.
def removeExt(filename):
"""Removes the extension from the given file name. See
:func:`~fsl.utils.path.removeExt`.
"""
return fslpath.addExt(prefix,
ALLOWED_EXTENSIONS,
mustExist,
defaultExt(),
fileGroups=FILE_GROUPS)
return fslpath.removeExt(filename, ALLOWED_EXTENSIONS)
def defaultExt():
......
#!/usr/bin/env python
#
# imcp.py - Functions for moving/copying NIFTI image files.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""
.. autosummary::
:nosignatures:
imcp
immv
"""
import os
import os.path as op
import shutil
import fsl.utils.path as fslpath
import fsl.data.image as fslimage
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`.
: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)
# 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 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
# conveting 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.
copySrcs = fslpath.getFileGroup(src,
fslimage.ALLOWED_EXTENSIONS,
fslimage.FILE_GROUPS,
fullPaths=False)
copySrcs = [(srcBase, e) for e in copySrcs]
# Note that these additional files
# do not have to exist, e.g.
# imcp('blah.img', ...) will still
# work if there is no blah.hdr
copySrcs = [(b, e) for (b, e) in copySrcs if op.exists(b + e)]
# 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)
def immv(src,
dest,
overwrite=False,
useDefaultExt=True):
"""Move the specified ``src`` to the specified ``dest``. See :func:`imcp`.
"""
imcp(src,
dest,
overwrite=overwrite,
useDefaultExt=useDefaultExt,
move=True)
......@@ -18,13 +18,10 @@ paths.
getExt
splitExt
getFileGroup
imcp
immv
"""
import os.path as op
import shutil
class PathError(Exception):
......@@ -231,23 +228,23 @@ def removeDuplicates(paths, allowedExts=None, fileGroups=None):
001.img
002.hdr
002.img
003.img
003.hdr
003.img
And you call ``removeDuplicates`` like so::
paths = ['001.img', '001.hdr',
'002.img', '002.hdr',
'003.img', '003.hdr']
paths = ['001.img', '001.hdr',
'002.img', '002.hdr',
'003.img', '003.hdr']
allowedExts = ['.img', '.hdr']
fileGroups = [('.img', '.hdr')]
allowedExts = ['.img', '.hdr']
fileGroups = [('.img', '.hdr')]
removeDuplicates(paths, allowedExts, fileGroups)
The returned list will be::
['001.img', '003.img', '003.img']
['001.img', '002.img', '003.img']
:arg paths: List of paths to reduce.
......@@ -290,16 +287,17 @@ def getFileGroup(path, allowedExts=None, fileGroups=None, fullPaths=True):
return ``file.img`` (i.e. the file which matches the first extension in
the group).
Similarly, if you call the :func:`imcp` or :func:`immv` functions with the
above parameters, both ``file.img`` and ``file.hdr`` will be moved.
Similarly, if you call the :func:`.imcp.imcp` or :func:`.imcp.immv`
functions with the above parameters, both ``file.img`` and ``file.hdr``
will be moved.
.. note:: The primary use-case of file groups is to resolve ambiguity with
respect to NIFTI and ANALYSE75 image pairs. By specifying
``fileGroups=[('.img', '.hdr'), ('.img.gz', '.hdr.gz')]``, the
:func:`addExt`, :func:`immv` and :func:`imcp` functions are able
to figure out what you mean when you specify ``file``, and both
``file.hdr`` and ``file.img`` (or ``file.hdr.gz`` and
``file.img.gz``) exist.
:func:`addExt`, :func:`.imcp.immv` and :func:`.imcp.imcp`
functions are able to figure out what you mean when you specify
``file``, and both ``file.hdr`` and ``file.img`` (or
``file.hdr.gz`` and ``file.img.gz``) exist.
:arg path: Path to the file. Must contain the file extension.
......@@ -342,116 +340,3 @@ def getFileGroup(path, allowedExts=None, fileGroups=None, fullPaths=True):
else:
if fullPaths: return matchedGroupFiles[0]
else: return matchedGroups[ 0]
def imcp(src,
dest,
allowedExts=None,
fileGroups=None,
overwrite=False,
move=False):
"""Copy the given ``src`` file to destination ``dest``.
: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 allowedExts: Allowed/recognised file extensions.
:arg fileGroups: Recognised file groups - see the :func:`getFileGroup`
documentation.
:arg overwrite: If ``True`` this function will overwrite files that
already exist. Defaults to ``False``.
:arg move: If ``True``, the files are moved, instead of being
copied.
"""
base, ext = splitExt(src, allowedExts)
destIsDir = op.isdir(dest)
# If dest has been specified
# as a file name, we don't
# care about its extension.
if not destIsDir:
dest = removeExt(dest, allowedExts)
# src was specified without an
# extension, or the specitifed
# src does not have an allowed
# extension.
if ext == '':
# 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 = addExt(src,
allowedExts,
mustExist=True,
fileGroups=fileGroups)
# We've resolved src to a
# full filename - split it
# again to get its extension
base, ext = splitExt(src, allowedExts)
# If the source is part of a file group,
# e.g. src.img/src.hdr, we want 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.
copySrcs = getFileGroup(src, allowedExts, fileGroups, fullPaths=False)
copySrcs = [(base, e) for e in copySrcs]
# Note that these additional files
# do not have to exist, e.g.
# imcp('blah.img', ...) will still
# work if there is no blah.hdr
copySrcs = [(b, e) for (b, e) in copySrcs if op.exists(b + e)]
# 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.
copyDests = []
for i, (base, ext) in enumerate(copySrcs):
# We'll also take this opportunity
# to re-combine the source paths
copySrcs[i] = base + ext
basename = op.basename(base)
if destIsDir: copyDests.append(op.join(dest, basename + ext))
else: copyDests.append(dest + ext)
# Fail if any of the destination
# paths already exist
if not overwrite and any([op.exists(d) for d in copyDests]):
raise 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)
def immv(src, dest, allowedExts=None, fileGroups=None, overwrite=False):
"""Move the specified ``src`` to the specified ``dest``. See :func:`imcp`.
"""
imcp(src, dest, allowedExts, fileGroups, overwrite, move=True)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment