# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import numpy as np from shapely.geometry import Polygon def points2polygon(points): """Convert k points to 1 polygon. Args: points (ndarray or list): A ndarray or a list of shape (2k) that indicates k points. Returns: polygon (Polygon): A polygon object. """ if isinstance(points, list): points = np.array(points) assert isinstance(points, np.ndarray) assert (points.size % 2 == 0) and (points.size >= 8) point_mat = points.reshape([-1, 2]) return Polygon(point_mat) def poly_intersection(poly_det, poly_gt): """Calculate the intersection area between two polygon. Args: poly_det (Polygon): A polygon predicted by detector. poly_gt (Polygon): A gt polygon. Returns: intersection_area (float): The intersection area between two polygons. """ assert isinstance(poly_det, Polygon) assert isinstance(poly_gt, Polygon) poly_inter = poly_det.buffer(0.001) & poly_gt.buffer(0.001) return poly_inter.area, poly_inter def poly_union(poly_det, poly_gt): """Calculate the union area between two polygon. Args: poly_det (Polygon): A polygon predicted by detector. poly_gt (Polygon): A gt polygon. Returns: union_area (float): The union area between two polygons. """ assert isinstance(poly_det, Polygon) assert isinstance(poly_gt, Polygon) area_det = poly_det.area area_gt = poly_gt.area area_inters, _ = poly_intersection(poly_det, poly_gt) return area_det + area_gt - area_inters def valid_boundary(x, with_score=True): num = len(x) if num < 8: return False if num % 2 == 0 and (not with_score): return True if num % 2 == 1 and with_score: return True return False def boundary_iou(src, target): """Calculate the IOU between two boundaries. Args: src (list): Source boundary. target (list): Target boundary. Returns: iou (float): The iou between two boundaries. """ assert valid_boundary(src, False) assert valid_boundary(target, False) src_poly = points2polygon(src) target_poly = points2polygon(target) return poly_iou(src_poly, target_poly) def poly_iou(poly_det, poly_gt): """Calculate the IOU between two polygons. Args: poly_det (Polygon): A polygon predicted by detector. poly_gt (Polygon): A gt polygon. Returns: iou (float): The IOU between two polygons. """ assert isinstance(poly_det, Polygon) assert isinstance(poly_gt, Polygon) area_inters, _ = poly_intersection(poly_det, poly_gt) area_union = poly_union(poly_det, poly_gt) if area_union == 0: return 0.0 return area_inters / area_union def poly_nms(polygons, threshold): assert isinstance(polygons, list) polygons = np.array(sorted(polygons, key=lambda x: x[-1])) keep_poly = [] index = [i for i in range(polygons.shape[0])] while len(index) > 0: keep_poly.append(polygons[index[-1]].tolist()) A = polygons[index[-1]][:-1] index = np.delete(index, -1) iou_list = np.zeros((len(index), )) for i in range(len(index)): B = polygons[index[i]][:-1] iou_list[i] = boundary_iou(A, B) remove_index = np.where(iou_list > threshold) index = np.delete(index, remove_index) return keep_poly