~ubuntu-branches/ubuntu/wily/nibabel/wily-proposed

« back to all changes in this revision

Viewing changes to nibabel/tests/test_scaling.py

  • Committer: Package Import Robot
  • Author(s): Yaroslav Halchenko
  • Date: 2012-05-06 12:58:22 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20120506125822-3jiwjkmdqcxkrior
Tags: 1.2.0-1
* New upstream release: bugfixes, support for new formats
  (Freesurfer, ECAT, etc), nib-ls tool.
* debian/copyright
  - corrected reference to the format specs
  - points to github's repository as the origin of sources
  - removed lightunit license/copyright -- removed upstream
  - added netcdf module license/copyright terms
* debian/control
  - added python-fuse to Recommends and Build-Depends for dicomfs
  - boosted policy compliance to 3.9.3 (no further changes)
* debian/watch
  - adjusted to download numbered tag

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
 
4
#
 
5
#   See COPYING file distributed along with the NiBabel package for the
 
6
#   copyright and license terms.
 
7
#
 
8
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
 
9
''' Test for scaling / rounding in volumeutils module '''
 
10
from __future__ import with_statement
 
11
 
 
12
import sys
 
13
 
 
14
import numpy as np
 
15
 
 
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
 
20
 
 
21
from numpy.testing import (assert_array_almost_equal, assert_array_equal)
 
22
 
 
23
from nose.tools import (assert_true, assert_equal, assert_raises,
 
24
                        assert_not_equal)
 
25
 
 
26
 
 
27
# Debug print statements
 
28
DEBUG = True
 
29
 
 
30
def test_scale_min_max():
 
31
    mx_dt = np.maximum_sctype(np.float)
 
32
    for tp in np.sctypes['uint'] + np.sctypes['int']:
 
33
        info = np.iinfo(tp)
 
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)
 
37
        value_pairs = (
 
38
            (0, imax),
 
39
            (imin, 0),
 
40
            (imin, imax),
 
41
            (1, 10),
 
42
            (-1, -1),
 
43
            (1, 1),
 
44
            (-10, -1),
 
45
            (-100, 10))
 
46
        for mn, mx in value_pairs:
 
47
            # with intercept
 
48
            scale, inter = scale_min_max(mn, mx, tp, True)
 
49
            if mx-mn:
 
50
                assert_array_almost_equal, (mx-inter) / scale, imax
 
51
                assert_array_almost_equal, (mn-inter) / scale, imin
 
52
            else:
 
53
                assert_equal, (scale, inter), (1.0, mn)
 
54
            # without intercept
 
55
            if imin == 0 and mn < 0 and mx > 0:
 
56
                (assert_raises, ValueError,
 
57
                       scale_min_max, mn, mx, tp, False)
 
58
                continue
 
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
 
63
                continue
 
64
            sc_mn = mn / scale
 
65
            sc_mx = mx / scale
 
66
            assert_true, sc_mn >= imin
 
67
            assert_true, sc_mx <= imax
 
68
            if imin == 0:
 
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
 
73
                continue
 
74
            if abs(mx) >= abs(mn):
 
75
                assert_array_almost_equal, mx / scale, imax
 
76
            else:
 
77
                assert_array_almost_equal, mn / scale, imin
 
78
 
 
79
 
 
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))
 
90
    a = a + 1j
 
91
    assert_raises(TypeError, finite_range, a)
 
92
    # 1D case
 
93
    a = np.array([0., 1, 2, 3])
 
94
    assert_equal(finite_range(a), (0,3))
 
95
 
 
96
 
 
97
def test_calculate_scale():
 
98
    # Test for special cases in scale calculation
 
99
    npa = np.array
 
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))
 
115
 
 
116
 
 
117
def test_a2f_mn_mx():
 
118
    # Test array to file mn, mx handling
 
119
    str_io = BytesIO()
 
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)
 
127
        # Clip low
 
128
        array_to_file(arr, str_io, mn=2)
 
129
        data_back = array_from_file(arr.shape, out_type, str_io)
 
130
        # arr unchanged
 
131
        assert_array_equal(arr, arr_orig)
 
132
        # returned value clipped low
 
133
        assert_array_equal(data_back, [2,2,2,3,4,5])
 
134
        # Clip high
 
135
        array_to_file(arr, str_io, mx=4)
 
136
        data_back = array_from_file(arr.shape, out_type, str_io)
 
137
        # arr unchanged
 
138
        assert_array_equal(arr, arr_orig)
 
139
        # returned value clipped high
 
140
        assert_array_equal(data_back, [0,1,2,3,4,4])
 
141
        # Clip both
 
142
        array_to_file(arr, str_io, mn=2, mx=4)
 
143
        data_back = array_from_file(arr.shape, out_type, str_io)
 
144
        # arr unchanged
 
145
        assert_array_equal(arr, arr_orig)
 
146
        # returned value clipped high
 
147
        assert_array_equal(data_back, [2,2,2,3,4,4])
 
148
 
 
149
 
 
150
def test_a2f_nan2zero():
 
151
    # Test conditions under which nans written to zero
 
152
    arr = np.array([np.nan, 99.], dtype=np.float32)
 
153
    str_io = BytesIO()
 
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])
 
169
 
 
170
 
 
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.
 
174
    bio = BytesIO()
 
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']
 
185
        if not err is None:
 
186
            assert_raises(err, calculate_scale, arr, out_dtype, True)
 
187
            continue
 
188
        slope, inter, mn, mx = calculate_scale(arr, out_dtype, True)
 
189
        array_to_file(arr, bio, out_type, 0, inter, slope, mn, mx)
 
190
        bio.seek(0)
 
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))
 
196
        bio.truncate(0)
 
197
        bio.seek(0)
 
198
 
 
199
 
 
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'),
 
204
                                 ('uint', 'int'),
 
205
                                ):
 
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'),
 
211
                                 ('float', 'uint'),
 
212
                                 ('complex', 'int'),
 
213
                                 ('complex', 'uint'),
 
214
                                ):
 
215
        for in_type in np.sctypes[category0]:
 
216
            for out_type in np.sctypes[category1]:
 
217
                check_int_a2f(in_type, out_type)
 
218
 
 
219
 
 
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
 
231
    str_io = BytesIO()
 
232
    try:
 
233
        scale, inter, mn, mx = calculate_scale(data, out_type, True)
 
234
    except ValueError:
 
235
        if DEBUG:
 
236
            print in_type, out_type, sys.exc_info()[1]
 
237
        return
 
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:
 
246
        return
 
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))))