From 7774a76eea757b7f84ddf4b03ba74a1ed61a6884 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 31 Oct 2013 14:46:04 +1100 Subject: [PATCH 1/4] Add plugin return values for ImageViewer The model supported is that plugins can return an overlay, some data, or both. Each plugin therefore returns an `(overlay, data)` tuple in which each element can be `None`. To allow return values, the plugin need only override the `output` method defined in the base Plugin class. See discussions here: https://groups.google.com/d/msg/scikit-image/0nkJM-WguXA/iqogBABa748J and here: https://github.com/scikit-image/scikit-image/pull/805 --- skimage/viewer/plugins/base.py | 17 +++++++++++++++++ skimage/viewer/viewers/core.py | 1 + 2 files changed, 18 insertions(+) diff --git a/skimage/viewer/plugins/base.py b/skimage/viewer/plugins/base.py index 50b7601c..1da020d0 100644 --- a/skimage/viewer/plugins/base.py +++ b/skimage/viewer/plugins/base.py @@ -235,3 +235,20 @@ class Plugin(QtGui.QDialog): """Remove artists that are connected to the image viewer.""" for a in self.artists: a.remove() + + def output(self): + """Return the plugin's representation and data. + + Parameters + ---------- + None + + Returns + ------- + overlay : array, same shape as ``self.image_viewer.image``, or None + The filtered image. + data : array, arbitrary shape and type, or None + Any data associated with the plugin. + """ + return (self.image_viewer.image, None) + diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 8b5eb7ba..27cf7c4e 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -228,6 +228,7 @@ class ImageViewer(QtGui.QMainWindow): self._show() if main_window: utils.start_qtapp() + return [p.output() for p in self.plugins] def redraw(self): self.canvas.draw_idle() From fae2456b490af7a52c097599b244b8ac2fcab38f Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 31 Oct 2013 14:52:01 +1100 Subject: [PATCH 2/4] Allow OverlayPlugin to return the current overlay --- skimage/viewer/plugins/overlayplugin.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/skimage/viewer/plugins/overlayplugin.py b/skimage/viewer/plugins/overlayplugin.py index dc5ca060..049f81a5 100644 --- a/skimage/viewer/plugins/overlayplugin.py +++ b/skimage/viewer/plugins/overlayplugin.py @@ -107,3 +107,19 @@ class OverlayPlugin(Plugin): # clear overlay from ImageViewer on close self.overlay = None super(OverlayPlugin, self).closeEvent(event) + + def output(self): + """Return the overlaid image. + + Parameters + ---------- + None + + Returns + ------- + overlay : array, same shape as image + The overlay currently displayed. + data : None + """ + return (self.overlay, None) + From 4e05ebf7112ed56caf68cdccc0206048cb8e1e7c Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Fri, 1 Nov 2013 15:10:30 +1100 Subject: [PATCH 3/4] Incorporate @tonysyu's suggestions re:docstrings --- skimage/viewer/plugins/base.py | 17 ++++++++++------- skimage/viewer/plugins/overlayplugin.py | 4 ---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/skimage/viewer/plugins/base.py b/skimage/viewer/plugins/base.py index 1da020d0..1a2eedd9 100644 --- a/skimage/viewer/plugins/base.py +++ b/skimage/viewer/plugins/base.py @@ -238,17 +238,20 @@ class Plugin(QtGui.QDialog): def output(self): """Return the plugin's representation and data. - - Parameters - ---------- - None - + Returns ------- - overlay : array, same shape as ``self.image_viewer.image``, or None + image : array, same shape as ``self.image_viewer.image``, or None The filtered image. - data : array, arbitrary shape and type, or None + data : None Any data associated with the plugin. + + Notes + ----- + Derived classes should override this method to return a tuple + containing an *overlay* of the same shape of the image, and a + *data* object. Either of these is optional: return ``None`` if + you don't want to return a value. """ return (self.image_viewer.image, None) diff --git a/skimage/viewer/plugins/overlayplugin.py b/skimage/viewer/plugins/overlayplugin.py index 049f81a5..e154281c 100644 --- a/skimage/viewer/plugins/overlayplugin.py +++ b/skimage/viewer/plugins/overlayplugin.py @@ -111,10 +111,6 @@ class OverlayPlugin(Plugin): def output(self): """Return the overlaid image. - Parameters - ---------- - None - Returns ------- overlay : array, same shape as image From 6ff1068bafc210ff6707363d2016405ff25d7050 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Sat, 16 Nov 2013 16:53:30 +1100 Subject: [PATCH 4/4] Add output for color histogram plugin --- skimage/viewer/plugins/color_histogram.py | 35 ++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/skimage/viewer/plugins/color_histogram.py b/skimage/viewer/plugins/color_histogram.py index 39a75004..e27cf15b 100644 --- a/skimage/viewer/plugins/color_histogram.py +++ b/skimage/viewer/plugins/color_histogram.py @@ -26,10 +26,12 @@ class ColorHistogram(PlotPlugin): L, a, b = self.lab_image.T left, right = -100, 100 ab_extents = [left, right, right, left] + self.mask = np.ones(L.shape, bool) bins = np.arange(left, right) - hist, x_edges, y_edges = np.histogram2d(a.flatten(), b.flatten(), bins, - normed=True) - + hist, x_edges, y_edges = np.histogram2d(a.flatten(), b.flatten(), + bins, normed=True) + self.data = {'bins': bins, 'hist': hist, 'edges': (x_edges, y_edges), + 'extents': (left, right, left, right)} # Clip bin heights that dominate a-b histogram max_val = pct_total_area(hist, percentile=self.max_pct) hist = exposure.rescale_intensity(hist, in_range=(0, max_val)) @@ -46,15 +48,36 @@ class ColorHistogram(PlotPlugin): def ab_selected(self, extents): x0, x1, y0, y1 = extents + self.data['extents'] = extents lab_masked = self.lab_image.copy() L, a, b = lab_masked.T - mask = ((a > y0) & (a < y1)) & ((b > x0) & (b < x1)) - lab_masked[..., 1:][~mask.T] = 0 + self.mask = ((a > y0) & (a < y1)) & ((b > x0) & (b < x1)) + lab_masked[..., 1:][~self.mask.T] = 0 self.image_viewer.image = color.lab2rgb(lab_masked) + def output(self): + """Return the image mask and the histogram data. + + Returns + ------- + mask : array of bool, same shape as image + The selected pixels. + data : dict + The data describing the histogram and the selected region. + Keys: + - 'bins' : array of float, the bin boundaries for both + `a` and `b` channels. + - 'hist' : 2D array of float, the normalized histogram. + - 'edges' : tuple of array of float, the bin edges + along each dimension + - 'extents' : tuple of float, the left and right and + top and bottom of the selected region. + """ + return (self.mask, self.data) + def pct_total_area(image, percentile=0.80): """Return threshold value based on percentage of total area. @@ -65,5 +88,3 @@ def pct_total_area(image, percentile=0.80): sorted_pixels = np.sort(image.flat) return sorted_pixels[idx] - -