Repository: scikit-image/scikit-image

Issue:
threshold_li changed from version 0.14.0 to version 0.14.1
## Description
The code for threshold_li in filters/thresholding.py changed from version 0.14.0 to 0.14.1. The new method does not work at all for my images.

Example data (test.npy must be unzipped): [test.zip](https://github.com/scikit-image/scikit-image/files/2691279/test.zip)

```python
import matplotlib.pylab as plt
import numpy as np
from skimage.filters import threshold_li

image = np.load("test.npy")
thresh = threshold_li(image)

ax1 = plt.subplot(121)
ax1.imshow(image)

ax2 = plt.subplot(122)
ax2 = plt.imshow(image > thresh)

plt.savefig("result.png")
```

Old code (0.14.0):
https://github.com/scikit-image/scikit-image/blob/v0.14.0/skimage/filters/thresholding.py#L519-L533
![result_14 0](https://user-images.githubusercontent.com/2853022/50166366-374ced00-02e7-11e9-8150-adf119dc6774.png)

New code (0.14.1):
https://github.com/scikit-image/scikit-image/blob/v0.14.1/skimage/filters/thresholding.py#L594-L604
![result_14 1](https://user-images.githubusercontent.com/2853022/50166481-754a1100-02e7-11e9-8e8f-8fffb079424d.png)

Not sure whether this change is intended. The while loop in the new code seems to be different. For one, the parameter ``threshold`` is not used there.


Discussion:
@jni This is something you changed in this commit... Do you remember? https://github.com/scikit-image/scikit-image/commit/f328085c6d2771fdffc13822b9c135cd089b1c00#diff-3d45990596f953f3e60ccfb305cf8924
@sciunto @paulmueller yep, I changed this in https://github.com/scikit-image/scikit-image/pull/3402 and I remember it well, because I spent a long time reviewing the code and the original paper. Long story short: unless I am severely mistaken, our implementation of Li thresholding was incorrect before and it was just a coincidence that it gave good results for your use case.

How to test my hypothesis:

- `from skimage.filters.thresholding import _cross_entropy`
- try *all possible threshold values* and measure the cross entropy with `_cross_entropy(image, threshold)`. This is the measure that `threshold_li` tries to minimise.
- Check whether the returned value by `threshold_li` matches the above-found threshold

More practically, maybe another thresholding method like `threshold_otsu` would work better on your image?

I intend to try all these things out later today myself but thought I'd get you started if you have time. Sorry about breaking your pipeline!

Resolving patch:
diff --git a/skimage/filters/thresholding.py b/skimage/filters/thresholding.py
--- a/skimage/filters/thresholding.py
+++ b/skimage/filters/thresholding.py
@@ -527,8 +527,8 @@ def threshold_li(image, *, tolerance=None):
 
     tolerance : float, optional
         Finish the computation when the change in the threshold in an iteration
-        is less than this value. By default, this is half of the range of the
-        input image, divided by 256.
+        is less than this value. By default, this is half the smallest
+        difference between intensity values in ``image``.
 
     Returns
     -------
@@ -578,8 +578,7 @@ def threshold_li(image, *, tolerance=None):
     # Li's algorithm requires positive image (because of log(mean))
     image_min = np.min(image)
     image -= image_min
-    image_range = np.max(image)
-    tolerance = tolerance or 0.5 * image_range / 256
+    tolerance = tolerance or np.min(np.diff(np.unique(image))) / 2
 
     # Initial estimate
     t_curr = np.mean(image)


Test patch: