66# copyright and license terms.
77#
88### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9+ """ Classes defining Gifti objects
10+
11+ The Gifti specification was (at time of writing) available as a PDF download
12+ from http://www.nitrc.org/projects/gifti/
13+ """
914from __future__ import division , print_function , absolute_import
1015
1116import sys
2530
2631
2732class GiftiMetaData (xml .XmlSerializable ):
28- """ A list of GiftiNVPairs in stored in
29- the list self.data """
33+ """ A sequence of GiftiNVPairs containing metadata for a gifti data array
34+ """
3035
3136 def __init__ (self , nvpair = None ):
3237 self .data = []
@@ -68,16 +73,26 @@ def print_summary(self):
6873
6974
7075class GiftiNVPairs (object ):
71- """
72- name = str
73- value = str
76+ """ Gifti name / value pairs
77+
78+ Attributes
79+ ----------
80+ name : str
81+ value : str
7482 """
7583 def __init__ (self , name = '' , value = '' ):
7684 self .name = name
7785 self .value = value
7886
7987
8088class GiftiLabelTable (xml .XmlSerializable ):
89+ """ Gifti label table: a sequence of key, label pairs
90+
91+ From the gifti spec dated 2011-01-14:
92+ The label table is used by DataArrays whose values are an key into the
93+ LabelTable's labels. A file should contain at most one LabelTable and
94+ it must be located in the file prior to any DataArray elements.
95+ """
8196
8297 def __init__ (self ):
8398 self .labels = []
@@ -104,17 +119,34 @@ def print_summary(self):
104119
105120
106121class GiftiLabel (xml .XmlSerializable ):
107- """
108- key = int
109- label = str
110- # rgba
111- # freesurfer examples seem not to conform
112- # to datatype "NIFTI_TYPE_RGBA32" because they
113- # are floats, not unsigned 32-bit integers
114- red = float
115- green = float
116- blue = float
117- alpha = float
122+ """ Gifti label: association of integer key with optional RGBA values
123+
124+ Quotes are from the gifti spec dated 2011-01-14.
125+
126+ Attributes
127+ ----------
128+ key : int
129+ (From the spec): "This required attribute contains a non-negative
130+ integer value. If a DataArray's Intent is NIFTI_INTENT_LABEL and a
131+ value in the DataArray is 'X', its corresponding label is the label
132+ with the Key attribute containing the value 'X'. In early versions of
133+ the GIFTI file format, the attribute Index was used instead of Key. If
134+ an Index attribute is encountered, it should be processed like the Key
135+ attribute."
136+ label : str
137+ red : None or float
138+ Optional value for red.
139+ green : None or float
140+ Optional value for green.
141+ blue : None or float
142+ Optional value for blue.
143+ alpha : None or float
144+ Optional value for alpha.
145+
146+ Notes
147+ -----
148+ freesurfer examples seem not to conform to datatype "NIFTI_TYPE_RGBA32"
149+ because they are floats, not 4 8-bit integers.
118150 """
119151
120152 def __init__ (self , key = 0 , label = '' , red = None , green = None , blue = None ,
@@ -137,12 +169,12 @@ def rgba(self):
137169
138170 @rgba .setter
139171 def rgba (self , rgba ):
140- """ Set RGBA via tuple
172+ """ Set RGBA via sequence
141173
142174 Parameters
143175 ----------
144- rgba : tuple (red, green, blue, alpha)
145-
176+ rgba : length 4 sequence
177+ Sequence containing values for red, green, blue, alpha.
146178 """
147179 if len (rgba ) != 4 :
148180 raise ValueError ('rgba must be length 4.' )
@@ -159,10 +191,38 @@ def _arr2txt(arr, elem_fmt):
159191
160192
161193class GiftiCoordSystem (xml .XmlSerializable ):
162- """
163- dataspace = int
164- xformspace = int
165- xform = np.ndarray # 4x4 numpy array
194+ """ Gifti coordinate system transform matrix
195+
196+ Quotes are from the gifti spec dated 2011-01-14.
197+
198+ "For a DataArray with an Intent NIFTI_INTENT_POINTSET, this element
199+ describes the stereotaxic space of the data before and after the
200+ application of a transformation matrix. The most common stereotaxic
201+ space is the Talairach Space that places the origin at the anterior
202+ commissure and the negative X, Y, and Z axes correspond to left,
203+ posterior, and inferior respectively. At least one
204+ CoordinateSystemTransformMatrix is required in a DataArray with an
205+ intent of NIFTI_INTENT_POINTSET. Multiple
206+ CoordinateSystemTransformMatrix elements may be used to describe the
207+ transformation to multiple spaces."
208+
209+ Attributes
210+ ----------
211+ dataspace : int
212+ From the spec: "Contains the stereotaxic space of a DataArray's data
213+ prior to application of the transformation matrix. The stereotaxic
214+ space should be one of:
215+ NIFTI_XFORM_UNKNOWN
216+ NIFTI_XFORM_SCANNER_ANAT
217+ NIFTI_XFORM_ALIGNED_ANAT
218+ NIFTI_XFORM_TALAIRACH
219+ NIFTI_XFORM_MNI_152"
220+ xformspace : int
221+ Spec: "Contains the stereotaxic space of a DataArray's data after
222+ application of the transformation matrix. See the DataSpace element for
223+ a list of stereotaxic spaces."
224+ xform : array-like shape (4, 4)
225+ Affine transformation matrix
166226 """
167227
168228 def __init__ (self , dataspace = 0 , xformspace = 0 , xform = None ):
@@ -205,8 +265,8 @@ def _to_xml_element(self):
205265
206266
207267def _data_tag_element (dataarray , encoding , datatype , ordering ):
208- """ Creates the data tag depending on the required encoding,
209- returns as XML element """
268+ """ Creates data tag with given ` encoding`, returns as XML element
269+ """
210270 import zlib
211271 ord = array_index_order_codes .npcode [ordering ]
212272 enclabel = gifti_encoding_codes .label [encoding ]
@@ -228,42 +288,84 @@ def _data_tag_element(dataarray, encoding, datatype, ordering):
228288
229289
230290class GiftiDataArray (xml .XmlSerializable ):
231- """
232- # These are for documentation only; we don't use these class variables
233- intent = int
234- datatype = int
235- ind_ord = int
236- dims = list
237- encoding = int
238- endian = int
239- ext_fname = str
240- ext_offset = str
241- data = np.ndarray
242- coordsys = GiftiCoordSystem
243- meta = GiftiMetaData
291+ """ Container for Gifti numerical data array and associated metadata
292+
293+ Quotes are from the gifti spec dated 2011-01-14.
294+
295+ Description of DataArray in spec:
296+ "This element contains the numeric data and its related metadata. The
297+ CoordinateSystemTransformMatrix child is only used when the DataArray's
298+ Intent is NIFTI_INTENT_POINTSET. FileName and FileOffset are required
299+ if the data is stored in an external file."
300+
301+ Attributes
302+ ----------
303+ darray : None or ndarray
304+ Data array
305+ intent : int
306+ NIFTI intent code, see nifti1.intent_codes
307+ datatype : int
308+ NIFTI data type codes, see nifti1.data_type_codes. From the spec:
309+ "This required attribute describes the numeric type of the data
310+ contained in a Data Array and are limited to the types displayed in the
311+ table:
312+
313+ NIFTI_TYPE_UINT8 : Unsigned, 8-bit bytes.
314+ NIFTI_TYPE_INT32 : Signed, 32-bit integers.
315+ NIFTI_TYPE_FLOAT32 : 32-bit single precision floating point."
316+
317+ At the moment, we do not enforce that the datatype is one of these
318+ three.
319+ encoding : string
320+ Encoding of the data, see util.gifti_encoding_codes; default is
321+ GIFTI_ENCODING_B64GZ.
322+ endian : string
323+ The Endianness to store the data array. Should correspond to the
324+ machine endianness. Default is system byteorder.
325+ coordsys : :class:`GiftiCoordSystem` instance
326+ Input and output coordinate system with tranformation matrix between
327+ the two.
328+ ind_ord : int
329+ The ordering of the array. see util.array_index_order_codes. Default
330+ is RowMajorOrder - C ordering
331+ meta : :class:`GiftiMetaData` instance
332+ An instance equivalent to a dictionary for metadata information.
333+ ext_fname : str
334+ Filename in which data is stored, or empty string if no corresponding
335+ filename.
336+ ext_offset : int
337+ Position in bytes within `ext_fname` at which to start reading data.
244338 """
245339
246- def __init__ (self , data = None ,
340+ def __init__ (self ,
341+ data = None ,
342+ intent = 'NIFTI_INTENT_NONE' ,
343+ datatype = None ,
247344 encoding = "GIFTI_ENCODING_B64GZ" ,
248345 endian = sys .byteorder ,
249346 coordsys = None ,
250347 ordering = "C" ,
251- meta = None ):
348+ meta = None ,
349+ ext_fname = '' ,
350+ ext_offset = 0 ):
252351 """
253352 Returns a shell object that cannot be saved.
254353 """
255- self .data = data
256- self .dims = []
257- self .meta = meta or GiftiMetaData ()
354+ self .data = None if data is None else np .asarray (data )
355+ self .intent = intent_codes .code [intent ]
356+ if datatype is None :
357+ datatype = 'none' if self .data is None else self .data .dtype
358+ self .datatype = data_type_codes .code [datatype ]
359+ self .encoding = gifti_encoding_codes .code [encoding ]
360+ self .endian = gifti_endian_codes .code [endian ]
258361 self .coordsys = coordsys or GiftiCoordSystem ()
259- self .ext_fname = ''
260- self .ext_offset = ''
261- self .intent = 0 # required attribute, NIFTI_INTENT_NONE
262- self .datatype = 0 # required attribute, void/none
263- # python/numpy default: column major order
264362 self .ind_ord = array_index_order_codes .code [ordering ]
265- self .encoding = encoding
266- self .endian = endian
363+ self .meta = (GiftiMetaData () if meta is None else
364+ meta if isinstance (meta , GiftiMetaData ) else
365+ GiftiMetaData .from_dict (meta ))
366+ self .ext_fname = ext_fname
367+ self .ext_offset = ext_offset
368+ self .dims = [] if self .data is None else list (self .data .shape )
267369
268370 @property
269371 def num_dim (self ):
@@ -308,22 +410,14 @@ def from_array(klass,
308410 -------
309411 da : instance of our own class
310412 """
311- if meta is None :
312- meta = {}
313- cda = klass (darray )
314- cda .dims = list (darray .shape )
315- if datatype is None :
316- cda .datatype = data_type_codes .code [darray .dtype ]
317- else :
318- cda .datatype = data_type_codes .code [datatype ]
319- cda .intent = intent_codes .code [intent ]
320- cda .encoding = gifti_encoding_codes .code [encoding ]
321- cda .endian = gifti_endian_codes .code [endian ]
322- if coordsys is not None :
323- cda .coordsys = coordsys
324- cda .ind_ord = array_index_order_codes .code [ordering ]
325- cda .meta = GiftiMetaData .from_dict (meta )
326- return cda
413+ return klass (data = darray ,
414+ intent = intent ,
415+ datatype = datatype ,
416+ encoding = encoding ,
417+ endian = endian ,
418+ coordsys = coordsys ,
419+ ordering = ordering ,
420+ meta = meta )
327421
328422 def _to_xml_element (self ):
329423 # fix endianness to machine endianness
@@ -364,7 +458,7 @@ def to_xml_open(self):
364458%s\t Encoding="%s"
365459\t Endian="%s"
366460\t ExternalFileName="%s"
367- \t ExternalFileOffset="%s ">\n """
461+ \t ExternalFileOffset="%d ">\n """
368462 di = ""
369463 for i , n in enumerate (self .dims ):
370464 di = di + '\t Dim%s=\" %s\" \n ' % (str (i ), str (n ))
@@ -475,8 +569,7 @@ def labeltable(self, labeltable):
475569
476570 Parameters
477571 ----------
478- labeltable : GiftiLabelTable
479-
572+ labeltable : :class:`GiftiLabelTable` instance
480573 """
481574 if not isinstance (labeltable , GiftiLabelTable ):
482575 raise TypeError ("Not a valid GiftiLabelTable instance" )
@@ -500,11 +593,7 @@ def meta(self, meta):
500593
501594 Parameters
502595 ----------
503- meta : GiftiMetaData
504-
505- Returns
506- -------
507- None
596+ meta : :class:`GiftiMetaData` instance
508597 """
509598 if not isinstance (meta , GiftiMetaData ):
510599 raise TypeError ("Not a valid GiftiMetaData instance" )
@@ -523,7 +612,7 @@ def add_gifti_data_array(self, dataarr):
523612
524613 Parameters
525614 ----------
526- dataarr : GiftiDataArray
615+ dataarr : :class:` GiftiDataArray` instance
527616 """
528617 if not isinstance (dataarr , GiftiDataArray ):
529618 raise TypeError ("Not a valid GiftiDataArray instance" )
@@ -541,11 +630,9 @@ def remove_gifti_data_array_by_intent(self, intent):
541630 self .darrays .remove (dele )
542631
543632 def get_arrays_from_intent (self , intent ):
544- """ Returns a a list of GiftiDataArray elements matching
545- the given intent """
546-
633+ """ Return list of GiftiDataArray elements matching given intent
634+ """
547635 it = intent_codes .code [intent ]
548-
549636 return [x for x in self .darrays if x .intent == it ]
550637
551638 @np .deprecate_with_doc ("Use get_arrays_from_intent instead." )
@@ -594,7 +681,9 @@ def to_file_map(self, file_map=None):
594681
595682 Parameters
596683 ----------
597- file_map : string
684+ file_map : dict
685+ Dictionary with single key ``image`` with associated value which is
686+ a :class:`FileHolder` instance pointing to the image file.
598687
599688 Returns
600689 -------
@@ -610,17 +699,18 @@ def from_file_map(klass, file_map, buffer_size=35000000):
610699 """ Load a Gifti image from a file_map
611700
612701 Parameters
613- file_map : string
702+ ----------
703+ file_map : dict
704+ Dictionary with single key ``image`` with associated value which is
705+ a :class:`FileHolder` instance pointing to the image file.
614706
615707 Returns
616708 -------
617709 img : GiftiImage
618- Returns a GiftiImage
619- """
710+ """
620711 parser = klass .parser (buffer_size = buffer_size )
621712 parser .parse (fptr = file_map ['image' ].get_prepare_fileobj ('rb' ))
622- img = parser .img
623- return img
713+ return parser .img
624714
625715 @classmethod
626716 def from_filename (klass , filename , buffer_size = 35000000 ):
0 commit comments