-
Notifications
You must be signed in to change notification settings - Fork 145
Description
Hi,
I have encountered this issue with pycortex 1.2.12 where I access FreeSurfer via shims that invoke a containerised installation, so SUBJECTS_DIR is not set in my shell environment. Calling cortex.segment.cut_surface() with freesurfer_subject_dir specified causes a KeyError when SUBJECTS_DIR is inaccessible via os.environ. The issue stems from freesurfer_subject_dir not being passed to freesurfer.get_surf() during the vertex check for 'inflated' and 'fiducial' surfaces (line 188-189). This causes subsequent function calls to default the freesurfer_subject_dir path to None, which triggers an error in freesurfer.get_paths() if SUBJECTS_DIR is not exported to the environment (as in my case).
In [3]: cortex.segment.cut_surface(cx_subject="sub-102311", hemi="lh", name="flatten", freesurfer_subject_dir="/home/keanu/projects/hcp-retinotopy/derivatives/freesurfer", flatten_with="blender", recache=True, do_import_subject=False)
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[3], line 1
----> 1 cortex.segment.cut_surface(cx_subject="sub-102311", hemi="lh", name="flatten", freesurfer_subject_dir="/home/keanu/projects/hcp-retinotopy/derivatives/freesurfer", flatten_with="blender", recache=True, do_import_subject=False)
File ~/projects/hcp-retinotopy/.venv/lib/python3.13/site-packages/cortex/segment.py:189, in cut_surface(cx_subject, hemi, name, fs_subject, data, freesurfer_subject_dir, flatten_with, method, do_import_subject, blender_path, recache, auto_overwrite, **kwargs)
184 fs_subject = cx_subject
186 # Double-check that fiducial and inflated vertex counts match
187 # (these may not match if a subject is initially imported from freesurfer to pycortex,
188 # and then edited further for a better segmentation and not re-imported)
--> 189 ipt, ipoly, inrm = freesurfer.get_surf(fs_subject, hemi, "inflated")
190 fpt, fpoly, fnrm = freesurfer.get_surf(fs_subject, hemi, "fiducial")
191 if ipt.shape[0] != fpt.shape[0]:
File ~/projects/hcp-retinotopy/.venv/lib/python3.13/site-packages/cortex/freesurfer.py:495, in get_surf(subject, hemi, type, patch, flatten_step, freesurfer_subject_dir)
493 surf_file = get_paths(subject, hemi, 'surf', freesurfer_subject_dir=freesurfer_subject_dir).format(name='smoothwm')
494 else:
--> 495 surf_file = get_paths(subject, hemi, 'surf', freesurfer_subject_dir=freesurfer_subject_dir).format(name=type)
497 pts, polys = parse_surf(surf_file)
499 if patch is not None:
File ~/projects/hcp-retinotopy/.venv/lib/python3.13/site-packages/cortex/freesurfer.py:43, in get_paths(fs_subject, hemi, type, freesurfer_subject_dir)
27 """Retrieve paths for all surfaces for a subject processed by freesurfer
28
29 Parameters
(...) 40 by freesurfer)
41 """
42 if freesurfer_subject_dir is None:
---> 43 freesurfer_subject_dir = os.environ['SUBJECTS_DIR']
44 base = os.path.join(freesurfer_subject_dir, fs_subject)
45 if type == "patch":
File <frozen os>:717, in __getitem__(self, key)
KeyError: 'SUBJECTS_DIR'
It looks like freesurfer.get_surf() should be passed freesurfer_subject_dir, as in other functions, to prevent the path regressing to None and triggering freesurfer.get_paths() to pull from SUBJECTS_DIR. Under normal installations this would not trigger this error, as SUBJECTS_DIR would be available, however this would still cause issues when specifying a custom path unless it is exported to the environment beforehand.
Suggested fix:
Pass freesurfer_subject_dir to freesurfer.get_surf() calls in segment.cut_surface(). This would not affect cases where the argument is not specified, since it will default to None anyway and be set by the environment SUBJECTS_DIR.
ipt, ipoly, inrm = freesurfer.get_surf(fs_subject, hemi, "inflated", freesurfer_subject_dir=freesurfer_subject_dir)
fpt, fpoly, fnrm = freesurfer.get_surf(fs_subject, hemi, "fiducial", freesurfer_subject_dir=freesurfer_subject_dir)
Testing the changes appears to fix the issue:
In [5]: cortex.segment.cut_surface(cx_subject="sub-102311", hemi="lh", name="flatten", freesurfer_subject_dir="/home/keanu/projects/hcp-retinotopy/derivatives/freesurfer", flatten_with="blender", recache=True, do_import_subject=False)
b'created by kltoomer on Sat Jan 24 14:30:38 2026\n'
b'\n'
Vert check ok!
Initializing blender file /home/keanu/projects/hcp-retinotopy/.venv/share/pycortex/db/sub-102311/anatomicals/cutsurf[hemi=lh,name=flatten].blend...
b'created by kltoomer on Sat Jan 24 14:30:30 2026\n'
b'created by kltoomer on Sat Jan 24 14:30:38 2026\n'
In new named temp file: /tmp/tmpcsb2fw00
Calling blender:
blender -b -P /tmp/tmpcsb2fw00
Blender 4.3.2
Adding python site directory to sys.path: /home/keanu/projects/hcp-retinotopy/.venv/lib/python3.13/site-packages/cortex/blender
Adding python site directory to sys.path: /home/keanu/projects/hcp-retinotopy/.venv/lib/python3.13/site-packages
Adding python site directory to sys.path: /home/keanu/projects/hcp-retinotopy/.venv/local/lib/python3.13/dist-packages
Adding python site directory to sys.path: /home/keanu/projects/hcp-retinotopy/.venv/lib/python3/dist-packages
Adding python site directory to sys.path: /home/keanu/projects/hcp-retinotopy/.venv/lib/python3.13/dist-packages
Started init_subject in blender!
Successfully added vcolor 'curvature'
Info: Saved "cutsurf[hemi=lh,name=flatten].blend"
Blender quit
Opening blender file /home/keanu/projects/hcp-retinotopy/.venv/share/pycortex/db/sub-102311/anatomicals/cutsurf[hemi=lh,name=flatten].blend...
In new named temp file: /tmp/tmp8l5mx2a0
Detected Blender 4.3.2
Calling blender:
blender /home/keanu/projects/hcp-retinotopy/.venv/share/pycortex/db/sub-102311/anatomicals/cutsurf[hemi=lh,name=flatten].blend
Read blend: "/home/keanu/projects/hcp-retinotopy/.venv/share/pycortex/db/sub-102311/anatomicals/cutsurf[hemi=lh,name=flatten].blend"