Skip to content

Commit 7877add

Browse files
authored
Merge pull request #666 from effigies/fix/annot_labels
FIX: Annotation labels are packed RGB, not RGBA
2 parents 436df7c + 7b59675 commit 7877add

File tree

2 files changed

+33
-21
lines changed

2 files changed

+33
-21
lines changed

nibabel/freesurfer/io.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,24 @@ def _read_volume_info(fobj):
8181
return volume_info
8282

8383

84-
def _pack_rgba(rgba):
85-
"""Pack an RGBA sequence into a single integer.
84+
def _pack_rgb(rgb):
85+
"""Pack an RGB sequence into a single integer.
8686
8787
Used by :func:`read_annot` and :func:`write_annot` to generate
8888
"annotation values" for a Freesurfer ``.annot`` file.
8989
9090
Parameters
9191
----------
92-
rgba : ndarray, shape (n, 4)
93-
RGBA colors
92+
rgb : ndarray, shape (n, 3)
93+
RGB colors
9494
9595
Returns
9696
-------
9797
out : ndarray, shape (n, 1)
9898
Annotation values for each color.
9999
"""
100-
bitshifts = 2 ** np.array([[0], [8], [16], [24]], dtype=rgba.dtype)
101-
return rgba.dot(bitshifts)
100+
bitshifts = 2 ** np.array([[0], [8], [16]], dtype=rgb.dtype)
101+
return rgb.dot(bitshifts)
102102

103103

104104
def read_geometry(filepath, read_metadata=False, read_stamp=False):
@@ -333,9 +333,13 @@ def read_annot(filepath, orig_ids=False):
333333
Annotation file format versions 1 and 2 are supported, corresponding to
334334
the "old-style" and "new-style" color table layout.
335335
336+
Note that the output color table ``ctab`` is in RGBT form, where T
337+
(transparency) is 255 - alpha.
338+
336339
See:
337340
* https://surfer.nmr.mgh.harvard.edu/fswiki/LabelsClutsAnnotationFiles#Annotation
338341
* https://github.com/freesurfer/freesurfer/blob/dev/matlab/read_annotation.m
342+
* https://github.com/freesurfer/freesurfer/blob/8b88b34/utils/colortab.c
339343
340344
Parameters
341345
----------
@@ -352,7 +356,7 @@ def read_annot(filepath, orig_ids=False):
352356
Annotation id at each vertex. If a vertex does not belong
353357
to any label and orig_ids=False, its id will be set to -1.
354358
ctab : ndarray, shape (n_labels, 5)
355-
RGBA + label id colortable array.
359+
RGBT + label id colortable array.
356360
names : list of str (python 2), list of bytes (python 3)
357361
The names of the labels. The length of the list is n_labels.
358362
"""
@@ -384,7 +388,7 @@ def read_annot(filepath, orig_ids=False):
384388
ctab, names = _read_annot_ctab_new_format(fobj, -n_entries)
385389

386390
# generate annotation values for each LUT entry
387-
ctab[:, [4]] = _pack_rgba(ctab[:, :4])
391+
ctab[:, [4]] = _pack_rgb(ctab[:, :3])
388392

389393
if not orig_ids:
390394
ord = np.argsort(ctab[:, -1])
@@ -397,6 +401,9 @@ def read_annot(filepath, orig_ids=False):
397401
def _read_annot_ctab_old_format(fobj, n_entries):
398402
"""Read in an old-style Freesurfer color table from `fobj`.
399403
404+
Note that the output color table ``ctab`` is in RGBT form, where T
405+
(transparency) is 255 - alpha.
406+
400407
This function is used by :func:`read_annot`.
401408
402409
Parameters
@@ -412,7 +419,7 @@ def _read_annot_ctab_old_format(fobj, n_entries):
412419
-------
413420
414421
ctab : ndarray, shape (n_entries, 5)
415-
RGBA colortable array - the last column contains all zeros.
422+
RGBT colortable array - the last column contains all zeros.
416423
names : list of str
417424
The names of the labels. The length of the list is n_entries.
418425
"""
@@ -430,7 +437,7 @@ def _read_annot_ctab_old_format(fobj, n_entries):
430437
name_length = np.fromfile(fobj, dt, 1)[0]
431438
name = np.fromfile(fobj, "|S%d" % name_length, 1)[0]
432439
names.append(name)
433-
# read RGBA for this entry
440+
# read RGBT for this entry
434441
ctab[i, :4] = np.fromfile(fobj, dt, 4)
435442

436443
return ctab, names
@@ -439,6 +446,9 @@ def _read_annot_ctab_old_format(fobj, n_entries):
439446
def _read_annot_ctab_new_format(fobj, ctab_version):
440447
"""Read in a new-style Freesurfer color table from `fobj`.
441448
449+
Note that the output color table ``ctab`` is in RGBT form, where T
450+
(transparency) is 255 - alpha.
451+
442452
This function is used by :func:`read_annot`.
443453
444454
Parameters
@@ -454,7 +464,7 @@ def _read_annot_ctab_new_format(fobj, ctab_version):
454464
-------
455465
456466
ctab : ndarray, shape (n_labels, 5)
457-
RGBA colortable array - the last column contains all zeros.
467+
RGBT colortable array - the last column contains all zeros.
458468
names : list of str
459469
The names of the labels. The length of the list is n_labels.
460470
"""
@@ -480,7 +490,7 @@ def _read_annot_ctab_new_format(fobj, ctab_version):
480490
name_length = np.fromfile(fobj, dt, 1)[0]
481491
name = np.fromfile(fobj, "|S%d" % name_length, 1)[0]
482492
names.append(name)
483-
# RGBA
493+
# RGBT
484494
ctab[idx, :4] = np.fromfile(fobj, dt, 4)
485495

486496
return ctab, names
@@ -489,9 +499,13 @@ def _read_annot_ctab_new_format(fobj, ctab_version):
489499
def write_annot(filepath, labels, ctab, names, fill_ctab=True):
490500
"""Write out a "new-style" Freesurfer annotation file.
491501
502+
Note that the color table ``ctab`` is in RGBT form, where T (transparency)
503+
is 255 - alpha.
504+
492505
See:
493506
* https://surfer.nmr.mgh.harvard.edu/fswiki/LabelsClutsAnnotationFiles#Annotation
494507
* https://github.com/freesurfer/freesurfer/blob/dev/matlab/write_annotation.m
508+
* https://github.com/freesurfer/freesurfer/blob/8b88b34/utils/colortab.c
495509
496510
Parameters
497511
----------
@@ -500,7 +514,7 @@ def write_annot(filepath, labels, ctab, names, fill_ctab=True):
500514
labels : ndarray, shape (n_vertices,)
501515
Annotation id at each vertex.
502516
ctab : ndarray, shape (n_labels, 5)
503-
RGBA + label id colortable array.
517+
RGBT + label id colortable array.
504518
names : list of str
505519
The names of the labels. The length of the list is n_labels.
506520
fill_ctab : {True, False} optional
@@ -523,8 +537,8 @@ def write_string(s):
523537

524538
# Generate annotation values for each ctab entry
525539
if fill_ctab:
526-
ctab = np.hstack((ctab[:, :4], _pack_rgba(ctab[:, :4])))
527-
elif not np.array_equal(ctab[:, [4]], _pack_rgba(ctab[:, :4])):
540+
ctab = np.hstack((ctab[:, :4], _pack_rgb(ctab[:, :3])))
541+
elif not np.array_equal(ctab[:, [4]], _pack_rgb(ctab[:, :3])):
528542
warnings.warn('Annotation values in {} will be incorrect'.format(
529543
filepath))
530544

nibabel/freesurfer/tests/test_io.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from .. import (read_geometry, read_morph_data, read_annot, read_label,
1818
write_geometry, write_morph_data, write_annot)
19-
from ..io import _pack_rgba
19+
from ..io import _pack_rgb
2020

2121
from ...tests.nibabel_data import get_nibabel_data, needs_nibabel_data
2222
from ...fileslice import strided_scalar
@@ -236,8 +236,7 @@ def test_read_write_annot():
236236
# Generate the annotation values for each LUT entry
237237
rgbal[:, 4] = (rgbal[:, 0] +
238238
rgbal[:, 1] * (2 ** 8) +
239-
rgbal[:, 2] * (2 ** 16) +
240-
rgbal[:, 3] * (2 ** 24))
239+
rgbal[:, 2] * (2 ** 16))
241240
annot_path = 'c.annot'
242241
with InTemporaryDirectory():
243242
write_annot(annot_path, labels, rgbal, names, fill_ctab=False)
@@ -287,8 +286,7 @@ def test_write_annot_fill_ctab():
287286
rgbal = np.hstack((rgba, np.zeros((nlabels, 1), dtype=np.int32)))
288287
rgbal[:, 4] = (rgbal[:, 0] +
289288
rgbal[:, 1] * (2 ** 8) +
290-
rgbal[:, 2] * (2 ** 16) +
291-
rgbal[:, 3] * (2 ** 24))
289+
rgbal[:, 2] * (2 ** 16))
292290
with clear_and_catch_warnings() as w:
293291
write_annot(annot_path, labels, rgbal, names, fill_ctab=False)
294292
assert_true(
@@ -307,7 +305,7 @@ def gen_old_annot_file(fpath, nverts, labels, rgba, names):
307305
dt = '>i'
308306
vdata = np.zeros((nverts, 2), dtype=dt)
309307
vdata[:, 0] = np.arange(nverts)
310-
vdata[:, [1]] = _pack_rgba(rgba[labels, :])
308+
vdata[:, [1]] = _pack_rgb(rgba[labels, :3])
311309
fbytes = b''
312310
# number of vertices
313311
fbytes += struct.pack(dt, nverts)

0 commit comments

Comments
 (0)