Skip to content
Snippets Groups Projects
gifti.py 3.84 KiB
Newer Older
#!/usr/bin/env python
#
# gifti.py - GIFTI file support.
#
# Author: Paul McCarthy  <pauldmccarthy@gmail.com>
#         Michiel Cottar <michiel.cottaar@ndcn.ox.ac.uk>
#
"""This class provides classes and functions for working with GIFTI files.

The GIFTI file format specification can be found at
http://www.nitrc.org/projects/gifti/

Support is currently very basic - only the following classes/functions
are available:

  .. autosummary::
     :nosignatures:

     GiftiSurface
     extractGiftiSurface
"""


import os.path as op

import fsl.utils.path as fslpath
from . import            mesh


class GiftiSurface(mesh.TriangleMesh):
    """Class which represents a GIFTI surface image. This is essentially
    just a 3D model made of triangles.

    In addition to the ``vertices`` and ``indices`` provided by the
    :class:`.TriangleMesh` class (from which the ``GiftiSurface`` class
    derives), a ``GiftiSurface`` instance has the following attributes:

    ============== ====================================================
    ``name``       A name, typically the file name sans-suffix.
    ``dataSource`` Full path to the GIFTI file.
    ``surfImg``    Reference to the loaded ``nibabel.gifti.GiftiImage``
    ============== ====================================================
    """


    def __init__(self, infile):
        """Load the given GIFTI file using ``nibabel``, and extracts surface
        data using the  :func:`extractGiftiSurface` function.

        :arg infile: A GIFTI surface file
        """

        import nibabel as nib

        surfimg           = nib.load(infile)
        vertices, indices = extractGiftiSurface(surfimg)

        mesh.TriangleMesh.__init__(self, vertices, indices)

        name   = fslpath.removeExt(op.basename(infile), ALLOWED_EXTENSIONS)
        infile = op.abspath(infile)

        self.name       = name
        self.dataSource = infile
        self.surfImg    = surfimg


ALLOWED_EXTENSIONS = ['.surf.gii', '.gii']
"""List of file extensions that a file containing Gifti surface data
is expected to have.
"""


EXTENSION_DESCRIPTIONS = ['GIFTI surface file', 'GIFTI surface file']
"""A description for each of the :data:`ALLOWED_EXTENSIONS`.
"""


def extractGiftiSurface(surfimg):
    """Extracts surface data from the given ``nibabel.gifti.GiftiImage``.

    The image is expected to contain the following``<DataArray>`` elements:

      - one comprising ``NIFTI_INTENT_POINTSET`` data (the surface vertices)
      - one comprising ``NIFTI_INTENT_TRIANGLE`` data (vertex indices
        defining the triangles).

    A ``ValueError`` will be raised if this is not the case.

    :arg surfimg: A ``GiftiImage`` containing surface data.

    :returns:     A tuple containing these values:

                   - A :math:`N\\times 3` ``numpy`` array containing :math:`N`
                     vertices.
    
                   - A :math:`M\\times 3` ``numpy`` array containing the 
                     vertex indices for :math:`M` triangles.
    """

    from nibabel import gifti

    codes    = gifti.gifti.intent_codes.code
    
    indices  = None
    vertices = None
    
    for darray in surfimg.darrays:
        
        if darray.intent == codes['pointset']:
            
            if vertices is not None:
                raise ValueError('multiple arrays with intent "{}"'.format(
                    darray.intent))
            
            vertices = darray.data
            
        elif darray.intent == codes['triangle']:
            if indices is not None:
                raise ValueError('multiple arrays with intent "{}"'.format(
                    darray.intent))
            
            indices = darray.data
            
    if vertices is None:
        raise ValueError('no array with intent "pointset" found')
    
    if indices is None:
        raise ValueError('no array witbh intent "triangle"found')
    
    return vertices, indices