3
3
from os .path import join as pjoin , isdir
4
4
import getpass
5
5
import time
6
+ import struct
6
7
import hashlib
7
8
import warnings
8
9
15
16
16
17
from .. import (read_geometry , read_morph_data , read_annot , read_label ,
17
18
write_geometry , write_morph_data , write_annot )
19
+ from ..io import _pack_rgba
18
20
19
21
from ...tests .nibabel_data import get_nibabel_data , needs_nibabel_data
20
22
from ...fileslice import strided_scalar
@@ -169,7 +171,7 @@ def test_write_morph_data():
169
171
170
172
@freesurfer_test
171
173
def test_annot ():
172
- """Test IO of .annot"""
174
+ """Test IO of .annot against freesurfer example data. """
173
175
annots = ['aparc' , 'aparc.a2005s' ]
174
176
for a in annots :
175
177
annot_path = pjoin (data_path , "label" , "%s.%s.annot" % ("lh" , a ))
@@ -210,14 +212,12 @@ def test_annot():
210
212
211
213
212
214
def test_read_write_annot ():
213
-
215
+ """Test generating .annot fiole and reading it back."""
214
216
# This annot file will store a LUT for a mesh made of 10 vertices, with
215
217
# 3 colours in the LUT.
216
218
nvertices = 10
217
219
nlabels = 3
218
-
219
220
names = ['label {}' .format (l ) for l in range (1 , nlabels + 1 )]
220
-
221
221
# randomly generate a label for each vertex, making sure
222
222
# that at least one of each label value is present. Label
223
223
# values are in the range (0, nlabels-1) - they are used
@@ -226,24 +226,19 @@ def test_read_write_annot():
226
226
list (np .random .randint (0 , nlabels , nvertices - nlabels ))
227
227
labels = np .array (labels , dtype = np .int32 )
228
228
np .random .shuffle (labels )
229
-
230
229
# Generate some random colours for the LUT
231
230
rgbal = np .zeros ((nlabels , 5 ), dtype = np .int32 )
232
231
rgbal [:, :4 ] = np .random .randint (0 , 255 , (nlabels , 4 ))
233
-
234
232
# But make sure we have at least one large alpha, to make sure that when
235
233
# it is packed into a signed 32 bit int, it results in a negative value
236
234
# for the annotation value.
237
235
rgbal [0 , 3 ] = 255
238
-
239
236
# Generate the annotation values for each LUT entry
240
237
rgbal [:, 4 ] = (rgbal [:, 0 ] +
241
238
rgbal [:, 1 ] * (2 ** 8 ) +
242
239
rgbal [:, 2 ] * (2 ** 16 ) +
243
240
rgbal [:, 3 ] * (2 ** 24 ))
244
-
245
241
annot_path = 'c.annot'
246
-
247
242
with InTemporaryDirectory ():
248
243
write_annot (annot_path , labels , rgbal , names , fill_ctab = False )
249
244
labels2 , rgbal2 , names2 = read_annot (annot_path )
@@ -253,6 +248,7 @@ def test_read_write_annot():
253
248
254
249
255
250
def test_write_annot_fill_ctab ():
251
+ """Test the `fill_ctab` parameter to :func:`.write_annot`. """
256
252
nvertices = 10
257
253
nlabels = 3
258
254
names = ['label {}' .format (l ) for l in range (1 , nlabels + 1 )]
@@ -262,14 +258,12 @@ def test_write_annot_fill_ctab():
262
258
np .random .shuffle (labels )
263
259
rgba = np .array (np .random .randint (0 , 255 , (nlabels , 4 )), dtype = np .int32 )
264
260
annot_path = 'c.annot'
265
-
266
261
with InTemporaryDirectory ():
267
262
write_annot (annot_path , labels , rgba , names , fill_ctab = True )
268
263
labels2 , rgbal2 , names2 = read_annot (annot_path )
269
264
assert np .all (np .isclose (rgbal2 [:, :4 ], rgba ))
270
265
assert np .all (np .isclose (labels2 , labels ))
271
266
assert names2 == names
272
-
273
267
# make sure a warning is emitted if fill_ctab is False, and the
274
268
# annotation values are wrong. Use orig_ids=True so we get those bad
275
269
# values back.
@@ -285,7 +279,6 @@ def test_write_annot_fill_ctab():
285
279
assert np .all (np .isclose (rgbal2 [:, :4 ], rgba ))
286
280
assert np .all (np .isclose (labels2 , badannot [labels ].squeeze ()))
287
281
assert names2 == names
288
-
289
282
# make sure a warning is *not* emitted if fill_ctab is False, but the
290
283
# annotation values are correct.
291
284
rgbal = np .hstack ((rgba , np .zeros ((nlabels , 1 ), dtype = np .int32 )))
@@ -304,6 +297,49 @@ def test_write_annot_fill_ctab():
304
297
assert names2 == names
305
298
306
299
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
+
307
343
@freesurfer_test
308
344
def test_label ():
309
345
"""Test IO of .label"""
0 commit comments