Repository: scikit-image/scikit-image

Issue:
Problem in variation of infromation, should we ignore 0 in pred?
## Description
I found there are problem in the calculation of variation of information.
I found that the codes of vi in skimage and cremi are also based on gala's code.
*gala: https://github.com/janelia-flyem/gala/blob/master/gala/evaluate.py
*cremi: https://github.com/cremi/cremi_python/blob/master/cremi/evaluation/voi.py
But there is a tiny question: Where and why to ignore 0 in VI?
If I don't ignore 0 in the prediction, I match [1, 1, 0] in pred to [1, 2, 3] in GT, and I get 2/3 * 1 + 1/3 * 0 = 2/3 for the VI.

Beside
For gala code,  they ignore 0 both in gt and pred (Default)
For cremi code, they only ignore 0 in gt, not in pred (Deafult)
For skimage, you did not ignore 0 in gt and pred (Default)
I made a simple comparison below, and i found that different setting will draw out different result. 
![image](https://user-images.githubusercontent.com/6437651/88139255-e9663c80-cc21-11ea-97f7-194d043efc30.png)


Discussion:

Resolving patch:
diff --git a/skimage/metrics/_contingency_table.py b/skimage/metrics/_contingency_table.py
--- a/skimage/metrics/_contingency_table.py
+++ b/skimage/metrics/_contingency_table.py
@@ -4,7 +4,8 @@
 __all__ = ['contingency_table']
 
 
-def contingency_table(im_true, im_test, *, ignore_labels=(), normalize=False):
+def contingency_table(im_true, im_test, *, ignore_labels=None,
+                      normalize=False):
     """
     Return the contingency table for all regions in matched segmentations.
 
@@ -27,14 +28,12 @@ def contingency_table(im_true, im_test, *, ignore_labels=(), normalize=False):
         labeled `i` in `im_true` and `j` in `im_test`.
     """
 
-    im_test_r = im_test.ravel()
-    im_true_r = im_true.ravel()
-    ignored = np.zeros(im_true_r.shape, np.bool)
-    for label in ignore_labels:
-        ignored[im_true_r == label] = True
-    data = np.ones(im_true_r.shape)
-    data[ignored] = 0
+    if ignore_labels is None:
+        ignore_labels = []
+    im_test_r = im_test.reshape(-1)
+    im_true_r = im_true.reshape(-1)
+    data = np.isin(im_true_r, ignore_labels, invert=True).astype(float)
     if normalize:
-        data = data / im_true.size
+        data /= np.count_nonzero(data)
     cont = sparse.coo_matrix((data, (im_true_r, im_test_r))).tocsr()
     return cont


Test patch:
diff --git a/skimage/metrics/tests/test_segmentation_metrics.py b/skimage/metrics/tests/test_segmentation_metrics.py
--- a/skimage/metrics/tests/test_segmentation_metrics.py
+++ b/skimage/metrics/tests/test_segmentation_metrics.py
@@ -1,11 +1,12 @@
 import numpy as np
-import scipy.sparse as sparse
 
 from skimage.metrics import (adapted_rand_error,
                              variation_of_information,
                              contingency_table)
 
-from skimage._shared.testing import assert_equal, assert_almost_equal, assert_array_equal
+from skimage._shared.testing import (assert_equal,
+                                     assert_almost_equal,
+                                     assert_array_equal)
 
 
 def test_contingency_table():
@@ -29,6 +30,17 @@ def test_vi():
     assert_equal(np.sum(variation_of_information(im_true, im_test)), 1)
 
 
+def test_vi_ignore_labels():
+    im1 = np.array([[1, 0],
+                    [2, 3]], dtype='uint8')
+    im2 = np.array([[1, 1],
+                    [1, 0]], dtype='uint8')
+
+    false_splits, false_merges = variation_of_information(im1, im2,
+                                                          ignore_labels=[0])
+    assert (false_splits, false_merges) == (0, 2 / 3)
+
+
 def test_are():
     im_true = np.array([[2, 1], [1, 2]])
     im_test = np.array([[1, 2], [3, 1]])