Skip to content

quasiraw: fix N4BiasFieldCorrection "not same physical space" crash - #65

Open
Duplums wants to merge 1 commit into
brainprepdesk:mainfrom
Duplums:fix/quasiraw-n4-physical-space
Open

Duplums wants to merge 1 commit into
brainprepdesk:mainfrom
Duplums:fix/quasiraw-n4-physical-space

Conversation

@Duplums

@Duplums Duplums commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Problem

brainprep_quasiraw() passes brainmask()'s output straight to ANTs' N4BiasFieldCorrection as the -x mask. mri_synthstrip (FreeSurfer) and fslreorient2std (FSL) each write their own NIfTI header independently, so the mask can end up on a grid that's numerically equivalent to the reoriented image but not bit-for-bit identical — down to the qform and sform of a single file sometimes disagreeing with each other by a float32 ULP or two. N4's ITK reader enforces exact geometry equality between -i and -x and aborts otherwise with:

itk::ExceptionObject: Inputs do not occupy the same physical space!

Running the current main quasiraw workflow against real data (OpenMind, ~72k scans across T1w/T2w/FLAIR) hit this on a meaningful share of scans, heavily concentrated in a handful of source datasets whose particular header conventions land on the wrong side of that tolerance.

Fix

Adds interfaces.regridmask(), inserted between brainmask() and biasfield(). It:

  1. Rewrites the mask onto the reference image's exact voxel grid (via nibabel, same shape/data, just re-anchored).
  2. Transcribes the raw qform/sform NIfTI header fields (quaternion components, offsets, sform rows, codes) byte-for-byte from the reference image's own header.

Step 2 matters on its own: I initially tried flirt -applyxfm with an identity transform, and separately nibabel's set_qform()/set_sform() with the reference's affine — both still failed on one case, because NIfTI's qform is quaternion-based, and any fresh matrix → quaternion decomposition (including nibabel's own) can round differently than whatever decomposition originally produced the reference's qform (e.g. FSL's). The only way to get a genuinely identical header is to not recompute anything and copy the raw fields directly.

Testing

Verified against real OpenMind scans on the actual container pipeline (bind-mounting this branch's source over the installed package, no rebuild needed):

  • 30/30 previously-failing scans (sampled across 3 source datasets, both FLAIR and T1w) now succeed.
  • 5/5 already-passing scans still succeed (no regression).
  • Spot-checked output geometry/intensity stats for sanity on several outputs.

🤖 Generated with Claude Code

brainprep_quasiraw() was passing brainmask()'s output straight to ANTs'
N4BiasFieldCorrection as the -x mask. mri_synthstrip (FreeSurfer) and
fslreorient2std (FSL) each write their own NIfTI header independently,
so the mask can end up on a grid that's numerically equivalent to the
reoriented image but not bit-for-bit identical — down to the qform and
sform of a single file sometimes disagreeing with each other by a
float32 ULP or two. N4's ITK reader enforces exact geometry equality
between -i and -x and aborts otherwise with:
"itk::ExceptionObject: Inputs do not occupy the same physical space".

Observed on real data (OpenMind), this affected a meaningful share of
T1w/T2w/FLAIR scans, heavily concentrated in a handful of source
datasets whose particular header conventions land on the wrong side of
that tolerance.

Adds interfaces.regridmask(), inserted between brainmask() and
biasfield(). It rewrites the mask onto the reference image's exact
grid and transcribes the raw qform/sform NIfTI fields byte-for-byte
from the reference image's own header — deliberately not recomputing
either form from an affine matrix, since any fresh matrix -> quaternion
decomposition (including nibabel's own set_qform()) can round differently
than whatever decomposition originally produced the reference's qform.

Verified on 30 real OpenMind scans that previously failed this way
(across 3 source datasets, both FLAIR and T1w) plus 5 that already
succeeded — 35/35 pass after this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant