test_gifti.py 13.6 KB
Newer Older
Paul McCarthy's avatar
Paul McCarthy committed
1
2
3
4
5
6
7
8
#!/usr/bin/env python
#
# test_gifti.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#


Paul McCarthy's avatar
Paul McCarthy committed
9
import            shutil
Paul McCarthy's avatar
Paul McCarthy committed
10
11
12
13
14
15
16
17
18
import            glob
import os.path as op

import numpy   as np
import nibabel as nib
import pytest

import fsl.data.gifti as gifti

Paul McCarthy's avatar
Paul McCarthy committed
19
from . import tempdir
Paul McCarthy's avatar
Paul McCarthy committed
20

Paul McCarthy's avatar
Paul McCarthy committed
21
22

def test_GiftiMesh_create():
Paul McCarthy's avatar
Paul McCarthy committed
23
24
25
26

    testdir   = op.join(op.dirname(__file__), 'testdata')
    testfile  = op.join(testdir, 'example.surf.gii')

Paul McCarthy's avatar
Paul McCarthy committed
27
    surf      = gifti.GiftiMesh(testfile)
Paul McCarthy's avatar
Paul McCarthy committed
28
29
30
    minbounds = np.array([ 59.50759888,  88.43039703,  72.10890198])
    maxbounds = np.array([ 77.72619629, 128.40600586,  94.82050323])

Paul McCarthy's avatar
Paul McCarthy committed
31
    minb, maxb = surf.bounds
Paul McCarthy's avatar
Paul McCarthy committed
32
33
34
35
36

    assert surf.name                  == 'example'
    assert surf.dataSource            == testfile
    assert tuple(surf.vertices.shape) == (642,  3)
    assert tuple(surf.indices .shape) == (1280, 3)
Paul McCarthy's avatar
Paul McCarthy committed
37
    assert isinstance(surf.getMeta(testfile), nib.gifti.GiftiImage)
Paul McCarthy's avatar
Paul McCarthy committed
38
39
40
41
42

    assert np.all(np.isclose(minbounds, minb))
    assert np.all(np.isclose(maxbounds, maxb))


Paul McCarthy's avatar
Paul McCarthy committed
43
44
45
46
47
48
49
def test_GiftiMesh_create_loadAll():

    testdir  = op.join(op.dirname(__file__), 'testdata')
    testfile = op.join(testdir, 'example.surf.gii')

    with tempdir() as td:

50
51
52
        vertSets = [op.join(td, 'prefix.L.1.space.surf.gii'),
                    op.join(td, 'prefix.L.2.space.surf.gii'),
                    op.join(td, 'prefix.L.3.space.surf.gii')]
Paul McCarthy's avatar
Paul McCarthy committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

        for vs in vertSets:
            shutil.copy(testfile, vs)

        mesh = gifti.GiftiMesh(vertSets[0], loadAll=True)

        assert mesh.selectedVertices() == vertSets[0]

        mesh.vertices = vertSets[1]
        assert mesh.selectedVertices() == vertSets[1]

        mesh.vertices = vertSets[2]
        assert mesh.selectedVertices() == vertSets[2]


def test_loadGiftiMesh():
Paul McCarthy's avatar
Paul McCarthy committed
69
70
71
72

    testdir  = op.join(op.dirname(__file__), 'testdata')
    testfile = op.join(testdir, 'example.surf.gii')

Paul McCarthy's avatar
Paul McCarthy committed
73
    gimg, idxs, verts, _ = gifti.loadGiftiMesh(testfile)
Paul McCarthy's avatar
Paul McCarthy committed
74
75

    assert isinstance(gimg, nib.gifti.GiftiImage)
Paul McCarthy's avatar
Paul McCarthy committed
76
77
78
    assert len(verts)            == 1
    assert tuple(verts[0].shape) == (642,  3)
    assert tuple(idxs.shape)     == (1280, 3)
Paul McCarthy's avatar
Paul McCarthy committed
79
80
81
82
83
84
85
86

    badfiles = glob.glob(op.join(testdir, 'example_bad*surf.gii'))

    for bf in badfiles:
        with pytest.raises(Exception):
            gifti.loadGiftiSurface(bf)


Paul McCarthy's avatar
Paul McCarthy committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
def test_loadVertices():

    testdir  = op.join(op.dirname(__file__), 'testdata')
    testfile = op.join(testdir, 'example.surf.gii')

    with tempdir():

        mesh = gifti.GiftiMesh(testfile)

        shutil.copy(testfile, 'example2.surf.gii')


        verts  = mesh.vertices
        verts2 = verts * 2

        np.savetxt('verts.txt', verts2)

        assert np.all(np.isclose(mesh.loadVertices('example2.surf.gii'), verts))
        assert np.all(np.isclose(mesh.loadVertices('verts.txt')        , verts2))


Paul McCarthy's avatar
Paul McCarthy committed
108
109
def test_GiftiMesh_loadVertexData():

Paul McCarthy's avatar
Paul McCarthy committed
110
111
    testdir   = op.join(op.dirname(__file__), 'testdata')
    surffile  = op.join(testdir, 'example.surf.gii')
Paul McCarthy's avatar
Paul McCarthy committed
112

Paul McCarthy's avatar
Paul McCarthy committed
113
114
115
    shapefile = op.join(testdir, 'example.shape.gii')
    txtfile   = op.join(testdir, 'test_mesh_data.txt')
    memdata   = np.random.randint(1, 10, 642)
Paul McCarthy's avatar
Paul McCarthy committed
116

Paul McCarthy's avatar
Paul McCarthy committed
117
    # load from .gii file
Paul McCarthy's avatar
Paul McCarthy committed
118
119
    surf = gifti.GiftiMesh(surffile)
    assert surf.loadVertexData(shapefile).shape == (642, 1)
Paul McCarthy's avatar
Paul McCarthy committed
120
121

    # load from .txt file
Paul McCarthy's avatar
Paul McCarthy committed
122
    assert surf.loadVertexData(txtfile).shape == (642, 1)
Paul McCarthy's avatar
Paul McCarthy committed
123

Paul McCarthy's avatar
Paul McCarthy committed
124
    # add from memory
Paul McCarthy's avatar
Paul McCarthy committed
125
    assert np.all(np.isclose(surf.addVertexData('inmemdata', memdata), memdata.reshape(-1, 1)))
Paul McCarthy's avatar
Paul McCarthy committed
126
127

    # check cached
Paul McCarthy's avatar
Paul McCarthy committed
128
129
130
    assert surf.getVertexData(shapefile)  .shape == (642, 1)
    assert surf.getVertexData(txtfile)    .shape == (642, 1)
    assert surf.getVertexData('inmemdata').shape == (642, 1)
Paul McCarthy's avatar
Paul McCarthy committed
131
132
133


def test_loadGiftiVertexData():
Paul McCarthy's avatar
Paul McCarthy committed
134

Paul McCarthy's avatar
Paul McCarthy committed
135
136
137
138
139
140
141
142
143
144
145
146
    testdir = op.join(op.dirname(__file__), 'testdata')
    surffiles = glob.glob(op.join(testdir, 'example*surf.gii'))
    for sf in surffiles:
        with pytest.raises(Exception):
            gifti.loadGiftiVertexData(sf)

    ex3Dfile  = op.join(testdir, 'example.shape.gii')
    ex4Dfile  = op.join(testdir, 'example4D.shape.gii')
    ex4D2file = op.join(testdir, 'example4D_multiple_darrays.shape.gii')

    gimg, data = gifti.loadGiftiVertexData(ex3Dfile)
    assert isinstance(gimg, nib.gifti.GiftiImage)
Paul McCarthy's avatar
Paul McCarthy committed
147
148
    assert tuple(data.shape) == (642, 1)

Paul McCarthy's avatar
Paul McCarthy committed
149
150
151
152
153
154
    gimg, data = gifti.loadGiftiVertexData(ex4Dfile)
    assert isinstance(gimg, nib.gifti.GiftiImage)
    assert tuple(data.shape) == (642, 10)

    gimg, data = gifti.loadGiftiVertexData(ex4D2file)
    assert isinstance(gimg, nib.gifti.GiftiImage)
Paul McCarthy's avatar
Paul McCarthy committed
155
    assert tuple(data.shape) == (642, 10)
Paul McCarthy's avatar
Paul McCarthy committed
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
hcp_listing = [
    'subject.L.ArealDistortion_FS.32k_fs_LR.shape.gii',
    'subject.L.ArealDistortion_MSMSulc.32k_fs_LR.shape.gii',
    'subject.L.BA.32k_fs_LR.label.gii',
    'subject.L.MyelinMap.32k_fs_LR.func.gii',
    'subject.L.MyelinMap_BC.32k_fs_LR.func.gii',
    'subject.L.SmoothedMyelinMap.32k_fs_LR.func.gii',
    'subject.L.SmoothedMyelinMap_BC.32k_fs_LR.func.gii',
    'subject.L.aparc.32k_fs_LR.label.gii',
    'subject.L.aparc.a2009s.32k_fs_LR.label.gii',
    'subject.L.atlasroi.32k_fs_LR.shape.gii',
    'subject.L.corrThickness.32k_fs_LR.shape.gii',
    'subject.L.curvature.32k_fs_LR.shape.gii',
    'subject.L.flat.32k_fs_LR.surf.gii',
    'subject.L.inflated.32k_fs_LR.surf.gii',
    'subject.L.midthickness.32k_fs_LR.surf.gii',
    'subject.L.pial.32k_fs_LR.surf.gii',
    'subject.L.sphere.32k_fs_LR.surf.gii',
    'subject.L.sulc.32k_fs_LR.shape.gii',
    'subject.L.thickness.32k_fs_LR.shape.gii',
    'subject.L.very_inflated.32k_fs_LR.surf.gii',
    'subject.L.white.32k_fs_LR.surf.gii',
    'subject.R.ArealDistortion_FS.32k_fs_LR.shape.gii',
    'subject.R.ArealDistortion_MSMSulc.32k_fs_LR.shape.gii',
    'subject.R.BA.32k_fs_LR.label.gii',
    'subject.R.MyelinMap.32k_fs_LR.func.gii',
    'subject.R.MyelinMap_BC.32k_fs_LR.func.gii',
    'subject.R.SmoothedMyelinMap.32k_fs_LR.func.gii',
    'subject.R.SmoothedMyelinMap_BC.32k_fs_LR.func.gii',
    'subject.R.aparc.32k_fs_LR.label.gii',
    'subject.R.aparc.a2009s.32k_fs_LR.label.gii',
    'subject.R.atlasroi.32k_fs_LR.shape.gii',
    'subject.R.corrThickness.32k_fs_LR.shape.gii',
    'subject.R.curvature.32k_fs_LR.shape.gii',
    'subject.R.flat.32k_fs_LR.surf.gii',
    'subject.R.inflated.32k_fs_LR.surf.gii',
    'subject.R.midthickness.32k_fs_LR.surf.gii',
    'subject.R.pial.32k_fs_LR.surf.gii',
    'subject.R.sphere.32k_fs_LR.surf.gii',
    'subject.R.sulc.32k_fs_LR.shape.gii',
    'subject.R.thickness.32k_fs_LR.shape.gii',
    'subject.R.very_inflated.32k_fs_LR.surf.gii',
    'subject.R.white.32k_fs_LR.surf.gii'
]

bids_listing = [
    'sub-001_ses-001_hemi-L_desc-corr_space-T2w_thickness.shape.gii',
    'sub-001_ses-001_hemi-L_desc-drawem_space-T2w_dparc.label.gii',
    'sub-001_ses-001_hemi-L_space-T2w_desc-medialwall_mask.shape.gii',
    'sub-001_ses-001_hemi-L_desc-smoothed_space-T2w_myelinmap.shape.gii',
    'sub-001_ses-001_hemi-L_space-T2w_curv.shape.gii',
    'sub-001_ses-001_hemi-L_space-T2w_inflated.surf.gii',
    'sub-001_ses-001_hemi-L_space-T2w_midthickness.surf.gii',
    'sub-001_ses-001_hemi-L_space-T2w_myelinmap.shape.gii',
    'sub-001_ses-001_hemi-L_space-T2w_pial.surf.gii',
    'sub-001_ses-001_hemi-L_space-T2w_sphere.surf.gii',
    'sub-001_ses-001_hemi-L_space-T2w_sulc.shape.gii',
    'sub-001_ses-001_hemi-L_space-T2w_thickness.shape.gii',
    'sub-001_ses-001_hemi-L_space-T2w_veryinflated.surf.gii',
    'sub-001_ses-001_hemi-L_space-T2w_wm.surf.gii',
    'sub-001_ses-001_hemi-R_desc-corr_space-T2w_thickness.shape.gii',
    'sub-001_ses-001_hemi-R_desc-drawem_space-T2w_dparc.label.gii',
    'sub-001_ses-001_hemi-R_space-T2w_desc-medialwall_mask.shape.gii',
    'sub-001_ses-001_hemi-R_desc-smoothed_space-T2w_myelinmap.shape.gii',
    'sub-001_ses-001_hemi-R_space-T2w_curv.shape.gii',
    'sub-001_ses-001_hemi-R_space-T2w_inflated.surf.gii',
    'sub-001_ses-001_hemi-R_space-T2w_midthickness.surf.gii',
    'sub-001_ses-001_hemi-R_space-T2w_myelinmap.shape.gii',
    'sub-001_ses-001_hemi-R_space-T2w_pial.surf.gii',
    'sub-001_ses-001_hemi-R_space-T2w_sphere.surf.gii',
    'sub-001_ses-001_hemi-R_space-T2w_sulc.shape.gii',
    'sub-001_ses-001_hemi-R_space-T2w_thickness.shape.gii',
    'sub-001_ses-001_hemi-R_space-T2w_veryinflated.surf.gii',
    'sub-001_ses-001_hemi-R_space-T2w_wm.surf.gii'
]


def  test_relatedFiles_hcp():  _test_relatedFiles(hcp_listing)
def  test_relatedFiles_bids(): _test_relatedFiles(bids_listing)
def _test_relatedFiles(listing):

    listing = list(listing)
    listing.append('badly-formed-filename.surf.gii')

    def ishemi(f, hemi):
        return ('hemi-{}'.format(hemi) in f) or \
               ('.{}.'   .format(hemi) in f)

    def issurf(f):
        return f.endswith('surf.gii')

    def isrelated(f):
        return not issurf(f)

    lsurfaces = [l for l in listing if issurf(   l) and ishemi(l, 'L')]
    lrelated  = [l for l in listing if isrelated(l) and ishemi(l, 'L')]
    rsurfaces = [l for l in listing if issurf(   l) and ishemi(l, 'R')]
    rrelated  = [l for l in listing if isrelated(l) and ishemi(l, 'R')]
Paul McCarthy's avatar
Paul McCarthy committed
255

Paul McCarthy's avatar
Paul McCarthy committed
256
    with tempdir() as td:
Paul McCarthy's avatar
Paul McCarthy committed
257

258
259
260
261
262
263
        listing   = [op.join(td, f) for f in listing]
        lsurfaces = [op.join(td, f) for f in lsurfaces]
        rsurfaces = [op.join(td, f) for f in rsurfaces]
        lrelated  = [op.join(td, f) for f in lrelated]
        rrelated  = [op.join(td, f) for f in rrelated]

Paul McCarthy's avatar
Paul McCarthy committed
264
        for l in listing:
265
            with open(l, 'wt') as f:
Paul McCarthy's avatar
Paul McCarthy committed
266
267
                f.write(l)

268
        badname = op.join(td, 'badly-formed-filename.surf.gii')
Paul McCarthy's avatar
Paul McCarthy committed
269

Paul McCarthy's avatar
Paul McCarthy committed
270
271
        assert len(gifti.relatedFiles(badname))       == 0
        assert len(gifti.relatedFiles('nonexistent')) == 0
Paul McCarthy's avatar
Paul McCarthy committed
272
273
274
275
276
277

        for s in lsurfaces:
            result = gifti.relatedFiles(s)
            assert sorted(lrelated) == sorted(result)
        for s in rsurfaces:
            result = gifti.relatedFiles(s)
Paul McCarthy's avatar
Paul McCarthy committed
278
            assert sorted(rrelated) == sorted(result)
Paul McCarthy's avatar
Paul McCarthy committed
279

280
        exp = lsurfaces
281
282
        exp = [f for f in exp if f != lsurfaces[0]]
        result = gifti.relatedFiles(lsurfaces[0],
283
                                    ftypes=[gifti.ALLOWED_EXTENSIONS[0]])
284
285
        assert sorted(exp) == sorted(result)

286

Paul McCarthy's avatar
Paul McCarthy committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
TEST_VERTS = np.array([
    [0, 0, 0],
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]])
TEST_IDXS = np.array([
    [0, 1, 2],
    [0, 3, 1],
    [0, 2, 3],
    [1, 3, 2]])

TEST_VERT_ARRAY = nib.gifti.GiftiDataArray(
    TEST_VERTS, intent='NIFTI_INTENT_POINTSET')
TEST_IDX_ARRAY  = nib.gifti.GiftiDataArray(
    TEST_IDXS, intent='NIFTI_INTENT_TRIANGLE')

def test_GiftiMesh_surface_and_data():

    data1   = np.random.randint(0, 10, len(TEST_VERTS))
    data2   = np.random.randint(0, 10, len(TEST_VERTS))
    expdata = np.vstack([data1, data2]).T
    verts   = TEST_VERT_ARRAY
    tris    = TEST_IDX_ARRAY
    data1   = nib.gifti.GiftiDataArray(data1, intent='NIFTI_INTENT_SHAPE')
    data2   = nib.gifti.GiftiDataArray(data2, intent='NIFTI_INTENT_SHAPE')
    gimg    = nib.gifti.GiftiImage(darrays=[verts, tris, data1, data2])

    with tempdir():
        fname = op.abspath('test.gii')
        gimg.to_filename(fname)
        surf = gifti.GiftiMesh(fname)

        assert np.all(surf.vertices  == TEST_VERTS)
        assert np.all(surf.indices   == TEST_IDXS)
        assert surf.vertexDataSets() == [fname]
        assert np.all(surf.getVertexData(fname) == expdata)



def test_GiftiMesh_multiple_vertices():

    tris   = TEST_IDX_ARRAY
    verts1 = TEST_VERT_ARRAY
    verts2 = nib.gifti.GiftiDataArray(
        TEST_VERTS * 5, intent='NIFTI_INTENT_POINTSET')
332
333
    verts3 = nib.gifti.GiftiDataArray(
        TEST_VERTS * 10, intent='NIFTI_INTENT_POINTSET')
Paul McCarthy's avatar
Paul McCarthy committed
334
335

    gimg  = nib.gifti.GiftiImage(darrays=[verts1, verts2, tris])
Paul McCarthy's avatar
Paul McCarthy committed
336
    gimg2 = nib.gifti.GiftiImage(darrays=[verts3, tris])
Paul McCarthy's avatar
Paul McCarthy committed
337
338

    with tempdir():
339
340
341
342
343
        fname  = op.abspath('test.gii')
        fname2 = op.abspath('test2.gii')
        gimg .to_filename(fname)
        gimg2.to_filename(fname2)

Paul McCarthy's avatar
Paul McCarthy committed
344
345
        surf  = gifti.GiftiMesh(fname)

Paul McCarthy's avatar
Paul McCarthy committed
346
347
348
349
350
        expvsets = [fname, '{}_1'.format(fname)]

        expbounds1 = np.min(verts1.data, axis=0), np.max(verts1.data, axis=0)
        expbounds2 = np.min(verts2.data, axis=0), np.max(verts2.data, axis=0)
        expbounds3 = np.min(verts3.data, axis=0), np.max(verts3.data, axis=0)
Paul McCarthy's avatar
Paul McCarthy committed
351
352
353
354

        assert np.all(surf.vertices == TEST_VERTS)
        assert np.all(surf.indices  == TEST_IDXS)
        assert  surf.vertexSets()   == expvsets
Paul McCarthy's avatar
Paul McCarthy committed
355
        assert np.all(np.isclose(surf.bounds, expbounds1))
Paul McCarthy's avatar
Paul McCarthy committed
356
357
358

        surf.vertices = expvsets[1]
        assert np.all(surf.vertices == TEST_VERTS * 5)
Paul McCarthy's avatar
Paul McCarthy committed
359
        assert np.all(np.isclose(surf.bounds, expbounds2))
360

361
362
        surf.loadVertices(fname2, select=True)
        assert np.all(surf.vertices == TEST_VERTS * 10)
Paul McCarthy's avatar
Paul McCarthy committed
363
        assert np.all(np.isclose(surf.bounds, expbounds3))
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383

def test_GiftiMesh_needsFixing():
    from . import test_mesh

    verts      = test_mesh.CUBE_VERTICES
    idxs       = test_mesh.CUBE_TRIANGLES_CW
    idxs_fixed = test_mesh.CUBE_TRIANGLES_CCW

    verts = nib.gifti.GiftiDataArray(verts, intent='NIFTI_INTENT_POINTSET')
    idxs  = nib.gifti.GiftiDataArray(idxs,  intent='NIFTI_INTENT_TRIANGLE')
    gimg  = nib.gifti.GiftiImage(darrays=[verts, idxs])

    with tempdir():
        fname = op.abspath('test.gii')
        gimg.to_filename(fname)

        surf = gifti.GiftiMesh(fname, fixWinding=True)

        assert np.all(np.isclose(surf.indices, idxs_fixed))
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401


def test_loadGiftiMesh_onetriangle():
    verts = np.array([[0, 0, 0], [1, 1, 1], [0, 1, 0]])
    idxs  = np.array([[0, 1, 2]])
    verts = nib.gifti.GiftiDataArray(verts, intent='NIFTI_INTENT_POINTSET')
    idxs  = nib.gifti.GiftiDataArray(idxs,  intent='NIFTI_INTENT_TRIANGLE')
    gimg  = nib.gifti.GiftiImage(darrays=[verts, idxs])

    with tempdir():
        fname = op.abspath('test.gii')
        gimg.to_filename(fname)

        gimg, tris, verts, _ = gifti.loadGiftiMesh('test.gii')
        verts = verts[0]

        assert verts.shape == (3, 3)
        assert tris.shape  == (1, 3)