diff --git a/skimage/io/_io.py b/skimage/io/_io.py index fb3e6fbc..ee01e405 100644 --- a/skimage/io/_io.py +++ b/skimage/io/_io.py @@ -10,7 +10,8 @@ from ..exposure import is_low_contrast from .._shared.utils import all_warnings, warn -__all__ = ['imread', 'imread_collection', 'imsave', 'imshow', 'show'] +__all__ = ['imread', 'imsave', 'imshow', 'show', + 'imread_collection', 'imshow_collection'] def imread(fname, as_grey=False, plugin=None, flatten=None, @@ -155,6 +156,26 @@ def imshow(arr, plugin=None, **plugin_args): return call_plugin('imshow', arr, plugin=plugin, **plugin_args) +def imshow_collection(ic, plugin=None, **plugin_args): + """Display a collection of images. + + Parameters + ---------- + ic : ImageCollection + Collection to display. + plugin : str + Name of plugin to use. By default, the different plugins are + tried until a suitable candidate is found. + + Other parameters + ---------------- + plugin_args : keywords + Passed to the given plugin. + + """ + return call_plugin('imshow_collection', ic, plugin=plugin, **plugin_args) + + def show(): '''Display pending images. diff --git a/skimage/io/_plugins/matplotlib_plugin.ini b/skimage/io/_plugins/matplotlib_plugin.ini index 1c29e215..a58b5aeb 100644 --- a/skimage/io/_plugins/matplotlib_plugin.ini +++ b/skimage/io/_plugins/matplotlib_plugin.ini @@ -1,4 +1,3 @@ [matplotlib] description = Display or save images using Matplotlib -provides = imshow, imread, _app_show - +provides = imshow, imread, imshow_collection, _app_show diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 34afb7ff..f54ac74b 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -147,6 +147,7 @@ def imshow(im, ax=None, show_cbar=None, **kwargs): if kwargs.get('cmap', None) == 'viridis': kwargs['cmap'] = viridis lo, hi, cmap = _get_display_range(im) + kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('cmap', cmap) kwargs.setdefault('vmin', lo) @@ -160,8 +161,20 @@ def imshow(im, ax=None, show_cbar=None, **kwargs): plt.colorbar(ax_im, cax=cax) ax.set_adjustable('box-forced') ax.get_figure().tight_layout() + return ax_im + +def imshow_collection(ic, *args, **kwargs): + """Display all images in the collection. + + """ + fig, axes = plt.subplots(1, len(ic)) + for n, image in enumerate(ic): + kwargs['ax'] = axes[n] + imshow(image, *args, **kwargs) + + imread = plt.imread show = plt.show diff --git a/skimage/io/_plugins/test_plugin.ini b/skimage/io/_plugins/test_plugin.ini index 76cf7d6f..184d19ad 100644 --- a/skimage/io/_plugins/test_plugin.ini +++ b/skimage/io/_plugins/test_plugin.ini @@ -1,3 +1,3 @@ [test] description = Test plugin -provides = imsave, imshow, imread, imread_collection +provides = imsave, imshow, imread, imread_collection, imshow_collection diff --git a/skimage/io/_plugins/test_plugin.py b/skimage/io/_plugins/test_plugin.py index 18f750f3..b325cf4a 100644 --- a/skimage/io/_plugins/test_plugin.py +++ b/skimage/io/_plugins/test_plugin.py @@ -1,6 +1,8 @@ # This mock-up is called by ../tests/test_plugin.py # to verify the behaviour of the plugin infrastructure +from skimage.io import ImageCollection + def imread(fname, dtype=None): assert fname == 'test.png' @@ -20,3 +22,8 @@ def imshow(arr, plugin_arg=None): def imread_collection(x, conserve_memory=True): assert conserve_memory == False assert x == '*.png' + return ImageCollection([0, 1], load_func=lambda x: x) + + +def imshow_collection(x): + assert len(x) == 2 diff --git a/skimage/io/manage_plugins.py b/skimage/io/manage_plugins.py index 326502c5..8231446e 100644 --- a/skimage/io/manage_plugins.py +++ b/skimage/io/manage_plugins.py @@ -46,7 +46,8 @@ plugin_meta_data = {} preferred_plugins = { # Default plugins for all types (overridden by specific types below). 'all': ['pil', 'matplotlib', 'qt', 'freeimage'], - 'imshow': ['matplotlib'] + 'imshow': ['matplotlib'], + 'imshow_collection': ['matplotlib'] } @@ -59,13 +60,15 @@ def _clear_plugins(): 'imsave': [], 'imshow': [], 'imread_collection': [], + 'imshow_collection': [], '_app_show': []} _clear_plugins() def _load_preferred_plugins(): # Load preferred plugin for each io function. - io_types = ['imsave', 'imshow', 'imread_collection', 'imread'] + io_types = ['imsave', 'imshow', 'imread_collection', 'imshow_collection', + 'imread'] for p_type in io_types: _set_plugin(p_type, preferred_plugins['all']) @@ -190,8 +193,8 @@ def call_plugin(kind, *args, **kwargs): if len(plugin_funcs) == 0: msg = ("No suitable plugin registered for %s.\n\n" "You may load I/O plugins with the `skimage.io.use_plugin` " - "command. A list of all available plugins can be found using " - "`skimage.io.plugins()`.") + "command. A list of all available plugins are shown in the " + "`skimage.io` docstring.") raise RuntimeError(msg % kind) plugin = kwargs.pop('plugin', None) @@ -216,7 +219,7 @@ def use_plugin(name, kind=None): ---------- name : str Name of plugin. - kind : {'imsave', 'imread', 'imshow', 'imread_collection'}, optional + kind : {'imsave', 'imread', 'imshow', 'imread_collection', 'imshow_collection'}, optional Set the plugin for this function. By default, the plugin is set for all functions. diff --git a/skimage/io/tests/test_plugin.py b/skimage/io/tests/test_plugin.py index 00dd5b15..37c61467 100644 --- a/skimage/io/tests/test_plugin.py +++ b/skimage/io/tests/test_plugin.py @@ -41,7 +41,8 @@ def test_show(): def test_collection(): - io.imread_collection('*.png', conserve_memory=False, plugin='test') + ic = io.imread_collection('*.png', conserve_memory=False, plugin='test') + io.imshow_collection(ic) def test_use(): diff --git a/skimage/novice/__init__.py b/skimage/novice/__init__.py index 02da082b..ed46f6ad 100644 --- a/skimage/novice/__init__.py +++ b/skimage/novice/__init__.py @@ -56,9 +56,9 @@ Changing `size` resizes the picture. >>> picture.size = (45, 30) -We can preview the changes we made to the picture with our earlier command: +We can preview the changes we made to the picture with the ``compare`` command: ->>> picture.show() # doctest: +SKIP +>>> picture.compare() # doctest: +SKIP You can iterate over pixels, which have RGB values between 0 and 255, and know their location in the picture. @@ -88,6 +88,13 @@ True >>> picture.modified False +An image can also be restored to its original state after modification: + +>>> picture[0:20, 0:20] = (0, 0, 0) +>>> picture.compare() # doctest: +SKIP +>>> picture.reset() +>>> picture.compare() # doctest: +SKIP + """ from ._novice import Picture, open, colors, color_dict diff --git a/skimage/novice/_novice.py b/skimage/novice/_novice.py index 83d064c0..5d383e70 100644 --- a/skimage/novice/_novice.py +++ b/skimage/novice/_novice.py @@ -7,6 +7,7 @@ from .. import io, img_as_ubyte from ..transform import resize from ..color import color_dict from ..io.util import file_or_url_context, is_url +from ..io.collection import ImageCollection import six from six.moves.urllib import request @@ -303,8 +304,9 @@ class Picture(object): @array.setter def array(self, array): - self._array = array - self._xy_array = array_to_xy_origin(array) + self._array = array.astype(np.uint8) + self._xy_array = array_to_xy_origin(self._array) + self._array_backup = self._array.copy() @property def xy_array(self): @@ -329,6 +331,12 @@ class Picture(object): self._path = os.path.abspath(path) self._format = imghdr.what(path) + def reset(self): + """Reset image to its original state, removing modifications. + + """ + self.array = self._array_backup + @property def path(self): """The path to the picture.""" @@ -388,6 +396,13 @@ class Picture(object): io.imshow(self.array) io.show() + def compare(self): + """Compare the image to its unmodified version.""" + images = [self._array_backup, self.array] + ic = ImageCollection([0, 1], load_func=lambda x: images[x]) + io.imshow_collection(images) + io.show() + def _makepixel(self, x, y): """Create a Pixel object for a given x, y location.""" rgb = self.xy_array[x, y] diff --git a/skimage/novice/tests/test_novice.py b/skimage/novice/tests/test_novice.py index 70483c4e..76648fb0 100644 --- a/skimage/novice/tests/test_novice.py +++ b/skimage/novice/tests/test_novice.py @@ -2,6 +2,7 @@ import os import tempfile import numpy as np +from nose.tools import assert_true from numpy.testing import assert_equal, raises, assert_allclose from skimage import novice from skimage.novice._novice import (array_to_xy_origin, xy_to_array_origin, @@ -135,8 +136,18 @@ def test_modified_on_set_pixel(): assert pic.modified +def test_reset(): + pic = novice.Picture(SMALL_IMAGE_PATH) + v = pic[0, 0] + pic[0, 0] = (1, 1, 1) + pic.reset() + assert_true(pic[0, 0] == v) + + def test_update_on_save(): pic = novice.Picture(array=np.zeros((3, 3, 3))) + pic[0, 0] = (255, 255, 255) # prevent attempting to save low-contrast image + with all_warnings(): # precision loss pic.size = (6, 6) assert pic.modified