@@ -15,15 +15,17 @@ class FSNifti1Extension:
15
15
This class handles Freesurfer Nifti1 header extension IO.
16
16
17
17
Class variables:
18
+ _verbose: if it is True, output debug information
18
19
_content: FSNifti1Extension.Content
19
20
"""
20
21
21
- def __init__ (self ):
22
+ def __init__ (self , verbose = False ):
22
23
"""
23
24
FSNifti1Extension Constructor
24
25
"""
25
26
26
27
# initialization
28
+ self ._verbose = verbose
27
29
self ._content = FSNifti1Extension .Content ()
28
30
29
31
@@ -56,8 +58,9 @@ def read(self, fileobj, esize, offset=0):
56
58
self .content .intent = int .from_bytes (fsexthdr [1 :3 ], byteorder = 'big' )
57
59
self .content .version = fsexthdr [3 ]
58
60
59
- print (f'[DEBUG] FSNifti1Extension.read(): esize = { esize :6d} ' )
60
- print (f'[DEBUG] FSNifti1Extension.read(): endian = \' { self .content .endian :c} \' , intent = { self .content .intent :d} , version = { self .content .version :d} ' )
61
+ if (self ._verbose ):
62
+ print (f'[DEBUG] FSNifti1Extension.read(): esize = { esize :6d} ' )
63
+ print (f'[DEBUG] FSNifti1Extension.read(): endian = \' { self .content .endian :c} \' , intent = { self .content .intent :d} , version = { self .content .version :d} ' )
61
64
62
65
# process Freesurfer Nifti1 extension tag data
63
66
tagdatalen = esize - 12 # exclude esize, ecode, fsexthdr
@@ -66,7 +69,8 @@ def read(self, fileobj, esize, offset=0):
66
69
# read tagid (4 bytes), data-length (8 bytes)
67
70
(tag , length ) = FSNifti1Extension .read_tag (fileobj )
68
71
69
- print (f'[DEBUG] FSNifti1Extension.read(): remaining taglen = { tagdatalen :6d} (tag = { tag :2d} , length = { length :5d} )' )
72
+ if (self ._verbose ):
73
+ print (f'[DEBUG] FSNifti1Extension.read(): remaining taglen = { tagdatalen :6d} (tag = { tag :2d} , length = { length :5d} )' )
70
74
71
75
if (tag == 0 ):
72
76
break
@@ -88,20 +92,20 @@ def read(self, fileobj, esize, offset=0):
88
92
dof = iou .read_int (fileobj , length )
89
93
self .content .dof = dof
90
94
91
- # ras_xform (TAG_RAS_XFORM = 8)
92
- elif (tag == FSNifti1Extension .Tags .ras_xform ):
93
- self .content .ras_xform = dict (
94
- rotation = iou .read_bytes (fileobj , '>f4' , 9 ).reshape ((3 , 3 ), order = 'F' ),
95
- center = iou .read_bytes (fileobj , '>f4' , 3 )
96
- )
97
-
98
95
# gcamorph src & trg geoms (warp) (TAG_GCAMORPH_GEOM = 10)
99
96
elif (tag == FSNifti1Extension .Tags .gcamorph_geom ):
100
97
if (not self .content .warpmeta ):
101
98
self .content .warpmeta = {}
102
99
103
100
(self .content .warpmeta ['source-geom' ], self .content .warpmeta ['source-valid' ], self .content .warpmeta ['source-fname' ]) = iou .read_geom (fileobj , niftiheaderext = True )
104
101
(self .content .warpmeta ['target-geom' ], self .content .warpmeta ['target-valid' ], self .content .warpmeta ['target-fname' ]) = iou .read_geom (fileobj , niftiheaderext = True )
102
+ # gcamorph src & trg geoms (warp) (TAG_GCAMORPH_GEOM_PLUSSHEAR = 15)
103
+ elif (tag == FSNifti1Extension .Tags .gcamorph_geom_plusshear ):
104
+ if (not self .content .warpmeta ):
105
+ self .content .warpmeta = {}
106
+
107
+ (self .content .warpmeta ['source-geom' ], self .content .warpmeta ['source-valid' ], self .content .warpmeta ['source-fname' ]) = iou .read_geom (fileobj , niftiheaderext = True , shearless = False )
108
+ (self .content .warpmeta ['target-geom' ], self .content .warpmeta ['target-valid' ], self .content .warpmeta ['target-fname' ]) = iou .read_geom (fileobj , niftiheaderext = True , shearless = False )
105
109
# gcamorph meta (warp: int int float) (TAG_GCAMORPH_META = 13)
106
110
elif (tag == FSNifti1Extension .Tags .gcamorph_meta ):
107
111
if (not self .content .warpmeta ):
@@ -129,7 +133,8 @@ def read(self, fileobj, esize, offset=0):
129
133
# check if we reach the end
130
134
tagdatalen -= (len_tagheader + length )
131
135
if (tagdatalen < len_tagheader ):
132
- print (f'[DEBUG] FSNifti1Extension.read(): remaining taglen = { tagdatalen :6d} ' )
136
+ if (self ._verbose ):
137
+ print (f'[DEBUG] FSNifti1Extension.read(): remaining taglen = { tagdatalen :6d} ' )
133
138
break ;
134
139
135
140
return self .content
@@ -163,7 +168,8 @@ def write(self, fileobj, content, countbytesonly=False):
163
168
iou .write_int (fileobj , content .version , size = 1 , byteorder = 'big' )
164
169
165
170
num_bytes = 4
166
- print (f'[DEBUG] FSNifti1Extension.write(): dlen = { num_bytes :6d} ' )
171
+ if (self ._verbose ):
172
+ print (f'[DEBUG] FSNifti1Extension.write(): dlen = { num_bytes :6d} ' )
167
173
168
174
# tag data
169
175
addtaglength = 12
@@ -175,7 +181,8 @@ def write(self, fileobj, content, countbytesonly=False):
175
181
tag = FSNifti1Extension .Tags .gcamorph_geom
176
182
length = FSNifti1Extension .getlen_gcamorph_geom (source_fname , target_fname )
177
183
num_bytes += length + addtaglength
178
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
184
+ if (self ._verbose ):
185
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
179
186
if (not countbytesonly ):
180
187
FSNifti1Extension .write_tag (fileobj , tag , length )
181
188
iou .write_geom (fileobj ,
@@ -189,11 +196,32 @@ def write(self, fileobj, content, countbytesonly=False):
189
196
fname = target_fname ,
190
197
niftiheaderext = True )
191
198
199
+ tag = FSNifti1Extension .Tags .gcamorph_geom_plusshear
200
+ length = FSNifti1Extension .getlen_gcamorph_geom (source_fname , target_fname , shearless = False )
201
+ num_bytes += length + addtaglength
202
+ if (self ._verbose ):
203
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
204
+ if (not countbytesonly ):
205
+ FSNifti1Extension .write_tag (fileobj , tag , length )
206
+ iou .write_geom (fileobj ,
207
+ geom = content .warpmeta ['source-geom' ],
208
+ valid = content .warpmeta .get ('source-valid' , True ),
209
+ fname = source_fname ,
210
+ niftiheaderext = True ,
211
+ shearless = False )
212
+ iou .write_geom (fileobj ,
213
+ geom = content .warpmeta ['target-geom' ],
214
+ valid = content .warpmeta .get ('target-valid' , True ),
215
+ fname = target_fname ,
216
+ niftiheaderext = True ,
217
+ shearless = False )
218
+
192
219
# gcamorph meta (warp: int int float) (TAG_GCAMORPH_META = 13)
193
220
tag = FSNifti1Extension .Tags .gcamorph_meta
194
221
length = 12
195
222
num_bytes += length + addtaglength
196
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
223
+ if (self ._verbose ):
224
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
197
225
if (not countbytesonly ):
198
226
FSNifti1Extension .write_tag (fileobj , tag , length )
199
227
iou .write_bytes (fileobj , content .warpmeta ['format' ], dtype = '>i4' )
@@ -206,7 +234,8 @@ def write(self, fileobj, content, countbytesonly=False):
206
234
tag = FSNifti1Extension .Tags .end_data
207
235
length = 1 # extra char '*'
208
236
num_bytes += length + addtaglength
209
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
237
+ if (self ._verbose ):
238
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
210
239
if (not countbytesonly ):
211
240
FSNifti1Extension .write_tag (fileobj , tag , length )
212
241
extrachar = '*'
@@ -220,7 +249,8 @@ def write(self, fileobj, content, countbytesonly=False):
220
249
tag = FSNifti1Extension .Tags .old_colortable
221
250
length = FSNifti1Extension .getlen_labels (content .labels )
222
251
num_bytes += length + addtaglength
223
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
252
+ if (self ._verbose ):
253
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
224
254
if (not countbytesonly ):
225
255
FSNifti1Extension .write_tag (fileobj , tag , length )
226
256
fsio .write_binary_lookup_table (fileobj , content .labels )
@@ -231,7 +261,8 @@ def write(self, fileobj, content, countbytesonly=False):
231
261
tag = FSNifti1Extension .Tags .history
232
262
length = len (hist )
233
263
num_bytes += length + addtaglength
234
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
264
+ if (self ._verbose ):
265
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
235
266
if (not countbytesonly ):
236
267
FSNifti1Extension .write_tag (fileobj , tag , length )
237
268
fileobj .write (hist .encode ('utf-8' ))
@@ -240,28 +271,19 @@ def write(self, fileobj, content, countbytesonly=False):
240
271
tag = FSNifti1Extension .Tags .dof
241
272
length = 4
242
273
num_bytes += length + addtaglength
243
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
274
+ if (self ._verbose ):
275
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
244
276
if (not countbytesonly ):
245
277
FSNifti1Extension .write_tag (fileobj , tag , length )
246
278
iou .write_int (fileobj , content .dof , size = 4 )
247
279
248
- # ras_xform (TAG_RAS_XFORM = 8)
249
- if (content .ras_xform ):
250
- tag = FSNifti1Extension .Tags .ras_xform
251
- length = 48
252
- num_bytes += length + addtaglength
253
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
254
- if (not countbytesonly ):
255
- FSNifti1Extension .write_tag (fileobj , tag , length )
256
- iou .write_bytes (fileobj , np .ravel (content .ras_xform ['rotation' ], order = 'F' ), '>f4' )
257
- iou .write_bytes (fileobj , content .ras_xform ['center' ], '>f4' )
258
-
259
280
# scan_parameters (TAG_SCAN_PARAMETERS = 45)
260
281
if (content .scan_parameters ):
261
282
tag = FSNifti1Extension .Tags .scan_parameters
262
283
length = 20 + len (content .scan_parameters ['pedir' ])
263
284
num_bytes += length + addtaglength
264
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
285
+ if (self ._verbose ):
286
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
265
287
if (not countbytesonly ):
266
288
FSNifti1Extension .write_tag (fileobj , tag , length )
267
289
iou .write_bytes (fileobj , content .scan_parameters ['te' ], '>f4' )
@@ -284,7 +306,8 @@ def write(self, fileobj, content, countbytesonly=False):
284
306
tag = FSNifti1Extension .Tags .end_data
285
307
length = 1 # extra char '*'
286
308
num_bytes += length + addtaglength
287
- print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
309
+ if (self ._verbose ):
310
+ print (f'[DEBUG] FSNifti1Extension.write(): +{ length :5d} , +{ addtaglength :d} , dlen = { num_bytes :6d} , TAG = { tag :2d} ' )
288
311
if (not countbytesonly ):
289
312
FSNifti1Extension .write_tag (fileobj , tag , length )
290
313
extrachar = '*'
@@ -364,7 +387,7 @@ def getlen_labels(labels):
364
387
365
388
366
389
@staticmethod
367
- def getlen_gcamorph_geom (fname_source , fname_target ):
390
+ def getlen_gcamorph_geom (fname_source , fname_target , shearless = True ):
368
391
"""
369
392
Calculate total bytes that will be written for the labels.
370
393
@@ -383,6 +406,8 @@ def getlen_gcamorph_geom(fname_source, fname_target):
383
406
384
407
# See freesurfer/utils/fstagsio.cpp::getlen_gcamorph_geom().
385
408
num_bytes = 2 * 80
409
+ if (not shearless ):
410
+ num_bytes += 2 * 12 # 2 * 3 * 4 bytes (3 shear float numbers each)
386
411
num_bytes += len (fname_source )
387
412
num_bytes += len (fname_target )
388
413
@@ -407,16 +432,20 @@ class Tags:
407
432
408
433
This class defines the tags recognized in surfa.
409
434
It is a subset of tag IDs defined in freesurfer/include/tags.h
435
+
436
+ TAG_RAS_XFORM is also output by Freesurfer for FS nifti1 header extension only to store shearless rotation and center parameters.
437
+ This is to prevent precision loss from sform/qform decompose and compose when nii is read and written in Freesurfer.
438
+ The tag is not necessary for Surfa.
410
439
"""
411
440
412
- old_colortable = 1 # TAG_OLD_COLORTABLE
413
- history = 3 # TAG_CMDLINE
414
- dof = 7 # TAG_DOF
415
- ras_xform = 8 # TAG_RAS_XFORM
416
- gcamorph_geom = 10 # TAG_GCAMORPH_GEOM
417
- gcamorph_meta = 13 # TAG_GCAMORPH_META
418
- scan_parameters = 45 # TAG_SCAN_PARAMETERS
419
- end_data = - 1 # TAG_END_NIIHDREXTENSION
441
+ old_colortable = 1 # TAG_OLD_COLORTABLE
442
+ history = 3 # TAG_CMDLINE
443
+ dof = 7 # TAG_DOF
444
+ gcamorph_geom = 10 # TAG_GCAMORPH_GEOM
445
+ gcamorph_meta = 13 # TAG_GCAMORPH_META
446
+ gcamorph_geom_plusshear = 15 # information output under gcamorph_geom + shear components
447
+ scan_parameters = 45 # TAG_SCAN_PARAMETERS
448
+ end_data = - 1 # TAG_END_NIIHDREXTENSION
420
449
421
450
422
451
class Content :
0 commit comments