Skip to content

Restore qform_code on NIFTI_NORDIC output NIfTIs#28

Open
jessefontaine wants to merge 1 commit into
SteenMoeller:mainfrom
jessefontaine:fix-qform-stripping
Open

Restore qform_code on NIFTI_NORDIC output NIfTIs#28
jessefontaine wants to merge 1 commit into
SteenMoeller:mainfrom
jessefontaine:fix-qform-stripping

Conversation

@jessefontaine

Copy link
Copy Markdown

Fixes #27

Summary

niftiwrite ignores info.raw.qform_code / quatern_* / qoffset_* on the write path, so every NIFTI_NORDIC output ends up with qform_code = 0. AFNI tools (which prefer qform over sform) then read the output as identity-voxel-space, silently misaligning oblique data with paired fieldmaps and crashing fMRIPrep's PEPOLAR SDC workflow on strongly-oblique acquisitions. See # for the full bug analysis and reproducer.

What this PR changes

  • Adds a local helper restore_qform_from_source(target_file, source_info, is_compressed) at the end of NIFTI_NORDIC.m.
  • Calls it immediately after each of the four niftiwrite invocations (gfactor write, complex-mode magn write, complex-mode phase write, main output write).

The helper writes qform_code, quatern_b/c/d, qoffset_x/y/z, and pixdim[0] (qfac) at their documented NIfTI-1 header byte offsets. The voxel data block is never opened. .nii.gz outputs are transparently gunzipped, patched, and re-gzipped.

No new dependencies — uses only fopen / fwrite / gzip / gunzip from base MATLAB.

Verification

On a synthetic oblique input (qform_code = 1, 45° rotation), before this PR:

file qform_code sform_code
input mag.nii.gz 1 1
output NORDIC_mag.nii.gz 0 1

After this PR, the output's qform matches the input's bit-for-bit.

Full reproducer in the linked issue.

Notes

  • I only touched NIFTI_NORDIC.m. NORDIC.m may have similar niftiwrite call sites; happy to extend the patch if you confirm.
  • Open to alternative implementations (e.g. switching the write path to save_untouch_nii from Jimmy Shen's NIfTI Tools, which would preserve qform natively — but that adds an external dependency, which I assumed you'd want to avoid).

MATLAB's niftiwrite drops qform_code on output (sets it to 0), even
when the input has a valid qform. Downstream AFNI tools prefer qform
over sform and fall back to identity-voxel-space when qform_code=0,
silently misaligning oblique data. Add a small helper that patches
the qform_code and qform parameters at the documented NIfTI-1 header
byte offsets after each niftiwrite call. Voxel data is not touched.
No new dependencies (base MATLAB only).

Fixes SteenMoeller#27
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.

niftiwrite drops qform_code on NIFTI_NORDIC output (silent breakage for AFNI-based downstream tools)

1 participant