From 2bdd23949609603f47fb195634bfb235ac4cb8c1 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Thu, 15 Jan 2015 22:25:41 +1100 Subject: [PATCH] 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