Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Evan Edmond
fslpy
Commits
8cadf624
Commit
8cadf624
authored
Apr 25, 2017
by
Paul McCarthy
Browse files
Removing spurious whitespace (just found out that emacs can do it for me).
parent
4b520dd4
Changes
27
Expand all
Hide whitespace changes
Inline
Side-by-side
fsl/__init__.py
View file @
8cadf624
...
...
@@ -5,7 +5,7 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""The :mod:`fsl` package is a library which contains convenience classes
and functions for use by FSL python tools. It is broadly split into the
and functions for use by FSL python tools. It is broadly split into the
following sub-packages:
.. autosummary::
...
...
fsl/data/atlases.py
View file @
8cadf624
#!/usr/bin/env python
#
# atlases.py - API which provides access to the atlas image files contained
# atlases.py - API which provides access to the atlas image files contained
# in $FSLDIR/data/atlases/
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
...
...
@@ -68,7 +68,7 @@ class AtlasRegistry(notifier.Notifier):
``$FSLDIR/data/atlases``, and builds a list of :class:`AtlasDescription`
instances, each of which contains information about one atlas.
The :meth:`addAtlas` method allows other atlases to be added to the
registry. Whenever a new atlas is added, the ``AtlasRegistry`` notifies
any registered listeners via the :class:`.Notifier` interface with the
...
...
@@ -81,18 +81,18 @@ class AtlasRegistry(notifier.Notifier):
The ``AtlasRegistry`` stores a list of all known atlases via the
:mod:`.settings` module. When an ``AtlasRegistry`` is created, it loads
in any previously known atlases. Whenever a new atlas is added, this
in any previously known atlases. Whenever a new atlas is added, this
list is updated. See the :meth:`__getKnownAtlases` and
:meth:`_saveKnownAtlases` methods.
"""
def
__init__
(
self
):
"""Create an ``AtlasRegistry``. """
# A list of all AtlasDescription
# instances in existence, sorted
# by AtlasDescription.name.
# instances in existence, sorted
# by AtlasDescription.name.
self
.
__atlasDescs
=
[]
...
...
@@ -105,9 +105,9 @@ class AtlasRegistry(notifier.Notifier):
log
.
debug
(
'Initialising atlas registry'
)
self
.
__atlasDescs
=
[]
# Get $FSLDIR atlases
# Get $FSLDIR atlases
fslPaths
=
[]
if
platform
.
fsldir
is
not
None
:
if
platform
.
fsldir
is
not
None
:
fsldir
=
op
.
join
(
platform
.
fsldir
,
'data'
,
'atlases'
)
fslPaths
=
sorted
(
glob
.
glob
(
op
.
join
(
fsldir
,
'*.xml'
)))
...
...
@@ -115,14 +115,14 @@ class AtlasRegistry(notifier.Notifier):
# been loaded in the past
extraIDs
,
extraPaths
=
self
.
__getKnownAtlases
()
# FSLDIR atlases first, any
# FSLDIR atlases first, any
# other atlases second.
atlasPaths
=
list
(
fslPaths
)
+
extraPaths
atlasIDs
=
[
None
]
*
len
(
fslPaths
)
+
extraIDs
with
self
.
skipAll
():
for
atlasID
,
atlasPath
in
zip
(
atlasIDs
,
atlasPaths
):
# The FSLDIR atlases are probably
# listed twice - from the above glob,
# and from the saved extraPaths. So
...
...
@@ -137,7 +137,7 @@ class AtlasRegistry(notifier.Notifier):
'specification {}'
.
format
(
atlasPath
),
exc_info
=
True
)
def
listAtlases
(
self
):
"""Returns a list containing :class:`AtlasDescription` objects for
all available atlases. The atlases are ordered in terms of the
...
...
@@ -157,17 +157,17 @@ class AtlasRegistry(notifier.Notifier):
"""Returns an :class:`AtlasDescription` instance describing the
atlas with the given ``atlasID``.
"""
for
desc
in
self
.
__atlasDescs
:
if
desc
.
atlasID
==
atlasID
:
return
desc
raise
KeyError
(
'Unknown atlas ID: {}'
.
format
(
atlasID
))
def
loadAtlas
(
self
,
atlasID
,
loadSummary
=
False
,
resolution
=
None
):
"""Loads and returns an :class:`Atlas` instance for the atlas
with the given ``atlasID``.
with the given ``atlasID``.
:arg loadSummary: If ``True``, a 3D :class:`LabelAtlas` image is
loaded. Otherwise, if the atlas is probabilistic,
...
...
@@ -203,8 +203,8 @@ class AtlasRegistry(notifier.Notifier):
atlas with the given ID already exists, this new atlas
is given a unique id.
:arg save: If ``True`` (the default), this atlas will be saved
so that it will be available in future instantiations.
:arg save: If ``True`` (the default), this atlas will be saved
so that it will be available in future instantiations.
"""
filename
=
op
.
abspath
(
filename
)
...
...
@@ -233,7 +233,7 @@ class AtlasRegistry(notifier.Notifier):
self
.
__saveKnownAtlases
()
self
.
notify
(
topic
=
'add'
,
value
=
desc
)
return
desc
...
...
@@ -248,15 +248,15 @@ class AtlasRegistry(notifier.Notifier):
log
.
debug
(
'Removing atlas from registry: {} / {}'
.
format
(
desc
.
atlasID
,
desc
.
specPath
))
self
.
__atlasDescs
.
pop
(
i
)
break
self
.
__saveKnownAtlases
()
self
.
notify
(
topic
=
'remove'
,
value
=
desc
)
def
__getKnownAtlases
(
self
):
"""Returns a list of tuples containing the IDs and paths of all known
atlases .
...
...
@@ -282,15 +282,15 @@ class AtlasRegistry(notifier.Notifier):
paths
=
[
e
[
1
]
for
e
in
atlases
]
return
names
,
paths
except
:
return
[],
[]
def
__saveKnownAtlases
(
self
):
"""Saves the IDs and paths of all atlases which are currently in
the registry. The atlases are saved via the :mod:`.settings` module.
"""
"""
if
self
.
__atlasDescs
is
None
:
return
...
...
@@ -302,10 +302,10 @@ class AtlasRegistry(notifier.Notifier):
atlases
=
[
'{}={}'
.
format
(
name
,
path
)
for
name
,
path
in
atlases
]
atlases
=
op
.
pathsep
.
join
(
atlases
)
fslsettings
.
write
(
'fsl.data.atlases'
,
atlases
)
class
AtlasDescription
(
object
):
"""An ``AtlasDescription`` instance parses and stores the information
stored in the FSL XML file that describes a single FSL atlas. An XML
...
...
@@ -325,11 +325,11 @@ class AtlasDescription(object):
# label, Otherwise, if type is
# Label, path to 3D label file
# (identical to the summaryimagefile
# below). The path must be specified
# below). The path must be specified
# as relative to the location of this
# XML file.
<summaryimagefile> # Path to 3D summary file, with each
<summaryimagefile> # Path to 3D summary file, with each
</summaryimagefile> # region having value (index + 1)
</images>
...
...
@@ -342,7 +342,7 @@ class AtlasDescription(object):
# index - For probabilistic atlases, index of corresponding volume in
# 4D image file. For label images, the value of voxels which
# are in the corresponding region.
#
#
# x |
# y |- XYZ *voxel* coordinates into the first image of the <images>
# | list
...
...
@@ -352,7 +352,7 @@ class AtlasDescription(object):
</data>
</atlas>
Each ``AtlasDescription`` is assigned an identifier, which is simply the
XML file name describing the atlas, sans-suffix, and converted to lower
case. For exmaple, the atlas described by:
...
...
@@ -363,7 +363,7 @@ class AtlasDescription(object):
``harvardoxford-cortical``
This identifier is intended to be unique.
...
...
@@ -371,26 +371,26 @@ class AtlasDescription(object):
================= ======================================================
``atlasID`` The atlas ID, as described above.
``name`` Name of the atlas.
``specPath`` Path to the atlas XML specification file.
``atlasType`` Atlas type - either *probabilistic* or *label*.
``images`` A list of images available for this atlas - usually
:math:`1mm^3` and :math:`2mm^3` images are present.
``summaryImages`` For probabilistic atlases, a list of *summary* images,
which are just 3D labelled variants of the atlas.
``pixdims`` A list of ``(x, y, z)`` pixdim tuples in mm, one for
each image in ``images``.
``xforms`` A list of affine transformation matrices (as ``4*4``
``numpy`` arrays), one for each image in ``images``,
defining the voxel to world coordinate transformations.
``labels`` A list of ``AtlasLabel`` objects, describing each
region / label in the atlas.
================= ======================================================
...
...
@@ -417,7 +417,7 @@ class AtlasDescription(object):
MNI152 space).
"""
def
__init__
(
self
,
filename
,
atlasID
=
None
):
"""Create an ``AtlasDescription`` instance.
...
...
@@ -440,7 +440,7 @@ class AtlasDescription(object):
self
.
specPath
=
op
.
abspath
(
filename
)
self
.
name
=
header
.
find
(
'name'
).
text
self
.
atlasType
=
header
.
find
(
'type'
).
text
.
lower
()
# Spelling error in some of the atlas.xml files.
if
self
.
atlasType
==
'probabalistic'
:
self
.
atlasType
=
'probabilistic'
...
...
@@ -452,7 +452,7 @@ class AtlasDescription(object):
self
.
xforms
=
[]
atlasDir
=
op
.
dirname
(
self
.
specPath
)
for
image
in
images
:
imagefile
=
image
.
find
(
'imagefile'
)
.
text
summaryimagefile
=
image
.
find
(
'summaryimagefile'
).
text
...
...
@@ -485,7 +485,7 @@ class AtlasDescription(object):
coords
=
np
.
zeros
((
len
(
labels
),
3
),
dtype
=
np
.
float32
)
for
i
,
label
in
enumerate
(
labels
):
al
=
AtlasLabel
()
al
.
name
=
label
.
text
al
.
index
=
int
(
label
.
attrib
[
'index'
])
...
...
@@ -502,7 +502,7 @@ class AtlasDescription(object):
# into world coordinates
coords
=
transform
.
transform
(
coords
,
self
.
xforms
[
0
])
# Update the coordinates
# Update the coordinates
# in our label objects
for
i
,
label
in
enumerate
(
self
.
labels
):
...
...
@@ -514,12 +514,12 @@ class AtlasDescription(object):
"""
return
self
.
atlasID
==
other
.
atlasID
def
__neq__
(
self
,
other
):
"""Compares the ``atlasID`` of this ``AtlasDescription`` with another.
"""
return
self
.
atlasID
!=
other
.
atlasID
return
self
.
atlasID
!=
other
.
atlasID
def
__cmp__
(
self
,
other
):
"""Compares this ``AtlasDescription`` with another by their ``name``
...
...
@@ -534,11 +534,11 @@ class Atlas(fslimage.Image):
logic common to both.
"""
def
__init__
(
self
,
atlasDesc
,
resolution
=
None
,
isLabel
=
False
):
"""Initialise an ``Atlas``.
:arg atlasDesc: The :class:`AtlasDescription` instance which
:arg atlasDesc: The :class:`AtlasDescription` instance which
describes the atlas.
:arg resolution: Desired isotropic resolution in millimetres.
...
...
@@ -552,13 +552,13 @@ class Atlas(fslimage.Image):
# If a reslution has not been provided,
# choose the atlas image with the
# highest resolution.
#
#
# We divide by three to get the atlas
# image index because there are three
# pixdim values for each atlas.
res
=
resolution
reses
=
np
.
concatenate
(
atlasDesc
.
pixdims
)
if
resolution
is
None
:
imageIdx
=
np
.
argmin
(
reses
)
/
3
else
:
imageIdx
=
np
.
argmin
(
np
.
abs
(
reses
-
res
))
/
3
...
...
@@ -575,7 +575,7 @@ class Atlas(fslimage.Image):
self
.
desc
=
atlasDesc
class
LabelAtlas
(
Atlas
):
"""A 3D atlas which contains integer labels for each region.
...
...
@@ -593,7 +593,7 @@ class LabelAtlas(Atlas):
"""
Atlas
.
__init__
(
self
,
atlasDesc
,
resolution
,
True
)
def
label
(
self
,
worldLoc
):
"""Looks up and returns the label of the region at the given world
location, or ``None`` if the location is out of bounds.
...
...
@@ -609,16 +609,16 @@ class LabelAtlas(Atlas):
voxelLoc
[
1
]
>=
self
.
shape
[
1
]
or
\
voxelLoc
[
2
]
>=
self
.
shape
[
2
]:
return
None
val
=
self
[
voxelLoc
[
0
],
voxelLoc
[
1
],
voxelLoc
[
2
]]
if
self
.
desc
.
atlasType
==
'label'
:
return
val
elif
self
.
desc
.
atlasType
==
'probabilistic'
:
return
val
-
1
class
ProbabilisticAtlas
(
Atlas
):
"""A 4D atlas which contains one volume for each region.
...
...
@@ -633,10 +633,10 @@ class ProbabilisticAtlas(Atlas):
the atlas.
:arg resolution: Desired isotropic resolution in millimetres.
"""
"""
Atlas
.
__init__
(
self
,
atlasDesc
,
resolution
,
False
)
def
proportions
(
self
,
worldLoc
):
"""Looks up the region probabilities for the given location.
...
...
@@ -657,7 +657,7 @@ class ProbabilisticAtlas(Atlas):
voxelLoc
[
1
]
>=
self
.
shape
[
1
]
or
\
voxelLoc
[
2
]
>=
self
.
shape
[
2
]:
return
[]
return
self
[
voxelLoc
[
0
],
voxelLoc
[
1
],
voxelLoc
[
2
],
:]
...
...
fsl/data/dtifit.py
View file @
8cadf624
...
...
@@ -5,7 +5,7 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`.DTIFitTensor` class, which encapsulates
the diffusion tensor data generated by the FSL ``dtifit`` tool.
the diffusion tensor data generated by the FSL ``dtifit`` tool.
The following utility functions are also defined:
...
...
@@ -34,11 +34,11 @@ log = logging.getLogger(__name__)
def
getDTIFitDataPrefix
(
path
):
"""Returns the prefix (a.k,a, base name) used for the ``dtifit`` file
names in the given directory, or ``None`` if the ``dtifit`` files could
"""Returns the prefix (a.k,a, base name) used for the ``dtifit`` file
names in the given directory, or ``None`` if the ``dtifit`` files could
not be identified.
"""
v1s
=
glob
.
glob
(
op
.
join
(
path
,
'*_V1.*'
))
v2s
=
glob
.
glob
(
op
.
join
(
path
,
'*_V2.*'
))
v3s
=
glob
.
glob
(
op
.
join
(
path
,
'*_V3.*'
))
...
...
@@ -58,8 +58,8 @@ def getDTIFitDataPrefix(path):
if
prefix
not
in
prefixes
:
prefixes
[
prefix
]
=
[
f
]
else
:
prefixes
[
prefix
].
append
(
f
)
# Discard any prefixes which are
# Discard any prefixes which are
# not present for every file type.
for
prefix
,
files
in
list
(
prefixes
.
items
()):
if
len
(
files
)
!=
6
:
...
...
@@ -82,7 +82,7 @@ def getDTIFitDataPrefix(path):
# If there's more than one remaining
# prefix, I don't know what to do -
# just return the first one.
# just return the first one.
if
len
(
prefixes
)
>
1
:
log
.
warning
(
'Multiple dtifit prefixes detected: {}'
.
format
(
prefixes
))
...
...
@@ -93,12 +93,12 @@ def isDTIFitPath(path):
"""Returns ``True`` if the given directory path looks like it contains
``dtifit`` data, ``False`` otherwise.
"""
return
getDTIFitDataPrefix
(
path
)
is
not
None
def
looksLikeTensorImage
(
image
):
"""Returns ``True`` if the given :class:`.Image` looks like it could
"""Returns ``True`` if the given :class:`.Image` looks like it could
contain tensor matrix data, ``False`` otherwise.
"""
...
...
@@ -114,7 +114,8 @@ def decomposeTensorMatrix(data):
the unique elements of diffusion tensor matrices at
every voxel.
:returns:
:returns: A tuple containing the principal eigenvectors and
eigenvalues of the tensor matrix.
"""
# The image contains 6 volumes, corresponding
...
...
@@ -141,13 +142,13 @@ def decomposeTensorMatrix(data):
matrices
[:,
2
,
1
]
=
data
[...,
4
].
flat
matrices
[:,
2
,
2
]
=
data
[...,
5
].
flat
# Calculate the eigenvectors and
# Calculate the eigenvectors and
# values on all of those matrices
vals
,
vecs
=
npla
.
eig
(
matrices
)
vecShape
=
list
(
shape
)
+
[
3
]
# Grr, np.linalg.eig does not
# sort the eigenvalues/vectors,
# Grr, np.linalg.eig does not
# sort the eigenvalues/vectors,
# so we have to do it ourselves.
order
=
vals
.
argsort
(
axis
=
1
)
i
=
np
.
arange
(
nvoxels
)[:,
np
.
newaxis
]
...
...
@@ -173,7 +174,7 @@ class DTIFitTensor(fslimage.Nifti):
separate NIFTI images.
"""
def
__init__
(
self
,
path
):
"""Create a ``DTIFitTensor``.
...
...
@@ -202,9 +203,15 @@ class DTIFitTensor(fslimage.Nifti):
self
.
dataSource
=
op
.
abspath
(
path
)
self
.
name
=
'{}'
.
format
(
op
.
basename
(
path
))
def
V1
(
self
):
return
self
.
__v1
def
V2
(
self
):
return
self
.
__v2
def
V3
(
self
):
return
self
.
__v3
def
L1
(
self
):
return
self
.
__l1
def
L2
(
self
):
return
self
.
__l2
def
L3
(
self
):
return
self
.
__l3
def
V1
(
self
):
return
self
.
__v1
def
V2
(
self
):
return
self
.
__v2
def
V3
(
self
):
return
self
.
__v3
def
L1
(
self
):
return
self
.
__l1
def
L2
(
self
):
return
self
.
__l2
def
L3
(
self
):
return
self
.
__l3
fsl/data/featanalysis.py
View file @
8cadf624
...
...
@@ -51,7 +51,7 @@ import fsl.utils.path as fslpath
import
fsl.utils.transform
as
transform
from
.
import
image
as
fslimage
from
.
import
featdesign
from
.
import
featdesign
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -70,7 +70,7 @@ def isFEATImage(path):
dirname
=
op
.
dirname
(
path
)
filename
=
op
.
basename
(
path
)
return
filename
.
startswith
(
'filtered_func_data'
)
and
isFEATDir
(
dirname
)
return
filename
.
startswith
(
'filtered_func_data'
)
and
isFEATDir
(
dirname
)
def
isFEATDir
(
path
):
...
...
@@ -88,10 +88,10 @@ def isFEATDir(path):
"""
path
=
op
.
abspath
(
path
)
if
op
.
isdir
(
path
):
dirname
=
path
else
:
dirname
=
op
.
dirname
(
path
)
if
not
dirname
.
endswith
(
'.feat'
):
return
False
...
...
@@ -99,11 +99,11 @@ def isFEATDir(path):
fslimage
.
addExt
(
op
.
join
(
dirname
,
'filtered_func_data'
),
mustExist
=
True
)
except
fslimage
.
PathError
:
return
False
if
not
op
.
exists
(
op
.
join
(
dirname
,
'design.fsf'
)):
return
False
if
not
op
.
exists
(
op
.
join
(
dirname
,
'design.mat'
)):
return
False
if
not
op
.
exists
(
op
.
join
(
dirname
,
'design.con'
)):
return
False
return
True
...
...
@@ -117,7 +117,7 @@ def hasStats(featdir):
return
True
except
:
return
False
def
hasMelodicDir
(
featdir
):
"""Returns ``True`` if the data for the given FEAT directory has had
...
...
@@ -150,7 +150,7 @@ def getReportFile(featdir):
"""Returns the path to the FEAT report index file, or ``None`` if there
is no report.
"""
report
=
op
.
join
(
featdir
,
'report.html'
)
if
op
.
exists
(
report
):
return
report
else
:
return
None
...
...
@@ -158,9 +158,9 @@ def getReportFile(featdir):
def
loadContrasts
(
featdir
):
"""Loads the contrasts from a FEAT directory. Returns a tuple containing:
- A list of names, one for each contrast.
- A list of contrast vectors (each of which is a list itself).
:arg featdir: A FEAT directory.
...
...
@@ -172,7 +172,7 @@ def loadContrasts(featdir):
designcon
=
op
.
join
(
featdir
,
'design.con'
)
log
.
debug
(
'Loading FEAT contrasts from {}'
.
format
(
designcon
))
with
open
(
designcon
,
'rt'
)
as
f
:
while
True
:
...
...
@@ -183,7 +183,7 @@ def loadContrasts(featdir):
num
=
[
c
for
c
in
tkns
[
0
]
if
c
.
isdigit
()]
num
=
int
(
''
.
join
(
num
))
# The /ContrastName field may not
# The /ContrastName field may not
# actually have a name specified
if
len
(
tkns
)
>
1
:
name
=
tkns
[
1
].
strip
()
...
...
@@ -248,7 +248,7 @@ def loadSettings(featdir):
key
=
key
[
5
:
-
1
]
settings
[
key
]
=
val
return
settings
...
...
@@ -259,7 +259,7 @@ def loadDesign(featdir, settings):
:arg settings: Dictionary containing FEAT settings (see
:func:`loadSettings`).
:returns: a :class:`.FEATFSFDesign` instance which represents the
design matrix.
"""
...
...
@@ -273,7 +273,7 @@ def getThresholds(settings):
The following keys will be present. Threshold values will be ``None``
if the respective statistical thresholding was not carried out:
- ``p``: P-value thresholding
- ``z``: Z-statistic thresholding
...
...
@@ -378,14 +378,14 @@ def loadClusterResults(featdir, settings, contrast):
class
Cluster
(
object
):
def
__init__
(
self
,
**
kwargs
):
for
name
,
val
in
kwargs
.
items
():
attrName
=
colmap
[
name
]
if
val
is
not
None
:
val
=
float
(
val
)
setattr
(
self
,
attrName
,
val
)
# This dict provides a mapping between
# This dict provides a mapping between
# Cluster object attribute names, and
# the corresponding column name in the
# cluster.txt file.
...
...
@@ -484,7 +484,7 @@ def getDataFile(featdir):
def
getMelodicFile
(
featdir
):
"""Returns the name of the file in the FEAT results which contains the
"""Returns the name of the file in the FEAT results which contains the
melodic components (if melodic ICA was performed as part of the FEAT
analysis). This file can be loaded as a :class:`.MelodicImage`.
...
...
@@ -505,7 +505,7 @@ def getResidualFile(featdir):
resfile
=
op
.
join
(
featdir
,
'stats'
,
'res4d'
)
return
fslimage
.
addExt
(
resfile
,
mustExist
=
True
)
def
getPEFile
(
featdir
,
ev
):
"""Returns the path of the PE file for the specified EV.