diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 638cf43f..cfbeb0c0 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,4 +1,4 @@ try: - from viewers import ImageViewer + from viewers import ImageViewer, CollectionViewer except ImportError: print("Could not import PyQt4 -- ImageViewer not available.") diff --git a/skimage/viewer/viewers/__init__.py b/skimage/viewer/viewers/__init__.py index bb67a43f..a8339edc 100644 --- a/skimage/viewer/viewers/__init__.py +++ b/skimage/viewer/viewers/__init__.py @@ -1 +1 @@ -from .core import * +from .core import ImageViewer, CollectionViewer diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 156f08bd..d29f950e 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -12,6 +12,10 @@ except ImportError: from skimage.util.dtype import dtype_range from ..utils import figimage, MatplotlibCanvas +from ..widgets import Slider + + +__all__ = ['ImageViewer', 'CollectionViewer'] qApp = None @@ -194,3 +198,86 @@ class ImageViewer(QMainWindow): return "%4s @ [%4s, %4s]" % (self.image[y, x], x, y) except IndexError: return "" + + +class CollectionViewer(ImageViewer): + """Viewer for displaying image collections. + + Select the displayed frame of the image collection using the slider or + with the following keyboard shortcuts: + + left/right arrows + Previous/next image in collection. + number keys, 0--9 + 0% to 90% of collection. For example, "5" goes to the image in the + middle (i.e. 50%) of the collection. + home/end keys + First/last image in collection. + + Subclasses and plugins will likely extend the `update_image` method to add + custom overlays or filter the displayed image. + + Parameters + ---------- + image_collection : list of images + List of images to be displayed. + update_on : {'on_slide' | 'on_release'} + Control whether image is updated on slide or release of the image + slider. Using 'on_release' will give smoother behavior when displaying + large images or when writing a plugin/subclass that requires heavy + computation. + """ + + def __init__(self, image_collection, update_on='move', **kwargs): + self.image_collection = image_collection + self.index = 0 + self.num_images = len(self.image_collection) + + first_image = image_collection[0] + super(CollectionViewer, self).__init__(first_image) + + slider_kws = dict(value=0, low=0, high=self.num_images-1) + slider_kws['update_on'] = update_on + slider_kws['callback'] = self.update_index + slider_kws['value_type'] = 'int' + self.slider = Slider('frame', **slider_kws) + self.layout.addWidget(self.slider) + + #TODO: Adjust height to accomodate slider; the following doesn't work + # s_size = self.slider.sizeHint() + # cs_size = self.canvas.sizeHint() + # self.resize(cs_size.width(), cs_size.height() + s_size.height()) + + def update_index(self, name, index): + """Select image on display using index into image collection.""" + index = int(round(index)) + + if index == self.index: + return + + # clip index value to collection limits + index = max(index, 0) + index = min(index, self.num_images-1) + + self.index = index + self.slider.val = index + self.update_image(self.image_collection[index]) + + def update_image(self, image): + """Update displayed image. + + This method can be overridden or extended in subclasses and plugins to + react to image changes. + """ + self.image = image + + def keyPressEvent(self, event): + if type(event) == QtGui.QKeyEvent: + key = event.key() + # Number keys (code: 0 = key 48, 9 = key 57) move to deciles + if 48 <= key < 58: + index = 0.1 * int(key - 48) * self.num_images + self.update_index('', index) + event.accept() + else: + event.ignore() diff --git a/viewer_examples/viewers/collection_viewer.py b/viewer_examples/viewers/collection_viewer.py new file mode 100644 index 00000000..b36ddf82 --- /dev/null +++ b/viewer_examples/viewers/collection_viewer.py @@ -0,0 +1,29 @@ +""" +===================== +CollectionViewer demo +===================== + +Demo of CollectionViewer for viewing collections of images. This demo uses +successively darker versions of the same image to fake an image collection. + +You can scroll through images with the slider, or you can interact with the +viewer using your keyboard: + +left/right arrows + Previous/next image in collection. +number keys, 0--9 + 0% to 90% of collection. For example, "5" goes to the image in the + middle (i.e. 50%) of the collection. +home/end keys + First/last image in collection. + +""" +import numpy as np +from skimage import data +from skimage.viewer import CollectionViewer + +img = data.lena() +img_collection = [np.uint8(img * 0.9**i) for i in range(20)] + +view = CollectionViewer(img_collection) +view.show()