Skip to content

Commit 5bcf012

Browse files
Merge pull request #1 from effigies/mnt/reshape-gifti-ascii-data
RF: Consistently apply data type, shape and index order
2 parents 6ffeeac + afbcc88 commit 5bcf012

File tree

3 files changed

+41
-44
lines changed

3 files changed

+41
-44
lines changed

nibabel/gifti/gifti.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ def agg_data(self, intent_code=None):
745745
>>> triangles_2 = surf_img.agg_data('triangle')
746746
>>> triangles_3 = surf_img.agg_data(1009) # Numeric code for pointset
747747
>>> print(np.array2string(triangles))
748-
[0 1 2]
748+
[[0 1 2]]
749749
>>> np.array_equal(triangles, triangles_2)
750750
True
751751
>>> np.array_equal(triangles, triangles_3)

nibabel/gifti/parse_gifti_fast.py

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,21 @@ def read_data_block(darray, fname, data, mmap):
6868
if mmap is True:
6969
mmap = 'c'
7070
enclabel = gifti_encoding_codes.label[darray.encoding]
71-
dtype = data_type_codes.type[darray.datatype]
7271

72+
if enclabel not in ('ASCII', 'B64BIN', 'B64GZ', 'External'):
73+
raise GiftiParseError(f'Unknown encoding {darray.encoding}')
74+
75+
# Encode the endianness in the dtype
76+
byteorder = gifti_endian_codes.byteorder[darray.endian]
77+
dtype = data_type_codes.dtype[darray.datatype].newbyteorder(byteorder)
78+
79+
shape = tuple(darray.dims)
80+
order = array_index_order_codes.npcode[darray.ind_ord]
81+
82+
# GIFTI_ENCODING_ASCII
7383
if enclabel == 'ASCII':
74-
# GIFTI_ENCODING_ASCII
75-
c = StringIO(data)
76-
da = np.loadtxt(c, dtype=dtype)
77-
# Reshape to dims specified in GiftiDataArray attributes, but preserve
78-
# existing behaviour of loading as 1D for arrays with a dimension of
79-
# length 1
80-
da = da.reshape(darray.dims).squeeze()
81-
return da # independent of the endianness
82-
elif enclabel not in ('B64BIN', 'B64GZ', 'External'):
83-
return 0
84-
85-
# GIFTI_ENCODING_EXTBIN
84+
return np.loadtxt(StringIO(data), dtype=dtype, ndmin=1).reshape(shape, order=order)
85+
8686
# We assume that the external data file is raw uncompressed binary, with
8787
# the data type/endianness/ordering specified by the other DataArray
8888
# attributes
@@ -98,53 +98,41 @@ def read_data_block(darray, fname, data, mmap):
9898
newarr = None
9999
if mmap:
100100
try:
101-
newarr = np.memmap(
101+
return np.memmap(
102102
ext_fname,
103103
dtype=dtype,
104104
mode=mmap,
105105
offset=darray.ext_offset,
106-
shape=tuple(darray.dims),
106+
shape=shape,
107+
order=order,
107108
)
108109
# If the memmap fails, we ignore the error and load the data into
109110
# memory below
110111
except (AttributeError, TypeError, ValueError):
111112
pass
112113
# mmap=False or np.memmap failed
113114
if newarr is None:
114-
# We can replace this with a call to np.fromfile in numpy>=1.17,
115-
# as an "offset" parameter was added in that version.
116-
with open(ext_fname, 'rb') as f:
117-
f.seek(darray.ext_offset)
118-
nbytes = np.prod(darray.dims) * dtype().itemsize
119-
buff = f.read(nbytes)
120-
newarr = np.frombuffer(buff, dtype=dtype)
115+
return np.fromfile(
116+
ext_fname,
117+
dtype=dtype,
118+
count=np.prod(darray.dims),
119+
offset=darray.ext_offset,
120+
).reshape(shape, order=order)
121121

122122
# Numpy arrays created from bytes objects are read-only.
123123
# Neither b64decode nor decompress will return bytearrays, and there
124124
# are not equivalents to fobj.readinto to allow us to pass them, so
125125
# there is not a simple way to avoid making copies.
126126
# If this becomes a problem, we should write a decoding interface with
127127
# a tunable chunk size.
128+
dec = base64.b64decode(data.encode('ascii'))
129+
if enclabel == 'B64BIN':
130+
buff = bytearray(dec)
128131
else:
129-
dec = base64.b64decode(data.encode('ascii'))
130-
if enclabel == 'B64BIN':
131-
# GIFTI_ENCODING_B64BIN
132-
buff = bytearray(dec)
133-
else:
134-
# GIFTI_ENCODING_B64GZ
135-
buff = bytearray(zlib.decompress(dec))
136-
del dec
137-
newarr = np.frombuffer(buff, dtype=dtype)
138-
139-
sh = tuple(darray.dims)
140-
if len(newarr.shape) != len(sh):
141-
newarr = newarr.reshape(sh, order=array_index_order_codes.npcode[darray.ind_ord])
142-
143-
# check if we need to byteswap
144-
required_byteorder = gifti_endian_codes.byteorder[darray.endian]
145-
if required_byteorder in ('big', 'little') and required_byteorder != sys.byteorder:
146-
newarr = newarr.byteswap()
147-
return newarr
132+
# GIFTI_ENCODING_B64GZ
133+
buff = bytearray(zlib.decompress(dec))
134+
del dec
135+
return np.frombuffer(buff, dtype=dtype).reshape(shape, order=order)
148136

149137

150138
def _str2int(in_str):

nibabel/gifti/tests/test_parse_gifti_fast.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@
4141
DATA_FILE7 = pjoin(IO_DATA_PATH, 'external.gii')
4242
DATA_FILE8 = pjoin(IO_DATA_PATH, 'ascii_flat_data.gii')
4343

44-
datafiles = [DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4, DATA_FILE5, DATA_FILE6, DATA_FILE7, DATA_FILE8]
44+
datafiles = [
45+
DATA_FILE1,
46+
DATA_FILE2,
47+
DATA_FILE3,
48+
DATA_FILE4,
49+
DATA_FILE5,
50+
DATA_FILE6,
51+
DATA_FILE7,
52+
DATA_FILE8,
53+
]
4554
numDA = [2, 1, 1, 1, 2, 1, 2, 2]
4655

4756
DATA_FILE1_darr1 = np.array(
@@ -51,7 +60,7 @@
5160
[-17.614349, -65.401642, 21.071466],
5261
]
5362
)
54-
DATA_FILE1_darr2 = np.array([0, 1, 2])
63+
DATA_FILE1_darr2 = np.array([[0, 1, 2]])
5564

5665
DATA_FILE2_darr1 = np.array(
5766
[

0 commit comments

Comments
 (0)