Skip to content

Commit 54c3019

Browse files
author
Ben Cipollini
committed
Merge branch 'gifti-fix3' into gifti-loadsave
2 parents c4e3217 + bbfc4ac commit 54c3019

File tree

3 files changed

+109
-108
lines changed

3 files changed

+109
-108
lines changed

nibabel/gifti/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
"""
1818

1919
from .gifti import (GiftiMetaData, GiftiNVPairs, GiftiLabelTable, GiftiLabel,
20-
GiftiCoordSystem, data_tag, GiftiDataArray, GiftiImage)
20+
GiftiCoordSystem, GiftiDataArray, GiftiImage)

nibabel/gifti/gifti.py

Lines changed: 83 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import numpy as np
1414

15+
from .. import xmlutils as xml
1516
from ..filebasedimages import FileBasedHeader, FileBasedImage
1617
from ..nifti1 import data_type_codes, xform_codes, intent_codes
1718
from .util import (array_index_order_codes, gifti_encoding_codes,
@@ -23,7 +24,7 @@
2324
import base64
2425

2526

26-
class GiftiMetaData(object):
27+
class GiftiMetaData(xml.XmlSerializable):
2728
""" A list of GiftiNVPairs in stored in
2829
the list self.data """
2930
def __init__(self, nvpair=None):
@@ -46,18 +47,15 @@ def get_metadata(self):
4647
self.data_as_dict[ele.name] = ele.value
4748
return self.data_as_dict
4849

49-
def to_xml(self):
50-
if len(self.data) == 0:
51-
return b"<MetaData/>\n"
52-
res = "<MetaData>\n"
50+
def _to_xml_element(self):
51+
metadata = xml.Element('MetaData')
5352
for ele in self.data:
54-
nvpair = """<MD>
55-
\t<Name><![CDATA[%s]]></Name>
56-
\t<Value><![CDATA[%s]]></Value>
57-
</MD>\n""" % (ele.name, ele.value)
58-
res = res + nvpair
59-
res = res + "</MetaData>\n"
60-
return res.encode('utf-8')
53+
md = xml.SubElement(metadata, 'MD')
54+
name = xml.SubElement(md, 'Name')
55+
value = xml.SubElement(md, 'Value')
56+
name.text = ele.name
57+
value.text = ele.value
58+
return metadata
6159

6260
def print_summary(self):
6361
print(self.get_metadata())
@@ -73,7 +71,7 @@ def __init__(self, name='', value=''):
7371
self.value = value
7472

7573

76-
class GiftiLabelTable(object):
74+
class GiftiLabelTable(xml.XmlSerializable):
7775

7876
def __init__(self):
7977
self.labels = []
@@ -84,31 +82,22 @@ def get_labels_as_dict(self):
8482
self.labels_as_dict[ele.key] = ele.label
8583
return self.labels_as_dict
8684

87-
def to_xml(self):
88-
if len(self.labels) == 0:
89-
return b"<LabelTable/>\n"
90-
res = "<LabelTable>\n"
85+
def _to_xml_element(self):
86+
labeltable = xml.Element('LabelTable')
9187
for ele in self.labels:
92-
col = ''
93-
if not ele.red is None:
94-
col += ' Red="%s"' % str(ele.red)
95-
if not ele.green is None:
96-
col += ' Green="%s"' % str(ele.green)
97-
if not ele.blue is None:
98-
col += ' Blue="%s"' % str(ele.blue)
99-
if not ele.alpha is None:
100-
col += ' Alpha="%s"' % str(ele.alpha)
101-
lab = """\t<Label Key="%s"%s><![CDATA[%s]]></Label>\n""" % \
102-
(str(ele.key), col, ele.label)
103-
res = res + lab
104-
res = res + "</LabelTable>\n"
105-
return res.encode('utf-8')
88+
label = xml.SubElement(labeltable, 'Label')
89+
label.attrib['Key'] = str(ele.key)
90+
label.text = ele.label
91+
for attr in ['Red', 'Green', 'Blue', 'Alpha']:
92+
if getattr(ele, attr.lower(), None) is not None:
93+
label.attrib[attr] = str(getattr(ele, attr.lower()))
94+
return labeltable
10695

10796
def print_summary(self):
10897
print(self.get_labels_as_dict())
10998

11099

111-
class GiftiLabel(object):
100+
class GiftiLabel(xml.XmlSerializable):
112101
key = int
113102
label = str
114103
# rgba
@@ -143,7 +132,7 @@ def _arr2txt(arr, elem_fmt):
143132
return '\n'.join(fmt % tuple(row) for row in arr)
144133

145134

146-
class GiftiCoordSystem(object):
135+
class GiftiCoordSystem(xml.XmlSerializable):
147136
dataspace = int
148137
xformspace = int
149138
xform = np.ndarray # 4x4 numpy array
@@ -157,29 +146,26 @@ def __init__(self, dataspace=0, xformspace=0, xform=None):
157146
else:
158147
self.xform = xform
159148

160-
def to_xml(self):
161-
if self.xform is None:
162-
return b"<CoordinateSystemTransformMatrix/>\n"
163-
res = ("""<CoordinateSystemTransformMatrix>
164-
\t<DataSpace><![CDATA[%s]]></DataSpace>
165-
\t<TransformedSpace><![CDATA[%s]]></TransformedSpace>\n"""
166-
% (xform_codes.niistring[self.dataspace],
167-
xform_codes.niistring[self.xformspace]))
168-
res = res + "<MatrixData>\n"
169-
res += _arr2txt(self.xform, '%10.6f')
170-
res = res + "</MatrixData>\n"
171-
res = res + "</CoordinateSystemTransformMatrix>\n"
172-
return res.encode('utf-8')
149+
def _to_xml_element(self):
150+
coord_xform = xml.Element('CoordinateSystemTransformMatrix')
151+
if self.xform is not None:
152+
dataspace = xml.SubElement(coord_xform, 'DataSpace')
153+
dataspace.text = xform_codes.niistring[self.dataspace]
154+
xformed_space = xml.SubElement(coord_xform, 'TransformedSpace')
155+
xformed_space.text = xform_codes.niistring[self.xformspace]
156+
matrix_data = xml.SubElement(coord_xform, 'MatrixData')
157+
matrix_data.text = _arr2txt(self.xform, '%10.6f')
158+
return coord_xform
173159

174160
def print_summary(self):
175161
print('Dataspace: ', xform_codes.niistring[self.dataspace])
176162
print('XFormSpace: ', xform_codes.niistring[self.xformspace])
177163
print('Affine Transformation Matrix: \n', self.xform)
178164

179165

180-
def data_tag(dataarray, encoding, datatype, ordering):
166+
def _data_tag_element(dataarray, encoding, datatype, ordering):
181167
""" Creates the data tag depending on the required encoding,
182-
returns as bytes"""
168+
returns as XML element"""
183169
import zlib
184170
ord = array_index_order_codes.npcode[ordering]
185171
enclabel = gifti_encoding_codes.label[encoding]
@@ -194,10 +180,13 @@ def data_tag(dataarray, encoding, datatype, ordering):
194180
raise NotImplementedError("In what format are the external files?")
195181
else:
196182
da = ''
197-
return ("<Data>" + da + "</Data>\n").encode('utf-8')
198183

184+
data = xml.Element('Data')
185+
data.text = da
186+
return data
199187

200-
class GiftiDataArray(object):
188+
189+
class GiftiDataArray(xml.XmlSerializable):
201190

202191
# These are for documentation only; we don't use these class variables
203192
intent = int
@@ -278,51 +267,35 @@ def from_array(klass,
278267
cda.meta = GiftiMetaData.from_dict(meta)
279268
return cda
280269

281-
def to_xml(self):
270+
def _to_xml_element(self):
282271
# fix endianness to machine endianness
283272
self.endian = gifti_endian_codes.code[sys.byteorder]
284-
result = ""
285-
result += self.to_xml_open().decode('utf-8')
286-
# write metadata
287-
if not self.meta is None:
288-
result += self.meta.to_xml().decode('utf-8')
289-
# write coord sys
290-
if not self.coordsys is None:
291-
result += self.coordsys.to_xml().decode('utf-8')
273+
274+
data_array = xml.Element('DataArray', attrib={
275+
'Intent': intent_codes.niistring[self.intent],
276+
'DataType': data_type_codes.niistring[self.datatype],
277+
'ArrayIndexingOrder': array_index_order_codes.label[self.ind_ord],
278+
'Dimensionality': str(self.num_dim),
279+
'Encoding': gifti_encoding_codes.specs[self.encoding],
280+
'Endian': gifti_endian_codes.specs[self.endian],
281+
'ExternalFileName': self.ext_fname,
282+
'ExternalFileOffset': self.ext_offset})
283+
for di, dn in enumerate(self.dims):
284+
data_array.attrib['Dim%d' % di] = str(dn)
285+
286+
if self.meta is not None:
287+
data_array.append(self.meta._to_xml_element())
288+
if self.coordsys is not None:
289+
data_array.append(self.coordsys._to_xml_element())
292290
# write data array depending on the encoding
293291
dt_kind = data_type_codes.dtype[self.datatype].kind
294-
result += data_tag(self.data,
295-
gifti_encoding_codes.specs[self.encoding],
296-
KIND2FMT[dt_kind],
297-
self.ind_ord).decode('utf-8')
298-
result = result + self.to_xml_close().decode('utf-8')
299-
return result.encode('utf-8')
300-
301-
def to_xml_open(self):
302-
out = """<DataArray Intent="%s"
303-
\tDataType="%s"
304-
\tArrayIndexingOrder="%s"
305-
\tDimensionality="%s"
306-
%s\tEncoding="%s"
307-
\tEndian="%s"
308-
\tExternalFileName="%s"
309-
\tExternalFileOffset="%s">\n"""
310-
di = ""
311-
for i, n in enumerate(self.dims):
312-
di = di + '\tDim%s=\"%s\"\n' % (str(i), str(n))
313-
return (out % (intent_codes.niistring[self.intent],
314-
data_type_codes.niistring[self.datatype],
315-
array_index_order_codes.label[self.ind_ord],
316-
str(self.num_dim),
317-
str(di),
318-
gifti_encoding_codes.specs[self.encoding],
319-
gifti_endian_codes.specs[self.endian],
320-
self.ext_fname,
321-
self.ext_offset,
322-
)).encode('utf-8')
323-
324-
def to_xml_close(self):
325-
return b"</DataArray>\n"
292+
data_array.append(
293+
_data_tag_element(self.data,
294+
gifti_encoding_codes.specs[self.encoding],
295+
KIND2FMT[dt_kind],
296+
self.ind_ord))
297+
298+
return data_array
326299

327300
def print_summary(self):
328301
print('Intent: ', intent_codes.niistring[self.intent])
@@ -348,15 +321,14 @@ def get_metadata(self):
348321
class GiftiHeader(FileBasedHeader):
349322
pass
350323

351-
352-
class GiftiImage(FileBasedImage):
324+
class GiftiImage(FileBasedImage, xml.XmlSerializable):
353325
valid_exts = ('.gii',)
354326
files_types = (('image', '.gii'),)
355327

356328
def __init__(self, header=None, extra=None, file_map=None, meta=None,
357329
labeltable=None, darrays=None, version="1.0"):
358-
super(GiftiImage, self).__init__(header=header, extra=extra,
359-
file_map=file_map)
330+
FileBasedImage.__init__(self, header=header, extra=extra,
331+
file_map=file_map)
360332

361333
if darrays is None:
362334
darrays = []
@@ -466,20 +438,24 @@ def print_summary(self):
466438
print(da.print_summary())
467439
print('----end----')
468440

469-
def to_xml(self):
441+
442+
def _to_xml_element(self):
443+
GIFTI = xml.Element('GIFTI', attrib={
444+
'Version': self.version,
445+
'NumberOfDataArrays': str(self.numDA)})
446+
if self.meta is not None:
447+
GIFTI.append(self.meta._to_xml_element())
448+
if self.labeltable is not None:
449+
GIFTI.append(self.labeltable._to_xml_element())
450+
for dar in self.darrays:
451+
GIFTI.append(dar._to_xml_element())
452+
return GIFTI
453+
454+
def to_xml(self, enc='utf-8'):
470455
""" Return XML corresponding to image content """
471-
res = """<?xml version="1.0" encoding="UTF-8"?>
456+
return b"""<?xml version="1.0" encoding="UTF-8"?>
472457
<!DOCTYPE GIFTI SYSTEM "http://www.nitrc.org/frs/download.php/115/gifti.dtd">
473-
<GIFTI Version="%s" NumberOfDataArrays="%s">\n""" % (self.version,
474-
str(self.numDA))
475-
if not self.meta is None:
476-
res += self.meta.to_xml().decode('utf-8')
477-
if not self.labeltable is None:
478-
res += self.labeltable.to_xml().decode('utf-8')
479-
for dar in self.darrays:
480-
res += dar.to_xml().decode('utf-8')
481-
res += "</GIFTI>"
482-
return res.encode('utf-8')
458+
""" + xml.XmlSerializable.to_xml(self, enc)
483459

484460
@classmethod
485461
def from_file_map(klass, file_map):

nibabel/xmlutils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4+
#
5+
# See COPYING file distributed along with the NiBabel package for the
6+
# copyright and license terms.
7+
#
8+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9+
"""
10+
Thin layer around xml.etree.ElementTree, to abstract nibabel xml support.
11+
"""
12+
from xml.etree.ElementTree import Element, SubElement, tostring
13+
14+
15+
class XmlSerializable(object):
16+
""" Basic interface for serializing an object to xml"""
17+
18+
def _to_xml_element(self):
19+
""" Output should be a xml.etree.ElementTree.Element"""
20+
raise NotImplementedError()
21+
22+
def to_xml(self, enc='utf-8'):
23+
""" Output should be an xml string with the given encoding.
24+
(default: utf-8)"""
25+
return tostring(self._to_xml_element(), enc)

0 commit comments

Comments
 (0)