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

Merge branch 'bf/voxelwise_ev' into 'master'

Bf/voxelwise ev

See merge request fsl/fslpy!83
parents 9c452125 ee42060d
No related branches found
No related tags found
No related merge requests found
...@@ -29,6 +29,17 @@ Removed ...@@ -29,6 +29,17 @@ Removed
* Many deprecated items removed. * Many deprecated items removed.
Fixed
^^^^^
* Added a missing ``image`` attribute in the :class:`.VoxelwiseConfoundEV`
class.
* Make sure that FEAT ``Cluster`` objects (created by the
:func:`.loadClusterResults` function) contain ``p`` and ``logp`` attributes,
even when cluster thresholding was not used.
1.13.0 (Thursday 22nd November 2018) 1.13.0 (Thursday 22nd November 2018)
------------------------------------ ------------------------------------
...@@ -36,9 +47,9 @@ Removed ...@@ -36,9 +47,9 @@ Removed
Added Added
^^^^^ ^^^^^
* New wrapper functions for :func:`.fsl_anat`, :func:`.applytopup` (Matrin * New wrapper functions for :func:`.fsl_anat`, :func:`.applytopup` (Martin
Craig). Craig).
* New :func:`.fileOrText` decorator for use in wrapper functions (Matrin * New :func:`.fileOrText` decorator for use in wrapper functions (Martin
Craig). Craig).
......
...@@ -385,6 +385,12 @@ def loadClusterResults(featdir, settings, contrast): ...@@ -385,6 +385,12 @@ def loadClusterResults(featdir, settings, contrast):
setattr(self, attrName, val) setattr(self, attrName, val)
# if cluster thresholding was not used,
# the cluster table will not contain
# P valuse.
if not hasattr(self, 'p'): self.p = 1.0
if not hasattr(self, 'logp'): self.logp = 0.0
# This dict provides a mapping between # This dict provides a mapping between
# Cluster object attribute names, and # Cluster object attribute names, and
# the corresponding column name in the # the corresponding column name in the
......
...@@ -219,6 +219,59 @@ class FEATFSFDesign(object): ...@@ -219,6 +219,59 @@ class FEATFSFDesign(object):
return design return design
class VoxelwiseEVMixin(object):
"""Mixin class for voxelwise EVs.
``VoxelwiseEVMixin`` instances contain the following attributes:
============ ======================================================
``filename`` Path to the image file containing the data for this EV
``image`` Reference to the :class:`.Image` object
============ ======================================================
"""
def __init__(self, filename):
"""Create a ``VoxelwiseEVMixin``.
:arg filename: Path to the file containing the data for this
``VoxelwiseEV``.
"""
if op.exists(filename):
self.__filename = filename
else:
log.warning('Voxelwise EV file does not '
'exist: {}'.format(filename))
self.__filename = None
self.__image = None
def __del__(self):
"""Clears any reference to the voxelwise EV image. """
self.__image = None
@property
def filename(self):
"""Returns the path to the image file containing the data for this EV.
"""
return self.__filename
@property
def image(self):
"""Returns the :class:`.Image` containing the voxelwise EV data. """
if self.__filename is None:
return None
if self.__image is not None:
return self.__image
self.__image = fslimage.Image(self.__filename, mmap=False)
return self.__image
class EV(object): class EV(object):
"""Class representing an explanatory variable in a FEAT design matrix. """Class representing an explanatory variable in a FEAT design matrix.
...@@ -276,7 +329,7 @@ class BasisFunctionEV(NormalEV): ...@@ -276,7 +329,7 @@ class BasisFunctionEV(NormalEV):
pass pass
class VoxelwiseEV(NormalEV): class VoxelwiseEV(NormalEV, VoxelwiseEVMixin):
"""Class representing an EV with different values for each voxel in the """Class representing an EV with different values for each voxel in the
analysis. analysis.
...@@ -308,34 +361,7 @@ class VoxelwiseEV(NormalEV): ...@@ -308,34 +361,7 @@ class VoxelwiseEV(NormalEV):
``VoxelwiseEV``. ``VoxelwiseEV``.
""" """
NormalEV.__init__(self, realIdx, origIdx, title) NormalEV.__init__(self, realIdx, origIdx, title)
VoxelwiseEVMixin.__init__(self, filename)
if op.exists(filename):
self.filename = filename
else:
log.warning('Voxelwise EV file does not '
'exist: {}'.format(filename))
self.filename = None
self.__image = None
def __del__(self):
"""Clears any reference to the voxelwise EV image. """
self.__image = None
@property
def image(self):
"""Returns the :class:`.Image` containing the voxelwise EV data. """
if self.filename is None:
return None
if self.__image is not None:
return self.__image
self.__image = fslimage.Image(self.filename, mmap=False)
return self.__image
class ConfoundEV(EV): class ConfoundEV(EV):
...@@ -386,7 +412,7 @@ class MotionParameterEV(EV): ...@@ -386,7 +412,7 @@ class MotionParameterEV(EV):
self.motionIndex = motionIndex self.motionIndex = motionIndex
class VoxelwiseConfoundEV(EV): class VoxelwiseConfoundEV(EV, VoxelwiseEVMixin):
"""Class representing a voxelwise confound EV. """Class representing a voxelwise confound EV.
``VoxelwiseConfoundEV`` instances contain the following attributes (in ``VoxelwiseConfoundEV`` instances contain the following attributes (in
...@@ -396,6 +422,7 @@ class VoxelwiseConfoundEV(EV): ...@@ -396,6 +422,7 @@ class VoxelwiseConfoundEV(EV):
``voxIndex`` Index of this ``VoxelwiseConfoundEV`` (starting from 0) in ``voxIndex`` Index of this ``VoxelwiseConfoundEV`` (starting from 0) in
relation to all other voxelwise confound EVs. relation to all other voxelwise confound EVs.
``filename`` Path to the image file containing the data for this EV ``filename`` Path to the image file containing the data for this EV
``image`` Reference to the :class:`.Image` object
============ ========================================================== ============ ==========================================================
""" """
def __init__(self, index, voxIndex, title, filename): def __init__(self, index, voxIndex, title, filename):
...@@ -407,17 +434,13 @@ class VoxelwiseConfoundEV(EV): ...@@ -407,17 +434,13 @@ class VoxelwiseConfoundEV(EV):
``VoxelwiseConfoundEV`` in relation to all other ``VoxelwiseConfoundEV`` in relation to all other
voxelwise confound EVs. voxelwise confound EVs.
:arg title: Name of this ``VoxelwiseConfoundEV``. :arg title: Name of this ``VoxelwiseConfoundEV``.
:arg filename: Path to the file containing the data for this
``VoxelwiseConfoundEV``.
""" """
EV.__init__(self, index, title) EV.__init__(self, index, title)
VoxelwiseEVMixin.__init__(self, filename)
self.voxIndex = voxIndex self.voxIndex = voxIndex
if op.exists(filename):
self.filename = filename
else:
log.warning('Voxelwise confound EV file does '
'not exist: {}'.format(filename))
self.filename = None
def getFirstLevelEVs(featDir, settings, designMat): def getFirstLevelEVs(featDir, settings, designMat):
"""Derives the EVs for the given first level FEAT analysis. """Derives the EVs for the given first level FEAT analysis.
......
...@@ -109,8 +109,9 @@ import numpy as np ...@@ -109,8 +109,9 @@ import numpy as np
import pytest import pytest
import tests import tests
import fsl.data.featdesign as featdesign from fsl.utils.tempdir import tempdir
import fsl.data.featanalysis as featanalysis import fsl.data.featdesign as featdesign
import fsl.data.featanalysis as featanalysis
datadir = op.join(op.dirname(__file__), 'testdata', 'test_feat') datadir = op.join(op.dirname(__file__), 'testdata', 'test_feat')
...@@ -397,3 +398,17 @@ def test_loadDesignMat(): ...@@ -397,3 +398,17 @@ def test_loadDesignMat():
with pytest.raises(Exception): with pytest.raises(Exception):
featdesign.loadDesignMat(badfile) featdesign.loadDesignMat(badfile)
def test_VoxelwiseEVs():
with tempdir():
img = tests.make_random_image('image.nii.gz', (10, 10, 10, 10))
ev1 = featdesign.VoxelwiseEV( 0, 0, 'ev1', 'image.nii.gz')
ev2 = featdesign.VoxelwiseConfoundEV(0, 0, 'ev2', 'image.nii.gz')
for xyz in tests.random_voxels((10, 10, 10), 10):
x, y, z = map(int, xyz)
exp = img.dataobj[x, y, z, :]
assert np.all(ev1.image[x, y, z, :] == exp)
assert np.all(ev2.image[x, y, z, :] == exp)
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