Skip to content

Commit 6591cf4

Browse files
authored
Merge pull request #16 from MichielCottaar/enh/cifti2-tests
additional tests covering the various CIFTI file types
2 parents 0964d6d + 62c56fe commit 6591cf4

File tree

3 files changed

+609
-2
lines changed

3 files changed

+609
-2
lines changed

nibabel/cifti2/parse_cifti2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ def flush_chardata(self):
547547
# conversion to numpy array
548548
c = BytesIO(data.strip().encode('utf-8'))
549549
parent = self.struct_state[-1]
550-
parent.voxel_indices_ijk.extend(np.genfromtxt(c, dtype=np.int))
550+
parent.voxel_indices_ijk.extend(np.genfromtxt(c, dtype=np.int).reshape(-1, 3))
551551
c.close()
552552

553553
elif self.write_to == 'VertexIndices':
@@ -566,7 +566,7 @@ def flush_chardata(self):
566566

567567
elif self.write_to == 'Label':
568568
label = self.struct_state[-1]
569-
label.label = data.strip().encode('utf-8')
569+
label.label = data.strip()
570570

571571
elif self.write_to == 'MapName':
572572
named_map = self.struct_state[-1]

nibabel/cifti2/tests/test_cifti2io.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,207 @@ def test_cifti2types():
208208
assert_true(count > 0, "No exercise of " + klass.__name__)
209209

210210

211+
@needs_nibabel_data('nitest-cifti2')
212+
def test_read_geometry():
213+
img = ci.Cifti2Image.from_filename(DATA_FILE6)
214+
geometry_mapping = img.header.matrix.get_index_map(1)
215+
216+
# For every brain model in ones.dscalar.nii defines:
217+
# brain structure name, number of grayordinates, first vertex or voxel, last vertex or voxel
218+
expected_geometry = [('CIFTI_STRUCTURE_CORTEX_LEFT', 29696, 0, 32491),
219+
('CIFTI_STRUCTURE_CORTEX_RIGHT', 29716, 0, 32491),
220+
('CIFTI_STRUCTURE_ACCUMBENS_LEFT', 135, [49, 66, 28], [48, 72, 35]),
221+
('CIFTI_STRUCTURE_ACCUMBENS_RIGHT', 140, [40, 66, 29], [43, 66, 36]),
222+
('CIFTI_STRUCTURE_AMYGDALA_LEFT', 315, [55, 61, 21], [56, 58, 31]),
223+
('CIFTI_STRUCTURE_AMYGDALA_RIGHT', 332, [34, 62, 20], [36, 61, 31]),
224+
('CIFTI_STRUCTURE_BRAIN_STEM', 3472, [42, 41, 0], [46, 50, 36]),
225+
('CIFTI_STRUCTURE_CAUDATE_LEFT', 728, [50, 72, 32], [53, 60, 49]),
226+
('CIFTI_STRUCTURE_CAUDATE_RIGHT', 755, [40, 68, 33], [37, 62, 49]),
227+
('CIFTI_STRUCTURE_CEREBELLUM_LEFT', 8709, [49, 35, 4], [46, 37, 37]),
228+
('CIFTI_STRUCTURE_CEREBELLUM_RIGHT', 9144, [38, 35, 4], [44, 38, 36]),
229+
('CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_LEFT', 706, [52, 53, 26], [56, 49, 35]),
230+
('CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_RIGHT', 712, [39, 54, 26], [35, 49, 36]),
231+
('CIFTI_STRUCTURE_HIPPOCAMPUS_LEFT', 764, [55, 60, 21], [54, 44, 39]),
232+
('CIFTI_STRUCTURE_HIPPOCAMPUS_RIGHT', 795, [33, 60, 21], [38, 45, 39]),
233+
('CIFTI_STRUCTURE_PALLIDUM_LEFT', 297, [56, 59, 32], [55, 61, 39]),
234+
('CIFTI_STRUCTURE_PALLIDUM_RIGHT', 260, [36, 62, 32], [35, 62, 39]),
235+
('CIFTI_STRUCTURE_PUTAMEN_LEFT', 1060, [51, 66, 28], [58, 64, 43]),
236+
('CIFTI_STRUCTURE_PUTAMEN_RIGHT', 1010, [34, 66, 29], [31, 62, 43]),
237+
('CIFTI_STRUCTURE_THALAMUS_LEFT', 1288, [55, 47, 33], [52, 53, 46]),
238+
('CIFTI_STRUCTURE_THALAMUS_RIGHT', 1248, [32, 47, 34], [38, 55, 46])]
239+
current_index = 0
240+
for from_file, expected in zip(geometry_mapping.brain_models, expected_geometry):
241+
assert_true(from_file.model_type in ("CIFTI_MODEL_TYPE_SURFACE", "CIFTI_MODEL_TYPE_VOXELS"))
242+
assert_equal(from_file.brain_structure, expected[0])
243+
assert_equal(from_file.index_offset, current_index)
244+
assert_equal(from_file.index_count, expected[1])
245+
current_index += from_file.index_count
246+
247+
if from_file.model_type == 'CIFTI_MODEL_TYPE_SURFACE':
248+
assert_equal(from_file.voxel_indices_ijk, None)
249+
assert_equal(len(from_file.vertex_indices), expected[1])
250+
assert_equal(from_file.vertex_indices[0], expected[2])
251+
assert_equal(from_file.vertex_indices[-1], expected[3])
252+
assert_equal(from_file.surface_number_of_vertices, 32492)
253+
else:
254+
assert_equal(from_file.vertex_indices, None)
255+
assert_equal(from_file.surface_number_of_vertices, None)
256+
assert_equal(len(from_file.voxel_indices_ijk), expected[1])
257+
assert_equal(from_file.voxel_indices_ijk[0], expected[2])
258+
assert_equal(from_file.voxel_indices_ijk[-1], expected[3])
259+
assert_equal(current_index, img.shape[1])
260+
261+
expected_affine = [[-2, 0, 0, 90],
262+
[ 0, 2, 0, -126],
263+
[ 0, 0, 2, -72],
264+
[ 0, 0, 0, 1]]
265+
expected_dimensions = (91, 109, 91)
266+
assert_true((geometry_mapping.volume.transformation_matrix_voxel_indices_ijk_to_xyz.matrix ==
267+
expected_affine).all())
268+
assert_equal(geometry_mapping.volume.volume_dimensions, expected_dimensions)
269+
270+
271+
@needs_nibabel_data('nitest-cifti2')
272+
def test_read_parcels():
273+
img = ci.Cifti2Image.from_filename(DATA_FILE4)
274+
parcel_mapping = img.header.matrix.get_index_map(1)
275+
276+
expected_parcels = [('MEDIAL.WALL', ((719, 20, 28550), (810, 21, 28631))),
277+
('BA2_FRB08', ((516, 6757, 17888), (461, 6757, 17887))),
278+
('BA1_FRB08', ((211, 5029, 17974), (214, 3433, 17934))),
279+
('BA3b_FRB08', ((444, 3436, 18065), (397, 3436, 18065))),
280+
('BA4p_FRB08', ((344, 3445, 18164), (371, 3443, 18175))),
281+
('BA3a_FRB08', ((290, 3441, 18140), (289, 3440, 18140))),
282+
('BA4a_FRB08', ((471, 3446, 18181), (455, 3446, 19759))),
283+
('BA6_FRB08', ((1457, 2, 30951), (1400, 2, 30951))),
284+
('BA17_V1_FRB08', ((629, 23155, 25785), (635, 23155, 25759))),
285+
('BA45_FRB08', ((245, 10100, 18774), (214, 10103, 18907))),
286+
('BA44_FRB08', ((226, 10118, 19240), (273, 10119, 19270))),
287+
('hOc5_MT_FRB08', ((104, 15019, 23329), (80, 15023, 23376))),
288+
('BA18_V2_FRB08', ((702, 95, 25902), (651, 98, 25903))),
289+
('V3A_SHM07', ((82, 4, 25050), (82, 4, 25050))),
290+
('V3B_SHM07', ((121, 13398, 23303), (121, 13398, 23303))),
291+
('LO1_KPO10', ((54, 15007, 23543), (54, 15007, 23543))),
292+
('LO2_KPO10', ((79, 15013, 23636), (79, 15013, 23636))),
293+
('PITd_KPO10', ((53, 15018, 23769), (65, 15018, 23769))),
294+
('PITv_KPO10', ((72, 23480, 23974), (72, 23480, 23974))),
295+
('OP1_BSW08', ((470, 8421, 18790), (470, 8421, 18790))),
296+
('OP2_BSW08', ((67, 10, 31060), (67, 10, 31060))),
297+
('OP3_BSW08', ((119, 10137, 18652), (119, 10137, 18652))),
298+
('OP4_BSW08', ((191, 16613, 19429), (192, 16613, 19429))),
299+
('IPS1_SHM07', ((54, 11775, 14496), (54, 11775, 14496))),
300+
('IPS2_SHM07', ((71, 11771, 14587), (71, 11771, 14587))),
301+
('IPS3_SHM07', ((114, 11764, 14783), (114, 11764, 14783))),
302+
('IPS4_SHM07', ((101, 11891, 12653), (101, 11891, 12653))),
303+
('V7_SHM07', ((140, 11779, 14002), (140, 11779, 14002))),
304+
('V4v_SHM07', ((81, 23815, 24557), (90, 23815, 24557))),
305+
('V3d_KPO10', ((90, 23143, 25192), (115, 23143, 25192))),
306+
('14c_OFP03', ((22, 19851, 21311), (22, 19851, 21311))),
307+
('13a_OFP03', ((20, 20963, 21154), (20, 20963, 21154))),
308+
('47s_OFP03', ((211, 10182, 20343), (211, 10182, 20343))),
309+
('14r_OFP03', ((54, 21187, 21324), (54, 21187, 21324))),
310+
('13m_OFP03', ((103, 20721, 21075), (103, 20721, 21075))),
311+
('13l_OFP03', ((101, 20466, 20789), (101, 20466, 20789))),
312+
('32pl_OFP03', ((14, 19847, 21409), (14, 19847, 21409))),
313+
('25_OFP03', ((8, 19844, 27750), (8, 19844, 27750))),
314+
('47m_OFP03', ((200, 10174, 20522), (200, 10174, 20522))),
315+
('47l_OFP03', ((142, 10164, 19969), (160, 10164, 19969))),
316+
('Iai_OFP03', ((153, 10188, 20199), (153, 10188, 20199))),
317+
('10r_OFP03', ((138, 19811, 28267), (138, 19811, 28267))),
318+
('11m_OFP03', ((92, 20850, 21165), (92, 20850, 21165))),
319+
('11l_OFP03', ((200, 20275, 21029), (200, 20275, 21029))),
320+
('47r_OFP03', ((259, 10094, 20535), (259, 10094, 20535))),
321+
('10m_OFP03', ((102, 19825, 21411), (102, 19825, 21411))),
322+
('Iam_OFP03', ((15, 20346, 20608), (15, 20346, 20608))),
323+
('Ial_OFP03', ((89, 10194, 11128), (89, 10194, 11128))),
324+
('24_OFP03', ((39, 19830, 28279), (36, 19830, 28279))),
325+
('Iapm_OFP03', ((7, 20200, 20299), (7, 20200, 20299))),
326+
('10p_OFP03', ((480, 19780, 28640), (480, 19780, 28640))),
327+
('V6_PHG06', ((72, 12233, 12869), (72, 12233, 12869))),
328+
('ER_FRB08', ((103, 21514, 26470), (103, 21514, 26470))),
329+
('13b_OFP03', ((60, 21042, 21194), (71, 21040, 21216)))]
330+
331+
assert_equal(img.shape[1], len(expected_parcels))
332+
assert_equal(len(list(parcel_mapping.parcels)), len(expected_parcels))
333+
334+
for (name, expected_surfaces), parcel in zip(expected_parcels, parcel_mapping.parcels):
335+
assert_equal(parcel.name, name)
336+
assert_equal(len(parcel.vertices), 2)
337+
for vertices, orientation, (length, first_element, last_element) in zip(parcel.vertices, ('LEFT', 'RIGHT'),
338+
expected_surfaces):
339+
assert_equal(len(vertices), length)
340+
assert_equal(vertices[0], first_element)
341+
assert_equal(vertices[-1], last_element)
342+
assert_equal(vertices.brain_structure, 'CIFTI_STRUCTURE_CORTEX_%s' % orientation)
343+
344+
345+
@needs_nibabel_data('nitest-cifti2')
346+
def test_read_scalar():
347+
img = ci.Cifti2Image.from_filename(DATA_FILE2)
348+
scalar_mapping = img.header.matrix.get_index_map(0)
349+
350+
expected_names = ('MyelinMap_BC_decurv', 'corrThickness')
351+
assert_equal(img.shape[0], len(expected_names))
352+
assert_equal(len(list(scalar_mapping.named_maps)), len(expected_names))
353+
354+
expected_meta = [('PaletteColorMapping', '<PaletteColorMapping Version="1">\n <ScaleMo')]
355+
for scalar, name in zip(scalar_mapping.named_maps, expected_names):
356+
assert_equal(scalar.map_name, name)
357+
358+
assert_equal(len(scalar.metadata), len(expected_meta))
359+
print(expected_meta[0], scalar.metadata.data.keys())
360+
for key, value in expected_meta:
361+
assert_true(key in scalar.metadata.data.keys())
362+
assert_equal(scalar.metadata[key][:len(value)], value)
363+
364+
assert_equal(scalar.label_table, None, ".dscalar file should not define a label table")
365+
366+
367+
@needs_nibabel_data('nitest-cifti2')
368+
def test_read_series():
369+
img = ci.Cifti2Image.from_filename(DATA_FILE4)
370+
series_mapping = img.header.matrix.get_index_map(0)
371+
assert_equal(series_mapping.series_start, 0.)
372+
assert_equal(series_mapping.series_step, 1.)
373+
assert_equal(series_mapping.series_unit, 'SECOND')
374+
assert_equal(series_mapping.series_exponent, 0.)
375+
assert_equal(series_mapping.number_of_series_points, img.shape[0])
376+
377+
378+
@needs_nibabel_data('nitest-cifti2')
379+
def test_read_labels():
380+
img = ci.Cifti2Image.from_filename(DATA_FILE5)
381+
label_mapping = img.header.matrix.get_index_map(0)
382+
383+
expected_names = ['Composite Parcellation-lh (FRB08_OFP03_retinotopic)',
384+
'Brodmann lh (from colin.R via pals_R-to-fs_LR)',
385+
'MEDIAL WALL lh (fs_LR)']
386+
assert_equal(img.shape[0], len(expected_names))
387+
assert_equal(len(list(label_mapping.named_maps)), len(expected_names))
388+
389+
some_expected_labels = {0: ('???', (0.667, 0.667, 0.667, 0.0)),
390+
1: ('MEDIAL.WALL', (0.075, 0.075, 0.075, 1.0)),
391+
2: ('BA2_FRB08', (0.467, 0.459, 0.055, 1.0)),
392+
3: ('BA1_FRB08', (0.475, 0.722, 0.859, 1.0)),
393+
4: ('BA3b_FRB08', (0.855, 0.902, 0.286, 1.0)),
394+
5: ('BA4p_FRB08', (0.902, 0.573, 0.122, 1.0)),
395+
89: ('36_B05', (0.467, 0.0, 0.129, 1.0)),
396+
90: ('35_B05', (0.467, 0.067, 0.067, 1.0)),
397+
91: ('28_B05', (0.467, 0.337, 0.271, 1.0)),
398+
92: ('29_B05', (0.267, 0.0, 0.529, 1.0)),
399+
93: ('26_B05', (0.757, 0.2, 0.227, 1.0)),
400+
94: ('33_B05', (0.239, 0.082, 0.373, 1.0)),
401+
95: ('13b_OFP03', (1.0, 1.0, 0.0, 1.0))}
402+
403+
for named_map, name in zip(label_mapping.named_maps, expected_names):
404+
assert_equal(named_map.map_name, name)
405+
assert_equal(len(named_map.metadata), 0)
406+
assert_equal(len(named_map.label_table), 96)
407+
for index, (label, rgba) in some_expected_labels.items():
408+
assert_equal(named_map.label_table[index].label, label)
409+
assert_equal(named_map.label_table[index].rgba, rgba)
410+
411+
211412
class TestCifti2SingleHeader(TestNifti2SingleHeader):
212413
header_class = _Cifti2AsNiftiHeader
213414
_pixdim_message = 'pixdim[1,2,3] should be zero or positive'

0 commit comments

Comments
 (0)