exposure.equalize_hist doesn't output the same type as the input #2677
I am not completely sure if it should be a feature request or a bug.
#1192 mentions this, but the issue is not really addressed.
Histogram equalization would simply be a transformation curve, mapping one intensity to another corresponding intensity (the formulae given in the mentioned link) and it should be all in the integer domain when the input is of type uint8.
Also as far as I know, MATLAB also does the same thing, that is given an input of uint8 returns output in uint8 itself. Link for the same https://www.mathworks.com/help/images/ref/histeq.html
In short, the current implementation of the equalize_hist is doing something similar but will give a different result from what a normal histogram equalization would return at least for an uint8 image. Also I think it is more sensible to return image of the same dtype.
Now for the skimage code.
The text was updated successfully, but these errors were encountered:
I think the difference is much smaller than you think. One of the perils of working with uint8 instead of float!
I don't know that it's generally true that "it should be all in the integer domain when the input is of type uint8". The choice of
Could you post links to your rice image and the Matlab-equalised image? I can investigate a bit more if I have some sample test data.
Thanks for the links.
Although it may seem counter-intuitive, it is very often necessary. As an analogy, if your physical measurements are never more precise than 3 significant digits, does that mean you can work in 10-bit floats? No, because mathematical rounding operations incur precision loss, so you want to maximise the precision of your mathematics, and only round at the end.
In the case in point, firstly, doing a QQ plot of the image intensity shows that the Matlab image is imprecise:
import numpy as np from scipy import stats import matplotlib.pyplot as plt from skimage import io from skimage import exposure rice = io.imread('Downloads/rice.png') rice_matlab_histeq = io.imread('Downloads/rice-matlab-histeq.png') rice_skimage_histeq = exposure.equalize_hist(rice) fig, ax = plt.subplots(1, 2) stats.probplot(rice_skimage_histeq.ravel(), dist='uniform', plot=ax) ax.set_title('scikit-image histeq distribution') stats.probplot(rice_matlab_histeq.ravel(), dist='uniform', plot=ax); ax.set_title('Matlab histeq distribution') fig.tight_layout() plt.show()
Then, looking at the number of levels in each image, you can see that Matlab incurs information loss, while skimage keeps all of the information of the original image:
print(len(np.unique(rice))) print(len(np.unique(rice_matlab_histeq))) print(len(np.unique(rice_skimage_histeq)))
In fact, even discretising the skimage version into 0-255 bins, I can't get down to 61 values, so I can only assume that the Matlab version has gone through more than one iteration of rounding errors.
So, in short, if you want 8-bit images, I suggest you use
In the long term, I can see us considering a
# Now: result = img_as_ubyte(exposure.equalize_hist(image)) # Maybe: result = exposure.equalize_hist(image, preserve_dtype=True)
Honestly, though, I don't think the second version is much clearer than the first, and, I hope I've convinced you that it's generally a bad idea, except for final output. I'm going to close this for now, but please feel free to reopen if you want to propose a third option.