diff --git a/.gitignore b/.gitignore index a0b34de90f..d6996550dc 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ dist/ .tox/ .coverage .ropeproject/ +htmlcov/ # Logs and databases # ###################### diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index 071f2c3968..c3199064b5 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -13,6 +13,7 @@ import numpy as np from .. import xmlutils as xml +from ..filebasedimages import FileBasedImage from ..nifti1 import data_type_codes, xform_codes, intent_codes from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes, KIND2FMT) @@ -190,6 +191,7 @@ def data_tag(dataarray, encoding, datatype, ordering): class DataTag(xml.XmlSerializable): def __init__(self, *args): self.args = args + def _to_xml_element(self): return _data_tag_element(*self.args) @@ -384,10 +386,15 @@ def metadata(self): return self.meta.metadata -class GiftiImage(xml.XmlSerializable): +class GiftiImage(FileBasedImage, xml.XmlSerializable): + valid_exts = ('.gii',) + files_types = (('image', '.gii'),) + + def __init__(self, header=None, extra=None, file_map=None, meta=None, + labeltable=None, darrays=None, version="1.0"): + FileBasedImage.__init__(self, header=header, extra=extra, + file_map=file_map) - def __init__(self, meta=None, labeltable=None, darrays=None, - version="1.0"): if darrays is None: darrays = [] if meta is None: @@ -511,7 +518,6 @@ def print_summary(self): print(da.print_summary()) print('----end----') - def _to_xml_element(self): GIFTI = xml.Element('GIFTI', attrib={ 'Version': self.version, @@ -529,3 +535,67 @@ def to_xml(self, enc='utf-8'): return b""" """ + xml.XmlSerializable.to_xml(self, enc) + + @classmethod + def from_file_map(klass, file_map): + """ Load a Gifti image from a file_map + + Parameters + file_map : string + + Returns + ------- + img : GiftiImage + Returns a GiftiImage + """ + from .parse_gifti_fast import parse_gifti_file + return parse_gifti_file( + fptr=file_map['image'].get_prepare_fileobj('rb')) + + def to_file_map(self, file_map=None): + """ Save the current image to the specified file_map + + Parameters + ---------- + file_map : string + + Returns + ------- + None + + Notes + ----- + We write all files with utf-8 encoding, and specify this at the top of + the XML file with the ``encoding`` attribute. + + The Gifti spec suggests using the following suffixes to your + filename when saving each specific type of data: + + .gii + Generic GIFTI File + .coord.gii + Coordinates + .func.gii + Functional + .label.gii + Labels + .rgba.gii + RGB or RGBA + .shape.gii + Shape + .surf.gii + Surface + .tensor.gii + Tensors + .time.gii + Time Series + .topo.gii + Topology + + The Gifti file is stored in endian convention of the current machine. + """ + # Our giftis are always utf-8 encoded - see GiftiImage.to_xml + if file_map is None: + file_map = self.file_map + f = file_map['image'].get_prepare_fileobj('wb') + f.write(self.to_xml()) diff --git a/nibabel/gifti/giftiio.py b/nibabel/gifti/giftiio.py index 2660267021..fcd1a48847 100644 --- a/nibabel/gifti/giftiio.py +++ b/nibabel/gifti/giftiio.py @@ -10,12 +10,10 @@ # Stephan Gerhard, Oktober 2010 ############## -import os -import codecs - -from .parse_gifti_fast import parse_gifti_file +import numpy as np +@np.deprecate_with_doc("Use nibabel.load() instead.") def read(filename): """ Load a Gifti image from a file @@ -29,11 +27,11 @@ def read(filename): img : GiftiImage Returns a GiftiImage """ - if not os.path.isfile(filename): - raise IOError("No such file or directory: '%s'" % filename) - return parse_gifti_file(filename) + from ..loadsave import load + return load(filename) +@np.deprecate_with_doc("Use nibabel.save() instead.") def write(image, filename): """ Save the current image to a new file @@ -79,6 +77,5 @@ def write(image, filename): The Gifti file is stored in endian convention of the current machine. """ - # Our giftis are always utf-8 encoded - see GiftiImage.to_xml - with open(filename, 'wb') as f: - f.write(image.to_xml()) + from ..loadsave import save + return save(image, filename) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index 4f7f292605..aa3995b6b7 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -318,7 +318,7 @@ def pending_data(self): return not self._char_blocks is None -def parse_gifti_file(fname, buffer_size=None): +def parse_gifti_file(fname=None, fptr=None, buffer_size=None): """ Parse gifti file named `fname`, return image Parameters @@ -334,28 +334,37 @@ def parse_gifti_file(fname, buffer_size=None): ------- img : gifti image """ + assert (fname is not None) + (fptr is not None) == 1, "Specify only fname or fptr, not both" + + if fptr is None: + with open(fname, 'rb') as datasource: + return parse_gifti_file(fptr=datasource, buffer_size=buffer_size) + else: + datasource = fptr + if buffer_size is None: buffer_sz_val = 35000000 else: buffer_sz_val = buffer_size - with open(fname, 'rb') as datasource: - parser = ParserCreate() - parser.buffer_text = True - try: - parser.buffer_size = buffer_sz_val - except AttributeError: - if not buffer_size is None: - raise ValueError('Cannot set buffer size for parser') - HANDLER_NAMES = ['StartElementHandler', - 'EndElementHandler', - 'CharacterDataHandler'] - out = Outputter() - for name in HANDLER_NAMES: - setattr(parser, name, getattr(out, name)) - try: - parser.ParseFile(datasource) - except ExpatError: - print('An expat error occured while parsing the Gifti file.') + + parser = ParserCreate() + parser.buffer_text = True + try: + parser.buffer_size = buffer_sz_val + except AttributeError: + if not buffer_size is None: + raise ValueError('Cannot set buffer size for parser') + HANDLER_NAMES = ['StartElementHandler', + 'EndElementHandler', + 'CharacterDataHandler'] + out = Outputter() + for name in HANDLER_NAMES: + setattr(parser, name, getattr(out, name)) + try: + parser.ParseFile(datasource) + except ExpatError: + print('An expat error occured while parsing the Gifti file.') + # Reality check for pending data assert out.pending_data is False # update filename diff --git a/nibabel/gifti/tests/test_gifti.py b/nibabel/gifti/tests/test_gifti.py index 9433c90219..73919d94e6 100644 --- a/nibabel/gifti/tests/test_gifti.py +++ b/nibabel/gifti/tests/test_gifti.py @@ -4,9 +4,10 @@ import numpy as np +import nibabel as nib from nibabel.externals.six import string_types from nibabel.gifti import (GiftiImage, GiftiDataArray, GiftiLabel, - GiftiLabelTable, GiftiMetaData, giftiio) + GiftiLabelTable, GiftiMetaData) from nibabel.gifti.gifti import data_tag from nibabel.nifti1 import data_type_codes, intent_codes @@ -14,8 +15,8 @@ assert_array_equal) from nose.tools import (assert_true, assert_false, assert_equal, assert_raises) from nibabel.testing import clear_and_catch_warnings -from .test_giftiio import (DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4, - DATA_FILE5, DATA_FILE6) +from .test_parse_gifti_fast import (DATA_FILE1, DATA_FILE2, DATA_FILE3, + DATA_FILE4, DATA_FILE5, DATA_FILE6) def test_gifti_image(): @@ -142,7 +143,7 @@ def assign_rgba(gl, val): def test_print_summary(): for fil in [DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4, DATA_FILE5, DATA_FILE6]: - gimg = giftiio.read(fil) + gimg = nib.load(fil) gimg.print_summary() diff --git a/nibabel/gifti/tests/test_giftiio.py b/nibabel/gifti/tests/test_giftiio.py index ba03cd276f..9c12b7c07a 100644 --- a/nibabel/gifti/tests/test_giftiio.py +++ b/nibabel/gifti/tests/test_giftiio.py @@ -6,317 +6,30 @@ # copyright and license terms. # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -from __future__ import division, print_function, absolute_import -from os.path import join as pjoin, dirname -import sys import warnings -import numpy as np - -from ... import gifti as gi -from ..util import gifti_endian_codes -from ...nifti1 import xform_codes - -from ...tmpdirs import InTemporaryDirectory - -from numpy.testing import assert_array_equal, assert_array_almost_equal - from nose.tools import (assert_true, assert_false, assert_equal, assert_raises) from ...testing import clear_and_catch_warnings -IO_DATA_PATH = pjoin(dirname(__file__), 'data') - -DATA_FILE1 = pjoin(IO_DATA_PATH, 'ascii.gii') -DATA_FILE2 = pjoin(IO_DATA_PATH, 'gzipbase64.gii') -DATA_FILE3 = pjoin(IO_DATA_PATH, 'label.gii') -DATA_FILE4 = pjoin(IO_DATA_PATH, 'rh.shape.curv.gii') -# The base64bin file uses non-standard encoding and endian strings, and has -# line-breaks in the base64 encoded data, both of which will break other -# readers, such as Connectome workbench; for example: -# wb_command -gifti-convert ASCII base64bin.gii test.gii -DATA_FILE5 = pjoin(IO_DATA_PATH, 'base64bin.gii') -DATA_FILE6 = pjoin(IO_DATA_PATH, 'rh.aparc.annot.gii') - -datafiles = [DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4, DATA_FILE5, DATA_FILE6] -numDA = [2, 1, 1, 1, 2, 1] - -DATA_FILE1_darr1 = np.array( - [[-16.07201 , -66.187515, 21.266994], - [-16.705893, -66.054337, 21.232786], - [-17.614349, -65.401642, 21.071466]]) -DATA_FILE1_darr2 = np.array( [0,1,2] ) - -DATA_FILE2_darr1 = np.array([[ 0.43635699], - [ 0.270017 ], - [ 0.133239 ], - [ 0.35054299], - [ 0.26538199], - [ 0.32122701], - [ 0.23495001], - [ 0.26671499], - [ 0.306851 ], - [ 0.36302799]], dtype=np.float32) - -DATA_FILE3_darr1 = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]) - -DATA_FILE4_darr1 = np.array([[-0.57811606], - [-0.53871965], - [-0.44602534], - [-0.56532663], - [-0.51392376], - [-0.43225467], - [-0.54646534], - [-0.48011276], - [-0.45624232], - [-0.31101292]], dtype=np.float32) - -DATA_FILE5_darr1 = np.array([[ 155.17539978, 135.58103943, 98.30715179], - [ 140.33973694, 190.0491333 , 73.24776459], - [ 157.3598938 , 196.97969055, 83.65809631], - [ 171.46174622, 137.43661499, 78.4709549 ], - [ 148.54592896, 97.06752777, 65.96373749], - [ 123.45701599, 111.46841431, 66.3571167 ], - [ 135.30892944, 202.28720093, 36.38148499], - [ 178.28155518, 162.59469604, 37.75128937], - [ 178.11087036, 115.28820038, 57.17986679], - [ 142.81582642, 82.82115173, 31.02205276]], dtype=np.float32) - -DATA_FILE5_darr2 = np.array([[ 6402, 17923, 25602], - [14085, 25602, 17923], - [25602, 14085, 4483], - [17923, 1602, 14085], - [ 4483, 25603, 25602], - [25604, 25602, 25603], - [25602, 25604, 6402], - [25603, 3525, 25604], - [ 1123, 17922, 12168], - [25604, 12168, 17922]], dtype=np.int32) - -DATA_FILE6_darr1 = np.array([9182740, 9182740, 9182740], dtype=np.float32) - - -def test_read_ordering(): - # DATA_FILE1 has an expected darray[0].data shape of (3,3). However if we - # read another image first (DATA_FILE2) then the shape is wrong - # Read an image - img2 = gi.read(DATA_FILE2) - assert_equal(img2.darrays[0].data.shape, (143479, 1)) - # Read image for which we know output shape - img = gi.read(DATA_FILE1) - assert_equal(img.darrays[0].data.shape, (3,3)) - +from .test_parse_gifti_fast import (DATA_FILE1, DATA_FILE2, DATA_FILE3, + DATA_FILE4, DATA_FILE5, DATA_FILE6) -def test_load_metadata(): - for i, dat in enumerate(datafiles): - img = gi.read(dat) - me = img.meta - assert_equal(numDA[i], img.numDA) - assert_equal(img.version,'1.0') +class TestGiftiIO(object): + def setUp(self): + with clear_and_catch_warnings() as w: + warnings.simplefilter('always', DeprecationWarning) + import nibabel.gifti.giftiio + assert_equal(len(w), 1) -def test_metadata_deprecations(): - img = gi.read(datafiles[0]) - me = img.meta - - # Test deprecation - with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) - assert_equal(me, img.get_meta()) +def test_read_deprecated(): with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) - img.set_metadata(me) - assert_equal(me, img.meta) - - -def test_load_dataarray1(): - img1 = gi.read(DATA_FILE1) - - # Round trip - with InTemporaryDirectory(): - gi.write(img1, 'test.gii') - bimg = gi.read('test.gii') - for img in (img1, bimg): - assert_array_almost_equal(img.darrays[0].data, DATA_FILE1_darr1) - assert_array_almost_equal(img.darrays[1].data, DATA_FILE1_darr2) - me=img.darrays[0].meta.metadata - assert_true('AnatomicalStructurePrimary' in me) - assert_true('AnatomicalStructureSecondary' in me) - assert_equal(me['AnatomicalStructurePrimary'], 'CortexLeft') - assert_array_almost_equal(img.darrays[0].coordsys.xform, np.eye(4,4)) - assert_equal(xform_codes.niistring[img.darrays[0].coordsys.dataspace],'NIFTI_XFORM_TALAIRACH') - assert_equal(xform_codes.niistring[img.darrays[0].coordsys.xformspace],'NIFTI_XFORM_TALAIRACH') - - -def test_load_dataarray2(): - img2 = gi.read(DATA_FILE2) - - # Round trip - with InTemporaryDirectory(): - gi.write(img2, 'test.gii') - bimg = gi.read('test.gii') - for img in (img2, bimg): - assert_array_almost_equal(img.darrays[0].data[:10], DATA_FILE2_darr1) - - -def test_load_dataarray3(): - img3 = gi.read(DATA_FILE3) - - with InTemporaryDirectory(): - gi.write(img3, 'test.gii') - bimg = gi.read('test.gii') - for img in (img3, bimg): - assert_array_almost_equal(img.darrays[0].data[30:50], DATA_FILE3_darr1) - - -def test_load_dataarray4(): - img4 = gi.read(DATA_FILE4) + warnings.simplefilter('always', DeprecationWarning) + from nibabel.gifti.giftiio import read - # Round trip - with InTemporaryDirectory(): - gi.write(img4, 'test.gii') - bimg = gi.read('test.gii') - for img in (img4, bimg): - assert_array_almost_equal(img.darrays[0].data[:10], DATA_FILE4_darr1) - - -def test_dataarray5(): - img5 = gi.read(DATA_FILE5) - - for da in img5.darrays: - assert_equal(gifti_endian_codes.byteorder[da.endian], 'little') - assert_array_almost_equal(img5.darrays[0].data, DATA_FILE5_darr1) - assert_array_almost_equal(img5.darrays[1].data, DATA_FILE5_darr2) - # Round trip tested below - - -def test_base64_written(): - with InTemporaryDirectory(): - with open(DATA_FILE5, 'rb') as fobj: - contents = fobj.read() - # Confirm the bad tags are still in the file - assert_true(b'GIFTI_ENCODING_B64BIN' in contents) - assert_true(b'GIFTI_ENDIAN_LITTLE' in contents) - # The good ones are missing - assert_false(b'Base64Binary' in contents) - assert_false(b'LittleEndian' in contents) - # Round trip - img5 = gi.read(DATA_FILE5) - gi.write(img5, 'fixed.gii') - with open('fixed.gii', 'rb') as fobj: - contents = fobj.read() - # The bad codes have gone, replaced by the good ones - assert_false(b'GIFTI_ENCODING_B64BIN' in contents) - assert_false(b'GIFTI_ENDIAN_LITTLE' in contents) - assert_true(b'Base64Binary' in contents) - if sys.byteorder == 'little': - assert_true(b'LittleEndian' in contents) - else: - assert_true(b'BigEndian' in contents) - img5_fixed = gi.read('fixed.gii') - darrays = img5_fixed.darrays - assert_array_almost_equal(darrays[0].data, DATA_FILE5_darr1) - assert_array_almost_equal(darrays[1].data, DATA_FILE5_darr2) - - -def test_readwritedata(): - img = gi.read(DATA_FILE2) - with InTemporaryDirectory(): - gi.write(img, 'test.gii') - img2 = gi.read('test.gii') - assert_equal(img.numDA,img2.numDA) - assert_array_almost_equal(img.darrays[0].data, - img2.darrays[0].data) - - -def test_write_newmetadata(): - img = gi.GiftiImage() - attr = gi.GiftiNVPairs(name = 'mykey', value = 'val1') - newmeta = gi.GiftiMetaData(attr) - img.meta = newmeta - myme = img.meta.metadata - assert_true('mykey' in myme) - newmeta = gi.GiftiMetaData.from_dict( {'mykey1' : 'val2'} ) - img.meta = newmeta - myme = img.meta.metadata - assert_true('mykey1' in myme) - assert_false('mykey' in myme) - - -def test_load_getbyintent(): - img = gi.read(DATA_FILE1) - - da = img.get_arrays_from_intent("NIFTI_INTENT_POINTSET") - assert_equal(len(da), 1) - - with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) - da = img.getArraysFromIntent("NIFTI_INTENT_POINTSET") - assert_equal(len(da), 1) + img = read(DATA_FILE1) assert_equal(len(w), 1) - assert_equal(w[0].category, DeprecationWarning) - - da = img.get_arrays_from_intent("NIFTI_INTENT_TRIANGLE") - assert_equal(len(da), 1) - - da = img.get_arrays_from_intent("NIFTI_INTENT_CORREL") - assert_equal(len(da), 0) - assert_equal(da, []) - - -def test_load_labeltable(): - img6 = gi.read(DATA_FILE6) - - # Round trip - with InTemporaryDirectory(): - gi.write(img6, 'test.gii') - bimg = gi.read('test.gii') - for img in (img6, bimg): - assert_array_almost_equal(img.darrays[0].data[:3], DATA_FILE6_darr1) - assert_equal(len(img.labeltable.labels), 36) - labeldict = img.labeltable.get_labels_as_dict() - assert_true(660700 in labeldict) - assert_equal(labeldict[660700], 'entorhinal') - assert_equal(img.labeltable.labels[1].key, 2647065) - assert_equal(img.labeltable.labels[1].red, 0.0980392) - assert_equal(img.labeltable.labels[1].green, 0.392157) - assert_equal(img.labeltable.labels[1].blue, 0.156863) - assert_equal(img.labeltable.labels[1].alpha, 1) - - -def test_labeltable_deprecations(): - img = gi.read(DATA_FILE6) - lt = img.labeltable - - # Test deprecation - with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) - assert_equal(lt, img.get_labeltable()) - - with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) - img.set_labeltable(lt) - assert_equal(lt, img.labeltable) - - -def test_parse_dataarrays(): - fn = 'bad_daa.gii' - img = gi.GiftiImage() - - with InTemporaryDirectory(): - gi.write(img, fn) - with open(fn, 'r') as fp: - txt = fp.read() - # Make a bad gifti. - txt = txt.replace('NumberOfDataArrays="0"', 'NumberOfDataArrays ="1"') - with open(fn, 'w') as fp: - fp.write(txt) - - with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=UserWarning) - gi.read(fn) - assert_equal(len(w), 1) - assert_equal(img.numDA, 0) diff --git a/nibabel/gifti/tests/test_parse_gifti_fast.py b/nibabel/gifti/tests/test_parse_gifti_fast.py new file mode 100644 index 0000000000..f2470c707b --- /dev/null +++ b/nibabel/gifti/tests/test_parse_gifti_fast.py @@ -0,0 +1,315 @@ +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +from __future__ import division, print_function, absolute_import + +from os.path import join as pjoin, dirname +import sys +import warnings + +import numpy as np + +import nibabel.gifti as gi +from nibabel.gifti.util import gifti_endian_codes +from nibabel.loadsave import load, save +from nibabel.nifti1 import xform_codes +from nibabel.tmpdirs import InTemporaryDirectory + +from numpy.testing import assert_array_equal, assert_array_almost_equal + +from nose.tools import (assert_true, assert_false, assert_equal, + assert_raises) +from ...testing import clear_and_catch_warnings + + +IO_DATA_PATH = pjoin(dirname(__file__), 'data') + +DATA_FILE1 = pjoin(IO_DATA_PATH, 'ascii.gii') +DATA_FILE2 = pjoin(IO_DATA_PATH, 'gzipbase64.gii') +DATA_FILE3 = pjoin(IO_DATA_PATH, 'label.gii') +DATA_FILE4 = pjoin(IO_DATA_PATH, 'rh.shape.curv.gii') +# The base64bin file uses non-standard encoding and endian strings, and has +# line-breaks in the base64 encoded data, both of which will break other +# readers, such as Connectome workbench; for example: +# wb_command -gifti-convert ASCII base64bin.gii test.gii +DATA_FILE5 = pjoin(IO_DATA_PATH, 'base64bin.gii') +DATA_FILE6 = pjoin(IO_DATA_PATH, 'rh.aparc.annot.gii') + +datafiles = [DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4, DATA_FILE5, DATA_FILE6] +numDA = [2, 1, 1, 1, 2, 1] + +DATA_FILE1_darr1 = np.array( + [[-16.07201 , -66.187515, 21.266994], + [-16.705893, -66.054337, 21.232786], + [-17.614349, -65.401642, 21.071466]]) +DATA_FILE1_darr2 = np.array( [0,1,2] ) + +DATA_FILE2_darr1 = np.array([[ 0.43635699], + [ 0.270017 ], + [ 0.133239 ], + [ 0.35054299], + [ 0.26538199], + [ 0.32122701], + [ 0.23495001], + [ 0.26671499], + [ 0.306851 ], + [ 0.36302799]], dtype=np.float32) + +DATA_FILE3_darr1 = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]) + +DATA_FILE4_darr1 = np.array([[-0.57811606], + [-0.53871965], + [-0.44602534], + [-0.56532663], + [-0.51392376], + [-0.43225467], + [-0.54646534], + [-0.48011276], + [-0.45624232], + [-0.31101292]], dtype=np.float32) + +DATA_FILE5_darr1 = np.array([[ 155.17539978, 135.58103943, 98.30715179], + [ 140.33973694, 190.0491333 , 73.24776459], + [ 157.3598938 , 196.97969055, 83.65809631], + [ 171.46174622, 137.43661499, 78.4709549 ], + [ 148.54592896, 97.06752777, 65.96373749], + [ 123.45701599, 111.46841431, 66.3571167 ], + [ 135.30892944, 202.28720093, 36.38148499], + [ 178.28155518, 162.59469604, 37.75128937], + [ 178.11087036, 115.28820038, 57.17986679], + [ 142.81582642, 82.82115173, 31.02205276]], dtype=np.float32) + +DATA_FILE5_darr2 = np.array([[ 6402, 17923, 25602], + [14085, 25602, 17923], + [25602, 14085, 4483], + [17923, 1602, 14085], + [ 4483, 25603, 25602], + [25604, 25602, 25603], + [25602, 25604, 6402], + [25603, 3525, 25604], + [ 1123, 17922, 12168], + [25604, 12168, 17922]], dtype=np.int32) + +DATA_FILE6_darr1 = np.array([9182740, 9182740, 9182740], dtype=np.float32) + + +def test_read_ordering(): + # DATA_FILE1 has an expected darray[0].data shape of (3,3). However if we + # read another image first (DATA_FILE2) then the shape is wrong + # Read an image + img2 = load(DATA_FILE2) + assert_equal(img2.darrays[0].data.shape, (143479, 1)) + # Read image for which we know output shape + img = load(DATA_FILE1) + assert_equal(img.darrays[0].data.shape, (3,3)) + + +def test_load_metadata(): + for i, dat in enumerate(datafiles): + img = load(dat) + me = img.meta + assert_equal(numDA[i], img.numDA) + assert_equal(img.version,'1.0') + + +def test_metadata_deprecations(): + img = load(datafiles[0]) + me = img.meta + + # Test deprecation + with clear_and_catch_warnings() as w: + warnings.filterwarnings('once', category=DeprecationWarning) + assert_equal(me, img.get_meta()) + + with clear_and_catch_warnings() as w: + warnings.filterwarnings('once', category=DeprecationWarning) + img.set_metadata(me) + assert_equal(me, img.meta) + + +def test_load_dataarray1(): + img1 = load(DATA_FILE1) + # Round trip + with InTemporaryDirectory(): + save(img1, 'test.gii') + bimg = load('test.gii') + for img in (img1, bimg): + assert_array_almost_equal(img.darrays[0].data, DATA_FILE1_darr1) + assert_array_almost_equal(img.darrays[1].data, DATA_FILE1_darr2) + me=img.darrays[0].meta.metadata + assert_true('AnatomicalStructurePrimary' in me) + assert_true('AnatomicalStructureSecondary' in me) + assert_equal(me['AnatomicalStructurePrimary'], 'CortexLeft') + assert_array_almost_equal(img.darrays[0].coordsys.xform, np.eye(4,4)) + assert_equal(xform_codes.niistring[img.darrays[0].coordsys.dataspace],'NIFTI_XFORM_TALAIRACH') + assert_equal(xform_codes.niistring[img.darrays[0].coordsys.xformspace],'NIFTI_XFORM_TALAIRACH') + + +def test_load_dataarray2(): + img2 = load(DATA_FILE2) + # Round trip + with InTemporaryDirectory(): + save(img2, 'test.gii') + bimg = load('test.gii') + for img in (img2, bimg): + assert_array_almost_equal(img.darrays[0].data[:10], DATA_FILE2_darr1) + + +def test_load_dataarray3(): + img3 = load(DATA_FILE3) + with InTemporaryDirectory(): + save(img3, 'test.gii') + bimg = load('test.gii') + for img in (img3, bimg): + assert_array_almost_equal(img.darrays[0].data[30:50], DATA_FILE3_darr1) + + +def test_load_dataarray4(): + img4 = load(DATA_FILE4) + # Round trip + with InTemporaryDirectory(): + save(img4, 'test.gii') + bimg = load('test.gii') + for img in (img4, bimg): + assert_array_almost_equal(img.darrays[0].data[:10], DATA_FILE4_darr1) + + +def test_dataarray5(): + img5 = load(DATA_FILE5) + for da in img5.darrays: + assert_equal(gifti_endian_codes.byteorder[da.endian], 'little') + assert_array_almost_equal(img5.darrays[0].data, DATA_FILE5_darr1) + assert_array_almost_equal(img5.darrays[1].data, DATA_FILE5_darr2) + # Round trip tested below + + +def test_base64_written(): + with InTemporaryDirectory(): + with open(DATA_FILE5, 'rb') as fobj: + contents = fobj.read() + # Confirm the bad tags are still in the file + assert_true(b'GIFTI_ENCODING_B64BIN' in contents) + assert_true(b'GIFTI_ENDIAN_LITTLE' in contents) + # The good ones are missing + assert_false(b'Base64Binary' in contents) + assert_false(b'LittleEndian' in contents) + # Round trip + img5 = load(DATA_FILE5) + save(img5, 'fixed.gii') + with open('fixed.gii', 'rb') as fobj: + contents = fobj.read() + # The bad codes have gone, replaced by the good ones + assert_false(b'GIFTI_ENCODING_B64BIN' in contents) + assert_false(b'GIFTI_ENDIAN_LITTLE' in contents) + assert_true(b'Base64Binary' in contents) + if sys.byteorder == 'little': + assert_true(b'LittleEndian' in contents) + else: + assert_true(b'BigEndian' in contents) + img5_fixed = load('fixed.gii') + darrays = img5_fixed.darrays + assert_array_almost_equal(darrays[0].data, DATA_FILE5_darr1) + assert_array_almost_equal(darrays[1].data, DATA_FILE5_darr2) + + +def test_readwritedata(): + img = load(DATA_FILE2) + with InTemporaryDirectory(): + save(img, 'test.gii') + img2 = load('test.gii') + assert_equal(img.numDA,img2.numDA) + assert_array_almost_equal(img.darrays[0].data, + img2.darrays[0].data) + + +def test_write_newmetadata(): + img = gi.GiftiImage() + attr = gi.GiftiNVPairs(name = 'mykey', value = 'val1') + newmeta = gi.GiftiMetaData(attr) + img.meta = newmeta + myme = img.meta.metadata + assert_true('mykey' in myme) + newmeta = gi.GiftiMetaData.from_dict( {'mykey1' : 'val2'} ) + img.meta = newmeta + myme = img.meta.metadata + assert_true('mykey1' in myme) + assert_false('mykey' in myme) + + +def test_load_getbyintent(): + img = load(DATA_FILE1) + da = img.get_arrays_from_intent("NIFTI_INTENT_POINTSET") + assert_equal(len(da), 1) + + with clear_and_catch_warnings() as w: + warnings.filterwarnings('once', category=DeprecationWarning) + da = img.getArraysFromIntent("NIFTI_INTENT_POINTSET") + assert_equal(len(da), 1) + assert_equal(len(w), 1) + assert_equal(w[0].category, DeprecationWarning) + + da = img.get_arrays_from_intent("NIFTI_INTENT_TRIANGLE") + assert_equal(len(da), 1) + + da = img.get_arrays_from_intent("NIFTI_INTENT_CORREL") + assert_equal(len(da), 0) + assert_equal(da, []) + + +def test_load_labeltable(): + img6 = load(DATA_FILE6) + # Round trip + with InTemporaryDirectory(): + save(img6, 'test.gii') + bimg = load('test.gii') + for img in (img6, bimg): + assert_array_almost_equal(img.darrays[0].data[:3], DATA_FILE6_darr1) + assert_equal(len(img.labeltable.labels), 36) + labeldict = img.labeltable.get_labels_as_dict() + assert_true(660700 in labeldict) + assert_equal(labeldict[660700], 'entorhinal') + assert_equal(img.labeltable.labels[1].key, 2647065) + assert_equal(img.labeltable.labels[1].red, 0.0980392) + assert_equal(img.labeltable.labels[1].green, 0.392157) + assert_equal(img.labeltable.labels[1].blue, 0.156863) + assert_equal(img.labeltable.labels[1].alpha, 1) + + +def test_labeltable_deprecations(): + img = load(DATA_FILE6) + lt = img.labeltable + + # Test deprecation + with clear_and_catch_warnings() as w: + warnings.filterwarnings('once', category=DeprecationWarning) + assert_equal(lt, img.get_labeltable()) + + with clear_and_catch_warnings() as w: + warnings.filterwarnings('once', category=DeprecationWarning) + img.set_labeltable(lt) + assert_equal(lt, img.labeltable) + + +def test_parse_dataarrays(): + fn = 'bad_daa.gii' + img = gi.GiftiImage() + + with InTemporaryDirectory(): + save(img, fn) + with open(fn, 'r') as fp: + txt = fp.read() + # Make a bad gifti. + txt = txt.replace('NumberOfDataArrays="0"', 'NumberOfDataArrays ="1"') + with open(fn, 'w') as fp: + fp.write(txt) + + with clear_and_catch_warnings() as w: + warnings.filterwarnings('once', category=UserWarning) + load(fn) + assert_equal(len(w), 1) + assert_equal(img.numDA, 0) diff --git a/nibabel/imageclasses.py b/nibabel/imageclasses.py index 20e67c06ef..7c224e1af9 100644 --- a/nibabel/imageclasses.py +++ b/nibabel/imageclasses.py @@ -10,15 +10,17 @@ import warnings from .analyze import AnalyzeImage -from .spm99analyze import Spm99AnalyzeImage -from .spm2analyze import Spm2AnalyzeImage -from .nifti1 import Nifti1Pair, Nifti1Image -from .nifti2 import Nifti2Pair, Nifti2Image +from .freesurfer import MGHImage +from .gifti import GiftiImage from .minc1 import Minc1Image from .minc2 import Minc2Image -from .freesurfer import MGHImage +from .nifti1 import Nifti1Pair, Nifti1Image +from .nifti2 import Nifti2Pair, Nifti2Image from .parrec import PARRECImage +from .spm99analyze import Spm99AnalyzeImage +from .spm2analyze import Spm2AnalyzeImage from .volumeutils import Recoder + from .optpkg import optional_package _, have_scipy, _ = optional_package('scipy') @@ -27,7 +29,7 @@ all_image_classes = [Nifti1Pair, Nifti1Image, Nifti2Pair, Nifti2Image, Spm2AnalyzeImage, Spm99AnalyzeImage, AnalyzeImage, Minc1Image, Minc2Image, MGHImage, - PARRECImage] + PARRECImage, GiftiImage] # DEPRECATED: mapping of names to classes and class functionality diff --git a/nibabel/tests/test_files_interface.py b/nibabel/tests/test_files_interface.py index 2137de8d09..418cf66763 100644 --- a/nibabel/tests/test_files_interface.py +++ b/nibabel/tests/test_files_interface.py @@ -15,17 +15,20 @@ from .. import Nifti1Image, Nifti1Pair, MGHImage, all_image_classes from ..externals.six import BytesIO from ..fileholders import FileHolderError +from ..spatialimages import SpatialImage from nose.tools import (assert_true, assert_false, assert_equal, assert_raises) from numpy.testing import assert_array_equal, assert_array_almost_equal -def test_files_images(): +def test_files_spatialimages(): # test files creation in image classes arr = np.zeros((2,3,4)) aff = np.eye(4) - for klass in all_image_classes: + klasses = [klass for klass in all_image_classes + if klass.rw and issubclass(klass, SpatialImage)] + for klass in klasses: file_map = klass.make_file_map() for key, value in file_map.items(): assert_equal(value.filename, None) @@ -81,11 +84,12 @@ def test_files_interface(): assert_array_equal(img2.get_data(), img.get_data()) -def test_round_trip(): +def test_round_trip_spatialimages(): # write an image to files data = np.arange(24, dtype='i4').reshape((2, 3, 4)) aff = np.eye(4) - klasses = filter(lambda klass: klass.rw, all_image_classes) + klasses = [klass for klass in all_image_classes + if klass.rw and issubclass(klass, SpatialImage)] for klass in klasses: file_map = klass.make_file_map() for key in file_map: diff --git a/nibabel/tests/test_image_load_save.py b/nibabel/tests/test_image_load_save.py index 362c15313b..dbaf76a272 100644 --- a/nibabel/tests/test_image_load_save.py +++ b/nibabel/tests/test_image_load_save.py @@ -27,6 +27,7 @@ from ..tmpdirs import InTemporaryDirectory from ..volumeutils import native_code, swapped_code from ..optpkg import optional_package +from ..spatialimages import SpatialImage from numpy.testing import assert_array_equal, assert_array_almost_equal from nose.tools import assert_true, assert_equal @@ -45,17 +46,19 @@ def round_trip(img): return img2 -def test_conversion(): +def test_conversion_spatialimages(): shape = (2, 4, 6) affine = np.diag([1, 2, 3, 1]) + klasses = [klass for klass in all_image_classes + if klass.rw and issubclass(klass, SpatialImage)] for npt in np.float32, np.int16: data = np.arange(np.prod(shape), dtype=npt).reshape(shape) - for r_class in all_image_classes: + for r_class in klasses: if not r_class.makeable: continue img = r_class(data, affine) img.set_data_dtype(npt) - for w_class in all_image_classes: + for w_class in klasses: if not w_class.makeable: continue img2 = w_class.from_image(img)