Skip to content

Commit 94cb828

Browse files
committed
Merge branch 'ppc-fixes-rebased' into main-master
* ppc-fixes-rebased: (22 commits) RF - correct name of timeseries option and add misspelled alias BF - encode string data as utf-8 on save, matching specified encoding BF+TEST - set data array datatype correctly RF - move datatype def and intents back to nifti1 to reduce duplication RF - added ability for make_dt_codes to include niistring field, with tests RF - increase use of Recoder; clean whitespace; use class in classmethod RF - klass used in classmethod (in case of inheritance); replace mutable default arg BF - use InTemporaryDirectory for gifti round trip test RF - refactoring gifti code etc use, cleaning whitespace for PEP8 Removed debug messages again. Test pass on Mac BF - Correct save endianness BF - Update endianness while reading BF - Update write logic Possible fix Print Print Test Add more test message BF - Add print statements BF - First byteswap fix ...
2 parents f000c95 + 317381c commit 94cb828

File tree

9 files changed

+325
-384
lines changed

9 files changed

+325
-384
lines changed

nibabel/gifti/gifti.py

Lines changed: 64 additions & 104 deletions
Large diffs are not rendered by default.

nibabel/gifti/giftiio.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def write(image, filename):
4242
A GiftiImage instance to store
4343
filename : string
4444
Filename to store the Gifti file to
45-
45+
4646
Returns
4747
-------
4848
None
@@ -72,7 +72,10 @@ def write(image, filename):
7272
Time Series
7373
.topo.gii
7474
Topology
75+
76+
The Gifti file is stored in endian convention of the current machine.
7577
"""
76-
f = open(filename, 'w')
77-
f.write(image.to_xml())
78+
f = open(filename, 'wb')
79+
# Our giftis are always utf-8 encoded - see GiftiImage.to_xml
80+
f.write(image.to_xml().encode('utf-8'))
7881
f.close()

nibabel/gifti/parse_gifti_fast.py

Lines changed: 41 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99

1010
import base64
11+
import sys
1112
import zlib
1213
from StringIO import StringIO
1314
from xml.parsers.expat import ParserCreate, ExpatError
1415

1516
import numpy as np
1617

18+
from ..nifti1 import data_type_codes, xform_codes, intent_codes
1719
from . import gifti as gi
18-
from .util import (data_type_codes, xform_codes, intent_codes,
19-
array_index_order_codes, gifti_encoding_codes,
20+
from .util import (array_index_order_codes, gifti_encoding_codes,
2021
gifti_endian_codes)
2122

2223

@@ -25,32 +26,24 @@
2526

2627
def read_data_block(encoding, endian, ordering, datatype, shape, data):
2728
""" Tries to unzip, decode, parse the funny string data """
28-
if ordering == 1:
29-
ord = 'C'
30-
elif ordering == 2:
31-
ord = 'F'
32-
else:
33-
ord = 'C'
34-
35-
if encoding == 1:
29+
ord = array_index_order_codes.npcode[ordering]
30+
enclabel = gifti_encoding_codes.label[encoding]
31+
if enclabel == 'ASCII':
3632
# GIFTI_ENCODING_ASCII
3733
c = StringIO(data)
3834
da = np.loadtxt(c)
3935
da = da.astype(data_type_codes.type[datatype])
36+
# independent of the endianness
4037
return da
41-
42-
elif encoding == 2:
38+
elif enclabel == 'B64BIN':
4339
# GIFTI_ENCODING_B64BIN
4440
dec = base64.decodestring(data.encode('ascii'))
4541
dt = data_type_codes.type[datatype]
4642
sh = tuple(shape)
4743
newarr = np.fromstring(dec, dtype = dt)
48-
if len(newarr.shape) == len(sh):
49-
return newarr
50-
else:
51-
return newarr.reshape(sh, order = ord)
52-
53-
elif encoding == 3:
44+
if len(newarr.shape) != len(sh):
45+
newarr = newarr.reshape(sh, order = ord)
46+
elif enclabel == 'B64GZ':
5447
# GIFTI_ENCODING_B64GZ
5548
# convert to bytes array for python 3.2
5649
# http://diveintopython3.org/strings.html#byte-arrays
@@ -59,16 +52,20 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data):
5952
dt = data_type_codes.type[datatype]
6053
sh = tuple(shape)
6154
newarr = np.fromstring(zdec, dtype = dt)
62-
if len(newarr.shape) == len(sh):
63-
return newarr
64-
else:
65-
return newarr.reshape(sh, order = ord)
66-
67-
elif encoding == 4:
55+
if len(newarr.shape) != len(sh):
56+
newarr = newarr.reshape(sh, order = ord)
57+
elif enclabel == 'External':
6858
# GIFTI_ENCODING_EXTBIN
6959
raise NotImplementedError("In what format are the external files?")
7060
else:
7161
return 0
62+
# check if we need to byteswap
63+
required_byteorder = gifti_endian_codes.byteorder[endian]
64+
if (required_byteorder in ('big', 'little') and
65+
required_byteorder != sys.byteorder):
66+
newarr = newarr.byteswap()
67+
return newarr
68+
7269

7370
class Outputter(object):
7471

@@ -95,11 +92,10 @@ def initialize(self):
9592
# where to write CDATA:
9693
self.write_to = None
9794
self.img = None
98-
95+
9996
def StartElementHandler(self, name, attrs):
10097
if DEBUG_PRINT:
10198
print 'Start element:\n\t', repr(name), attrs
102-
10399
if name == 'GIFTI':
104100
# create gifti image
105101
self.img = gi.GiftiImage()
@@ -110,7 +106,6 @@ def StartElementHandler(self, name, attrs):
110106
self.count_da = False
111107

112108
self.fsm_state.append('GIFTI')
113-
114109
elif name == 'MetaData':
115110
self.fsm_state.append('MetaData')
116111

@@ -120,28 +115,22 @@ def StartElementHandler(self, name, attrs):
120115
else:
121116
# otherwise, create darray.meta
122117
self.meta_da = gi.GiftiMetaData()
123-
124-
125118
elif name == 'MD':
126119
self.nvpair = gi.GiftiNVPairs()
127120
self.fsm_state.append('MD')
128-
129121
elif name == 'Name':
130122
if self.nvpair == None:
131123
raise ExpatError
132124
else:
133125
self.write_to = 'Name'
134-
135126
elif name == 'Value':
136127
if self.nvpair == None:
137128
raise ExpatError
138129
else:
139130
self.write_to = 'Value'
140-
141131
elif name == 'LabelTable':
142132
self.lata = gi.GiftiLabelTable()
143133
self.fsm_state.append('LabelTable')
144-
145134
elif name == 'Label':
146135
self.label = gi.GiftiLabel()
147136
if "Index" in attrs:
@@ -154,9 +143,7 @@ def StartElementHandler(self, name, attrs):
154143
self.label.blue = float(attrs["Blue"])
155144
if "Alpha" in attrs:
156145
self.label.alpha = float(attrs["Alpha"])
157-
158146
self.write_to = 'Label'
159-
160147
elif name == 'DataArray':
161148
self.da = gi.GiftiDataArray()
162149
if "Intent" in attrs:
@@ -171,7 +158,6 @@ def StartElementHandler(self, name, attrs):
171158
di = "Dim%s" % str(i)
172159
if di in attrs:
173160
self.da.dims.append(int(attrs[di]))
174-
175161
# dimensionality has to correspond to the number of DimX given
176162
assert len(self.da.dims) == self.da.num_dim
177163
if "Encoding" in attrs:
@@ -182,49 +168,39 @@ def StartElementHandler(self, name, attrs):
182168
self.da.ext_fname = attrs["ExternalFileName"]
183169
if "ExternalFileOffset" in attrs:
184170
self.da.ext_offset = attrs["ExternalFileOffset"]
185-
186171
self.img.darrays.append(self.da)
187172
self.fsm_state.append('DataArray')
188-
189173
elif name == 'CoordinateSystemTransformMatrix':
190174
self.coordsys = gi.GiftiCoordSystem()
191175
self.img.darrays[-1].coordsys = self.coordsys
192176
self.fsm_state.append('CoordinateSystemTransformMatrix')
193-
194177
elif name == 'DataSpace':
195178
if self.coordsys == None:
196179
raise ExpatError
197180
else:
198181
self.write_to = 'DataSpace'
199-
200182
elif name == 'TransformedSpace':
201183
if self.coordsys == None:
202184
raise ExpatError
203185
else:
204186
self.write_to = 'TransformedSpace'
205-
206187
elif name == 'MatrixData':
207188
if self.coordsys == None:
208189
raise ExpatError
209190
else:
210191
self.write_to = 'MatrixData'
211-
212192
elif name == 'Data':
213193
self.write_to = 'Data'
214194

215-
216195
def EndElementHandler(self, name):
217196
if DEBUG_PRINT:
218197
print 'End element:\n\t', repr(name)
219-
220198
if name == 'GIFTI':
221199
# remove last element of the list
222200
self.fsm_state.pop()
223201
# assert len(self.fsm_state) == 0
224-
225202
elif name == 'MetaData':
226203
self.fsm_state.pop()
227-
228204
if len(self.fsm_state) == 1:
229205
# only Gifti there, so this was a closing global
230206
# metadata tag
@@ -233,26 +209,19 @@ def EndElementHandler(self, name):
233209
else:
234210
self.img.darrays[-1].meta = self.meta_da
235211
self.meta_da = None
236-
237212
elif name == 'MD':
238213
self.fsm_state.pop()
239-
240214
if not self.meta_global is None and self.meta_da == None:
241215
self.meta_global.data.append(self.nvpair)
242-
243216
elif not self.meta_da is None and self.meta_global == None:
244217
self.meta_da.data.append(self.nvpair)
245-
246218
# remove reference
247219
self.nvpair = None
248-
249220
elif name == 'LabelTable':
250221
self.fsm_state.pop()
251-
252222
# add labeltable
253223
self.img.labeltable = self.lata
254224
self.lata = None
255-
256225
elif name == 'DataArray':
257226
if self.count_da:
258227
self.img.numDA += 1
@@ -277,9 +246,7 @@ def EndElementHandler(self, name):
277246
self.label = None
278247
self.write_to = None
279248

280-
281249
def CharacterDataHandler(self, data):
282-
283250
if self.write_to == 'Name':
284251
data = data.strip()
285252
self.nvpair.name = data
@@ -302,30 +269,41 @@ def CharacterDataHandler(self, data):
302269
da_tmp.data = read_data_block(da_tmp.encoding, da_tmp.endian, \
303270
da_tmp.ind_ord, da_tmp.datatype, \
304271
da_tmp.dims, data)
272+
# update the endianness according to the
273+
# current machine setting
274+
self.endian = gifti_endian_codes.code[sys.byteorder]
305275
elif self.write_to == 'Label':
306276
self.label.label = data.strip()
307277

308278

309279
def parse_gifti_file(fname, buffer_size = 35000000):
310-
280+
""" Parse gifti file named `fname`, return image
281+
282+
Parameters
283+
----------
284+
fname : str
285+
filename of gifti file
286+
buffer_size: int, optional
287+
size of read buffer.
288+
289+
Returns
290+
-------
291+
img : gifti image
292+
"""
311293
datasource = open(fname,'rb')
312-
313294
parser = ParserCreate()
314295
parser.buffer_text = True
315296
parser.buffer_size = buffer_size
316-
HANDLER_NAMES = [
317-
'StartElementHandler', 'EndElementHandler',
318-
'CharacterDataHandler',
319-
]
297+
HANDLER_NAMES = ['StartElementHandler',
298+
'EndElementHandler',
299+
'CharacterDataHandler']
320300
out = Outputter()
321301
for name in HANDLER_NAMES:
322302
setattr(parser, name, getattr(out, name))
323-
324303
try:
325304
parser.ParseFile(datasource)
326305
except ExpatError:
327306
print 'An expat error occured while parsing the Gifti file.'
328-
329307
# update filename
330308
out.img.filename = fname
331309
return out.img

nibabel/gifti/tests/test_gifti.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33

44
import numpy as np
55

6-
from ..gifti import GiftiImage
6+
from ...nifti1 import data_type_codes, intent_codes
7+
8+
from ..gifti import GiftiImage, GiftiDataArray
79

810
from numpy.testing import (assert_array_almost_equal,
911
assert_array_equal)
1012

1113
from nose.tools import assert_true, assert_equal, assert_raises
1214

1315

14-
def test_gifti_data():
16+
def test_gifti_image():
1517
# Check that we're not modifying the default empty list in the default
1618
# arguments.
1719
gi = GiftiImage()
@@ -22,3 +24,15 @@ def test_gifti_data():
2224
gi = GiftiImage()
2325
assert_equal(gi.darrays, [])
2426

27+
28+
def test_dataarray():
29+
for dt_code in data_type_codes.value_set():
30+
data_type = data_type_codes.type[dt_code]
31+
if data_type is np.void: # not supported
32+
continue
33+
arr = np.zeros((10,3), dtype=data_type)
34+
da = GiftiDataArray.from_array(arr, 'triangle')
35+
assert_equal(da.datatype, data_type_codes[arr.dtype])
36+
bs_arr = arr.byteswap().newbyteorder()
37+
da = GiftiDataArray.from_array(bs_arr, 'triangle')
38+
assert_equal(da.datatype, data_type_codes[arr.dtype])

0 commit comments

Comments
 (0)