Skip to content
Snippets Groups Projects
Commit 5792f491 authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

Merge branch 'rf/concatxfm' into 'master'

Rf/concatxfm

See merge request fsl/fslpy!224
parents 685b68d3 831fdf6e
No related branches found
No related tags found
No related merge requests found
Pipeline #5217 passed
...@@ -2,6 +2,19 @@ This document contains the ``fslpy`` release history in reverse chronological ...@@ -2,6 +2,19 @@ This document contains the ``fslpy`` release history in reverse chronological
order. order.
3.0.1 (Wednesday 15th April 2020)
---------------------------------
Changed
^^^^^^^
* The :func:`.isMelodicDir` function now accepts directories that do not end
with ``.ica``, as long as all required files are present.
3.0.0 (Sunday 29th March 2020) 3.0.0 (Sunday 29th March 2020)
------------------------------ ------------------------------
......
...@@ -622,8 +622,9 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -622,8 +622,9 @@ class Nifti(notifier.Notifier, meta.Meta):
if from_ not in ('voxel', 'fsl', 'world') or \ if from_ not in ('voxel', 'fsl', 'world') or \
to not in ('voxel', 'fsl', 'world'): to not in ('voxel', 'fsl', 'world'):
raise ValueError('Invalid source/reference spaces: ' raise ValueError('Invalid source/reference spaces: "{}" -> "{}".'
'{} -> {}'.format(from_, to)) 'Recognised spaces are "voxel", "fsl", and '
'"world"'.format(from_, to))
return np.copy(self.__affines[from_, to]) return np.copy(self.__affines[from_, to])
......
...@@ -33,7 +33,6 @@ import logging ...@@ -33,7 +33,6 @@ import logging
import os.path as op import os.path as op
import numpy as np import numpy as np
import fsl.utils.path as fslpath
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.data.featanalysis as featanalysis import fsl.data.featanalysis as featanalysis
...@@ -63,10 +62,9 @@ def isMelodicImage(path): ...@@ -63,10 +62,9 @@ def isMelodicImage(path):
def isMelodicDir(path): def isMelodicDir(path):
"""Returns ``True`` if the given path looks like it is contained within """Returns ``True`` if the given path looks like it is a MELODIC directory,
a MELODIC directory, ``False`` otherwise. A melodic directory: ``False`` otherwise. A MELODIC directory:
- Must be named ``*.ica``.
- Must contain a file called ``melodic_IC.nii.gz`` or - Must contain a file called ``melodic_IC.nii.gz`` or
``melodic_oIC.nii.gz``. ``melodic_oIC.nii.gz``.
- Must contain a file called ``melodic_mix``. - Must contain a file called ``melodic_mix``.
...@@ -75,12 +73,7 @@ def isMelodicDir(path): ...@@ -75,12 +73,7 @@ def isMelodicDir(path):
path = op.abspath(path) path = op.abspath(path)
if op.isdir(path): dirname = path if not op.isdir(path):
else: dirname = op.dirname(path)
sufs = ['.ica']
if not any([dirname.endswith(suf) for suf in sufs]):
return False return False
# Must contain an image file called # Must contain an image file called
...@@ -88,7 +81,7 @@ def isMelodicDir(path): ...@@ -88,7 +81,7 @@ def isMelodicDir(path):
prefixes = ['melodic_IC', 'melodic_oIC'] prefixes = ['melodic_IC', 'melodic_oIC']
for p in prefixes: for p in prefixes:
try: try:
fslimage.addExt(op.join(dirname, p)) fslimage.addExt(op.join(path, p))
break break
except fslimage.PathError: except fslimage.PathError:
pass pass
...@@ -97,8 +90,8 @@ def isMelodicDir(path): ...@@ -97,8 +90,8 @@ def isMelodicDir(path):
# Must contain files called # Must contain files called
# melodic_mix and melodic_FTmix # melodic_mix and melodic_FTmix
if not op.exists(op.join(dirname, 'melodic_mix')): return False if not op.exists(op.join(path, 'melodic_mix')): return False
if not op.exists(op.join(dirname, 'melodic_FTmix')): return False if not op.exists(op.join(path, 'melodic_FTmix')): return False
return True return True
...@@ -108,10 +101,13 @@ def getAnalysisDir(path): ...@@ -108,10 +101,13 @@ def getAnalysisDir(path):
to that MELODIC directory is returned. Otherwise, ``None`` is returned. to that MELODIC directory is returned. Otherwise, ``None`` is returned.
""" """
meldir = fslpath.deepest(path, ['.ica']) if not op.isdir(path):
path = op.dirname(path)
if meldir is not None and isMelodicDir(meldir): while path not in (op.sep, ''):
return meldir if isMelodicDir(path):
return path
path = op.dirname(path)
return None return None
......
...@@ -98,19 +98,26 @@ def invxfm(inmat, omat): ...@@ -98,19 +98,26 @@ def invxfm(inmat, omat):
return ['convert_xfm', '-omat', omat, '-inverse', inmat] return ['convert_xfm', '-omat', omat, '-inverse', inmat]
@wutils.fileOrArray('inmat1', 'inmat2', 'outmat') @wutils.fileOrArray('atob', 'atoc', 'btoc')
@wutils.fslwrapper @wutils.fslwrapper
def concatxfm(inmat1, inmat2, outmat): def concatxfm(atob, btoc, atoc):
"""Use ``convert_xfm`` to concatenate two affines.""" """Use ``convert_xfm`` to concatenate two affines. Note that the
order of the input matrices is the opposite of the order expected
by ``convert_xfm``.
:arg atob: Input matrix, transforming from "A" to "B".
:arg btoc: Input matrix, transforming from "B" to "C".
:arg atoc: Output matrix, transforming from "A" to "C".
"""
asrt.assertFileExists(inmat1, inmat2) asrt.assertFileExists(atob, btoc)
cmd = ['convert_xfm', cmd = ['convert_xfm',
'-omat', '-omat',
outmat, atoc,
'-concat', '-concat',
inmat2, btoc,
inmat1] atob]
return cmd return cmd
......
...@@ -160,14 +160,14 @@ def test_assertIsMelodicDir(): ...@@ -160,14 +160,14 @@ def test_assertIsMelodicDir():
('analysis.ica', [ 'melodic_mix', 'melodic_FTmix'], False), ('analysis.ica', [ 'melodic_mix', 'melodic_FTmix'], False),
('analysis.ica', ['melodic_IC.nii.gz', 'melodic_FTmix'], False), ('analysis.ica', ['melodic_IC.nii.gz', 'melodic_FTmix'], False),
('analysis.ica', ['melodic_IC.nii.gz', 'melodic_mix'], False), ('analysis.ica', ['melodic_IC.nii.gz', 'melodic_mix'], False),
('analysis', ['melodic_IC.nii.gz', 'melodic_mix', 'melodic_FTmix'], False), ('analysis', ['melodic_IC.nii.gz', 'melodic_mix', 'melodic_FTmix'], True),
('analysis', ['melodic_oIC.nii.gz', 'melodic_mix', 'melodic_FTmix'], False), ('analysis', [ 'melodic_mix', 'melodic_FTmix'], False),
] ]
for dirname, paths, expected in tests: for dirname, paths, expected in tests:
with testdir(paths, dirname): with testdir(paths, dirname):
if expected: if expected:
assertions.assertIsMelodicDir(dirname) assertions.assertIsMelodicDir('.')
else: else:
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assertions.assertIsMelodicDir(dirname) assertions.assertIsMelodicDir(dirname)
......
...@@ -13,7 +13,8 @@ import os ...@@ -13,7 +13,8 @@ import os
import os.path as op import os.path as op
import numpy as np import numpy as np
import mock
from unittest import mock
import pytest import pytest
import tests import tests
......
...@@ -22,7 +22,7 @@ from . import mockFSLDIR ...@@ -22,7 +22,7 @@ from . import mockFSLDIR
mock_fsl_sub = """ mock_fsl_sub = """
#!{} #!/usr/bin/env python3
import random import random
import os import os
...@@ -62,8 +62,7 @@ with open('{{}}.o{{}}'.format(cmd, jobid), 'w') as stdout, \ ...@@ -62,8 +62,7 @@ with open('{{}}.o{{}}'.format(cmd, jobid), 'w') as stdout, \
print(str(jobid)) print(str(jobid))
sys.exit(0) sys.exit(0)
""".format(sys.executable, op.dirname(fsl.__file__)).strip() """.format(op.dirname(fsl.__file__)).strip()
@contextlib.contextmanager @contextlib.contextmanager
def fslsub_mockFSLDIR(): def fslsub_mockFSLDIR():
......
...@@ -55,10 +55,10 @@ def test_isMelodicDir(): ...@@ -55,10 +55,10 @@ def test_isMelodicDir():
meldir = op.join(testdir, 'analysis.ica') meldir = op.join(testdir, 'analysis.ica')
assert mela.isMelodicDir(meldir) assert mela.isMelodicDir(meldir)
# Directory must end in .ica # non-.ica prefix is ok
with tests.testdir([p.replace('.ica', '.blob') for p in paths]) as testdir: with tests.testdir([p.replace('.ica', '.blob') for p in paths]) as testdir:
meldir = op.join(testdir, 'analysis.blob') meldir = op.join(testdir, 'analysis.blob')
assert not mela.isMelodicDir(meldir) assert mela.isMelodicDir(meldir)
# Directory must exist! # Directory must exist!
assert not mela.isMelodicDir('non-existent.ica') assert not mela.isMelodicDir('non-existent.ica')
......
...@@ -18,9 +18,9 @@ from .. import mockFSLDIR as mockFSLDIR_base, make_random_image ...@@ -18,9 +18,9 @@ from .. import mockFSLDIR as mockFSLDIR_base, make_random_image
mock_fslstats = """ mock_fslstats = """
#!{} #!/usr/bin/env python3
shape = {{outshape}} shape = {outshape}
import sys import sys
import numpy as np import numpy as np
...@@ -31,7 +31,7 @@ if len(shape) == 1: ...@@ -31,7 +31,7 @@ if len(shape) == 1:
data = data.reshape(1, -1) data = data.reshape(1, -1)
np.savetxt(sys.stdout, data, fmt='%i') np.savetxt(sys.stdout, data, fmt='%i')
""".format(sys.executable).strip() """.strip()
@contextlib.contextmanager @contextlib.contextmanager
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment