Skip to content

Commit 0964d6d

Browse files
committed
Merge remote-tracking branch 'upstream/master' into enh/cifti2
* upstream/master: DOC: change notation, add intermediate logic Address more comments. Format string {} not allowed in Python 2.6 Test case and more complete fix for missing variable values. Address more comments, add a test case for 4d float64 data. Fix overly long line. Test some MINC 1 and MINC 2 edge cases. Address comments. Address comments. BF: back out unnecessary change. BF: Some NetCDF variables have no data allocated - they end up having a shape with a single element set to zero. Assigning an _empty_ shape triggers an exception in this case. We now harmlessly avoid the assignment in these cases. BF: use correct defaults for step in get_zooms(). BF: use correct defaults for start and step. BF: Correctly use data type's inherent range if the valid_range attribute is missing. BF: never more dimension names than the variable actually has. It is possible (though incorrect) that the dimorder can be set on a scalar image-min, e.g. This fix avoids triggering an error on these (arguably malformed) files. RF+TST: allow dtype specifiers as fileslice input DOC: add advice for citing nibabel
2 parents 0df5209 + 0f3048c commit 0964d6d

16 files changed

+154
-29
lines changed

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,13 @@ License
7777
Nibabel is licensed under the terms of the MIT license. Some code included
7878
with nibabel is licensed under the BSD license. Please see the COPYING file
7979
in the nibabel distribution.
80+
81+
Citing nibabel
82+
==============
83+
84+
Please see the `available releases`_ for the release of nibabel that you are
85+
using. Recent releases have a Zenodo_ `Digital Object Identifier`_ badge at
86+
the top of the release notes. Click on the badge for more information.
87+
88+
.. _zenodo: https://zenodo.org
89+
.. _Digital Object Identifier: https://en.wikipedia.org/wiki/Digital_object_identifier

doc/source/coordinate_systems.rst

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -515,16 +515,16 @@ We could record the parameters necessary for $f$ as the 3 by 3 matrix, $M$
515515
and the 3 by 1 vector $(a, b, c)$.
516516

517517
In fact, the 4 by 4 image *affine array* does include exactly this
518-
information. If $m_{ij}$ is the value in row $i$ column $j$ of matrix $M$,
518+
information. If $m_{i,j}$ is the value in row $i$ column $j$ of matrix $M$,
519519
then the image affine matrix $A$ is:
520520

521521
.. math::
522522
523523
A =
524524
\begin{bmatrix}
525-
m_{11} & m_{12} & m_{13} & a \\
526-
m_{21} & m_{22} & m_{23} & b \\
527-
m_{31} & m_{32} & m_{33} & c \\
525+
m_{1,1} & m_{1,2} & m_{1,3} & a \\
526+
m_{2,1} & m_{2,2} & m_{2,3} & b \\
527+
m_{3,1} & m_{3,2} & m_{3,3} & c \\
528528
0 & 0 & 0 & 1 \\
529529
\end{bmatrix}
530530
@@ -546,9 +546,9 @@ vectors:
546546
1\\
547547
\end{bmatrix} =
548548
\begin{bmatrix}
549-
m_{11} & m_{12} & m_{13} & a \\
550-
m_{21} & m_{22} & m_{23} & b \\
551-
m_{31} & m_{32} & m_{33} & c \\
549+
m_{1,1} & m_{1,2} & m_{1,3} & a \\
550+
m_{2,1} & m_{2,2} & m_{2,3} & b \\
551+
m_{3,1} & m_{3,2} & m_{3,3} & c \\
552552
0 & 0 & 0 & 1 \\
553553
\end{bmatrix}
554554
\begin{bmatrix}
@@ -614,6 +614,19 @@ matrix. Put another way:
614614
1\\
615615
\end{bmatrix}
616616
617+
A^{-1}\begin{bmatrix}
618+
x\\
619+
y\\
620+
z\\
621+
1\\
622+
\end{bmatrix} = A^{-1} A
623+
\begin{bmatrix}
624+
i\\
625+
j\\
626+
k\\
627+
1\\
628+
\end{bmatrix}
629+
617630
\begin{bmatrix}
618631
i\\
619632
j\\

doc/source/devel/make_release.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,22 @@ Release checklist
292292
Please take a look at the Makefile rules ``devel-src``, ``devel-dsc`` and
293293
``orig-src``.
294294

295+
* Go to: https://github.com/nipy/nibabel/tags and select the new tag, to fill
296+
in the release notes. Copy the relevant part of the Changelog into the
297+
release notes. Click on "Publish release". This will cause Zenodo_ to
298+
generate a new release "upload", including a DOI. After a few minutes, go
299+
to https://zenodo.org/deposit and click on the new release upload. Click on
300+
the "View" button and click on the DOI badge at the right to display the
301+
text for adding a DOI badge in various formats. Copy the DOI Markdown text.
302+
The markdown will look something like this::
303+
304+
[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.60847.svg)](http://dx.doi.org/10.5281/zenodo.60847)
305+
306+
Go back to the Github release page for this release, click "Edit release".
307+
and copy the DOI into the release notes. Click "Update release".
308+
309+
See: https://guides.github.com/activities/citable-code
310+
295311
* Announce to the mailing lists.
296312

297313
.. _setuptools intro: https://pythonhosted.org/an_example_pypi_project/setuptools.html

doc/source/links_names.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
.. _reST: http://docutils.sourceforge.net/rst.html
4343
.. _docutils: http://docutils.sourceforge.net
4444
.. _IPython notebook viewer: http://nbviewer.ipython.org
45+
.. _zenodo: https://zenodo.org
4546

4647
.. Licenses
4748
.. _GPL: https://www.gnu.org/licenses/gpl.html

nibabel/deprecator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def __call__(self, message, since='', until='',
146146
if since:
147147
messages.append('* deprecated from version: ' + since)
148148
if until:
149-
messages.append('* {} {} as of version: {}'.format(
149+
messages.append('* {0} {1} as of version: {2}'.format(
150150
"Raises" if self.is_bad_version(until) else "Will raise",
151151
error_class,
152152
until))

nibabel/externals/netcdf.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ def __init__(self, filename, mode='r', mmap=None, version=1):
210210
self.fp = open(self.filename, '%sb' % mode)
211211
if mmap is None:
212212
mmap = True
213+
try:
214+
self.fp.seek(0, 2)
215+
except ValueError:
216+
self.file_bytes = -1 # Unknown file length (gzip).
217+
else:
218+
self.file_bytes = self.fp.tell()
219+
self.fp.seek(0)
220+
213221
self.use_mmap = mmap
214222
self.version_byte = version
215223

@@ -599,14 +607,22 @@ def _read_var_array(self):
599607
else: # not a record variable
600608
# Calculate size to avoid problems with vsize (above)
601609
a_size = reduce(mul, shape, 1) * size
602-
if self.use_mmap:
610+
if self.file_bytes >= 0 and begin_ + a_size > self.file_bytes:
611+
data = fromstring(b'\x00'*a_size, dtype=dtype_)
612+
elif self.use_mmap:
603613
mm = mmap(self.fp.fileno(), begin_+a_size, access=ACCESS_READ)
604614
data = ndarray.__new__(ndarray, shape, dtype=dtype_,
605615
buffer=mm, offset=begin_, order=0)
606616
else:
607617
pos = self.fp.tell()
608618
self.fp.seek(begin_)
609-
data = fromstring(self.fp.read(a_size), dtype=dtype_)
619+
# Try to read file, which may fail because the data is
620+
# at or past the end of file. In that case, we treat
621+
# this data as zeros.
622+
buf = self.fp.read(a_size)
623+
if len(buf) < a_size:
624+
buf = b'\x00'*a_size
625+
data = fromstring(buf, dtype=dtype_)
610626
data.shape = shape
611627
self.fp.seek(pos)
612628

nibabel/fileslice.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -719,17 +719,19 @@ def fileslice(fileobj, sliceobj, shape, dtype, offset=0, order='C',
719719
Parameters
720720
----------
721721
fileobj : file-like object
722-
binary file-like object. Implements ``read`` and ``seek``
722+
file-like object, opened for reading in binary mode. Implements
723+
``read`` and ``seek``.
723724
sliceobj : object
724-
something that can be used to slice an array as in ``arr[sliceobj]``
725+
something that can be used to slice an array as in ``arr[sliceobj]``.
725726
shape : sequence
726-
shape of full array inside `fileobj`
727-
dtype : dtype object
728-
dtype of array inside `fileobj`
727+
shape of full array inside `fileobj`.
728+
dtype : dtype specifier
729+
dtype of array inside `fileobj`, or input to ``numpy.dtype`` to specify
730+
array dtype.
729731
offset : int, optional
730732
offset of array data within `fileobj`
731733
order : {'C', 'F'}, optional
732-
memory layout of array in `fileobj`
734+
memory layout of array in `fileobj`.
733735
heuristic : callable, optional
734736
function taking slice object, axis length, stride length as arguments,
735737
returning one of 'full', 'contiguous', None. See
@@ -743,7 +745,8 @@ def fileslice(fileobj, sliceobj, shape, dtype, offset=0, order='C',
743745
"""
744746
if is_fancy(sliceobj):
745747
raise ValueError("Cannot handle fancy indexing")
746-
itemsize = dtype.itemsize
748+
dtype = np.dtype(dtype)
749+
itemsize = int(dtype.itemsize)
747750
segments, sliced_shape, post_slicers = calc_slicedefs(
748751
sliceobj, shape, itemsize, offset, order)
749752
n_bytes = reduce(operator.mul, sliced_shape, 1) * itemsize

nibabel/info.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ def cmp_pkg_version(version_str, pkg_version_str=__version__):
170170
Nibabel is licensed under the terms of the MIT license. Some code included
171171
with nibabel is licensed under the BSD license. Please see the COPYING file
172172
in the nibabel distribution.
173+
174+
Citing nibabel
175+
==============
176+
177+
Please see the `available releases`_ for the release of nibabel that you are
178+
using. Recent releases have a Zenodo_ `Digital Object Identifier`_ badge at
179+
the top of the release notes. Click on the badge for more information.
180+
181+
.. _zenodo: https://zenodo.org
182+
.. _Digital Object Identifier: https://en.wikipedia.org/wiki/Digital_object_identifier
173183
"""
174184

175185
# versions for dependencies. Check these against:

nibabel/minc1.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ def get_data_shape(self):
9595
def get_zooms(self):
9696
""" Get real-world sizes of voxels """
9797
# zooms must be positive; but steps in MINC can be negative
98-
return tuple(
99-
[abs(float(dim.step)) for dim in self._dims])
98+
return tuple([abs(float(dim.step)) if hasattr(dim, 'step') else 1.0
99+
for dim in self._dims])
100100

101101
def get_affine(self):
102102
nspatial = len(self._spatial_dims)
@@ -106,13 +106,11 @@ def get_affine(self):
106106
dim_names = list(self._dim_names) # for indexing in loop
107107
for i, name in enumerate(self._spatial_dims):
108108
dim = self._dims[dim_names.index(name)]
109-
try:
110-
dir_cos = dim.direction_cosines
111-
except AttributeError:
112-
dir_cos = _default_dir_cos[name]
113-
rot_mat[:, i] = dir_cos
114-
steps[i] = dim.step
115-
starts[i] = dim.start
109+
rot_mat[:, i] = (dim.direction_cosines
110+
if hasattr(dim, 'direction_cosines')
111+
else _default_dir_cos[name])
112+
steps[i] = dim.step if hasattr(dim, 'step') else 1.0
113+
starts[i] = dim.start if hasattr(dim, 'start') else 0.0
116114
origin = np.dot(rot_mat, starts)
117115
aff = np.eye(nspatial + 1)
118116
aff[:nspatial, :nspatial] = rot_mat * steps

nibabel/minc2.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ def _get_dimensions(self, var):
7777
dimorder = var.attrs['dimorder'].decode()
7878
except KeyError: # No specified dimensions
7979
return []
80-
return dimorder.split(',')
80+
# The dimension name list must contain only as many entries
81+
# as the variable has dimensions. This reduces errors when an
82+
# unnecessary dimorder attribute is left behind.
83+
return dimorder.split(',')[:len(var.shape)]
8184

8285
def get_data_dtype(self):
8386
return self._image.dtype
@@ -95,7 +98,7 @@ def _get_valid_range(self):
9598
info = np.iinfo(ddt.type)
9699
try:
97100
valid_range = self._image.attrs['valid_range']
98-
except AttributeError:
101+
except (AttributeError, KeyError):
99102
valid_range = [info.min, info.max]
100103
else:
101104
if valid_range[0] < info.min or valid_range[1] > info.max:

0 commit comments

Comments
 (0)