Merge pull request #1412 from blink1073/contrast-check-function

Add a function to check whether an image is low contrast for its data type.
This commit is contained in:
Juan Nunez-Iglesias
2015-03-11 00:27:16 +11:00
9 changed files with 87 additions and 10 deletions
+4 -2
View File
@@ -1,6 +1,7 @@
from .exposure import histogram, equalize_hist, \
rescale_intensity, cumulative_distribution, \
adjust_gamma, adjust_sigmoid, adjust_log
adjust_gamma, adjust_sigmoid, adjust_log, \
is_low_contrast
from ._adapthist import equalize_adapthist
@@ -12,4 +13,5 @@ __all__ = ['histogram',
'cumulative_distribution',
'adjust_gamma',
'adjust_sigmoid',
'adjust_log']
'adjust_log',
'is_low_contrast']
+47 -1
View File
@@ -1,7 +1,8 @@
from __future__ import division
import warnings
import numpy as np
from .. import img_as_float
from ..color import rgb2gray
from ..util.dtype import dtype_range, dtype_limits
@@ -463,3 +464,48 @@ def adjust_sigmoid(image, cutoff=0.5, gain=10, inv=False):
out = (1 / (1 + np.exp(gain * (cutoff - image / scale)))) * scale
return dtype(out)
def is_low_contrast(image, fraction_threshold=0.05, lower_percentile=1,
upper_percentile=99, method='linear'):
"""Detemine if an image is low contrast.
Parameters
----------
image : array-like
The image under test.
fraction_threshold : float, optional
The low contrast fraction threshold.
lower_bound : float, optional
Disregard values below this percentile when computing image contrast.
upper_bound : float, optional
Disregard values above this percentile when computing image contrast.
method : str, optional
The contrast determination method. Right now the only available
option is "linear".
Returns
-------
out : bool
True when the image is determined to be low contrast.
Examples
--------
>>> image = np.linspace(0, 0.04, 100)
>>> is_low_contrast(image)
True
>>> image[-1] = 1
>>> is_low_contrast(image)
True
>>> is_low_contrast(image, upper_percentile=100)
False
"""
image = np.asanyarray(image)
if image.ndim == 3 and image.shape[2] in [3, 4]:
image = rgb2gray(image)
dlimits = dtype_limits(image)
limits = np.percentile(image, [lower_percentile, upper_percentile])
ratio = (limits[1] - limits[0]) / (dlimits[1] - dlimits[0])
return ratio < fraction_threshold
+16
View File
@@ -472,6 +472,22 @@ def test_negative():
assert_raises(ValueError, exposure.adjust_gamma, image)
def test_is_low_contrast():
image = np.linspace(0, 0.04, 100)
assert exposure.is_low_contrast(image)
image[-1] = 1
assert exposure.is_low_contrast(image)
assert not exposure.is_low_contrast(image, upper_percentile=100)
image = (image * 255).astype(np.uint8)
assert exposure.is_low_contrast(image)
assert not exposure.is_low_contrast(image, upper_percentile=100)
image = (image.astype(np.uint16)) * 2**8
assert exposure.is_low_contrast(image)
assert not exposure.is_low_contrast(image, upper_percentile=100)
if __name__ == '__main__':
from numpy import testing
testing.run_module_suite()
+5
View File
@@ -1,4 +1,5 @@
from io import BytesIO
import warnings
import numpy as np
import six
@@ -6,6 +7,8 @@ import six
from ..io.manage_plugins import call_plugin
from ..color import rgb2grey
from .util import file_or_url_context
from ..exposure import is_low_contrast
from .._shared._warnings import all_warnings
__all__ = ['Image', 'imread', 'imread_collection', 'imsave', 'imshow', 'show']
@@ -152,6 +155,8 @@ def imsave(fname, arr, plugin=None, **plugin_args):
Passed to the given plugin.
"""
if is_low_contrast(arr):
warnings.warn('%s is a low contrast image' % fname)
return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args)
+6 -4
View File
@@ -2,7 +2,9 @@ from collections import namedtuple
import numpy as np
import warnings
import matplotlib.pyplot as plt
from skimage.util import dtype as dtypes
from ...util import dtype as dtypes
from ...exposure import is_low_contrast
from ..._shared._warnings import all_warnings
_default_colormap = 'gray'
@@ -49,7 +51,7 @@ def _get_image_properties(image):
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))
is_low_contrast(image))
unsupported_dtype = image.dtype not in dtypes._supported_types
return ImageProperties(signed, out_of_range_float,
@@ -72,8 +74,8 @@ def _raise_warnings(image_properties):
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.")
warnings.warn("Float image out of standard range; displaying "
"image with stretched contrast.")
def _get_display_range(image):
+2 -1
View File
@@ -87,7 +87,8 @@ def test_outside_standard_range():
def test_nonstandard_type():
plt.figure()
with expected_warnings(["Non-standard image type"]):
with expected_warnings(["Non-standard image type",
"Low image dynamic range"]):
ax_im = io.imshow(im64)
assert ax_im.get_clim() == (im64.min(), im64.max())
assert n_subplots(ax_im) == 2
+2 -1
View File
@@ -147,7 +147,8 @@ def test_imsave_filelike():
s = BytesIO()
# save to file-like object
with expected_warnings(['precision loss|unclosed file']):
with expected_warnings(['precision loss|unclosed file',
'is a low contrast image']):
imsave(s, image)
# read from file-like object
+1 -1
View File
@@ -26,7 +26,7 @@ INTENSITY_SAMPLE[1, 9:11] = 2
def test_all_props():
region = regionprops(SAMPLE, INTENSITY_SAMPLE)[0]
for prop in PROPS:
assert_equal(region[prop], getattr(region, PROPS[prop]))
assert_almost_equal(region[prop], getattr(region, PROPS[prop]))
def test_dtype():
+4
View File
@@ -11,6 +11,10 @@ dtype_range = {np.bool_: (False, True),
np.uint16: (0, 65535),
np.int8: (-128, 127),
np.int16: (-32768, 32767),
np.int64: (-2**63, 2**63 - 1),
np.uint64: (0, 2**64 - 1),
np.int32: (-2**31, 2**31 - 1),
np.uint32: (0, 2**32 - 1),
np.float32: (-1, 1),
np.float64: (-1, 1)}