Commit 5792f491 authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'rf/concatxfm' into 'master'


See merge request fsl/fslpy!224
parents 685b68d3 831fdf6e
......@@ -2,6 +2,19 @@ This document contains the ``fslpy`` release history in reverse chronological
3.0.1 (Wednesday 15th April 2020)
* 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)
......@@ -622,8 +622,9 @@ class Nifti(notifier.Notifier, meta.Meta):
if from_ not in ('voxel', 'fsl', 'world') or \
to not in ('voxel', 'fsl', 'world'):
raise ValueError('Invalid source/reference spaces: '
'{} -> {}'.format(from_, to))
raise ValueError('Invalid source/reference spaces: "{}" -> "{}".'
'Recognised spaces are "voxel", "fsl", and '
'"world"'.format(from_, to))
return np.copy(self.__affines[from_, to])
......@@ -33,7 +33,6 @@ import logging
import os.path as op
import numpy as np
import fsl.utils.path as fslpath
import as fslimage
import as featanalysis
......@@ -63,10 +62,9 @@ def isMelodicImage(path):
def isMelodicDir(path):
"""Returns ``True`` if the given path looks like it is contained within
a MELODIC directory, ``False`` otherwise. A melodic directory:
"""Returns ``True`` if the given path looks like it is 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_mix``.
......@@ -75,12 +73,7 @@ def isMelodicDir(path):
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]):
if not op.isdir(path):
return False
# Must contain an image file called
......@@ -88,7 +81,7 @@ def isMelodicDir(path):
prefixes = ['melodic_IC', 'melodic_oIC']
for p in prefixes:
fslimage.addExt(op.join(dirname, p))
fslimage.addExt(op.join(path, p))
except fslimage.PathError:
......@@ -97,8 +90,8 @@ def isMelodicDir(path):
# 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
if not op.exists(op.join(path, 'melodic_mix')): return False
if not op.exists(op.join(path, 'melodic_FTmix')): return False
return True
......@@ -108,10 +101,13 @@ def getAnalysisDir(path):
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):
return meldir
while path not in (op.sep, ''):
if isMelodicDir(path):
return path
path = op.dirname(path)
return None
......@@ -98,19 +98,26 @@ def invxfm(inmat, omat):
return ['convert_xfm', '-omat', omat, '-inverse', inmat]
@wutils.fileOrArray('inmat1', 'inmat2', 'outmat')
@wutils.fileOrArray('atob', 'atoc', 'btoc')
def concatxfm(inmat1, inmat2, outmat):
"""Use ``convert_xfm`` to concatenate two affines."""
def concatxfm(atob, btoc, atoc):
"""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',
return cmd
......@@ -160,14 +160,14 @@ def test_assertIsMelodicDir():
('analysis.ica', [ 'melodic_mix', 'melodic_FTmix'], False),
('analysis.ica', ['melodic_IC.nii.gz', 'melodic_FTmix'], False),
('analysis.ica', ['melodic_IC.nii.gz', 'melodic_mix'], False),
('analysis', ['melodic_IC.nii.gz', 'melodic_mix', 'melodic_FTmix'], False),
('analysis', ['melodic_oIC.nii.gz', 'melodic_mix', 'melodic_FTmix'], False),
('analysis', ['melodic_IC.nii.gz', 'melodic_mix', 'melodic_FTmix'], True),
('analysis', [ 'melodic_mix', 'melodic_FTmix'], False),
for dirname, paths, expected in tests:
with testdir(paths, dirname):
if expected:
with pytest.raises(AssertionError):
......@@ -13,7 +13,8 @@ import os
import os.path as op
import numpy as np
import mock
from unittest import mock
import pytest
import tests
......@@ -22,7 +22,7 @@ from . import mockFSLDIR
mock_fsl_sub = """
#!/usr/bin/env python3
import random
import os
......@@ -62,8 +62,7 @@ with open('{{}}.o{{}}'.format(cmd, jobid), 'w') as stdout, \
""".format(sys.executable, op.dirname(fsl.__file__)).strip()
def fslsub_mockFSLDIR():
......@@ -55,10 +55,10 @@ def test_isMelodicDir():
meldir = op.join(testdir, 'analysis.ica')
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:
meldir = op.join(testdir, 'analysis.blob')
assert not mela.isMelodicDir(meldir)
assert mela.isMelodicDir(meldir)
# Directory must exist!
assert not mela.isMelodicDir('non-existent.ica')
......@@ -18,9 +18,9 @@ from .. import mockFSLDIR as mockFSLDIR_base, make_random_image
mock_fslstats = """
#!/usr/bin/env python3
shape = {{outshape}}
shape = {outshape}
import sys
import numpy as np
......@@ -31,7 +31,7 @@ if len(shape) == 1:
data = data.reshape(1, -1)
np.savetxt(sys.stdout, data, fmt='%i')
Supports Markdown
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