exposure.rescale_intensity does not rescale the image to the range defined in out_range when min and max are equal.
This leads to equalize_adapthist to fail for many cases of constant inputs.
(In principle one wouldn't apply equalize_adapthist in the case of constant inputs, but probably it shouldn't fail, e.g. when processing a constant chunk of a large dask array).
from skimage import exposure
import numpy as np
ar = np.array([300,300])
ar_rescaled = exposure.rescale_intensity(ar, out_range=(0,255))
print(ar_rescaled)
[76500 76500]
This should be within (0,255).
Here's how to make exposure.equalize_adapthist fail:
img = np.zeros((8, 8)) + 0.1
img = img.astype(np.float64)
adapted = exposure.equalize_adapthist(img, 3) # yields exception
IndexError: index 26214 is out of bounds for axis 0 with size 16384
# Paste the output of the following python commands
from __future__ import print_function
import sys; print(sys.version)
import platform; print(platform.platform())
import skimage; print("scikit-image version: {}".format(skimage.__version__))
import numpy; print("numpy version: {}".format(numpy.__version__))
3.8.2 | packaged by conda-forge | (default, Mar 23 2020, 17:55:48)
[Clang 9.0.1 ]
macOS-10.14.6-x86_64-i386-64bit
scikit-image version: 0.17.dev0
numpy version: 1.18.2
It seems the problem is that when imin == imax, rescale_intensity doesn't do more than clipping the image to the in_range:
In equalize_adapthist, images are therefore not properly rescaled to (0, NR_OF_GRAY - 1) here:
https://github.com/scikit-image/scikit-image/blob/37ca7deb15c1dd4d89b7c7e777a32909122009b5/skimage/exposure/_adapthist.py#L77-L78
However, correct scaling is assumed here:
https://github.com/scikit-image/scikit-image/blob/37ca7deb15c1dd4d89b7c7e777a32909122009b5/skimage/exposure/_adapthist.py#L126
which then leads to an index error here:
https://github.com/scikit-image/scikit-image/blob/37ca7deb15c1dd4d89b7c7e777a32909122009b5/skimage/exposure/_adapthist.py#L145
How to fix this? Probably rescaling is not well defined for constant input. The most sensible might be to clip the input image to out_range?
Meaning something like this:
image = np.clip(image, imin, imax)
if imin != imax:
image = (image - imin) / float(imax - imin)
return np.asarray(image * (omax - omin) + omin, dtype=dtype)
else:
return np.clip(image, omin, omax).astype(dtype)
This would be in agreement with the following already existing test:
https://github.com/scikit-image/scikit-image/blob/37ca7deb15c1dd4d89b7c7e777a32909122009b5/skimage/exposure/tests/test_exposure.py#L277-L281
@m-albert Yes, I think clipping is the correct approach, good thinking.
Most helpful comment
How to fix this? Probably rescaling is not well defined for constant input. The most sensible might be to clip the input image to
out_range?Meaning something like this:
This would be in agreement with the following already existing test:
https://github.com/scikit-image/scikit-image/blob/37ca7deb15c1dd4d89b7c7e777a32909122009b5/skimage/exposure/tests/test_exposure.py#L277-L281