AFNI 3dretroicor: `*** Error transforming resp data` on fmriprep'd data

Hi,

In a follow-up to this post, I am using AFNI 3dretroicor to filter out physio noise from rest data.

Currently, I am running into the same issue as this user: When running with only the cardiac file, 3dretroicor appears to work, but when including the respiratory file, it crashes with the vague error *** Error transforming resp data.

Here is an example command:

3dretroicor -prefix sub-s001_ses-1_task-taskA_run-1_space-MNI152NLin2009cAsym_desc-preproc_bold_retroicor.nii.gz -card func/sub-s001_ses-1_task-taskA_run-1_recording-cardiac_physio.slibase.1D -resp func/sub-s001_ses-1_task-taskA_run-1_recording-respiratory_physio.slibase.1D func/sub-s001_ses-1_task-taskA_run-1_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz

(removing the -resp …respiratory_physio.slibase.1D argument works, as reported by the other user.)

I am using AFNI 21.1.00, and the data were preprocessed with FMRIPREP LTS 20.2. The 1D files were created by calling RetroTS.py on the raw physio files using 2 commands like the following:

python /Users/henrymj/Documents/afni/src/jzosky/RetroTS.py -phys_file func/sub-s001_ses-1_task-taskA_run-1_physio.tsv.gz -phys_json func/sub-s001_ses-1_task-taskA_run-1_physio.json -n 8 -v 0.68 -prefix func/sub-s001_ses-1_task-taskA_run-1_recording-respiratory_physio -respiration_out 1 -cardiac_out 0

python /Users/henrymj/Documents/afni/src/jzosky/RetroTS.py -phys_file func/sub-s001_ses-1_task-taskA_run-1_physio.tsv.gz -phys_json func/sub-s001_ses-1_task-taskA_run-1_physio.json -n 8 -v 0.68 -prefix func/sub-s001_ses-1_task-taskA_run-1_recording-cardiac_physio -respiration_out 0 -cardiac_out 1

Here are the details of the preprocessed nifti image:

<class 'nibabel.nifti1.Nifti1Image'>
data shape (89, 105, 89, 1030)
affine: 
[[   2.20000005    0.            0.          -96.5       ]
 [   0.            2.20000005    0.         -132.5       ]
 [   0.            0.            2.20000005  -78.5       ]
 [   0.            0.            0.            1.        ]]
metadata:
<class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b'r'
dim_info        : 0
dim             : [  4  89 105  89   1030   1   1   1]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : float32
bitpix          : 32
slice_start     : 0
pixdim          : [1.   2.2  2.2  2.2  0.68 1.   1.   1.  ]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 10
cal_max         : 15254.564
cal_min         : -1786.5756
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b'xform matrices modified by FixHeaderApplyTransforms (niworkflows v1.3.1).'
aux_file        : b''
qform_code      : mni
sform_code      : mni
quatern_b       : 0.0
quatern_c       : 0.0
quatern_d       : 0.0
qoffset_x       : -96.5
qoffset_y       : -132.5
qoffset_z       : -78.5
srow_x          : [  2.2   0.    0.  -96.5]
srow_y          : [   0.     2.2    0.  -132.5]
srow_z          : [  0.    0.    2.2 -78.5]
intent_name     : b''
magic           : b'n+1'

Update (but not a solution): I misread the user’s solution when I first attempting implementing it. I tried again changing slice_duration to nimg.header['dim'][3]/nimg.header['pixdim'][4] (my tr is already in seconds; it is .68s), which in this case was 130.88235, but that produced no change in behavior.

Any help would be sincerely appreciated!

3dretroicor hasn’t been updated in a very long time, and we typically use RetroTS.py instead, not in combination with 3dretroicorr. See the help for afni_proc.py for examples 5a,b,c on how to integrate the output of RetroTS.py into your analysis.

1 Like

@henrymj, this usage does not look quite correct.
Note that 3dretroicor (which I confess to having never actually used) predates RetroTS. From the help output, it looks like the original physio signals would be input to it, not those from RetroTS.py.
To use RetroTS.py, consider the good suggestion by @dglen, to apply it with afni_proc.py. If, for some reason, you do not want to use the proc script, at least review what afni_proc.py does, to regress those 13 per-slice signals early in the processing stream.

Hi @dglen @rickr,

Are there any write-ups on when to calculate and apply RetroTS regressors? I’m trying to figure out how we might include this in fMRIPrep, and it’s unclear if it expects the BOLD series to be the original or if any of STC/HMC/SDC can/should be applied. And supposing that we calculate regressors on raw data, do any of STC/HMC/SDC invalidate it?

I’ve tried looking through afni_proc.py, and it looks like you apply the “ricor” block after despike, volreg and tshift, but I’m not 100% on whether these are estimation, application or estimate-and-apply steps (I’m pretty confident tshift is estimate-and-apply, since I haven’t been able to elicit a transform file from any STC algorithm).

Thanks,
Chris

I’m not completely sure of the abbreviations (STC=Slice Timing Correction, HMC=Head Motion Correction, SDC=?), but the physiological regressors from something like RetroIcorr (RetroTS.py) need to be applied by slice, so the order does matter. We typically recommend this as an early step before any other motion or time correction. Example 13 in the afni_proc.py help shows it in the list of processing “blocks” just after despiking but before slice timing correction (tshift) or motion correction (volreg). Here is an excerpt with some relevant “ricor” lines.

          afni_proc.py                                               \
             -subj_id FT.complicated                                 \
             -blocks despike ricor tshift align volreg mask combine  \
                 surf blur scale regress                             \
             -ricor_regs_nfirst 2                                    \
             -ricor_regs FT/fake.slibase.FT.r?.1D                    \
             -ricor_regress_method per-run                           \

Indeed, any shifting across time (or space, including motion registration, since space implies time due to slice timing) would throw off the precision of the slicewise regrssors.
The alternative, which we have not bothered to do since no has asked for it, would be to simply pass one set of regressors (per run) to the final regression model, and forget about the slice timing. But Bob believes that would make the 8 phase-based regressors much less useful.