Repository: Project-MONAI/MONAI

Issue:
SurfaceDistanceMetric (SDM) and HausdorffDistanceMetric (HDM) print wrong all-zero warning messages
**Describe the bug**
SDM and HDM incorrectly warn that both prediction and ground truth are all 0 (for some class) even when only one of them is all 0 (for some class):

**To Reproduce**
Steps to reproduce the behavior (for HDM):

```
import torch
import monai
metric = monai.metrics.HausdorffDistanceMetric()
#forward direction
metric(torch.cat((torch.ones(1,1,256,256),torch.zeros(1,1,256,256)), dim=1), torch.randint(0,2,(1,2,256,256)))

/autofs/space/vault_021/users/siy0/anaconda3/lib/python3.8/site-packages/monai/metrics/utils.py:199: UserWarning: prediction is all 0, this may result in nan/inf distance.
  warnings.warn("prediction is all 0, this may result in nan/inf distance.")
/autofs/space/vault_021/users/siy0/anaconda3/lib/python3.8/site-packages/monai/metrics/utils.py:195: UserWarning: ground truth is all 0, this may result in nan/inf distance.
  warnings.warn("ground truth is all 0, this may result in nan/inf distance.")

#backward direction
metric(torch.randint(0,2,(1,2,256,256)),torch.cat((torch.ones(1,1,256,256),torch.zeros(1,1,256,256)), dim=1))

/autofs/space/vault_021/users/siy0/anaconda3/lib/python3.8/site-packages/monai/metrics/utils.py:195: UserWarning: ground truth is all 0, this may result in nan/inf distance.
  warnings.warn("ground truth is all 0, this may result in nan/inf distance.")
/autofs/space/vault_021/users/siy0/anaconda3/lib/python3.8/site-packages/monai/metrics/utils.py:199: UserWarning: prediction is all 0, this may result in nan/inf distance.
  warnings.warn("prediction is all 0, this may result in nan/inf distance.")

```

The same bug is there if SDM is run with `symmetric=True`

**Expected behavior**
Only the first warning should be displayed in each of the two above cases.



Discussion:

Resolving patch:
diff --git a/monai/metrics/hausdorff_distance.py b/monai/metrics/hausdorff_distance.py
--- a/monai/metrics/hausdorff_distance.py
+++ b/monai/metrics/hausdorff_distance.py
@@ -157,6 +157,11 @@ def compute_hausdorff_distance(
     hd = np.empty((batch_size, n_class))
     for b, c in np.ndindex(batch_size, n_class):
         (edges_pred, edges_gt) = get_mask_edges(y_pred[b, c], y[b, c])
+        if not np.any(edges_gt):
+            warnings.warn(f"the ground truth of class {c} is all 0, this may result in nan/inf distance.")
+        if not np.any(edges_pred):
+            warnings.warn(f"the prediction of class {c} is all 0, this may result in nan/inf distance.")
+
         distance_1 = compute_percent_hausdorff_distance(edges_pred, edges_gt, distance_metric, percentile)
         if directed:
             hd[b, c] = distance_1
diff --git a/monai/metrics/surface_distance.py b/monai/metrics/surface_distance.py
--- a/monai/metrics/surface_distance.py
+++ b/monai/metrics/surface_distance.py
@@ -152,6 +152,11 @@ def compute_average_surface_distance(
 
     for b, c in np.ndindex(batch_size, n_class):
         (edges_pred, edges_gt) = get_mask_edges(y_pred[b, c], y[b, c])
+        if not np.any(edges_gt):
+            warnings.warn(f"the ground truth of class {c} is all 0, this may result in nan/inf distance.")
+        if not np.any(edges_pred):
+            warnings.warn(f"the prediction of class {c} is all 0, this may result in nan/inf distance.")
+
         surface_distance = get_surface_distance(edges_pred, edges_gt, distance_metric=distance_metric)
         if surface_distance.shape == (0,):
             avg_surface_distance = np.nan
diff --git a/monai/metrics/utils.py b/monai/metrics/utils.py
--- a/monai/metrics/utils.py
+++ b/monai/metrics/utils.py
@@ -9,7 +9,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import warnings
 from typing import Tuple, Union
 
 import numpy as np
@@ -188,15 +187,17 @@ def get_surface_distance(
             - ``"euclidean"``, uses Exact Euclidean distance transform.
             - ``"chessboard"``, uses `chessboard` metric in chamfer type of transform.
             - ``"taxicab"``, uses `taxicab` metric in chamfer type of transform.
+
+    Note:
+        If seg_pred or seg_gt is all 0, may result in nan/inf distance.
+
     """
 
     if not np.any(seg_gt):
         dis = np.inf * np.ones_like(seg_gt)
-        warnings.warn("ground truth is all 0, this may result in nan/inf distance.")
     else:
         if not np.any(seg_pred):
             dis = np.inf * np.ones_like(seg_gt)
-            warnings.warn("prediction is all 0, this may result in nan/inf distance.")
             return np.asarray(dis[seg_gt])
         if distance_metric == "euclidean":
             dis = distance_transform_edt(~seg_gt)


Test patch: