Merge branch 'io' of git://github.com/sccolbert/scikits.image

This commit is contained in:
Stefan van der Walt
2009-11-02 20:58:18 +02:00
19 changed files with 9894 additions and 10458 deletions
+2
View File
@@ -4,6 +4,8 @@
*egg-info
*.so
*.bak
*.c
.gitignore
doc/source/api
doc/build
source/api
+5 -4
View File
@@ -1,10 +1,11 @@
"""Utilities to read and write images in various formats."""
import pil_plugin
import matplotlib_plugin
import qt_plugin
from _plugins import load as load_plugin
from _plugins import use as use_plugin
from _plugins import available as plugins
from plugin import register as register_plugin
# Add this plugin so that we can read images by default
load_plugin('pil')
from sift import *
from collection import *
-69
View File
@@ -1,69 +0,0 @@
import numpy as np
# utilities to make life easier for plugin writers.
def prepare_for_display(npy_img):
'''Convert a 2D or 3D numpy array of any dtype into a
3D numpy array with dtype uint8. This array will
be suitable for use in passing to gui toolkits for
image display purposes.
Parameters
----------
npy_img : ndarray, 2D or 3D
The image to convert for display
Returns
-------
out : ndarray, 3D dtype=np.uint8
The converted image. This is guaranteed to be a contiguous array.
Notes
-----
If the input image is floating point, it is assumed that the data
is in the range of 0.0 - 1.0. No check is made to assert this
condition. The image is then scaled to be in the range 0 - 255
and then cast to np.uint8
For all other dtypes, the array is simply cast to np.uint8
If a 2D array is passed, the single channel is replicated
to the 2nd and 3rd channels.
If the array contains an alpha channel, this channel is
ignored.
'''
if len(npy_img.shape) < 2:
raise ValueError('Image must be 2D or 3D array')
height = npy_img.shape[0]
width = npy_img.shape[1]
out = np.empty((height, width, 3), dtype=np.uint8)
if len(npy_img.shape) == 2 or \
(len(npy_img.shape) == 3 and npy_img.shape[2] == 1):
if npy_img.dtype in [np.float32, np.float64]:
out[:,:,0] = npy_img*255
out[:,:,1] = out[:,:,0]
out[:,:,2] = out[:,:,0]
else:
out[:,:,0] = npy_img
out[:,:,1] = npy_img
out[:,:,2] = npy_img
elif len(npy_img.shape) == 3:
if npy_img.shape[2] == 3 or npy_img.shape[2] == 4:
if npy_img.dtype in [np.float32, np.float64]:
out[:,:,:3] = (npy_img[:,:,:3])*255
else:
out[:,:,:3] = npy_img[:,:,:3]
else:
raise ValueError('Image must have 1, 3, or 4 channels')
else:
raise ValueError('Image must have 2 or 3 dimensions')
return out
+1
View File
@@ -0,0 +1 @@
from plugin import *
+61
View File
@@ -0,0 +1,61 @@
from util import prepare_for_display, window_manager, GuiLockError
import plugin
try:
# we try to aquire the gui lock first
# or else the gui import might trample another
# gui's pyos_inputhook.
window_manager.acquire('gtk')
except GuiLockError, gle:
print gle
else:
try:
import gtk
except ImportError:
print 'pygtk libraries not installed.'
print 'plugin not loaded.'
window_manager._release('gtk')
else:
class ImageWindow(gtk.Window):
def __init__(self, arr, mgr):
gtk.Window.__init__(self)
self.mgr = mgr
self.mgr.add_window(self)
self.connect("destroy", self.destroy)
width = arr.shape[1]
height = arr.shape[0]
rstride = arr.strides[0]
pb = gtk.gdk.pixbuf_new_from_data(arr.data,
gtk.gdk.COLORSPACE_RGB,
False, 8, width, height,
rstride)
self.img = gtk.Image()
self.img.set_from_pixbuf(pb)
self.add(self.img)
self.img.show()
def destroy(self, widget, data=None):
self.mgr.remove_window(self)
def gtk_imshow(arr):
arr = prepare_for_display(arr)
iw = ImageWindow(arr, window_manager)
iw.show()
def gtk_show():
if window_manager.has_images():
window_manager.register_callback(gtk.main_quit)
gtk.main()
else:
print 'no images to display'
plugin.register('gtk', show=gtk_imshow, appshow=gtk_show)
@@ -2,7 +2,7 @@ import plugin
try:
import matplotlib.pyplot as plt
except ImportError:
pass
except ImportError, e:
print e
else:
plugin.register('matplotlib', show=plt.imshow, save=plt.imsave)
@@ -1,7 +1,7 @@
__all__ = ['imread']
import numpy as np
import plugin
import numpy as np
try:
from PIL import Image
@@ -51,4 +51,4 @@ def palette_is_grayscale(pil_image):
if has_pil:
plugin.register('PIL', read=imread)
plugin.register('pil', read=imread)
@@ -2,13 +2,14 @@
"""
__all__ = ['register', 'use']
__all__ = ['register', 'use', 'load', 'available', 'call']
import warnings
plugin_store = {'read': [],
'save': [],
'show': []}
'show': [],
'appshow': []}
def register(name, **kwds):
"""Register an image I/O plugin.
@@ -38,7 +39,7 @@ def register(name, **kwds):
if not callable(func):
raise ValueError('Can only register functions as plugins.')
plugin_store[kind].append((name, func))
plugin_store[kind].insert(0, (name, func))
def call(kind, *args, **kwargs):
@@ -60,7 +61,11 @@ def call(kind, *args, **kwargs):
plugin_funcs = plugin_store[kind]
if len(plugin_funcs) == 0:
raise RuntimeError('No suitable plugin registered for %s' % kind)
raise RuntimeError('''No suitable plugin registered for %s.
You may load I/O plugins with the `scikits.image.io.load_plugin`
command. A list of all available plugins can be found using
`scikits.image.io.plugins()`.''' % kind)
plugin = kwargs.pop('plugin', None)
if plugin is None:
@@ -123,8 +128,9 @@ def available(kind=None):
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.
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:
@@ -141,3 +147,21 @@ def available(kind=None):
d[k] = [name for (name, func) in plugin_store[k]]
return d
def load(plugin):
"""Load the given plugin.
Parameters
----------
plugin : str
Name of plugin to load.
See Also
--------
plugins : List of available plugins
"""
try:
__import__('scikits.image.io._plugins.' + plugin + "_plugin")
except ImportError:
raise ValueError('Plugin %s not found.' % plugin)
+73
View File
@@ -0,0 +1,73 @@
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.
window_manager.acquire('qt')
except GuiLockError, gle:
print gle
else:
try:
from PyQt4.QtGui import (QApplication, QMainWindow, QImage, QPixmap,
QLabel)
except ImportError:
print 'pyqt4 libraries not installed.'
print 'plugin not loaded'
window_manager._release('qt')
else:
app = None
class ImageWindow(QMainWindow):
def __init__(self, arr, mgr):
QMainWindow.__init__(self)
self.mgr = mgr
img = QImage(arr.data, arr.shape[1], arr.shape[0],
QImage.Format_RGB888)
pm = QPixmap.fromImage(img)
label = QLabel()
label.setPixmap(pm)
label.show()
self.label = label
self.setCentralWidget(self.label)
self.mgr.add_window(self)
def closeEvent(self, event):
# Allow window to be destroyed by removing any
# references to it
self.mgr.remove_window(self)
def qt_imshow(arr):
global app
if not app:
app = QApplication([])
arr = prepare_for_display(arr)
iw = ImageWindow(arr, window_manager)
iw.show()
def qt_show():
global app
if app and window_manager.has_images():
app.exec_()
else:
print 'no images to show'
plugin.register('qt', show=qt_imshow, appshow=qt_show)
+6
View File
@@ -0,0 +1,6 @@
import plugin
def save(fname, arr):
return fname, arr
plugin.register('test', save=save)
+151
View File
@@ -0,0 +1,151 @@
import numpy as np
# utilities to make life easier for plugin writers.
class GuiLockError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
class WindowManager(object):
''' A class to keep track of spawned windows,
and make any needed callback once all the windows,
are closed.'''
def __init__(self):
self._windows = []
self._callback = None
self._callback_args = ()
self._callback_kwargs = {}
self._gui_lock = False
self._guikit = ''
def _check_locked(self):
if not self._gui_lock:
raise GuiLockError(\
'Must first acquire the gui lock before using this image manager')
def _exec_callback(self):
if self._callback:
self._callback(*self._callback_args, **self._callback_kwargs)
def acquire(self, kit):
if self._gui_lock:
raise GuiLockError(\
'The gui lock can only be acquired by one toolkit per session. \
The lock is already aquired by %s' % self._guikit)
else:
self._gui_lock = True
self._guikit = str(kit)
def _release(self, kit):
# releaseing the lock will lose all references to currently
# track images and callback.
# this function is private for reason!
self._check_locked()
if str(kit) == self._guikit:
self._windows = []
self._callback = None
self._callback_args = ()
self._callback_kwargs = {}
self._gui_lock = False
self._guikit = ''
else:
raise RuntimeError('Only the toolkit that owns the lock may release it')
def add_window(self, win):
self._check_locked()
self._windows.append(win)
def remove_window(self, win):
self._check_locked()
try:
self._windows.remove(win)
except ValueError:
print 'Unable to find referenced window in tracked windows.'
print 'Ignoring...'
else:
if len(self._windows) == 0:
self._exec_callback()
def register_callback(self, cb, *cbargs, **cbkwargs):
self._check_locked()
self._callback = cb
self._callback_args = cbargs
self._callback_kwargs = cbkwargs
def has_images(self):
if len(self._windows) > 0:
return True
else:
return False
window_manager = WindowManager()
def prepare_for_display(npy_img):
'''Convert a 2D or 3D numpy array of any dtype into a
3D numpy array with dtype uint8. This array will
be suitable for use in passing to gui toolkits for
image display purposes.
Parameters
----------
npy_img : ndarray, 2D or 3D
The image to convert for display
Returns
-------
out : ndarray, 3D dtype=np.uint8
The converted image. This is guaranteed to be a contiguous array.
Notes
-----
If the input image is floating point, it is assumed that the data
is in the range of 0.0 - 1.0. No check is made to assert this
condition. The image is then scaled to be in the range 0 - 255
and then cast to np.uint8
For all other dtypes, the array is simply cast to np.uint8
If a 2D array is passed, the single channel is replicated
to the 2nd and 3rd channels.
If the array contains an alpha channel, this channel is
ignored.
'''
if len(npy_img.shape) < 2:
raise ValueError('Image must be 2D or 3D array')
height = npy_img.shape[0]
width = npy_img.shape[1]
out = np.empty((height, width, 3), dtype=np.uint8)
if len(npy_img.shape) == 2 or \
(len(npy_img.shape) == 3 and npy_img.shape[2] == 1):
if npy_img.dtype in [np.float32, np.float64]:
out[:,:,0] = npy_img*255
out[:,:,1] = out[:,:,0]
out[:,:,2] = out[:,:,0]
else:
out[:,:,0] = npy_img
out[:,:,1] = npy_img
out[:,:,2] = npy_img
elif len(npy_img.shape) == 3:
if npy_img.shape[2] == 3 or npy_img.shape[2] == 4:
if npy_img.dtype in [np.float32, np.float64]:
out[:,:,:3] = (npy_img[:,:,:3])*255
else:
out[:,:,:3] = npy_img[:,:,:3]
else:
raise ValueError('Image must have 1, 3, or 4 channels')
else:
raise ValueError('Image must have 2 or 3 dimensions')
return out
+19 -6
View File
@@ -1,6 +1,6 @@
__all__ = ['imread', 'imsave', 'imshow']
__all__ = ['imread', 'imsave', 'imshow', 'show']
from scikits.image.io import plugin as _plugin
from scikits.image.io._plugins import call as call_plugin
def imread(fname, as_grey=False, dtype=None, plugin=None, flatten=None,
**plugin_args):
@@ -44,8 +44,8 @@ def imread(fname, as_grey=False, dtype=None, plugin=None, flatten=None,
if flatten is not None:
as_grey = flatten
return _plugin.call('read', fname, as_grey=as_grey, dtype=dtype,
plugin=plugin, **plugin_args)
return call_plugin('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.
@@ -67,7 +67,7 @@ def imsave(fname, arr, plugin=None, **plugin_args):
Passed to the given plugin.
"""
return _plugin.call('save', fname, arr, plugin=plugin, **plugin_args)
return call_plugin('save', fname, arr, plugin=plugin, **plugin_args)
def imshow(arr, plugin=None, **plugin_args):
"""Display an image.
@@ -87,4 +87,17 @@ def imshow(arr, plugin=None, **plugin_args):
Passed to the given plugin.
"""
return _plugin.call('show', arr, plugin=plugin, **plugin_args)
return call_plugin('show', 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.
If this is called from the interactive terminal,
it will block until all windows are closed.'''
return call_plugin('appshow')
-66
View File
@@ -1,66 +0,0 @@
import plugin
from _plugin_util import prepare_for_display
import numpy as np
import sys
app = None
windows = []
try:
from PyQt4.QtGui import (QApplication, QMainWindow, QImage, QPixmap,
QLabel)
except ImportError:
pass
else:
class ImageWindow(QMainWindow):
def __init__(self, arr):
QMainWindow.__init__(self)
img = QImage(arr.data, arr.shape[1], arr.shape[0],
QImage.Format_RGB888)
pm = QPixmap.fromImage(img)
label = QLabel()
label.setPixmap(pm)
label.show()
self.label = label
self.setCentralWidget(self.label)
def closeEvent(self, event):
# Allow window to be destroyed by removing any
# references to it
windows.remove(self)
def show(arr, block=True):
global app
if not '-qt4thread' in sys.argv and app is None:
app = QApplication([])
arr = prepare_for_display(arr)
iw = ImageWindow(arr)
iw.show()
# Keep track of window so that it doesn't get destroyed
windows.append(iw)
if app and block:
app.exec_()
plugin.register('qt', show=show)
if __name__ == "__main__":
import scikits.image.io as io
io.plugin.use('qt', 'show')
img = np.empty((200, 200, 3), dtype=np.uint8)
img[:50, :50, 0] = 100
img[25:100, 25:100, 1] = 200
img[:, :, 2] = 155
io.imshow(img, block=False)
io.imshow(img)
+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_plugin import palette_is_grayscale
from scikits.image.io._plugins.pil_plugin import palette_is_grayscale
def test_imread_flatten():
# a color image is flattened and returned as float32
+12 -5
View File
@@ -1,7 +1,7 @@
from numpy.testing import *
from scikits.image import io
from scikits.image.io import plugin
from scikits.image.io._plugins import plugin
from copy import deepcopy
@@ -23,7 +23,7 @@ def show_other(arr):
def setup_module(self):
self.backup_plugin_store = deepcopy(plugin.plugin_store)
plugin.register('test', read=read, save=save, show=show)
plugin.register('testcase', read=read, save=save, show=show)
plugin.register('other', show=show_other)
def teardown_module(self):
@@ -31,13 +31,13 @@ def teardown_module(self):
class TestPlugin:
def test_read(self):
io.imread('test.png', as_grey=True, dtype='i4', plugin='test')
io.imread('test.png', as_grey=True, dtype='i4', plugin='testcase')
def test_save(self):
io.imsave('test.png', [1, 2, 3], plugin='test')
io.imsave('test.png', [1, 2, 3], plugin='testcase')
def test_show(self):
io.imshow([1, 2, 3], plugin_arg=(1, 2), plugin='test')
io.imshow([1, 2, 3], plugin_arg=(1, 2), plugin='testcase')
def test_use(self):
plugin.use('other', 'show')
@@ -48,5 +48,12 @@ class TestPlugin:
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')
if __name__ == "__main__":
run_module_suite()
-53
View File
@@ -1,53 +0,0 @@
import numpy as np
import plugin
from _plugin_util import prepare_for_display
try:
import wx
except ImportError:
pass
else:
# idea shamelessly taken from here:
# http://wiki.wxpython.org/WorkingWithImages
class ImagePanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id)
self.bitmap = None
self.Bind(wx.EVT_PAINT, self.OnPaint)
def display(self, npy_img):
self.bitmap = self.get_bitmap(npy_img)
self.Refresh(True)
def OnPaint(self, evt):
dc = wx.PaintDC(self)
if self.bitmap:
dc.DrawBitmap(self.bitmap, 0, 0)
def get_bitmap(self, npy_img):
width = npy_img.shape[1]
height = npy_img.shape[0]
wx_img = wx.EmptyImage(width, height)
wx_img.SetData(npy_img.data)
return wx.BitmapFromImage(wx_img)
class ImageFrame(wx.Frame):
def __init__(self, img):
self.img = img
width = img.shape[1]
height = img.shape[0]
wx.Frame.__init__(self, None, -1, 'wx', wx.DefaultPosition,
wx.Size(width, height))
self.iPanel = ImagePanel(self, -1)
self.iPanel.display(self.img)
def wx_imshow(img):
f = ImageFrame(prepare_for_display(img))
f.CenterOnScreen()
f.Show()
plugin.register('wx', show=wx_imshow)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,2 +1,2 @@
version='unbuilt-dev'
# THIS FILE IS GENERATED FROM THE SCIKITS.IMAGE SETUP.PY
version='0.2dev'