diff --git a/doc/source/nifti_images.rst b/doc/source/nifti_images.rst index 257465f3a9..2f8a7cd0db 100644 --- a/doc/source/nifti_images.rst +++ b/doc/source/nifti_images.rst @@ -314,6 +314,63 @@ The algorithm is defined in the ``get_best_affine()`` method. It is: #. If ``qform_code`` != 0 ('unknown') use the qform affine; else #. Use the fall-back affine. +.. _default-sform-qform-codes: + +Default sform and qform codes +============================= + +If you create a new image, e.g.: + +>>> data = np.random.random((20, 20, 20)) +>>> xform = np.eye(4) * 2 +>>> img = nib.nifti1.Nifti1Image(data, xform) + +The sform and qform codes will be initialised to 2 (aligned) and 0 (unknown) +respectively: + +>>> img.get_sform(coded=True) # doctest: +NORMALIZE_WHITESPACE +(array([[ 2., 0., 0., 0.], + [ 0., 2., 0., 0.], + [ 0., 0., 2., 0.], + [ 0., 0., 0., 1.]]), array(2, dtype=int16)) +>>> img.get_qform(coded=True) +(None, 0) + +This is based on the assumption that the affine you specify for a newly +created image will align the image to some known coordinate system. According +to the `NIfTI specification `_, the qform is intended to encode a +transformation into scanner coordinates - for a programmatically created +image, we have no way of knowing what the scanner coordinate system is; +furthermore, the qform cannot be used to store an arbitrary affine transform, +as it is unable to encode shears. So the provided affine will be stored in the +sform, and the qform will be left uninitialised. + +If you create a new image and specify an existing header, e.g.: + +>>> example_ni1 = os.path.join(data_path, 'example4d.nii.gz') +>>> n1_img = nib.load(example_ni1) +>>> new_header = header=n1_img.header.copy() +>>> new_data = np.random.random(n1_img.shape[:3]) +>>> new_img = nib.nifti1.Nifti1Image(data, None, header=new_header) + +then the newly created image will inherit the same sform and qform codes that +are in the provided header. However, if you create a new image with both an +affine and a header specified, e.g.: + +>>> xform = np.eye(4) +>>> new_img = nib.nifti1.Nifti1Image(data, xform, header=new_header) + +then the sform and qform codes will *only* be preserved if the provided affine +is the same as the affine in the provided header. If the affines do not match, +the sform and qform codes will be set to their default values of 2 and 0 +respectively. This is done on the basis that, if you are changing the affine, +you are likely to be changing the space to which the affine is pointing. So +the original sform and qform codes can no longer be assumed to be valid. + +If you wish to set the sform and qform affines and/or codes to some other +value, you can always set them after creation using the ``set_sform`` and +``set_qform`` methods, as described above. + ************ Data scaling ************ diff --git a/nibabel/nifti1.py b/nibabel/nifti1.py index 4d766a76f5..ff3ee32118 100644 --- a/nibabel/nifti1.py +++ b/nibabel/nifti1.py @@ -1764,7 +1764,20 @@ def __init__(self, dataobj, affine, header=None, if header is None and affine is not None: self._affine2header() # Copy docstring - __init__.doc = analyze.AnalyzeImage.__init__.__doc__ + __init__.__doc__ = analyze.AnalyzeImage.__init__.__doc__ + ''' + Notes + ----- + + If both a `header` and an `affine` are specified, and the `affine` does + not match the affine that is in the `header`, the `affine` will be used, + but the ``sform_code`` and ``qform_code`` fields in the header will be + re-initialised to their default values. This is performed on the basis + that, if you are changing the affine, you are likely to be changing the + space to which the affine is pointing. The :meth:`set_sform` and + :meth:`set_qform` methods can be used to update the codes after an image + has been created - see those methods, and the :ref:`manual + ` for more details. ''' + def update_header(self): ''' Harmonize header with image data and affine