# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the NiBabel package for the # copyright and license terms. # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import sys from StringIO import StringIO if sys.version_info[0] >= 3: from io import BytesIO else: BytesIO = StringIO import numpy as np from ..nifti1 import data_type_codes, xform_codes, intent_codes from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes, KIND2FMT) class GiftiMetaData(object): """ A list of GiftiNVPairs in stored in the list self.data """ def __init__(self, nvpair = None): self.data = [] if not nvpair is None: self.data.append(nvpair) @classmethod def from_dict(klass, data_dict): meda = klass() for k,v in data_dict.items(): nv = GiftiNVPairs(k, v) meda.data.append(nv) return meda def get_metadata(self): """ Returns metadata as dictionary """ self.data_as_dict = {} for ele in self.data: self.data_as_dict[ele.name] = ele.value return self.data_as_dict def to_xml(self): if len(self.data) == 0: return "\n" res = "\n" for ele in self.data: nvpair = """ \t \t \n""" % (ele.name, ele.value) res = res + nvpair res = res + "\n" return res def print_summary(self): print self.get_metadata() class GiftiNVPairs(object): name = str value = str def __init__(self, name = '', value = ''): self.name = name self.value = value class GiftiLabelTable(object): def __init__(self): self.labels = [] def get_labels_as_dict(self): self.labels_as_dict = {} for ele in self.labels: self.labels_as_dict[ele.key] = ele.label return self.labels_as_dict def to_xml(self): if len(self.labels) == 0: return "\n" res = "\n" for ele in self.labels: col = '' if not ele.red is None: col += ' Red="%s"' % str(ele.red) if not ele.green is None: col += ' Green="%s"' % str(ele.green) if not ele.blue is None: col += ' Blue="%s"' % str(ele.blue) if not ele.alpha is None: col += ' Alpha="%s"' % str(ele.alpha) lab = """\t\n""" % \ (str(ele.key), col, ele.label) res = res + lab res = res + "\n" return res def print_summary(self): print self.get_labels_as_dict() class GiftiLabel(object): key = int label = str # rgba # freesurfer examples seem not to conform # to datatype "NIFTI_TYPE_RGBA32" because they # are floats, not unsigned 32-bit integers red = float green = float blue = float alpha = float def __init__(self, key = 0, label = '', red = None,\ green = None, blue = None, alpha = None): self.key = key self.label = label self.red = red self.green = green self.blue = blue self.alpha = alpha def get_rgba(self): """ Returns RGBA as tuple """ return (self.red, self.green, self.blue, self.alpha) class GiftiCoordSystem(object): dataspace = int xformspace = int xform = np.ndarray # 4x4 numpy array def __init__(self, dataspace = 0, xformspace = 0, xform = None): self.dataspace = dataspace self.xformspace = xformspace if xform is None: # create identity matrix self.xform = np.identity(4) else: self.xform = xform def to_xml(self): if self.xform is None: return "\n" res = (""" \t \t\n""" % (xform_codes.niistring[self.dataspace], xform_codes.niistring[self.xformspace])) e = BytesIO() np.savetxt(e, self.xform, '%10.6f') e.seek(0) res = res + "\n" res = res + e.read().decode() e.close() res = res + "\n" res = res + "\n" return res def print_summary(self): print 'Dataspace: ', xform_codes.niistring[self.dataspace] print 'XFormSpace: ', xform_codes.niistring[self.xformspace] print 'Affine Transformation Matrix: \n', self.xform def data_tag(dataarray, encoding, datatype, ordering): """ Creates the data tag depending on the required encoding """ import base64 import zlib ord = array_index_order_codes.npcode[ordering] enclabel = gifti_encoding_codes.label[encoding] if enclabel == 'ASCII': c = BytesIO() # np.savetxt(c, dataarray, format, delimiter for columns) np.savetxt(c, dataarray, datatype, ' ') c.seek(0) da = c.read() elif enclabel == 'B64BIN': da = base64.encodestring(dataarray.tostring(ord)) elif enclabel == 'B64GZ': # first compress comp = zlib.compress(dataarray.tostring(ord)) da = base64.encodestring(comp) da = da.decode() elif enclabel == 'External': raise NotImplementedError("In what format are the external files?") else: da = '' return ""+da+"\n" class GiftiDataArray(object): # These are for documentation only; we don't use these class variables intent = int datatype = int ind_ord = int num_dim = int dims = list encoding = int endian = int ext_fname = str ext_offset = str data = np.ndarray coordsys = GiftiCoordSystem meta = GiftiMetaData def __init__(self, data=None): self.data = data self.dims = [] self.meta = GiftiMetaData() self.coordsys = GiftiCoordSystem() self.ext_fname = '' self.ext_offset = '' @classmethod def from_array(klass, darray, intent, datatype = None, encoding = "GIFTI_ENCODING_B64GZ", endian = sys.byteorder, coordsys = None, ordering = "C", meta = None): """ Creates a new Gifti data array Parameters ---------- darray : ndarray NumPy data array intent : string NIFTI intent code, see nifti1.intent_codes datatype : None or string, optional NIFTI data type codes, see nifti1.data_type_codes If None, the datatype of the NumPy array is taken. encoding : string, optionaal Encoding of the data, see util.gifti_encoding_codes; default: GIFTI_ENCODING_B64GZ endian : string, optional The Endianness to store the data array. Should correspond to the machine endianness. default: system byteorder coordsys : GiftiCoordSystem, optional If None, a identity transformation is taken. ordering : string, optional The ordering of the array. see util.array_index_order_codes; default: RowMajorOrder - C ordering meta : None or dict, optional A dictionary for metadata information. If None, gives empty dict. Returns ------- da : instance of our own class """ if meta is None: meta = {} cda = klass(darray) cda.num_dim = len(darray.shape) cda.dims = list(darray.shape) if datatype == None: cda.datatype = data_type_codes.code[darray.dtype] else: cda.datatype = data_type_codes.code[datatype] cda.intent = intent_codes.code[intent] cda.encoding = gifti_encoding_codes.code[encoding] cda.endian = gifti_endian_codes.code[endian] if not coordsys is None: cda.coordsys = coordsys cda.ind_ord = array_index_order_codes.code[ordering] cda.meta = GiftiMetaData.from_dict(meta) return cda def to_xml(self): # fix endianness to machine endianness self.endian = gifti_endian_codes.code[sys.byteorder] result = "" result += self.to_xml_open() # write metadata if not self.meta is None: result += self.meta.to_xml() # write coord sys if not self.coordsys is None: result += self.coordsys.to_xml() # write data array depending on the encoding dt_kind = data_type_codes.dtype[self.datatype].kind result += data_tag(self.data, gifti_encoding_codes.specs[self.encoding], KIND2FMT[dt_kind], self.ind_ord) result = result + self.to_xml_close() return result def to_xml_open(self): out = """\n""" di = "" for i, n in enumerate(self.dims): di = di + '\tDim%s=\"%s\"\n' % (str(i), str(n)) return out % (intent_codes.niistring[self.intent], \ data_type_codes.niistring[self.datatype], \ array_index_order_codes.label[self.ind_ord], \ str(self.num_dim), \ str(di), \ gifti_encoding_codes.specs[self.encoding], \ gifti_endian_codes.giistring[self.endian], \ self.ext_fname, self.ext_offset, ) def to_xml_close(self): return "\n" def print_summary(self): print 'Intent: ', intent_codes.niistring[self.intent] print 'DataType: ', data_type_codes.niistring[self.datatype] print 'ArrayIndexingOrder: ', array_index_order_codes.label[self.ind_ord] print 'Dimensionality: ', self.num_dim print 'Dimensions: ', self.dims print 'Encoding: ', gifti_encoding_codes.specs[self.encoding] print 'Endian: ', gifti_endian_codes.giistring[self.endian] print 'ExternalFileName: ', self.ext_fname print 'ExternalFileOffset: ', self.ext_offset if not self.coordsys == None: print '----' print 'Coordinate System:' print self.coordsys.print_summary() def get_metadata(self): """ Returns metadata as dictionary """ return self.meta.get_metadata() class GiftiImage(object): numDA = int version = str filename = str def __init__(self, meta = None, labeltable = None, darrays = None, version = "1.0"): if darrays is None: darrays = [] self.darrays = darrays if meta is None: self.meta = GiftiMetaData() else: self.meta = meta if labeltable is None: self.labeltable = GiftiLabelTable() else: self.labeltable = labeltable self.numDA = len(self.darrays) self.version = version # @classmethod # def from_array(cls): # pass #def GiftiImage_fromarray(data, intent = GiftiIntentCode.NIFTI_INTENT_NONE, encoding=GiftiEncoding.GIFTI_ENCODING_B64GZ, endian = GiftiEndian.GIFTI_ENDIAN_LITTLE): # """ Returns a GiftiImage from a Numpy array with a given intent code and # encoding """ # @classmethod # def from_vertices_and_triangles(cls): # pass # def from_vertices_and_triangles(cls, vertices, triangles, coordsys = None, \ # encoding = GiftiEncoding.GIFTI_ENCODING_B64GZ,\ # endian = GiftiEndian.GIFTI_ENDIAN_LITTLE): # """ Returns a GiftiImage from two numpy arrays representing the vertices # and the triangles. Additionally defining the coordinate system and encoding """ def get_labeltable(self): return self.labeltable def set_labeltable(self, labeltable): """ Set the labeltable for this GiftiImage Parameters ---------- labeltable : GiftiLabelTable """ if isinstance(labeltable, GiftiLabelTable): self.labeltable = labeltable else: print "Not a valid GiftiLabelTable instance" def get_metadata(self): return self.meta def set_metadata(self, meta): """ Set the metadata for this GiftiImage Parameters ---------- meta : GiftiMetaData Returns ------- None """ if isinstance(meta, GiftiMetaData): self.meta = meta print "New Metadata set. Be aware of changing coordinate transformation!" else: print "Not a valid GiftiMetaData instance" def add_gifti_data_array(self, dataarr): """ Adds a data array to the GiftiImage Parameters ---------- dataarr : GiftiDataArray """ if isinstance(dataarr, GiftiDataArray): self.darrays.append(dataarr) self.numDA += 1 else: print "dataarr paramater must be of tzpe GiftiDataArray" def remove_gifti_data_array(self, ith): """ Removes the ith data array element from the GiftiImage """ self.darrays.pop(ith) self.numDA -= 1 def remove_gifti_data_array_by_intent(self, intent): """ Removes all the data arrays with the given intent type """ intent2remove = intent_codes.code[intent] for dele in self.darrays: if dele.intent == intent2remove: self.darrays.remove(dele) self.numDA -= 1 def getArraysFromIntent(self, intent): """ Returns a a list of GiftiDataArray elements matching the given intent """ it = intent_codes.code[intent] return [x for x in self.darrays if x.intent == it] def print_summary(self): print '----start----' print 'Source filename: ', self.filename print 'Number of data arrays: ', self.numDA print 'Version: ', self.version if not self.meta == None: print '----' print 'Metadata:' print self.meta.print_summary() if not self.labeltable == None: print '----' print 'Labeltable:' print self.labeltable.print_summary() for i, da in enumerate(self.darrays): print '----' print 'DataArray %s:' % i print da.print_summary() print '----end----' def to_xml(self): """ Return XML corresponding to image content """ res = """ \n""" % (self.version, str(self.numDA)) if not self.meta is None: res += self.meta.to_xml() if not self.labeltable is None: res += self.labeltable.to_xml() for dar in self.darrays: res += dar.to_xml() res += "" return res