Skip to content

Proposal: affine_source (or header_source) in output File traits #2223

Open
@effigies

Description

@effigies

Several packages handle NIfTI headers with insufficient reverence, ending up with affines that don't really match the expected result of an operation. For example, AFNI converts NIfTI to BRIK+HEAD, performs its operation, and makes a somewhat wild stab at the correct qform/sform codes based on an environment variable, so operations that don't move the data might still change the affine. ANTs' dependence on ITK means they don't recognize sforms as legitimate. Meanwhile the standard's order of precedence (sform > qform > pixdims) means that only updating the sform is fairly common practice by still other software (I believe FSL only updates sforms).

All of this means that mixing and matching these tools, one of the primary use-cases of nipype, can result in some profound disagreements of how the same input file is aligned and how to write a header in the output file. In practice this can mean that two files with the same affine (as far as the standard is concerned), with the same transform applied by the same software can end up misaligned.

While an incomplete solution for qforms/sforms that get out of sync, at least preventing affine corruption would be a start to handling issues like this. We have addressed this previously in N4BiasFieldCorrection (#2034), but a consistent interface across tools would be very useful.

I propose:

class XInputSpec(BaseInterfaceSpec):
    ref_file = File(desc='file defining target space')

class XOutputSpec(TraitedSpec):
    out_file = File(affine_source=['ref_file'], desc='output file in target space')

A post-run hook would then copy the affines from inputs.ref_file to outputs.out_file, with the following:

try:
    ref_img = nib.load(ref_file)
    out_img = nib.load(out_file)
    affine = ref_img.afffine
    new_img = out_img.__class__(out_img.dataobj, affine, out_img.header)
except:
    iflogger.warn("Could not copy affine!")
try:
    sform, sform_code = ref_img.header.get_sform(coded=True)
    qform, qform_code = ref_img.header.get_qform(coded=True)
    new_img.header.set_sform(sform, sform_code)
    new_img.header.set_qform(qform, qform_code)
except:
    # Input file or output file is not NIfTI
    pass
new_img.to_filename(out_file)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions