Skip to content

Resample BOLD with nitransforms (closes #112)#336

Open
nx10 wants to merge 2 commits into
mainfrom
resampling-nitransforms
Open

Resample BOLD with nitransforms (closes #112)#336
nx10 wants to merge 2 commits into
mainfrom
resampling-nitransforms

Conversation

@nx10
Copy link
Copy Markdown
Contributor

@nx10 nx10 commented May 19, 2026

  • Per-volume antsApplyTransforms fan-out in resample_bold_to_template and apply_motion_transforms is gone — we now compose the static chain (anat→template warp, BBR, optional distortion) into one coord map up front and loop volumes with scipy.ndimage.map_coordinates. No subprocess spawning, no split/merge bookkeeping.
  • Same treatment for longitudinal func_transform, so split_4d, the strategy="chunked"|"single" kwarg, and _transform_4d_chunked are deleted.
  • Distortion warps now write canonical ANTs 5D (X, Y, Z, 1, 3) so DenseFieldTransform.from_filename(fmt="itk") accepts them; _load_ants_warp is a one-liner delegating to nitransforms.
  • Light benchmark on 64×64×40×300 → 91×109×91: ~670 ms/vol, RSS bounded (~400 MB working set + the output array). Threading the per-volume loop is an easy follow-up (~4–8×) but deferred.

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.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

Coverage

Tests Skipped Failures Errors Time
800 0 💤 0 ❌ 0 🔥 11.904s ⏱️

@nx10 nx10 requested review from jpillai00 and kaitj May 19, 2026 20:19
Copy link
Copy Markdown
Contributor

@kaitj kaitj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh okay, I think this is doing the mapping I was wondering about above 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants