@@ -701,9 +701,29 @@ def get_qform_quaternion(self):
701
701
# Adjust threshold to fact that source data was float32
702
702
return fillpositive (bcd , FLOAT32_EPS_3 )
703
703
704
- def get_qform (self ):
705
- ''' Return 4x4 affine matrix from qform parameters in header '''
704
+ def get_qform (self , coded = False ):
705
+ """ Return 4x4 affine matrix from qform parameters in header
706
+
707
+ Parameters
708
+ ----------
709
+ coded : bool, optional
710
+ If True, return {affine or None}, and qform code. If False, just
711
+ return affine. {affine or None} means, return None if qform code ==
712
+ 0, and affine otherwise.
713
+
714
+ Returns
715
+ -------
716
+ affine : None or (4,4) ndarray
717
+ If `coded` is False, always return affine reconstructed from qform
718
+ quaternion. If `coded` is True, return None if qform code is 0,
719
+ else return the affine.
720
+ code : int
721
+ Qform code. Only returned if `coded` is True.
722
+ """
706
723
hdr = self ._structarr
724
+ code = hdr ['qform_code' ]
725
+ if code == 0 and coded :
726
+ return None , 0
707
727
quat = self .get_qform_quaternion ()
708
728
R = quat2mat (quat )
709
729
vox = hdr ['pixdim' ][1 :4 ].copy ()
@@ -718,30 +738,39 @@ def get_qform(self):
718
738
out = np .eye (4 )
719
739
out [0 :3 , 0 :3 ] = M
720
740
out [0 :3 , 3 ] = [hdr ['qoffset_x' ], hdr ['qoffset_y' ], hdr ['qoffset_z' ]]
741
+ if coded :
742
+ return out , code
721
743
return out
722
744
723
- def set_qform (self , affine , code = None ):
745
+ def set_qform (self , affine , code = None , strip_shears = True ):
724
746
''' Set qform header values from 4x4 affine
725
747
726
748
Parameters
727
749
----------
728
750
hdr : nifti1 header
729
- affine : 4x4 array
730
- affine transform to write into qform
751
+ affine : None or 4x4 array
752
+ affine transform to write into sform. If None, only set code.
731
753
code : None, string or integer
732
754
String or integer giving meaning of transform in *affine*.
733
- The default is None. If code is None, then {if current
734
- qform code is not 0, leave code as it is in the header; else
735
- set to 2 ('aligned')}.
755
+ The default is None. If code is None, then:
756
+ * If affine is None, `code`-> 0
757
+ * If affine not None and sform code in header == 0, `code`-> 2
758
+ (aligned)
759
+ * If affine not None and sform code in header != 0, `code`-> sform
760
+ code in header
761
+ strip_shears : bool, optional
762
+ Whether to strip shears in `affine`. If True, shears will be
763
+ silently stripped. If False, the presence of shears will raise a
764
+ ``HeaderDataError``
736
765
737
766
Notes
738
767
-----
739
768
The qform transform only encodes translations, rotations and
740
- zooms. If there are shear components to the `affine` transform,
741
- the written qform gives the closest approximation where the
742
- rotation matrix is orthogonal. This is to allow quaternion
743
- representation. The orthogonal representation enforces
744
- orthogonal axes.
769
+ zooms. If there are shear components to the `affine` transform, and
770
+ `strip_shears` is True (the default), the written qform gives the
771
+ closest approximation where the rotation matrix is orthogonal. This is
772
+ to allow quaternion representation. The orthogonal representation
773
+ enforces orthogonal axes.
745
774
746
775
Examples
747
776
--------
@@ -765,15 +794,25 @@ def set_qform(self, affine, code=None):
765
794
>>> hdr.set_qform(affine, code='scanner')
766
795
>>> int(hdr['qform_code'])
767
796
1
797
+ >>> hdr.set_qform(None)
798
+ >>> int(hdr['qform_code'])
799
+ 0
768
800
'''
769
801
hdr = self ._structarr
802
+ old_code = hdr ['qform_code' ]
770
803
if code is None :
771
- code = hdr ['qform_code' ]
772
- if code == 0 : # default is 'aligned'
773
- hdr ['qform_code' ] = 2
774
- else :
804
+ if affine is None :
805
+ code = 0
806
+ elif old_code == 0 :
807
+ code = 2 # aligned
808
+ else :
809
+ code = old_code
810
+ else : # code set
775
811
code = self ._field_recoders ['qform_code' ][code ]
776
- hdr ['qform_code' ] = code
812
+ hdr ['qform_code' ] = code
813
+ if affine is None :
814
+ return
815
+ affine = np .asarray (affine )
777
816
if not affine .shape == (4 , 4 ):
778
817
raise TypeError ('Need 4x4 affine as input' )
779
818
trans = affine [:3 , 3 ]
@@ -793,6 +832,9 @@ def set_qform(self, affine, code=None):
793
832
# orthogonal matrix PR, to input R
794
833
P , S , Qs = npl .svd (R )
795
834
PR = np .dot (P , Qs )
835
+ if not strip_shears and not np .allclose (PR , R ):
836
+ raise HeaderDataError ("Shears in affine and `strip_shears` is "
837
+ "False" )
796
838
# Convert to quaternion
797
839
quat = mat2quat (PR )
798
840
# Set into header
@@ -801,13 +843,35 @@ def set_qform(self, affine, code=None):
801
843
hdr ['pixdim' ][1 :4 ] = zooms
802
844
hdr ['quatern_b' ], hdr ['quatern_c' ], hdr ['quatern_d' ] = quat [1 :]
803
845
804
- def get_sform (self ):
805
- ''' Return sform 4x4 affine matrix from header '''
846
+ def get_sform (self , coded = False ):
847
+ """ Return 4x4 affine matrix from sform parameters in header
848
+
849
+ Parameters
850
+ ----------
851
+ coded : bool, optional
852
+ If True, return {affine or None}, and sform code. If False, just
853
+ return affine. {affine or None} means, return None if sform code ==
854
+ 0, and affine otherwise.
855
+
856
+ Returns
857
+ -------
858
+ affine : None or (4,4) ndarray
859
+ If `coded` is False, always return affine from sform fields. If
860
+ `coded` is True, return None if sform code is 0, else return the
861
+ affine.
862
+ code : int
863
+ Sform code. Only returned if `coded` is True.
864
+ """
806
865
hdr = self ._structarr
866
+ code = hdr ['sform_code' ]
867
+ if code == 0 and coded :
868
+ return None , 0
807
869
out = np .eye (4 )
808
870
out [0 , :] = hdr ['srow_x' ][:]
809
871
out [1 , :] = hdr ['srow_y' ][:]
810
872
out [2 , :] = hdr ['srow_z' ][:]
873
+ if coded :
874
+ return out , code
811
875
return out
812
876
813
877
def set_sform (self , affine , code = None ):
@@ -816,13 +880,16 @@ def set_sform(self, affine, code=None):
816
880
Parameters
817
881
----------
818
882
hdr : nifti1 header
819
- affine : 4x4 array
820
- affine transform to write into sform
883
+ affine : None or 4x4 array
884
+ affine transform to write into sform. If None, only set `code`
821
885
code : None, string or integer
822
886
String or integer giving meaning of transform in *affine*.
823
- The default is None. If code is None, then {if current
824
- sform code is not 0, leave code as it is in the header; else
825
- set to 2 ('aligned')}.
887
+ The default is None. If code is None, then:
888
+ * If affine is None, `code`-> 0
889
+ * If affine not None and sform code in header == 0, `code`-> 2
890
+ (aligned)
891
+ * If affine not None and sform code in header != 0, `code`-> sform
892
+ code in header
826
893
827
894
Examples
828
895
--------
@@ -846,15 +913,25 @@ def set_sform(self, affine, code=None):
846
913
>>> hdr.set_sform(affine, code='scanner')
847
914
>>> int(hdr['sform_code'])
848
915
1
916
+ >>> hdr.set_sform(None)
917
+ >>> int(hdr['sform_code'])
918
+ 0
849
919
'''
850
920
hdr = self ._structarr
921
+ old_code = hdr ['sform_code' ]
851
922
if code is None :
852
- code = hdr ['sform_code' ]
853
- if code == 0 :
854
- hdr ['sform_code' ] = 2 # aligned
855
- else :
923
+ if affine is None :
924
+ code = 0
925
+ elif old_code == 0 :
926
+ code = 2 # aligned
927
+ else :
928
+ code = old_code
929
+ else : # code set
856
930
code = self ._field_recoders ['sform_code' ][code ]
857
- hdr ['sform_code' ] = code
931
+ hdr ['sform_code' ] = code
932
+ if affine is None :
933
+ return
934
+ affine = np .asarray (affine )
858
935
hdr ['srow_x' ][:] = affine [0 , :]
859
936
hdr ['srow_y' ][:] = affine [1 , :]
860
937
hdr ['srow_z' ][:] = affine [2 , :]
@@ -1512,6 +1589,170 @@ def update_header(self):
1512
1589
# Make qform 'unknown'
1513
1590
hdr .set_qform (self ._affine , code = 'unknown' )
1514
1591
1592
+ def get_qform (self , coded = False ):
1593
+ """ Return 4x4 affine matrix from qform parameters in header
1594
+
1595
+ Parameters
1596
+ ----------
1597
+ coded : bool, optional
1598
+ If True, return {affine or None}, and qform code. If False, just
1599
+ return affine. {affine or None} means, return None if qform code ==
1600
+ 0, and affine otherwise.
1601
+
1602
+ Returns
1603
+ -------
1604
+ affine : None or (4,4) ndarray
1605
+ If `coded` is False, always return affine reconstructed from qform
1606
+ quaternion. If `coded` is True, return None if qform code is 0,
1607
+ else return the affine.
1608
+ code : int
1609
+ Qform code. Only returned if `coded` is True.
1610
+
1611
+ See also
1612
+ --------
1613
+ Nifti1Header.set_qform
1614
+ """
1615
+ return self ._header .get_qform (coded )
1616
+
1617
+ def set_qform (self , affine , code = None , strip_shears = True , ** kwargs ):
1618
+ ''' Set qform header values from 4x4 affine
1619
+
1620
+ Parameters
1621
+ ----------
1622
+ hdr : nifti1 header
1623
+ affine : None or 4x4 array
1624
+ affine transform to write into sform. If None, only set code.
1625
+ code : None, string or integer
1626
+ String or integer giving meaning of transform in *affine*.
1627
+ The default is None. If code is None, then:
1628
+ * If affine is None, `code`-> 0
1629
+ * If affine not None and sform code in header == 0, `code`-> 2
1630
+ (aligned)
1631
+ * If affine not None and sform code in header != 0, `code`-> sform
1632
+ code in header
1633
+ strip_shears : bool, optional
1634
+ Whether to strip shears in `affine`. If True, shears will be
1635
+ silently stripped. If False, the presence of shears will raise a
1636
+ ``HeaderDataError``
1637
+ update_affine : bool, optional
1638
+ Whether to update the image affine from the header best affine after
1639
+ setting the qform. Must be keyword argumemt (because of different
1640
+ position in `set_qform`). Default is True
1641
+
1642
+ See also
1643
+ --------
1644
+ Nifti1Header.set_qform
1645
+
1646
+ Examples
1647
+ --------
1648
+ >>> data = np.arange(24).reshape((2,3,4))
1649
+ >>> aff = np.diag([2, 3, 4, 1])
1650
+ >>> img = Nifti1Pair(data, aff)
1651
+ >>> img.get_qform()
1652
+ array([[ 2., 0., 0., 0.],
1653
+ [ 0., 3., 0., 0.],
1654
+ [ 0., 0., 4., 0.],
1655
+ [ 0., 0., 0., 1.]])
1656
+ >>> img.get_qform(coded=True)
1657
+ (None, 0)
1658
+ >>> aff2 = np.diag([3, 4, 5, 1])
1659
+ >>> img.set_qform(aff2, 'talairach')
1660
+ >>> qaff, code = img.get_qform(coded=True)
1661
+ >>> np.all(qaff == aff2)
1662
+ True
1663
+ >>> int(code)
1664
+ 3
1665
+ '''
1666
+ update_affine = kwargs .pop ('update_affine' , True )
1667
+ if kwargs :
1668
+ raise TypeError ('Unexpected keyword argument(s) %s' % kwargs )
1669
+ self ._header .set_qform (affine , code , strip_shears )
1670
+ if update_affine :
1671
+ self ._affine [:] = self ._header .get_best_affine ()
1672
+
1673
+ def get_sform (self , coded = False ):
1674
+ """ Return 4x4 affine matrix from sform parameters in header
1675
+
1676
+ Parameters
1677
+ ----------
1678
+ coded : bool, optional
1679
+ If True, return {affine or None}, and sform code. If False, just
1680
+ return affine. {affine or None} means, return None if sform code ==
1681
+ 0, and affine otherwise.
1682
+
1683
+ Returns
1684
+ -------
1685
+ affine : None or (4,4) ndarray
1686
+ If `coded` is False, always return affine from sform fields. If
1687
+ `coded` is True, return None if sform code is 0, else return the
1688
+ affine.
1689
+ code : int
1690
+ Sform code. Only returned if `coded` is True.
1691
+
1692
+ See also
1693
+ --------
1694
+ Nifti1Header.get_sform
1695
+ """
1696
+ return self ._header .get_sform (coded )
1697
+
1698
+ def set_sform (self , affine , code = None , ** kwargs ):
1699
+ ''' Set sform transform from 4x4 affine
1700
+
1701
+ Parameters
1702
+ ----------
1703
+ hdr : nifti1 header
1704
+ affine : None or 4x4 array
1705
+ affine transform to write into sform. If None, only set `code`
1706
+ code : None, string or integer
1707
+ String or integer giving meaning of transform in *affine*.
1708
+ The default is None. If code is None, then:
1709
+ * If affine is None, `code`-> 0
1710
+ * If affine not None and sform code in header == 0, `code`-> 2
1711
+ (aligned)
1712
+ * If affine not None and sform code in header != 0, `code`-> sform
1713
+ code in header
1714
+ update_affine : bool, optional
1715
+ Whether to update the image affine from the header best affine after
1716
+ setting the qform. Must be keyword argumemt (because of different
1717
+ position in `set_qform`). Default is True
1718
+
1719
+ See also
1720
+ --------
1721
+ Nifti1Header.set_sform
1722
+
1723
+ Examples
1724
+ --------
1725
+ >>> data = np.arange(24).reshape((2,3,4))
1726
+ >>> aff = np.diag([2, 3, 4, 1])
1727
+ >>> img = Nifti1Pair(data, aff)
1728
+ >>> img.get_sform()
1729
+ array([[ 2., 0., 0., 0.],
1730
+ [ 0., 3., 0., 0.],
1731
+ [ 0., 0., 4., 0.],
1732
+ [ 0., 0., 0., 1.]])
1733
+ >>> saff, code = img.get_sform(coded=True)
1734
+ >>> saff
1735
+ array([[ 2., 0., 0., 0.],
1736
+ [ 0., 3., 0., 0.],
1737
+ [ 0., 0., 4., 0.],
1738
+ [ 0., 0., 0., 1.]])
1739
+ >>> int(code)
1740
+ 2
1741
+ >>> aff2 = np.diag([3, 4, 5, 1])
1742
+ >>> img.set_sform(aff2, 'talairach')
1743
+ >>> saff, code = img.get_sform(coded=True)
1744
+ >>> np.all(saff == aff2)
1745
+ True
1746
+ >>> int(code)
1747
+ 3
1748
+ '''
1749
+ update_affine = kwargs .pop ('update_affine' , True )
1750
+ if kwargs :
1751
+ raise TypeError ('Unexpected keyword argument(s) %s' % kwargs )
1752
+ self ._header .set_sform (affine , code )
1753
+ if update_affine :
1754
+ self ._affine [:] = self ._header .get_best_affine ()
1755
+
1515
1756
1516
1757
class Nifti1Image (Nifti1Pair ):
1517
1758
header_class = Nifti1Header
0 commit comments