""" Locality aware nms. This code is refered from: https://github.com/songdejia/EAST/blob/master/locality_aware_nms.py """ import numpy as np from shapely.geometry import Polygon def intersection(g, p): """ Intersection. """ g = Polygon(g[:8].reshape((4, 2))) p = Polygon(p[:8].reshape((4, 2))) g = g.buffer(0) p = p.buffer(0) if not g.is_valid or not p.is_valid: return 0 inter = Polygon(g).intersection(Polygon(p)).area union = g.area + p.area - inter if union == 0: return 0 else: return inter / union def intersection_iog(g, p): """ Intersection_iog. """ g = Polygon(g[:8].reshape((4, 2))) p = Polygon(p[:8].reshape((4, 2))) if not g.is_valid or not p.is_valid: return 0 inter = Polygon(g).intersection(Polygon(p)).area # union = g.area + p.area - inter union = p.area if union == 0: print("p_area is very small") return 0 else: return inter / union def weighted_merge(g, p): """ Weighted merge. """ g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8]) g[8] = g[8] + p[8] return g def standard_nms(S, thres): """ Standard nms. """ order = np.argsort(S[:, 8])[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) inds = np.where(ovr <= thres)[0] order = order[inds + 1] return S[keep] def standard_nms_inds(S, thres): """ Standard nms, retun inds. """ order = np.argsort(S[:, 8])[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) inds = np.where(ovr <= thres)[0] order = order[inds + 1] return keep def nms(S, thres): """ nms. """ order = np.argsort(S[:, 8])[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) ovr = np.array([intersection(S[i], S[t]) for t in order[1:]]) inds = np.where(ovr <= thres)[0] order = order[inds + 1] return keep def soft_nms(boxes_in, Nt_thres=0.3, threshold=0.8, sigma=0.5, method=2): """ soft_nms :para boxes_in, N x 9 (coords + score) :para threshould, eliminate cases min score(0.001) :para Nt_thres, iou_threshi :para sigma, gaussian weght :method, linear or gaussian """ boxes = boxes_in.copy() N = boxes.shape[0] if N is None or N < 1: return np.array([]) pos, maxpos = 0, 0 weight = 0.0 inds = np.arange(N) tbox, sbox = boxes[0].copy(), boxes[0].copy() for i in range(N): maxscore = boxes[i, 8] maxpos = i tbox = boxes[i].copy() ti = inds[i] pos = i + 1 # get max box while pos < N: if maxscore < boxes[pos, 8]: maxscore = boxes[pos, 8] maxpos = pos pos = pos + 1 # add max box as a detection boxes[i, :] = boxes[maxpos, :] inds[i] = inds[maxpos] # swap boxes[maxpos, :] = tbox inds[maxpos] = ti tbox = boxes[i].copy() pos = i + 1 # NMS iteration while pos < N: sbox = boxes[pos].copy() ts_iou_val = intersection(tbox, sbox) if ts_iou_val > 0: if method == 1: if ts_iou_val > Nt_thres: weight = 1 - ts_iou_val else: weight = 1 elif method == 2: weight = np.exp(-1.0 * ts_iou_val**2 / sigma) else: if ts_iou_val > Nt_thres: weight = 0 else: weight = 1 boxes[pos, 8] = weight * boxes[pos, 8] # if box score falls below thresold, discard the box by # swaping last box update N if boxes[pos, 8] < threshold: boxes[pos, :] = boxes[N - 1, :] inds[pos] = inds[N - 1] N = N - 1 pos = pos - 1 pos = pos + 1 return boxes[:N] def nms_locality(polys, thres=0.3): """ locality aware nms of EAST :param polys: a N*9 numpy array. first 8 coordinates, then prob :return: boxes after nms """ S = [] p = None for g in polys: if p is not None and intersection(g, p) > thres: p = weighted_merge(g, p) else: if p is not None: S.append(p) p = g if p is not None: S.append(p) if len(S) == 0: return np.array([]) return standard_nms(np.array(S), thres) if __name__ == "__main__": # 343,350,448,135,474,143,369,359 print(Polygon(np.array([[343, 350], [448, 135], [474, 143], [369, 359]])).area)