@@ -389,14 +389,6 @@ def write_to(self, fileobj, byteswap):
389
389
-------
390
390
None
391
391
'''
392
- # not extensions -> nothing to do
393
- if not len (self ):
394
- # no extensions: be nice and write appropriate flag
395
- fileobj .write (np .array ((0 , 0 , 0 , 0 ), dtype = np .int8 ).tostring ())
396
- return
397
- # since we have extensions write the appropriate flag
398
- fileobj .write (np .array ((1 , 0 , 0 , 0 ), dtype = np .int8 ).tostring ())
399
- # and now each extension
400
392
for e in self :
401
393
e .write_to (fileobj , byteswap )
402
394
@@ -407,41 +399,24 @@ def from_fileobj(klass, fileobj, size, byteswap):
407
399
Parameters
408
400
----------
409
401
fileobj : file-like object
410
- It is assumed to be positions right after the NIfTI magic field.
402
+ We begin reading the extensions at the current file position
411
403
size : int
412
- Number of bytes to read. If negative, fileobj will be read till its
413
- end.
404
+ Number of bytes to read. If negative, fileobj will be read till its
405
+ end.
414
406
byteswap : boolean
415
- Flag if byteswapping the read data is required.
407
+ Flag if byteswapping the read data is required.
416
408
417
409
Returns
418
410
-------
419
- An extension list. This list might be empty in case not extensions
420
- were present in fileobj.
411
+ An extension list. This list might be empty in case not extensions
412
+ were present in fileobj.
421
413
'''
422
414
# make empty extension list
423
415
extensions = klass ()
424
- # assume the fileptr is just after header (magic field)
425
- # try reading the next 4 bytes after the initial header
426
- extension_status = fileobj .read (4 )
427
- if not len (extension_status ):
428
- # if there is nothing the NIfTI standard requires to assume zeros
429
- extension_status = np .zeros ((4 ,), dtype = np .int8 )
430
- else :
431
- extension_status = np .fromstring (extension_status , dtype = np .int8 )
432
- if byteswap :
433
- extension_status = extension_status .byteswap ()
434
- # NIfTI1 says: if first element is non-zero there are extensions present
435
- # if not there is nothing left to do
436
- if not extension_status [0 ]:
437
- return extensions
438
- # note that we read the extension flag
439
- if not size < 0 :
440
- size = size - 4
416
+ # assume the file pointer is at the beginning of any extensions.
441
417
# read until the whole header is parsed (each extension is a multiple
442
418
# of 16 bytes) or in case of a separate header file till the end
443
419
# (break inside the body)
444
- # XXX not sure if the separate header behavior is sane
445
420
while size >= 16 or size < 0 :
446
421
# the next 8 bytes should have esize and ecode
447
422
ext_def = fileobj .read (8 )
@@ -485,7 +460,14 @@ class Nifti1Header(SpmAnalyzeHeader):
485
460
''' Class for NIFTI1 header
486
461
487
462
The NIFTI1 header has many more coded fields than the simpler Analyze
488
- variants. Analyze headers also have extensions
463
+ variants. Nifti1 headers also have extensions.
464
+
465
+ Nifti allows the header to be a separate file, as part of a nifti image /
466
+ header pair, or to precede the data in a single file. The object needs to
467
+ know which type it is, in order to manage the voxel offset pointing to the
468
+ data, extension reading, and writing the correct magic string.
469
+
470
+ This class handles the header-preceding-data case.
489
471
'''
490
472
# Copies of module level definitions
491
473
_dtype = header_dtype
@@ -506,6 +488,9 @@ class Nifti1Header(SpmAnalyzeHeader):
506
488
# ``from_fileobj`` for reading from file
507
489
exts_klass = Nifti1Extensions
508
490
491
+ # Signal whether this is single (header + data) file
492
+ is_single = True
493
+
509
494
def __init__ (self ,
510
495
binaryblock = None ,
511
496
endianness = None ,
@@ -521,7 +506,7 @@ def __init__(self,
521
506
def copy (self ):
522
507
''' Return copy of header
523
508
524
- Take extensions as well as header
509
+ Take reference to extensions as well as copy of header contents
525
510
'''
526
511
return self .__class__ (
527
512
self .binaryblock ,
@@ -533,29 +518,34 @@ def copy(self):
533
518
def from_fileobj (klass , fileobj , endianness = None , check = True ):
534
519
raw_str = fileobj .read (klass ._dtype .itemsize )
535
520
hdr = klass (raw_str , endianness , check )
536
- hdr_len = hdr ._header_len ()
537
- if hdr_len == - 1 :
521
+ # Read next 4 bytes to see if we have extensions. The nifti standard
522
+ # has this as a 4 byte string; if the first value is not zero, then we
523
+ # have extensions.
524
+ extension_status = fileobj .read (4 )
525
+ if len (extension_status ) < 4 or extension_status [0 ] == '\x00 ' :
526
+ return hdr
527
+ # If this is a detached header file read to end
528
+ if not klass .is_single :
538
529
extsize = - 1
539
- else :
540
- extsize = hdr_len - fileobj .tell ()
530
+ else : # otherwise read until the beginning of the data
531
+ extsize = hdr . _header_data [ 'vox_offset' ] - fileobj .tell ()
541
532
byteswap = endian_codes ['native' ] != hdr .endianness
542
533
hdr .extensions = klass .exts_klass .from_fileobj (fileobj , extsize , byteswap )
543
534
return hdr
544
535
545
536
def write_to (self , fileobj ):
546
537
super (Nifti1Header , self ).write_to (fileobj )
538
+ n_exts = len (self .extensions )
539
+ if n_exts == 0 :
540
+ # If single file, write required 0 stream to signal no extensions
541
+ if self .is_single :
542
+ fileobj .write ('\x00 ' * 4 )
543
+ return
544
+ # Signal there are extensions that follow
545
+ fileobj .write ('\x01 \x00 \x00 \x00 ' )
547
546
byteswap = endian_codes ['native' ] != self .endianness
548
547
self .extensions .write_to (fileobj , byteswap )
549
548
550
- def _header_len (self ):
551
- ''' Return header length in bytes or -1 for unknown
552
-
553
- This will be -1 for headers that are their own files, as in the .hdr
554
- file of a nifti pair, or the same as the start of the data (vox_offset)
555
- in single file niftis
556
- '''
557
- return self ._header_data ['vox_offset' ]
558
-
559
549
def get_best_affine (self ):
560
550
''' Select best of available transforms '''
561
551
hdr = self ._header_data
@@ -569,8 +559,12 @@ def _empty_headerdata(self, endianness=None):
569
559
''' Create empty header binary block with given endianness '''
570
560
hdr_data = analyze .AnalyzeHeader ._empty_headerdata (self , endianness )
571
561
hdr_data ['scl_slope' ] = 1
572
- hdr_data ['magic' ] = 'n+1'
573
- hdr_data ['vox_offset' ] = 352
562
+ if self .is_single :
563
+ hdr_data ['magic' ] = 'n+1'
564
+ hdr_data ['vox_offset' ] = 352
565
+ else :
566
+ hdr_data ['magic' ] = 'ni1'
567
+ hdr_data ['vox_offset' ] = 0
574
568
return hdr_data
575
569
576
570
def get_qform_quaternion (self ):
@@ -1208,9 +1202,12 @@ def set_xyzt_units(self, xyz=None, t=None):
1208
1202
1209
1203
def _set_format_specifics (self ):
1210
1204
''' Utility routine to set format specific header stuff '''
1211
- self ._header_data ['magic' ] = 'n+1'
1212
- if self ._header_data ['vox_offset' ] < 352 :
1213
- self ._header_data ['vox_offset' ] = 352
1205
+ if self .is_single :
1206
+ self ._header_data ['magic' ] = 'n+1'
1207
+ if self ._header_data ['vox_offset' ] < 352 :
1208
+ self ._header_data ['vox_offset' ] = 352
1209
+ else :
1210
+ self ._header_data ['magic' ] = 'ni1'
1214
1211
1215
1212
''' Checks only below here '''
1216
1213
@@ -1325,26 +1322,9 @@ def _chk_xform_code(klass, code_type, hdr, fix):
1325
1322
1326
1323
class Nifti1PairHeader (Nifti1Header ):
1327
1324
''' Class for nifti1 pair header '''
1328
- def _empty_headerdata (self , endianness = None ):
1329
- ''' Create empty header binary block with given endianness '''
1330
- hdr_data = analyze .AnalyzeHeader ._empty_headerdata (self , endianness )
1331
- hdr_data ['scl_slope' ] = 1
1332
- hdr_data ['magic' ] = 'ni1'
1333
- hdr_data ['vox_offset' ] = 0
1334
- return hdr_data
1335
-
1336
- def _set_format_specifics (self ):
1337
- ''' Utility routine to set format specific header stuff '''
1338
- self ._header_data ['magic' ] = 'ni1'
1339
-
1340
- def _header_len (self ):
1341
- ''' Return header length in bytes or -1 for unknown
1325
+ # Signal whether this is single (header + data) file
1326
+ is_single = False
1342
1327
1343
- This will be -1 for headers that are their own files, as in the .hdr
1344
- file of a nifti pair, or the same as the start of the data (vox_offset)
1345
- in single file niftis
1346
- '''
1347
- return - 1
1348
1328
1349
1329
class Nifti1Pair (analyze .AnalyzeImage ):
1350
1330
header_class = Nifti1PairHeader
0 commit comments