test_image.py 46.7 KB
Newer Older
1
2
#!/usr/bin/env python
#
3
# test_image.py - Unit tests for the fsl.data.image module.
4
5
6
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
7
"""Unit tests for the fsl.data.image module. """
8

9

Paul McCarthy's avatar
Paul McCarthy committed
10
import              os
11
import              json
12
13
import os.path   as op
import itertools as it
14

15
import pytest
16

Paul McCarthy's avatar
Paul McCarthy committed
17
18
19
import numpy        as np
import numpy.linalg as npla
import nibabel      as nib
20

Paul McCarthy's avatar
Paul McCarthy committed
21
from nibabel.spatialimages import ImageFileError
Paul McCarthy's avatar
Paul McCarthy committed
22

Paul McCarthy's avatar
Paul McCarthy committed
23
24
25
26
import fsl.data.constants   as constants
import fsl.data.image       as fslimage
import fsl.utils.path       as fslpath
import fsl.transform.affine as affine
27

28
29
from fsl.utils.tempdir import tempdir

30
31
32
from . import make_random_image
from . import make_dummy_file

33
34
35
36
37
38
39
40
41
try:
    from unittest import mock
except ImportError:
    import mock


try:
    import indexed_gzip as igzip
except ImportError:
42
43
44
45

    class MockError(Exception):
        pass

46
    igzip           = mock.MagicMock()
47
    igzip.ZranError = MockError
48

49

Paul McCarthy's avatar
Paul McCarthy committed
50
def make_image(filename=None,
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
               imgtype=1,
               dims=(10, 10, 10),
               pixdims=(1, 1, 1),
               dtype=np.float32):
    """Convenience function which makes an image containing random data.
    Saves and returns the nibabel object.

    imgtype == 0: ANALYZE
    imgtype == 1: NIFTI1
    imgtype == 2: NIFTI2
    """

    if   imgtype == 0: hdr = nib.AnalyzeHeader()
    elif imgtype == 1: hdr = nib.Nifti1Header()
    elif imgtype == 2: hdr = nib.Nifti2Header()

    pixdims = pixdims[:len(dims)]

    hdr.set_data_dtype(dtype)
    hdr.set_data_shape(dims)
    hdr.set_zooms([abs(p) for p in pixdims])

    xform = np.eye(4)
Paul McCarthy's avatar
Paul McCarthy committed
74
    for i, p in enumerate(pixdims[:3]):
75
        xform[i, i] = p
76

77
78
79
80
81
82
    data  = np.array(np.random.random(dims) * 100, dtype=dtype)

    if   imgtype == 0: img = nib.AnalyzeImage(data, xform, hdr)
    elif imgtype == 1: img = nib.Nifti1Image( data, xform, hdr)
    elif imgtype == 2: img = nib.Nifti2Image( data, xform, hdr)

Paul McCarthy's avatar
Paul McCarthy committed
83
84
85
86
87
    if filename is not None:

        if op.splitext(filename)[1] == '':
            if imgtype == 0: filename = '{}.img'.format(filename)
            else:            filename = '{}.nii'.format(filename)
88

Paul McCarthy's avatar
Paul McCarthy committed
89
        nib.save(img, filename)
90
91
92
93

    return img


94
95
96
97
98
99
100
101

# Need to test:
#     - Create image from existing nibabel image
#     - Create image from numpy array
#     - calcRange
#     - loadData


102
103
104
105
106
107
108
109
110
111
112
113
114
def test_load():
    """Create an Image from a file name. """

    # notnifti.nii.gz is just a plain text
    # file, the rest are NIFTI images
    toCreate = ['compressed.nii.gz',
                'uncompressed.nii',
                'img_hdr_pair.img',
                'compressed_img_hdr_pair.img.gz',
                'ambiguous.nii',
                'ambiguous.img',
                'ambiguous.img.gz',
                'notnifti.nii.gz']
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

    shouldPass = ['compressed',
                  'compressed.nii.gz',
                  'uncompressed',
                  'uncompressed.nii',
                  'img_hdr_pair',
                  'img_hdr_pair.img',
                  'img_hdr_pair.hdr',
                  'compressed_img_hdr_pair',
                  'compressed_img_hdr_pair.img.gz',
                  'compressed_img_hdr_pair.hdr.gz',
                  'ambiguous.nii',
                  'ambiguous.hdr',
                  'ambiguous.img',
                  'ambiguous.hdr.gz',
                  'ambiguous.img.gz']
Paul McCarthy's avatar
Paul McCarthy committed
132
133
134
    shouldRaise = [('notexist',        fslpath.PathError),
                   ('notexist.nii.gz', fslpath.PathError),
                   ('ambiguous',       fslpath.PathError),
135
136
                   ('notnifti',        (ImageFileError, igzip.ZranError)),
                   ('notnifti.nii.gz', (ImageFileError, igzip.ZranError))]
137
138


139
    with tempdir() as testdir:
140

141
142
143
144
145
146
        for f in toCreate:

            if f.startswith('notnifti'):
                make_dummy_file(op.join(testdir, f))
            else:
                make_random_image(op.join(testdir, f))
147

148
        # Not raising an error means the test passes
149
150
151
152
153
154
155
156
157
        for fname in shouldPass:
            fslimage.Image(op.join(testdir, fname))

        # These should raise an error
        for fname, exc in shouldRaise:
            with pytest.raises(exc):
                fslimage.Image(op.join(testdir, fname))


Paul McCarthy's avatar
Paul McCarthy committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def test_create():

    # Test creating:
    #  from a numpy array
    #  from a numpy array + xform
    #  from a numpy array + nibabel header
    #  from a numpy array + nibabel header + xform
    #  from a file
    #  from a nibabel image

    data  = np.random.random((10, 10, 10))
    xform = np.diag([2, 3, 4, 1])

    img = fslimage.Image(data)
    assert np.all(np.isclose(img.pixdim, (1, 1, 1)))
    assert np.all(np.isclose(img.voxToWorldMat, np.eye(4)))
    assert img.niftiVersion == 1

    img = fslimage.Image(data, xform=xform)
    assert np.all(np.isclose(img.pixdim, (2, 3, 4)))
    assert np.all(np.isclose(img.voxToWorldMat, xform))
    assert img.niftiVersion == 1

    for imgType in [0, 1, 2]:
182

Paul McCarthy's avatar
Paul McCarthy committed
183
184
185
186
187
188
189
190
191
192
193
194
        nimg = make_image(imgtype=imgType, pixdims=(5, 6, 7))
        nhdr = nimg.header

        img = fslimage.Image(data, header=nhdr)
        assert img.niftiVersion == imgType
        assert np.all(np.isclose(img.pixdim, (5, 6, 7)))

        img = fslimage.Image(data, header=nhdr, xform=xform)
        assert img.niftiVersion == imgType
        assert np.all(np.isclose(img.pixdim, (2, 3, 4)))

    for imgtype in [0, 1, 2]:
195
        with tempdir() as testdir:
Paul McCarthy's avatar
Paul McCarthy committed
196
197
198
199
200
201
202
203
204
205
            fname = op.join(testdir, 'myimage')
            nimg  = make_image(fname, imgtype, pixdims=(2, 3, 4))

            img = fslimage.Image(fname)
            assert img.niftiVersion == imgtype
            assert np.all(np.isclose(img.pixdim, (2, 3, 4)))

            img = fslimage.Image(nimg)
            assert img.niftiVersion == imgtype
            assert np.all(np.isclose(img.pixdim, (2, 3, 4)))
206

Paul McCarthy's avatar
Paul McCarthy committed
207
208

def test_bad_create():
209

Paul McCarthy's avatar
Paul McCarthy committed
210
211
212
213
214
215
216
217
    class BadThing(object):
        pass

    # Bad header object
    with pytest.raises(Exception):
        fslimage.Image(
            np.random.random((10, 10, 10)),
            header=BadThing())
218

Paul McCarthy's avatar
Paul McCarthy committed
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    # Bad data
    with pytest.raises(Exception):
        fslimage.Image(BadThing())

    # Bad data
    with pytest.raises(Exception):
        fslimage.Image(np.random.random(10, 10, 10, 10, 10))

    # Bad xform
    with pytest.raises(Exception):
        fslimage.Image(np.random.random(10, 10, 10),
                       xform=BadThing())
    # Bad xform
    with pytest.raises(Exception):
        fslimage.Image(np.random.random(10, 10, 10),
                       xform=np.eye(3))
235

Paul McCarthy's avatar
Paul McCarthy committed
236
237
238
239
240
    with pytest.raises(Exception):
        fslimage.Image(np.random.random(10, 10, 10),
                       xform=np.eye(5))


241
def  test_Image_atts_analyze(): _test_Image_atts(0)
242
243
244
245
def  test_Image_atts_nifti1():  _test_Image_atts(1)
def  test_Image_atts_nifti2():  _test_Image_atts(2)
def _test_Image_atts(imgtype):
    """Test that basic Nifti/Image attributes are correct. """
246

247
248
249
250
251
    allowedExts = fslimage.ALLOWED_EXTENSIONS
    fileGroups  = fslimage.FILE_GROUPS

    # (file, dims, pixdims, dtype)
    dtypes = [np.uint8, np.int16, np.int32, np.float32, np.double]
Paul McCarthy's avatar
Paul McCarthy committed
252
253
    dims   = [(1,   1,  1),
              (10,  1,  1),
254
255
256
257
258
259
              (1,  10,  1),
              (1,  1,  10),
              (10,  10, 1),
              (10,  1, 10),
              (1,  10, 10),
              (10, 10, 10),
Paul McCarthy's avatar
Paul McCarthy committed
260
261
262
263
264
265
              (1,   1,  1, 1),
              (10,  1,  1, 1),
              (1,  10,  1, 1),
              (1,   1, 10, 1),
              (10, 10,  1, 1),
              (10, 10, 1, 5),
266
267
268
269
270
271
272
273
              (10,  1, 10, 5),
              (1,  10, 10, 5),
              (10, 10, 10, 5)]
    pixdims = [(0.5, 0.5, 0.5, 2),
               (1.0, 1.0, 1.0, 2),
               (2.0, 2.0, 2.0, 2),
               (1.0, 5.0, 1.0, 3)]

Paul McCarthy's avatar
Paul McCarthy committed
274
275
    tests = it.product(dims, pixdims, dtypes)
    tests = list(tests)
276
    paths = ['test{:03d}'.format(i) for i in range(len(tests))]
277

278
    with tempdir() as testdir:
Paul McCarthy's avatar
Paul McCarthy committed
279

280
        for path, atts in zip(paths, tests):
281

282
            dims, pixdims, dtype = atts
283

284
285
            ndims   = len(dims)
            pixdims = pixdims[:ndims]
286

287
288
            path = op.abspath(op.join(testdir, path))
            make_image(path, imgtype, dims, pixdims, dtype)
289

Paul McCarthy's avatar
Paul McCarthy committed
290
291
292
        for path, atts in zip(paths, tests):

            dims, pixdims, dtype = atts
293

294
            expdims    = fslimage.canonicalShape(dims)
Paul McCarthy's avatar
Paul McCarthy committed
295
296
297
298
            expndims   = len(expdims)
            ndims      = len(dims)
            pixdims    = pixdims[:ndims]
            exppixdims = pixdims[:expndims]
299

300
301
            path = op.abspath(op.join(testdir, path))
            i    = fslimage.Image(path)
302

Paul McCarthy's avatar
Paul McCarthy committed
303
            assert not   i.iscomplex
Paul McCarthy's avatar
Paul McCarthy committed
304
            assert tuple(i.shape)                       == tuple(expdims)
305
            assert tuple(i.data.shape)                  == tuple(expdims)
Paul McCarthy's avatar
Paul McCarthy committed
306
            assert tuple(i.pixdim)                      == tuple(exppixdims)
307
308
            assert tuple(i.nibImage.shape)              == tuple(dims)
            assert tuple(i.nibImage.header.get_zooms()) == tuple(pixdims)
309

310
            assert i.nvals      == 1
311
            assert i.ndim       == expndims
312
313
314
315
316
317
            assert i.dtype      == dtype
            assert i.name       == op.basename(path)
            assert i.dataSource == fslpath.addExt(path,
                                                  allowedExts=allowedExts,
                                                  mustExist=True,
                                                  fileGroups=fileGroups)
Paul McCarthy's avatar
Paul McCarthy committed
318
            i = None
319

320
321

def  test_Image_atts2_analyze(): _test_Image_atts2(0)
Paul McCarthy's avatar
Paul McCarthy committed
322
323
324
def  test_Image_atts2_nifti1():  _test_Image_atts2(1)
def  test_Image_atts2_nifti2():  _test_Image_atts2(2)
def _test_Image_atts2(imgtype):
325

Paul McCarthy's avatar
Paul McCarthy committed
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
    # See fsl.utils.constants for the meanings of these codes
    xyzUnits  = [0, 1, 2, 3]
    timeUnits = [8, 16, 24, 32, 40, 48]
    intents   = [0, 2, 1007, 2005, 2006, 2016]

    for xyzu, timeu, intent in it.product(xyzUnits, timeUnits, intents):

        nimg = make_image(imgtype=imgtype)

        # Analyze images do not have units or intent
        if imgtype != 0:
            nimg.header.set_xyzt_units(xyzu, timeu)
            nimg.header['intent_code'] = intent
            nimg.update_header()

        img = fslimage.Image(nimg)

        if imgtype == 0:
            assert img.xyzUnits  == constants.NIFTI_UNITS_MM
            assert img.timeUnits == constants.NIFTI_UNITS_SEC
            assert img.intent    == constants.NIFTI_INTENT_NONE
        else:
            assert img.xyzUnits  == xyzu
            assert img.timeUnits == timeu
            assert img.intent    == intent
351
352


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
def test_canonicalShape():

    # (input, expected)
    tests = [
        ((10,),                   (10,  1,  1)),
        ((10,  1),                (10,  1,  1)),
        ((10,  1,  1),            (10,  1,  1)),
        ((10,  1,  1,  1),        (10,  1,  1)),
        ((10,  1,  1,  1,  1),    (10,  1,  1)),
        ((10, 10),                (10, 10,  1)),
        ((10, 10,  1),            (10, 10,  1)),
        ((10, 10,  1,  1),        (10, 10,  1)),
        ((10, 10,  1,  1,  1),    (10, 10,  1)),
        ((10, 10, 10),            (10, 10, 10)),
        ((10, 10, 10,  1),        (10, 10, 10)),
        ((10, 10, 10,  1,  1),    (10, 10, 10)),
        ((10, 10, 10, 10),        (10, 10, 10, 10)),
        ((10, 10, 10, 10,  1),    (10, 10, 10, 10)),
        ((10, 10, 10, 10,  1, 1), (10, 10, 10, 10)),
        ((10, 10, 10, 10, 10),    (10, 10, 10, 10, 10)),
        ((10, 10, 10, 10, 10, 1), (10, 10, 10, 10, 10)),
    ]

    for input, expected in tests:
        assert tuple(fslimage.canonicalShape(input)) == expected


380
def test_looksLikeImage():
381
    """Test the looksLikeImage function. """
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397

    # (file, expected)
    tests = [
        ('blah',        False),
        ('blah.moo',    False),
        ('blah.nii',    True),
        ('blah.nii.gz', True),
        ('blah.hdr',    True),
        ('blah.img',    True),
        ('blah.hdr.gz', True),
        ('blah.img.gz', True),
    ]

    for path, expected in tests:
        assert fslimage.looksLikeImage(path) == expected

398

399
400
def test_addExt():
    """Test the addExt function. """
401

Paul McCarthy's avatar
Paul McCarthy committed
402
    default = fslimage.defaultExt()
403
404
405
406
407
408
409
410
411
412
413

    toCreate = [
        'compressed.nii.gz',
        'uncompressed.nii',
        'img_hdr_pair.img',
        'compressed_img_hdr_pair.img.gz',
        'ambiguous.nii',
        'ambiguous.nii.gz',
        'ambiguous.img',
        'ambiguous.img.gz'
    ]
414
415
416
417
418
419
420
421
422
423
424
425
426
427

    # (file, mustExist, expected)
    tests = [
        ('blah',                           False, 'blah{}'.format(default)),
        ('blah.nii',                       False, 'blah.nii'),
        ('blah.nii.gz',                    False, 'blah.nii.gz'),
        ('blah.img',                       False, 'blah.img'),
        ('blah.hdr',                       False, 'blah.hdr'),
        ('blah.img.gz',                    False, 'blah.img.gz'),
        ('blah.hdr.gz',                    False, 'blah.hdr.gz'),
        ('compressed',                     True,  'compressed.nii.gz'),
        ('compressed.nii.gz',              True,  'compressed.nii.gz'),
        ('uncompressed',                   True,  'uncompressed.nii'),
        ('uncompressed.nii',               True,  'uncompressed.nii'),
428
        ('img_hdr_pair',                   True,  'img_hdr_pair.hdr'),
429
430
        ('img_hdr_pair.hdr',               True,  'img_hdr_pair.hdr'),
        ('img_hdr_pair.img',               True,  'img_hdr_pair.img'),
431
        ('compressed_img_hdr_pair',        True,  'compressed_img_hdr_pair.hdr.gz'),
432
433
434
435
436
437
438
439
440
        ('compressed_img_hdr_pair.img.gz', True,  'compressed_img_hdr_pair.img.gz'),
        ('compressed_img_hdr_pair.hdr.gz', True,  'compressed_img_hdr_pair.hdr.gz'),
        ('ambiguous.nii',                  True,  'ambiguous.nii'),
        ('ambiguous.nii.gz',               True,  'ambiguous.nii.gz'),
        ('ambiguous.img',                  True,  'ambiguous.img'),
        ('ambiguous.hdr',                  True,  'ambiguous.hdr'),
        ('ambiguous.img.gz',               True,  'ambiguous.img.gz'),
        ('ambiguous.hdr.gz',               True,  'ambiguous.hdr.gz')]

441

442
443
444
445
446
447
    with tempdir() as testdir:

        for path in toCreate:
            path = op.abspath(op.join(testdir, path))
            make_random_image(path)

448
449
450
451
452
453
454
455
456
457
458
459
        for path, mustExist, expected in tests:

            path     = op.abspath(op.join(testdir, path))
            expected = op.abspath(op.join(testdir, expected))

            assert fslimage.addExt(path, mustExist) == expected

        # Make sure that an ambiguous path fails
        with pytest.raises(fslimage.PathError):
            path = op.join(testdir, 'ambiguous')
            fslimage.addExt(path, mustExist=True)

Paul McCarthy's avatar
Paul McCarthy committed
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506

def test_removeExt():

    exts = ['.nii.gz', '.nii', '.img', '.img.gz', '.hdr', '.hdr.gz']

    for e in exts:
        prefix = 'blob'
        fname  = '{}{}'.format(prefix, e)

        assert fslimage.removeExt(fname) == prefix


def test_getExt():

    exts = ['.nii.gz', '.nii', '.img', '.img.gz', '.hdr', '.hdr.gz']

    for e in exts:
        prefix = 'blob'
        fname  = '{}{}'.format(prefix, e)

        assert fslimage.getExt(fname) == e


def test_splitExt():

    exts = ['.nii.gz', '.nii', '.img', '.img.gz', '.hdr', '.hdr.gz']

    for e in exts:
        prefix = 'blob'
        fname  = '{}{}'.format(prefix, e)

        assert fslimage.splitExt(fname) == (prefix, e)


def test_defaultExt():

    fslOutputTypes = ['NIFTI', 'NIFTI_PAIR', 'NIFTI_GZ']
    exts           = ['.nii', '.img', '.nii.gz']

    os.environ.pop('FSLOUTPUTTYPE', None)
    assert fslimage.defaultExt() == '.nii.gz'

    for o, e in zip(fslOutputTypes, exts):

        os.environ['FSLOUTPUTTYPE'] = o

        assert fslimage.defaultExt() == e
507

Paul McCarthy's avatar
Paul McCarthy committed
508

Paul McCarthy's avatar
Paul McCarthy committed
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
def test_fixExt():
    with tempdir():

        # error if if file doesn't exist
        with pytest.raises(fslpath.PathError):
            fslimage.fixExt('file.nii.gz')

        with open('file.nii', 'w') as f:
            f.write('1')
        assert fslimage.fixExt('file.nii.gz') == 'file.nii'
        assert fslimage.fixExt('file.nii')    == 'file.nii'

        with open('file.nii.gz', 'w') as f:
            f.write('1')

        assert fslimage.fixExt('file.nii.gz') == 'file.nii.gz'
        assert fslimage.fixExt('file.nii')    == 'file.nii'

        os.remove('file.nii')
        os.remove('file.nii.gz')
        with open('file.nii.gz', 'w') as f:
            f.write('1')
        assert fslimage.fixExt('file.nii') == 'file.nii.gz'


534
535
536
537
538
def  test_Image_orientation_analyze_neuro(): _test_Image_orientation(0, 'neuro')
def  test_Image_orientation_analyze_radio(): _test_Image_orientation(0, 'radio')
def  test_Image_orientation_nifti1_neuro():  _test_Image_orientation(1, 'neuro')
def  test_Image_orientation_nifti1_radio():  _test_Image_orientation(1, 'radio')
def  test_Image_orientation_nifti2_neuro():  _test_Image_orientation(2, 'neuro')
539
def  test_Image_orientation_nifti2_radio():  _test_Image_orientation(2, 'radio')
540
541
542
def _test_Image_orientation(imgtype, voxorient):
    """Test the Nifti.isNeurological and Nifti.getOrientation methods. """

543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
    with tempdir() as testdir:
        imagefile = op.join(testdir, 'image')

        # an image with RAS voxel storage order
        # (affine has a positive determinant)
        # is said to be "neurological", whereas
        # an image with LAS voxel storage order
        # (negative determinant - x axis must
        # be flipped to bring it into RAS nifti
        # world coordinates)) is said to be
        # "radiological".  The make_image function
        # forms the affine from these pixdims.
        if   voxorient == 'neuro': pixdims = ( 1, 1, 1)
        elif voxorient == 'radio': pixdims = (-1, 1, 1)

        make_image(imagefile, imgtype, (10, 10, 10), pixdims, np.float32)

Paul McCarthy's avatar
Paul McCarthy committed
560
        image = fslimage.Image(imagefile, mmap=False)
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579

        # analyze images are always assumed to be
        # stored in radiological (LAS) orientation
        if imgtype == 0:
            expectNeuroTest       = False
            expectvox0Orientation = constants.ORIENT_R2L
            expectvox1Orientation = constants.ORIENT_P2A
            expectvox2Orientation = constants.ORIENT_I2S

        elif voxorient == 'neuro':
            expectNeuroTest       = True
            expectvox0Orientation = constants.ORIENT_L2R
            expectvox1Orientation = constants.ORIENT_P2A
            expectvox2Orientation = constants.ORIENT_I2S
        else:
            expectNeuroTest       = False
            expectvox0Orientation = constants.ORIENT_R2L
            expectvox1Orientation = constants.ORIENT_P2A
            expectvox2Orientation = constants.ORIENT_I2S
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595

        assert image.isNeurological() == expectNeuroTest

        # All images should have the
        # same orientation in the
        # world coordinate system
        assert image.getOrientation(0, np.eye(4)) == constants.ORIENT_L2R
        assert image.getOrientation(1, np.eye(4)) == constants.ORIENT_P2A
        assert image.getOrientation(2, np.eye(4)) == constants.ORIENT_I2S

        # But the voxel orientation
        # is dependent on the affine
        affine = image.voxToWorldMat
        assert image.getOrientation(0, affine) == expectvox0Orientation
        assert image.getOrientation(1, affine) == expectvox1Orientation
        assert image.getOrientation(2, affine) == expectvox2Orientation
Paul McCarthy's avatar
Paul McCarthy committed
596
        image = None
597
598
599
600
601
602
603
604
605


def  test_Image_sqforms_nifti1_normal():   _test_Image_sqforms(1, 1, 1)
def  test_Image_sqforms_nifti1_nosform():  _test_Image_sqforms(1, 0, 1)
def  test_Image_sqforms_nifti1_noqform():  _test_Image_sqforms(1, 1, 0)
def  test_Image_sqforms_nifti1_nosqform(): _test_Image_sqforms(1, 1, 0)
def  test_Image_sqforms_nifti2_normal():   _test_Image_sqforms(2, 1, 1)
def  test_Image_sqforms_nifti2_nosform():  _test_Image_sqforms(2, 0, 1)
def  test_Image_sqforms_nifti2_noqform():  _test_Image_sqforms(2, 1, 0)
606
def  test_Image_sqforms_nifti2_nosqform(): _test_Image_sqforms(2, 0, 0)
607
608
609
610
611
def _test_Image_sqforms(imgtype, sformcode, qformcode):
    """Test the Nifti.getXFormCode method, and the voxToWorldMat/worldToVoxMat
    attributes for NIFTI images with the given sform/qform code combination.
    """

612
    with tempdir() as testdir:
613

614
        imagefile = op.abspath(op.join(testdir, 'image.nii.gz'))
615

616
617
618
619
620
621
622
623
624
        # For an image with no s/q form, we expect the
        # fallback affine - a simple scaling matrix.
        # We add some offsets to the actual affine so
        # we can distinguish it from the fallback affine.
        scaleMat      = np.diag([2,   2,   2,   1])
        invScaleMat   = np.diag([0.5, 0.5, 0.5, 1])
        affine        = np.array(scaleMat)
        affine[:3, 3] = [25, 20, 20]
        invAffine     = npla.inv(affine)
625

626
        image = make_image(imagefile, imgtype, (10, 10, 10), (2, 2, 2), np.float32)
627

628
629
630
        image.set_sform(affine, sformcode)
        image.set_qform(affine, qformcode)
        image.update_header()
631

632
        nib.save(image, imagefile)
633

634
635
636
637
638
639
        # No s or qform - we expect the fallback affine
        if sformcode == 0 and qformcode == 0:
            expAffine    = scaleMat
            invExpAffine = invScaleMat
            expCode      = constants.NIFTI_XFORM_UNKNOWN
            expOrient    = constants.ORIENT_UNKNOWN
640

641
642
643
644
645
646
        # No sform, but valid qform - expect the affine
        elif sformcode == 0 and qformcode > 0:
            expAffine    = affine
            invExpAffine = invAffine
            expCode      = qformcode
            expOrient    = constants.ORIENT_L2R
647

648
649
650
651
652
653
        # Valid sform (qform irrelevant) - expect the affine
        elif sformcode > 0:
            expAffine    = affine
            invExpAffine = invAffine
            expCode      = sformcode
            expOrient    = constants.ORIENT_L2R
654

655
        image = fslimage.Image(imagefile)
Paul McCarthy's avatar
Paul McCarthy committed
656

657
658
        with pytest.raises(ValueError):
            image.getXFormCode('badcode')
659

660
661
        assert np.all(np.isclose(image.voxToWorldMat,  expAffine))
        assert np.all(np.isclose(image.worldToVoxMat,  invExpAffine))
662

663
664
665
        assert image.getXFormCode()        == expCode
        assert image.getXFormCode('sform') == sformcode
        assert image.getXFormCode('qform') == qformcode
Paul McCarthy's avatar
Paul McCarthy committed
666
667

        assert image.getOrientation(0, image.voxToWorldMat) == expOrient
668
669


670
def  test_Image_changeXform_analyze():         _test_Image_changeXform(0)
Paul McCarthy's avatar
Paul McCarthy committed
671
672
def  test_Image_changeXform_nifti1():          _test_Image_changeXform(1)
def  test_Image_changeXform_nifti1_nosqform(): _test_Image_changeXform(1, 0, 0)
673
def  test_Image_changeXform_nifti2():          _test_Image_changeXform(2)
Paul McCarthy's avatar
Paul McCarthy committed
674
def _test_Image_changeXform(imgtype, sformcode=None, qformcode=None):
675
    """Test changing the Nifti.voxToWorldMat attribute. """
676

677
678
    with tempdir() as testdir:
        imagefile = op.join(testdir, 'image')
679

680
        image = make_image(imagefile, imgtype)
Paul McCarthy's avatar
Paul McCarthy committed
681

682
        if imgtype > 0:
Paul McCarthy's avatar
Paul McCarthy committed
683

684
685
686
687
            if sformcode is not None: image.set_sform(image.affine, sformcode)
            if qformcode is not None: image.set_qform(image.affine, qformcode)
            image.update_header()
            nib.save(image, imagefile)
Paul McCarthy's avatar
Paul McCarthy committed
688

689
        notified = {}
Paul McCarthy's avatar
Paul McCarthy committed
690

691
692
        def onXform(*a):
            notified['xform'] = True
693

694
695
        def onSave(*a):
            notified['save'] = True
Paul McCarthy's avatar
Paul McCarthy committed
696

697
        img = fslimage.Image(imagefile)
Paul McCarthy's avatar
Paul McCarthy committed
698

699
700
        img.register('name1', onXform, 'transform')
        img.register('name2', onSave,  'saveState')
Paul McCarthy's avatar
Paul McCarthy committed
701

702
703
704
705
        newXform = np.array([[5, 0, 0, 10],
                             [0, 2, 0, 23],
                             [0, 0, 14, 5],
                             [0, 0, 0, 1]])
706

707
708
709
        if imgtype > 0:
            expSformCode = image.get_sform(coded=True)[1]
            expQformCode = image.get_qform(coded=True)[1]
Paul McCarthy's avatar
Paul McCarthy committed
710

711
712
713
714
715
            if sformcode == 0:
                expSformCode = constants.NIFTI_XFORM_ALIGNED_ANAT
        else:
            expSformCode = constants.NIFTI_XFORM_ANALYZE
            expQformCode = constants.NIFTI_XFORM_ANALYZE
Paul McCarthy's avatar
Paul McCarthy committed
716

717
718
        # Image state should initially be saved
        assert img.saveState
Paul McCarthy's avatar
Paul McCarthy committed
719

720
        if imgtype == 0:
721
            # ANALYZE affine is not editable
722
723
            with pytest.raises(Exception):
                img.voxToWorldMat = newXform
Paul McCarthy's avatar
Paul McCarthy committed
724
725
            del img
            del image
726
            return
Paul McCarthy's avatar
Paul McCarthy committed
727

728
729
730
731
732
733
734
735
        img.voxToWorldMat = newXform

        invx = npla.inv(newXform)

        # Did we get notified?
        assert notified.get('xform', False)
        assert notified.get('save',  False)
        assert not img.saveState
Paul McCarthy's avatar
Paul McCarthy committed
736

737
738
739
        # Did the affine get updated?
        assert np.all(np.isclose(img.voxToWorldMat, newXform))
        assert np.all(np.isclose(img.worldToVoxMat, invx))
Paul McCarthy's avatar
Paul McCarthy committed
740
741
        assert img.getXFormCode('sform') == expSformCode
        assert img.getXFormCode('qform') == expQformCode
Paul McCarthy's avatar
Paul McCarthy committed
742
743
744
        del img
        del image
        image = None
Paul McCarthy's avatar
Paul McCarthy committed
745
746


747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
def  test_Image_changeIntent_analyze(): _test_Image_changeIntent(0)
def  test_Image_changeIntent_nifti1():  _test_Image_changeIntent(1)
def  test_Image_changeIntent_nifti2():  _test_Image_changeIntent(2)
def _test_Image_changeIntent(imgtype):
    """Test changing the Nifti.intent attribute. """

    with tempdir() as testdir:
        imagefile = op.join(testdir, 'image')

        image = make_image(imagefile, imgtype)
        if imgtype > 0:
            image.header.set_intent(constants.NIFTI_INTENT_NONE)
        nib.save(image, imagefile)

        notified = {}
        def onHdr( *a): notified['header'] = True
        def onSave(*a): notified['save']   = True

        img = fslimage.Image(imagefile)

        img.register('name1', onHdr,  'header')
        img.register('name2', onSave, 'saveState')

        assert img.intent == constants.NIFTI_INTENT_NONE
        img.intent = constants.NIFTI_INTENT_BETA

        if imgtype == 0: exp = constants.NIFTI_INTENT_NONE
        else:            exp = constants.NIFTI_INTENT_BETA

        assert img.intent == exp

        if imgtype > 0:
            assert img         .header.get_intent('code')[0] == exp
            assert img.nibImage.header.get_intent('code')[0] == exp

            assert notified.get('header', False)
            assert notified.get('save',   False)





789
790
def  test_Image_changeData_analyze(seed): _test_Image_changeData(0)
def  test_Image_changeData_nifti1(seed):  _test_Image_changeData(1)
791
def  test_Image_changeData_nifti2(seed):  _test_Image_changeData(2)
792
793
794
795
796
def _test_Image_changeData(imgtype):
    """Test that changing image data triggers notification, and also causes
    the dataRange attribute to be updated.
    """

797
798
    with tempdir() as testdir:
        imagefile = op.join(testdir, 'image')
799

800
        make_image(imagefile, imgtype)
Paul McCarthy's avatar
Paul McCarthy committed
801

Paul McCarthy's avatar
Paul McCarthy committed
802
803
        img = fslimage.Image(imagefile, mmap=False)
        shape = img.shape
804

805
        notified = {}
Paul McCarthy's avatar
Paul McCarthy committed
806

807
        def randvox():
Paul McCarthy's avatar
Paul McCarthy committed
808
809
810
            return (np.random.randint(0, shape[0]),
                    np.random.randint(0, shape[1]),
                    np.random.randint(0, shape[2]))
Paul McCarthy's avatar
Paul McCarthy committed
811

812
813
        def onData(*a):
            notified['data'] = True
Paul McCarthy's avatar
Paul McCarthy committed
814

815
816
        def onSaveState(*a):
            notified['save'] = True
Paul McCarthy's avatar
Paul McCarthy committed
817

818
819
        def onDataRange(*a):
            notified['dataRange'] = True
Paul McCarthy's avatar
Paul McCarthy committed
820

821
822
823
        img.register('name1', onData,      'data')
        img.register('name2', onSaveState, 'saveState')
        img.register('name3', onDataRange, 'dataRange')
Paul McCarthy's avatar
Paul McCarthy committed
824

825
        # Calculate the actual data range
Paul McCarthy's avatar
Paul McCarthy committed
826
        data   = np.asanyarray(img.nibImage.dataobj)
827
828
829
        dmin   = data.min()
        dmax   = data.max()
        drange = dmax - dmin
Paul McCarthy's avatar
Paul McCarthy committed
830

831
832
        assert img.saveState
        assert np.all(np.isclose(img.dataRange, (dmin, dmax)))
Paul McCarthy's avatar
Paul McCarthy committed
833

834
835
836
837
838
839
840
841
842
843
        # random value within the existing data range,
        # making sure not to overwite the min or max
        randval = dmin + np.random.random() * drange

        while True:
            rx, ry, rz = randvox()
            if not (np.isclose(img[rx, ry, rz], dmin) or
                    np.isclose(img[rx, ry, rz], dmax)):
                img[rx, ry, rz] = randval
                break
Paul McCarthy's avatar
Paul McCarthy committed
844

845
846
847
848
        assert np.isclose(img[rx, ry, rz], randval)
        assert notified.get('data', False)
        assert notified.get('save', False)
        assert not img.saveState
Paul McCarthy's avatar
Paul McCarthy committed
849

850
        notified.pop('data')
Paul McCarthy's avatar
Paul McCarthy committed
851

852
853
        newdmin = dmin - 100
        newdmax = dmax + 100
Paul McCarthy's avatar
Paul McCarthy committed
854

Paul McCarthy's avatar
Paul McCarthy committed
855
856
857
858
859
860
861
862
        # random value below the data range,
        # making sure not to overwrite the
        # max
        while True:
            minx, miny, minz = randvox()
            if not np.isclose(img[minx, miny, minz], dmax):
                img[minx, miny, minz] = newdmin
                break
Paul McCarthy's avatar
Paul McCarthy committed
863

864
865
        assert notified.get('data',      False)
        assert notified.get('dataRange', False)
Paul McCarthy's avatar
Paul McCarthy committed
866
        assert np.isclose(img[minx, miny, minz], newdmin)
867
        assert np.all(np.isclose(img.dataRange, (newdmin, dmax)))
Paul McCarthy's avatar
Paul McCarthy committed
868

869
870
        notified.pop('data')
        notified.pop('dataRange')
Paul McCarthy's avatar
Paul McCarthy committed
871

Paul McCarthy's avatar
Paul McCarthy committed
872
873
874
875
        # random value above the data range,
        # making sure not to overwrite the
        # min
        while True:
Paul McCarthy's avatar
Paul McCarthy committed
876
            maxx, maxy, maxz = randvox()
Paul McCarthy's avatar
Paul McCarthy committed
877
878
879
            if not np.isclose(img[maxx, maxy, maxz], newdmin):
                img[maxx, maxy, maxz] = newdmax
                break
880

881
882
        assert notified.get('data',      False)
        assert notified.get('dataRange', False)
Paul McCarthy's avatar
Paul McCarthy committed
883
        assert np.isclose(img[maxx, maxy, maxz], newdmax)
884
        assert np.all(np.isclose(img.dataRange, (newdmin, newdmax)))
Paul McCarthy's avatar
Paul McCarthy committed
885
886
887
888
        img.deregister('name1', 'data')
        img.deregister('name2', 'data')
        img.deregister('name3', 'data')
        img = None
889
890


891
892
893
894
def  test_Image_2D_analyze(): _test_Image_2D(0)
def  test_Image_2D_nifti1():  _test_Image_2D(1)
def  test_Image_2D_nifti2():  _test_Image_2D(2)
def _test_Image_2D(imgtype):
895

896
897
898
899
900
    # The first shape tests when the
    # nifti dim0 field is set to 2,
    # which happens when you create
    # an XY slice with fslroi. This
    # should still be read in as a
901
    # 3D image.
902
903
904
905
906
907
908
909
    testdims = [(10, 20),
                (10, 20, 1),
                (10, 1,  20),
                (1,  10, 20),
                (10, 20, 1,  5),
                (10, 1,  20, 5),
                (1,  10, 20, 5)]

910
    with tempdir() as testdir:
911
912
913
914

        for shape in testdims:

            pixdim = [2] * len(shape)
915

916
            imagefile = op.join(testdir, 'image')
917

918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
            make_image(imagefile, imgtype, shape, pixdim)

            image = fslimage.Image(imagefile)

            # 2D should appear as 3D
            if len(shape) == 2:
                shape  = list(shape)  + [1]
                pixdim = list(pixdim) + [1]

            assert len(shape)  == len(image   .shape)
            assert len(shape)  == len(image[:].shape)
            assert len(pixdim) == len(image   .pixdim)

            assert tuple(map(float, shape))  == tuple(map(float, image   .shape))
            assert tuple(map(float, shape))  == tuple(map(float, image[:].shape))
            assert tuple(map(float, pixdim)) == tuple(map(float, image   .pixdim))
Paul McCarthy's avatar
Paul McCarthy committed
934
            image = None
935

Paul McCarthy's avatar
Paul McCarthy committed
936

Paul McCarthy's avatar
Paul McCarthy committed
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
def  test_Image_5D_analyze(): _test_Image_5D(0)
def  test_Image_5D_nifti1():  _test_Image_5D(1)
def  test_Image_5D_nifti2():  _test_Image_5D(2)
def _test_Image_5D(imgtype):

    testdims = [
        ( 1,  1,  1, 1, 5),
        (10, 10,  1, 1, 5),
        (10, 10, 10, 1, 5),
        ( 1,  1,  1, 4, 5),
        (10, 10,  1, 4, 5),
        (10, 10, 10, 4, 5),
    ]

    for dims in testdims:

953
        with tempdir() as td:
Paul McCarthy's avatar
Paul McCarthy committed
954
955
956
957
958
959
960

            path = op.join(td, 'test.nii')

            make_image(path, imgtype, dims, [1] * len(dims))

            img = fslimage.Image(path)

961
962
963
            assert img.shape      == dims
            assert img.ndim       == 5
            assert img.data.shape == dims
Paul McCarthy's avatar
Paul McCarthy committed
964
965
            del img
            img = None
Paul McCarthy's avatar
Paul McCarthy committed
966
967


968
969
970
971
972
def test_Image_voxToScaledVox_analyze(): _test_Image_voxToScaledVox(0)
def test_Image_voxToScaledVox_nifti1():  _test_Image_voxToScaledVox(1)
def test_Image_voxToScaledVox_nifti2():  _test_Image_voxToScaledVox(2)

def _test_Image_voxToScaledVox(imgtype):
Paul McCarthy's avatar
Paul McCarthy committed
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990

    dims     = [(10, 10, 10)]
    pixdims  = [(-1, 1, 1),
                ( 1, 1, 1),
                (-2, 2, 2),
                ( 2, 2, 2),
                (-3, 4, 5),
                ( 3, 4, 5)]

    def expect(itype, dims, pixdims):
        xf = np.eye(4)
        xf[0, 0] = abs(pixdims[0])
        xf[1, 1] =     pixdims[1]
        xf[2, 2] =     pixdims[2]

        if itype > 0 and pixdims[0] > 0:
            xf[0, 0] = -pixdims[0]
            xf[0, 3] =  pixdims[0] * (dims[0] - 1)
991

Paul McCarthy's avatar
Paul McCarthy committed
992
        return xf
993

994
995
    for dim, pixdim in it.product(dims, pixdims):
        nimg = make_image(imgtype=imgtype, dims=dim, pixdims=pixdim)
Paul McCarthy's avatar
Paul McCarthy committed
996
997
        img  = fslimage.Image(nimg)

998
999
        expected    = expect(imgtype, dim, pixdim)
        invexpected = npla.inv(expected)
Paul McCarthy's avatar
Paul McCarthy committed
1000

1001
1002
        assert np.all(np.isclose(expected,    img.voxToScaledVoxMat))
        assert np.all(np.isclose(invexpected, img.scaledVoxToVoxMat))
Paul McCarthy's avatar
Paul McCarthy committed
1003
        img = None
1004

Paul McCarthy's avatar
Paul McCarthy committed
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014

def test_Image_sameSpace():

    imgTypes = [0, 1, 2]
    dims     = [(10, 10),
                (10, 10, 10),
                (10, 10, 10, 10)]
    pixdims = [(2, 2, 2, 1),
               (2, 3, 4, 1)]

1015
    for (imgType,
Paul McCarthy's avatar
Paul McCarthy committed
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
         dim1,
         dim2,
         pixdim1,
         pixdim2) in it.product(imgTypes, dims, dims, pixdims, pixdims):

        expected = dim1[:3] == dim2[:3] and pixdim1[:3] == pixdim2[:3]

        img1 = fslimage.Image(make_image(imgtype=imgType, dims=dim1, pixdims=pixdim1))
        img2 = fslimage.Image(make_image(imgtype=imgType, dims=dim2, pixdims=pixdim2))

        assert img1.sameSpace(img2) == expected
        assert img2.sameSpace(img1) == expected

def  test_Image_save_analyze(seed): _test_Image_save(0)
def  test_Image_save_nifti1( seed): _test_Image_save(1)
def  test_Image_save_nifti2( seed): _test_Image_save(2)
def _test_Image_save(imgtype):

    def randvox():
        return (np.random.randint(0, 10),
                np.random.randint(0, 10),
                np.random.randint(0, 10))
Paul McCarthy's avatar
Paul McCarthy committed
1038
1039
1040
1041
1042
1043
1044
1045
1046

    def randvoxes(num):
        rvoxes = []

        while len(rvoxes) < num:
            rvox = randvox()
            if rvox not in rvoxes:
                rvoxes.append(rvox)
        return rvoxes
1047
1048


1049
1050
1051
1052
1053
1054
1055
    with tempdir() as testdir:
        if imgtype == 0:
            filename  = op.join(testdir, 'blob.img')
            filename2 = op.join(testdir, 'blob_copy.img')
        else:
            filename  = op.join(testdir, 'blob.nii')
            filename2 = op.join(testdir, 'blob_copy.nii')
Paul McCarthy's avatar
Paul McCarthy committed
1056

1057
1058
1059
1060
1061
        xform = np.eye(4)
        xform[:3, 3] = [-10, 20, 30]
        xform[ 0, 0] = 33
        xform[ 1, 1] = 55
        xform[ 2, 2] = 38
Paul McCarthy's avatar
Paul McCarthy committed
1062
1063
1064

        make_image(filename, imgtype)

1065
1066
1067
1068
1069
        # Save to original location, and
        # to a different location. And
        # test both with and without mmap
        targets = [None, filename, filename2]
        mmaps   = [False, True]
Paul McCarthy's avatar
Paul McCarthy committed
1070

1071
        for target, mmap in it.product(targets, mmaps):
1072

1073
            img = fslimage.Image(filename, mmap=mmap)
Paul McCarthy's avatar
Paul McCarthy committed
1074

1075
1076
            rvoxs = randvoxes(5)
            rvals = [np.random.random() for i in range(5)]
Paul McCarthy's avatar
Paul McCarthy committed
1077

1078
1079
            for (x, y, z), v in zip(rvoxs, rvals):
                img[x, y, z] = v
Paul McCarthy's avatar
Paul McCarthy committed
1080

1081
1082
            if imgtype > 0:
                img.voxToWorldMat = xform
Paul McCarthy's avatar
Paul McCarthy committed
1083

1084
            img.save(target)
Paul McCarthy's avatar
Paul McCarthy committed
1085

1086
1087
            if target is None: expDataSource = filename
            else:              expDataSource = target
Paul McCarthy's avatar
Paul McCarthy committed
1088
1089
1090
1091
1092
1093
1094

            assert img.saveState
            assert img.dataSource == expDataSource

            if imgtype > 0:
                assert np.all(np.isclose(img.voxToWorldMat, xform))

1095
            for (x, y, z), v in zip(rvoxs, rvals):
Paul McCarthy's avatar
Paul McCarthy committed
1096
1097
                assert np.isclose(img[x, y, z], v)

Paul McCarthy's avatar
Paul McCarthy committed
1098
1099
            # Load the image back in
            img2 = fslimage.Image(img.dataSource)
1100

1101
1102
            assert img2.saveState
            assert img2.dataSource == expDataSource
Paul McCarthy's avatar
Paul McCarthy committed
1103

1104
1105
1106
            for i in (img, img2):
                if imgtype > 0:
                    assert np.all(np.isclose(i.voxToWorldMat, xform))
Paul McCarthy's avatar
Paul McCarthy committed
1107

1108
1109
1110
                for (x, y, z), v in zip(rvoxs, rvals):
                    assert np.isclose(i[x, y, z], v)
            img  = None
Paul McCarthy's avatar
Paul McCarthy committed
1111
            img2 = None
1112

Paul McCarthy's avatar
Paul McCarthy committed
1113

1114
1115
1116
1117
1118
1119
def  test_Image_init_xform_nifti1():  _test_Image_init_xform(1)
def  test_Image_init_xform_nifti2():  _test_Image_init_xform(2)
def _test_Image_init_xform(imgtype):

    with tempdir() as td:

Paul McCarthy's avatar
Paul McCarthy committed
1120
1121
1122
1123
1124
1125
        sform = affine.compose(np.random.random(3),
                               np.random.random(3),
                               np.random.random(3))
        qform = affine.compose(np.random.random(3),
                               np.random.random(3),
                               np.random.random(3))
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139

        sform_code = 3
        qform_code = 4

        # Create a base nifti image
        img = make_image('file.nii')
        img.set_sform(sform, code=sform_code)
        img.set_qform(qform, code=qform_code)
        nib.save(img, 'file.nii')
        img = nib.load('file.nii')

        # an image created off a
        # header should have
        # identical sform/qform
Paul McCarthy's avatar
Paul McCarthy committed
1140
        fimg = fslimage.Image(np.asanyarray(img.dataobj), header=img.header)
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157

        fsform, fsform_code = fimg.header.get_sform(True)
        fqform, fqform_code = fimg.header.get_qform(True)
        xform               = fimg.voxToWorldMat

        assert np.all(np.isclose(fsform, sform))
        assert np.all(np.isclose(fqform, qform))
        assert np.all(np.isclose(xform,  sform))
        assert fsform_code == sform_code
        assert fqform_code == qform_code

        # an image created off
        # an xform only should
        # get its sform set
        # set to that xform,
        # qform to None, and
        # and codes set to (s2, q0)
Paul McCarthy's avatar
Paul McCarthy committed
1158
        fimg = fslimage.Image(np.asanyarray(img.dataobj), xform=sform)
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175

        fsform, fsform_code = fimg.header.get_sform(True)
        fqform, fqform_code = fimg.header.get_qform(True)
        xform               = fimg.voxToWorldMat

        assert np.all(np.isclose(fsform, sform))
        assert np.all(np.isclose(xform,  sform))
        assert fqform is None
        assert fsform_code == 2
        assert fqform_code == 0

        # an image created with a
        # header and an xform should
        # have its s/q forms set
        # to the xform. and its
        # s/q form codes the same
        # as what is in the header
Paul McCarthy's avatar
Paul McCarthy committed
1176
1177
1178
        rxform = affine.compose(np.random.random(3),
                                np.random.random(3),
                                np.random.random(3))
Paul McCarthy's avatar
Paul McCarthy committed
1179
        fimg = fslimage.Image(np.asanyarray(img.dataobj),
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
                              header=img.header,
                              xform=rxform)

        fsform, fsform_code = fimg.header.get_sform(True)
        fqform, fqform_code = fimg.header.get_qform(True)
        xform               = fimg.voxToWorldMat

        assert np.all(np.isclose(fsform, rxform))
        assert np.all(np.isclose(fqform, rxform))
        assert np.all(np.isclose(xform,  rxform))
        assert fsform_code == sform_code
        assert fqform_code == qform_code
Paul McCarthy's avatar
Paul McCarthy committed
1192
1193
1194
1195

        del fimg
        del img
        img = None
Paul McCarthy's avatar
Paul McCarthy committed
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218


def test_rgb_image():
    with tempdir():

        dtype = np.dtype([('R', 'uint8'),
                          ('G', 'uint8'),
                          ('B', 'uint8')])
        data  = np.zeros((20, 20, 20), dtype=dtype)

        for i in np.ndindex(data.shape):
            data['R'][i] = np.random.randint(0,   100)
            data['G'][i] = np.random.randint(100, 200)
            data['B'][i] = np.random.randint(200, 256)

        # fix the data limits
        data['R'][0, 0, 0] = 0
        data['B'][0, 0, 0] = 255

        nib.Nifti1Image(data, np.eye(4)).to_filename('rgb.nii')

        img = fslimage.Image('rgb.nii')

1219
        assert img.nvals     == 3
Paul McCarthy's avatar
Paul McCarthy committed
1220
        assert img.dataRange == (0, 255)
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278


def test_determineShape():
    class MockHeader(object):
        def __init__(self, shape, zooms):
            self.shape = shape
            self.zooms = zooms
        def __getitem__(self, key):
            return [len(self.zooms)] + self.zooms
        def get_data_shape(self):
            return self.shape
        def get_zooms(self):
            return self.zooms

    # inshape, inzooms, outshape, outzooms)
    tests = [
        ([10],         [2, 2, 2], [10,  1,  1], [2, 2, 2]),
        ([10],         [2],       [10,  1,  1], [2, 1, 1]),
        ([10],         [2, 2, 2], [10,  1,  1], [2, 2, 2]),
        ([10, 10],     [2, 2],    [10, 10,  1], [2, 2, 1]),
        ([10, 10],     [2, 2, 2], [10, 10,  1], [2, 2, 2]),
        ([10, 10, 10], [2, 2, 2], [10, 10, 10], [2, 2, 2]),

        ([10, 10, 10, 10], [2, 2, 2, 2],
         [10, 10, 10, 10], [2, 2, 2, 2]),
        ([10, 10, 10, 10, 10], [2, 2, 2, 2, 2],
         [10, 10, 10, 10, 10], [2, 2, 2, 2, 2]),
    ]

    for inshape, inzooms, outshape