Skip to content

Commit a5694e3

Browse files
committed
TEST: New test for reading old-style .annot file. Other minor refactorings.
1 parent 9c67036 commit a5694e3

File tree

1 file changed

+48
-12
lines changed

1 file changed

+48
-12
lines changed

nibabel/freesurfer/tests/test_io.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from os.path import join as pjoin, isdir
44
import getpass
55
import time
6+
import struct
67
import hashlib
78
import warnings
89

@@ -15,6 +16,7 @@
1516

1617
from .. import (read_geometry, read_morph_data, read_annot, read_label,
1718
write_geometry, write_morph_data, write_annot)
19+
from ..io import _pack_rgba
1820

1921
from ...tests.nibabel_data import get_nibabel_data, needs_nibabel_data
2022
from ...fileslice import strided_scalar
@@ -169,7 +171,7 @@ def test_write_morph_data():
169171

170172
@freesurfer_test
171173
def test_annot():
172-
"""Test IO of .annot"""
174+
"""Test IO of .annot against freesurfer example data."""
173175
annots = ['aparc', 'aparc.a2005s']
174176
for a in annots:
175177
annot_path = pjoin(data_path, "label", "%s.%s.annot" % ("lh", a))
@@ -210,14 +212,12 @@ def test_annot():
210212

211213

212214
def test_read_write_annot():
213-
215+
"""Test generating .annot fiole and reading it back."""
214216
# This annot file will store a LUT for a mesh made of 10 vertices, with
215217
# 3 colours in the LUT.
216218
nvertices = 10
217219
nlabels = 3
218-
219220
names = ['label {}'.format(l) for l in range(1, nlabels + 1)]
220-
221221
# randomly generate a label for each vertex, making sure
222222
# that at least one of each label value is present. Label
223223
# values are in the range (0, nlabels-1) - they are used
@@ -226,24 +226,19 @@ def test_read_write_annot():
226226
list(np.random.randint(0, nlabels, nvertices - nlabels))
227227
labels = np.array(labels, dtype=np.int32)
228228
np.random.shuffle(labels)
229-
230229
# Generate some random colours for the LUT
231230
rgbal = np.zeros((nlabels, 5), dtype=np.int32)
232231
rgbal[:, :4] = np.random.randint(0, 255, (nlabels, 4))
233-
234232
# But make sure we have at least one large alpha, to make sure that when
235233
# it is packed into a signed 32 bit int, it results in a negative value
236234
# for the annotation value.
237235
rgbal[0, 3] = 255
238-
239236
# Generate the annotation values for each LUT entry
240237
rgbal[:, 4] = (rgbal[:, 0] +
241238
rgbal[:, 1] * (2 ** 8) +
242239
rgbal[:, 2] * (2 ** 16) +
243240
rgbal[:, 3] * (2 ** 24))
244-
245241
annot_path = 'c.annot'
246-
247242
with InTemporaryDirectory():
248243
write_annot(annot_path, labels, rgbal, names, fill_ctab=False)
249244
labels2, rgbal2, names2 = read_annot(annot_path)
@@ -253,6 +248,7 @@ def test_read_write_annot():
253248

254249

255250
def test_write_annot_fill_ctab():
251+
"""Test the `fill_ctab` parameter to :func:`.write_annot`. """
256252
nvertices = 10
257253
nlabels = 3
258254
names = ['label {}'.format(l) for l in range(1, nlabels + 1)]
@@ -262,14 +258,12 @@ def test_write_annot_fill_ctab():
262258
np.random.shuffle(labels)
263259
rgba = np.array(np.random.randint(0, 255, (nlabels, 4)), dtype=np.int32)
264260
annot_path = 'c.annot'
265-
266261
with InTemporaryDirectory():
267262
write_annot(annot_path, labels, rgba, names, fill_ctab=True)
268263
labels2, rgbal2, names2 = read_annot(annot_path)
269264
assert np.all(np.isclose(rgbal2[:, :4], rgba))
270265
assert np.all(np.isclose(labels2, labels))
271266
assert names2 == names
272-
273267
# make sure a warning is emitted if fill_ctab is False, and the
274268
# annotation values are wrong. Use orig_ids=True so we get those bad
275269
# values back.
@@ -285,7 +279,6 @@ def test_write_annot_fill_ctab():
285279
assert np.all(np.isclose(rgbal2[:, :4], rgba))
286280
assert np.all(np.isclose(labels2, badannot[labels].squeeze()))
287281
assert names2 == names
288-
289282
# make sure a warning is *not* emitted if fill_ctab is False, but the
290283
# annotation values are correct.
291284
rgbal = np.hstack((rgba, np.zeros((nlabels, 1), dtype=np.int32)))
@@ -304,6 +297,49 @@ def test_write_annot_fill_ctab():
304297
assert names2 == names
305298

306299

300+
def test_read_annot_old_format():
301+
"""Test reading an old-style .annot file."""
302+
def gen_old_annot_file(fpath, nverts, labels, rgba, names):
303+
dt = '>i'
304+
vdata = np.zeros((nverts, 2))
305+
vdata[:, 0] = np.arange(nverts)
306+
vdata[:, [1]] = _pack_rgba(rgba[labels, :])
307+
fbytes = b''
308+
# number of vertices
309+
fbytes += struct.pack(dt, nverts)
310+
# vertices + annotation values
311+
fbytes += vdata.astype(dt).tobytes()
312+
# is there a colour table?
313+
fbytes += struct.pack(dt, 1)
314+
# number of entries in colour table
315+
fbytes += struct.pack(dt, rgba.shape[0])
316+
# length of orig_tab string
317+
fbytes += struct.pack(dt, 5)
318+
fbytes += b'abcd\00'
319+
for i in range(rgba.shape[0]):
320+
# length of entry name (+1 for terminating byte)
321+
fbytes += struct.pack(dt, len(names[i]) + 1)
322+
fbytes += names[i].encode('ascii') + b'\00'
323+
fbytes += rgba[i, :].astype(dt).tobytes()
324+
with open(fpath, 'wb') as f:
325+
f.write(fbytes)
326+
with InTemporaryDirectory():
327+
nverts = 10
328+
nlabels = 3
329+
names = ['Label {}'.format(l) for l in range(nlabels)]
330+
labels = np.concatenate((
331+
np.arange(nlabels), np.random.randint(0, 3, nverts - nlabels)))
332+
np.random.shuffle(labels)
333+
rgba = np.random.randint(0, 255, (nlabels, 4))
334+
# write an old .annot file
335+
gen_old_annot_file('blah.annot', nverts, labels, rgba, names)
336+
# read it back
337+
rlabels, rrgba, rnames = read_annot('blah.annot')
338+
assert np.all(np.isclose(labels, rlabels))
339+
assert np.all(np.isclose(rgba, rrgba[:, :4]))
340+
assert names == rnames
341+
342+
307343
@freesurfer_test
308344
def test_label():
309345
"""Test IO of .label"""

0 commit comments

Comments
 (0)