Skip to content

Commit fcc5448

Browse files
authored
Merge pull request #847 from effigies/fix/slope_inter_guarantees
TEST: Check non-integral slopes, intercepts in ArrayProxy API
2 parents 7527247 + b1f6e2c commit fcc5448

File tree

4 files changed

+25
-14
lines changed

4 files changed

+25
-14
lines changed

.azure-pipelines/windows.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ jobs:
2929
displayName: 'Update build tools'
3030
- script: |
3131
python -m pip install --find-links %EXTRA_WHEELS% %DEPENDS%
32-
python -m pip install nose mock coverage codecov
33-
python -m pip install pytest
32+
python -m pip install nose mock coverage codecov pytest
3433
displayName: 'Install dependencies'
3534
- script: |
3635
python -m pip install .
@@ -41,7 +40,6 @@ jobs:
4140
cd for_testing
4241
cp ../.coveragerc .
4342
nosetests --with-doctest --with-coverage --cover-package nibabel nibabel
44-
pytest -v ../nibabel/tests/test_affines.py ../nibabel/tests/test_volumeutils.py
4543
displayName: 'Nose tests'
4644
- script: |
4745
cd for_testing

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ script:
130130
cd for_testing
131131
cp ../.coveragerc .
132132
nosetests --with-doctest --with-coverage --cover-package nibabel nibabel
133-
pytest -v ../nibabel/tests/test_affines.py ../nibabel/tests/test_volumeutils.py
134133
else
135134
false
136135
fi

nibabel/arrayproxy.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,13 @@ def _get_scaled(self, dtype, slicer):
360360
scl_slope = np.asanyarray(self._slope)
361361
scl_inter = np.asanyarray(self._inter)
362362
use_dtype = scl_slope.dtype if dtype is None else dtype
363-
slope = scl_slope.astype(use_dtype)
364-
inter = scl_inter.astype(use_dtype)
363+
364+
if np.can_cast(scl_slope, use_dtype):
365+
scl_slope = scl_slope.astype(use_dtype)
366+
if np.can_cast(scl_inter, use_dtype):
367+
scl_inter = scl_inter.astype(use_dtype)
365368
# Read array and upcast as necessary for big slopes, intercepts
366-
scaled = apply_read_scaling(self._get_unscaled(slicer=slicer), slope, inter)
369+
scaled = apply_read_scaling(self._get_unscaled(slicer=slicer), scl_slope, scl_inter)
367370
if dtype is not None:
368371
scaled = scaled.astype(np.promote_types(scaled.dtype, dtype), copy=False)
369372
return scaled

nibabel/tests/test_proxy_api.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@
4747
from .._h5py_compat import h5py, have_h5py
4848
from .. import ecat
4949
from .. import parrec
50+
from ..casting import have_binary128
5051

5152
from ..arrayproxy import ArrayProxy, is_proxy
5253

5354
from nose import SkipTest
5455
from nose.tools import (assert_true, assert_false, assert_raises,
5556
assert_equal, assert_not_equal, assert_greater_equal)
5657

57-
from numpy.testing import (assert_almost_equal, assert_array_equal)
58+
from numpy.testing import assert_almost_equal, assert_array_equal, assert_allclose
5859

5960
from ..testing import data_path as DATA_PATH, assert_dt_equal
6061

@@ -142,7 +143,10 @@ def validate_get_scaled(self, pmaker, params):
142143

143144
for dtype in np.sctypes['float'] + np.sctypes['int'] + np.sctypes['uint']:
144145
out = prox.get_scaled(dtype=dtype)
145-
assert_almost_equal(out, params['arr_out'])
146+
# Half-precision is imprecise. Obviously. It's a bad idea, but don't break
147+
# the test over it.
148+
rtol = 1e-03 if dtype == np.float16 else 1e-05
149+
assert_allclose(out, params['arr_out'].astype(out.dtype), rtol=rtol, atol=1e-08)
146150
assert_greater_equal(out.dtype, np.dtype(dtype))
147151
# Shape matches expected shape
148152
assert_equal(out.shape, params['shape'])
@@ -192,6 +196,7 @@ class TestAnalyzeProxyAPI(_TestProxyAPI):
192196
shapes = ((2,), (2, 3), (2, 3, 4), (2, 3, 4, 5))
193197
has_slope = False
194198
has_inter = False
199+
data_dtypes = (np.uint8, np.int16, np.int32, np.float32, np.complex64, np.float64)
195200
array_order = 'F'
196201
# Cannot set offset for Freesurfer
197202
settable_offset = True
@@ -216,11 +221,12 @@ def obj_params(self):
216221
offsets = (self.header_class().get_data_offset(),)
217222
else:
218223
offsets = (0, 16)
219-
slopes = (1., 2.) if self.has_slope else (1.,)
220-
inters = (0., 10.) if self.has_inter else (0.,)
221-
dtypes = (np.uint8, np.int16, np.float32)
224+
# For non-integral parameters, cast to float32 value can be losslessly cast
225+
# later, enabling exact checks, then back to float for consistency
226+
slopes = (1., 2., float(np.float32(3.1416))) if self.has_slope else (1.,)
227+
inters = (0., 10., float(np.float32(2.7183))) if self.has_inter else (0.,)
222228
for shape, dtype, offset, slope, inter in product(self.shapes,
223-
dtypes,
229+
self.data_dtypes,
224230
offsets,
225231
slopes,
226232
inters):
@@ -262,7 +268,7 @@ def sio_func():
262268
dtype=dtype,
263269
dtype_out=dtype_out,
264270
arr=arr.copy(),
265-
arr_out=arr * slope + inter,
271+
arr_out=arr.astype(dtype_out) * slope + inter,
266272
shape=shape,
267273
offset=offset,
268274
slope=slope,
@@ -325,6 +331,10 @@ class TestSpm2AnalyzeProxyAPI(TestSpm99AnalyzeProxyAPI):
325331
class TestNifti1ProxyAPI(TestSpm99AnalyzeProxyAPI):
326332
header_class = Nifti1Header
327333
has_inter = True
334+
data_dtypes = (np.uint8, np.int16, np.int32, np.float32, np.complex64, np.float64,
335+
np.int8, np.uint16, np.uint32, np.int64, np.uint64, np.complex128)
336+
if have_binary128():
337+
data_dtypes.extend(np.float128, np.complex256)
328338

329339

330340
class TestMGHAPI(TestAnalyzeProxyAPI):
@@ -334,6 +344,7 @@ class TestMGHAPI(TestAnalyzeProxyAPI):
334344
has_inter = False
335345
settable_offset = False
336346
data_endian = '>'
347+
data_dtypes = (np.uint8, np.int16, np.int32, np.float32)
337348

338349

339350
class TestMinc1API(_TestProxyAPI):

0 commit comments

Comments
 (0)