Skip to content

Commit ba18b33

Browse files
committed
Merge pull request #86 from matthew-brett/uncontroversial-snippets
Uncontroversial snippets Some docstring updates Adding a test for image array shapes too large to fit within header dimension dtypes (to avoid silent overflow) Adding tests to confirm safe behavior when array scaling intercept or slope causes overflow when testing for clip values (test only)
2 parents e8cbf05 + 283b648 commit ba18b33

File tree

4 files changed

+54
-0
lines changed

4 files changed

+54
-0
lines changed

nibabel/analyze.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,10 @@ def set_data_shape(self, shape):
635635
dims[:] = 1
636636
dims[0] = ndims
637637
dims[1:ndims+1] = shape
638+
# Check that dimensions fit
639+
if not np.all(dims[1:ndims+1] == shape):
640+
raise HeaderDataError('shape %s does not fit in dim datatype' %
641+
shape)
638642
self._structarr['pixdim'][ndims+1:] = 1.0
639643

640644
def get_base_affine(self):

nibabel/tests/test_analyze.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from ..nifti1 import Nifti1Header
2626
from ..loadsave import read_img_data
2727
from .. import imageglobals
28+
from ..casting import as_int
2829

2930
from numpy.testing import (assert_array_equal,
3031
assert_array_almost_equal)
@@ -217,6 +218,22 @@ def test_data_dtype(self):
217218
hdr.set_data_dtype,
218219
inp)
219220

221+
def test_shapes(self):
222+
# Test that shape checks work
223+
hdr = self.header_class()
224+
for shape in ((2, 3, 4), (2, 3, 4, 5), (2, 3), (2,)):
225+
hdr.set_data_shape(shape)
226+
assert_equal(hdr.get_data_shape(), shape)
227+
# Check max works, but max+1 raises error
228+
dim_dtype = hdr.structarr['dim'].dtype
229+
# as_int for safety to deal with numpy 1.4.1 int conversion errors
230+
mx = as_int(np.iinfo(dim_dtype).max)
231+
shape = (mx,)
232+
hdr.set_data_shape(shape)
233+
assert_equal(hdr.get_data_shape(), shape)
234+
shape = (mx+1,)
235+
assert_raises(HeaderDataError, hdr.set_data_shape, shape)
236+
220237
def test_read_write_data(self):
221238
# Check reading and writing of data
222239
hdr = self.header_class()

nibabel/tests/test_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,34 @@ def test_array_to_file():
160160
assert_array_equal(data_back, np.zeros(arr.shape))
161161

162162

163+
def test_a2f_big_scalers():
164+
# Check that clip works even for overflowing scalers / data
165+
info = np.finfo(np.float32)
166+
arr = np.array([info.min, np.nan, info.max], dtype=np.float32)
167+
str_io = BytesIO()
168+
# Intercept causes overflow - does routine scale correctly?
169+
array_to_file(arr, str_io, np.int8, intercept=np.float32(2**120))
170+
data_back = array_from_file(arr.shape, np.int8, str_io)
171+
assert_array_equal(data_back, [-128, 0, 127])
172+
# Scales also if mx, mn specified?
173+
str_io.seek(0)
174+
array_to_file(arr, str_io, np.int8, mn=info.min, mx=info.max,
175+
intercept=np.float32(2**120))
176+
data_back = array_from_file(arr.shape, np.int8, str_io)
177+
assert_array_equal(data_back, [-128, 0, 127])
178+
# And if slope causes overflow?
179+
str_io.seek(0)
180+
array_to_file(arr, str_io, np.int8, divslope=np.float32(0.5))
181+
data_back = array_from_file(arr.shape, np.int8, str_io)
182+
assert_array_equal(data_back, [-128, 0, 127])
183+
# with mn, mx specified?
184+
str_io.seek(0)
185+
array_to_file(arr, str_io, np.int8, mn=info.min, mx=info.max,
186+
divslope=np.float32(0.5))
187+
data_back = array_from_file(arr.shape, np.int8, str_io)
188+
assert_array_equal(data_back, [-128, 0, 127])
189+
190+
163191
def write_return(data, fileobj, out_dtype, *args, **kwargs):
164192
fileobj.truncate(0)
165193
array_to_file(data, fileobj, out_dtype, *args, **kwargs)

nibabel/volumeutils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,8 @@ def scale_min_max(mn, mx, out_type, allow_intercept):
696696
intercept : numpy scalar, dtype=np.maximum_sctype(np.float)
697697
value to subtract from data before dividing by scalefactor
698698
699+
Examples
700+
--------
699701
>>> scale_min_max(0, 255, np.uint8, False)
700702
(1.0, 0.0)
701703
>>> scale_min_max(-128, 127, np.int8, False)
@@ -720,6 +722,9 @@ def scale_min_max(mn, mx, out_type, allow_intercept):
720722
721723
Notes
722724
-----
725+
We don't use this function anywhere in nibabel now, it's here for API
726+
compatibility only.
727+
723728
The large integers lead to python long types as max / min for type.
724729
To contain the rounding error, we need to use the maximum numpy
725730
float types when casting to float.

0 commit comments

Comments
 (0)