mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-15 11:25:53 +08:00
Finish merging multiimage capability into collections
This commit is contained in:
committed by
Stefan van der Walt
parent
b4838c3bd9
commit
76fc909b62
+106
-167
@@ -75,167 +75,6 @@ def alphanumeric_key(s):
|
||||
return k
|
||||
|
||||
|
||||
class MultiImage(object):
|
||||
"""A class containing a single multi-frame 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.
|
||||
|
||||
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.
|
||||
|
||||
The current implementation makes use of PIL.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data_dir
|
||||
|
||||
>>> img = MultiImage(data_dir + '/multipage.tif') # doctest: +SKIP
|
||||
>>> len(img) # doctest: +SKIP
|
||||
2
|
||||
>>> for frame in img: # doctest: +SKIP
|
||||
... print(frame.shape) # doctest: +SKIP
|
||||
(15, 10)
|
||||
(15, 10)
|
||||
|
||||
"""
|
||||
def __init__(self, filename, conserve_memory=True, dtype=None):
|
||||
"""Load a multi-img."""
|
||||
self._filename = filename
|
||||
self._conserve_memory = conserve_memory
|
||||
self._dtype = dtype
|
||||
self._cached = None
|
||||
|
||||
if filename.lower().endswith(('.tiff', '.tif')):
|
||||
self.tif_img = img = TiffFile(self._filename)
|
||||
|
||||
else:
|
||||
img = Image.open(self._filename)
|
||||
self.tif_img = None
|
||||
|
||||
if self._conserve_memory:
|
||||
self._numframes = self._find_numframes(img)
|
||||
|
||||
else:
|
||||
self._frames = self._getallframes()
|
||||
# TODO: this is not correct
|
||||
self._numframes = self._frames.shape[0]
|
||||
|
||||
if not self.tif_img:
|
||||
img.fp.close()
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return self._filename
|
||||
|
||||
@property
|
||||
def conserve_memory(self):
|
||||
return self._conserve_memory
|
||||
|
||||
def _find_numframes(self, img):
|
||||
"""Find the number of frames in the multi-img."""
|
||||
if self.tif_img:
|
||||
return len(img.pages)
|
||||
|
||||
else:
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
try:
|
||||
img.seek(i)
|
||||
except EOFError:
|
||||
break
|
||||
return i
|
||||
|
||||
def _getframe(self, framenum):
|
||||
"""Open the image and extract the frame."""
|
||||
if self.tif_img:
|
||||
return self.tif_img[framenum].asarray()
|
||||
|
||||
else:
|
||||
from ._io import imread
|
||||
return imread(self.filename, self._dtype, img_num=framenum)
|
||||
|
||||
def _getallframes(self):
|
||||
"""Extract all frames from the multi-img."""
|
||||
if self.tif_img:
|
||||
return self.tif_img.asarray()
|
||||
else:
|
||||
from ._io import imread
|
||||
return imread(self.filename, self._dtype)
|
||||
|
||||
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) + ' [%s frames]' % self._numframes
|
||||
|
||||
def concatenate(self):
|
||||
"""Concatenate all images in the multi-image into an array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ar : np.ndarray
|
||||
An array having one more dimension than the images in `self`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
concatenate_images
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If images in the `MultiImage` don't have identical shapes.
|
||||
"""
|
||||
return concatenate_images(self)
|
||||
|
||||
|
||||
class ImageCollection(object):
|
||||
"""Load and manage a collection of image files.
|
||||
|
||||
@@ -295,6 +134,9 @@ class ImageCollection(object):
|
||||
|
||||
ic = ImageCollection('/tmp/*.png', load_func=imread_convert)
|
||||
|
||||
For files with multiple images, the images will be flattened into a list
|
||||
and added to the list of available images.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import skimage.io as io
|
||||
@@ -317,13 +159,16 @@ class ImageCollection(object):
|
||||
for pattern in load_pattern:
|
||||
self._files.extend(glob(pattern))
|
||||
self._files = sorted(self._files, key=alphanumeric_key)
|
||||
self._numframes = self._find_images()
|
||||
else:
|
||||
self._files = load_pattern
|
||||
self._numframes = len(load_pattern)
|
||||
self._frame_index = None
|
||||
|
||||
if conserve_memory:
|
||||
memory_slots = 1
|
||||
else:
|
||||
memory_slots = len(self._files)
|
||||
memory_slots = self._numframes
|
||||
|
||||
self._conserve_memory = conserve_memory
|
||||
self._cached = None
|
||||
@@ -344,6 +189,32 @@ class ImageCollection(object):
|
||||
def conserve_memory(self):
|
||||
return self._conserve_memory
|
||||
|
||||
def _find_images(self):
|
||||
index = []
|
||||
for fname in self._files:
|
||||
if fname.lower().endswith(('.tiff', '.tif')):
|
||||
img = TiffFile(fname)
|
||||
index += [(fname, i) for i in range(len(img.pages))]
|
||||
else:
|
||||
im = Image.open(fname)
|
||||
try:
|
||||
# this will raise an IOError if the file is not readable
|
||||
im.getdata()[0]
|
||||
except IOError:
|
||||
site = "http://pillow.readthedocs.org/en/latest/installation.html#external-libraries"
|
||||
raise ValueError('Could not load "%s"\nPlease see documentation at: %s' % (fname, site))
|
||||
else:
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
index.append((fname, i))
|
||||
try:
|
||||
im.seek(i)
|
||||
except EOFError:
|
||||
break
|
||||
self._frame_index = index
|
||||
return len(index)
|
||||
|
||||
def __getitem__(self, n):
|
||||
"""Return selected image(s) in the collection.
|
||||
|
||||
@@ -374,7 +245,12 @@ class ImageCollection(object):
|
||||
|
||||
if (self.conserve_memory and n != self._cached) or \
|
||||
(self.data[idx] is None):
|
||||
self.data[idx] = self.load_func(self.files[n])
|
||||
if self._frame_index:
|
||||
fname, img_num = self._frame_index[idx]
|
||||
print('loading function')
|
||||
self.data[idx] = self.load_func(fname, img_num=img_num)
|
||||
else:
|
||||
self.data[idx] = self.load_func(self.files[n])
|
||||
self._cached = n
|
||||
|
||||
return self.data[idx]
|
||||
@@ -383,9 +259,17 @@ class ImageCollection(object):
|
||||
# object. Any loaded image data in the original ImageCollection
|
||||
# will be copied by reference to the new object. Image data
|
||||
# loaded after this creation is not linked.
|
||||
fidx = range(len(self.files))[n]
|
||||
fidx = range(self._numframes)[n]
|
||||
new_ic = copy(self)
|
||||
new_ic._files = [self.files[i] for i in fidx]
|
||||
|
||||
if self._frame_index:
|
||||
new_ic._files = [self._frame_index[i][0] for i in fidx]
|
||||
new_ic._frame_index = [self._frame_index[i] for i in fidx]
|
||||
else:
|
||||
new_ic._files = [self._files[i] for i in fidx]
|
||||
|
||||
new_ic._numframes = len(fidx)
|
||||
|
||||
if self.conserve_memory:
|
||||
if self._cached in fidx:
|
||||
new_ic._cached = fidx.index(self._cached)
|
||||
@@ -398,7 +282,7 @@ class ImageCollection(object):
|
||||
|
||||
def _check_imgnum(self, n):
|
||||
"""Check that the given image number is valid."""
|
||||
num = len(self.files)
|
||||
num = self._numframes
|
||||
if -num <= n < num:
|
||||
n = n % num
|
||||
else:
|
||||
@@ -413,7 +297,7 @@ class ImageCollection(object):
|
||||
|
||||
def __len__(self):
|
||||
"""Number of images in collection."""
|
||||
return len(self.files)
|
||||
return self._numframes
|
||||
|
||||
def __str__(self):
|
||||
return str(self.files)
|
||||
@@ -474,3 +358,58 @@ def imread_collection_wrapper(imread):
|
||||
return ImageCollection(load_pattern, conserve_memory=conserve_memory,
|
||||
load_func=imread)
|
||||
return imread_collection
|
||||
|
||||
|
||||
class MultiImage(ImageCollection):
|
||||
|
||||
"""A class containing a single multi-frame 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.
|
||||
|
||||
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.
|
||||
|
||||
The current implementation makes use of ``tifffile`` for Tiff files and
|
||||
PIL otherwise.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data_dir
|
||||
|
||||
>>> img = MultiImage(data_dir + '/multipage.tif') # doctest: +SKIP
|
||||
>>> len(img) # doctest: +SKIP
|
||||
2
|
||||
>>> for frame in img: # doctest: +SKIP
|
||||
... print(frame.shape) # doctest: +SKIP
|
||||
(15, 10)
|
||||
(15, 10)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, filename, conserve_memory=True, dtype=None):
|
||||
"""Load a multi-img."""
|
||||
from ._io import imread
|
||||
|
||||
def load_func(fname, **kwargs):
|
||||
kwargs.setdefault('dtype', dtype)
|
||||
return imread(fname, **kwargs)
|
||||
|
||||
self._filename = filename
|
||||
super(MultiImage, self).__init__(filename, conserve_memory,
|
||||
load_func=load_func)
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return self._filename
|
||||
|
||||
Reference in New Issue
Block a user