Repository: open-mmlab/mmocr

Issue:
There is a chance for the contour area equals 0, which will raise error when training and testing.
I found the bug when testing the textsnake with my own dataset, the bug randomly occurred during training and testing. After serveral hours reading the code and paper, I managed to fix it by adding a check inside.
change this
https://github.com/open-mmlab/mmocr/blob/ce98d23ba2b5323348679dc19614d35c747c6a3c/mmocr/models/textdet/postprocess/wrapper.py#L390
to 
```python
if len(contours) > 0 and cv2.contourArea(contours[0]) > 0:
```
The contour for the boundary may be a single point sometime. 


Discussion:
Thanks! Would you like to making a pull request, so that your contribution would be recorded? Adding a test case would be greatly appreciated :) Also, we can add the change if you choose so
> Thanks! Would you like to making a pull request, so that your contribution would be recorded? Adding a test case would be greatly appreciated :) Also, we can add the change if you choose so

Sorry. I can't make pull request due to some limitations of the network. 
Thanks 😊
And also the contour returned by cv2 may not be a 8 points polygon.
which means 
```python
if len(contours) > 0 and cv2.contourArea(contours[0]) > 0:
    boundary = contours[0].flatten().tolist()
    if len(boundary) >= 8:
        boundaries.append(boundary)
```


Resolving patch:
diff --git a/mmocr/models/textdet/postprocess/wrapper.py b/mmocr/models/textdet/postprocess/wrapper.py
--- a/mmocr/models/textdet/postprocess/wrapper.py
+++ b/mmocr/models/textdet/postprocess/wrapper.py
@@ -373,14 +373,15 @@ def textsnake_decode(preds,
 
         instance_mask = np.zeros(mask_sz, dtype=np.uint8)
         for x, y, radius, score in instance_disks:
-            if radius > 0:
+            if radius > 1:
                 cv2.circle(instance_mask, (int(x), int(y)), int(radius), 1, -1)
         contours, _ = cv2.findContours(instance_mask, cv2.RETR_TREE,
                                        cv2.CHAIN_APPROX_SIMPLE)
 
         score = np.sum(instance_mask * pred_text_score) / (
             np.sum(instance_mask) + 1e-8)
-        if len(contours) > 0:
+        if (len(contours) > 0 and cv2.contourArea(contours[0]) > 0
+                and contours[0].size > 8):
             boundary = contours[0].flatten().tolist()
             boundaries.append(boundary + [score])
 


Test patch:
diff --git a/tests/test_utils/test_wrapper.py b/tests/test_utils/test_wrapper.py
--- a/tests/test_utils/test_wrapper.py
+++ b/tests/test_utils/test_wrapper.py
@@ -1,10 +1,14 @@
 import numpy as np
 import torch
 
+from mmocr.models.textdet.postprocess.wrapper import (comps2boundaries,
+                                                      db_decode, fcenet_decode,
+                                                      poly_nms,
+                                                      textsnake_decode)
+
 
 def test_db_boxes_from_bitmaps():
     """Test the boxes_from_bitmaps function in db_decoder."""
-    from mmocr.models.textdet.postprocess.wrapper import db_decode
     pred = np.array([[[0.8, 0.8, 0.8, 0.8, 0], [0.8, 0.8, 0.8, 0.8, 0],
                       [0.8, 0.8, 0.8, 0.8, 0], [0.8, 0.8, 0.8, 0.8, 0],
                       [0.8, 0.8, 0.8, 0.8, 0]]])
@@ -15,7 +19,6 @@ def test_db_boxes_from_bitmaps():
 
 
 def test_fcenet_decode():
-    from mmocr.models.textdet.postprocess.wrapper import fcenet_decode
 
     k = 1
     preds = []
@@ -33,7 +36,6 @@ def test_fcenet_decode():
 
 
 def test_poly_nms():
-    from mmocr.models.textdet.postprocess.wrapper import poly_nms
     threshold = 0
     polygons = []
     polygons.append([10, 10, 10, 30, 30, 30, 30, 10, 0.95])
@@ -46,7 +48,6 @@ def test_poly_nms():
 
 
 def test_comps2boundaries():
-    from mmocr.models.textdet.postprocess.wrapper import comps2boundaries
 
     # test comps2boundaries
     x1 = np.arange(2, 18, 2)
@@ -64,3 +65,31 @@ def test_comps2boundaries():
     # test comps2boundaries with blank inputs
     boundaries = comps2boundaries(text_comps[[]], comp_labels[[]])
     assert len(boundaries) == 0
+
+
+def test_textsnake_decode():
+
+    maps = torch.zeros((1, 6, 224, 224), dtype=torch.float)
+    maps[:, 0:2, :, :] = -10.
+    maps[:, 0, 60:100, 50:170] = 10.
+    maps[:, 1, 75:85, 60:160] = 10.
+    maps[:, 2, 75:85, 60:160] = 0.
+    maps[:, 3, 75:85, 60:160] = 1.
+    maps[:, 4, 75:85, 60:160] = 10.
+    # test decoding with text center region of small area
+    maps[:, 0:2, 150:152, 5:7] = 10.
+
+    results = textsnake_decode(torch.squeeze(maps))
+    assert len(results) == 1
+
+    # test decoding with small radius
+    maps.fill_(0.)
+    maps[:, 0:2, :, :] = -10.
+    maps[:, 0, 120:140, 20:40] = 10.
+    maps[:, 1, 120:140, 20:40] = 10.
+    maps[:, 2, 120:140, 20:40] = 0.
+    maps[:, 3, 120:140, 20:40] = 1.
+    maps[:, 4, 120:140, 20:40] = 0.5
+
+    results = textsnake_decode(torch.squeeze(maps))
+    assert len(results) == 0