From 879f8bb8a88e0539562ea65823b52e64b125b74d Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 24 Nov 2014 23:57:39 +1100 Subject: [PATCH] Protect exposure.histogram from integer overflow Fixes #1228 . I also removed a min() calculation and prevented an image copy when offset is 0, which is most of the time. --- skimage/exposure/exposure.py | 15 ++++++++++++--- skimage/exposure/tests/test_exposure.py | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/skimage/exposure/exposure.py b/skimage/exposure/exposure.py index 40ed3d10..7066cc46 100644 --- a/skimage/exposure/exposure.py +++ b/skimage/exposure/exposure.py @@ -62,9 +62,18 @@ def histogram(image, nbins=256): # For integer types, histogramming with bincount is more efficient. if np.issubdtype(image.dtype, np.integer): offset = 0 - if np.min(image) < 0: - offset = np.min(image) - hist = np.bincount(image.ravel() - offset) + image_min = np.min(image) + if image_min < 0: + offset = image_min + image_range = np.max(image).astype(np.int64) - image_min + # get smallest dtype that can hold both minimum and offset maximum + offset_dtype = np.promote_types(np.min_scalar_type(image_range), + np.min_scalar_type(image_min)) + if image.dtype != offset_dtype: + # prevent overflow errors when offsetting + image = image.astype(offset_dtype) + image = image - offset + hist = np.bincount(image.ravel()) bin_centers = np.arange(len(hist)) + offset # clip histogram to start with a non-zero bin diff --git a/skimage/exposure/tests/test_exposure.py b/skimage/exposure/tests/test_exposure.py index bb114e4f..49560be5 100644 --- a/skimage/exposure/tests/test_exposure.py +++ b/skimage/exposure/tests/test_exposure.py @@ -12,6 +12,27 @@ from skimage.color import rgb2gray from skimage.util.dtype import dtype_range +# Test integer histograms +# ======================= + +def test_negative_overflow(): + im = np.array([-1, 127], dtype=np.int8) + frequencies, bin_centers = exposure.histogram(im) + assert_array_equal(bin_centers, np.arange(-1, 128)) + assert frequencies[0] == 1 + assert frequencies[-1] == 1 + assert_array_equal(frequencies[1:-1], 0) + + +def test_all_negative_image(): + im = np.array([-128, -1], dtype=np.int8) + frequencies, bin_centers = exposure.histogram(im) + assert_array_equal(bin_centers, np.arange(-128, 0)) + assert frequencies[0] == 1 + assert frequencies[-1] == 1 + assert_array_equal(frequencies[1:-1], 0) + + # Test histogram equalization # ===========================