Skip to content

Commit f5de829

Browse files
committed
Merge pull request #6 from effigies/enh/cifti2
RF: Remove externals/inflection; BF: Fix Cifti2Label._to_xml_element
2 parents 9f22915 + 3ef28f5 commit f5de829

File tree

5 files changed

+123
-446
lines changed

5 files changed

+123
-446
lines changed

nibabel/cifti2/cifti2.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
'''
2020
from __future__ import division, print_function, absolute_import
2121

22+
import re
2223
import numpy as np
2324

2425
from .. import xmlutils as xml
25-
from ..externals import inflection
2626
from ..externals.six import string_types
2727
from ..externals.six.moves import reduce
2828
from ..filebasedimages import FileBasedHeader, FileBasedImage
@@ -93,6 +93,12 @@ def _value_or_make_klass(val, klass):
9393
return _value_if_klass(val, klass)
9494

9595

96+
def _underscore(string):
97+
""" Convert a string from camelcase to underscored """
98+
string = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', string)
99+
return re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', string).lower()
100+
101+
96102
class Cifti2MetaData(xml.XmlSerializable):
97103
""" A list of key-value pairs stored in the list self.data """
98104

@@ -207,17 +213,17 @@ def rgba(self):
207213
return (self.red, self.green, self.blue, self.alpha)
208214

209215
def _to_xml_element(self):
210-
lab = xml.SubElement(labeltable, 'Label')
211-
lab.attrib['Key'] = str(ele.key)
212-
lab.text = str(ele.label)
213-
if ele.red is not None:
214-
lab.attrib['Red'] = str(ele.red)
215-
if ele.green is not None:
216-
lab.attrib['Green'] = str(ele.green)
217-
if ele.blue is not None:
218-
lab.attrib['Blue'] = str(ele.blue)
219-
if ele.alpha is not None:
220-
lab.attrib['Alpha'] = str(ele.alpha)
216+
lab = xml.Element('Label')
217+
lab.attrib['Key'] = str(self.key)
218+
lab.text = str(self.label)
219+
if self.red is not None:
220+
lab.attrib['Red'] = str(self.red)
221+
if self.green is not None:
222+
lab.attrib['Green'] = str(self.green)
223+
if self.blue is not None:
224+
lab.attrib['Blue'] = str(self.blue)
225+
if self.alpha is not None:
226+
lab.attrib['Alpha'] = str(self.alpha)
221227
return lab
222228

223229

@@ -466,7 +472,7 @@ def _to_xml_element(self):
466472

467473
for key in ['IndexOffset', 'IndexCount', 'ModelType', 'BrainStructure',
468474
'SurfaceNumberOfVertices']:
469-
attr = inflection.underscore(key)
475+
attr = _underscore(key)
470476
value = getattr(self, attr)
471477
if value is not None:
472478
brain_model.attrib[key] = str(value)
@@ -598,7 +604,7 @@ def _to_xml_element(self):
598604
mat_ind_map.attrib['AppliesToMatrixDimension'] = ','.join(dims_as_strings)
599605
for key in ['IndicesMapToDataType', 'NumberOfSeriesPoints', 'SeriesExponent',
600606
'SeriesStart', 'SeriesStep', 'SeriesUnit']:
601-
attr = inflection.underscore(key)
607+
attr = _underscore(key)
602608
value = getattr(self, attr)
603609
if value is not None:
604610
mat_ind_map.attrib[key] = str(value)

nibabel/cifti2/parse_cifti2_fast.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
Cifti2MatrixIndicesMap, Cifti2NamedMap, Cifti2Parcel,
1919
Cifti2Surface, Cifti2TransformationMatrixVoxelIndicesIJKtoXYZ,
2020
Cifti2Vertices, Cifti2Volume, CIFTI_BrainStructures,
21-
CIFTI_MODEL_TYPES)
21+
CIFTI_MODEL_TYPES, _underscore)
2222
from .. import xmlutils as xml
23-
from ..externals import inflection
2423
from ..externals.six import BytesIO
2524
from ..externals.six.moves import reduce
2625
from ..nifti1 import Nifti1Extension, extension_codes, intent_codes
@@ -182,8 +181,7 @@ def StartElementHandler(self, name, attrs):
182181
("SeriesStep", float),
183182
("SeriesUnit", str)]:
184183
if key in attrs:
185-
attr = inflection.underscore(key)
186-
setattr(mim, attr, dtype(attrs[key]))
184+
setattr(mim, _underscore(key), dtype(attrs[key]))
187185
matrix = self.struct_state[-1]
188186
assert isinstance(matrix, Cifti2Matrix)
189187
matrix.add_cifti_matrix_indices_map(mim)
@@ -205,6 +203,7 @@ def StartElementHandler(self, name, attrs):
205203
assert isinstance(named_map, Cifti2NamedMap)
206204
self.fsm_state.append('LabelTable')
207205
self.struct_state.append(lata)
206+
named_map.label_table = lata
208207

209208
elif name == 'Label':
210209
lata = self.struct_state[-1]
@@ -296,8 +295,7 @@ def StartElementHandler(self, name, attrs):
296295
("BrainStructure", str),
297296
("SurfaceNumberOfVertices", int)]:
298297
if key in attrs:
299-
attr = inflection.underscore(key)
300-
setattr(model, attr, dtype(attrs[key]))
298+
setattr(model, _underscore(key), dtype(attrs[key]))
301299
assert model.brain_structure in CIFTI_BrainStructures
302300
assert model.model_type in CIFTI_MODEL_TYPES
303301
mim.add_cifti_brain_model(model)

nibabel/cifti2/tests/test_cifti2.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,20 @@ def test_cifti2_metadata():
4040
md.remove_metadata(['b', 'bval'])
4141
assert_equal(len(md.data), 0)
4242
assert_equal(md.to_xml().decode('utf-8'), '<MetaData />')
43+
44+
45+
def test_underscoring():
46+
# Pairs taken from inflection tests
47+
# https://github.com/jpvanhal/inflection/blob/663982e/test_inflection.py#L113-L125
48+
pairs = (("Product", "product"),
49+
("SpecialGuest", "special_guest"),
50+
("ApplicationController", "application_controller"),
51+
("Area51Controller", "area51_controller"),
52+
("HTMLTidy", "html_tidy"),
53+
("HTMLTidyGenerator", "html_tidy_generator"),
54+
("FreeBSD", "free_bsd"),
55+
("HTML", "html"),
56+
)
57+
58+
for camel, underscored in pairs:
59+
assert_equal(ci.cifti2._underscore(camel), underscored)

nibabel/cifti2/tests/test_cifti2io.py

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,85 @@ def test_readwritedata():
7575
img = nib.load(name)
7676
nib.save(img, 'test.nii')
7777
img2 = nib.load('test.nii')
78-
assert_equal(len(img.header.matrix.mims), len(img2.header.matrix.mims))
79-
assert_array_almost_equal(img.data,
80-
img2.data)
78+
assert_equal(len(img.header.matrix.mims),
79+
len(img2.header.matrix.mims))
80+
# Order should be preserved in load/save
81+
for mim1, mim2 in zip(img.header.matrix.mims,
82+
img2.header.matrix.mims):
83+
assert_equal(len(mim1.named_maps), len(mim2.named_maps))
84+
for map1, map2 in zip(mim1.named_maps, mim2.named_maps):
85+
assert_equal(map1.map_name, map2.map_name)
86+
if map1.label_table is None:
87+
assert_true(map2.label_table is None)
88+
else:
89+
assert_equal(len(map1.label_table.labels),
90+
len(map2.label_table.labels))
91+
assert_array_almost_equal(img.data, img2.data)
92+
93+
94+
def test_cifti2types():
95+
"""Check that we instantiate Cifti2 classes correctly, and that our
96+
test files exercise all classes"""
97+
counter = {ci.Cifti2LabelTable: 0,
98+
ci.Cifti2Label: 0,
99+
ci.Cifti2NamedMap: 0,
100+
ci.Cifti2Surface: 0,
101+
ci.Cifti2VoxelIndicesIJK: 0,
102+
ci.Cifti2Vertices: 0,
103+
ci.Cifti2Parcel: 0,
104+
ci.Cifti2TransformationMatrixVoxelIndicesIJKtoXYZ: 0,
105+
ci.Cifti2Volume: 0,
106+
ci.Cifti2VertexIndices: 0,
107+
ci.Cifti2BrainModel: 0,
108+
ci.Cifti2MatrixIndicesMap: 0,
109+
}
110+
for name in datafiles:
111+
hdr = nib.load(name).header
112+
# Matrix and MetaData aren't conditional, so don't bother counting
113+
assert_true(isinstance(hdr.matrix, ci.Cifti2Matrix))
114+
assert_true(isinstance(hdr.matrix.metadata, ci.Cifti2MetaData))
115+
assert_true(isinstance(hdr.matrix.mims, list))
116+
for mim in hdr.matrix.mims:
117+
assert_true(isinstance(mim, ci.Cifti2MatrixIndicesMap))
118+
counter[ci.Cifti2MatrixIndicesMap] += 1
119+
assert_true(isinstance(mim.brain_models, list))
120+
for bm in mim.brain_models:
121+
assert_true(isinstance(bm, ci.Cifti2BrainModel))
122+
counter[ci.Cifti2BrainModel] += 1
123+
if isinstance(bm.vertex_indices, ci.Cifti2VertexIndices):
124+
counter[ci.Cifti2VertexIndices] += 1
125+
if isinstance(bm.voxel_indices_ijk, ci.Cifti2VoxelIndicesIJK):
126+
counter[ci.Cifti2VoxelIndicesIJK] += 1
127+
assert_true(isinstance(mim.named_maps, list))
128+
for nm in mim.named_maps:
129+
assert_true(isinstance(nm, ci.Cifti2NamedMap))
130+
counter[ci.Cifti2NamedMap] += 1
131+
assert_true(isinstance(nm.metadata, ci.Cifti2MetaData))
132+
if isinstance(nm.label_table, ci.Cifti2LabelTable):
133+
counter[ci.Cifti2LabelTable] += 1
134+
assert_true(isinstance(nm.label_table.labels, list))
135+
for label in nm.label_table.labels:
136+
assert_true(isinstance(label, ci.Cifti2Label))
137+
counter[ci.Cifti2Label] += 1
138+
assert_true(isinstance(mim.parcels, list))
139+
for parc in mim.parcels:
140+
assert_true(isinstance(parc, ci.Cifti2Parcel))
141+
counter[ci.Cifti2Parcel] += 1
142+
if isinstance(parc.voxel_indices_ijk,
143+
ci.Cifti2VoxelIndicesIJK):
144+
counter[ci.Cifti2VoxelIndicesIJK] += 1
145+
assert_true(isinstance(parc.vertices, list))
146+
for vtcs in parc.vertices:
147+
assert_true(isinstance(vtcs, ci.Cifti2Vertices))
148+
counter[ci.Cifti2Vertices] += 1
149+
assert_true(isinstance(mim.surfaces, list))
150+
for surf in mim.surfaces:
151+
assert_true(isinstance(surf, ci.Cifti2Surface))
152+
counter[ci.Cifti2Surface] += 1
153+
if isinstance(mim.volume, ci.Cifti2Volume):
154+
counter[ci.Cifti2Volume] += 1
155+
if isinstance(mim.volume.transformation_matrix_voxel_indices_ijk_to_xyz,
156+
ci.Cifti2TransformationMatrixVoxelIndicesIJKtoXYZ):
157+
counter[ci.Cifti2TransformationMatrixVoxelIndicesIJKtoXYZ] += 1
158+
for klass, count in counter.items():
159+
assert_true(count > 0, "No exercise of " + klass.__name__)

0 commit comments

Comments
 (0)