Skip to content

Commit 0c06464

Browse files
matthew-bretteffigies
authored andcommitted
RF: refactor using valid_exts, _meta_sniff_len
This is a refactor, trying to separate the idea of valid extensions for loading images, from compressed extra suffixes.
1 parent 01af20f commit 0c06464

File tree

10 files changed

+45
-44
lines changed

10 files changed

+45
-44
lines changed

nibabel/analyze.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,9 @@ class AnalyzeImage(SpatialImage):
908908
""" Class for basic Analyze format image
909909
"""
910910
header_class = AnalyzeHeader
911+
_meta_sniff_len = header_class.sizeof_hdr
911912
files_types = (('image', '.img'), ('header', '.hdr'))
913+
valid_exts = ('.img', '.hdr')
912914
_compressed_exts = ('.gz', '.bz2')
913915

914916
makeable = True

nibabel/ecat.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,7 @@ class EcatImage(SpatialImage):
732732
"""
733733
_header = EcatHeader
734734
header_class = _header
735+
valid_exts = ('.v',)
735736
_subheader = EcatSubHeader
736737
files_types = (('image', '.v'), ('header', '.v'))
737738

nibabel/freesurfer/mghformat.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,13 +454,17 @@ def writeftr_to(self, fileobj):
454454
fileobj.write(ftr_nd.tostring())
455455

456456

457-
@ImageOpener.register_ext_from_image('.mgz', ImageOpener.gz_def)
457+
# Register .mgz extension as compressed
458+
ImageOpener.compress_ext_map['.mgz'] = ImageOpener.gz_def
459+
460+
458461
class MGHImage(SpatialImage):
459462
""" Class for MGH format image
460463
"""
461464
header_class = MGHHeader
465+
valid_exts = ('.mgh', '.mgz')
462466
files_types = (('image', '.mgh'),)
463-
_compressed_exts = (('.gz',))
467+
_compressed_exts = ()
464468

465469
makeable = True
466470
rw = True

nibabel/freesurfer/tests/test_mghformat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def test_filename_exts():
145145
# and the default affine matrix (Note the "None")
146146
img = MGHImage(v, None)
147147
# Check if these extensions allow round trip
148-
for ext in ('.mgh', '.mgz', '.mgh.gz'):
148+
for ext in ('.mgh', '.mgz'):
149149
with InTemporaryDirectory():
150150
fname = 'tmpname' + ext
151151
save(img, fname)

nibabel/loadsave.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def save(img, filename):
110110
klass = Nifti2Image
111111
else: # arbitrary conversion
112112
valid_klasses = [klass for klass in all_image_classes
113-
if klass.is_valid_extension(ext)]
113+
if ext in klass.valid_exts]
114114
if not valid_klasses: # if list is empty
115115
raise ImageFileError('Cannot work out file type of "%s"' %
116116
filename)

nibabel/minc1.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ class Minc1Image(SpatialImage):
298298
load.
299299
'''
300300
header_class = Minc1Header
301+
_meta_sniff_len = 4
302+
valid_exts = ('.mnc',)
301303
files_types = (('image', '.mnc'),)
302304
_compressed_exts = ('.gz', '.bz2')
303305

nibabel/nifti1.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,7 @@ class Nifti1Pair(analyze.AnalyzeImage):
16321632
""" Class for NIfTI1 format image, header pair
16331633
"""
16341634
header_class = Nifti1PairHeader
1635+
_meta_sniff_len = header_class.sizeof_hdr
16351636
rw = True
16361637

16371638
def __init__(self, dataobj, affine, header=None,
@@ -1856,6 +1857,7 @@ class Nifti1Image(Nifti1Pair):
18561857
""" Class for single file NIfTI1 format image
18571858
"""
18581859
header_class = Nifti1Header
1860+
valid_exts = ('.nii',)
18591861
files_types = (('image', '.nii'),)
18601862

18611863
@staticmethod

nibabel/nifti2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,12 +243,14 @@ class Nifti2Pair(Nifti1Pair):
243243
""" Class for NIfTI2 format image, header pair
244244
"""
245245
header_class = Nifti2PairHeader
246+
_meta_sniff_len = header_class.sizeof_hdr
246247

247248

248249
class Nifti2Image(Nifti1Image):
249250
""" Class for single file NIfTI2 format image
250251
"""
251252
header_class = Nifti2Header
253+
_meta_sniff_len = header_class.sizeof_hdr
252254

253255

254256
def load(filename):

nibabel/parrec.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,7 @@ def get_sorted_slice_indices(self):
10201020
class PARRECImage(SpatialImage):
10211021
"""PAR/REC image"""
10221022
header_class = PARRECHeader
1023+
valid_exts = ('.rec', '.par')
10231024
files_types = (('image', '.rec'), ('header', '.par'))
10241025

10251026
makeable = False

nibabel/spatialimages.py

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,9 @@ class ImageFileError(Exception):
324324
class SpatialImage(object):
325325
''' Template class for images '''
326326
header_class = Header
327+
_meta_sniff_len = 0
327328
files_types = (('image', None),)
328-
alternate_exts = () # Modified by @ImageOpener.register_ext_from_image
329+
valid_exts = ()
329330
_compressed_exts = ()
330331

331332
makeable = True # Used in test code
@@ -874,51 +875,37 @@ def from_image(klass, img):
874875
extra=img.extra.copy())
875876

876877
@classmethod
877-
def is_valid_extension(klass, ext):
878-
valid = tuple(ft[1] for ft in klass.files_types) + klass.alternate_exts
879-
return ext.lower() in valid
878+
def _sniff_meta_for(klass, filename, sniff_nbytes):
879+
froot, ext, trailing = splitext_addext(filename,
880+
klass._compressed_exts)
881+
# Determine the metadata location, then sniff it
882+
t_fnames = types_filenames(filename,
883+
klass.files_types,
884+
trailing_suffixes=klass._compressed_exts)
885+
meta_fname = t_fnames.get('header', filename)
886+
try:
887+
with ImageOpener(meta_fname, 'rb') as fobj:
888+
sniff = fobj.read(sniff_nbytes)
889+
except IOError:
890+
return None
891+
return sniff
880892

881893
@classmethod
882-
def path_maybe_image(klass, filename, sniff=None):
894+
def path_maybe_image(klass, filename, sniff=None, sniff_max=1024):
883895
froot, ext, trailing = splitext_addext(filename,
884896
klass._compressed_exts)
885-
886-
if not klass.is_valid_extension(ext):
897+
if ext.lower() not in klass.valid_exts:
887898
return False, sniff
888-
elif not hasattr(klass.header_class, 'may_contain_header'):
899+
if not hasattr(klass.header_class, 'may_contain_header'):
889900
return True, sniff
890-
891-
# Determine the metadata location, then sniff it
892-
header_exts = [ft[1] for ft in klass.files_types if ft[0] == 'header']
893-
if len(header_exts) == 0:
894-
metadata_filename = filename
895-
else:
896-
# Search for an acceptable existing header;
897-
# could be compressed or not...
898-
for ext in header_exts:
899-
for tr_ext in np.unique([trailing, ''] +
900-
list(klass._compressed_exts)):
901-
metadata_filename = froot + ext + tr_ext
902-
if os.path.exists(metadata_filename):
903-
break
904-
905-
try:
906-
klass_sizeof_hdr = getattr(klass.header_class, 'sizeof_hdr', 0)
907-
908-
if not sniff or len(sniff) < klass_sizeof_hdr:
909-
# 1024 bytes is currently larger than all headers
910-
sizeof_hdr = np.max([1024, klass_sizeof_hdr])
911-
with ImageOpener(metadata_filename, 'rb') as fobj:
912-
sniff = fobj.read(sizeof_hdr)
913-
914-
may_contain_header = klass.header_class.may_contain_header(sniff)
915-
except Exception:
916-
# Can happen if: file doesn't exist,
917-
# filesize < necessary sniff size (this happens!)
918-
# other unexpected errors.
919-
may_contain_header = False
920-
921-
return may_contain_header, sniff
901+
if sniff is None or len(sniff) < klass._meta_sniff_len:
902+
sniff_nbytes = max(klass._meta_sniff_len, sniff_max)
903+
sniff = klass._sniff_meta_for(filename, sniff_nbytes)
904+
if sniff is None: # Can't sniff, won't sniff
905+
return False, None
906+
if len(sniff) < klass._meta_sniff_len:
907+
return False, sniff
908+
return klass.header_class.may_contain_header(sniff), sniff
922909

923910
def __getitem__(self):
924911
''' No slicing or dictionary interface for images

0 commit comments

Comments
 (0)