diff --git a/mmocr/utils/box_util.py b/mmocr/utils/box_util.py index de7be7aa6..7de5d21eb 100644 --- a/mmocr/utils/box_util.py +++ b/mmocr/utils/box_util.py @@ -1,5 +1,7 @@ # Copyright (c) OpenMMLab. All rights reserved. -import functools +import math +import operator +from functools import reduce import numpy as np @@ -160,8 +162,14 @@ def bezier_to_polygon(bezier_points, num_sample=20): def sort_points(points): - """Sort arbitory points in clockwise order. Reference: - https://stackoverflow.com/a/6989383. + """Sort arbitrary points in clockwise order in Cartesian coordinate, you + may need to reverse the output sequence if you are using OpenCV's image + coordinate. + + Reference: + https://github.com/novioleo/Savior/blob/master/Utils/GeometryUtils.py. + + Warning: This function can only sort convex polygons. Args: points (list[ndarray] or ndarray or list[list]): A list of unsorted @@ -170,30 +178,13 @@ def sort_points(points): Returns: list[ndarray]: A list of points sorted in clockwise order. """ - assert is_type_list(points, np.ndarray) or isinstance(points, np.ndarray) \ or is_2dlist(points) - - points = np.array(points) - center = np.mean(points, axis=0) - - def cmp(a, b): - oa = a - center - ob = b - center - - # Some corner cases - if oa[0] >= 0 and ob[0] < 0: - return 1 - if oa[0] < 0 and ob[0] >= 0: - return -1 - - prod = np.cross(oa, ob) - if prod > 0: - return 1 - if prod < 0: - return -1 - - # a, b are on the same line from the center - return 1 if (oa**2).sum() < (ob**2).sum() else -1 - - return sorted(points, key=functools.cmp_to_key(cmp)) + center_point = tuple( + map(operator.truediv, + reduce(lambda x, y: map(operator.add, x, y), points), + [len(points)] * 2)) + return sorted( + points, + key=lambda coord: (180 + math.degrees( + math.atan2(*tuple(map(operator.sub, coord, center_point))))) % 360) diff --git a/tests/test_utils/test_box.py b/tests/test_utils/test_box.py index 9af23cc51..2a045f671 100644 --- a/tests/test_utils/test_box.py +++ b/tests/test_utils/test_box.py @@ -124,5 +124,27 @@ def test_sort_points(): points = [[1, 1], [1, -1], [-1, 1], [-1, -1]] assert np.allclose(target, sort_points(points)) + points = [[0.5, 0.3], [1, 0.5], [-0.5, 0.8], [-0.1, 1]] + target = [[-0.5, 0.8], [-0.1, 1], [1, 0.5], [0.5, 0.3]] + assert np.allclose(target, sort_points(points)) + + points = [[0.5, 3], [0.1, -0.2], [-0.5, -0.3], [-0.7, 3.1]] + target = [[-0.5, -0.3], [-0.7, 3.1], [0.5, 3], [0.1, -0.2]] + assert np.allclose(target, sort_points(points)) + + points = [[1, 0.8], [0.8, -1], [1.8, 0.5], [1.9, -0.6], [-0.5, 2], + [-1, 1.8], [-2, 0.7], [-1.6, -0.2], [-1, -0.5]] + target = [[-1, -0.5], [-1.6, -0.2], [-2, 0.7], [-1, 1.8], [-0.5, 2], + [1, 0.8], [1.8, 0.5], [1.9, -0.6], [0.8, -1]] + assert np.allclose(target, sort_points(points)) + + # concave polygon may failed + points = [[1, 0], [-1, 0], [0, 0], [0, -1], [0.25, 1], [0.75, 1], + [-0.25, 1], [-0.75, 1]] + target = [[-1, 0], [-0.75, 1], [-0.25, 1], [0, 0], [0.25, 1], [0.75, 1], + [1, 0], [0, -1]] + with pytest.raises(AssertionError): + assert np.allclose(target, sort_points(points)) + with pytest.raises(AssertionError): sort_points([1, 2])