Merge plugin branch.

This commit is contained in:
Stefan van der Walt
2009-11-01 17:55:45 +02:00
9 changed files with 356 additions and 71 deletions
+5 -1
View File
@@ -1,7 +1,11 @@
"""Utilities to read and write images in various formats."""
import pil_plugin
import matplotlib_plugin
from plugin import register as register_plugin
from pil_imread import *
from sift import *
from collection import *
from io import *
+3 -2
View File
@@ -8,7 +8,7 @@ from glob import glob
import os.path
import numpy as np
from pil_imread import imread
from io import imread
from PIL import Image
@@ -266,7 +266,8 @@ class ImageCollection(object):
n = self._check_imgnum(n)
idx = n % len(self.data)
if (self.conserve_memory and n != self._cached) or (self.data[idx] is None):
if (self.conserve_memory and n != self._cached) or \
(self.data[idx] is None):
self.data[idx] = imread(self.files[n], self.as_grey,
dtype=self._dtype)
self._cached = n
+90
View File
@@ -0,0 +1,90 @@
__all__ = ['imread', 'imsave', 'imshow']
from scikits.image.io import plugin as _plugin
def imread(fname, as_grey=False, dtype=None, plugin=None, flatten=None,
**plugin_args):
"""Load an image from file.
Parameters
----------
fname : string
Image file name, e.g. ``test.jpg``.
as_grey : bool
If True, convert color images to grey-scale. If `dtype` is not given,
converted color images are returned as 32-bit float images.
Images that are already in grey-scale format are not converted.
dtype : dtype, optional
NumPy data-type specifier. If given, the returned image has this type.
If None (default), the data-type is determined automatically.
plugin : str
Name of plugin to use. By default, the different plugins are
tried (starting with the Python Imaging Library) until a suitable
candidate is found.
Other Parameters
----------------
flatten : bool
Backward compatible keyword, superseded by `as_grey`.
Returns
-------
img_array : ndarray
The different colour bands/channels are stored in the
third dimension, such that a grey-image is MxN, an
RGB-image MxNx3 and an RGBA-image MxNx4.
Other parameters
----------------
plugin_args : keywords
Passed to the given plugin.
"""
# Backward compatibility
if flatten is not None:
as_grey = flatten
return _plugin.call('read', fname, as_grey=as_grey, dtype=dtype,
plugin=plugin, **plugin_args)
def imsave(fname, arr, plugin=None, **plugin_args):
"""Save an image to file.
Parameters
----------
fname : str
Target filename.
arr : ndarray of shape (M,N) or (M,N,3) or (M,N,4)
Image data.
plugin : str
Name of plugin to use. By default, the different plugins are
tried (starting with the Python Imaging Library) until a suitable
candidate is found.
Other parameters
----------------
plugin_args : keywords
Passed to the given plugin.
"""
return _plugin.call('save', fname, arr, plugin=plugin, **plugin_args)
def imshow(arr, plugin=None, **plugin_args):
"""Display an image.
Parameters
----------
arr : ndarray
Image data.
plugin : str
Name of plugin to use. By default, the different plugins are
tried (starting with the Python Imaging Library) until a suitable
candidate is found.
Other parameters
----------------
plugin_args : keywords
Passed to the given plugin.
"""
return _plugin.call('show', arr, plugin=plugin, **plugin_args)
+8
View File
@@ -0,0 +1,8 @@
import plugin
try:
import matplotlib.pyplot as plt
except ImportError:
pass
else:
plugin.register('matplotlib', show=plt.imshow, save=plt.imsave)
-67
View File
@@ -1,67 +0,0 @@
__all__ = ['imread']
import numpy as np
def imread(fname, flatten=False, dtype=None):
"""Load an image from file.
Parameters
----------
fname : string
Image file name, e.g. ``test.jpg``.
flatten : bool
If True, convert color images to grey-scale. If `dtype` is not given,
converted color images are returned as 32-bit float images.
Images that are already in grey-scale format are not converted.
dtype : dtype, optional
NumPy data-type specifier. If given, the returned image has this type.
If None (default), the data-type is determined automatically.
Returns
-------
img_array : ndarray
The different colour bands/channels are stored in the
third dimension, such that a grey-image is MxN, an
RGB-image MxNx3 and an RGBA-image MxNx4.
"""
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.")
im = Image.open(fname)
if im.mode == 'P':
if palette_is_grayscale(im):
im = im.convert('L')
else:
im = im.convert('RGB')
if flatten and not im.mode in ('1', 'L', 'I', 'F', 'I;16', 'I;16L', 'I;16B'):
im = im.convert('F')
return np.array(im, dtype=dtype)
def palette_is_grayscale(pil_image):
"""Return True if PIL image in palette mode is grayscale.
Parameters
----------
pil_image : PIL image
PIL Image that is in Palette mode.
Returns
-------
is_grayscale : bool
True if all colors in image palette are gray.
"""
assert pil_image.mode == 'P'
# get palette as an array with R, G, B columns
palette = np.asarray(pil_image.getpalette()).reshape((256, 3))
# Not all palette colors are used; unused colors have junk values.
start, stop = pil_image.getextrema()
valid_palette = palette[start:stop]
# Image is grayscale if channel differences (R - G and G - B) are all zero.
return np.allclose(np.diff(valid_palette), 0)
+54
View File
@@ -0,0 +1,54 @@
__all__ = ['imread']
import numpy as np
import plugin
try:
from PIL import Image
has_pil = True
except ImportError:
has_pil = False
def imread(fname, as_grey=False, dtype=None):
"""Load an image from file.
"""
im = Image.open(fname)
if im.mode == 'P':
if palette_is_grayscale(im):
im = im.convert('L')
else:
im = im.convert('RGB')
if as_grey and not \
im.mode in ('1', 'L', 'I', 'F', 'I;16', 'I;16L', 'I;16B'):
im = im.convert('F')
return np.array(im, dtype=dtype)
def palette_is_grayscale(pil_image):
"""Return True if PIL image in palette mode is grayscale.
Parameters
----------
pil_image : PIL image
PIL Image that is in Palette mode.
Returns
-------
is_grayscale : bool
True if all colors in image palette are gray.
"""
assert pil_image.mode == 'P'
# get palette as an array with R, G, B columns
palette = np.asarray(pil_image.getpalette()).reshape((256, 3))
# Not all palette colors are used; unused colors have junk values.
start, stop = pil_image.getextrema()
valid_palette = palette[start:stop]
# Image is grayscale if channel differences (R - G and G - B)
# are all zero.
return np.allclose(np.diff(valid_palette), 0)
if has_pil:
plugin.register('PIL', read=imread)
+143
View File
@@ -0,0 +1,143 @@
"""Handle image reading, writing and plotting plugins.
"""
__all__ = ['register', 'use']
import warnings
plugin_store = {'read': [],
'save': [],
'show': []}
def register(name, **kwds):
"""Register an image I/O plugin.
Parameters
----------
name : str
Name of this plugin.
read : callable, optional
Function with signature
``read(filename, as_grey=False, dtype=None, **plugin_specific_args)``
that reads images.
save : callable, optional
Function with signature
``write(filename, arr, **plugin_specific_args)``
that writes an image to disk.
show : callable, optional
Function with signature
``show(X, **plugin_specific_args)`` that displays an image.
"""
for kind in kwds:
if kind not in plugin_store.keys():
raise ValueError('Tried to register invalid plugin method.')
func = kwds[kind]
if not callable(func):
raise ValueError('Can only register functions as plugins.')
plugin_store[kind].append((name, func))
def call(kind, *args, **kwargs):
"""Find the appropriate plugin of 'kind' and execute it.
Parameters
----------
kind : {'show', 'save', 'read'}
Function to look up.
plugin : str, optional
Plugin to load. Defaults to None, in which case the first
matching plugin is used.
*args, **kwargs : arguments and keyword arguments
Passed to the plugin function.
"""
if not kind in plugin_store:
raise ValueError('Invalid function (%s) requested.' % kind)
plugin_funcs = plugin_store[kind]
if len(plugin_funcs) == 0:
raise RuntimeError('No suitable plugin registered for %s' % kind)
plugin = kwargs.pop('plugin', None)
if plugin is None:
_, func = plugin_funcs[0]
else:
try:
func = [f for (p,f) in plugin_funcs if p == plugin][0]
except IndexError:
raise RuntimeError('Could not find the plugin "%s" for %s.' % \
(plugin, kind))
return func(*args, **kwargs)
def use(name, kind=None):
"""Set the default plugin for a specified operation.
Parameters
----------
name : str
Name of plugin.
kind : {'save', 'read', 'show'}, optional
Set the plugin for this function. By default,
the plugin is set for all functions.
Examples
--------
Use Python Imaging Library to read images:
>>> from scikits.image.io import plugin
>>> plugin.use('PIL', 'read')
"""
if kind is None:
kind = plugin_store.keys()
else:
kind = [kind]
for k in kind:
if not k in plugin_store:
raise RuntimeError("Could not find plugin for '%s'" % k)
funcs = plugin_store[k]
# Shuffle the plugins so that the requested plugin stands first
# in line
funcs = [(n, f) for (n, f) in funcs if n == name] + \
[(n, f) for (n, f) in funcs if n != name]
n, f = funcs[0]
if not n == name:
warnings.warn(RuntimeWarning('Could not set plugin "%s" for'
' function "%s".' % (name, k)))
plugin_store[k] = funcs
def available(kind=None):
"""List available plugins.
Parameters
----------
kind : {'show', 'save', 'read'}, optional
Display the plugin list for the given function type. If not specified,
return a dictionary with the plugins for all functions.
"""
if kind is None:
kind = plugin_store.keys()
else:
kind = [kind]
d = {}
for k in kind:
if not k in plugin_store:
raise ValueError('No function "%s" exists in the plugin registry.'
% kind)
d[k] = [name for (name, func) in plugin_store[k]]
return d
+1 -1
View File
@@ -3,7 +3,7 @@ import numpy as np
from scikits.image import data_dir
from scikits.image.io import imread
from scikits.image.io.pil_imread import palette_is_grayscale
from scikits.image.io.pil_plugin import palette_is_grayscale
def test_imread_flatten():
# a color image is flattened and returned as float32
+52
View File
@@ -0,0 +1,52 @@
from numpy.testing import *
from scikits.image import io
from scikits.image.io import plugin
from copy import deepcopy
def read(fname, as_grey=False, dtype=None):
assert fname == 'test.png'
assert as_grey == True
assert dtype == 'i4'
def save(fname, arr):
assert fname == 'test.png'
assert arr == [1, 2, 3]
def show(arr, plugin_arg=None):
assert arr == [1, 2, 3]
assert plugin_arg == (1, 2)
def show_other(arr):
return "other"
def setup_module(self):
self.backup_plugin_store = deepcopy(plugin.plugin_store)
plugin.register('test', read=read, save=save, show=show)
plugin.register('other', show=show_other)
def teardown_module(self):
plugin.plugin_store = self.backup_plugin_store
class TestPlugin:
def test_read(self):
io.imread('test.png', as_grey=True, dtype='i4', plugin='test')
def test_save(self):
io.imsave('test.png', [1, 2, 3], plugin='test')
def test_show(self):
io.imshow([1, 2, 3], plugin_arg=(1, 2), plugin='test')
def test_use(self):
plugin.use('other', 'show')
assert io.imshow(None) == 'other'
def test_available(self):
plugin.use('other', 'show')
d = plugin.available('show')
assert d['show'][0] == 'other'
if __name__ == "__main__":
run_module_suite()