Possible bug nipype freesurfer label2label


#1

Ahoi hoi folks,

while working on some pipelines I stumbled across a possible bug within nipype’s label2label interface, but wanted to ask here before I go nuts on github.

In my pipeline I want to transform some .label files into native space using freesurfer’s label2label. More precisely, I have 5 .label files per hemisphere in two versions that I want to transform from fsaverage into certain participant’s native space. The respective node looks like this:

# Transform the surface ROIs to the target space
  inverse_transform_mni_surface_lh_post2ant = MapNode(Label2Label(hemisphere = 'lh'), 
                                                            iterfield=['source_label'], 
                                                            name = 'inverse_transform_mni_surface_lh_post2ant') 

I collect the remaining inputs via fssource, infosource and selectfiles:

fssource_lh = Node(FreeSurferSource(subjects_dir=fs_dir, hemi='lh'),
            run_without_submitting=True,
            name='fssource_lh') 

infosource = Node(IdentityInterface(fields=['subject_id',
                                        'source_subject']),
                                    name="infosource")
infosource.iterables = [('subject_id', subject_list),
                        ('source_subject', source_id)]

source_subject_white_lh = opj(input_dir_source, '{source_subject}', 'surf/lh.white') # source .white
source_subject_sphere_lh = opj(input_dir_source, '{source_subject}', 'surf/lh.sphere.reg') # source .sphere.reg
mni_surface_lh_post2ant = opj(input_dir_ROI, 'surf_posterior2anterior/lh*.label') # label files in source space that should be transformed
target = opj(input_dir_preproc, 'realign', '{subject_id}', 'mean*merged.nii') # participant's target space

Subsequently I connect everything within my workflow:

inverse_ROI_ANTS_flow.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),
                                                          ('source_subject', 'source_subject')]),
                  (infosource, fssource_lh, [('subject_id', 'subject_id')]),
                  (infosource, fssource_rh, [('subject_id', 'subject_id')]),  
                  (infosource, inverse_transform_mni_surface_lh_post2ant, [('subject_id', 'subject_id')]),
                  (fssource_lh, inverse_transform_mni_surface_lh_post2ant, [('sphere_reg', 'sphere_reg')]),
                  (fssource_lh, inverse_transform_mni_surface_lh_post2ant, [('white', 'white')]),
                  (infosource, inverse_transform_mni_surface_lh_post2ant, [('source_subject', 'source_subject')]),
                  (selectfiles, inverse_transform_mni_surface_lh_post2ant, [('source_subject_white_lh', 'source_white')]),
                  (selectfiles, inverse_transform_mni_surface_lh_post2ant, [('source_subject_sphere_lh', 'source_sphere_reg')]),                 
                  (selectfiles, inverse_transform_mni_surface_lh_post2ant, [('mni_surface_lh_post2ant', 'source_label')]),
                  ])

Running the workflow like this results in the following error:

 Traceback (most recent call last):
 File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/plugins/multiproc.py", line 52, in 
 run_node 
 result['result'] = node.run(updatehash=updatehash)
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 372, in run 
self._run_interface()
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 482, in  
_run_interface 
self._result = self._run_command(execute)
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 613, in 
_run_command
result = self._interface.run()
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/freesurfer/model.py", line 1167, 
in run
return super(Label2Label, self).run(**inputs)
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/freesurfer/base.py", line 162, in 
run
return super(FSCommand, self).run(**inputs)
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/base.py", line 1082, in run
outputs = self.aggregate_outputs(runtime)
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/base.py", line 1153, in 
aggregate_outputs
predicted_outputs = self._list_outputs()
File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/freesurfer/model.py", line 1140, 
in _list_outputs
self.inputs.out_file)
File "/home/lmn/install/miniconda3/lib/python3.6/posixpath.py", line 92, in join
genericpath._check_arg_types('join', a, *p)
File "/home/lmn/install/miniconda3/lib/python3.6/genericpath.py", line 149, in _check_arg_types
(funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not '_Undefined'
Interface Label2Label failed to run. 

As far as I get it, there’s problem creating the output path for the out_files of label2label.
Both, report.rst and command.txt within the node’s workingdir folder look fine. Except of course, I don’t get any output there. Interestingly, the transformed .label files are still correctly generated and stored in the participant’s freesurfer/{subject_id}/label/ folder. Setting a specific out_file name within the node like this:

  inverse_transform_mni_surface_lh_post2ant = MapNode(Label2Label(hemisphere = 'lh',
                                                                  out_file='test.label'), 
                                                            iterfield=['source_label'], 
                                                            name = 'inverse_transform_mni_surface_lh_post2ant') 

solves the error and puts a .label file with the respective name in the node’s workingdir as well as datasink folder.
Am I being a dumb dumb and missing something obvious here, or is it really a label2label/nipype problem in that sense, that the output path isn’t generated/passed correctly?

Would be great to get some input from you.

Best, Peer


#2

It looks like you don’t have a subject directory input (subjects_dir) to inverse_transform_mni_surface_lh_post2ant.


#3

Hey @effigies,

thank you very much for the quick response.
I included the subjects_dir in my node like this:

inverse_transform_mni_surface_lh_post2ant = MapNode(Label2Label(hemisphere = 'lh',
                                                                subjects_dir=fs_dir), 
                                                                iterfield=['source_label'], 
                                                                name = 'inverse_transform_mni_surface_lh_post2ant')

but the same error appears.
Checking the documentation again, I recognized that copy_inputs should be set to True if the function is running as a node, resulting in a node definition like this:

inverse_transform_mni_surface_lh_post2ant = MapNode(Label2Label(hemisphere = 'lh',
                                                                subjects_dir=fs_dir,
                                                                copy_inputs=True), 
                                                                iterfield=['source_label'], 
                                                                name = 'inverse_transform_mni_surface_lh_post2ant')

Unfortunately, doing that results in a different error:

Traceback (most recent call last):
  File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/plugins/multiproc.py", line 52, in run_node
result['result'] = node.run(updatehash=updatehash)
  File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 372, in run
self._run_interface()
  File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 482, in _run_interface
self._result = self._run_command(execute)
  File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 613, in _run_command
result = self._interface.run()
  File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/freesurfer/model.py", line 1150, in run
'{0}.sphere.reg'.format(hemi))
  File "/home/lmn/install/miniconda3/lib/python3.6/site-packages/nipype/interfaces/freesurfer/utils.py", line 72, in copy2subjdir
shutil.copy(in_file, out_file)
  File "/home/lmn/install/miniconda3/lib/python3.6/shutil.py", line 242, in copy
copymode(src, dst, follow_symlinks=follow_symlinks)
  File "/home/lmn/install/miniconda3/lib/python3.6/shutil.py", line 144, in copymode
chmod_func(dst, stat.S_IMODE(st.st_mode))
PermissionError: [Errno 1] Operation not permitted: '/path/to/my/folder/workingdir_inverse_transform_ROIs/inverse_ROI_flow/_source_subject_fsaverage_subject_id_AM_1/inverse_transform_mni_surface_lh_post2ant/mapflow/_inverse_transform_mni_surface_lh_post2ant4/AM_1/surf/lh.sphere.reg'

Path and folder permissions seem to be okay and I never encountered a permission related error before.
Also I don’t get why setting an out_file name during the definition of the node solves the error.

Do you and/or the rest maybe got an idea?

Thx again, best, Peer


#4

This most likely means that the out file is derived from the input file, and for some reason the logic for doing this doesn’t match your use case. This is probably a bug in nipype, so I’d encourage you to open an issue at https://github.com/nipy/nipype/issues. In the meantime, though, setting out_file is a reasonable workaround.