From b0271a872a9a65332a5f4bc20b4ed181d8a32398 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Tue, 1 Sep 2015 15:49:42 +0100
Subject: [PATCH] Documentation for all modules in the fsl.data package.

---
 doc/fsl.data.rst                     |  16 --
 fsl/data/__init__.py                 |  16 +-
 fsl/data/atlases.py                  | 246 ++++++++++++++++++++-------
 fsl/data/constants.py                |  76 +++++++--
 fsl/data/featimage.py                | 103 +++++++++--
 fsl/data/featresults.py              | 143 +++++++++++++---
 fsl/data/image.py                    | 190 ++++++++++++---------
 fsl/data/model.py                    | 136 ++++++++++-----
 fsl/data/strings.py                  |  23 ++-
 fsl/fsleyes/views/timeseriespanel.py |   2 +-
 fsl/utils/colourbarbitmap.py         |   2 +-
 11 files changed, 710 insertions(+), 243 deletions(-)

diff --git a/doc/fsl.data.rst b/doc/fsl.data.rst
index f768559d5..31516eabd 100644
--- a/doc/fsl.data.rst
+++ b/doc/fsl.data.rst
@@ -1,22 +1,6 @@
 fsl.data package
 ================
 
-Submodules
-----------
-
-.. toctree::
-
-   fsl.data.atlases
-   fsl.data.constants
-   fsl.data.featimage
-   fsl.data.featresults
-   fsl.data.image
-   fsl.data.model
-   fsl.data.strings
-
-Module contents
----------------
-
 .. automodule:: fsl.data
     :members:
     :undoc-members:
diff --git a/fsl/data/__init__.py b/fsl/data/__init__.py
index 27a9696ea..7372461c5 100644
--- a/fsl/data/__init__.py
+++ b/fsl/data/__init__.py
@@ -4,4 +4,18 @@
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
-"""Data structures and models."""
+"""This module contains various data types and associated I/O routines,
+models, constants, and other data-like things used throughout ``fslpy``.
+
+
+ .. autosummary::
+    :nosignatures:
+
+    ~fsl.data.image.Image
+    ~fsl.data.featimage.FEATImage
+    ~fsl.data.model.Model
+    ~fsl.data.featresults
+    ~fsl.data.atlases
+    ~fsl.data.strings
+    ~fsl.data.constants
+"""
diff --git a/fsl/data/atlases.py b/fsl/data/atlases.py
index dd5165a7b..b747ae867 100644
--- a/fsl/data/atlases.py
+++ b/fsl/data/atlases.py
@@ -6,45 +6,82 @@
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
 """This module provides access to the atlas images which are contained in
-``$FSLDIR/data/atlases/``.
-
-Instances of the :class:`Atlas` class is a
-
-MNI152
-
-
-<atlas>
-  <header>
-    <name></name>        # Atlas name
-    <type></type>        # 'Probabilistic' or 'Label'
-    <images>
-     <imagefile>
-     </imagefile>        # If type is Probabilistic, path
-                         # to 4D image file, one volume per
-                         # label, Otherwise, if type is
-                         # Label, path to 3D label file
-                         # (identical to the summaryimagefile
-                         # below)
-                         
-     <summaryimagefile>  # Path to 3D summary file, with each 
-     </summaryimagefile> # region having value (index + 1)
-
-    </images>
-    ...                  # More images - generally both
-                         # 1mm and 2mm  versions (in
-                         # MNI152 space) are available
-  </header>
- <data>
-  # index - index of corresponding volume in 4D image file
-  # x    |
-  # y    |- XYZ *voxel* coordinates into the first image of the <images> list
-  # z    |
-  <label index="0" x="0" y="0" z="0">Name</label>
-  ...
- </data>
-</atlas>
+``$FSLDIR/data/atlases/``. This directory contains XML files which describe
+all of the available atlases.  An XML atlas description file is assumed to
+have a structure that looks like the following:
+
+.. code-block:: xml
+
+   <atlas>
+     <header>
+       <name></name>        # Atlas name
+       <type></type>        # 'Probabilistic' or 'Label'
+       <images>
+        <imagefile>
+        </imagefile>        # If type is Probabilistic, path
+                            # to 4D image file, one volume per
+                            # label, Otherwise, if type is
+                            # Label, path to 3D label file
+                            # (identical to the summaryimagefile
+                            # below)
+
+        <summaryimagefile>  # Path to 3D summary file, with each 
+        </summaryimagefile> # region having value (index + 1)
+
+       </images>
+       ...                  # More images - generally both
+                            # 1mm and 2mm  versions (in
+                            # MNI152 space) are available
+     </header>
+    <data>
+
+     # 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
+     # z    |
+     <label index="0" x="0" y="0" z="0">Name</label>
+     ...
+    </data>
+   </atlas>
+
+
+This module reads in all of these XML files, and builds a list of
+:class:`AtlasDescription` instances, each of which contains information about
+one atlas. Each atlas is assigned an identifier, which is simply the XML file
+name describing the atlas, sans-suffix.  For exmaple, the atlas described by:
+
+    ``$FSLDIR/data/atlases/HarvardOxford-Cortical.xml``
+
+is given the identifier
+
+    ``HarvardOxford-Cortical``
+
+
+The following functions provide access to the available
+:class:`AtlasDescription` instances:
+
+.. autosummary::
+   :nosignatures:
+
+   listAtlases
+   getAtlasDescription
+
+
+The :func:`loadAtlas` function allows you to load an atlas image, which will
+be one of the following  atlas-specific :class:`.Image` sub-classes:
+
+.. autosummary::
+   :nosignatures:
+
+   LabelAtlas
+   ProbabilisticAtlas
 """
 
+
 import                          os
 import xml.etree.ElementTree as et
 import os.path               as op
@@ -61,30 +98,14 @@ import fsl.utils.transform   as transform
 
 log = logging.getLogger(__name__)
 
-ATLAS_DIR = None
-
-def _setAtlasDir():
-    global ATLAS_DIR
-
-    if ATLAS_DIR is not None:
-        return
-    
-    if os.environ.get('FSLDIR', None) is None:
-        log.warn('$FSLDIR is not set - atlases are not available')
-    else:
-        ATLAS_DIR = op.join(os.environ['FSLDIR'], 'data', 'atlases')
-
 
-ATLAS_DESCRIPTIONS = collections.OrderedDict()
-
-    
 def listAtlases(refresh=False):
     """Returns a dictionary containing :class:`AtlasDescription` objects for
     all available atlases.
 
     :arg refresh: If ``True``, or if the atlas desriptions have not
                   previously been loaded, atlas descriptions are
-                  loaded from the atlas files. Otherwise, prefviously
+                  loaded from the atlas files. Otherwise, previously
                   loaded descriptions are returned (see 
                   :attr:`ATLAS_DESCRIPTIONS`).
     """
@@ -109,7 +130,6 @@ def listAtlases(refresh=False):
     for i, desc in enumerate(atlasDescs):
         desc.index                       = i
         ATLAS_DESCRIPTIONS[desc.atlasID] = desc
-        
 
     return atlasDescs
 
@@ -161,12 +181,50 @@ def loadAtlas(atlasID, loadSummary=False):
 
 
 class AtlasDescription(object):
-    """Loads the data stored in an Atlas XML description, and makes said
-    information accessible via instance attributes.
+    """An ``AtlasDescription`` instance parses and stores the information
+    stored in the XML file that describes one atlas.
+
+    The following attributes are available on an ``AtlasDescription`` instance:
+
+    ================= ======================================================
+    ``atlasID``       The atlas ID, as described above.
+    ``name``          Name of the atlas.
+    ``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.
+    ``labels``        A list of ``AtlasLabel`` objects, describing each
+                      region / label in the atlas.
+    ================= ======================================================
+
+    Each ``AtlasLabel`` instance in the ``labels`` list contains the
+    following attributes:
+
+    ========= ==============================================================
+    ``name``  Region name
+    ``index`` For probabilistic atlases, the volume index into the 4D atlas
+              image that corresponds to this region. For label atlases, the
+              value of voxels that are in this region. For summary images of
+              probabilistic atlases, add 1 to this value to get the
+              corresponding voxel values.
+    ``x``     X coordinate of the region in world space
+    ``y``     Y coordinate of the region in world space
+    ``z``     Z coordinate of the region in world space
+    ========= ==============================================================
+
+    .. note:: The ``x``, ``y`` and ``z`` label coordinates are pre-calculated
+              centre-of-gravity coordinates, as listed in the atlas xml file.
+              They are in the coordinate system defined by the atlas image
+              transformation matrix (typically MNI152 space).
     """
 
     
     def __init__(self, filename):
+        """Create an ``AtlasDescription`` instance.
+
+        :arg filename: Name of the XML file describing the atlas.
+        """
 
         log.debug('Loading atlas description from {}'.format(filename))
 
@@ -237,8 +295,21 @@ class AtlasDescription(object):
 
 
 class Atlas(fslimage.Image):
+    """This is the base class for the :class:`LabelAtlas` and
+    :class:`ProbabilisticAtlas` classes. It contains some initialisation
+    logic common to both.
+    """
+
     
     def __init__(self, atlasDesc, isLabel=False):
+        """Initialise an ``Atlas``.
+
+        :arg atlasDesc: The :class:`AtlasDescription` instance which describes
+                        the atlas.
+
+        :arg isLabel:   Pass in ``True`` for label atlases, ``False`` for
+                        probabilistic atlases.
+        """
 
         # Choose the atlas image
         # with the highest resolution 
@@ -262,17 +333,30 @@ class Atlas(fslimage.Image):
         # their sform_codes are correctly set
         self.nibImage.get_header().set_sform(
             None, code=constants.NIFTI_XFORM_MNI_152)
-                    
 
         self.desc = atlasDesc
 
         
 class LabelAtlas(Atlas):
+    """A 3D atlas which contains integer labels for each region.
+
+    The ``LabelAtlas`` class provides the :meth:`label` method, which
+    makes looking up the label at a location easy.
+    """
 
     def __init__(self, atlasDesc):
+        """Create a ``LabelAtlas`` instance.
+
+        :arg atlasDesc: The :class:`AtlasDescription` instance describing
+                        the atlas.
+        """
         Atlas.__init__(self, atlasDesc, isLabel=True)
 
+        
     def label(self, worldLoc):
+        """Looks up and returns the label of the region at the given world
+        location, or ``np.nan`` if the location is out of bounds.
+        """
 
         voxelLoc = transform.transform([worldLoc], self.worldToVoxMat.T)[0]
 
@@ -294,12 +378,27 @@ class LabelAtlas(Atlas):
 
     
 class ProbabilisticAtlas(Atlas):
+    """A 4D atlas which contains one volume for each region.
+
+    The ``ProbabilisticAtlas`` provides the :meth`proportions` method,
+    which makes looking up region probabilities easy.
+    """
 
     def __init__(self, atlasDesc):
+        """Create a ``ProbabilisticAtlas`` instance.
+
+        :arg atlasDesc: The :class:`AtlasDescription` instance describing
+                        the atlas.
+        """ 
         Atlas.__init__(self, atlasDesc, isLabel=False)
 
         
     def proportions(self, worldLoc):
+        """:returns: a list of values, one per region, which represent
+                     the probability of each region for the given world
+                     location. Returns an empty list if the given
+                     location is out of bounds.
+        """
         voxelLoc = transform.transform([worldLoc], self.worldToVoxMat.T)[0]
 
         if voxelLoc[0] <  0             or \
@@ -311,3 +410,34 @@ class ProbabilisticAtlas(Atlas):
             return []
         
         return self.data[voxelLoc[0], voxelLoc[1], voxelLoc[2], :]
+
+
+
+ATLAS_DIR = None
+"""This attribute stores the absolute path to ``$FSLDIR/data/atlases/``. It is
+``None`` if ``$FSLDIR`` is not set. See :func:`_setAtlasDir`.
+"""
+
+
+ATLAS_DESCRIPTIONS = collections.OrderedDict()
+"""This dictionary contains an ``{atlasID : AtlasDescription}`` mapping for
+all atlases contained in ``$FSLDIR/data/atlases/``.
+"""
+
+
+def _setAtlasDir():
+    """Called by the :func:`listAtlases`, :func:`getAtlasDescription` and
+    :func:`loadAtlas` functions.
+
+    Sets the :data:`ATLAS_DIR` attribute if it has not already been set, and
+    if the ``$FSLDIR`` environment variable is set.
+    """
+    global ATLAS_DIR
+
+    if ATLAS_DIR is not None:
+        return
+    
+    if os.environ.get('FSLDIR', None) is None:
+        log.warn('$FSLDIR is not set - atlases are not available')
+    else:
+        ATLAS_DIR = op.join(os.environ['FSLDIR'], 'data', 'atlases')
diff --git a/fsl/data/constants.py b/fsl/data/constants.py
index d2fa0bfde..52a2f8200 100644
--- a/fsl/data/constants.py
+++ b/fsl/data/constants.py
@@ -4,22 +4,78 @@
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module defines some constant values used throughout ``fsleyes``.
+
+
+The following constants relate to the orientation of an axis, in either
+voxel or world space:
+
+.. autosummary::
+   ORIENT_L2R
+   ORIENT_R2L
+   ORIENT_P2A
+   ORIENT_A2P
+   ORIENT_I2S
+   ORIENT_S2I
+   ORIENT_UNKNOWN
+
+
+These constants relate to the *space* in which a NIFTI1 image is assumed to be
+(i.e. the transformed coordinate space); they are defined in the NIFTI1
+specification:
+
+.. autosummary::
+   NIFTI_XFORM_UNKNOWN
+   NIFTI_XFORM_SCANNER_ANAT
+   NIFTI_XFORM_ALIGNED_ANAT
+   NIFTI_XFORM_TALAIRACH
+   NIFTI_XFORM_MNI_152
+"""
+
+
+ORIENT_L2R     =  0
+"""The axis goes from left to right."""
+
+
+ORIENT_R2L     =  1
+"""The axis goes from right to left."""
+
+
+ORIENT_P2A     =  2
+"""The axis goes from posterior to anterior."""
+
+
+ORIENT_A2P     =  3
+"""The axis goes from anterior to posterior."""
+
+
+ORIENT_I2S     =  4
+"""The axis goes from inferior to superior."""
+
+
+ORIENT_S2I     =  5
+"""The axis goes from superior to inferior."""
+
 
-# Constants which represent the orientation
-# of an axis, in either voxel or world space.
 ORIENT_UNKNOWN = -1
-ORIENT_L2R     = 0
-ORIENT_R2L     = 1
-ORIENT_P2A     = 2
-ORIENT_A2P     = 3
-ORIENT_I2S     = 4
-ORIENT_S2I     = 5
+"""The axis has an unknown orientation."""
 
 
-# Constants from the NIFTI1 specification that define
-# the 'space' in which an image is assumed to be.
 NIFTI_XFORM_UNKNOWN      = 0
+"""Arbitrary coordinates."""
+
+
 NIFTI_XFORM_SCANNER_ANAT = 1
+"""Scanner-based anatomical coordinates."""
+
+
 NIFTI_XFORM_ALIGNED_ANAT = 2
+"""Coordinates aligned to another file's, or to anatomical "truth"."""
+
+
 NIFTI_XFORM_TALAIRACH    = 3
+"""Coordinates aligned to Talairach-Tournoux Atlas; (0,0,0)=AC, etc."""
+
+
 NIFTI_XFORM_MNI_152      = 4
+"""MNI 152 normalized coordinates."""
diff --git a/fsl/data/featimage.py b/fsl/data/featimage.py
index c87121f21..59d409158 100644
--- a/fsl/data/featimage.py
+++ b/fsl/data/featimage.py
@@ -5,10 +5,10 @@
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
 """This module provides the :class:`FEATImage` class, a subclass of
-:class:`.Image` designed for the ``filtered_func_data`` file of a FEAT
-analysis.
+:class:`.Image` designed to encapsulate data from a FEAT analysis.
 """
 
+
 import os.path as op
 
 import numpy   as np
@@ -18,11 +18,49 @@ import            featresults
 
 
 class FEATImage(fslimage.Image):
+    """An ``Image`` from a FEAT analysis.
+
+    The :class:`FEATImage` class makes use of the functions defined in the
+    :mod:`.featresults` module.
+
+
+    An example of using the ``FEATImage`` class::
+
+        import fsl.data.featimage as featimage
+
+        # You can pass in the name of the
+        # .feat/.gfeat directory, or any
+        # file contained within that directory.
+        img = featimage.FEATImage('myanalysis.feat/filtered_func_data.nii.gz')
+
+        # Query information about the FEAT analysis
+        print img.numEVs()
+        print img.contrastNames()
+        print img.numPoints()
+
+        # Get the model fit residuals
+        res4d = img.getResiduals()
+
+        # Get the full model fit for voxel
+        # [23, 30, 42] (in this example, we
+        # have 4 EVs - the first argument
+        # is a contrast vector).
+        img.fit([1, 1, 1, 1], [23, 30, 42], fullmodel=True)
+    """
+    
 
     def __init__(self, path, **kwargs):
-        """
-        The specified ``path`` may be a FEAT analysis directory, or the model
-        data input file (e.g. ``analysis.feat/filtered_func_data.nii.gz``).
+        """Create a ``FEATImage`` instance.
+
+        :arg path:   A FEAT analysis directory, or an image file contained
+                     within such a directory.
+
+        :arg kwargs: Passed to the :meth:`.Image.__init__` constructor.
+
+        .. note:: If a FEAT directory is passed in for the ``path``
+                  argument, this ``FEATImage`` instance will encapsulate
+                  the model input data, typically called
+                  ``<directory>.feat/filtered_func_data.nii.gz``.
         """
 
         featDir = featresults.getFEATDir(path)
@@ -59,53 +97,82 @@ class FEATImage(fslimage.Image):
 
 
     def getFEATDir(self):
+        """Returns the FEAT directory this image is contained in."""
         return self.__featDir
 
 
     def getAnalysisName(self):
+        """Returns the FEAT analysis name, which is the FEAT directory
+        name, minus the ``.feat`` / ``.gfeat`` suffix.
+        """
         return self.__analysisName
         
 
     def getDesign(self):
+        """Returns the analysis design matrix as a :mod:`numpy` array
+        with shape :math:`numPoints\\times numEVs`.
+        """
         return np.array(self.__design)
         
     
     def numPoints(self):
+        """Returns the number of points (e.g. time points, number of
+        subjects, etc) in the analysis.
+        """
         return self.__design.shape[0] 
 
     
     def numEVs(self):
+        """Returns the number of explanatory variables (EVs) in the analysis.
+        """
         return self.__design.shape[1]
 
 
     def evNames(self):
+        """Returns a list containing the name of each EV in the analysis."""
         return list(self.__evNames)
 
     
     def numContrasts(self):
+        """Returns the number of contrasts in the analysis."""
         return len(self.__contrasts)
 
     
     def contrastNames(self):
+        """Returns a list containing the name of each contrast in the analysis.
+        """
         return list(self.__contrastNames)
 
 
     def contrasts(self):
+        """Returns a list containing the analysis contrast vectors.
+
+        See :func:`.featresults.loadContrasts`
+
+        """
         return [list(c) for c in self.__contrasts]
 
 
     def thresholds(self):
+        """Returns the statistical thresholds used in the analysis.
+
+        See :func:`.featresults.getThresholds`
+        """
         return featresults.getThresholds(self.__settings)
 
 
     def clusterResults(self, contrast):
+        """Returns the clusters found in the analysis.
 
+        See :func:.featresults.loadClusterResults`
+        """
         return featresults.loadClusterResults(self.__featDir,
                                               self.__settings,
                                               contrast)
 
 
     def getPE(self, ev):
+        """Returns the PE image for the given EV (0-indexed). """
 
         if self.__pes[ev] is None:
             pefile = featresults.getPEFile(self.__featDir, ev)
@@ -120,6 +187,7 @@ class FEATImage(fslimage.Image):
 
 
     def getResiduals(self):
+        """Returns the residuals of the full model fit. """
         
         if self.__residuals is None:
             resfile = featresults.getResidualFile(self.__featDir)
@@ -131,6 +199,7 @@ class FEATImage(fslimage.Image):
 
     
     def getCOPE(self, con):
+        """Returns the COPE image for the given contrast (0-indexed). """
         
         if self.__copes[con] is None:
             copefile = featresults.getPEFile(self.__featDir, con)
@@ -145,6 +214,8 @@ class FEATImage(fslimage.Image):
 
 
     def getZStats(self, con):
+        """Returns the Z statistic image for the given contrast (0-indexed).
+        """
         
         if self.__zstats[con] is None:
             zfile = featresults.getZStatFile(self.__featDir, con)
@@ -160,6 +231,8 @@ class FEATImage(fslimage.Image):
 
 
     def getClusterMask(self, con):
+        """Returns the cluster mask image for the given contrast (0-indexed).
+        """
         
         if self.__clustMasks[con] is None:
             mfile = featresults.getClusterMaskFile(self.__featDir, con)
@@ -175,12 +248,22 @@ class FEATImage(fslimage.Image):
             
 
     def fit(self, contrast, xyz, fullmodel=False):
-        """
+        """Calculates the model fit for the given contrast vector
+        at the given voxel.
 
         Passing in a contrast of all 1s, and ``fullmodel=True`` will
         get you the full model fit. Pass in ``fullmodel=False`` for
         all other contrasts, otherwise the model fit values will not
         be scaled correctly.
+
+        :arg contrast:  The contrast vector (pass all 1s for a full model
+                        fit).
+
+        :arg xyz:       Coordinates of the voxel to calculate the model fit
+                        for.
+
+        :arg fullmodel: Set to ``True`` for a full model fit, ``False``
+                        otherwise.
         """
 
         if not fullmodel:
@@ -205,11 +288,11 @@ class FEATImage(fslimage.Image):
         return modelfit + data.mean()
 
 
-    def reducedData(self, xyz, contrast, fullmodel=False):
-        """
+    def partialFit(self, contrast, xyz, fullmodel=False):
+        """Calculates and returns the partial model fit for the specified
+        contrast vector at the specified voxel.
 
-        Passing in a contrast of all 1s, and ``fullmodel=True`` will
-        get you the model fit residuals.
+        See :meth:`fit` for details on the arguments.
         """
 
         x, y, z   = xyz
diff --git a/fsl/data/featresults.py b/fsl/data/featresults.py
index 4d3ae675f..1a65c2ce9 100644
--- a/fsl/data/featresults.py
+++ b/fsl/data/featresults.py
@@ -6,7 +6,34 @@
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
 """This module provides a few utility functions for loading/querying the
-contents of a FEAT analysis directory.
+contents of a FEAT analysis directory. They are primarily for use by the
+:class:`.FEATImage` class, but are available for other uses if needed. The
+following functions are provided:
+
+.. autosummary::
+   :nosignatures:
+
+   isFEATDir
+   getFEATDir
+   loadDesign
+   loadContrasts
+   loadSettings
+   getEVNames
+   getThresholds
+   loadClusterResults
+
+
+The following functions return the names of various files of interest:
+
+.. autosummary::
+   :nosignatures:
+
+   getDataFile
+   getResidualFile
+   getPEFile
+   getCOPEFile
+   getZStatFile
+   getClusterMaskFile
 """
 
 
@@ -25,8 +52,9 @@ log = logging.getLogger(__name__)
 def isFEATDir(path):
     """Returns ``True`` if the given path looks like a FEAT directory, or
     looks like the input data for a FEAT analysis, ``False`` otherwise.
-    """
 
+    :arg path: A file / directory path.
+    """
 
     dirname, filename = op.split(path)
 
@@ -50,6 +78,15 @@ def isFEATDir(path):
 
 
 def getFEATDir(path):
+    """Given the path of any file/directory which is within a ``.feat`` or
+    ``.gfeat`` directory, strips all trailing components of the path name,
+    and returns the root FEAT directory.
+    
+    Returns ``None`` if the given path is not contained within a ``.feat``
+    or ``.gfeat`` directory.
+
+    :arg path: A file / directory path.
+    """
 
     sufs     = ['.feat', '.gfeat']
     idxs     = [(path.rfind(s), s) for s in sufs]
@@ -68,10 +105,12 @@ def getFEATDir(path):
 
 
 def loadDesign(featdir):
-    """Loads the design matrix from a FEAT folder.
+    """Loads the design matrix from a FEAT directory.
 
     Returns a ``numpy`` array containing the design matrix data, where the
     first dimension corresponds to the data points, and the second to the EVs.
+
+    :arg featdir: A FEAT directory.
     """
 
     matrix    = None 
@@ -96,11 +135,14 @@ def loadDesign(featdir):
 
 
 def loadContrasts(featdir):
-    """Loads the contrasts from a FEAT folder. Returns a tuple containing:
+    """Loads the contrasts from a FEAT directory. Returns a tuple containing:
     
-      - A dictionary of ``{contrastnum : name}`` mappings
+      - A dictionary of ``{contrastnum : name}`` mappings (the ``contrastnum``
+        values are 1-indexed).
     
       - A list of contrast vectors (each of which is a list itself).
+
+    :arg featdir: A FEAT directory.
     """
 
     matrix       = None
@@ -151,9 +193,12 @@ def loadContrasts(featdir):
 
 
 def loadSettings(featdir):
-    """Loads the analysis settings from a a FEAT folder.
+    """Loads the analysis settings from a FEAT directory.
+
+    Returns a dict containing the settings specified in the ``design.fsf``
+    file within the directory
 
-    Returns a dict containing the settings specified in the given file.
+    :arg featdir: A FEAT directory.
     """
 
     settings  = {}
@@ -183,6 +228,18 @@ def loadSettings(featdir):
 
 
 def getThresholds(settings):
+    """Given a FEAT settings dictionary, returns a dictionary of
+    ``{stat : threshold}`` mappings, containing the thresholds used
+    in the FEAT statistical analysis.
+
+    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
+
+    :arg settings: A FEAT settings dictionary (see :func:`loadSettings`).
+    """
     return {
         'p' : settings.get('prob_thresh', None),
         'z' : settings.get('z_thresh',    None)
@@ -191,13 +248,43 @@ def getThresholds(settings):
 
 def loadClusterResults(featdir, settings, contrast):
     """If cluster thresholding was used in the FEAT analysis, this function
-    will load and return the cluster results for the specified contrast
-    (which is assumed to be 0-indexed).
+    will load and return the cluster results for the specified (0-indexed)
+    contrast number.
 
-    If there are no cluster results for the given contrast,
-    ``None`` is returned.
+    If there are no cluster results for the given contrast, ``None`` is
+    returned.
 
     An error will be raised if the cluster file cannot be parsed.
+
+    :arg featdir:  A FEAT directory.
+    :arg settings: A FEAT settings dictionary.
+    :arg contrast: 0-indexed contrast number.
+
+    :returns:      A list of ``Cluster`` instances, each of which contains
+                   information about one cluster. A ``Cluster`` object has the
+                   following attributes:
+
+                     ============ =========================================
+                     ``index``    Cluster index.
+                     ``nvoxels``  Number of voxels in cluster.
+                     ``p``        Cluster p value.
+                     ``logp``     :math:`-log_{10}` of the cluster P value.
+                     ``zmax``     Maximum Z value in cluster.
+                     ``zmaxx``    X voxel coordinate of maximum Z value.
+                     ``zmaxy``    Y voxel coordinate of maximum Z value.
+                     ``zmaxz``    Z voxel coordinate of maximum Z value.
+                     ``zcogx``    X voxel coordinate of cluster centre of
+                                  gravity.
+                     ``zcogy``    Y voxel coordinate of cluster centre of
+                                  gravity.
+                     ``zcogz``    Z voxel coordinate of cluster centre of
+                                  gravity.
+                     ``copemax``  Maximum COPE value in cluster.
+                     ``copemaxx`` X voxel coordinate of maximum COPE value.
+                     ``copemaxy`` Y voxel coordinate of maximum COPE value.
+                     ``copemaxz`` Z voxel coordinate of maximum COPE value.
+                     ``copemean`` Mean COPE of all voxels in the cluster.
+                     ============ =========================================
     """
 
     # Cluster files are named like
@@ -209,7 +296,6 @@ def loadClusterResults(featdir, settings, contrast):
     clusterFile = op.join(
         featdir, 'cluster_zstat{}.txt'.format(contrast + 1))
 
-
     if not op.exists(clusterFile):
 
         # If the analysis was performed in standard
@@ -334,8 +420,10 @@ def loadClusterResults(featdir, settings, contrast):
 
 
 def getDataFile(featdir):
-    """Returns the name of the file in the FEAT results which contains
+    """Returns the name of the file in the FEAT directory which contains
     the model input data (typically called ``filtered_func_data.nii.gz``).
+
+    :arg featdir: A FEAT directory.
     """
     
     # Assuming here that there is only
@@ -346,6 +434,8 @@ def getDataFile(featdir):
 def getResidualFile(featdir):
     """Returns the name of the file in the FEAT results which contains
     the model fit residuals (typically called ``res4d.nii.gz``).
+
+    :arg featdir: A FEAT directory.
     """
     
     # Assuming here that there is only
@@ -354,33 +444,40 @@ def getResidualFile(featdir):
 
     
 def getPEFile(featdir, ev):
-    """Returns the path of the PE file for the specified ``ev``, which is
-    assumed to be 0-indexed. 
-    """
+    """Returns the path of the PE file for the specified EV.
 
+    :arg featdir: A FEAT directory.
+    :arg ev:      The EV number (0-indexed).
+    """
     pefile = op.join(featdir, 'stats', 'pe{}.*'.format(ev + 1))
     return glob.glob(pefile)[0]
 
 
 def getCOPEFile(featdir, contrast):
-    """Returns the path of the COPE file for the specified ``contrast``, which
-    is assumed to be 0-indexed. 
+    """Returns the path of the COPE file for the specified contrast.
+
+    :arg featdir:  A FEAT directory.
+    :arg contrast: The contrast number (0-indexed). 
     """
     copefile = op.join(featdir, 'stats', 'cope{}.*'.format(contrast + 1))
     return glob.glob(copefile)[0]
 
 
 def getZStatFile(featdir, contrast):
-    """Returns the path of the Z-statistic file for the specified
-    ``contrast``, which is assumed to be 0-indexed. 
+    """Returns the path of the Z-statistic file for the specified contrast.
+
+    :arg featdir:  A FEAT directory.
+    :arg contrast: The contrast number (0-indexed). 
     """
     zfile = op.join(featdir, 'stats', 'zstat{}.*'.format(contrast + 1))
     return glob.glob(zfile)[0]
 
 
 def getClusterMaskFile(featdir, contrast):
-    """Returns the path of the cluster mask file for the specified
-    ``contrast``, which is assumed to be 0-indexed. 
+    """Returns the path of the cluster mask file for the specified contrast.
+
+    :arg featdir:  A FEAT directory.
+    :arg contrast: The contrast number (0-indexed). 
     """
     mfile = op.join(featdir, 'cluster_mask_zstat{}.*'.format(contrast + 1))
     return glob.glob(mfile)[0]
@@ -392,6 +489,8 @@ def getEVNames(settings):
 
     An error of some sort will be raised if the EV names cannot be determined
     from the FEAT settings.
+
+    :arg settings: A FEAT settings dictionary (see :func:`loadSettings`). 
     """
 
     numEVs = int(settings['evs_real'])
diff --git a/fsl/data/image.py b/fsl/data/image.py
index 87ae8d265..2547614fd 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -5,9 +5,32 @@
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
-"""Provides the :class:`Image` class, for representing 3D/4D NIFTI images.
+"""This module provides the :class:`Image` class, for representing 3D/4D NIFTI1
+images. The ``nibabel`` package is used for file I/O.
+
+.. note:: Currently, only NIFTI1 images are supported.
+
+
+It is very easy to load a NIFTI image::
+
+    from fsl.data.image import Image
+    myimg = Image('MNI152_T1_2mm.nii.gz')
+
+
+A number of other functions are also provided for working with image files and
+file names:
+
+.. autosummary::
+   :nosignatures:
+
+   isSupported
+   removeExt
+   addExt
+   loadImage
+   saveImage
 """
 
+
 import               logging
 import               tempfile
 import               os 
@@ -28,30 +51,28 @@ class Image(props.HasProperties):
     """Class which represents a 3D/4D image. Internally, the image is
     loaded/stored using :mod:`nibabel`.
 
-    In addition to the class-level properties defined below, the following
-    attributes are present on an :class:`Image` object:
-
-    :ivar nibImage:       The :mod:`nibabel` image object.
-
-    :ivar shape:          A list/tuple containing the number of voxels
-                          along each image dimension.
-
-    :ivar pixdim:         A list/tuple containing the size of one voxel
-                          along each image dimension.
-
-    :ivar voxToWorldMat:  A 4*4 array specifying the affine transformation
-                          for transforming voxel coordinates into real world
-                          coordinates.
-
-    :ivar worldToVoxMat:  A 4*4 array specifying the affine transformation
-                          for transforming real world coordinates into voxel
-                          coordinates.
-
-    :ivar dataSource:     The name of the file that the image was loaded from.
-
-    :ivar tempFile:       The name of the temporary file which was created (in
-                          the event that the image was large and was gzipped -
-                          see :func:`_loadImageFile`).
+    
+    In addition to the :attr:`name`, :attr:`data`, and :attr:`saved`
+    properties, the following attributes are present on an ``Image`` instance:
+
+
+    ================= ====================================================
+    ``nibImage``      The :mod:`nibabel` image object.
+    ``dataSource``    The name of the file that the image was loaded from.
+    ``tempFile``      The name of the temporary file which was created (in
+                      the event that the image was large and was gzipped -
+                      see :func:`loadImage`).
+    ``shape``         A list/tuple containing the number of voxels along
+                      each image dimension.
+    ``pixdim``        A list/tuple containing the size of one voxel along
+                      each image dimension.
+    ``voxToWorldMat`` A 4*4 array specifying the affine transformation
+                      for transforming voxel coordinates into real world
+                      coordinates.
+    ``worldToVoxMat`` A 4*4 array specifying the affine transformation
+                      for transforming real world coordinates into voxel
+                      coordinates.
+    ================= ====================================================
     """
 
 
@@ -77,14 +98,15 @@ class Image(props.HasProperties):
                  name=None,
                  header=None,
                  loadData=True):
-        """Initialise an Image object with the given image data or file name.
+        """Create an ``Image`` object with the given image data or file name.
 
         :arg image:    A string containing the name of an image file to load, 
                        or a :mod:`numpy` array, or a :mod:`nibabel` image
                        object.
 
-        :arg xform:    A ``4*4`` affine transformation matrix which transforms
-                       voxel coordinates into real world coordinates.
+        :arg xform:    A :math:`4\\times 4` affine transformation matrix 
+                       which transforms voxel coordinates into real world
+                       coordinates.
 
         :arg name:     A name for the image.
 
@@ -100,9 +122,13 @@ class Image(props.HasProperties):
                        via the :meth:`loadData` method.
         """
 
-        self.nibImage   = None
-        self.dataSource = None
-        self.tempFile   = None
+        self.nibImage      = None
+        self.dataSource    = None
+        self.tempFile      = None
+        self.shape         = None
+        self.pixdim        = None
+        self.voxToWorldMat = None
+        self.worldToVoxMat = None
 
         if header is not None:
             header = header.copy()
@@ -173,8 +199,28 @@ class Image(props.HasProperties):
 
         
     def __del__(self):
+        """Prints a log message. """
         log.memory('{}.del ({})'.format(type(self).__name__, id(self)))
+
+
+    def __hash__(self):
+        """Returns a number which uniquely idenfities this ``Image`` instance
+        (the result of ``id(self)``).
+        """
+        return id(self)
+
+
+    def __str__(self):
+        """Return a string representation of this ``Image`` instance."""
+        return '{}({}, {})'.format(self.__class__.__name__,
+                                   self.name,
+                                   self.dataSource)
+
         
+    def __repr__(self):
+        """See the :meth:`__str__` method."""
+        return self.__str__()
+
         
     def loadData(self):
         """Loads the image data from the file. This method only needs to
@@ -253,28 +299,8 @@ class Image(props.HasProperties):
         return saveImage(self)
     
 
-    def __hash__(self):
-        """Returns a number which uniquely idenfities this :class:`Image`
-        object (the result of ``id(self)``).
-        """
-        return id(self)
-
-
-    def __str__(self):
-        """Return a string representation of this :class:`Image`."""
-        return '{}({}, {})'.format(self.__class__.__name__,
-                                   self.name,
-                                   self.dataSource)
-
-        
-    def __repr__(self):
-        """See the :meth:`__str__` method."""
-        return self.__str__()
-
-
     def is4DImage(self):
-        """Returns ``True`` if this image is 4D, ``False`` otherwise.
-        """
+        """Returns ``True`` if this image is 4D, ``False`` otherwise. """
         return len(self.shape) > 3 and self.shape[3] > 1
 
 
@@ -283,7 +309,14 @@ class Image(props.HasProperties):
         indicating the space to which the (transformed) image is oriented.
 
         The ``code`` parameter may be either ``sform`` (the default) or
-        ``qform`` in which case the corresponding matrix is used. 
+        ``qform`` in which case the corresponding matrix is used.
+
+        :returns: one of the following codes:
+                    - :data:`~.constants.NIFTI_XFORM_UNKNOWN`
+                    - :data:`~.constants.NIFTI_XFORM_SCANNER_ANAT`
+                    - :data:`~.constants.NIFTI_XFORM_ALIGNED_ANAT`
+                    - :data:`~.constants.NIFTI_XFORM_TALAIRACH`
+                    - :data:`~.constants.NIFTI_XFORM_MNI_152`
         """
 
         if   code is None:     code = 'sform_code'
@@ -306,21 +339,22 @@ class Image(props.HasProperties):
 
         This method returns one of the following values, indicating the
         direction in which coordinates along the specified axis increase:
-          - :attr:`~fsl.data.constants.ORIENT_L2R`:     Left to right
-          - :attr:`~fsl.data.constants.ORIENT_R2L`:     Right to left
-          - :attr:`~fsl.data.constants.ORIENT_A2P`:     Anterior to posterior
-          - :attr:`~fsl.data.constants.ORIENT_P2A`:     Posterior to anterior
-          - :attr:`~fsl.data.constants.ORIENT_I2S`:     Inferior to superior
-          - :attr:`~fsl.data.constants.ORIENT_S2I`:     Superior to inferior
-          - :attr:`~fsl.data.constants.ORIENT_UNKNOWN`: Orientation is unknown
+        
+          - :attr:`~.constants.ORIENT_L2R`:     Left to right
+          - :attr:`~.constants.ORIENT_R2L`:     Right to left
+          - :attr:`~.constants.ORIENT_A2P`:     Anterior to posterior
+          - :attr:`~.constants.ORIENT_P2A`:     Posterior to anterior
+          - :attr:`~.constants.ORIENT_I2S`:     Inferior to superior
+          - :attr:`~.constants.ORIENT_S2I`:     Superior to inferior
+          - :attr:`~.constants.ORIENT_UNKNOWN`: Orientation is unknown
 
         The returned value is dictated by the XForm code contained in the
-        image file header (see the :meth:`getXFormCode` method). Basically,
-        if the XForm code is 'unknown', this method will return -1 for all
-        axes. Otherwise, it is assumed that the image is in RAS orientation
-        (i.e. the X axis increases from left to right, the Y axis increases
-        from  posterior to anterior, and the Z axis increases from inferior
-        to superior).
+        image file header (see the :meth:`getXFormCode` method). Basically, if
+        the XForm code is *unknown*, this method will return
+        ``ORIENT_UNKNOWN`` for all axes. Otherwise, it is assumed that the
+        image is in RAS orientation (i.e. the X axis increases from left to
+        right, the Y axis increases from posterior to anterior, and the Z axis
+        increases from inferior to superior).
         """
 
         if self.getXFormCode(code) == constants.NIFTI_XFORM_UNKNOWN:
@@ -335,7 +369,10 @@ class Image(props.HasProperties):
 
     def getVoxelOrientation(self, axis, code=None):
         """Returns a code representing the (estimated) orientation of the
-        specified voxelwise axis.
+        specified data axis.
+
+        :arg code: May be either ``qform`` or ``sform``, specifying which
+                   transformation to use.
 
         See the :meth:`getWorldOrientation` method for a description
         of the return value.
@@ -368,10 +405,11 @@ class Image(props.HasProperties):
 # so i'm just providing '*.gz'for now
 ALLOWED_EXTENSIONS = ['.nii.gz', '.nii', '.img', '.hdr', '.img.gz', '.gz']
 """The file extensions which we understand. This list is used as the default
-if if the ``allowedExts`` parameter is not passed to any of the functions
+if the ``allowedExts`` parameter is not passed to any of the functions
 below.
 """
 
+
 EXTENSION_DESCRIPTIONS = ['Compressed NIFTI1 images',
                           'NIFTI1 images',
                           'ANALYZE75 images',
@@ -386,8 +424,7 @@ DEFAULT_EXTENSION  = '.nii.gz'
 
 
 def isSupported(filename, allowedExts=None):
-    """
-    Returns ``True`` if the given file has a supported extension, ``False``
+    """Returns ``True`` if the given file has a supported extension, ``False``
     otherwise.
 
     :arg filename:    The file name to test.
@@ -402,8 +439,7 @@ def isSupported(filename, allowedExts=None):
 
 
 def removeExt(filename, allowedExts=None):
-    """
-    Removes the extension from the given file name. Returns the filename
+    """Removes the extension from the given file name. Returns the filename
     unmodified if it does not have a supported extension.
 
     :arg filename:    The file name to strip.
@@ -429,11 +465,7 @@ def removeExt(filename, allowedExts=None):
     return filename[:-extLen]
 
 
-def addExt(
-        prefix,
-        mustExist=True,
-        allowedExts=None,
-        defaultExt=None):
+def addExt(prefix, mustExist=True, allowedExts=None, defaultExt=None):
     """Adds a file extension to the given file ``prefix``.
 
     If ``mustExist`` is False, and the file does not already have a 
@@ -571,9 +603,9 @@ def saveImage(image, fromDir=None):
     the image.
 
 
-    :param image:         The :class:`.Image` instance to be saved.
+    :arg image:           The :class:`.Image` instance to be saved.
 
-    :param str fromDir:   Directory in which the file dialog should start.
+    :arg fromDir:         Directory in which the file dialog should start.
                           If ``None``, the most recently visited directory
                           (via this method) is used, or the directory from
                           the given image, or the current working directory.
diff --git a/fsl/data/model.py b/fsl/data/model.py
index 1598f73db..a336304ba 100644
--- a/fsl/data/model.py
+++ b/fsl/data/model.py
@@ -1,9 +1,20 @@
 #!/usr/bin/env python
 #
-# model.py -
+# model.py - The Model class, for VTK polygon data.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module providse the :class:`Model` class, which represents a 3D model.
+
+.. note:: I/O support is very limited - currently, the only supported file 
+          type is the VTK legacy file format, containing the ``POLYDATA``
+          dataset. the :class:`Model` class assumes that every polygon defined
+          in an input file is a triangle (i.e. refers to three vertices).
+
+          See http://www.vtk.org/wp-content/uploads/2015/04/file-formats.pdf
+          for an overview of the VTK legacy file format.
+"""
+
 
 import logging
 
@@ -14,53 +25,22 @@ import numpy   as np
 log = logging.getLogger(__name__)
 
 
-ALLOWED_EXTENSIONS     = ['.vtk']
-EXTENSION_DESCRIPTIONS = ['VTK polygon model file']
-
-
-def loadVTKPolydataFile(infile):
-    
-    lines = None
-
-    with open(infile, 'rt') as f:
-        lines = f.readlines()
-
-    lines = [l.strip() for l in lines]
-
-    if lines[3] != 'DATASET POLYDATA':
-        raise ValueError('')
-    
-    nVertices = int(lines[4].split()[1])
-    nPolygons = int(lines[5 + nVertices].split()[1])
-    nIndices  = int(lines[5 + nVertices].split()[2]) - nPolygons 
-    
-    vertices       = np.zeros((nVertices, 3), dtype=np.float32)
-    polygonLengths = np.zeros( nPolygons,     dtype=np.uint32)
-    indices        = np.zeros( nIndices,      dtype=np.uint32)
-
-    for i in range(nVertices):
-        vertLine       = lines[i + 5]
-        vertices[i, :] = map(float, vertLine.split())
-
-    indexOffset = 0
-    for i in range(nPolygons):
-
-        polyLine          = lines[6 + nVertices + i].split()
-        polygonLengths[i] = int(polyLine[0])
-
-        start              = indexOffset
-        end                = indexOffset + polygonLengths[i]
-        indices[start:end] = map(int, polyLine[1:])
-
-        indexOffset        += polygonLengths[i]
+class Model(object):
+    """The ``Model`` class represents a 3D model. A model is defined by a
+    collection of vertices and indices.  The indices index into the list of
+    vertices, and define a set of triangles which make the model.
+    """
 
-    return vertices, polygonLengths, indices
     
+    def __init__(self, data, indices=None):
+        """Create a ``Model`` instance.
 
-class Model(object):
+        :arg data:    Can either be a file name, or a :math:`N\\times 3`
+                      ``numpy`` array containing vertex data. If ``data`` is
+                      a file name, it is passed to the
+                      :func:`loadVTKPolydataFile` function.
 
-    def __init__(self, data, indices=None):
-        """
+        :arg indices: A list of indices into the vertex data.
         """
 
         if isinstance(data, basestring):
@@ -90,17 +70,85 @@ class Model(object):
 
         
     def __del__(self):
+        """Prints a log message."""
         log.memory('{}.del ({})'.format(type(self).__name__, id(self)))
         
 
     def __repr__(self):
+        """Rewturns a string representation of this ``Model`` instance. """
         return '{}({}, {})'.format(type(self).__name__,
                                    self.name,
                                    self.dataSource)
 
     def __str__(self):
+        """Rewturns a string representation of this ``Model`` instance. """
         return self.__repr__()
 
 
     def getBounds(self):
+        """Returns a tuple of values which define a minimal bounding box that
+        will contain all vertices in this ``Model`` instance. The bounding
+        box is arranged like so:
+
+            ``((xlow, ylow, zlow), (xhigh, yhigh, zhigh))``
+        """
         return (self.__loBounds, self.__hiBounds)
+
+
+ALLOWED_EXTENSIONS     = ['.vtk']
+"""A list of file extensions which could contain :class:`Model` data. """
+
+
+EXTENSION_DESCRIPTIONS = ['VTK polygon model file']
+"""A description for each of the extensions in :data:`ALLOWED_EXTENSIONS`."""
+
+
+def loadVTKPolydataFile(infile):
+    """Loads a vtk legacy file containing a ``POLYDATA`` data set.
+
+    :arg infile: Name of a file to load from.
+
+    :returns: a tuple containing three values:
+    
+                - A :math:`N\\times 3` ``numpy`` array containing :math:`N`
+                  vertices.
+                - A 1D ``numpy`` array containing the lengths of each polygon.
+                - A 1D ``numpy`` array containing the vertex indices for all
+                  polygons.
+    """
+    
+    lines = None
+
+    with open(infile, 'rt') as f:
+        lines = f.readlines()
+
+    lines = [l.strip() for l in lines]
+
+    if lines[3] != 'DATASET POLYDATA':
+        raise ValueError('Only the POLYDATA data type is supported')
+    
+    nVertices = int(lines[4].split()[1])
+    nPolygons = int(lines[5 + nVertices].split()[1])
+    nIndices  = int(lines[5 + nVertices].split()[2]) - nPolygons 
+    
+    vertices       = np.zeros((nVertices, 3), dtype=np.float32)
+    polygonLengths = np.zeros( nPolygons,     dtype=np.uint32)
+    indices        = np.zeros( nIndices,      dtype=np.uint32)
+
+    for i in range(nVertices):
+        vertLine       = lines[i + 5]
+        vertices[i, :] = map(float, vertLine.split())
+
+    indexOffset = 0
+    for i in range(nPolygons):
+
+        polyLine          = lines[6 + nVertices + i].split()
+        polygonLengths[i] = int(polyLine[0])
+
+        start              = indexOffset
+        end                = indexOffset + polygonLengths[i]
+        indices[start:end] = map(int, polyLine[1:])
+
+        indexOffset        += polygonLengths[i]
+
+    return vertices, polygonLengths, indices
diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index 127a89b4d..df9fd6a79 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -4,10 +4,31 @@
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module contains a collection of strings used throughout ``fslpy`` for
+display purposes. Most of the strings are used by FSLeyes.
+
+
+The strings are stored in :class:`.TypeDict` dictionaries, roughly organised
+into the following categories:
+
+
+  - :data:`messages`:   Messages to be displayed to the user.
+  - :data:`titles`:     Titles of windows, panels, and dialogs.
+  - :data:`actions`:    Names of actions tied to menu options, buttons, etc.
+  - :data:`labels`:     Labels for miscellaneous things.
+  - :data:`properties`: Display names for ``props.HasProperties`` properties.
+  - :data:`choices`:    Display names for ``props.HasProperties`` choice
+                        properties.
+  - :data:`anatomy`:    Anatomical and orientation labels.
+  - :data:`nifti`:      Labels for NIFTI header fields.
+  - :data:`feat`:       FEAT specific names and labels.
+"""
+
 
 from fsl.utils.typedict import TypeDict
 import fsl.data.constants as constants
 
+
 messages = TypeDict({
 
     'FSLDirDialog.FSLDirNotSet'    : 'The $FSLDIR environment variable '
@@ -101,7 +122,6 @@ messages = TypeDict({
 })
 
 
-
 titles = TypeDict({
 
     'FSLDirDialog'           : '$FSLDIR is not set',
@@ -202,6 +222,7 @@ actions = TypeDict({
     'OrthoEditProfile.createROIFromSelection'  : 'ROI',
 })
 
+
 labels = TypeDict({
 
     'FSLDirDialog.locate' : 'Locate $FSLDIR',
diff --git a/fsl/fsleyes/views/timeseriespanel.py b/fsl/fsleyes/views/timeseriespanel.py
index f8e3d9072..63f058e52 100644
--- a/fsl/fsleyes/views/timeseriespanel.py
+++ b/fsl/fsleyes/views/timeseriespanel.py
@@ -343,7 +343,7 @@ class FEATReducedTimeSeries(TimeSeries):
 
     def getData(self):
         
-        data = self.overlay.reducedData(self.coords, self.contrast, False)
+        data = self.overlay.partialFit(self.contrast, self.coords, False)
         return TimeSeries.getData(self, ydata=data)
 
     
diff --git a/fsl/utils/colourbarbitmap.py b/fsl/utils/colourbarbitmap.py
index 25aeed4c3..3199b4eef 100644
--- a/fsl/utils/colourbarbitmap.py
+++ b/fsl/utils/colourbarbitmap.py
@@ -50,7 +50,7 @@ def colourBarBitmap(cmap,
     
     :arg label:        Text label to show next to the colour bar.
     
-    :arg orientation:  Either ``vertical`` or `horizontal``.
+    :arg orientation:  Either ``vertical`` or ``horizontal``.
     
     :arg labelside:    If ``orientation`` is ``vertical`` ``labelSide`` may
                        be either ``left`` or ``right``. Otherwise, if
-- 
GitLab