AFNI Preprocesamiento, QC y Análisis Seed‐based en Resting State - neuropsytox/Documentation GitHub Wiki
AFNI Preprocesamiento, QC y Análisis Seed-based en Resting State
Este ejemplo es el Cluster C13, pero puede hacerse localmente aunque toma mucho tiempo.
Primero correr cada sujeto en Freesurfer
Se corre un N4 para mejorar el contraste.
fsl_sub -N N4afni N4BiasFieldCorrection -d 3 -i sub-112_T1w.nii -o sub-112_T1w_N4.nii
Ejemplo de script en Cluster C13:
module load freesurfer/7.4.1 fsl/6.0.7.4 afni/25.2.13 ANTs/2.4.4
export data=/misc/tezca/egarza/afni_practice2025/raw20/
for suj in sub-020 sub-021 sub-022 sub-023 sub-024 sub-025 sub-025 sub-026 sub-027 sub-028 sub-029 sub-030 sub-031 sub-032 sub-033 sub-034 sub-035 sub-036 sub-038 sub-091 sub-093;
do
fsl_sub -N N4afni${suj} N4BiasFieldCorrection -d 3 -i $data/$suj/anat/${suj}_T1w.nii.gz -o $data/$suj/anat/${suj}_T1w_N4.nii.gz
done
bash 01_runN4
Cargar el modulo Freesurfer
module load freesurfer/7.4.1
export SUBJECTS_DIR=/path/to/data/derivatives/freesurfer
fsl_sub -N sub-112 recon-all -subject sub-112 -i /path/sub-112_T1w_N4.nii -all
Ejemplo de script en Cluster C13:
module load freesurfer/7.4.1 fsl/6.0.7.4 afni/25.2.13 ANTs/2.4.4
export SUBJECTS_DIR=/misc/tezca/egarza/afni_practice2025/derivatives_raw20/raw20fs
export data=/misc/tezca/egarza/afni_practice2025/raw20
for suj in sub-020 sub-021 sub-022 sub-023 sub-024 sub-025 sub-025 sub-026 sub-027 \
sub-028 sub-029 sub-030 sub-031 sub-032 sub-033 sub-034 sub-035 sub-036 sub-038 sub-091 sub-093;
do
fsl_sub -N fs${suj} recon-all -all -subject ${suj} \
-i $data/$suj/anat/${suj}_T1w_N4.nii.gz
done
bash 02_runfs
Convertir de Freesurfer a SUMA
@SUMA_Make_Spec_FS -sid sub-20 -NIFTI
Ejemplo de script en Cluster C13:
#!/bin/bash
module load freesurfer/7.4.1 fsl/6.0.7.4 afni/25.2.13 ANTs/2.4.4
export data=/misc/tezca/egarza/afni_practice2025/derivatives_raw20/raw20fs
for suj in sub-020 sub-021 sub-022 sub-023 sub-024 sub-025 \
sub-025 sub-026 sub-027 sub-028 sub-029 sub-030 sub-031 sub-032 \
sub-033 sub-034 sub-035 sub-036 sub-038 sub-091 sub-093;
do
cd ${data}/${suj}
fsl_sub -N FS2SUMA_$suj @SUMA_Make_Spec_FS -sid ${suj} -NIFTI
done
bash 03_runFS2SUMA
Revisa los resultados de Freesurfer con SUMA
Dentro del folder de SUMA donde convertiste todo de Freesurfer>
afni -niml & suma -spec std.141.sub-112_both.spec -sv sub-112_SurfVolcopy.nii
Me salía un error por usar AFNI viejo del cluster C13. Tuve que convertir el NIFTI para que no tuviera un problema de header.
3drefit -newid sub-111_SurfVol.nii
o
3dcopy sub-112_SurfVol.nii sub-112_SurfVolcopy.nii
Correr un SSWarper
Antes de correr afni_proc.py correr este SSWarper para obtener transformaciones y cerebro T1w sin craneo. Se corre con el comando siguiente, se puede hacer como script.
tcsh SSwarper
Primero creo un folder dentro de derivatives_raw20 llamado afniproc.
Dentro, creo un folder llamado AFNI_01_SSWarp
Script
#!/bin/bash
module load freesurfer/7.4.1 fsl/6.0.7.4 afni/25.2.13 ANTs/2.4.4
export data=/misc/tezca/egarza/afni_practice2025/raw20
export derivatives=/tezca/egarza/afni_practice2025/derivatives_raw20
export output=/misc/tezca/egarza/afni_practice2025/derivatives_raw20/afniproc/AFNI_01_SSWarp
for suj in sub-021 sub-022 sub-023 sub-024 sub-025 \
sub-025 sub-026 sub-027 sub-028 sub-029 sub-030 sub-031 sub-032 \
sub-033 sub-034 sub-035 sub-036 sub-038 sub-091;
do
fsl_sub -N SSWarper_${suj} @SSwarper -input ${data}/${suj}/anat/${suj}_T1w_N4.nii.gz \
-subid ${output}/${suj} \
-odir ${output}/${suj}_anat_warped \
-base MNI152_2009_template_SSW.nii.gz
done
bash 04_runSSWarper
Correr AFNI PROC
Al correr afni_proc.py se corre automaticamente el Quality Control.
Se tiene que estar seguro donde estan los archivos, ya sea ponerlos todos en el mismo folder o solo dar los paths correctos.
Primero se crea un script. Hay muchos ejemplos en la página de AFNI, este script lo modifiqué de este: https://afni.nimh.nih.gov/pub/dist/doc/htmldoc/programs/alpha/afni_proc.py_sphx.html#example-11-resting-state-analysis-now-even-more-modern
Creo un folder dentro de afniproc llamado AFNI_02_rest y copio dentro el siguiente script:
#!/bin/tcsh
#module load freesurfer/7.4.1 fsl/6.0.7.4 afni/25.2.13 ANTs/2.4.4
# --------------------------------------------------
# note fixed top-level directories
set SUMA=/misc/tezca/egarza/afni_practice2025/derivatives_raw20/raw20fs/
set warp=/misc/tezca/egarza/afni_practice2025/derivatives_raw20/afniproc/AFNI_01_SSWarp/
set data_root = /misc/tezca/egarza/afni_practice2025/
set input_root = $data_root/raw20
set output_root = $data_root/derivatives_raw20/afniproc/AFNI_02_rest
set subjects = (sub-021 sub-022 sub-023 sub-024 sub-025 sub-025 \
sub-026 sub-027 sub-028 sub-029 sub-030 sub-031 sub-032 \
sub-033 sub-034 sub-035 sub-036 sub-038 sub-091)
# process all subjects
foreach suj ($subjects)
#sub-022 sub-023 sub-024 sub-025 \
#sub-025 sub-026 sub-027 sub-028 sub-029 sub-030 sub-031 sub-032 \
#sub-033 sub-034 sub-035 sub-036 sub-038 sub-091;
# --------------------------------------------------
# note input and output directories
set subj_indir = $input_root/$suj/func
set subj_outdir = $output_root/$suj
# --------------------------------------------------
# if output dir exists, this subject has already been processed
if ( -d $subj_outdir ) then
echo "** results dir already exists, skipping subject $suj"
continue
endif
# --------------------------------------------------
# otherwise create the output directory, write an afni_proc.py
# command to it, and fire it up
mkdir -p $subj_outdir
cd $subj_outdir
# create a run.afni_proc script in this directory
cat > run.afni_proc << EOF
afni_proc.py \
-subj_id ${suj}.rest \
-blocks despike tshift align tlrc volreg blur \
mask scale regress \
-radial_correlate_blocks tcat volreg regress \
-copy_anat $warp/${suj}_anat_warped/anatSS.${suj}.nii \
-anat_has_skull no \
-anat_follower anat_w_skull anat $warp/${suj}_anat_warped/anatU.${suj}.nii \
-anat_follower_ROI aaseg anat \
$SUMA/${suj}/SUMA/aparc.a2009s+aseg_REN_all.nii.gz \
-anat_follower_ROI aeseg epi \
$SUMA/${suj}/SUMA/aparc.a2009s+aseg_REN_all.nii.gz \
-anat_follower_ROI FSvent epi $SUMA/${suj}/SUMA/fs_ap_latvent.nii.gz \
-anat_follower_ROI FSWe epi $SUMA/${suj}/SUMA/fs_ap_wm.nii.gz \
-anat_follower_erode FSvent FSWe \
-dsets $subj_indir/${suj}_task-rest_bold.nii.gz \
-align_unifize_epi local \
-align_opts_aea -cost lpc+ZZ \
-giant_move \
-check_flip \
-tlrc_base MNI152_2009_template_SSW.nii.gz \
-tlrc_NL_warp \
-tlrc_NL_warped_dsets $warp/${suj}_anat_warped/anatQQ.${suj}.nii $warp/${suj}_anat_warped/anatQQ.${suj}.aff12.1D \
$warp/${suj}_anat_warped/anatQQ.${suj}_WARP.nii \
-volreg_align_to MIN_OUTLIER \
-volreg_align_e2a \
-volreg_tlrc_warp \
-mask_epi_anat yes \
-blur_size 4 \
-regress_apply_mot_types demean deriv \
-regress_motion_per_run \
-regress_anaticor_fast \
-regress_anaticor_label FSWe \
-regress_ROI_PC FSvent 3 \
-regress_ROI_PC_per_run FSvent \
-regress_censor_motion 0.2 \
-regress_censor_outliers 0.05 \
-regress_make_corr_vols aeseg FSvent \
-regress_est_blur_epits \
-regress_est_blur_errts \
-html_review_style pythonic
EOF
# EOF terminates the 'cat > run.afni_proc' command, above
# (it must not be indented in the script)
# now run the analysis (generate proc and execute)
tcsh run.afni_proc
# end loop over subjects
end
Después se corre el script así:
tcsh afniproc_raw
Correr Preprocesamiento
Este script crea el script final para correr el preprocesamiento completo
fsl_sub -N sub20afniproc tcsh -xef proc.sub-020.rest |& tee output.proc.sub-020.rest
En forma de script para muchos sujetos:
for suj in sub-021 sub-022 sub-023 sub-024 sub-025 sub-026 sub-027 sub-028 sub-029 sub-030 sub-031 sub-032 sub-033 sub-034 sub-035 sub-036 sub-038 sub-091; do fsl_sub -N afniproc_$suj tcsh -xef $suj/proc.$suj.rest 2>&1 | tee $suj/output.proc.$suj.rest; done
Quality Control
Para entender el QC, pueden revisar esta página: https://afni.nimh.nih.gov/pub/dist/doc/htmldoc/tutorials/apqc_html/apqc_ex1.html
open_apqc.py -infiles QC_*/index.html
Extracción de GCOR
Corregir Global Signal es aún un gran tema y soy partidario de no hacerlo, pero hay que hacer algo para seguir corrigiendo por señales fisiológicas y movimiento. En AFNI recomiendan GCOR. Este se extrae ya de afni_proc.py:
grep GCOR sub-*.rest.results/out.ss_review.*.txt | awk '{print $5}' > GCOR.txt
Generar semilla del PCC
for suj in sub-021 sub-022 sub-023 sub-024 sub-025 sub-026 sub-027 \
sub-028 sub-029 sub-030 sub-031 sub-032 sub-033 sub-034 sub-035 \
sub-036 sub-038 sub-091;do 3dUndump -prefix seeds/${suj}-lh-PCC-BA31 \
-master $suj.rest.results/errts.$suj.rest.fanaticor+tlrc. -srad 5 -xyz lh-PCC-BA31.txt; done
Generar mapa de semilla por sujeto
#!/bin/bash
module load freesurfer/7.4.1 fsl/6.0.7.4 afni/25.2.13 ANTs/2.4.4
export data=/misc/tezca/egarza/afni_practice2025/raw20
export derivatives=/tezca/egarza/afni_practice2025/derivatives_raw20
export output=/misc/tezca/egarza/afni_practice2025/derivatives_raw20/afniproc/AFNI_02_rest
for suj in sub-021 sub-022 sub-023 sub-024 sub-025 sub-026 sub-027 \
sub-028 sub-029 sub-030 sub-031 sub-032 sub-033 sub-034 sub-035 \
sub-036 sub-038 sub-091;
do
fsl_sub -N CORR-PCC-$suj 3dNetCorr -inset $output/${suj}.rest.results/errts.${suj}.rest.fanaticor+tlrc \
-in_rois seeds/${suj}-lh-PCC-BA31+tlrc \
-ts_wb_corr -ts_wb_Z -ts_out -ts_indiv -prefix $output/${suj}.rest.results/${suj}.CORR-PCC
done
Generar mapa de conectividad media
Se puede sacar una lista fácilmente:
ls sub-*.rest.results/sub-*.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD
luego copiar esto a un script
touch meanCorr.sh
3dMean -prefix meanCorr sub-021.rest.results/sub-021.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-022.rest.results/sub-022.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-023.rest.results/sub-023.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-024.rest.results/sub-024.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-025.rest.results/sub-025.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-026.rest.results/sub-026.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-027.rest.results/sub-027.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-028.rest.results/sub-028.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-029.rest.results/sub-029.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-030.rest.results/sub-030.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-031.rest.results/sub-031.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-032.rest.results/sub-032.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-033.rest.results/sub-033.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-034.rest.results/sub-034.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-035.rest.results/sub-035.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-036.rest.results/sub-036.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-038.rest.results/sub-038.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD \
sub-091.rest.results/sub-091.CORR-PCC_000_INDIV/WB_CORR_ROI_001+tlrc.HEAD
bash meanCorr.sh
Al final obtenemos un mapa de la correlación Promedio de los sujetos. Esto solo para fines de visualización.
Análisis
Ya con todos los mapas de correlacion en Z para análisis, se debe escoger el tipo de análisis. Para este ejemplo, usaré 3dttest++ ya que es una comparación simple entre grupos.
3dttest++ -prefix stat.ttest \
-AminusB \
-setA CN \
sub-021 "sub-021.rest.results/sub-021.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-022 "sub-022.rest.results/sub-022.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-028 "sub-028.rest.results/sub-028.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-029 "sub-029.rest.results/sub-029.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-031 "sub-031.rest.results/sub-031.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-032 "sub-032.rest.results/sub-032.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-033 "sub-033.rest.results/sub-033.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-036 "sub-036.rest.results/sub-036.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-038 "sub-038.rest.results/sub-038.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
-setB SU \
sub-023 "sub-023.rest.results/sub-023.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-024 "sub-024.rest.results/sub-024.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-025 "sub-025.rest.results/sub-025.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-026 "sub-026.rest.results/sub-026.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-027 "sub-027.rest.results/sub-027.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-030 "sub-030.rest.results/sub-030.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-034 "sub-034.rest.results/sub-034.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-035 "sub-035.rest.results/sub-035.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-091 "sub-091.rest.results/sub-091.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
bash ttest_2
Al final ya nos da el archivo stat.ttest+tlrc que podemos abrir con AFNI para revisar los resultados.
Con Covariables
Crear lista de sujetos
grep subject sub-*.rest.results/out.ss_review.*.txt | awk '{print $4}' > subjects.txt
Crear lista de valores GCOR (En este ejemplo usaremos GCOR que es el Global Signal Media de cada sujeto, pero puede ser edad o cualquier otra variable)
grep GCOR sub-*.rest.results/out.ss_review.*.txt | awk '{print $5}' > GCOR.txt
Combinar
paste subjects.txt GCOR.txt > covariates.txt
Hay que editar para agregar los nombres de cada columna
Script de 3dttest++
3dttest++ -prefix stat.covar.ttest \
-AminusB \
-setA CN \
sub-021 "sub-021.rest.results/sub-021.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-022 "sub-022.rest.results/sub-022.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-028 "sub-028.rest.results/sub-028.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-029 "sub-029.rest.results/sub-029.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-031 "sub-031.rest.results/sub-031.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-032 "sub-032.rest.results/sub-032.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-033 "sub-033.rest.results/sub-033.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-036 "sub-036.rest.results/sub-036.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-038 "sub-038.rest.results/sub-038.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
-setB SU \
sub-023 "sub-023.rest.results/sub-023.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-024 "sub-024.rest.results/sub-024.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-025 "sub-025.rest.results/sub-025.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-026 "sub-026.rest.results/sub-026.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-027 "sub-027.rest.results/sub-027.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-030 "sub-030.rest.results/sub-030.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-034 "sub-034.rest.results/sub-034.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-035 "sub-035.rest.results/sub-035.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
sub-091 "sub-091.rest.results/sub-091.CORR-PCC_000_INDIV/WB_Z_ROI_001+tlrc.HEAD" \
-covariates covariates.txt
bash ttest_3