1
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2
# vi: set ft=python sts=4 ts=4 sw=4 et:
3
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
5
# See COPYING file distributed along with the NiBabel package for the
6
# copyright and license terms.
8
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9
''' Test for scaling / rounding in volumeutils module '''
10
from __future__ import with_statement
16
from ..py3k import BytesIO
17
from ..volumeutils import (calculate_scale, scale_min_max, finite_range,
18
apply_read_scaling, array_to_file, array_from_file)
19
from ..casting import type_info
21
from numpy.testing import (assert_array_almost_equal, assert_array_equal)
23
from nose.tools import (assert_true, assert_equal, assert_raises,
27
# Debug print statements
30
def test_scale_min_max():
31
mx_dt = np.maximum_sctype(np.float)
32
for tp in np.sctypes['uint'] + np.sctypes['int']:
34
# Need to pump up to max fp type to contain python longs
35
imin = np.array(info.min, dtype=mx_dt)
36
imax = np.array(info.max, dtype=mx_dt)
46
for mn, mx in value_pairs:
48
scale, inter = scale_min_max(mn, mx, tp, True)
50
assert_array_almost_equal, (mx-inter) / scale, imax
51
assert_array_almost_equal, (mn-inter) / scale, imin
53
assert_equal, (scale, inter), (1.0, mn)
55
if imin == 0 and mn < 0 and mx > 0:
56
(assert_raises, ValueError,
57
scale_min_max, mn, mx, tp, False)
59
scale, inter = scale_min_max(mn, mx, tp, False)
60
assert_equal, inter, 0.0
61
if mn == 0 and mx == 0:
62
assert_equal, scale, 1.0
66
assert_true, sc_mn >= imin
67
assert_true, sc_mx <= imax
69
if mx > 0: # numbers all +ve
70
assert_array_almost_equal, mx / scale, imax
71
else: # numbers all -ve
72
assert_array_almost_equal, mn / scale, imax
74
if abs(mx) >= abs(mn):
75
assert_array_almost_equal, mx / scale, imax
77
assert_array_almost_equal, mn / scale, imin
80
def test_finite_range():
81
# Finite range utility function
82
a = np.array([[-1, 0, 1],[np.inf, np.nan, -np.inf]])
83
assert_equal(finite_range(a), (-1.0, 1.0))
84
a = np.array([[np.nan],[np.nan]])
85
assert_equal(finite_range(a), (np.inf, -np.inf))
86
a = np.array([[-3, 0, 1],[2,-1,4]], dtype=np.int)
87
assert_equal(finite_range(a), (-3, 4))
88
a = np.array([[1, 0, 1],[2,3,4]], dtype=np.uint)
89
assert_equal(finite_range(a), (0, 4))
91
assert_raises(TypeError, finite_range, a)
93
a = np.array([0., 1, 2, 3])
94
assert_equal(finite_range(a), (0,3))
97
def test_calculate_scale():
98
# Test for special cases in scale calculation
100
# Here the offset handles it
101
res = calculate_scale(npa([-2, -1], dtype=np.int8), np.uint8, True)
102
assert_equal(res, (1.0, -2.0, None, None))
103
# Not having offset not a problem obviously
104
res = calculate_scale(npa([-2, -1], dtype=np.int8), np.uint8, 0)
105
assert_equal(res, (-1.0, 0.0, None, None))
106
# Case where offset handles scaling
107
res = calculate_scale(npa([-1, 1], dtype=np.int8), np.uint8, 1)
108
assert_equal(res, (1.0, -1.0, None, None))
109
# Can't work for no offset case
110
assert_raises(ValueError,
111
calculate_scale, npa([-1, 1], dtype=np.int8), np.uint8, 0)
112
# Offset trick can't work when max is out of range
113
res = calculate_scale(npa([-1, 255], dtype=np.int16), np.uint8, 1)
114
assert_not_equal(res, (1.0, -1.0, None, None))
117
def test_a2f_mn_mx():
118
# Test array to file mn, mx handling
120
for out_type in (np.int16, np.float32):
121
arr = np.arange(6, dtype=out_type)
122
arr_orig = arr.copy() # safe backup for testing against
123
# Basic round trip to warm up
124
array_to_file(arr, str_io)
125
data_back = array_from_file(arr.shape, out_type, str_io)
126
assert_array_equal(arr, data_back)
128
array_to_file(arr, str_io, mn=2)
129
data_back = array_from_file(arr.shape, out_type, str_io)
131
assert_array_equal(arr, arr_orig)
132
# returned value clipped low
133
assert_array_equal(data_back, [2,2,2,3,4,5])
135
array_to_file(arr, str_io, mx=4)
136
data_back = array_from_file(arr.shape, out_type, str_io)
138
assert_array_equal(arr, arr_orig)
139
# returned value clipped high
140
assert_array_equal(data_back, [0,1,2,3,4,4])
142
array_to_file(arr, str_io, mn=2, mx=4)
143
data_back = array_from_file(arr.shape, out_type, str_io)
145
assert_array_equal(arr, arr_orig)
146
# returned value clipped high
147
assert_array_equal(data_back, [2,2,2,3,4,4])
150
def test_a2f_nan2zero():
151
# Test conditions under which nans written to zero
152
arr = np.array([np.nan, 99.], dtype=np.float32)
154
array_to_file(arr, str_io)
155
data_back = array_from_file(arr.shape, np.float32, str_io)
156
assert_array_equal(np.isnan(data_back), [True, False])
157
# nan2zero ignored for floats
158
array_to_file(arr, str_io, nan2zero=True)
159
data_back = array_from_file(arr.shape, np.float32, str_io)
160
assert_array_equal(np.isnan(data_back), [True, False])
161
# Integer output with nan2zero gives zero
162
array_to_file(arr, str_io, np.int32, nan2zero=True)
163
data_back = array_from_file(arr.shape, np.int32, str_io)
164
assert_array_equal(data_back, [0, 99])
165
# Integer output with nan2zero=False gives whatever astype gives
166
array_to_file(arr, str_io, np.int32, nan2zero=False)
167
data_back = array_from_file(arr.shape, np.int32, str_io)
168
assert_array_equal(data_back, [np.array(np.nan).astype(np.int32), 99])
171
def test_array_file_scales():
172
# Test scaling works for max, min when going from larger to smaller type,
173
# and from float to integer.
175
for in_type, out_type, err in ((np.int16, np.int16, None),
176
(np.int16, np.int8, None),
177
(np.uint16, np.uint8, None),
178
(np.int32, np.int8, None),
179
(np.float32, np.uint8, None),
180
(np.float32, np.int16, None)):
181
out_dtype = np.dtype(out_type)
182
arr = np.zeros((3,), dtype=in_type)
183
info = type_info(in_type)
184
arr[0], arr[1] = info['min'], info['max']
186
assert_raises(err, calculate_scale, arr, out_dtype, True)
188
slope, inter, mn, mx = calculate_scale(arr, out_dtype, True)
189
array_to_file(arr, bio, out_type, 0, inter, slope, mn, mx)
191
arr2 = array_from_file(arr.shape, out_dtype, bio)
192
arr3 = apply_read_scaling(arr2, slope, inter)
193
# Max rounding error for integer type
194
max_miss = slope / 2.
195
assert_true(np.all(np.abs(arr - arr3) <= max_miss))
200
def test_scaling_in_abstract():
201
# Confirm that, for all ints and uints as input, and all possible outputs,
202
# for any simple way of doing the calculation, the result is near enough
203
for category0, category1 in (('int', 'int'),
206
for in_type in np.sctypes[category0]:
207
for out_type in np.sctypes[category1]:
208
check_int_a2f(in_type, out_type)
209
# Converting floats to integer
210
for category0, category1 in (('float', 'int'),
215
for in_type in np.sctypes[category0]:
216
for out_type in np.sctypes[category1]:
217
check_int_a2f(in_type, out_type)
220
def check_int_a2f(in_type, out_type):
221
# Check that array to / from file returns roughly the same as input
222
big_floater = np.maximum_sctype(np.float)
223
info = type_info(in_type)
224
this_min, this_max = info['min'], info['max']
225
if not in_type in np.sctypes['complex']:
226
data = np.array([this_min, this_max], in_type)
227
else: # Funny behavior with complex256
228
data = np.zeros((2,), in_type)
229
data[0] = this_min + 0j
230
data[1] = this_max + 0j
233
scale, inter, mn, mx = calculate_scale(data, out_type, True)
236
print in_type, out_type, sys.exc_info()[1]
238
array_to_file(data, str_io, out_type, 0, inter, scale, mn, mx)
239
data_back = array_from_file(data.shape, out_type, str_io)
240
data_back = apply_read_scaling(data_back, scale, inter)
241
assert_true(np.allclose(big_floater(data), big_floater(data_back)))
242
# Try with analyze-size scale and inter
243
scale32 = np.float32(scale)
244
inter32 = np.float32(inter)
245
if scale32 == np.inf or inter32 == np.inf:
247
data_back = array_from_file(data.shape, out_type, str_io)
248
data_back = apply_read_scaling(data_back, scale32, inter32)
249
# Clip at extremes to remove inf
250
info = type_info(in_type)
251
out_min, out_max = info['min'], info['max']
252
assert_true(np.allclose(big_floater(data),
253
big_floater(np.clip(data_back, out_min, out_max))))