diff --git a/mmocr/utils/bbox_utils.py b/mmocr/utils/bbox_utils.py index 19b28f62e..3b8b8631c 100644 --- a/mmocr/utils/bbox_utils.py +++ b/mmocr/utils/bbox_utils.py @@ -1,11 +1,10 @@ # Copyright (c) OpenMMLab. All rights reserved. -import functools from typing import List, Tuple import numpy as np from shapely.geometry import LineString, Point -from mmocr.utils.check_argument import is_2dlist, is_type_list +from mmocr.utils.check_argument import is_type_list from mmocr.utils.point_utils import point_distance, points_center from mmocr.utils.typing_utils import ArrayLike @@ -248,47 +247,6 @@ def bezier2polygon(bezier_points: np.ndarray, return points.tolist() -def sort_points(points): - # TODO Add typehints & test & docstring - """Sort arbitory points in clockwise order. Reference: - https://stackoverflow.com/a/6989383. - - Args: - points (list[ndarray] or ndarray or list[list]): A list of unsorted - boundary 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)) - - def sort_vertex(points_x, points_y): # TODO Add typehints & docstring & test """Sort box vertices in clockwise order from left-top first. diff --git a/mmocr/utils/polygon_utils.py b/mmocr/utils/polygon_utils.py index 7c6b857ff..77b552e9a 100644 --- a/mmocr/utils/polygon_utils.py +++ b/mmocr/utils/polygon_utils.py @@ -1,5 +1,7 @@ # Copyright (c) OpenMMLab. All rights reserved. -import functools +import math +import operator +from functools import reduce from typing import List, Optional, Sequence, Tuple, Union import numpy as np @@ -374,8 +376,14 @@ def boundary_iou(src: List, def sort_points(points): # TODO Add typehints & test & docstring - """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 @@ -384,33 +392,16 @@ def sort_points(points): Returns: list[ndarray]: A list of points sorted in clockwise order. """ - assert is_list_of(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) def sort_vertex(points_x, points_y): diff --git a/tests/test_utils/test_polygon_utils.py b/tests/test_utils/test_polygon_utils.py index 94b77d2dc..8cbd4f899 100644 --- a/tests/test_utils/test_polygon_utils.py +++ b/tests/test_utils/test_polygon_utils.py @@ -343,6 +343,27 @@ def test_sort_points(self): points = [[1, 1], [1, -1], [-1, 1], [-1, -1]] self.assertTrue(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]] + self.assertTrue(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]] + self.assertTrue(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]] + self.assertTrue(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]] + self.assertFalse(np.allclose(target, sort_points(points))) + with self.assertRaises(AssertionError): sort_points([1, 2])