Skip to content

Commit 879f8bb

Browse files
committed
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.
1 parent 0325d9f commit 879f8bb

2 files changed

Lines changed: 33 additions & 3 deletions

File tree

skimage/exposure/exposure.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,18 @@ def histogram(image, nbins=256):
6262
# For integer types, histogramming with bincount is more efficient.
6363
if np.issubdtype(image.dtype, np.integer):
6464
offset = 0
65-
if np.min(image) < 0:
66-
offset = np.min(image)
67-
hist = np.bincount(image.ravel() - offset)
65+
image_min = np.min(image)
66+
if image_min < 0:
67+
offset = image_min
68+
image_range = np.max(image).astype(np.int64) - image_min
69+
# get smallest dtype that can hold both minimum and offset maximum
70+
offset_dtype = np.promote_types(np.min_scalar_type(image_range),
71+
np.min_scalar_type(image_min))
72+
if image.dtype != offset_dtype:
73+
# prevent overflow errors when offsetting
74+
image = image.astype(offset_dtype)
75+
image = image - offset
76+
hist = np.bincount(image.ravel())
6877
bin_centers = np.arange(len(hist)) + offset
6978

7079
# clip histogram to start with a non-zero bin

skimage/exposure/tests/test_exposure.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@
1212
from skimage.util.dtype import dtype_range
1313

1414

15+
# Test integer histograms
16+
# =======================
17+
18+
def test_negative_overflow():
19+
im = np.array([-1, 127], dtype=np.int8)
20+
frequencies, bin_centers = exposure.histogram(im)
21+
assert_array_equal(bin_centers, np.arange(-1, 128))
22+
assert frequencies[0] == 1
23+
assert frequencies[-1] == 1
24+
assert_array_equal(frequencies[1:-1], 0)
25+
26+
27+
def test_all_negative_image():
28+
im = np.array([-128, -1], dtype=np.int8)
29+
frequencies, bin_centers = exposure.histogram(im)
30+
assert_array_equal(bin_centers, np.arange(-128, 0))
31+
assert frequencies[0] == 1
32+
assert frequencies[-1] == 1
33+
assert_array_equal(frequencies[1:-1], 0)
34+
35+
1536
# Test histogram equalization
1637
# ===========================
1738

0 commit comments

Comments
 (0)