mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-29 05:35:03 +08:00
Describe plugins using .ini files, so that we don't need to import them to know their capabilities.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
[gtk]
|
||||
provides = imshow, _app_show
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from util import prepare_for_display, window_manager, GuiLockError
|
||||
import plugin
|
||||
|
||||
try:
|
||||
# we try to aquire the gui lock first
|
||||
@@ -41,21 +40,15 @@ else:
|
||||
def destroy(self, widget, data=None):
|
||||
self.mgr.remove_window(self)
|
||||
|
||||
def gtk_imshow(arr):
|
||||
def imshow(arr):
|
||||
arr = prepare_for_display(arr)
|
||||
|
||||
iw = ImageWindow(arr, window_manager)
|
||||
iw.show()
|
||||
|
||||
def gtk_show():
|
||||
def _app_show():
|
||||
if window_manager.has_windows():
|
||||
window_manager.register_callback(gtk.main_quit)
|
||||
gtk.main()
|
||||
else:
|
||||
print 'no images to display'
|
||||
|
||||
|
||||
plugin.register('gtk', show=gtk_imshow, appshow=gtk_show)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[matplotlib]
|
||||
provides = imshow
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import plugin
|
||||
|
||||
try:
|
||||
import matplotlib.pyplot as plt
|
||||
except ImportError, e:
|
||||
print e
|
||||
else:
|
||||
plugin.register('matplotlib', show=plt.imshow, save=plt.imsave)
|
||||
from matplotlib.pyplot import imshow, imsave
|
||||
except ImportError:
|
||||
print "Could not import Matplotlib."
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[pil]
|
||||
provides = imread
|
||||
|
||||
@@ -5,50 +5,45 @@ import numpy as np
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
has_pil = True
|
||||
except ImportError:
|
||||
has_pil = False
|
||||
print 'Could not load Python Imaging Library'
|
||||
else:
|
||||
def imread(fname, as_grey=False, dtype=None):
|
||||
"""Load an image from file.
|
||||
|
||||
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')
|
||||
|
||||
"""
|
||||
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')
|
||||
|
||||
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)
|
||||
|
||||
return np.array(im, dtype=dtype)
|
||||
def palette_is_grayscale(pil_image):
|
||||
"""Return True if PIL image in palette mode is grayscale.
|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
@@ -2,45 +2,45 @@
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ['register', 'use', 'load', 'available', 'call']
|
||||
__all__ = ['use', 'load', 'available', 'call']
|
||||
|
||||
import warnings
|
||||
from ConfigParser import ConfigParser
|
||||
import os.path
|
||||
from glob import glob
|
||||
|
||||
plugin_store = {'read': [],
|
||||
'save': [],
|
||||
'show': [],
|
||||
'appshow': []}
|
||||
plugin_store = {'imread': [],
|
||||
'imsave': [],
|
||||
'imshow': [],
|
||||
'_app_show': []}
|
||||
|
||||
def register(name, **kwds):
|
||||
"""Register an image I/O plugin.
|
||||
plugin_provides = {}
|
||||
plugin_module_name = {}
|
||||
|
||||
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.
|
||||
def _scan_plugins():
|
||||
"""Scan the plugins directory for .ini files and parse them
|
||||
to gather plugin meta-data.
|
||||
|
||||
"""
|
||||
for kind in kwds:
|
||||
if kind not in plugin_store.keys():
|
||||
raise ValueError('Tried to register invalid plugin method.')
|
||||
pd = os.path.dirname(__file__)
|
||||
ini = glob(os.path.join(pd, '*.ini'))
|
||||
|
||||
func = kwds[kind]
|
||||
if not callable(func):
|
||||
raise ValueError('Can only register functions as plugins.')
|
||||
for f in ini:
|
||||
cp = ConfigParser()
|
||||
cp.read(f)
|
||||
name = cp.sections()[0]
|
||||
provides = [s.strip() for s in cp.get(name, 'provides').split(',')]
|
||||
valid_provides = [p for p in provides if p in plugin_store]
|
||||
|
||||
plugin_store[kind].insert(0, (name, func))
|
||||
for p in provides:
|
||||
if not p in plugin_store:
|
||||
print "Plugin `%s` wants to provide non-existent `%s`." \
|
||||
" Ignoring." % (name, p)
|
||||
|
||||
plugin_provides[name] = valid_provides
|
||||
plugin_module_name[name] = os.path.basename(f)[:-4]
|
||||
|
||||
_scan_plugins()
|
||||
|
||||
def call(kind, *args, **kwargs):
|
||||
"""Find the appropriate plugin of 'kind' and execute it.
|
||||
@@ -103,10 +103,16 @@ def use(name, kind=None):
|
||||
kind = plugin_store.keys()
|
||||
else:
|
||||
kind = [kind]
|
||||
if not kind in plugin_provides[name]:
|
||||
raise RuntimeError("Plugin %s does not support `%s`." % \
|
||||
(name, kind))
|
||||
|
||||
if not name in available(loaded=True):
|
||||
raise RuntimeError("No plugin '%s' has been loaded." % name)
|
||||
|
||||
for k in kind:
|
||||
if not k in plugin_store:
|
||||
raise RuntimeError("Could not find plugin for '%s'" % k)
|
||||
raise RuntimeError("'%s' is not a known plugin function." % k)
|
||||
|
||||
funcs = plugin_store[k]
|
||||
|
||||
@@ -115,36 +121,29 @@ def use(name, kind=None):
|
||||
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):
|
||||
def available(loaded=False):
|
||||
"""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.
|
||||
loaded : bool
|
||||
If True, show only those plugins currently loaded. By default,
|
||||
all plugins are shown.
|
||||
|
||||
"""
|
||||
if kind is None:
|
||||
kind = plugin_store.keys()
|
||||
else:
|
||||
kind = [kind]
|
||||
from copy import deepcopy
|
||||
active_plugins = set()
|
||||
for k in plugin_store:
|
||||
for plugin, fname in plugin_store[k]:
|
||||
active_plugins.add(plugin)
|
||||
|
||||
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]]
|
||||
for plugin in plugin_provides:
|
||||
if not loaded or plugin in active_plugins:
|
||||
d[plugin] = [f for f in plugin_provides[plugin] \
|
||||
if not f.startswith('_')]
|
||||
|
||||
return d
|
||||
|
||||
@@ -161,7 +160,21 @@ def load(plugin):
|
||||
plugins : List of available plugins
|
||||
|
||||
"""
|
||||
try:
|
||||
__import__('scikits.image.io._plugins.' + plugin + "_plugin")
|
||||
except ImportError:
|
||||
raise ValueError('Plugin %s not found.' % plugin)
|
||||
if not plugin in plugin_module_name:
|
||||
raise ValueError("Plugin %s not found." % plugin)
|
||||
else:
|
||||
modname = plugin + "_plugin"
|
||||
plugin_module = __import__('scikits.image.io._plugins.' + modname,
|
||||
fromlist=[modname])
|
||||
|
||||
provides = plugin_provides[plugin]
|
||||
for p in provides:
|
||||
if not hasattr(plugin_module, p):
|
||||
print "Plugin %s does not provide %s as advertised. Ignoring." % \
|
||||
(plugin, p)
|
||||
else:
|
||||
store = plugin_store[p]
|
||||
func = getattr(plugin_module, p)
|
||||
if not func in store:
|
||||
store.insert(0, (plugin, func))
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[qt]
|
||||
provides = imshow, _app_show
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import plugin
|
||||
from util import prepare_for_display, window_manager, GuiLockError
|
||||
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
|
||||
try:
|
||||
# We try to aquire the gui lock first or else the gui import might
|
||||
# trample another GUI's PyOS_InputHook.
|
||||
@@ -47,7 +45,7 @@ else:
|
||||
# references to it
|
||||
self.mgr.remove_window(self)
|
||||
|
||||
def qt_imshow(arr):
|
||||
def imshow(arr):
|
||||
global app
|
||||
|
||||
if not app:
|
||||
@@ -58,11 +56,9 @@ else:
|
||||
iw = ImageWindow(arr, window_manager)
|
||||
iw.show()
|
||||
|
||||
def qt_show():
|
||||
def _app_show():
|
||||
global app
|
||||
if app and window_manager.has_windows():
|
||||
app.exec_()
|
||||
else:
|
||||
print 'No images to show. See `imshow`.'
|
||||
|
||||
plugin.register('qt', show=qt_imshow, appshow=qt_show)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[test]
|
||||
provides = imsave, imshow, imread
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import plugin
|
||||
def imread(fname, as_grey=False, dtype=None):
|
||||
assert fname == 'test.png'
|
||||
assert as_grey == True
|
||||
assert dtype == 'i4'
|
||||
|
||||
def save(fname, arr):
|
||||
return fname, arr
|
||||
def imsave(fname, arr):
|
||||
assert fname == 'test.png'
|
||||
assert arr == [1, 2, 3]
|
||||
|
||||
plugin.register('test', save=save)
|
||||
def imshow(arr, plugin_arg=None):
|
||||
assert arr == [1, 2, 3]
|
||||
assert plugin_arg == (1, 2)
|
||||
|
||||
+19
-13
@@ -44,7 +44,7 @@ def imread(fname, as_grey=False, dtype=None, plugin=None, flatten=None,
|
||||
if flatten is not None:
|
||||
as_grey = flatten
|
||||
|
||||
return call_plugin('read', fname, as_grey=as_grey, dtype=dtype,
|
||||
return call_plugin('imread', fname, as_grey=as_grey, dtype=dtype,
|
||||
plugin=plugin, **plugin_args)
|
||||
|
||||
def imsave(fname, arr, plugin=None, **plugin_args):
|
||||
@@ -67,7 +67,7 @@ def imsave(fname, arr, plugin=None, **plugin_args):
|
||||
Passed to the given plugin.
|
||||
|
||||
"""
|
||||
return call_plugin('save', fname, arr, plugin=plugin, **plugin_args)
|
||||
return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args)
|
||||
|
||||
def imshow(arr, plugin=None, **plugin_args):
|
||||
"""Display an image.
|
||||
@@ -87,17 +87,23 @@ def imshow(arr, plugin=None, **plugin_args):
|
||||
Passed to the given plugin.
|
||||
|
||||
"""
|
||||
return call_plugin('show', arr, plugin=plugin, **plugin_args)
|
||||
return call_plugin('imshow', arr, plugin=plugin, **plugin_args)
|
||||
|
||||
def show():
|
||||
'''Launches the event loop of the current gui plugin,
|
||||
and displays all pending images. This is required,
|
||||
when using imshow() from a non-interactive script.
|
||||
Simply make all the calls to imshow() to queue up as many
|
||||
images as you need, then call show(). After the
|
||||
last window is closed, the gui event loop will exit,
|
||||
and you script will continue execution.
|
||||
'''Display pending images.
|
||||
|
||||
If this is called from the interactive terminal,
|
||||
it will block until all windows are closed.'''
|
||||
return call_plugin('appshow')
|
||||
Launch the event loop of the current gui plugin, and display all
|
||||
pending images, queued via `imshow`. This is required when using
|
||||
`imshow` from non-interactive scripts.
|
||||
|
||||
A call to `show` will block execution of code until all windows
|
||||
have been closed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> for i in range(4):
|
||||
... imshow(np.random.random((50, 50))
|
||||
>>> show()
|
||||
|
||||
'''
|
||||
return call_plugin('_app_show')
|
||||
|
||||
@@ -5,55 +5,45 @@ from scikits.image.io._plugins 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('testcase', read=read, save=save, show=show)
|
||||
plugin.register('other', show=show_other)
|
||||
plugin.load('test')
|
||||
|
||||
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='testcase')
|
||||
io.imread('test.png', as_grey=True, dtype='i4', plugin='test')
|
||||
|
||||
def test_save(self):
|
||||
io.imsave('test.png', [1, 2, 3], plugin='testcase')
|
||||
io.imsave('test.png', [1, 2, 3], plugin='test')
|
||||
|
||||
def test_show(self):
|
||||
io.imshow([1, 2, 3], plugin_arg=(1, 2), plugin='testcase')
|
||||
io.imshow([1, 2, 3], plugin_arg=(1, 2), plugin='test')
|
||||
|
||||
def test_use(self):
|
||||
plugin.use('other', 'show')
|
||||
assert io.imshow(None) == 'other'
|
||||
plugin.use('test')
|
||||
|
||||
@raises(RuntimeError)
|
||||
def test_failed_use(self):
|
||||
plugin.use('asd')
|
||||
|
||||
def test_use_priority(self):
|
||||
plugin.use('pil')
|
||||
plug, func = plugin.plugin_store['imread'][0]
|
||||
print plugin.plugin_store
|
||||
assert_equal(plug, 'pil')
|
||||
|
||||
plugin.use('test')
|
||||
plug, func = plugin.plugin_store['imread'][0]
|
||||
print plugin.plugin_store
|
||||
assert_equal(plug, 'test')
|
||||
|
||||
def test_available(self):
|
||||
plugin.use('other', 'show')
|
||||
d = plugin.available('show')
|
||||
assert d['show'][0] == 'other'
|
||||
|
||||
def test_load(self):
|
||||
plugin.load('test')
|
||||
fname, arr = io.imsave('outfile', [1, 2, 3])
|
||||
assert_equal(fname, 'outfile')
|
||||
assert_equal(arr, [1, 2, 3])
|
||||
assert_equal(plugin.available('save')['save'][0], 'test')
|
||||
assert 'qt' in io.plugins()
|
||||
assert 'test' in io.plugins(loaded=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_module_suite()
|
||||
|
||||
Reference in New Issue
Block a user