Skip to content

Commit 1478cc9

Browse files
committed
Merge pull request #360 from bcipolli/gifti-loadsave
MRG: Subclass GIFTI from FileBasedImage Deprecate gifti.read/write for nib.load/save
2 parents 598b739 + 6fc6899 commit 1478cc9

10 files changed

+464
-349
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ dist/
6262
.tox/
6363
.coverage
6464
.ropeproject/
65+
htmlcov/
6566

6667
# Logs and databases #
6768
######################

nibabel/gifti/gifti.py

+74-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import numpy as np
1414

1515
from .. import xmlutils as xml
16+
from ..filebasedimages import FileBasedImage
1617
from ..nifti1 import data_type_codes, xform_codes, intent_codes
1718
from .util import (array_index_order_codes, gifti_encoding_codes,
1819
gifti_endian_codes, KIND2FMT)
@@ -190,6 +191,7 @@ def data_tag(dataarray, encoding, datatype, ordering):
190191
class DataTag(xml.XmlSerializable):
191192
def __init__(self, *args):
192193
self.args = args
194+
193195
def _to_xml_element(self):
194196
return _data_tag_element(*self.args)
195197

@@ -384,10 +386,15 @@ def metadata(self):
384386
return self.meta.metadata
385387

386388

387-
class GiftiImage(xml.XmlSerializable):
389+
class GiftiImage(FileBasedImage, xml.XmlSerializable):
390+
valid_exts = ('.gii',)
391+
files_types = (('image', '.gii'),)
392+
393+
def __init__(self, header=None, extra=None, file_map=None, meta=None,
394+
labeltable=None, darrays=None, version="1.0"):
395+
FileBasedImage.__init__(self, header=header, extra=extra,
396+
file_map=file_map)
388397

389-
def __init__(self, meta=None, labeltable=None, darrays=None,
390-
version="1.0"):
391398
if darrays is None:
392399
darrays = []
393400
if meta is None:
@@ -511,7 +518,6 @@ def print_summary(self):
511518
print(da.print_summary())
512519
print('----end----')
513520

514-
515521
def _to_xml_element(self):
516522
GIFTI = xml.Element('GIFTI', attrib={
517523
'Version': self.version,
@@ -529,3 +535,67 @@ def to_xml(self, enc='utf-8'):
529535
return b"""<?xml version="1.0" encoding="UTF-8"?>
530536
<!DOCTYPE GIFTI SYSTEM "http://www.nitrc.org/frs/download.php/115/gifti.dtd">
531537
""" + xml.XmlSerializable.to_xml(self, enc)
538+
539+
@classmethod
540+
def from_file_map(klass, file_map):
541+
""" Load a Gifti image from a file_map
542+
543+
Parameters
544+
file_map : string
545+
546+
Returns
547+
-------
548+
img : GiftiImage
549+
Returns a GiftiImage
550+
"""
551+
from .parse_gifti_fast import parse_gifti_file
552+
return parse_gifti_file(
553+
fptr=file_map['image'].get_prepare_fileobj('rb'))
554+
555+
def to_file_map(self, file_map=None):
556+
""" Save the current image to the specified file_map
557+
558+
Parameters
559+
----------
560+
file_map : string
561+
562+
Returns
563+
-------
564+
None
565+
566+
Notes
567+
-----
568+
We write all files with utf-8 encoding, and specify this at the top of
569+
the XML file with the ``encoding`` attribute.
570+
571+
The Gifti spec suggests using the following suffixes to your
572+
filename when saving each specific type of data:
573+
574+
.gii
575+
Generic GIFTI File
576+
.coord.gii
577+
Coordinates
578+
.func.gii
579+
Functional
580+
.label.gii
581+
Labels
582+
.rgba.gii
583+
RGB or RGBA
584+
.shape.gii
585+
Shape
586+
.surf.gii
587+
Surface
588+
.tensor.gii
589+
Tensors
590+
.time.gii
591+
Time Series
592+
.topo.gii
593+
Topology
594+
595+
The Gifti file is stored in endian convention of the current machine.
596+
"""
597+
# Our giftis are always utf-8 encoded - see GiftiImage.to_xml
598+
if file_map is None:
599+
file_map = self.file_map
600+
f = file_map['image'].get_prepare_fileobj('wb')
601+
f.write(self.to_xml())

nibabel/gifti/giftiio.py

+7-10
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@
1010
# Stephan Gerhard, Oktober 2010
1111
##############
1212

13-
import os
14-
import codecs
15-
16-
from .parse_gifti_fast import parse_gifti_file
13+
import numpy as np
1714

1815

16+
@np.deprecate_with_doc("Use nibabel.load() instead.")
1917
def read(filename):
2018
""" Load a Gifti image from a file
2119
@@ -29,11 +27,11 @@ def read(filename):
2927
img : GiftiImage
3028
Returns a GiftiImage
3129
"""
32-
if not os.path.isfile(filename):
33-
raise IOError("No such file or directory: '%s'" % filename)
34-
return parse_gifti_file(filename)
30+
from ..loadsave import load
31+
return load(filename)
3532

3633

34+
@np.deprecate_with_doc("Use nibabel.save() instead.")
3735
def write(image, filename):
3836
""" Save the current image to a new file
3937
@@ -79,6 +77,5 @@ def write(image, filename):
7977
8078
The Gifti file is stored in endian convention of the current machine.
8179
"""
82-
# Our giftis are always utf-8 encoded - see GiftiImage.to_xml
83-
with open(filename, 'wb') as f:
84-
f.write(image.to_xml())
80+
from ..loadsave import save
81+
return save(image, filename)

nibabel/gifti/parse_gifti_fast.py

+28-19
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def pending_data(self):
318318
return not self._char_blocks is None
319319

320320

321-
def parse_gifti_file(fname, buffer_size=None):
321+
def parse_gifti_file(fname=None, fptr=None, buffer_size=None):
322322
""" Parse gifti file named `fname`, return image
323323
324324
Parameters
@@ -334,28 +334,37 @@ def parse_gifti_file(fname, buffer_size=None):
334334
-------
335335
img : gifti image
336336
"""
337+
assert (fname is not None) + (fptr is not None) == 1, "Specify only fname or fptr, not both"
338+
339+
if fptr is None:
340+
with open(fname, 'rb') as datasource:
341+
return parse_gifti_file(fptr=datasource, buffer_size=buffer_size)
342+
else:
343+
datasource = fptr
344+
337345
if buffer_size is None:
338346
buffer_sz_val = 35000000
339347
else:
340348
buffer_sz_val = buffer_size
341-
with open(fname, 'rb') as datasource:
342-
parser = ParserCreate()
343-
parser.buffer_text = True
344-
try:
345-
parser.buffer_size = buffer_sz_val
346-
except AttributeError:
347-
if not buffer_size is None:
348-
raise ValueError('Cannot set buffer size for parser')
349-
HANDLER_NAMES = ['StartElementHandler',
350-
'EndElementHandler',
351-
'CharacterDataHandler']
352-
out = Outputter()
353-
for name in HANDLER_NAMES:
354-
setattr(parser, name, getattr(out, name))
355-
try:
356-
parser.ParseFile(datasource)
357-
except ExpatError:
358-
print('An expat error occured while parsing the Gifti file.')
349+
350+
parser = ParserCreate()
351+
parser.buffer_text = True
352+
try:
353+
parser.buffer_size = buffer_sz_val
354+
except AttributeError:
355+
if not buffer_size is None:
356+
raise ValueError('Cannot set buffer size for parser')
357+
HANDLER_NAMES = ['StartElementHandler',
358+
'EndElementHandler',
359+
'CharacterDataHandler']
360+
out = Outputter()
361+
for name in HANDLER_NAMES:
362+
setattr(parser, name, getattr(out, name))
363+
try:
364+
parser.ParseFile(datasource)
365+
except ExpatError:
366+
print('An expat error occured while parsing the Gifti file.')
367+
359368
# Reality check for pending data
360369
assert out.pending_data is False
361370
# update filename

nibabel/gifti/tests/test_gifti.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@
44

55
import numpy as np
66

7+
import nibabel as nib
78
from nibabel.externals.six import string_types
89
from nibabel.gifti import (GiftiImage, GiftiDataArray, GiftiLabel,
9-
GiftiLabelTable, GiftiMetaData, giftiio)
10+
GiftiLabelTable, GiftiMetaData)
1011
from nibabel.gifti.gifti import data_tag
1112
from nibabel.nifti1 import data_type_codes, intent_codes
1213

1314
from numpy.testing import (assert_array_almost_equal,
1415
assert_array_equal)
1516
from nose.tools import (assert_true, assert_false, assert_equal, assert_raises)
1617
from nibabel.testing import clear_and_catch_warnings
17-
from .test_giftiio import (DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4,
18-
DATA_FILE5, DATA_FILE6)
18+
from .test_parse_gifti_fast import (DATA_FILE1, DATA_FILE2, DATA_FILE3,
19+
DATA_FILE4, DATA_FILE5, DATA_FILE6)
1920

2021

2122
def test_gifti_image():
@@ -142,7 +143,7 @@ def assign_rgba(gl, val):
142143
def test_print_summary():
143144
for fil in [DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4,
144145
DATA_FILE5, DATA_FILE6]:
145-
gimg = giftiio.read(fil)
146+
gimg = nib.load(fil)
146147
gimg.print_summary()
147148

148149

0 commit comments

Comments
 (0)