@@ -38,14 +38,16 @@ def add_phase_to_kspace_with_shifts(kspace_data, kspace_loc, normalized_shifts):
3838 return kspace_data * phase
3939
4040
41- def siemens_quat_to_rot_mat (quat ):
41+ def siemens_quat_to_rot_mat (quat , return_det = False ):
4242 """
4343 Calculate the rotation matrix from Siemens Twix quaternion.
4444
4545 Parameters
4646 ----------
4747 quat : np.ndarray
4848 The quaternion from the Siemens Twix file.
49+ return_det : bool
50+ Whether to return the determinent of the rotation before norm
4951
5052 Returns
5153 -------
@@ -56,11 +58,15 @@ def siemens_quat_to_rot_mat(quat):
5658 R = np .zeros ((4 , 4 ))
5759 R [:3 , :3 ] = Rotation .from_quat ([quat [1 ], quat [2 ], quat [3 ], quat [0 ]]).as_matrix ()
5860 R [:, (0 , 1 )] = R [:, (1 , 0 )]
59- if np .linalg .det (R [:3 , :3 ]) < 0 :
61+ det = np .linalg .det (R [:3 , :3 ])
62+ if det < 0 :
6063 R [2 ] = - R [2 ]
6164 R [- 1 , - 1 ] = 1
65+ if return_det :
66+ return R , det
6267 return R
6368
69+
6470def nifti_affine (twixObj ):
6571 """
6672 Calculate the affine transformation matrix from Siemens Twix object.
@@ -78,51 +84,56 @@ def nifti_affine(twixObj):
7884 """
7985 # required keys
8086 keys = {
81- ' dthick' : (' sSliceArray' , ' asSlice' , '0' , ' dThickness' ),
82- ' dread' : (' sSliceArray' , ' asSlice' , '0' , ' dReadoutFOV' ),
83- ' dphase' : (' sSliceArray' , ' asSlice' , '0' , ' dPhaseFOV' ),
84- ' lbase' : (' sKSpace' , ' lBaseResolution' ),
85- ' lphase' : (' sKSpace' , ' lPhaseEncodingLines' ),
86- ' ucdim' : (' sKSpace' , ' ucDimension' ),
87- }
88- sos = (' sKSpace' , ' dSliceOversamplingForDialog' )
89- rot = siemens_quat_to_rot_mat (twixObj .image .slicePos [0 ][- 4 :])
87+ " dthick" : (" sSliceArray" , " asSlice" , "0" , " dThickness" ),
88+ " dread" : (" sSliceArray" , " asSlice" , "0" , " dReadoutFOV" ),
89+ " dphase" : (" sSliceArray" , " asSlice" , "0" , " dPhaseFOV" ),
90+ " lbase" : (" sKSpace" , " lBaseResolution" ),
91+ " lphase" : (" sKSpace" , " lPhaseEncodingLines" ),
92+ " ucdim" : (" sKSpace" , " ucDimension" ),
93+ }
94+ sos = (" sKSpace" , " dSliceOversamplingForDialog" )
95+ rot , det = siemens_quat_to_rot_mat (twixObj .image .slicePos [0 ][- 4 :], True )
9096 my = twixObj .hdr .MeasYaps
9197
9298 for k in keys .keys ():
9399 if keys [k ] not in my :
94100 return rot
95101
96- dthick = my [keys ['dthick' ]]
97- fov = np .array ([
98- my [keys ['dread' ]],
99- my [keys ['dphase' ]],
102+ dthick = my [keys ["dthick" ]]
103+ fov = np .array (
104+ [
105+ my [keys ["dread" ]],
106+ my [keys ["dphase" ]],
100107 dthick * (1 + my [sos ] if sos in my else 1 ),
101- ])
102-
103- lpart = ('sKSpace' , 'lPartitions' )
104- res = np .array ([
105- my [keys ['lbase' ]],
106- my [keys ['lphase' ]],
107- my [lpart ] if my [keys ['ucdim' ]] == 4 and lpart in my else 1 ,
108- ])
108+ ]
109+ )
110+
111+ lpart = ("sKSpace" , "lPartitions" )
112+ res = np .array (
113+ [
114+ my [keys ["lbase" ]],
115+ my [keys ["lphase" ]],
116+ my [lpart ] if my [keys ["ucdim" ]] == 4 and lpart in my else 1 ,
117+ ]
118+ )
109119
110120 scale = np .diag ([* (fov / res ), 1 ])
111121
112122 offset = twixObj .image .slicePos [0 ][:3 ]
113123
114- center = [- fov [0 ]/ 2 ,
115- - fov [1 ]/ 2 ,
116- - (fov [2 ] - (my [sos ] * dthick if sos in my else 0 ))/ 2 ,
117- 1 ]
124+ fovz = fov [2 ] - (my [sos ] * dthick if sos in my else 0 )
125+ center = [- fov [0 ] / 2 , - fov [1 ] / 2 , - fovz / 2 , 1 ]
118126
119127 t = (rot @ center )[:3 ] - offset
128+ if det < 0 :
129+ t [2 ] = (rot @ center )[2 ] * 2 - t [2 ]
120130
121131 full_mat = rot @ scale
122132 full_mat [:3 , 3 ] = t
123133
124134 return full_mat
125135
136+
126137def remove_extra_kspace_samples (kspace_data , num_samples_per_shot ):
127138 """Remove extra samples from k-space data.
128139
0 commit comments