Spaces:
Build error
Build error
| # coding: utf-8 | |
| __author__ = 'ZFTurbo: https://kaggle.com/zfturbo' | |
| import numpy as np | |
| from numba import jit | |
| def prepare_boxes(boxes, scores, labels): | |
| result_boxes = boxes.copy() | |
| cond = (result_boxes < 0) | |
| cond_sum = cond.astype(np.int32).sum() | |
| if cond_sum > 0: | |
| print('Warning. Fixed {} boxes coordinates < 0'.format(cond_sum)) | |
| result_boxes[cond] = 0 | |
| cond = (result_boxes > 1) | |
| cond_sum = cond.astype(np.int32).sum() | |
| if cond_sum > 0: | |
| print('Warning. Fixed {} boxes coordinates > 1. Check that your boxes was normalized at [0, 1]'.format(cond_sum)) | |
| result_boxes[cond] = 1 | |
| boxes1 = result_boxes.copy() | |
| result_boxes[:, 0] = np.min(boxes1[:, [0, 2]], axis=1) | |
| result_boxes[:, 2] = np.max(boxes1[:, [0, 2]], axis=1) | |
| result_boxes[:, 1] = np.min(boxes1[:, [1, 3]], axis=1) | |
| result_boxes[:, 3] = np.max(boxes1[:, [1, 3]], axis=1) | |
| area = (result_boxes[:, 2] - result_boxes[:, 0]) * (result_boxes[:, 3] - result_boxes[:, 1]) | |
| cond = (area == 0) | |
| cond_sum = cond.astype(np.int32).sum() | |
| if cond_sum > 0: | |
| print('Warning. Removed {} boxes with zero area!'.format(cond_sum)) | |
| result_boxes = result_boxes[area > 0] | |
| scores = scores[area > 0] | |
| labels = labels[area > 0] | |
| return result_boxes, scores, labels | |
| def cpu_soft_nms_float(dets, sc, Nt, sigma, thresh, method): | |
| """ | |
| Based on: https://github.com/DocF/Soft-NMS/blob/master/soft_nms.py | |
| It's different from original soft-NMS because we have float coordinates on range [0; 1] | |
| :param dets: boxes format [x1, y1, x2, y2] | |
| :param sc: scores for boxes | |
| :param Nt: required iou | |
| :param sigma: | |
| :param thresh: | |
| :param method: 1 - linear soft-NMS, 2 - gaussian soft-NMS, 3 - standard NMS | |
| :return: index of boxes to keep | |
| """ | |
| # indexes concatenate boxes with the last column | |
| N = dets.shape[0] | |
| indexes = np.array([np.arange(N)]) | |
| dets = np.concatenate((dets, indexes.T), axis=1) | |
| # the order of boxes coordinate is [y1, x1, y2, x2] | |
| y1 = dets[:, 1] | |
| x1 = dets[:, 0] | |
| y2 = dets[:, 3] | |
| x2 = dets[:, 2] | |
| scores = sc | |
| areas = (x2 - x1) * (y2 - y1) | |
| for i in range(N): | |
| # intermediate parameters for later parameters exchange | |
| tBD = dets[i, :].copy() | |
| tscore = scores[i].copy() | |
| tarea = areas[i].copy() | |
| pos = i + 1 | |
| # | |
| if i != N - 1: | |
| maxscore = np.max(scores[pos:], axis=0) | |
| maxpos = np.argmax(scores[pos:], axis=0) | |
| else: | |
| maxscore = scores[-1] | |
| maxpos = 0 | |
| if tscore < maxscore: | |
| dets[i, :] = dets[maxpos + i + 1, :] | |
| dets[maxpos + i + 1, :] = tBD | |
| tBD = dets[i, :] | |
| scores[i] = scores[maxpos + i + 1] | |
| scores[maxpos + i + 1] = tscore | |
| tscore = scores[i] | |
| areas[i] = areas[maxpos + i + 1] | |
| areas[maxpos + i + 1] = tarea | |
| tarea = areas[i] | |
| # IoU calculate | |
| xx1 = np.maximum(dets[i, 1], dets[pos:, 1]) | |
| yy1 = np.maximum(dets[i, 0], dets[pos:, 0]) | |
| xx2 = np.minimum(dets[i, 3], dets[pos:, 3]) | |
| yy2 = np.minimum(dets[i, 2], dets[pos:, 2]) | |
| w = np.maximum(0.0, xx2 - xx1) | |
| h = np.maximum(0.0, yy2 - yy1) | |
| inter = w * h | |
| ovr = inter / (areas[i] + areas[pos:] - inter) | |
| # Three methods: 1.linear 2.gaussian 3.original NMS | |
| if method == 1: # linear | |
| weight = np.ones(ovr.shape) | |
| weight[ovr > Nt] = weight[ovr > Nt] - ovr[ovr > Nt] | |
| elif method == 2: # gaussian | |
| weight = np.exp(-(ovr * ovr) / sigma) | |
| else: # original NMS | |
| weight = np.ones(ovr.shape) | |
| weight[ovr > Nt] = 0 | |
| scores[pos:] = weight * scores[pos:] | |
| # select the boxes and keep the corresponding indexes | |
| inds = dets[:, 4][scores > thresh] | |
| keep = inds.astype(int) | |
| return keep | |
| def nms_float_fast(dets, scores, thresh): | |
| """ | |
| # It's different from original nms because we have float coordinates on range [0; 1] | |
| :param dets: numpy array of boxes with shape: (N, 5). Order: x1, y1, x2, y2, score. All variables in range [0; 1] | |
| :param thresh: IoU value for boxes | |
| :return: index of boxes to keep | |
| """ | |
| x1 = dets[:, 0] | |
| y1 = dets[:, 1] | |
| x2 = dets[:, 2] | |
| y2 = dets[:, 3] | |
| areas = (x2 - x1) * (y2 - y1) | |
| order = scores.argsort()[::-1] | |
| keep = [] | |
| while order.size > 0: | |
| i = order[0] | |
| keep.append(i) | |
| xx1 = np.maximum(x1[i], x1[order[1:]]) | |
| yy1 = np.maximum(y1[i], y1[order[1:]]) | |
| xx2 = np.minimum(x2[i], x2[order[1:]]) | |
| yy2 = np.minimum(y2[i], y2[order[1:]]) | |
| w = np.maximum(0.0, xx2 - xx1) | |
| h = np.maximum(0.0, yy2 - yy1) | |
| inter = w * h | |
| ovr = inter / (areas[i] + areas[order[1:]] - inter) | |
| inds = np.where(ovr <= thresh)[0] | |
| order = order[inds + 1] | |
| return keep | |
| def nms_method(boxes, scores, labels, method=3, iou_thr=0.5, sigma=0.5, thresh=0.001, weights=None): | |
| """ | |
| :param boxes: list of boxes predictions from each model, each box is 4 numbers. | |
| It has 3 dimensions (models_number, model_preds, 4) | |
| Order of boxes: x1, y1, x2, y2. We expect float normalized coordinates [0; 1] | |
| :param scores: list of scores for each model | |
| :param labels: list of labels for each model | |
| :param method: 1 - linear soft-NMS, 2 - gaussian soft-NMS, 3 - standard NMS | |
| :param iou_thr: IoU value for boxes to be a match | |
| :param sigma: Sigma value for SoftNMS | |
| :param thresh: threshold for boxes to keep (important for SoftNMS) | |
| :param weights: list of weights for each model. Default: None, which means weight == 1 for each model | |
| :return: boxes: boxes coordinates (Order of boxes: x1, y1, x2, y2). | |
| :return: scores: confidence scores | |
| :return: labels: boxes labels | |
| """ | |
| # If weights are specified | |
| if weights is not None: | |
| if len(boxes) != len(weights): | |
| print('Incorrect number of weights: {}. Must be: {}. Skip it'.format(len(weights), len(boxes))) | |
| else: | |
| weights = np.array(weights) | |
| for i in range(len(weights)): | |
| scores[i] = (np.array(scores[i]) * weights[i]) / weights.sum() | |
| # We concatenate everything | |
| boxes = np.concatenate(boxes) | |
| scores = np.concatenate(scores) | |
| labels = np.concatenate(labels) | |
| # Fix coordinates and removed zero area boxes | |
| boxes, scores, labels = prepare_boxes(boxes, scores, labels) | |
| # Run NMS independently for each label | |
| unique_labels = np.unique(labels) | |
| final_boxes = [] | |
| final_scores = [] | |
| final_labels = [] | |
| for l in unique_labels: | |
| condition = (labels == l) | |
| boxes_by_label = boxes[condition] | |
| scores_by_label = scores[condition] | |
| labels_by_label = np.array([l] * len(boxes_by_label)) | |
| if method != 3: | |
| keep = cpu_soft_nms_float(boxes_by_label.copy(), scores_by_label.copy(), Nt=iou_thr, sigma=sigma, thresh=thresh, method=method) | |
| else: | |
| # Use faster function | |
| keep = nms_float_fast(boxes_by_label, scores_by_label, thresh=iou_thr) | |
| final_boxes.append(boxes_by_label[keep]) | |
| final_scores.append(scores_by_label[keep]) | |
| final_labels.append(labels_by_label[keep]) | |
| final_boxes = np.concatenate(final_boxes) | |
| final_scores = np.concatenate(final_scores) | |
| final_labels = np.concatenate(final_labels) | |
| return final_boxes, final_scores, final_labels | |
| def nms(boxes, scores, labels, iou_thr=0.5, weights=None): | |
| """ | |
| Short call for standard NMS | |
| :param boxes: | |
| :param scores: | |
| :param labels: | |
| :param iou_thr: | |
| :param weights: | |
| :return: | |
| """ | |
| return nms_method(boxes, scores, labels, method=3, iou_thr=iou_thr, weights=weights) | |
| def soft_nms(boxes, scores, labels, method=2, iou_thr=0.5, sigma=0.5, thresh=0.001, weights=None): | |
| """ | |
| Short call for Soft-NMS | |
| :param boxes: | |
| :param scores: | |
| :param labels: | |
| :param method: | |
| :param iou_thr: | |
| :param sigma: | |
| :param thresh: | |
| :param weights: | |
| :return: | |
| """ | |
| return nms_method(boxes, scores, labels, method=method, iou_thr=iou_thr, sigma=sigma, thresh=thresh, weights=weights) |