Skip to content
Snippets Groups Projects
Commit 7242680f authored by Paul McCarthy's avatar Paul McCarthy
Browse files

ImageWrapper data range is internally kept as None until some image

data is read. But the dataRange property will return cal_min/cal_max
if no data has been read. Some documentation added too.
parent 95895993
No related branches found
No related tags found
No related merge requests found
...@@ -6,6 +6,25 @@ ...@@ -6,6 +6,25 @@
# #
"""This module provides the :class:`ImageWrapper` class, which can be used """This module provides the :class:`ImageWrapper` class, which can be used
to manage data access to ``nibabel`` NIFTI images. to manage data access to ``nibabel`` NIFTI images.
There are some confusing terms used in this module, so it may be of use to
get their definitions straight:
- *Coverage*: The portion of an image that has been covered in the data
range calculation. The ``ImageWrapper`` keeps track of
the coverage for individual volumes within a 4D image (or
slices in a 3D image).
- *Slice*: Portion of the image data which is being accessed. A slice
comprises either a tuple of ``slice`` objects (or integers),
or a sequence of ``(low, high)`` tuples, specifying the
index range into each image dimension that is covered by
the slice.
- *Expansion*: A sequence of ``(low, high)`` tuples, specifying an
index range into each image dimension, that is used to
*expand* the *coverage* of an image, based on a given set of
*slices*.
""" """
...@@ -26,10 +45,11 @@ class ImageWrapper(notifier.Notifier): ...@@ -26,10 +45,11 @@ class ImageWrapper(notifier.Notifier):
"""The ``ImageWrapper`` class is a convenience class which manages data """The ``ImageWrapper`` class is a convenience class which manages data
access to ``nibabel`` NIFTI images. The ``ImageWrapper`` class can be access to ``nibabel`` NIFTI images. The ``ImageWrapper`` class can be
used to: used to:
- Control whether the image is loaded into memory, or kept on disk - Control whether the image is loaded into memory, or kept on disk
- Incrementally update the known image data range, as more image - Incrementally update the known image data range, as more image
data is read in. data is read in.
...@@ -87,10 +107,7 @@ class ImageWrapper(notifier.Notifier): ...@@ -87,10 +107,7 @@ class ImageWrapper(notifier.Notifier):
# The current known image data range. This # The current known image data range. This
# gets updated as more image data gets read. # gets updated as more image data gets read.
# We default to whatever is stored in the self.__range = None, None
# header (which may or may not contain useful
# values).
self.__range = (float(hdr['cal_min']), float(hdr['cal_max']))
# The coverage array is used to keep track of # The coverage array is used to keep track of
# the portions of the image which have been # the portions of the image which have been
...@@ -130,7 +147,16 @@ class ImageWrapper(notifier.Notifier): ...@@ -130,7 +147,16 @@ class ImageWrapper(notifier.Notifier):
"""Returns the currently known data range as a tuple of ``(min, max)`` """Returns the currently known data range as a tuple of ``(min, max)``
values. values.
""" """
return tuple(self.__range) # If no image data has been read, we default
# to whatever is stored in the header (which
# may or may not contain useful values).
low, high = self.__range
hdr = self.__image.get_header()
if low is None: low = float(hdr['cal_min'])
if high is None: high = float(hdr['cal_max'])
return low, high
def loadData(self): def loadData(self):
...@@ -147,8 +173,7 @@ class ImageWrapper(notifier.Notifier): ...@@ -147,8 +173,7 @@ class ImageWrapper(notifier.Notifier):
def __getData(self, sliceobj, isTuple=False): def __getData(self, sliceobj, isTuple=False):
"""Retrieves and the image data at the location specified by """Retrieves the image data at the location specified by ``sliceobj``.
``sliceobj``.
:arg sliceobj: Something which can be used to slice an array, or :arg sliceobj: Something which can be used to slice an array, or
a sequence of (low, high) index pairs. a sequence of (low, high) index pairs.
...@@ -161,7 +186,7 @@ class ImageWrapper(notifier.Notifier): ...@@ -161,7 +186,7 @@ class ImageWrapper(notifier.Notifier):
sliceobj = sliceTupleToSliceObj(sliceobj) sliceobj = sliceTupleToSliceObj(sliceobj)
# If the image has not been loaded # If the image has not been loaded
# into memory, we can use the nibabel # into memory, we can use the nibabel
# ArrayProxy. Otheriwse if it is in # ArrayProxy. Otheriwse if it is in
# memory, we can access it directly. # memory, we can access it directly.
# #
...@@ -182,14 +207,15 @@ class ImageWrapper(notifier.Notifier): ...@@ -182,14 +207,15 @@ class ImageWrapper(notifier.Notifier):
the image specified by ``slices``), and updates the known data range the image specified by ``slices``), and updates the known data range
of the image. of the image.
:arg slices: A tuple of of tuples, each tuple being a ``(low, high)`` :arg slices: A tuple of tuples, each tuple being a ``(low, high)``
index pair, one for each dimension in the image. Tuples index pair, one for each dimension in the image.
must be used instead of lists or ``slice`` objects,
because this method is memoized (and ``slice``/``list``
objects are unhashable).
:arg data: The image data at the given ``slices`` (as a ``numpy`` :arg data: The image data at the given ``slices`` (as a ``numpy``
array). array).
.. note:: Tuples must be used instead of lists or ``slice`` objects fo
the ``slices`` argument, because this method is memoized
(and ``slice``/``list`` objects are unhashable).
""" """
oldmin, oldmax = self.__range oldmin, oldmax = self.__range
...@@ -351,10 +377,14 @@ def sliceCovered(slices, coverage, shape): ...@@ -351,10 +377,14 @@ def sliceCovered(slices, coverage, shape):
"""Returns ``True`` if the portion of the image data calculated by """Returns ``True`` if the portion of the image data calculated by
the given ``slices` has already been calculated, ``False`` otherwise. the given ``slices` has already been calculated, ``False`` otherwise.
:arg slices: :arg slices: A sequence of (low, high) index pairs, assumed to cover
:arg coverage: all image dimensions.
:arg shape: :arg coverage: A ``numpy`` array of shape ``(2, nd, nv)`` (where ``nd``
:arg volDim: is the number of dimensions being covered, and ``nv`` is
the number of volumes (or vectors/slices) in the image,
which contains the (low, high) index pairs describing
the current image coverage.
:arg shape: The full image shape.
""" """
numDims = coverage.shape[1] numDims = coverage.shape[1]
...@@ -381,10 +411,16 @@ def sliceCovered(slices, coverage, shape): ...@@ -381,10 +411,16 @@ def sliceCovered(slices, coverage, shape):
def calcExpansion(slices, coverage): def calcExpansion(slices, coverage):
""" """Calculates a series of *expansion* slices, which can be used to expand
the given ``coverage`` so that it includes the given ``slices``.
:arg slices: Slices that the coverage needs to be expanded to cover.
:arg coverage: Current image coverage.
:returns: A list of volume indices, and a corresponding list of
expansions.
""" """
# One per volume
numDims = coverage.shape[1] numDims = coverage.shape[1]
padDims = len(slices) - numDims - 1 padDims = len(slices) - numDims - 1
lowVol, highVol = slices[numDims] lowVol, highVol = slices[numDims]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment