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

Merge branch 'enh/cluster' into 'main'

ENH: Wrapper function for FSL `cluster` command

See merge request fsl/fslpy!417
parents 3bffd560 ab598b91
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,12 @@ order.
--------------------------
Added
^^^^^
* New :func:`.cluster` wrapper function for the FSL ``cluster`` /
``fsl-cluster`` command (!417).
Changed
^^^^^^^
......
......@@ -46,3 +46,4 @@ def testenv(*fslexes):
fslexes = [op.join(fsldir, 'bin', e) for e in fslexes]
if len(fslexes) == 1: yield fslexes[0]
else: yield fslexes
testenv.__test__ = False
#!/usr/bin/env python
#
# test_cluster.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import contextlib
import os
import os.path as op
import sys
import numpy as np
from fsl.wrappers.cluster import cluster, _cluster
from . import testenv
from .. import mockFSLDIR
@contextlib.contextmanager
def reseed(seed):
state = np.random.get_state()
np.random.seed(seed)
try:
yield
finally:
np.random.set_state(state)
def test_cluster_wrapper():
with testenv('fsl-cluster') as cluster_exe:
result = _cluster('input', 10, mm=True)
expected = [cluster_exe, '-i', 'input', '-t', '10', '--mm']
expected = ' '.join(expected)
assert result.stdout[0] == expected
mock_titles = 'ABCDEFGHIJ'
mock_cluster = f"""
#!{sys.executable}
import numpy as np
np.random.seed(12345)
data = np.random.randint(1, 10, (10, 10))
print('\t'.join('{mock_titles}'))
for row in data:
print('\t'.join([str(val) for val in row]))
""".strip()
def test_cluster():
with mockFSLDIR() as fsldir:
cluster_exe = op.join(fsldir, 'bin', 'fsl-cluster')
with open(cluster_exe, 'wt') as f:
f.write(mock_cluster)
os.chmod(cluster_exe, 0o755)
data, titles, result1 = cluster('input', 3.5)
result2 = cluster('input', 3.5, load=False)
with reseed(12345):
expected = np.random.randint(1, 10, (10, 10))
assert np.all(np.isclose(data, expected))
assert ''.join(titles) == mock_titles
assert result1.stdout == result2.stdout
......@@ -103,6 +103,7 @@ from fsl.wrappers.wrapperutils import (LOAD,
fslwrapper,
funcwrapper)
from fsl.wrappers import (tbss,)
from fsl.wrappers.cluster import (cluster,)
from fsl.wrappers.bet import (bet,
robustfov)
from fsl.wrappers.eddy import (eddy,
......
#!/usr/bin/env python
#
# cluster.py - Wrapper for the FSL cluster command.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module contains the :func:`cluster` function, which calls
the FSL ``cluster`` / ``fsl-cluster``command.
"""
import io
import numpy as np
from . import wrapperutils as wutils
def cluster(infile, thres, load=True, **kwargs):
"""Wrapper function for the FSL ``cluster`/``fsl-cluster``) command.
If ``load is True`` (the default) a tuple is returned, containing:
- A numpy array containing the cluster table
- A list of column titles
- A dictionary containing references to the standard output as an
attribute called ``stdout``, and to the output images if any,
e.g. ``--oindex``.
If ``load is False``, only the dictionary is returned.
"""
result = _cluster(infile, thres, **kwargs)
if load:
header, data = result.stdout[0].split('\n', 1)
titles = header.split('\t')
data = np.loadtxt(io.StringIO(data), delimiter='\t')
return data, titles, result
else:
return result
@wutils.fileOrImage('infile', 'othresh', 'olmaxim', 'osize',
'omax', 'omean', 'opvals', 'cope', 'warpvol',)
@wutils.fslwrapper
def _cluster(infile, thresh, **kwargs):
"""Actual wrapper function for the FSL ``cluster``/``fsl-cluster`` command.
"""
valmap = {
'fractional' : wutils.SHOW_IF_TRUE,
'mm' : wutils.SHOW_IF_TRUE,
'min' : wutils.SHOW_IF_TRUE,
'no_table' : wutils.SHOW_IF_TRUE,
'minclustersize' : wutils.SHOW_IF_TRUE,
'scalarname' : wutils.SHOW_IF_TRUE,
'verbose' : wutils.SHOW_IF_TRUE,
'voxthresh' : wutils.SHOW_IF_TRUE,
'voxuncthresh' : wutils.SHOW_IF_TRUE,
}
cmd = ['fsl-cluster', '-i', infile, '-t', str(thresh)]
cmd += wutils.applyArgStyle('--=', valmap=valmap, **kwargs)
return cmd
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment