nipy / nipype

Workflows and interfaces for neuroimaging packages
https://nipype.readthedocs.org/en/latest/
Other
749 stars 530 forks source link

nipype.interfaces.cat12.preprocess.CAT12Segment FileNotFoundError: No such file or directory '/[....]/catROIs_T1w.xml' for output 'label_rois' of a CAT12Segment interface #3654

Open JohannesWiesner opened 5 months ago

JohannesWiesner commented 5 months ago

Summary

I am running the Cat12Segment on a single T1w image. I don't want to run the surface reconstruction workflow so I set surface_and_thickness_estimation=0, and surface_measures=0.

Actual behavior

This is the error I get:

NodeExecutionError: Exception raised while executing Node cat12segment.

Traceback:
    Traceback (most recent call last):
      File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 453, in aggregate_outputs
        setattr(outputs, key, val)
      File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/traits_extension.py", line 330, in validate
        value = super(File, self).validate(objekt, name, value, return_pathlike=True)
      File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/traits_extension.py", line 135, in validate
        self.error(objekt, name, str(value))
      File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/traits/base_trait_handler.py", line 74, in error
        raise TraitError(
    traits.trait_errors.TraitError: The 'label_rois' trait of a CAT12SegmentOutputSpec instance must be a pathlike object or string representing an existing file, but a value of '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml' <class 'str'> was specified.

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 400, in run
        outputs = self.aggregate_outputs(runtime)
      File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 460, in aggregate_outputs
        raise FileNotFoundError(msg)
    FileNotFoundError: No such file or directory '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml' for output 'label_rois' of a CAT12Segment interface

Expected behavior

Only run the VBM-process of CAT12 (aka. only output mwp1, mwp2, etc.) but don't run the surface reconstruction.

I currently have two hypotheses (please take these with a big grain of salt, I am new to CAT12) what is causing the error:

1.) If I inspect the nodes output I can see that there is /pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml but not /pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROI_T1w.xml (s at the end!)

So the filename might have changed in later versions of CAT12? Probably @ChristianGaser can answer this?

2.) If I inspect the generated pyscript.m file I can see that even though jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; is appropriatly set to 0 these other lines are still added:

jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1;

which to me looks like as if we want to extract ROI labels for these atlases even though we didn't want them in the first place? In the GUI version the equivalent would be "if user doesn't want ROI estimates, then don't open dropdown menu where user can choose atlases" (I can confirm that this happens here). So either these lines should not even exist in the first place or the values should be set to 0 if surf_measures = 0?

Here's the full pyscript_cat12segment.m file:

fprintf(1,'Executing %s at %s:\n',mfilename(),datestr(now));
fprintf(1,'Executing %s at %s:\n',mfilename(),datestr(now));
ver,
try,
        %% Generated by nipype.interfaces.spm
        if isempty(which('spm')),
             throw(MException('SPMCheck:NotFound', 'SPM not in matlab path'));
        end
        [name, version] = spm('ver');
        fprintf('SPM version: %s Release: %s\n',name, version);
        fprintf('SPM path: %s\n', which('spm'));
        spm('Defaults','fMRI');

        if strcmp(name, 'SPM8') || strcmp(name(1:5), 'SPM12'),
           spm_jobman('initcfg');
           spm_get_defaults('cmdline', 1);
        end

        jobs{1}.spm.tools.cat.estwrite.data = {...
'/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/T1w.nii,1';...
};
jobs{1}.spm.tools.cat.estwrite.nproc = 1;
jobs{1}.spm.tools.cat.estwrite.opts.affreg = 'mni';
jobs{1}.spm.tools.cat.estwrite.opts.biasacc = 0.5;
jobs{1}.spm.tools.cat.estwrite.extopts.APP = 1070;
jobs{1}.spm.tools.cat.estwrite.extopts.spm_kamap = 0;
jobs{1}.spm.tools.cat.estwrite.extopts.LASstr = 0.5;
jobs{1}.spm.tools.cat.estwrite.extopts.gcutstr = 2;
jobs{1}.spm.tools.cat.estwrite.extopts.WMHC = 1;
jobs{1}.spm.tools.cat.estwrite.extopts.vox = 1.5;
jobs{1}.spm.tools.cat.estwrite.extopts.restypes.optimal(1) = 1;
jobs{1}.spm.tools.cat.estwrite.extopts.restypes.optimal(2) = 0.1;
jobs{1}.spm.tools.cat.estwrite.extopts.ignoreErrors = 1;
jobs{1}.spm.tools.cat.estwrite.surface = 0;
jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1;
jobs{1}.spm.tools.cat.estwrite.output.GM.native = 0;
jobs{1}.spm.tools.cat.estwrite.output.GM.mod = 1;
jobs{1}.spm.tools.cat.estwrite.output.GM.dartel = 0;
jobs{1}.spm.tools.cat.estwrite.output.WM.native = 0;
jobs{1}.spm.tools.cat.estwrite.output.WM.mod = 1;
jobs{1}.spm.tools.cat.estwrite.output.WM.dartel = 0;
jobs{1}.spm.tools.cat.estwrite.output.CSF.native = 0;
jobs{1}.spm.tools.cat.estwrite.output.CSF.mod = 1;
jobs{1}.spm.tools.cat.estwrite.output.CSF.dartel = 0;
jobs{1}.spm.tools.cat.estwrite.output.label.native = 0;
jobs{1}.spm.tools.cat.estwrite.output.label.warped = 1;
jobs{1}.spm.tools.cat.estwrite.output.label.dartel = 0;
jobs{1}.spm.tools.cat.estwrite.output.labelnative = 0;
jobs{1}.spm.tools.cat.estwrite.output.bias.warped = 1;
jobs{1}.spm.tools.cat.estwrite.output.las.native = 0;
jobs{1}.spm.tools.cat.estwrite.output.las.warped = 1;
jobs{1}.spm.tools.cat.estwrite.output.las.dartel = 0;
jobs{1}.spm.tools.cat.estwrite.output.jacobianwarped = 1;
jobs{1}.spm.tools.cat.estwrite.output.warps(1) = 1;
jobs{1}.spm.tools.cat.estwrite.output.warps(2) = 0;

        spm_jobman('run', jobs);

,catch ME,
fprintf(2,'MATLAB code threw an exception:\n');
fprintf(2,'%s\n',ME.message);
if length(ME.stack) ~= 0, fprintf(2,'File:%s\nName:%s\nLine:%d\n',ME.stack.file,ME.stack.name,ME.stack.line);, end;
end;

How to replicate the behavior

Here's a python script to reproduce the error

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Debug CAT12Segment
"""

import subprocess
from nipype.interfaces.cat12.preprocess import CAT12Segment
from nipype import Node
import os 

###############################################################################
# Preparation: Download an anatomical files from a public available AWS-bucket 
###############################################################################

# download T1w image
command = """
curl \
--create-dirs https://s3.amazonaws.com/openneuro.org/ds004302/sub-02/anat/sub-02_T1w.nii.gz?versionId=93eQ.AwPcMeccJAT3sO9otYv4A_WH3Bj  \
-o ./T1w.nii.gz
"""
subprocess.run(command,shell=True)

# gunzip image to get from .nii.gz to .nii
subprocess.run("gunzip -c ./T1w.nii.gz > T1w.nii",shell=True)

# get the full path to the directory where this script is being executed
script_dir = os.path.dirname(os.path.realpath(__file__))

# get full path to both images (nipype always needs absolut paths to inputs)
image_nii = os.path.join(script_dir,'T1w.nii')
image_nii_gz = os.path.join(script_dir,'T1w.nii.gz')

###############################################################################
# Run CAT12Segment inside a node on one image
###############################################################################

# create node
cat12segment = Node(CAT12Segment(surface_and_thickness_estimation=0,
                                 surface_measures=0),
                    name='cat12segment')

# input image (uncompressed, see nipype issue #3653). But should actually
# also work for .nii.gz files
cat12segment.inputs.in_files = image_nii

# set working directory of node to script directory
cat12segment.base_dir = script_dir

# run the node
cat12segment.run()

Platform details:

{'commit_hash': '%h',
 'commit_source': 'archive substitution',
 'networkx_version': '3.2.1',
 'nibabel_version': '5.2.0',
 'nipype_version': '1.8.6',
 'numpy_version': '1.26.3',
 'pkg_path': '/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype',
 'scipy_version': '1.12.0',
 'sys_executable': '/csp-tools/anaconda3/envs/csp_wiesner_johannes/bin/python',
 'sys_platform': 'linux',
 'sys_version': '3.9.18 | packaged by conda-forge | (main, Dec 23 2023, '
                '16:33:10) \n'
                '[GCC 12.3.0]',
 'traits_version': '6.3.2'}

Execution environment

Choose one

ChristianGaser commented 5 months ago

Dear Johannes,

Answers see below ...

Von: Johannes Wiesner @. @.>> Datum: 6. Juni 2024 um 14:44:53 MESZ An: nipy/nipype @. @.>> Kopie: Christian Gaser @. @.>>, Mention @. @.>> Betreff: [nipy/nipype] nipype.interfaces.cat12.preprocess.CAT12Segment FileNotFoundError: No such file or directory '/[....]/catROIs_T1w.xml' for output 'label_rois' of a CAT12Segment interface (Issue #3654) Antwort an: nipy/nipype @. @.>>

 Summary

I am running the Cat12Segment on a single T1w image. I don't want to run the surface reconstruction workflow so I set surface_and_thickness_estimation=0, and surface_measures=0.

Actual behavior

This is the error I get:

NodeExecutionError: Exception raised while executing Node cat12segment.

Traceback: Traceback (most recent call last): File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 453, in aggregate_outputs setattr(outputs, key, val) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/traits_extension.py", line 330, in validate value = super(File, self).validate(objekt, name, value, return_pathlike=True) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/traits_extension.py", line 135, in validate self.error(objekt, name, str(value)) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/traits/base_trait_handler.py", line 74, in error raise TraitError( traits.trait_errors.TraitError: The 'label_rois' trait of a CAT12SegmentOutputSpec instance must be a pathlike object or string representing an existing file, but a value of '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml' <class 'str'> was specified.

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 400, in run outputs = self.aggregate_outputs(runtime) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 460, in aggregate_outputs raise FileNotFoundError(msg) FileNotFoundError: No such file or directory '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml' for output 'label_rois' of a CAT12Segment interface Expected behavior

Only run the VBM-process of CAT12 (aka. only output mwp1, mwp2, etc.) but don't run the surface reconstruction.

I currently have two hypotheses (please take these with a big grain of salt, I am new to CAT12) what is causing the error:

1.) If I inspect the nodes output I can see that there is /pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml but not /pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROI_T1w.xml (s at the end!)

So the filename might have changed in later versions of CAT12? Probably @ChristianGaser https://github.com/ChristianGaser can answer this?

The filename hasn’t changed, the prefix catROI is for volume atlases, whereas catROIs is used for surface atlases. So it is correct that there is no surface ROI file but I don’t know based on the message above where such a file is requested.

The provided dataset was running (under Matlab) without surfaces and with/without ROIs but the skull-stripped input is problematic as the given skull-stripping is not really good and the further corrections are difficult.

2.) If I inspect the generated pyscript.m file I can see that even though jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; is appropriatly set to 0 these other lines are still added:

jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1; These are all volume atlases as described in the GUI. If you choose „no“ in the GUI the structure is replaced:

jobs{1}.spm.tools.cat.estwrite.output.ROImenu.noROI = struct([]);

which to me looks like as if we want to extract ROI labels for these atlases even though we didn't want them in the first place? In the GUI version the equivalent would be "if user doesn't want ROI estimates, then don't open dropdown menu where user can choose atlases" (I can confirm that this happens here). So either these lines should not even exist in the first place or the values should be set to 0 if surf_measures = 0?

To keep this short ;-) Surfaces ~= Volumes Backend ~= Frontend

If you don’t need surface/thickness then simply switch it off (as you did). If you don’t need volume-based ROIs then switch them off too.

Here's the full pyscript_cat12segment.m file:

fprintf(1,'Executing %s at %s:\n',mfilename(),datestr(now)); fprintf(1,'Executing %s at %s:\n',mfilename(),datestr(now)); ver, try, %% Generated by nipype.interfaces.spm if isempty(which('spm')), throw(MException('SPMCheck:NotFound', 'SPM not in matlab path')); end [name, version] = spm('ver'); fprintf('SPM version: %s Release: %s\n',name, version); fprintf('SPM path: %s\n', which('spm')); spm('Defaults','fMRI');

    if strcmp(name, 'SPM8') || strcmp(name(1:5), 'SPM12'),
       spm_jobman('initcfg');
       spm_get_defaults('cmdline', 1);
    end

    jobs{1}.spm.tools.cat.estwrite.data = {...

'/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/T1w.nii,1';... }; jobs{1}.spm.tools.cat.estwrite.nproc = 1; jobs{1}.spm.tools.cat.estwrite.opts.affreg = 'mni'; jobs{1}.spm.tools.cat.estwrite.opts.biasacc = 0.5; jobs{1}.spm.tools.cat.estwrite.extopts.APP = 1070; jobs{1}.spm.tools.cat.estwrite.extopts.spm_kamap = 0; jobs{1}.spm.tools.cat.estwrite.extopts.LASstr = 0.5; jobs{1}.spm.tools.cat.estwrite.extopts.gcutstr = 2; jobs{1}.spm.tools.cat.estwrite.extopts.WMHC = 1; jobs{1}.spm.tools.cat.estwrite.extopts.vox = 1.5; jobs{1}.spm.tools.cat.estwrite.extopts.restypes.optimal(1) = 1; jobs{1}.spm.tools.cat.estwrite.extopts.restypes.optimal(2) = 0.1; jobs{1}.spm.tools.cat.estwrite.extopts.ignoreErrors = 1; jobs{1}.spm.tools.cat.estwrite.surface = 0; jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1; jobs{1}.spm.tools.cat.estwrite.output.GM.native = 0; jobs{1}.spm.tools.cat.estwrite.output.GM.mod = 1; jobs{1}.spm.tools.cat.estwrite.output.GM.dartel = 0; jobs{1}.spm.tools.cat.estwrite.output.WM.native = 0; jobs{1}.spm.tools.cat.estwrite.output.WM.mod = 1; jobs{1}.spm.tools.cat.estwrite.output.WM.dartel = 0; jobs{1}.spm.tools.cat.estwrite.output.CSF.native = 0; jobs{1}.spm.tools.cat.estwrite.output.CSF.mod = 1; jobs{1}.spm.tools.cat.estwrite.output.CSF.dartel = 0; jobs{1}.spm.tools.cat.estwrite.output.label.native = 0; jobs{1}.spm.tools.cat.estwrite.output.label.warped = 1; jobs{1}.spm.tools.cat.estwrite.output.label.dartel = 0; jobs{1}.spm.tools.cat.estwrite.output.labelnative = 0; jobs{1}.spm.tools.cat.estwrite.output.bias.warped = 1; jobs{1}.spm.tools.cat.estwrite.output.las.native = 0; jobs{1}.spm.tools.cat.estwrite.output.las.warped = 1; jobs{1}.spm.tools.cat.estwrite.output.las.dartel = 0; jobs{1}.spm.tools.cat.estwrite.output.jacobianwarped = 1; jobs{1}.spm.tools.cat.estwrite.output.warps(1) = 1; jobs{1}.spm.tools.cat.estwrite.output.warps(2) = 0;

    spm_jobman('run', jobs);

,catch ME, fprintf(2,'MATLAB code threw an exception:\n'); fprintf(2,'%s\n',ME.message); if length(ME.stack) ~= 0, fprintf(2,'File:%s\nName:%s\nLine:%d\n',ME.stack.file,ME.stack.name,ME.stack.line);, end; end; How to replicate the behavior

Here's a python script to reproduce the error

!/usr/bin/env python3

-- coding: utf-8 --

""" Debug CAT12Segment """

import subprocess from nipype.interfaces.cat12.preprocess import CAT12Segment from nipype import Node import os

###############################################################################

Preparation: Download an anatomical files from a public available AWS-bucket

###############################################################################

download T1w image

command = """ curl \ --create-dirs https://s3.amazonaws.com/openneuro.org/ds004302/sub-02/anat/sub-02_T1w.nii.gz?versionId=93eQ.AwPcMeccJAT3sO9otYv4A_WH3Bj \ -o ./T1w.nii.gz """ subprocess.run(command,shell=True)

gunzip image to get from .nii.gz to .nii

subprocess.run("gunzip -c ./T1w.nii.gz > T1w.nii",shell=True)

get the full path to the directory where this script is being executed

script_dir = os.path.dirname(os.path.realpath(file))

get full path to both images (nipype always needs absolut paths to inputs)

image_nii = os.path.join(script_dir,'T1w.nii') image_nii_gz = os.path.join(script_dir,'T1w.nii.gz')

###############################################################################

Run CAT12Segment inside a node on one image

###############################################################################

create node

cat12segment = Node(CAT12Segment(surface_and_thickness_estimation=0, surface_measures=0), name='cat12segment')

input image (uncompressed, see nipype issue #3653). But should actually

also work for .nii.gz files

cat12segment.inputs.in_files = image_nii

set working directory of node to script directory

cat12segment.base_dir = script_dir

run the node

cat12segment.run() Platform details:

{'commit_hash': '%h', 'commit_source': 'archive substitution', 'networkx_version': '3.2.1', 'nibabel_version': '5.2.0', 'nipype_version': '1.8.6', 'numpy_version': '1.26.3', 'pkg_path': '/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype', 'scipy_version': '1.12.0', 'sys_executable': '/csp-tools/anaconda3/envs/csp_wiesner_johannes/bin/python', 'sys_platform': 'linux', 'sys_version': '3.9.18 | packaged by conda-forge | (main, Dec 23 2023, ' '16:33:10) \n' '[GCC 12.3.0]', 'traits_version': '6.3.2'}

Execution environment

Choose one

My python environment outside container. My CAT12 version is CAT12.9 (r2560) inside an spm12/9.14 environment module that is loaded using module load spm12/9.14 in my .bashrc file. — Reply to this email directly, view it on GitHub https://github.com/nipy/nipype/issues/3654, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFKSWFRDYLZGBNE5DJTQIADZGBKT7AVCNFSM6AAAAABI4VBGFKVHI2DSMVQWIX3LMV43ASLTON2WKOZSGMZTQMJWHE4TOOI. You are receiving this because you were mentioned.

ChristianGaser commented 5 months ago

To improve the skull-stripping you have to switch to the CAT12 expert mode and reopen the segmentation batch. Go to the skull-striping option and select the last entry „APRG approach (force skull-stripping, 12)". 

CAT will then try to run the default skull-stripping that was improving the results strongly for your example scan. 

Best regards, Robert

Am 07.06.2024 um 12:31 schrieb Robert Dahnke @.***>:

Dear Johannes,

Answers see below ...

Von: Johannes Wiesner @. @.>> Datum: 6. Juni 2024 um 14:44:53 MESZ An: nipy/nipype @. @.>> Kopie: Christian Gaser @. @.>>, Mention @. @.>> Betreff: [nipy/nipype] nipype.interfaces.cat12.preprocess.CAT12Segment FileNotFoundError: No such file or directory '/[....]/catROIs_T1w.xml' for output 'label_rois' of a CAT12Segment interface (Issue #3654) Antwort an: nipy/nipype @. @.>>



Summary

I am running the Cat12Segment on a single T1w image. I don't want to run the surface reconstruction workflow so I set surface_and_thickness_estimation=0, and surface_measures=0.

Actual behavior

This is the error I get:

NodeExecutionError: Exception raised while executing Node cat12segment.

Traceback: Traceback (most recent call last): File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 453, in aggregate_outputs setattr(outputs, key, val) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/traits_extension.py", line 330, in validate value = super(File, self).validate(objekt, name, value, return_pathlike=True) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/traits_extension.py", line 135, in validate self.error(objekt, name, str(value)) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/traits/base_trait_handler.py", line 74, in error raise TraitError( traits.trait_errors.TraitError: The 'label_rois' trait of a CAT12SegmentOutputSpec instance must be a pathlike object or string representing an existing file, but a value of '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml' <class 'str'> was specified.

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 400, in run outputs = self.aggregate_outputs(runtime) File "/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype/interfaces/base/core.py", line 460, in aggregate_outputs raise FileNotFoundError(msg) FileNotFoundError: No such file or directory '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml' for output 'label_rois' of a CAT12Segment interface Expected behavior

Only run the VBM-process of CAT12 (aka. only output mwp1, mwp2, etc.) but don't run the surface reconstruction.

I currently have two hypotheses (please take these with a big grain of salt, I am new to CAT12) what is causing the error:

1.) If I inspect the nodes output I can see that there is /pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROIs_T1w.xml but not /pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/label/catROI_T1w.xml (s at the end!)

So the filename might have changed in later versions of CAT12? Probably @ChristianGaser https://github.com/ChristianGaser can answer this?

The filename hasn’t changed, the prefix catROI is for volume atlases, whereas catROIs is used for surface atlases. So it is correct that there is no surface ROI file but I don’t know based on the message above where such a file is requested.

The provided dataset was running (under Matlab) without surfaces and with/without ROIs but the skull-stripped input is problematic as the given skull-stripping is not really good and the further corrections are difficult.

> 2.) If I inspect the generated pyscript.m file I can see that even though jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; is appropriatly set to 0 these other lines are still added: > > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1; These are all volume atlases as described in the GUI. If you choose „no“ in the GUI the structure is replaced: jobs{1}.spm.tools.cat.estwrite.output.ROImenu.noROI = struct([]); > which to me looks like as if we want to extract ROI labels for these atlases even though we didn't want them in the first place? In the GUI version the equivalent would be "if user doesn't want ROI estimates, then don't open dropdown menu where user can choose atlases" (I can confirm that this happens here). So either these lines should not even exist in the first place or the values should be set to 0 if surf_measures = 0? > To keep this short ;-) Surfaces ~= Volumes Backend ~= Frontend If you don’t need surface/thickness then simply switch it off (as you did). If you don’t need volume-based ROIs then switch them off too. > Here's the full pyscript_cat12segment.m file: > > fprintf(1,'Executing %s at %s:\n',mfilename(),datestr(now)); > fprintf(1,'Executing %s at %s:\n',mfilename(),datestr(now)); > ver, > try, > %% Generated by nipype.interfaces.spm > if isempty(which('spm')), > throw(MException('SPMCheck:NotFound', 'SPM not in matlab path')); > end > [name, version] = spm('ver'); > fprintf('SPM version: %s Release: %s\n',name, version); > fprintf('SPM path: %s\n', which('spm')); > spm('Defaults','fMRI'); > > if strcmp(name, 'SPM8') || strcmp(name(1:5), 'SPM12'), > spm_jobman('initcfg'); > spm_get_defaults('cmdline', 1); > end > > jobs{1}.spm.tools.cat.estwrite.data = {... > '/pandora/home/johannes.wiesner/work/testing/debug_cat12/cat12segment/T1w.nii,1';... > }; > jobs{1}.spm.tools.cat.estwrite.nproc = 1; > jobs{1}.spm.tools.cat.estwrite.opts.affreg = 'mni'; > jobs{1}.spm.tools.cat.estwrite.opts.biasacc = 0.5; > jobs{1}.spm.tools.cat.estwrite.extopts.APP = 1070; > jobs{1}.spm.tools.cat.estwrite.extopts.spm_kamap = 0; > jobs{1}.spm.tools.cat.estwrite.extopts.LASstr = 0.5; > jobs{1}.spm.tools.cat.estwrite.extopts.gcutstr = 2; > jobs{1}.spm.tools.cat.estwrite.extopts.WMHC = 1; > jobs{1}.spm.tools.cat.estwrite.extopts.vox = 1.5; > jobs{1}.spm.tools.cat.estwrite.extopts.restypes.optimal(1) = 1; > jobs{1}.spm.tools.cat.estwrite.extopts.restypes.optimal(2) = 0.1; > jobs{1}.spm.tools.cat.estwrite.extopts.ignoreErrors = 1; > jobs{1}.spm.tools.cat.estwrite.surface = 0; > jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1; > jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1; > jobs{1}.spm.tools.cat.estwrite.output.GM.native = 0; > jobs{1}.spm.tools.cat.estwrite.output.GM.mod = 1; > jobs{1}.spm.tools.cat.estwrite.output.GM.dartel = 0; > jobs{1}.spm.tools.cat.estwrite.output.WM.native = 0; > jobs{1}.spm.tools.cat.estwrite.output.WM.mod = 1; > jobs{1}.spm.tools.cat.estwrite.output.WM.dartel = 0; > jobs{1}.spm.tools.cat.estwrite.output.CSF.native = 0; > jobs{1}.spm.tools.cat.estwrite.output.CSF.mod = 1; > jobs{1}.spm.tools.cat.estwrite.output.CSF.dartel = 0; > jobs{1}.spm.tools.cat.estwrite.output.label.native = 0; > jobs{1}.spm.tools.cat.estwrite.output.label.warped = 1; > jobs{1}.spm.tools.cat.estwrite.output.label.dartel = 0; > jobs{1}.spm.tools.cat.estwrite.output.labelnative = 0; > jobs{1}.spm.tools.cat.estwrite.output.bias.warped = 1; > jobs{1}.spm.tools.cat.estwrite.output.las.native = 0; > jobs{1}.spm.tools.cat.estwrite.output.las.warped = 1; > jobs{1}.spm.tools.cat.estwrite.output.las.dartel = 0; > jobs{1}.spm.tools.cat.estwrite.output.jacobianwarped = 1; > jobs{1}.spm.tools.cat.estwrite.output.warps(1) = 1; > jobs{1}.spm.tools.cat.estwrite.output.warps(2) = 0; > > spm_jobman('run', jobs); > > > ,catch ME, > fprintf(2,'MATLAB code threw an exception:\n'); > fprintf(2,'%s\n',ME.message); > if length(ME.stack) ~= 0, fprintf(2,'File:%s\nName:%s\nLine:%d\n',ME.stack.file,ME.stack.name,ME.stack.line);, end; > end; > How to replicate the behavior > > Here's a python script to reproduce the error > > #!/usr/bin/env python3 > # -*- coding: utf-8 -*- > """ > Debug CAT12Segment > """ > > import subprocess > from nipype.interfaces.cat12.preprocess import CAT12Segment > from nipype import Node > import os > > ############################################################################### > # Preparation: Download an anatomical files from a public available AWS-bucket > ############################################################################### > > # download T1w image > command = """ > curl \ > --create-dirs https://s3.amazonaws.com/openneuro.org/ds004302/sub-02/anat/sub-02_T1w.nii.gz?versionId=93eQ.AwPcMeccJAT3sO9otYv4A_WH3Bj \ > -o ./T1w.nii.gz > """ > subprocess.run(command,shell=True) > > # gunzip image to get from .nii.gz to .nii > subprocess.run("gunzip -c ./T1w.nii.gz > T1w.nii",shell=True) > > # get the full path to the directory where this script is being executed > script_dir = os.path.dirname(os.path.realpath(__file__)) > > # get full path to both images (nipype always needs absolut paths to inputs) > image_nii = os.path.join(script_dir,'T1w.nii') > image_nii_gz = os.path.join(script_dir,'T1w.nii.gz') > > ############################################################################### > # Run CAT12Segment inside a node on one image > ############################################################################### > > # create node > cat12segment = Node(CAT12Segment(surface_and_thickness_estimation=0, > surface_measures=0), > name='cat12segment') > > # input image (uncompressed, see nipype issue #3653). But should actually > # also work for .nii.gz files > cat12segment.inputs.in_files = image_nii > > # set working directory of node to script directory > cat12segment.base_dir = script_dir > > # run the node > cat12segment.run() > Platform details: > > {'commit_hash': '%h', > 'commit_source': 'archive substitution', > 'networkx_version': '3.2.1', > 'nibabel_version': '5.2.0', > 'nipype_version': '1.8.6', > 'numpy_version': '1.26.3', > 'pkg_path': '/csp-tools/anaconda3/envs/csp_wiesner_johannes/lib/python3.9/site-packages/nipype', > 'scipy_version': '1.12.0', > 'sys_executable': '/csp-tools/anaconda3/envs/csp_wiesner_johannes/bin/python', > 'sys_platform': 'linux', > 'sys_version': '3.9.18 | packaged by conda-forge | (main, Dec 23 2023, ' > '16:33:10) \n' > '[GCC 12.3.0]', > 'traits_version': '6.3.2'} > > > Execution environment > > Choose one > > My python environment outside container. My CAT12 version is CAT12.9 (r2560) inside an spm12/9.14 environment module that is loaded using module load spm12/9.14 in my .bashrc file. > — > Reply to this email directly, view it on GitHub , or unsubscribe . > You are receiving this because you were mentioned. >
JohannesWiesner commented 5 months ago

Hi @ChristianGaser, I guess that goes in the direction of my hypotheses. Can you confirm the following, then we might be able to send a PR:

The following lines of code

jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1;

should be replaced with

jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0;
jobs{1}.spm.tools.cat.estwrite.output.ROImenu.noROI = struct([]);

?

ChristianGaser commented 5 months ago

Will Answer after my holydaysAm 07.06.2024 um 16:30 schrieb Johannes Wiesner @.***>: Hi @ChristianGaser, I guess that goes in the direction of my hypotheses. Can you confirm the following, then we might be able to send a PR: The following lines of code jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 1; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 1;

should be replaced with jobs{1}.spm.tools.cat.estwrite.output.surf_measures = 0; jobs{1}.spm.tools.cat.estwrite.output.ROImenu.noROI = struct([]);

?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

JailanOweda commented 4 months ago

@effigies we tried solving the issue by updating the traits in CAT12SegmentInputSpec to the dafaults from the latest CAT12 version. In this version we either set the atlases to 1 that we want to use for ROI processing, or to 0 if we don't want it. However, there is also the option to say in advance, that we don't want any ROI processing and in that case in the Matlab GUI all of the Atlases vanish, because they are no longer relevant. This is when a new trait comes in jobs{1}.spm.tools.cat.estwrite.output.ROImenu.noROI = struct([]); So we are currently trying to define this new trait in InputSpec, but how do we define a trait that when translated to Matlab code will be of type struct and will contain an empty list? In python the options I can think of are only List, Dict or Tuple.