diff --git a/nibabel/externals/netcdf.py b/nibabel/externals/netcdf.py index 121c4b6e7a..06375f8bdd 100644 --- a/nibabel/externals/netcdf.py +++ b/nibabel/externals/netcdf.py @@ -210,6 +210,14 @@ def __init__(self, filename, mode='r', mmap=None, version=1): self.fp = open(self.filename, '%sb' % mode) if mmap is None: mmap = True + try: + self.fp.seek(0, 2) + except ValueError: + self.file_bytes = -1 # Unknown file length (gzip). + else: + self.file_bytes = self.fp.tell() + self.fp.seek(0) + self.use_mmap = mmap self.version_byte = version @@ -599,14 +607,22 @@ def _read_var_array(self): else: # not a record variable # Calculate size to avoid problems with vsize (above) a_size = reduce(mul, shape, 1) * size - if self.use_mmap: + if self.file_bytes >= 0 and begin_ + a_size > self.file_bytes: + data = fromstring(b'\x00'*a_size, dtype=dtype_) + elif self.use_mmap: mm = mmap(self.fp.fileno(), begin_+a_size, access=ACCESS_READ) data = ndarray.__new__(ndarray, shape, dtype=dtype_, buffer=mm, offset=begin_, order=0) else: pos = self.fp.tell() self.fp.seek(begin_) - data = fromstring(self.fp.read(a_size), dtype=dtype_) + # Try to read file, which may fail because the data is + # at or past the end of file. In that case, we treat + # this data as zeros. + buf = self.fp.read(a_size) + if len(buf) < a_size: + buf = b'\x00'*a_size + data = fromstring(buf, dtype=dtype_) data.shape = shape self.fp.seek(pos) diff --git a/nibabel/minc1.py b/nibabel/minc1.py index 25beb7c994..5eb077ada0 100644 --- a/nibabel/minc1.py +++ b/nibabel/minc1.py @@ -95,8 +95,8 @@ def get_data_shape(self): def get_zooms(self): """ Get real-world sizes of voxels """ # zooms must be positive; but steps in MINC can be negative - return tuple( - [abs(float(dim.step)) for dim in self._dims]) + return tuple([abs(float(dim.step)) if hasattr(dim, 'step') else 1.0 + for dim in self._dims]) def get_affine(self): nspatial = len(self._spatial_dims) @@ -106,13 +106,11 @@ def get_affine(self): dim_names = list(self._dim_names) # for indexing in loop for i, name in enumerate(self._spatial_dims): dim = self._dims[dim_names.index(name)] - try: - dir_cos = dim.direction_cosines - except AttributeError: - dir_cos = _default_dir_cos[name] - rot_mat[:, i] = dir_cos - steps[i] = dim.step - starts[i] = dim.start + rot_mat[:, i] = (dim.direction_cosines + if hasattr(dim, 'direction_cosines') + else _default_dir_cos[name]) + steps[i] = dim.step if hasattr(dim, 'step') else 1.0 + starts[i] = dim.start if hasattr(dim, 'start') else 0.0 origin = np.dot(rot_mat, starts) aff = np.eye(nspatial + 1) aff[:nspatial, :nspatial] = rot_mat * steps diff --git a/nibabel/minc2.py b/nibabel/minc2.py index 2782f15146..f4ee3eab6b 100644 --- a/nibabel/minc2.py +++ b/nibabel/minc2.py @@ -77,7 +77,10 @@ def _get_dimensions(self, var): dimorder = var.attrs['dimorder'].decode() except KeyError: # No specified dimensions return [] - return dimorder.split(',') + # The dimension name list must contain only as many entries + # as the variable has dimensions. This reduces errors when an + # unnecessary dimorder attribute is left behind. + return dimorder.split(',')[:len(var.shape)] def get_data_dtype(self): return self._image.dtype @@ -95,7 +98,7 @@ def _get_valid_range(self): info = np.iinfo(ddt.type) try: valid_range = self._image.attrs['valid_range'] - except AttributeError: + except (AttributeError, KeyError): valid_range = [info.min, info.max] else: if valid_range[0] < info.min or valid_range[1] > info.max: diff --git a/nibabel/tests/data/minc1-no-att.mnc b/nibabel/tests/data/minc1-no-att.mnc new file mode 100644 index 0000000000..1fcd595f7e Binary files /dev/null and b/nibabel/tests/data/minc1-no-att.mnc differ diff --git a/nibabel/tests/data/minc2-4d-d.mnc b/nibabel/tests/data/minc2-4d-d.mnc new file mode 100644 index 0000000000..0000fa49e2 Binary files /dev/null and b/nibabel/tests/data/minc2-4d-d.mnc differ diff --git a/nibabel/tests/data/minc2-no-att.mnc b/nibabel/tests/data/minc2-no-att.mnc new file mode 100644 index 0000000000..15052c271d Binary files /dev/null and b/nibabel/tests/data/minc2-no-att.mnc differ diff --git a/nibabel/tests/test_minc1.py b/nibabel/tests/test_minc1.py index 0bbcd4b510..cb59d921eb 100644 --- a/nibabel/tests/test_minc1.py +++ b/nibabel/tests/test_minc1.py @@ -83,6 +83,21 @@ max=1.498039216, mean=0.9090422837), is_proxy=True), + dict( + fname=pjoin(data_path, 'minc1-no-att.mnc'), + shape=(10, 20, 20), + dtype=np.uint8, + affine=np.array([[0, 0, 1.0, 0], + [0, 1.0, 0, 0], + [1.0, 0, 0, 0], + [0, 0, 0, 1]]), + zooms=(1., 1., 1.), + # These values from SPM2/mincstats + data_summary=dict( + min=0.20784314, + max=0.74901961, + mean=0.6061103), + is_proxy=True), ] diff --git a/nibabel/tests/test_minc2.py b/nibabel/tests/test_minc2.py index c102a0a581..c4cb9341ca 100644 --- a/nibabel/tests/test_minc2.py +++ b/nibabel/tests/test_minc2.py @@ -72,7 +72,37 @@ min=0.2078431373, max=1.498039216, mean=0.9090422837), - is_proxy=True) + is_proxy=True), + dict( + fname=pjoin(data_path, 'minc2-no-att.mnc'), + shape=(10, 20, 20), + dtype=np.uint8, + affine=np.array([[0, 0, 1.0, 0], + [0, 1.0, 0, 0], + [1.0, 0, 0, 0], + [0, 0, 0, 1]]), + zooms=(1., 1., 1.), + # These values from SPM2/mincstats + data_summary=dict( + min=0.20784314, + max=0.74901961, + mean=0.6061103), + is_proxy=True), + dict( + fname=pjoin(data_path, 'minc2-4d-d.mnc'), + shape=(5, 16, 16, 16), + dtype=np.float64, + affine=np.array([[1., 0., 0., -6.96 ], + [0., 1., 0., -12.453], + [0., 0., 1., -9.48 ], + [0., 0., 0., 1.]]), + zooms=(1., 1., 1., 1.), + # These values from mincstats + data_summary=dict( + min=0.0, + max=5.0, + mean=2.00078125), + is_proxy=True), ] if have_h5py: