fmriprep to afni_proc - what am I missing?

Hello AFNI experts,

I have been working on a set of scripts for our lab that can be used in multiple MRI analyses. We have recently moved to pre-processing all of our data with fmriprep, but we like to use AFNI to do the last few steps, including the GLM. I would like to know if I am on the right track with the afni_proc.py command below.

Based on some conversations on this forum (including here and here), I've decided to add most of my regressors with -regress_extra_ortvec, including motion regressors, drift, and censoring. This is because depending on the experiment, lab members may choose different denoising and/or censoring strategies (and there is not 100% consensus on the right way to denoise anyway).

Below is an example of the afni_proc.py command that is generated for a single subject in one of our experiments, a go-nogo task with different stimulus durations. Is there anything to be wary of here? Anything I'm missing? Any suggestions of better/newer options compared to what I have here?

Many thanks in advance,

(Version = AFNI_24.0.06 'Caracalla')


#!/usr/bin/env tcsh

cd <task mri results directory>/sub-0131/ses-01

afni_proc.py \
  -scr_overwrite \
  -subj_id 0131 \
  -script sub-0131_ses-01_task-CPT_denoiser-<denoiser>_drop_nss_demean \
  -out_dir <task mri results directory>/sub-0131/ses-01/glm \
  -blocks mask blur scale regress \
  -dsets \
    <fmriprep-directory>/derivatives/fmriprep/sub-0131/ses-01/func/sub-0131_ses-01_task-CPT_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz \
  -radial_correlate_blocks regress \
  -tcat_remove_first_trs 3 \
  -copy_anat \
    <project mri directory>/skullstrips-masks/sub-0131/ses-01/anat/sub-0131_ses-01_space-MNI152NLin2009cAsym_desc-preproc_T1w_brain.nii.gz \
  -anat_has_skull no \
  -mask_epi_anat yes \
  -blur_size 4 \
  -blur_in_mask yes \
  -regress_polort 0 \
  -regress_stim_times  \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_go_250_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_go_350_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_nogo_050_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_nogo_150_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_nogo_350_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_go_050_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_go_150_stimtimes.1D \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_nogo_250_stimtimes.1D \
  -regress_stim_labels \
    go_250 go_350 nogo_050 nogo_150 nogo_350 go_050 go_150 nogo_250 \
  -regress_stim_types \
    AM1 AM1 AM1 AM1 AM1 AM1 AM1 AM1 \
  -regress_extra_ortvec \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_denoiser-<denoiser>_drop_nss_demean.1D \
  -regress_extra_ortvec_labels \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_denoiser-<denoiser>_drop_nss_demean_labels.csv \
  -regress_basis 'dmUBLOCK(0)' \
  -regress_opts_3dD -num_glt 3 \
      -gltsym 'SYM: +0.25*go_350 +0.25*go_250 +0.25*go_150 +0.25*go_050' -glt_label 1 GO-ALL \
      -gltsym 'SYM: +0.25*nogo_350 +0.25*nogo_250 +0.25*nogo_150 +0.25*nogo_050' -glt_label 2 NOGO-ALL \
      -gltsym 'SYM: +0.25*go_350 +0.25*go_250 +0.25*go_150 +0.25*go_050 -0.25*nogo_350 -0.25*nogo_250 -0.25*nogo_150 -0.25*nogo_050' -glt_label 3 GO-NOGO \
  -regress_reml_exec \
  -regress_compute_fitts \
  -regress_make_ideal_sum sum_ideal.1D \
  -regress_est_blur_epits \
  -regress_est_blur_errts \
  -regress_run_clustsim no \
  -html_review_style pythonic

Hello,

This seems reasonable, though it might be a little nicer to tell afni_proc.py what the motion file is (extract those 6 columns), so that such information can be included in the QC. That could be done without AP doing any actual motion censoring, but using an external censor file.

I don't see any censoring option (-regress_censor_extern).

For -regress_extra_ortvec and -regress_extra_ortvec_labels, it will not look for labels in a csv file and extract those columns from the tsv. Rather, provide -regress_extra_ortvec with a list of files (partitioned as you wish, e.g. drift, ventricle), and then provide -regress_extra_ortvec_labels with corresponding labels. This is more like the other -regress_* options (like -regress_stim_times), for example,

-regress_extra_ortvec regs_drift.1D regs_ventricle.1D \
-regress_extra_ortvec_labels drift ventricle \

Also, adding -compare_opts 'publish 3b' to your command will show the differences between it and the given example name. That is often a useful way to compare.

-rick

Hi Rick,

Thanks for the feedback!

though it might be a little nicer to tell afni_proc.py what the motion file is (extract those 6 columns)

If I do this, can I still include motion derivatives/powers/etc in the regress_extra_ortvec file without causing problems?

I don't see any censoring option (-regress_censor_extern)

Currently these are also accounted for in the regress_extra_ortvec file; each censored timepoint is its own regressor with a stick function at the censored TR (see sample X-matrix below). Is there a problem with specifying them like that?

This is more like the other -regress_* options (like -regress_stim_times)

Ah, I understand. I was wondering why the resulting afni files were labeled so generically.
I already filtered the tsv so that the file I feed to afni_proc includes only the regressors I want; so I guess I could make it simple by doing something like:

-regress_extra_ortvec \
    <task mri results directory>/sub-0131/ses-01/regressors/sub-0131_ses-01_task-CPT_denoiser-<denoiser>_drop_nss_demean.1D \
-regress_extra_ortvec_labels nointerest \
  

... or I could split them out into groups and label them. Correct?

Hi,

Yes, when providing the 6 motion params separately, it is still okay to pass other ortvecs. The only point would be to avoid overlap, but for example, you are not using -regress_apply_mot_types, so no problem there.

The -regress_censor_extern option is almost 15 years old, so you should have it. What is shown for "afni -ver"? And to be sure, where are you looking for that option, in case there is someplace that I forgot to add it?

Censoring via extra ortvecs should be fine. It won't affect the betas, but it is possible that it would affect at least the full F-stat (since it may alter what the baseline model is). Still, it wouldn't be bad for afni_proc.py to know what censoring was done for QC, like with the motion params.

Yes, using multiple regressor groups/labels for regress_extra_orvec is the intention of the option. However, unless using -bout with 3dDeconvolve, there will probably be no way to even detect any difference, as that is part of the baseline model. The -bout option would output those betas, so at least the labels for them would differ. So whatever you prefer is good.

-rick

Hi Rick,

Thanks for the clarifications. I used afni -ver to get the version number and the result was
AFNI_24.0.06 'Caracalla'

Would you clarify why you believe -regress_censor_extern does not work in afni_proc.py?

Thanks,
-rick

Oh, sorry, I think there was a slight misunderstanding-- when I pasted "I don't see any censoring option (-regress_censor_extern)" that was a quote from you, and I was replying to it. You were wondering why I did not use it in my afni_proc call, if I'm correct.

There is no error/problem with that option, I just chose to have everything in one chunk (fed to -regress_extra_ortvec) since our lab users may choose different censoring strategies that depend on various columns from the fmriprep regressor output. Once we know the pipeline works, I can go back and split things out into censors/motion/etc to get better QC and labeling, but that is lower priority at the moment.

Oh my, sorry about that. That's the second time recently that formatting in these threads has blown past me, plus I did not recognize my own text.
Is there any point left to resolve then, or are we good?

Sorry about my confusion, it will happen again. :)

-rick

All good here, thank you for taking the time!