Workaround / feature request for applying per-TR nonlinear warp series with 3dNwarpApply

Hi dear AFNI people,

I'm currently setting up a preprocessing pipeline for a multi-echo fMRI dataset. I'm using a newly published tool (MEDIC/warpkit: GitHub - vanandrew/warpkit: Python library for neuroimaging transforms. · GitHub) that does frame-wise estimation of a distortion field (so a 4D nifti file, same dimensions as original input) instead of one blip-based fieldmap per run. Looks like this does work better especially in cases of high motion, probably for many folks. It does come with a native applier function, but unfortunately it's quite slow (like, very slow). It also does not allow for combining it with e.g. affine matrices from rigid body realignment. 3dNwarpApply does this nicely, but, as far as I understand it, only for one warp field per run (or maybe one warp field from distortion correction plus one from MNI-warping, but not frame-wise warp fields).
I have quite large fmri data (from sleep recordings, sometimes >10.000 frames), so I would love to use super fast 3dNwarpApply. The warpkit toolbox does seem to export its 4D warp fields in AFNI-style format, ready for 3dNwarpApply (at least according to the docs). So I wondered, is there a way 3dNwarpApply can handle such a problem already or a workaround for this? If not, would this maybe be a reasonable enhancement request?

Any help and hints are much appreciated! Thanks in advance,

gregor

3dNwarpApply can apply a string that describes a list of affine and nonlinear warp transformations. In the example below, the affine varies per TR, so the affine matrix has the set of 12 parameters on each line for a single time point's transformation. This example is from a class demo example in the proc script produced by afni_proc.py for nonlinear alignment. The nonlinear warp, a 3-volume dataset with delta-x,y and z, is listed first followed by the affine matrix. This example includes motion correction, alignment from EPI to anatomical and alignment of the anatomical to a template with an affine matrix that varies by TR. Additional nonlinear warps and affine matrices can be concatenated depending on the situation.

    # catenate volreg/epi2anat/tlrc xforms
    cat_matvec -ONELINE                                                  \
               anatQQ.FT.aff12.1D                                        \
               anatSS.FT_al_junk_mat.aff12.1D -I                         \
               mat.r$run.vr.aff12.1D > mat.r$run.warp.aff12.1D

    # apply catenated xform: volreg/epi2anat/tlrc/NLtlrc
    # then apply non-linear standard-space warp
    3dNwarpApply -master anatQQ.FT+tlrc -dxyz 2.5                        \
                 -source pb01.$subj.r$run.tshift+orig                    \
                 -nwarp "anatQQ.FT_WARP.nii mat.r$run.warp.aff12.1D"     \
                 -prefix rm.epi.nomask.r$run

If you have a large number of time points and multiple transformations, you can pre-combine these transformations. For affine matrices with rigid or full 12-parameter affine matrices, use cat_matvec as above to contenate those kinds of transformations. For nonlinear warps, you can compose a concatenated warp transformation with 3dNwarpCat.

Applying a different nonlinear warp at each time point at present would require a script which looped over the time axis, as 3dNwarpApply only takes 3D (no time dependence) warps.

The program could be modified to allow a different nonlinear warp at each time point. Since I rewrote it about 12 years ago, and I've been retired for 5 of those years, it would take me a little while to re-familiarize myself with how it processes the input warp sets to produce the actual warp being applied. It would be interesting to try ... but ... I'm leaving on another hiking vacation tomorrow (23 Aug 2026) and so won't be able to even think about it until I return in mid-September.

If the multiple warps are affine+nonlinear, and it's only the affine part that changes, then 3dNwarpApply already handles it. There is an advantage for applying the transformations separately to each volume; the process can be parallelized for a big increase in speed. 3dNwarpApply can be called on a cluster for every 10000 time points over n workers in this case. If you can get 1000 worker nodes, you get almost that 1000x speed up, minus the additional I/O time of concatenating all the results back together again. Per Bob's suggestion, here's an example of how that might be scripted.

foreach TR (`count_afni  -digits 5 1 10000 1`)
       # apply ith warp to ith subbrick
       3dNwarpApply -master anatQQ.FT+tlrc -dxyz 2.5                        \
                 -source pb01.$subj.r$run.tshift+orig"[$TR]"                    \
                 -nwarp "anatQQ.FT_WARP.nii mat.r$run.warp.$TR.aff12.1D"     \
                 -prefix rm.epi.nomask.r$run.$TR
end
# concatenate the warped dataset back together again
# 3dbucket can also do this concatenation, and 
# it might work a little faster based on my one test
3dTcat -prefix  epi.nomask.r$run.nii.gz "rm.epi.nomask.r$run.*"