From 8f4d0247b56ae157e8486c37e38992015e55ac3e Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 15 Dec 2014 15:42:54 +1100 Subject: [PATCH 01/15] Add docstring to matplotlib imshow plugin The image is now named as an argument, and the axes are returned, in keeping with matplotlib convention. --- skimage/io/_plugins/matplotlib_plugin.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index ec36ce04..4aa0b7f1 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -1,12 +1,27 @@ import matplotlib.pyplot as plt -def imshow(*args, **kwargs): +def imshow(im, *args, **kwargs): + """Show the input image and return the current axes. + + Parameters + ---------- + im : array, shape (M, N[, 3]) + The image to display. + + *args, **kwargs : positional and keyword arguments + These are passed directly to `matplotlib.pyplot.imshow`. + + Returns + ------- + ax : `matplotlib.pyplot.Axes` + The axes showing the image. + """ if plt.gca().has_data(): plt.figure() kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('cmap', 'gray') - plt.imshow(*args, **kwargs) + return plt.imshow(im, *args, **kwargs) imread = plt.imread show = plt.show From fac8fde9dca1b2c37f5615468944d44c6e826e19 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 15 Dec 2014 21:41:11 +1100 Subject: [PATCH 02/15] Overhaul matplotlib imshow plugin - images are displayed within their native dtype range, - unless they are outside of their range (e.g. a float image with values greater than 1) or they are of an unsupported dtype (e.g. a uint64 image), in which case the dynamic range of the display corresponds to the image range, - which is also done for images with extremely low contrast for their native range (e.g. float images in [1e-7, 5e-7]. In the latter two cases, a colorbar is also displayed and a warning is raised. Finally, we return the axes object on which the image is plotted, to enable further plotting in the matplotlib new OO style. --- skimage/io/_io.py | 2 +- skimage/io/_plugins/matplotlib_plugin.py | 41 +++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/skimage/io/_io.py b/skimage/io/_io.py index 06da7adb..646369bd 100644 --- a/skimage/io/_io.py +++ b/skimage/io/_io.py @@ -193,7 +193,7 @@ def show(): >>> import skimage.io as io >>> for i in range(4): - ... io.imshow(np.random.rand(50, 50)) + ... ax = io.imshow(np.random.rand(50, 50)) >>> io.show() # doctest: +SKIP ''' diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 4aa0b7f1..a65f8cf0 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -1,9 +1,21 @@ +import numpy as np +import warnings import matplotlib.pyplot as plt +from skimage.util import dtype as dtypes def imshow(im, *args, **kwargs): """Show the input image and return the current axes. + Images are assumed to have standard range for their type. For + example, if a floating point image has values in [0, 0.5], the + most intense color will be gray50, not white, as would be the + default in matplotlib. + + In contrast, if the image exceeds the standard range, this + function defaults back to displaying exactly the range of the + input image. + Parameters ---------- im : array, shape (M, N[, 3]) @@ -19,9 +31,36 @@ def imshow(im, *args, **kwargs): """ if plt.gca().has_data(): plt.figure() + immin, immax = np.min(im), np.max(im) + supported_dtype = im.dtype in dtypes._supported_types + if supported_dtype: + lo, hi = dtypes.dtype_range[im.dtype.type] + if immin >= 0: + # display range starts at 0 for nonnegative images + lo = 0 + else: + warnings.warn("Non-standard image type; displaying image with " + "stretched contrast.") + out_of_range_float = (np.issubdtype(im.dtype, np.float) and + (immin < lo or immax > hi)) + if out_of_range_float: + warnings.warn("Float image out of standard range; displaying image " + "with stretched contrast.") + low_dynamic_range = (immin != immax and + (float(immax - immin) / (hi - lo)) < (1. / 255)) + if low_dynamic_range: + warnings.warn("Low image dynamic range; displaying image with " + "stretched contrast.") + if not supported_dtype or out_of_range_float or low_dynamic_range: + lo, hi = immin, immax kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('cmap', 'gray') - return plt.imshow(im, *args, **kwargs) + kwargs.setdefault('vmin', lo) + kwargs.setdefault('vmax', hi) + ax = plt.imshow(im, *args, **kwargs) + if not supported_dtype or out_of_range_float or low_dynamic_range: + ax.colorbar() + return ax imread = plt.imread show = plt.show From c68c8e5468e039ae7304f7bb4173b6c33f9aa2ca Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Tue, 13 Jan 2015 16:36:40 +1100 Subject: [PATCH 03/15] Improve mpl imshow plugin docstring --- skimage/io/_plugins/matplotlib_plugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index a65f8cf0..aab5e628 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -14,7 +14,8 @@ def imshow(im, *args, **kwargs): In contrast, if the image exceeds the standard range, this function defaults back to displaying exactly the range of the - input image. + input image, along with a colorbar to clearly indicate that + this range transformation has occurred. Parameters ---------- From fab4ff23055201e64b47a03ccb075bb40c9caf55 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Wed, 14 Jan 2015 09:55:10 +1100 Subject: [PATCH 04/15] Fix incorrect call to colorbar --- skimage/io/_plugins/matplotlib_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index aab5e628..01a873c0 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -60,7 +60,7 @@ def imshow(im, *args, **kwargs): kwargs.setdefault('vmax', hi) ax = plt.imshow(im, *args, **kwargs) if not supported_dtype or out_of_range_float or low_dynamic_range: - ax.colorbar() + plt.colorbar() return ax imread = plt.imread From 984e22b47ab59162277daf691613de64e0682f13 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Wed, 14 Jan 2015 20:11:19 +1100 Subject: [PATCH 05/15] Replace Axes with AxesImage where relevant --- skimage/io/_io.py | 2 +- skimage/io/_plugins/matplotlib_plugin.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/io/_io.py b/skimage/io/_io.py index 646369bd..71f8f25c 100644 --- a/skimage/io/_io.py +++ b/skimage/io/_io.py @@ -193,7 +193,7 @@ def show(): >>> import skimage.io as io >>> for i in range(4): - ... ax = io.imshow(np.random.rand(50, 50)) + ... ax_im = io.imshow(np.random.rand(50, 50)) >>> io.show() # doctest: +SKIP ''' diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 01a873c0..f6f6a5d3 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -27,8 +27,8 @@ def imshow(im, *args, **kwargs): Returns ------- - ax : `matplotlib.pyplot.Axes` - The axes showing the image. + ax : `matplotlib.pyplot.AxesImage` + The `AxesImage` object returned by `plt.imshow`. """ if plt.gca().has_data(): plt.figure() From 176889933feca3c1231634062b0507afde6ddac3 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Wed, 14 Jan 2015 20:11:55 +1100 Subject: [PATCH 06/15] Add tests for io.imshow mpl plugin --- skimage/io/tests/test_mpl_imshow.py | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 skimage/io/tests/test_mpl_imshow.py diff --git a/skimage/io/tests/test_mpl_imshow.py b/skimage/io/tests/test_mpl_imshow.py new file mode 100644 index 00000000..fbc517f9 --- /dev/null +++ b/skimage/io/tests/test_mpl_imshow.py @@ -0,0 +1,63 @@ +from __future__ import division + +import numpy as np +from skimage import io + +io.use_plugin('matplotlib', 'imshow') + + +# test images. Note that they don't have their full range for their dtype, +# but we still expect the display range to equal the full dtype range. +im8 = np.array([[0, 64], [128, 240]], np.uint8) +im16 = im8.astype(np.uint16) * 256 +imf = im8 / 255 +im_lo = imf / 1000 +im_hi = imf + 10 + + + +def n_subplots(ax_im): + """Return the number of subplots in the figure containing an ``AxesImage``. + + Parameters + ---------- + ax_im : matplotlib.pyplot.AxesImage object + The input ``AxesImage``. + + Returns + ------- + n : int + The number of subplots in the corresponding figure. + + Notes + ----- + This function is intended to check whether a colorbar was drawn, in + which case two subplots are expected. For standard imshows, one + subplot is expected. + """ + return len(ax_im.get_figure().get_axes()) + + +def test_uint8(): + ax_im = io.imshow(im8) + assert ax_im.cmap.name == 'gray' + assert ax_im.get_clim() == (0, 255) + assert n_subplots(ax_im) == 1 + + +def test_uint16(): + ax_im = io.imshow(im16) + assert ax_im.cmap.name == 'gray' + assert ax_im.get_clim() == (0, 65535) + assert n_subplots(ax_im) == 1 + + +def test_float(): + ax_im = io.imshow(imf) + assert ax_im.cmap.name == 'gray' + assert ax_im.get_clim() == (0, 1) + assert n_subplots(ax_im) == 1 + + +if __name__ == '__main__': + np.testing.run_module_suite() From 9fe4f33793019707da3fe03ce5a5b651824d37a3 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Wed, 14 Jan 2015 20:29:15 +1100 Subject: [PATCH 07/15] Add tests for nonstandard images --- skimage/io/_plugins/matplotlib_plugin.py | 1 + skimage/io/tests/test_mpl_imshow.py | 26 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index f6f6a5d3..187cab22 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -40,6 +40,7 @@ def imshow(im, *args, **kwargs): # display range starts at 0 for nonnegative images lo = 0 else: + lo, hi = immin, immax warnings.warn("Non-standard image type; displaying image with " "stretched contrast.") out_of_range_float = (np.issubdtype(im.dtype, np.float) and diff --git a/skimage/io/tests/test_mpl_imshow.py b/skimage/io/tests/test_mpl_imshow.py index fbc517f9..2a154d3a 100644 --- a/skimage/io/tests/test_mpl_imshow.py +++ b/skimage/io/tests/test_mpl_imshow.py @@ -2,6 +2,8 @@ from __future__ import division import numpy as np from skimage import io +from skimage._shared._warnings import expected_warnings + io.use_plugin('matplotlib', 'imshow') @@ -10,6 +12,7 @@ io.use_plugin('matplotlib', 'imshow') # but we still expect the display range to equal the full dtype range. im8 = np.array([[0, 64], [128, 240]], np.uint8) im16 = im8.astype(np.uint16) * 256 +im64 = im8.astype(np.uint64) imf = im8 / 255 im_lo = imf / 1000 im_hi = imf + 10 @@ -42,6 +45,7 @@ def test_uint8(): ax_im = io.imshow(im8) assert ax_im.cmap.name == 'gray' assert ax_im.get_clim() == (0, 255) + # check that no colorbar was created assert n_subplots(ax_im) == 1 @@ -59,5 +63,27 @@ def test_float(): assert n_subplots(ax_im) == 1 +def test_low_dynamic_range(): + with expected_warnings(["Low image dynamic range"]): + ax_im = io.imshow(im_lo) + assert ax_im.get_clim() == (im_lo.min(), im_lo.max()) + # check that a colorbar was created + assert n_subplots(ax_im) == 2 + + +def test_outside_standard_range(): + with expected_warnings(["out of standard range"]): + ax_im = io.imshow(im_hi) + assert ax_im.get_clim() == (im_hi.min(), im_hi.max()) + assert n_subplots(ax_im) == 2 + + +def test_nonstandard_type(): + with expected_warnings(["Non-standard image type"]): + ax_im = io.imshow(im64) + assert ax_im.get_clim() == (im64.min(), im64.max()) + assert n_subplots(ax_im) == 2 + + if __name__ == '__main__': np.testing.run_module_suite() From 400c3fd20228a6617f0b22be8ed8c04b5fc834a9 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 15:44:55 +1100 Subject: [PATCH 08/15] Rename ax to ax_im in mpl imshow plugin --- skimage/io/_plugins/matplotlib_plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 187cab22..ca3e6f90 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -27,7 +27,7 @@ def imshow(im, *args, **kwargs): Returns ------- - ax : `matplotlib.pyplot.AxesImage` + ax_im : `matplotlib.pyplot.AxesImage` The `AxesImage` object returned by `plt.imshow`. """ if plt.gca().has_data(): @@ -59,10 +59,10 @@ def imshow(im, *args, **kwargs): kwargs.setdefault('cmap', 'gray') kwargs.setdefault('vmin', lo) kwargs.setdefault('vmax', hi) - ax = plt.imshow(im, *args, **kwargs) + ax_im = plt.imshow(im, *args, **kwargs) if not supported_dtype or out_of_range_float or low_dynamic_range: plt.colorbar() - return ax + return ax_im imread = plt.imread show = plt.show From 30c522e70625f1d7f24051518c408fd483395651 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 20:16:24 +1100 Subject: [PATCH 09/15] Use different colormaps depending on input image --- skimage/io/_plugins/matplotlib_plugin.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index ca3e6f90..04ea20e0 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -4,6 +4,11 @@ import matplotlib.pyplot as plt from skimage.util import dtype as dtypes +_default_colormap = 'gray' +_nonstandard_colormap = 'cubehelix' +_diverging_colormap = 'RdBu' + + def imshow(im, *args, **kwargs): """Show the input image and return the current axes. @@ -33,14 +38,20 @@ def imshow(im, *args, **kwargs): if plt.gca().has_data(): plt.figure() immin, immax = np.min(im), np.max(im) + lo, hi = immin, immax + cmap = _default_colormap supported_dtype = im.dtype in dtypes._supported_types if supported_dtype: lo, hi = dtypes.dtype_range[im.dtype.type] - if immin >= 0: - # display range starts at 0 for nonnegative images + # for nonnegative float images, ensure lower bound is 0, not -1 + if immin > 0: lo = 0 + # for signed images, show diverging colormap centered at 0 + else: + cmap = _diverging_colormap + magnitude = max(-immin, abs(immax)) + lo, hi = -magnitude, magnitude else: - lo, hi = immin, immax warnings.warn("Non-standard image type; displaying image with " "stretched contrast.") out_of_range_float = (np.issubdtype(im.dtype, np.float) and @@ -54,9 +65,9 @@ def imshow(im, *args, **kwargs): warnings.warn("Low image dynamic range; displaying image with " "stretched contrast.") if not supported_dtype or out_of_range_float or low_dynamic_range: - lo, hi = immin, immax + cmap = _nonstandard_colormap kwargs.setdefault('interpolation', 'nearest') - kwargs.setdefault('cmap', 'gray') + kwargs.setdefault('cmap', cmap) kwargs.setdefault('vmin', lo) kwargs.setdefault('vmax', hi) ax_im = plt.imshow(im, *args, **kwargs) From 2bdd23949609603f47fb195634bfb235ac4cb8c1 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 22:25:41 +1100 Subject: [PATCH 10/15] Overhaul imshow to clarify flow --- skimage/io/_plugins/matplotlib_plugin.py | 133 ++++++++++++++++++----- 1 file changed, 103 insertions(+), 30 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 04ea20e0..3038c59b 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -1,3 +1,4 @@ +from collections import namedtuple import numpy as np import warnings import matplotlib.pyplot as plt @@ -9,6 +10,106 @@ _nonstandard_colormap = 'cubehelix' _diverging_colormap = 'RdBu' +ImageProperties = namedtuple('ImageProperties', + ['signed', 'out_of_range_float', + 'low_dynamic_range', 'unsupported_dtype']) + + +def _get_image_properties(image): + """Determine nonstandard properties of an input image. + + Parameters + ---------- + image : array + The input image. + + Returns + ------- + ip : ImageProperties named tuple + The properties of the image: + + - signed: whether the image has negative values. + - out_of_range_float: if the image has floating point data + outside of [-1, 1]. + - low_dynamic_range: if the image is in the standard image + range (e.g. [0, 1] for a floating point image) but its + dynamic range would be too small to display with standard + image ranges. + - unsupported_dtype: if the image data type is not a + standard skimage type, e.g. ``numpy.uint64``. + """ + immin, immax = np.min(image), np.max(image) + imtype = image.dtype.type + try: + lo, hi = dtypes.dtype_range[imtype] + except KeyError: + lo, hi = immin, immax + + signed = immin < 0 + out_of_range_float = (np.issubdtype(image.dtype, np.float) and + (immin < lo or immax > hi)) + low_dynamic_range = (immin != immax and + (float(immax - immin) / (hi - lo)) < (1. / 255)) + unsupported_dtype = image.dtype not in dtypes._supported_types + + ip = ImageProperties(signed, out_of_range_float, + low_dynamic_range, unsupported_dtype) + return ip + + +def _raise_warnings(image_properties): + """Raise the appropriate warning for each nonstandard image type. + + Parameters + ---------- + image_properties : ImageProperties named tuple + The properties of the considered image. + """ + ip = image_properties + if ip.unsupported_dtype: + warnings.warn("Non-standard image type; displaying image with " + "stretched contrast.") + if ip.low_dynamic_range: + warnings.warn("Low image dynamic range; displaying image with " + "stretched contrast.") + if ip.out_of_range_float: + warnings.warn("Float image out of standard range; displaying image " + "with stretched contrast.") + + +def _get_display_range(image): + """Return the display range for a given set of image properties. + + Parameters + ---------- + image : array + The input image. + + Returns + ------- + lo, hi : same type as immin, immax + The display range to be used for the input image. + cmap : string + The name of the colormap to use. + """ + ip = _get_image_properties(image) + immin, immax = np.min(image), np.max(image) + if ip.signed: + magnitude = max(abs(immin), abs(immax)) + lo, hi = -magnitude, magnitude + cmap = _diverging_colormap + elif any(ip): + _raise_warnings(ip) + lo, hi = immin, immax + cmap = _nonstandard_colormap + else: + lo = 0 + imtype = image.dtype.type + hi = dtypes.dtype_range[imtype][1] + cmap = _default_colormap + return lo, hi, cmap + + def imshow(im, *args, **kwargs): """Show the input image and return the current axes. @@ -37,41 +138,13 @@ def imshow(im, *args, **kwargs): """ if plt.gca().has_data(): plt.figure() - immin, immax = np.min(im), np.max(im) - lo, hi = immin, immax - cmap = _default_colormap - supported_dtype = im.dtype in dtypes._supported_types - if supported_dtype: - lo, hi = dtypes.dtype_range[im.dtype.type] - # for nonnegative float images, ensure lower bound is 0, not -1 - if immin > 0: - lo = 0 - # for signed images, show diverging colormap centered at 0 - else: - cmap = _diverging_colormap - magnitude = max(-immin, abs(immax)) - lo, hi = -magnitude, magnitude - else: - warnings.warn("Non-standard image type; displaying image with " - "stretched contrast.") - out_of_range_float = (np.issubdtype(im.dtype, np.float) and - (immin < lo or immax > hi)) - if out_of_range_float: - warnings.warn("Float image out of standard range; displaying image " - "with stretched contrast.") - low_dynamic_range = (immin != immax and - (float(immax - immin) / (hi - lo)) < (1. / 255)) - if low_dynamic_range: - warnings.warn("Low image dynamic range; displaying image with " - "stretched contrast.") - if not supported_dtype or out_of_range_float or low_dynamic_range: - cmap = _nonstandard_colormap + lo, hi, cmap = _get_display_range(im) kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('cmap', cmap) kwargs.setdefault('vmin', lo) kwargs.setdefault('vmax', hi) ax_im = plt.imshow(im, *args, **kwargs) - if not supported_dtype or out_of_range_float or low_dynamic_range: + if cmap != _default_colormap: plt.colorbar() return ax_im From e47200909086cc540d42762ccbb4b2e2da76978f Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 22:26:17 +1100 Subject: [PATCH 11/15] Add test for signed images --- skimage/io/tests/test_mpl_imshow.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/skimage/io/tests/test_mpl_imshow.py b/skimage/io/tests/test_mpl_imshow.py index 2a154d3a..ce618fe9 100644 --- a/skimage/io/tests/test_mpl_imshow.py +++ b/skimage/io/tests/test_mpl_imshow.py @@ -47,6 +47,7 @@ def test_uint8(): assert ax_im.get_clim() == (0, 255) # check that no colorbar was created assert n_subplots(ax_im) == 1 + assert ax_im.colorbar is None def test_uint16(): @@ -54,6 +55,7 @@ def test_uint16(): assert ax_im.cmap.name == 'gray' assert ax_im.get_clim() == (0, 65535) assert n_subplots(ax_im) == 1 + assert ax_im.colorbar is None def test_float(): @@ -61,6 +63,7 @@ def test_float(): assert ax_im.cmap.name == 'gray' assert ax_im.get_clim() == (0, 1) assert n_subplots(ax_im) == 1 + assert ax_im.colorbar is None def test_low_dynamic_range(): @@ -69,6 +72,7 @@ def test_low_dynamic_range(): assert ax_im.get_clim() == (im_lo.min(), im_lo.max()) # check that a colorbar was created assert n_subplots(ax_im) == 2 + assert ax_im.colorbar is not None def test_outside_standard_range(): @@ -76,6 +80,7 @@ def test_outside_standard_range(): ax_im = io.imshow(im_hi) assert ax_im.get_clim() == (im_hi.min(), im_hi.max()) assert n_subplots(ax_im) == 2 + assert ax_im.colorbar is not None def test_nonstandard_type(): @@ -83,6 +88,15 @@ def test_nonstandard_type(): ax_im = io.imshow(im64) assert ax_im.get_clim() == (im64.min(), im64.max()) assert n_subplots(ax_im) == 2 + assert ax_im.colorbar is not None + + +def test_signed_image(): + im_signed = np.array([[-0.5, -0.2], [0.1, 0.4]]) + ax_im = io.imshow(im_signed) + assert ax_im.get_clim() == (-0.5, 0.5) + assert n_subplots(ax_im) == 2 + assert ax_im.colorbar is not None if __name__ == '__main__': From c6dff6942d88f54182797f10862d5e2568f11399 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 22:26:55 +1100 Subject: [PATCH 12/15] Don't create new plot automatically This behavior is a bit too magical and may interfere with creation of interactive ImageCollection browsers, for example. --- skimage/io/_plugins/matplotlib_plugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 3038c59b..fb43fbf8 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -136,8 +136,6 @@ def imshow(im, *args, **kwargs): ax_im : `matplotlib.pyplot.AxesImage` The `AxesImage` object returned by `plt.imshow`. """ - if plt.gca().has_data(): - plt.figure() lo, hi, cmap = _get_display_range(im) kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('cmap', cmap) From 7b2dbcb15b4ea9e90db4cb123fb543b9413cbdec Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 22:39:50 +1100 Subject: [PATCH 13/15] Expand the docstring for imshow --- skimage/io/_plugins/matplotlib_plugin.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index fb43fbf8..0ed0bdcc 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -113,16 +113,20 @@ def _get_display_range(image): def imshow(im, *args, **kwargs): """Show the input image and return the current axes. + By default, the image is displayed in greyscale, rather than + the matplotlib default colormap, 'jet'. + Images are assumed to have standard range for their type. For example, if a floating point image has values in [0, 0.5], the - most intense color will be gray50, not white, as would be the - default in matplotlib. + most intense color will be gray50, not white. - In contrast, if the image exceeds the standard range, this - function defaults back to displaying exactly the range of the - input image, along with a colorbar to clearly indicate that + If the image exceeds the standard range, or if the range is too + small to display, we fall back on displaying exactly the range of + the input image, along with a colorbar to clearly indicate that this range transformation has occurred. + For signed images, we use a diverging colormap centered at 0. + Parameters ---------- im : array, shape (M, N[, 3]) From 5d115209b5f6ddd9fbf7b7c6e843cff11f9128da Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 23:07:09 +1100 Subject: [PATCH 14/15] Create new figure for each imshow test --- skimage/io/tests/test_mpl_imshow.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/skimage/io/tests/test_mpl_imshow.py b/skimage/io/tests/test_mpl_imshow.py index ce618fe9..b1ff625a 100644 --- a/skimage/io/tests/test_mpl_imshow.py +++ b/skimage/io/tests/test_mpl_imshow.py @@ -3,6 +3,7 @@ from __future__ import division import numpy as np from skimage import io from skimage._shared._warnings import expected_warnings +import matplotlib.pyplot as plt io.use_plugin('matplotlib', 'imshow') @@ -76,6 +77,7 @@ def test_low_dynamic_range(): def test_outside_standard_range(): + plt.figure() with expected_warnings(["out of standard range"]): ax_im = io.imshow(im_hi) assert ax_im.get_clim() == (im_hi.min(), im_hi.max()) @@ -84,6 +86,7 @@ def test_outside_standard_range(): def test_nonstandard_type(): + plt.figure() with expected_warnings(["Non-standard image type"]): ax_im = io.imshow(im64) assert ax_im.get_clim() == (im64.min(), im64.max()) @@ -92,6 +95,7 @@ def test_nonstandard_type(): def test_signed_image(): + plt.figure() im_signed = np.array([[-0.5, -0.2], [0.1, 0.4]]) ax_im = io.imshow(im_signed) assert ax_im.get_clim() == (-0.5, 0.5) From 61d9812cd814e155d20cc25ac5eb132b5cb7028f Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 23:31:51 +1100 Subject: [PATCH 15/15] Don't create superfluous ip variable --- skimage/io/_plugins/matplotlib_plugin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 0ed0bdcc..d5ec47d3 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -52,9 +52,8 @@ def _get_image_properties(image): (float(immax - immin) / (hi - lo)) < (1. / 255)) unsupported_dtype = image.dtype not in dtypes._supported_types - ip = ImageProperties(signed, out_of_range_float, - low_dynamic_range, unsupported_dtype) - return ip + return ImageProperties(signed, out_of_range_float, + low_dynamic_range, unsupported_dtype) def _raise_warnings(image_properties):