diff --git a/.zenodo.json b/.zenodo.json index 16555380a4..9b4621464f 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -87,6 +87,11 @@ { "name": "Moloney, Brendan" }, + { + "affiliation": "MIT", + "name": "Goncalves, Mathias", + "orcid": "0000-0002-7252-7771" + }, { "name": "Burns, Christopher" }, @@ -130,11 +135,6 @@ { "name": "Baker, Eric M." }, - { - "affiliation": "MIT", - "name": "Goncalves, Mathias", - "orcid": "0000-0002-7252-7771" - }, { "name": "Hayashi, Soichi" }, @@ -244,6 +244,11 @@ "name": "P\u00e9rez-Garc\u00eda, Fernando", "orcid": "0000-0001-9090-3024" }, + { + "affiliation": "Center for Magnetic Resonance Research, University of Minnesota", + "name": "Braun, Henry", + "orcid": "0000-0001-7003-9822" + }, { "name": "Solovey, Igor" }, diff --git a/Changelog b/Changelog index 832e7d0843..fb004c93e3 100644 --- a/Changelog +++ b/Changelog @@ -25,6 +25,33 @@ Eric Larson (EL), Demian Wassermann, and Stephan Gerhard. References like "pr/298" refer to github pull request numbers. +2.5.1 (Monday 23 September 2019) +================================ + +Enhancements +------------ +* Ignore endianness in ``nib-diff`` if values match (pr/799) (YOH, reviewed + by CM) + +Bug fixes +--------- +* Correctly handle Philips DICOMs w/ derived volume (pr/795) (Mathias + Goncalves, reviewed by CM) +* Raise CSA tag limit to 1000, parametrize for future relaxing (pr/798, + backported to 2.5.x in pr/800) (Henry Braun, reviewed by CM, MB) +* Coerce data types to match NIfTI intent codes when writing GIFTI data + arrays (pr/806) (CM, reported by Tom Holroyd) + +Maintenance +----------- +* Require h5py 2.10 for Windows + Python < 3.6 to resolve unexpected dtypes + in Minc2 data (pr/804) (CM, reviewed by YOH) + +API changes and deprecations +---------------------------- +* Deprecate ``nicom.dicomwrappers.Wrapper.get_affine()`` in favor of ``affine`` + property; final removal in nibabel 4.0 (pr/796) (YOH, reviewed by CM) + 2.5.0 (Sunday 4 August 2019) ============================ diff --git a/doc/source/index.rst b/doc/source/index.rst index ce1db32b86..09f09d4883 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -98,6 +98,7 @@ contributed code and discussion (in rough order of appearance): * Matt Cieslak * Egor Pafilov * Jath Palasubramaniam +* Henry Braun License reprise =============== diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index 95b0c4133a..001724edc9 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -264,16 +264,21 @@ def _to_xml_element(self): return DataTag(dataarray, encoding, datatype, ordering).to_xml() -def _data_tag_element(dataarray, encoding, datatype, ordering): +def _data_tag_element(dataarray, encoding, dtype, ordering): """ Creates data tag with given `encoding`, returns as XML element """ import zlib - ord = array_index_order_codes.npcode[ordering] + order = array_index_order_codes.npcode[ordering] enclabel = gifti_encoding_codes.label[encoding] if enclabel == 'ASCII': - da = _arr2txt(dataarray, datatype) + # XXX Accommodating data_tag API + # On removal (nibabel 4.0) drop str case + da = _arr2txt(dataarray, dtype if isinstance(dtype, str) else KIND2FMT[dtype.kind]) elif enclabel in ('B64BIN', 'B64GZ'): - out = dataarray.tostring(ord) + # XXX Accommodating data_tag API - don't try to fix dtype + if isinstance(dtype, str): + dtype = dataarray.dtype + out = np.asanyarray(dataarray, dtype).tostring(order) if enclabel == 'B64GZ': out = zlib.compress(out) da = base64.b64encode(out).decode() @@ -456,11 +461,10 @@ def _to_xml_element(self): if self.coordsys is not None: data_array.append(self.coordsys._to_xml_element()) # write data array depending on the encoding - dt_kind = data_type_codes.dtype[self.datatype].kind data_array.append( _data_tag_element(self.data, gifti_encoding_codes.specs[self.encoding], - KIND2FMT[dt_kind], + data_type_codes.dtype[self.datatype], self.ind_ord)) return data_array diff --git a/nibabel/gifti/tests/test_gifti.py b/nibabel/gifti/tests/test_gifti.py index d6367b30c8..e7bc5f0a16 100644 --- a/nibabel/gifti/tests/test_gifti.py +++ b/nibabel/gifti/tests/test_gifti.py @@ -20,6 +20,7 @@ from nibabel.testing import clear_and_catch_warnings from .test_parse_gifti_fast import (DATA_FILE1, DATA_FILE2, DATA_FILE3, DATA_FILE4, DATA_FILE5, DATA_FILE6) +import itertools def test_gifti_image(): @@ -399,3 +400,20 @@ def test_data_array_round_trip(): gio = GiftiImage.from_file_map(fmap) vertices = gio.darrays[0].data assert_array_equal(vertices, verts) + + +def test_darray_dtype_coercion_failures(): + dtypes = (np.uint8, np.int32, np.int64, np.float32, np.float64) + encodings = ('ASCII', 'B64BIN', 'B64GZ') + for data_dtype, darray_dtype, encoding in itertools.product(dtypes, + dtypes, + encodings): + da = GiftiDataArray(np.arange(10).astype(data_dtype), + encoding=encoding, + intent='NIFTI_INTENT_NODE_INDEX', + datatype=darray_dtype) + gii = GiftiImage(darrays=[da]) + gii_copy = GiftiImage.from_bytes(gii.to_bytes()) + da_copy = gii_copy.darrays[0] + assert_equal(np.dtype(da_copy.data.dtype), np.dtype(darray_dtype)) + assert_array_equal(da_copy.data, da.data) diff --git a/nibabel/nicom/dicomwrappers.py b/nibabel/nicom/dicomwrappers.py index cd0c1daf67..51e8475acc 100755 --- a/nibabel/nicom/dicomwrappers.py +++ b/nibabel/nicom/dicomwrappers.py @@ -22,6 +22,7 @@ from ..openers import ImageOpener from ..onetime import setattr_on_read as one_time from ..pydicom_compat import tag_for_keyword, Sequence +from ..deprecated import deprecate_with_version class WrapperError(Exception): @@ -94,7 +95,7 @@ class Wrapper(object): Methods: - * get_affine() + * get_affine() (deprecated, use affine property instead) * get_data() * get_pixel_array() * is_same_series(other) @@ -103,6 +104,7 @@ class Wrapper(object): Attributes and things that look like attributes: + * affine : (4, 4) array * dcm_data : object * image_shape : tuple * image_orient_patient : (3,2) array @@ -285,18 +287,19 @@ def get(self, key, default=None): """ Get values from underlying dicom data """ return self.dcm_data.get(key, default) + @deprecate_with_version('get_affine method is deprecated.\n' + 'Please use the ``img.affine`` property ' + 'instead.', + '2.5.1', '4.0') def get_affine(self): - """ Return mapping between voxel and DICOM coordinate system + return self.affine - Parameters - ---------- - None + @property + def affine(self): + """ Mapping between voxel and DICOM coordinate system - Returns - ------- - aff : (4,4) affine - Affine giving transformation between voxels in data array and - mm in the DICOM patient coordinate system. + (4, 4) affine matrix giving transformation between voxels in data array + and mm in the DICOM patient coordinate system. """ # rotation matrix already accounts for the ij transpose in the # DICOM image orientation patient transform. So. column 0 is diff --git a/setup.cfg b/setup.cfg index b2b3ecbcf7..874f652234 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,7 +31,7 @@ python_requires = >=3.5.1 install_requires = numpy >=1.12 tests_require = - nose >=0.10.1 + nose >=0.11 mock test_suite = nose.collector zip_safe = False @@ -52,7 +52,7 @@ style = test = coverage mock - nose >=0.10.1 + nose >=0.11 all = %(dicom)s %(doc)s