Added a MultiImg class similar to the ImageCollection one, to function as the data structure for multi-image files.

This commit is contained in:
Ralf Gommers
2009-10-09 19:07:28 +02:00
parent b35220f33c
commit 52f10ea293
+172 -40
View File
@@ -2,33 +2,39 @@
from __future__ import with_statement
__all__ = ['Image','ImageCollection','imread']
__all__ = ['Img', 'ImgCollection', 'imread']
from glob import glob
import os.path
import numpy as N
from numpy.testing import set_local_path, restore_path
from scipy.misc.pilutil import imread
import numpy as np
from pil_imread import imread
set_local_path('../..')
import supreme
from supreme.lib import EXIF
restore_path()
try:
from PIL import Image
except ImportError:
raise ImportError("Could not import the Python Imaging Library (PIL)"
" required to load image files. Please refer to"
" http://pypi.python.org/pypi/PIL/ for installation"
" instructions.")
class Image(N.ndarray):
#import supreme
#from supreme.lib import EXIF
class Img(np.ndarray):
"""Image data with tags."""
tags = {'filename' : '',
'EXIF' : {},
'info' : {}}
def __new__(image_cls,arr,**kwargs):
def __new__(image_cls, arr, **kwargs):
"""Set the image data and tags according to given parameters.
Input:
------
`image_cls` : Image class specification
`image_cls` : Img class specification
This is not normally specified by the user.
`arr` : ndarray
Image data.
@@ -36,42 +42,168 @@ class Image(N.ndarray):
Specified in the form ``tag0=value``, ``tag1=value``.
"""
x = N.asarray(arr).view(image_cls)
for tag,value in Image.tags.items():
setattr(x,tag,kwargs.get(tag,getattr(arr,tag,value)))
x = np.asarray(arr).view(image_cls)
for tag, value in Img.tags.items():
setattr(x, tag, kwargs.get(tag, getattr(arr, tag, value)))
return x
def __array_finalize__(self, obj):
"""Copy object tags."""
for tag,value in Image.tags.items():
setattr(self,tag,getattr(obj,tag,value))
for tag, value in Img.tags.items():
setattr(self, tag, getattr(obj, tag, value))
return
def __reduce__(self):
object_state = list(N.ndarray.__reduce__(self))
object_state = list(np.ndarray.__reduce__(self))
subclass_state = {}
for tag in self.tags:
subclass_state[tag] = getattr(self,tag)
object_state[2] = (object_state[2],subclass_state)
subclass_state[tag] = getattr(self, tag)
object_state[2] = (object_state[2], subclass_state)
return tuple(object_state)
def __setstate__(self,state):
nd_state,subclass_state = state
N.ndarray.__setstate__(self,nd_state)
def __setstate__(self, state):
nd_state, subclass_state = state
np.ndarray.__setstate__(self, nd_state)
for tag in subclass_state:
setattr(self,tag,subclass_state[tag])
setattr(self, tag, subclass_state[tag])
@property
def exposure(self):
"""Return exposure time based on EXIF tag."""
exposure = self.EXIF['EXIF ExposureTime'].values[0]
return exposure.num / float(exposure.den)
#@property
#def exposure(self):
# """Return exposure time based on EXIF tag."""
# exposure = self.EXIF['EXIF ExposureTime'].values[0]
# return exposure.num / float(exposure.den)
class ImageCollection(object):
class MultiImg(object):
"""A class containing a single multi-image.
Parameters
----------
filename : str
The complete path to the image file.
conserve_memory : bool, optional
Whether to conserve memory by only caching a single frame. Default is
True.
Attributes
----------
filename : str
The complete path to the image file.
conserve_memory : bool
Whether memory is conserved by only caching a single frame.
numframes : int
The number of frames in the image.
Notes
-----
If ``conserve_memory=True`` the memory footprint can be reduced, however
the performance can be affected because frames have to be read from file
more often.
The last accessed frame is cached, all other frames will have to be read
from file.
Examples
--------
::
>>> img = MultiImg(fname)
>>> img.numframes
3
>>> for frame in img:
... frame.shape
(576, 384)
(576, 384)
(576, 384)
"""
def __init__(self, filename, conserve_memory=True):
"""Load a multi-img"""
self.filename = filename
self.conserve_memory = conserve_memory
self._cached = None
img = Image.open(self.filename)
if self.conserve_memory:
self.numframes = self._find_numframes(img)
else:
self._frames = self._getallframes(img)
self.numframes = len(self._frames)
def _find_numframes(self, img):
"""Find the number of frames in the multi-img."""
try:
i = 0
while True:
i += 1
img.seek(i)
except EOFError:
return i
def _getframe(self, framenum):
"""Open the image and extract the frame."""
img = Image.open(self.filename)
img.seek(framenum)
return np.asarray(img)
def _getallframes(self, img):
"""Extract all frames from the multi-img."""
frames = []
try:
i = 0
while True:
frames.append(np.asarray(img))
i += 1
img.seek(i)
except EOFError:
return frames
def __getitem__(self, n):
"""Return the n-th frame as an array.
Parameters
----------
n : int
Number of the required frame.
Returns
-------
frame : ndarray
The n-th frame.
"""
numframes = self.numframes
if -numframes <= n < numframes:
n = n% numframes
else:
raise IndexError, "There are only %s frames in the image"%numframes
if self.conserve_memory:
if not self._cached == n:
frame = self._getframe(n)
self._cached = n
self._cachedframe = frame
return self._cachedframe
else:
return self._frames[n]
def __iter__(self):
"""Iterate over the frames."""
for i in range(len(self)):
yield self[i]
def __len__(self):
"""Number of images in collection."""
return self.numframes
def __str__(self):
return str(self.filename)
class ImgCollection(object):
"""Load and manage a collection of images."""
def __init__(self,file_pattern,conserve_memory=True,grey=False):
def __init__(self, file_pattern, conserve_memory=True, grey=False):
"""Load image files.
Note that files are always stored in alphabetical order.
@@ -89,9 +221,9 @@ class ImageCollection(object):
Example:
--------
>>> from os.path import dirname, join
>>> data_dir = join(dirname(__file__),'tests')
>>> data_dir = join(dirname(__file__), 'tests')
>>> c = ImageCollection(data_dir + '/*.png')
>>> c = ImgCollection(data_dir + '/*.png')
>>> len(c)
3
>>> c[2].shape
@@ -107,9 +239,9 @@ class ImageCollection(object):
self.conserve_memory = conserve_memory
self.grey = grey
self.data = N.empty(memory_slots,dtype=object)
self.data = np.empty(memory_slots, dtype=object)
def __getitem__(self,n,_cached=N.array(-1)):
def __getitem__(self, n, _cached=np.array(-1)):
"""Return image n in the queue.
Loading is done on demand.
@@ -127,14 +259,14 @@ class ImageCollection(object):
"""
idx = n % len(self.data)
if (_cached != n and self.conserve_memory) or (self.data[idx] is None):
image_data = imread(self.files[n],self.grey)
image_data = imread(self.files[n], self.grey)
with file(self.files[n]) as f:
exif = EXIF.process_file(f)
#with file(self.files[n]) as f:
#exif = EXIF.process_file(f)
self.data[idx] = Image(image_data,
filename=os.path.basename(self.files[n]),
EXIF=exif,info={})
#self.data[idx] = Img(image_data,
#filename=os.path.basename(self.files[n]),
#EXIF=exif, info={})
_cached.flat = n