Skip to content

Commit 2262b73

Browse files
committed
enh: cifti writer enabled
1 parent e80b4bc commit 2262b73

File tree

1 file changed

+159
-17
lines changed

1 file changed

+159
-17
lines changed

nibabel/cifti2.py

Lines changed: 159 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
import numpy as np
2626

27-
DEBUG_PRINT = True
27+
DEBUG_PRINT = False
2828

2929
CIFTI_MAP_TYPES = ('CIFTI_INDEX_TYPE_BRAIN_MODELS',
3030
'CIFTI_INDEX_TYPE_PARCELS',
@@ -100,17 +100,19 @@ def get_metadata(self):
100100
self.data_as_dict[ele.name] = ele.value
101101
return self.data_as_dict
102102

103-
def to_xml(self):
103+
def to_xml(self, prefix='', indent=' '):
104104
if len(self.data) == 0:
105-
return "<MetaData/>\n"
106-
res = "<MetaData>\n"
105+
return ''
106+
res = "%s<MetaData>\n" % prefix
107+
preindent = prefix + indent
107108
for ele in self.data:
108-
nvpair = """<MD>
109-
\t<Name><![CDATA[%s]]></Name>
110-
\t<Value><![CDATA[%s]]></Value>
111-
</MD>\n""" % (ele.name, ele.value)
112-
res = res + nvpair
113-
res = res + "</MetaData>\n"
109+
nvpair = """%s<MD>
110+
%s<Name>%s</Name>
111+
%s<Value>%s</Value>
112+
%s</MD>\n""" % (preindent, preindent + indent, ele.name, preindent + indent,
113+
ele.value, preindent)
114+
res += nvpair
115+
res += "%s</MetaData>\n" % prefix
114116
return res
115117

116118
def print_summary(self):
@@ -137,10 +139,10 @@ def get_labels_as_dict(self):
137139
self.labels_as_dict[ele.key] = ele.label
138140
return self.labels_as_dict
139141

140-
def to_xml(self):
142+
def to_xml(self, prefix='', indent=' '):
141143
if len(self.labels) == 0:
142-
return "<LabelTable/>\n"
143-
res = "<LabelTable>\n"
144+
return ''
145+
res = "%s<LabelTable>\n" % prefix
144146
for ele in self.labels:
145147
col = ''
146148
if not ele.red is None:
@@ -151,10 +153,10 @@ def to_xml(self):
151153
col += ' Blue="%s"' % str(ele.blue)
152154
if not ele.alpha is None:
153155
col += ' Alpha="%s"' % str(ele.alpha)
154-
lab = """\t<Label Key="%s"%s><![CDATA[%s]]></Label>\n""" % \
155-
(str(ele.key), col, ele.label)
156-
res = res + lab
157-
res = res + "</LabelTable>\n"
156+
lab = """%s<Label Key="%s"%s><![CDATA[%s]]></Label>\n""" % \
157+
(prefix + indent, str(ele.key), col, ele.label)
158+
res += lab
159+
res += "%s</LabelTable>\n" % prefix
158160
return res
159161

160162
def print_summary(self):
@@ -244,6 +246,16 @@ def set_label_table(self, label_table):
244246
else:
245247
print("Not a valid CiftiLabelTable instance")
246248

249+
def to_xml(self, prefix='', indent=' '):
250+
if self.map_name is None:
251+
return ''
252+
res = "%s<NamedMap>\n" % prefix
253+
res += self.meta.to_xml(prefix=prefix + indent, indent=indent)
254+
res += self.label_table.to_xml(prefix=prefix + indent, indent=indent)
255+
res += "%s<MapName>%s</MapName>\n" % (prefix + indent, self.map_name)
256+
res += "%s</NamedMap>\n" % prefix
257+
return res
258+
247259

248260
class CiftiSurface(object):
249261
"""Class for Surface """
@@ -254,13 +266,30 @@ def __init__(self, brainStructure=None, surfaceNumberOfVertices=None):
254266
self.brainStructure = brainStructure
255267
self.surfaceNumberOfVertices = surfaceNumberOfVertices
256268

269+
def to_xml(self, prefix='', indent=' '):
270+
if self.brainStructure is None:
271+
return ''
272+
res = ('%s<Surface BrainStructure="%s" '
273+
'SurfaceNumberOfVertices"%s" />\n') % (prefix,
274+
self.brainStructure,
275+
self.surfaceNumberOfVertices)
276+
return res
257277

258278
class CiftiVoxelIndicesIJK(object):
259279
indices = np.array
260280

261281
def __init__(self, indices=None):
262282
self.indices = indices
263283

284+
def to_xml(self, prefix='', indent=' '):
285+
if self.indices is None:
286+
return ''
287+
res = '%s<VoxelIndicesIJK>' % prefix
288+
for row in self.indices:
289+
res += ' '.join(row.astype(str).tolist()) + '\n'
290+
res += '</VoxelIndicesIJK>\n'
291+
return res
292+
264293

265294
class CiftiVertices(object):
266295

@@ -271,6 +300,14 @@ def __init__(self, brain_structure=None, vertices=None):
271300
self.vertices = vertices
272301
self.brainStructure = brain_structure
273302

303+
def to_xml(self, prefix='', indent=' '):
304+
if self.vertices is None:
305+
return ''
306+
res = '%s<Vertices BrainStructure="%s">' % (prefix, self.brainStructure)
307+
res += ' '.join(self.vertices.astype(str).tolist())
308+
res += '</Vertices>\n'
309+
return res
310+
274311

275312
class CiftiParcel(object):
276313
"""Class for Parcel"""
@@ -314,6 +351,16 @@ def remove_cifti_vertices(self, ith):
314351
self.vertices.pop(ith)
315352
self.numVA -= 1
316353

354+
def to_xml(self, prefix='', indent=' '):
355+
if self.name is None:
356+
return ''
357+
res = '%s<Parcel Name="%s">\n' % (prefix, self.name)
358+
res += self.voxelIndicesIJK.to_xml(prefix=prefix + indent, indent=indent)
359+
for vertices in self.vertices:
360+
res += vertices.to_xml(prefix=prefix + indent, indent=indent)
361+
res += "%s</Parcel>\n" % prefix
362+
return res
363+
317364

318365
class CiftiTransformationMatrixVoxelIndicesIJKtoXYZ(object):
319366

@@ -324,6 +371,16 @@ def __init__(self, meter_exponent=None, matrix=None):
324371
self.meterExponent = meter_exponent
325372
self.matrix = matrix
326373

374+
def to_xml(self, prefix='', indent=' '):
375+
if self.matrix is None:
376+
return ''
377+
res = ('%s<TransformationMatrixVoxelIndices'
378+
'IJKtoXYZ MeterExponend="%d">') % (prefix, self.meterExponent)
379+
for row in self.matrix:
380+
res += '\n' + ' '.join(['%.10f' % val for val in row])
381+
res += "</TransformationMatrixVoxelIndicesIJKtoXYZ>\n"
382+
return res
383+
327384

328385
class CiftiVolume(object):
329386

@@ -334,13 +391,29 @@ def __init__(self, volume_dimensions=None, transform_matrix=None):
334391
self.volumeDimensions = volume_dimensions
335392
self.transformationMatrixVoxelIndicesIJKtoXYZ = transform_matrix
336393

394+
def to_xml(self, prefix='', indent=' '):
395+
if not self.volumeDimensions:
396+
return ''
397+
res = '%s<Volume VolumeDimensions="%s">\n' % (prefix,
398+
','.join([str(val) for val in self.volumeDimensions]))
399+
res += self.transformationMatrixVoxelIndicesIJKtoXYZ.to_xml(prefix=prefix + '\t')
400+
res += "%s</Volume>\n" % prefix
401+
return res
402+
337403

338404
class CiftiVertexIndices(object):
339405
indices = np.array
340406

341407
def __init__(self, indices=None):
342408
self.indices = indices
343409

410+
def to_xml(self, prefix='', indent=' '):
411+
if self.indices is None:
412+
return ''
413+
indices = ' '.join(self.indices.astype(str).tolist())
414+
res = '%s<VertexIndices>%s</VertexIndices>\n' % (prefix, indices)
415+
return res
416+
344417

345418
class CiftiBrainModel(object):
346419

@@ -363,8 +436,12 @@ def __init__(self, index_offset=None, index_count=None, model_type=None,
363436

364437
if voxel_indices_ijk is not None:
365438
self.voxelIndicesIJK = voxel_indices_ijk
439+
else:
440+
self.voxelIndicesIJK = CiftiVoxelIndicesIJK()
366441
if vertex_indices is not None:
367442
self.vertexIndices = vertex_indices
443+
else:
444+
self.vertexIndices = CiftiVertexIndices()
368445

369446
@property
370447
def voxelIndicesIJK(self):
@@ -384,6 +461,27 @@ def vertexIndices(self, value):
384461
assert isinstance(value, CiftiVertexIndices)
385462
self._vertexIndices = value
386463

464+
def to_xml(self, prefix='', indent=' '):
465+
if self.indexOffset is None:
466+
return ''
467+
attrs = []
468+
for key in ['IndexOffset', 'IndexCount', 'ModelType', 'BrainStructure',
469+
'SurfaceNumberOfVertices']:
470+
attr = key[0].lower() + key[1:]
471+
value = getattr(self, attr)
472+
if value is not None:
473+
attrs += ['%s="%s"' % (key, value)]
474+
attrs = ' '.join(attrs)
475+
res = '%s<BrainModel %s>\n' % (prefix, attrs)
476+
if self.voxelIndicesIJK:
477+
res += self.voxelIndicesIJK.to_xml(prefix=prefix + indent,
478+
indent=indent)
479+
if self.vertexIndices:
480+
res += self.vertexIndices.to_xml(prefix=prefix + indent,
481+
indent=indent)
482+
res += "%s</BrainModel>\n" % prefix
483+
return res
484+
387485

388486
class CiftiMatrixIndicesMap(object):
389487
"""Class for Matrix Indices Map
@@ -529,6 +627,32 @@ def remove_cifti_volume(self):
529627
""" Removes the volume element from the CiftiMatrixIndicesMap """
530628
self.volume = None
531629

630+
def to_xml(self, prefix='', indent=' '):
631+
if self.appliesToMatrixDimension is None:
632+
return ''
633+
attrs = []
634+
for key in ['AppliesToMatrixDimension', 'IndicesMapToDataType',
635+
'NumberOfSeriesPoints', 'SeriesExponent', 'SeriesStart',
636+
'SeriesStep', 'SeriesUnit']:
637+
attr = key[0].lower() + key[1:]
638+
value = getattr(self, attr)
639+
if value is not None:
640+
attrs += ['%s="%s"' % (key, value)]
641+
attrs = ' '.join(attrs)
642+
res = '%s<MatrixIndicesMap %s>\n' % (prefix, attrs)
643+
for named_map in self.namedMaps:
644+
res += named_map.to_xml(prefix=prefix + indent, indent=indent)
645+
for surface in self.surfaces:
646+
res += surface.to_xml(prefix=prefix + indent, indent=indent)
647+
for parcel in self.parcels:
648+
res += parcel.to_xml(prefix=prefix + indent, indent=indent)
649+
if self.volume:
650+
res += self.volume.to_xml(prefix=prefix + indent, indent=indent)
651+
for model in self.brainModels:
652+
res += model.to_xml(prefix=prefix + indent, indent=indent)
653+
res += "%s</MatrixIndicesMap>\n" % prefix
654+
return res
655+
532656

533657
class CiftiMatrix(object):
534658

@@ -582,6 +706,17 @@ def remove_cifti_matrix_indices_map(self, ith):
582706
self.mims.pop(ith)
583707
self.numMIM -= 1
584708

709+
def to_xml(self, prefix='', indent=' '):
710+
if self.numMIM == 0:
711+
return ''
712+
res = '%s<Matrix>\n' % prefix
713+
if self.meta:
714+
res += self.meta.to_xml(prefix=prefix + indent, indent=indent)
715+
for mim in self.mims:
716+
res += mim.to_xml(prefix=prefix + indent, indent=indent)
717+
res += "%s</Matrix>\n" % prefix
718+
return res
719+
585720

586721
class CiftiHeader(object):
587722
''' Class for Cifti2 header extension '''
@@ -595,6 +730,12 @@ def __init__(self, matrix=None, version="2.0"):
595730
self.matrix = matrix
596731
self.version = version
597732

733+
def to_xml(self, prefix='', indent=' '):
734+
res = '%s<CIFTI Version="%s">\n' % (prefix, self.version)
735+
res += self.matrix.to_xml(prefix=prefix + indent, indent=indent)
736+
res += "%s</CIFTI>\n" % prefix
737+
return res
738+
598739

599740
class Outputter(object):
600741

@@ -742,6 +883,7 @@ def StartElementHandler(self, name, attrs):
742883
parent = self.struct_state[-1]
743884
assert isinstance(parent, (CiftiParcel, CiftiBrainModel))
744885
parent.voxelIndicesIJK = CiftiVoxelIndicesIJK()
886+
745887
self.write_to = 'VoxelIndices'
746888
elif name == "Volume":
747889
mim = self.struct_state[-1]

0 commit comments

Comments
 (0)