Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/dev/13931.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for interactive label browsing using ``hover=True`` in :meth:`mne.viz.Brain.add_annotation`, by `Eric Larson`_.
3 changes: 2 additions & 1 deletion mne/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ def pytest_configure(config: pytest.Config):
"slowtest: mark a test as slow",
"ultraslowtest: mark a test as ultraslow or to be run rarely",
"pgtest: mark a test as relevant for mne-qt-browser",
"pvtest: mark a test as relevant for pyvistaqt",
# used by PyVista's MNE integration tests (but also useful in some testing):
"pvtest: mark a test as relevant for pyvista",
):
config.addinivalue_line("markers", marker)

Expand Down
91 changes: 8 additions & 83 deletions mne/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
_check_fname,
_check_option,
_check_subject,
_import_nibabel,
_validate_type,
check_random_state,
fill_doc,
Expand Down Expand Up @@ -2123,87 +2124,6 @@ def _read_annot_cands(dir_name, raise_error=True):
return cands


def _read_annot(fname):
"""Read a Freesurfer annotation from a .annot file.

Note : Copied from PySurfer

Parameters
----------
fname : str
Path to annotation file

Returns
-------
annot : numpy array, shape=(n_verts)
Annotation id at each vertex
ctab : numpy array, shape=(n_entries, 5)
RGBA + label id colortable array
names : list of str
List of region names as stored in the annot file

"""
if not op.isfile(fname):
dir_name = op.split(fname)[0]
cands = _read_annot_cands(dir_name)
if len(cands) == 0:
raise OSError(
f"No such file {fname}, no candidate parcellations found in directory"
)
else:
raise OSError(
f"No such file {fname}, candidate parcellations in "
"that directory:\n" + "\n".join(cands)
)
with open(fname, "rb") as fid:
n_verts = np.fromfile(fid, ">i4", 1)[0]
data = np.fromfile(fid, ">i4", n_verts * 2).reshape(n_verts, 2)
annot = data[data[:, 0], 1]
ctab_exists = np.fromfile(fid, ">i4", 1)[0]
if not ctab_exists:
raise Exception("Color table not found in annotation file")
n_entries = np.fromfile(fid, ">i4", 1)[0]
if n_entries > 0:
length = np.fromfile(fid, ">i4", 1)[0]
np.fromfile(fid, ">c", length) # discard orig_tab

names = list()
ctab = np.zeros((n_entries, 5), np.int64)
for i in range(n_entries):
name_length = np.fromfile(fid, ">i4", 1)[0]
name = np.fromfile(fid, f"|S{name_length}", 1)[0]
names.append(name)
ctab[i, :4] = np.fromfile(fid, ">i4", 4)
ctab[i, 4] = (
ctab[i, 0]
+ ctab[i, 1] * (2**8)
+ ctab[i, 2] * (2**16)
+ ctab[i, 3] * (2**24)
)
else:
ctab_version = -n_entries
if ctab_version != 2:
raise Exception("Color table version not supported")
n_entries = np.fromfile(fid, ">i4", 1)[0]
ctab = np.zeros((n_entries, 5), np.int64)
length = np.fromfile(fid, ">i4", 1)[0]
np.fromfile(fid, f"|S{length}", 1) # Orig table path
entries_to_read = np.fromfile(fid, ">i4", 1)[0]
names = list()
for i in range(entries_to_read):
np.fromfile(fid, ">i4", 1) # Structure
name_length = np.fromfile(fid, ">i4", 1)[0]
name = np.fromfile(fid, f"|S{name_length}", 1)[0]
names.append(name)
ctab[i, :4] = np.fromfile(fid, ">i4", 4)
ctab[i, 4] = ctab[i, 0] + ctab[i, 1] * (2**8) + ctab[i, 2] * (2**16)

# convert to more common alpha value
ctab[:, 3] = 255 - ctab[:, 3]

return annot, ctab, names


def _get_annot_fname(annot_fname, subject, hemi, parc, subjects_dir):
"""Get the .annot filenames and hemispheres."""
if annot_fname is not None:
Expand Down Expand Up @@ -2251,6 +2171,7 @@ def read_labels_from_annot(
parc="aparc",
hemi="both",
surf_name="white",
*,
annot_fname=None,
regexp=None,
subjects_dir=None,
Expand Down Expand Up @@ -2295,6 +2216,8 @@ def read_labels_from_annot(
write_labels_to_annot
morph_labels
"""
nib = _import_nibabel("Reading labels from parcellations")

logger.info("Reading labels from parcellation...")

subjects_dir = get_subjects_dir(subjects_dir)
Expand All @@ -2318,7 +2241,9 @@ def read_labels_from_annot(
orig_names = set()
for fname, hemi in zip(annot_fname, hemis):
# read annotation
annot, ctab, label_names = _read_annot(fname)
_check_fname(fname, overwrite="read", must_exist=True, name="annotation file")
annot, ctab, label_names = nib.freesurfer.io.read_annot(fname, orig_ids=True)
ctab[:, 3] = 255 - ctab[:, 3]
label_rgbas = ctab[:, :4] / 255.0
label_ids = ctab[:, -1]

Expand Down Expand Up @@ -2362,7 +2287,7 @@ def read_labels_from_annot(
labels = sorted(labels, key=lambda label: label.name)

if len(labels) == 0:
msg = "No labels found."
msg = f"No labels found in {annot_fname[0]}."
if regexp is not None:
orig_names = "\n".join(sorted(orig_names))
msg += (
Expand Down
5 changes: 1 addition & 4 deletions mne/tests/test_label.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
_blend_colors,
_load_vert_pos,
_n_colors,
_read_annot,
_read_annot_cands,
label_sign_flip,
select_sources,
Expand Down Expand Up @@ -380,7 +379,7 @@ def test_annot_io(tmp_path):
shutil.copy(surf_src / "rh.white", surf_dir)

# read original labels
with pytest.raises(OSError, match="\nPALS_B12_Lobes$"):
with pytest.raises(OSError, match="PALS_B12_Lobesey"):
read_labels_from_annot(subject, "PALS_B12_Lobesey", subjects_dir=tmp_path)
labels = read_labels_from_annot(subject, "PALS_B12_Lobes", subjects_dir=tmp_path)

Expand Down Expand Up @@ -486,8 +485,6 @@ def test_read_labels_from_annot(tmp_path):
)
with pytest.raises(OSError, match="does not exist"):
_read_annot_cands("foo")
with pytest.raises(OSError, match="no candidate"):
_read_annot(str(tmp_path))

# read labels using hemi specification
labels_lh = read_labels_from_annot("sample", hemi="lh", subjects_dir=subjects_dir)
Expand Down
8 changes: 7 additions & 1 deletion mne/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,13 @@ def _auto_weakref(function):
__weakref_values__ = dict()
evaldict = dict(__weakref_values__=__weakref_values__)
for name, value in zip(names, function.__closure__):
__weakref_values__[name] = weakref.ref(value.cell_contents)
try:
__weakref_values__[name] = weakref.ref(value.cell_contents)
except TypeError: # pragma: no cover
raise TypeError(
f"Cannot create weak reference to {name} "
f"(type {type(value.cell_contents)})"
)
body = dedent(inspect.getsource(function))
body = body.splitlines()
for li, line in enumerate(body):
Expand Down
Loading
Loading