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

Image class conditionally uses indexed_gzip module for compressed

files. Log messages added to memoize functions.
parent 9c4ce29a
No related branches found
No related tags found
No related merge requests found
...@@ -289,11 +289,6 @@ class Image(Nifti1, notifier.Notifier): ...@@ -289,11 +289,6 @@ class Image(Nifti1, notifier.Notifier):
access managed by a :class:`.ImageWrapper`. access managed by a :class:`.ImageWrapper`.
.. todo:: If the image appears to be large, it is loaded using the
:mod:`indexed_gzip` module. Implement this, and also write
a note here about what it means.
In addition to the attributes added by the :meth:`Nifti1.__init__` method, In addition to the attributes added by the :meth:`Nifti1.__init__` method,
the following attributes/properties are present on an ``Image`` instance the following attributes/properties are present on an ``Image`` instance
as properties (https://docs.python.org/2/library/functions.html#property): as properties (https://docs.python.org/2/library/functions.html#property):
...@@ -346,7 +341,8 @@ class Image(Nifti1, notifier.Notifier): ...@@ -346,7 +341,8 @@ class Image(Nifti1, notifier.Notifier):
header=None, header=None,
xform=None, xform=None,
loadData=True, loadData=True,
calcRange=True): calcRange=True,
indexed=False):
"""Create an ``Image`` object with the given image data or file name. """Create an ``Image`` object with the given image data or file name.
:arg image: A string containing the name of an image file to load, :arg image: A string containing the name of an image file to load,
...@@ -378,6 +374,10 @@ class Image(Nifti1, notifier.Notifier): ...@@ -378,6 +374,10 @@ class Image(Nifti1, notifier.Notifier):
:meth:`calcRange`). Otherwise, the image range is :meth:`calcRange`). Otherwise, the image range is
incrementally updated as more data is read from memory incrementally updated as more data is read from memory
or disk. or disk.
:arg indexed: If ``True``, and the file is gzipped, it is opened
using the :mod:`indexed_gzip` package. Otherwise the
file is opened by ``nibabel``.
""" """
import nibabel as nib import nibabel as nib
...@@ -388,8 +388,39 @@ class Image(Nifti1, notifier.Notifier): ...@@ -388,8 +388,39 @@ class Image(Nifti1, notifier.Notifier):
# The image parameter may be the name of an image file # The image parameter may be the name of an image file
if isinstance(image, six.string_types): if isinstance(image, six.string_types):
image = op.abspath(addExt(image)) image = op.abspath(addExt(image))
nibImage = nib.load(image)
# Use indexed_gzip to open gzip files
# if requested - this provides fast
# on-disk access to the compressed
# data.
#
# If using indexed_gzip, we store a
# ref to the file object - we'll close
# it when we are destroyed.
if indexed and image.endswith('.gz'):
import indexed_gzip as igzip
log.debug('Loading {} using indexed gzip'.format(image))
fobj = igzip.IndexedGzipFile(
filename=image,
spacing=4194304,
readbuf_size=131072)
fmap = nib.Nifti1Image.make_file_map()
fmap['image'].fileobj = fobj
nibImage = nib.Nifti1Image.from_file_map(fmap)
self.__fileobj = fobj
# Otherwise we let nibabel
# manage the file reference(s)
else:
nibImage = nib.load(image)
self.__fileobj = None
dataSource = image dataSource = image
# Or a numpy array - we wrap it in a nibabel image, # Or a numpy array - we wrap it in a nibabel image,
...@@ -461,6 +492,12 @@ class Image(Nifti1, notifier.Notifier): ...@@ -461,6 +492,12 @@ class Image(Nifti1, notifier.Notifier):
"""See the :meth:`__str__` method.""" """See the :meth:`__str__` method."""
return self.__str__() return self.__str__()
def __del__(self):
"""Closes any open file handles. """
if self.__fileobj is not None:
self.__fileobj.close()
@property @property
def dataSource(self): def dataSource(self):
......
...@@ -16,10 +16,14 @@ a function: ...@@ -16,10 +16,14 @@ a function:
""" """
import logging
import hashlib import hashlib
import functools import functools
import six import six
log = logging.getLogger(__name__)
# TODO Make this a class, and add # TODO Make this a class, and add
# a "clearCache" method to it. # a "clearCache" method to it.
...@@ -72,6 +76,9 @@ def memoize(args=None, kwargs=None): ...@@ -72,6 +76,9 @@ def memoize(args=None, kwargs=None):
result = func(*a, **kwa) result = func(*a, **kwa)
cache[key] = result cache[key] = result
log.debug('Adding to cache[{}]: {}'.format(
key, result))
return result return result
return wrapper return wrapper
...@@ -104,6 +111,9 @@ def memoizeMD5(func): ...@@ -104,6 +111,9 @@ def memoizeMD5(func):
result = func(*args, **kwargs) result = func(*args, **kwargs)
log.debug('Adding to MD5 cache[{}]: {}'.format(
digest, result))
cache[digest] = result cache[digest] = result
return result return result
......
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