Resample BOLD with nitransforms (closes #112)#336
Conversation
Closes #112. Replaces the fan-out of antsApplyTransforms per volume with a single nitransforms + scipy.ndimage.map_coordinates pass: the static chain (anat->template warp, BBR affine, optional distortion) is composed into one template-grid coord map up front, then we loop over volumes applying the per-volume motion affine and sampling. Same idea applied to the longitudinal func_transform, so split_4d goes away. Distortion warps now write the canonical ANTs 5D (X, Y, Z, 1, 3) shape so DenseFieldTransform.from_filename can load them directly.
kaitj
left a comment
There was a problem hiding this comment.
This one largely looks good to me, just a few questions regarding the coordinate-to-voxel grid transformation.
We also have some use-cases (I don't know if testing or actual processing) of the current / previous versions of rbc. So long as this is backwards compatible with those ANTs transformations (and I think it is with the _load_ants_warp ) with those existing transforms, I don't think it's an issue.
| FUGUE shift maps contain per-voxel shifts (in voxels) along the PE | ||
| axis. This converts to a 3-component mm displacement field in LPS | ||
| (ANTs/ITK convention). | ||
| (ANTs/ITK convention), written in the canonical ANTs 5D shape |
There was a problem hiding this comment.
Looking at this and the below, what was the shape previously - just (X, Y, Z, 3)?
| return DenseFieldTransform.from_filename(str(path), fmt="itk") | ||
|
|
||
|
|
||
| def _ras_to_voxel_grid( |
There was a problem hiding this comment.
Maybe its handled further downstream and I haven't gotten there yet, but (maybe incorrectly) assuming the voxel grid is a float array, would this ultimately round to the nearest voxel coord?
| vol_data = np.asanyarray( | ||
| dataobj[..., t] if is_4d else dataobj, dtype=np.float32 | ||
| ) | ||
| ndi.map_coordinates( |
There was a problem hiding this comment.
Ahh okay, I think this is doing the mapping I was wondering about above 👍
antsApplyTransformsfan-out inresample_bold_to_templateandapply_motion_transformsis gone — we now compose the static chain (anat→template warp, BBR, optional distortion) into one coord map up front and loop volumes withscipy.ndimage.map_coordinates. No subprocess spawning, no split/merge bookkeeping.func_transform, sosplit_4d, thestrategy="chunked"|"single"kwarg, and_transform_4d_chunkedare deleted.(X, Y, Z, 1, 3)soDenseFieldTransform.from_filename(fmt="itk")accepts them;_load_ants_warpis a one-liner delegating to nitransforms.