Skip to content

2.2.1 bug fix release #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Nov 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fb7b016
Bump dev version
effigies Oct 13, 2017
b7eee37
Merge branch 'maint/2.2.x' into HEAD
effigies Oct 13, 2017
ee7d536
FIX: Set L/R labels in orthoview correctly
effigies Oct 14, 2017
b2c0f81
Merge pull request #564 from effigies/fix/orthoview_lr_labels
effigies Oct 18, 2017
6345d21
TEST: Check only relevant warnings
effigies Oct 24, 2017
0471ffb
STY: Narrow exception check
effigies Oct 24, 2017
d0c9e36
STY: No bare excepts
effigies Oct 24, 2017
b1bf5e7
Merge pull request #568 from effigies/fix/travis
effigies Oct 25, 2017
30f8ddd
BF: defer use of ufunc / memmap test until testing
matthew-brett Oct 28, 2017
2139ce0
Merge pull request #572 from matthew-brett/defer_viral_memmap
effigies Oct 29, 2017
4e128b8
DOC: Explicitly describe behaviour when an affine and a header are sp…
pauldmccarthy Nov 9, 2017
581a143
DOC: Add a new section in the Nifti images user doc page specifying h…
pauldmccarthy Nov 9, 2017
2de656b
DOC: Move that new nifti images section to the bottom
pauldmccarthy Nov 9, 2017
041be2f
DOC: Link to relevant section of user docs from Nifti1Pair docs
pauldmccarthy Nov 9, 2017
d158f50
DOC: typo
pauldmccarthy Nov 9, 2017
1b81908
DOC: Use numpydoc convention, not .rst
pauldmccarthy Nov 9, 2017
d42d7ee
DOC: doctest clause
pauldmccarthy Nov 9, 2017
92a8acb
DOC: Typo
pauldmccarthy Nov 9, 2017
a6b4d83
DOC: Small adjustments to grammar/flow
pauldmccarthy Nov 11, 2017
188b194
DOC: Single backticks when referring to parameters in documentation
pauldmccarthy Nov 11, 2017
084dd1c
Merge pull request #576 from pauldmccarthy/unambiguify_nifti_docs
matthew-brett Nov 11, 2017
15d838e
set sform with set_sform not get_sform
bennet-umich Nov 12, 2017
93cc592
two withs become one
bennet-umich Nov 12, 2017
3d492a6
Merge pull request #580 from justbennet/typo/nifti_images_doc
effigies Nov 12, 2017
3793876
BF: fix doctest failure with pre-release numpy
matthew-brett Nov 18, 2017
0d02553
Merge pull request #582 from matthew-brett/small-fixes
effigies Nov 20, 2017
ee7183e
BF: skip precision test on macOS, newer numpy
matthew-brett Nov 18, 2017
d20388b
Merge pull request #583 from matthew-brett/mac-precision-fix
effigies Nov 21, 2017
f292f59
MAINT: Remove conda from Appveyor script
matthew-brett Nov 22, 2017
d420dcf
Merge pull request #584 from matthew-brett/python-org-appveyor
effigies Nov 22, 2017
95f2116
Merge remote-tracking branch 'upstream/master' into maint/2.2.x
effigies Nov 23, 2017
a053e70
Reset maintenance version
effigies Nov 23, 2017
ac613a8
MAINT: Update changelog, authors
effigies Nov 23, 2017
9b1ead0
MAINT: Remove -dev from version
effigies Nov 23, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ Satrajit Ghosh <[email protected]> Satrajit Ghosh <[email protected]>
Jasper J.F. van den Bosch <[email protected]> Jasper <[email protected]>
Gregory R. Lee <[email protected]> Gregory R. Lee <[email protected]>
Demian Wassermann <[email protected]> Demian Wassermann <[email protected]>
Paul McCarthy <[email protected]> Paul McCarthy <[email protected]>
19 changes: 19 additions & 0 deletions Changelog
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,25 @@ Gerhard (SG) and Eric Larson (EL).

References like "pr/298" refer to github pull request numbers.

2.2.1 (Wednesday 22 November 2017)
==================================

Bug fixes
---------

* Set L/R labels in orthoview correctly (pr/564) (CM)
* Defer use of ufunc / memmap test - allows "freezing" (pr/572) (MB, reviewed
by Satra Ghosh)
* Fix doctest failures with pre-release numpy (pr/582) (MB, reviewed by CM)

Maintenance
-----------

* Update documentation around NIfTI qform/sform codes (pr/576) (Paul McCarthy,
reviewed by MB, CM) + (pr/580) (Bennet Fauber, reviewed by Paul McCarthy)
* Skip precision test on macOS, newer numpy (pr/583) (MB, reviewed by CM)
* Simplify AppVeyor script, removing conda (pr/584) (MB, reviewed by CM)

2.2 (Friday 13 October 2017)
============================

Expand Down
58 changes: 16 additions & 42 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,60 +1,34 @@
# vim ft=yaml
# CI on Windows via appveyor
# This file was based on Olivier Grisel's python-appveyor-demo

environment:

matrix:
- PYTHON: "C:\\Python27-conda32"
PYTHON_VERSION: "2.7"
PYTHON_ARCH: "32"

- PYTHON: "C:\\Python34-conda32"
PYTHON_VERSION: "3.4"
PYTHON_ARCH: "32"

- PYTHON: "C:\\Python34-conda64"
PYTHON_VERSION: "3.4"
PYTHON_ARCH: "64"

- PYTHON: "C:\\Python35-conda64"
PYTHON_VERSION: "3.5"
PYTHON_ARCH: "64"

- PYTHON: "C:\\Python35-conda32"
PYTHON_VERSION: "3.5"
PYTHON_ARCH: "32"
- PYTHON: C:\Python27
- PYTHON: C:\Python27-x64
- PYTHON: C:\Python34
- PYTHON: C:\Python34-x64
- PYTHON: C:\Python35
- PYTHON: C:\Python35-x64
- PYTHON: C:\Python36
- PYTHON: C:\Python36-x64

install:
# Install miniconda Python
- "powershell ./tools/install_python.ps1"

# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"

# Set up a conda environment:
- conda config --set always_yes yes
- conda update -q conda
- conda info -a
- conda create -q -n test-environment python=%PYTHON_VERSION%
- activate test-environment

# Check that we have the expected version and architecture for Python
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
- SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%

# Install the dependencies of the project.
- "conda install --yes --quiet numpy scipy matplotlib nose h5py mock"
- "pip install pydicom"
- "python setup.py install"
- "SET NIBABEL_DATA_DIR=%CD%\\nibabel-data"
- pip install numpy scipy matplotlib nose h5py mock
- pip install pydicom
- pip install .
- SET NIBABEL_DATA_DIR=%CD%\nibabel-data

build: false # Not a C# project, build stuff at the test step instead.

test_script:
# Change into an innocuous directory and find tests from installation
- "mkdir for_testing"
- "cd for_testing"
- "nosetests --with-doctest nibabel"
- mkdir for_testing
- cd for_testing
- nosetests --with-doctest nibabel
1 change: 1 addition & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ contributed code and discussion (in rough order of appearance):
* Venky Reddy
* Mark Hymers
* Jasper J.F. van den Bosch
* Bennet Fauber

License reprise
===============
Expand Down
59 changes: 58 additions & 1 deletion doc/source/nifti_images.rst
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ You can get the affine and the code using the ``coded=True`` argument to
[ 0. , 0.32, 2.17, -7.25],
[ 0. , 0. , 0. , 1. ]]), array(1, dtype=int16))

You can set the sform with with the ``get_sform()`` method of the header and
You can set the sform with the ``set_sform()`` method of the header and
the image.

>>> n1_header.set_sform(np.diag([2, 3, 4, 1]))
Expand Down Expand Up @@ -314,6 +314,63 @@ The algorithm is defined in the ``get_best_affine()`` method. It is:
#. If ``qform_code`` != 0 ('unknown') use the qform affine; else
#. Use the fall-back affine.

.. _default-sform-qform-codes:

Default sform and qform codes
=============================

If you create a new image, e.g.:

>>> data = np.random.random((20, 20, 20))
>>> xform = np.eye(4) * 2
>>> img = nib.nifti1.Nifti1Image(data, xform)

The sform and qform codes will be initialised to 2 (aligned) and 0 (unknown)
respectively:

>>> img.get_sform(coded=True) # doctest: +NORMALIZE_WHITESPACE
(array([[ 2., 0., 0., 0.],
[ 0., 2., 0., 0.],
[ 0., 0., 2., 0.],
[ 0., 0., 0., 1.]]), array(2, dtype=int16))
>>> img.get_qform(coded=True)
(None, 0)

This is based on the assumption that the affine you specify for a newly
created image will align the image to some known coordinate system. According
to the `NIfTI specification <nifti1>`_, the qform is intended to encode a
transformation into scanner coordinates - for a programmatically created
image, we have no way of knowing what the scanner coordinate system is;
furthermore, the qform cannot be used to store an arbitrary affine transform,
as it is unable to encode shears. So the provided affine will be stored in the
sform, and the qform will be left uninitialised.

If you create a new image and specify an existing header, e.g.:

>>> example_ni1 = os.path.join(data_path, 'example4d.nii.gz')
>>> n1_img = nib.load(example_ni1)
>>> new_header = header=n1_img.header.copy()
>>> new_data = np.random.random(n1_img.shape[:3])
>>> new_img = nib.nifti1.Nifti1Image(data, None, header=new_header)

then the newly created image will inherit the same sform and qform codes that
are in the provided header. However, if you create a new image with both an
affine and a header specified, e.g.:

>>> xform = np.eye(4)
>>> new_img = nib.nifti1.Nifti1Image(data, xform, header=new_header)

then the sform and qform codes will *only* be preserved if the provided affine
is the same as the affine in the provided header. If the affines do not match,
the sform and qform codes will be set to their default values of 2 and 0
respectively. This is done on the basis that, if you are changing the affine,
you are likely to be changing the space to which the affine is pointing. So
the original sform and qform codes can no longer be assumed to be valid.

If you wish to set the sform and qform affines and/or codes to some other
value, you can always set them after creation using the ``set_sform`` and
``set_qform`` methods, as described above.

************
Data scaling
************
Expand Down
2 changes: 1 addition & 1 deletion nibabel/benchmarks/bench_arrayproxy_slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def testfunc():
data[:, 1] = [r[4] for r in results]
try:
data[:, 2] = [r[3] / r[4] for r in results]
except:
except ZeroDivisionError:
data[:, 2] = np.nan
data[:, 3] = [r[5] - r[6] for r in results]

Expand Down
12 changes: 6 additions & 6 deletions nibabel/ecat.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,13 +468,13 @@ def get_series_framenumbers(mlist):
mlist_nframes = len(frames_order)
trueframenumbers = np.arange(nframes - mlist_nframes, nframes)
frame_dict = {}
try:
for frame_stored, (true_order, _) in frames_order.items():
# frame as stored in file -> true number in series
for frame_stored, (true_order, _) in frames_order.items():
# frame as stored in file -> true number in series
try:
frame_dict[frame_stored] = trueframenumbers[true_order] + 1
return frame_dict
except:
raise IOError('Error in header or mlist order unknown')
except IndexError:
raise IOError('Error in header or mlist order unknown')
return frame_dict


def read_subheaders(fileobj, mlist, endianness):
Expand Down
4 changes: 2 additions & 2 deletions nibabel/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
_version_major = 2
_version_minor = 2
_version_micro = 1
_version_extra = 'dev'
# _version_extra = ''
# _version_extra = 'dev'
_version_extra = ''

# Format expected by setup.py and doc/source/conf.py: string of form "X.Y.Z"
__version__ = "%s.%s.%s%s" % (_version_major,
Expand Down
2 changes: 1 addition & 1 deletion nibabel/nicom/dwiparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
'''
import numpy as np
import numpy.linalg as npl
from ..testing import setup_test # flake8: noqa F401
from ..testing import setup_test as setup_module # flake8: noqa F401


def B2q(B, tol=None):
Expand Down
15 changes: 14 additions & 1 deletion nibabel/nifti1.py
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,20 @@ def __init__(self, dataobj, affine, header=None,
if header is None and affine is not None:
self._affine2header()
# Copy docstring
__init__.doc = analyze.AnalyzeImage.__init__.__doc__
__init__.__doc__ = analyze.AnalyzeImage.__init__.__doc__ + '''
Notes
-----

If both a `header` and an `affine` are specified, and the `affine` does
not match the affine that is in the `header`, the `affine` will be used,
but the ``sform_code`` and ``qform_code`` fields in the header will be
re-initialised to their default values. This is performed on the basis
that, if you are changing the affine, you are likely to be changing the
space to which the affine is pointing. The :meth:`set_sform` and
:meth:`set_qform` methods can be used to update the codes after an image
has been created - see those methods, and the :ref:`manual
<default-sform-qform-codes>` for more details. '''


def update_header(self):
''' Harmonize header with image data and affine
Expand Down
4 changes: 2 additions & 2 deletions nibabel/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
data_path = abspath(pjoin(dirname(__file__), '..', 'tests', 'data'))


from .np_features import VIRAL_MEMMAP
from .np_features import memmap_after_ufunc

def assert_dt_equal(a, b):
""" Assert two numpy dtype specifiers are equal
Expand Down Expand Up @@ -218,4 +218,4 @@ def setup_test():
"""
from distutils.version import LooseVersion
if LooseVersion(np.__version__) >= LooseVersion('1.14'):
np.set_printoptions(sign='legacy')
np.set_printoptions(legacy="1.13")
16 changes: 10 additions & 6 deletions nibabel/testing/np_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
import numpy as np


def _memmap_after_ufunc():
def memmap_after_ufunc():
""" Return True if ufuncs on memmap arrays always return memmap arrays

This should be True for numpy < 1.12, False otherwise.

Memoize after first call. We do this to avoid having to call this when
importing nibabel.testing, because we cannot depend on the source file
being present - see gh-571.
"""
if memmap_after_ufunc.result is not None:
return memmap_after_ufunc.result
with open(__file__, 'rb') as fobj:
mm_arr = np.memmap(fobj, mode='r', shape=(10,), dtype=np.uint8)
mm_preserved = isinstance(mm_arr + 1, np.memmap)
return mm_preserved

memmap_after_ufunc.result = isinstance(mm_arr + 1, np.memmap)
return memmap_after_ufunc.result

# True if ufunc on memmap always returns a memmap
VIRAL_MEMMAP = _memmap_after_ufunc()
memmap_after_ufunc.result = None
8 changes: 5 additions & 3 deletions nibabel/tests/test_arrayproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from numpy.testing import assert_array_equal, assert_array_almost_equal
from nose.tools import (assert_true, assert_false, assert_equal,
assert_not_equal, assert_raises)
from nibabel.testing import VIRAL_MEMMAP
from nibabel.testing import memmap_after_ufunc

from .test_fileslice import slicer_samples
from .test_openers import patch_indexed_gzip
Expand Down Expand Up @@ -298,6 +298,8 @@ def check_mmap(hdr, offset, proxy_class,
# Whether scaled array memory backed by memory map (regardless of what
# numpy says).
scaled_really_mmap = unscaled_really_mmap and not has_scaling
# Whether ufunc on memmap return memmap
viral_memmap = memmap_after_ufunc()
with InTemporaryDirectory():
with open(fname, 'wb') as fobj:
fobj.write(b' ' * offset)
Expand All @@ -324,9 +326,9 @@ def check_mmap(hdr, offset, proxy_class,
assert_false(back_is_mmap)
else:
assert_equal(unscaled_is_mmap,
VIRAL_MEMMAP or unscaled_really_mmap)
viral_memmap or unscaled_really_mmap)
assert_equal(back_is_mmap,
VIRAL_MEMMAP or scaled_really_mmap)
viral_memmap or scaled_really_mmap)
if scaled_really_mmap:
assert_equal(back_data.mode, expected_mode)
del prox, back_data
Expand Down
9 changes: 8 additions & 1 deletion nibabel/tests/test_floating.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

PY2 = sys.version_info[0] < 3

from distutils.version import LooseVersion

import numpy as np

from ..casting import (floor_exact, ceil_exact, as_int, FloatingError,
Expand Down Expand Up @@ -103,7 +105,12 @@ def test_check_nmant_nexp():
ti = type_info(t)
if ti['nmant'] != 106: # This check does not work for PPC double pair
assert_true(_check_nmant(t, ti['nmant']))
assert_true(_check_maxexp(t, ti['maxexp']))
# Test fails for longdouble after blacklisting of OSX powl as of numpy
# 1.12 - see https://github.com/numpy/numpy/issues/8307
if (t != np.longdouble or
sys.platform != 'darwin' or
LooseVersion(np.__version__) < LooseVersion('1.12')):
assert_true(_check_maxexp(t, ti['maxexp']))


def test_as_int():
Expand Down
2 changes: 1 addition & 1 deletion nibabel/tests/test_image_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@ def validate_shape(self, imaker, params):

def validate_shape_deprecated(self, imaker, params):
# Check deprecated get_shape API
img = imaker()
with clear_and_catch_warnings() as w:
warnings.simplefilter('always', DeprecationWarning)
img = imaker()
assert_equal(img.get_shape(), params['shape'])
assert_equal(len(w), 1)

Expand Down
5 changes: 3 additions & 2 deletions nibabel/tests/test_spatialimages.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from .test_helpers import bytesio_round_trip
from ..testing import (clear_and_catch_warnings, suppress_warnings,
VIRAL_MEMMAP)
memmap_after_ufunc)
from ..tmpdirs import InTemporaryDirectory
from .. import load as top_load

Expand Down Expand Up @@ -464,6 +464,7 @@ def get_disk_image(self):
def test_load_mmap(self):
# Test memory mapping when loading images
img_klass = self.image_class
viral_memmap = memmap_after_ufunc()
with InTemporaryDirectory():
img, fname, has_scaling = self.get_disk_image()
file_map = img.file_map.copy()
Expand All @@ -485,7 +486,7 @@ def test_load_mmap(self):
# numpies returned a memmap object, even though the array
# has no mmap memory backing. See:
# https://github.com/numpy/numpy/pull/7406
if has_scaling and not VIRAL_MEMMAP:
if has_scaling and not viral_memmap:
expected_mode = None
kwargs = {}
if mmap is not None:
Expand Down
Loading