| | from typing import Optional |
| |
|
| | import numpy as np |
| |
|
| |
|
| | def w_np_non_max_suppression( |
| | prediction, |
| | conf_thresh: float = 0.25, |
| | iou_thresh: float = 0.45, |
| | class_agnostic: bool = False, |
| | max_detections: int = 300, |
| | max_candidate_detections: int = 3000, |
| | timeout_seconds: Optional[int] = None, |
| | num_masks: int = 0, |
| | box_format: str = "xywh", |
| | ): |
| | """Applies non-maximum suppression to predictions. |
| | |
| | Args: |
| | prediction (np.ndarray): Array of predictions. Format for single prediction is |
| | [bbox x 4, max_class_confidence, (confidence) x num_of_classes, additional_element x num_masks] |
| | conf_thresh (float, optional): Confidence threshold. Defaults to 0.25. |
| | iou_thresh (float, optional): IOU threshold. Defaults to 0.45. |
| | class_agnostic (bool, optional): Whether to ignore class labels. Defaults to False. |
| | max_detections (int, optional): Maximum number of detections. Defaults to 300. |
| | max_candidate_detections (int, optional): Maximum number of candidate detections. Defaults to 3000. |
| | timeout_seconds (Optional[int], optional): Timeout in seconds. Defaults to None. |
| | num_masks (int, optional): Number of masks. Defaults to 0. |
| | box_format (str, optional): Format of bounding boxes. Either 'xywh' or 'xyxy'. Defaults to 'xywh'. |
| | |
| | Returns: |
| | list: List of filtered predictions after non-maximum suppression. Format of a single result is: |
| | [bbox x 4, max_class_confidence, max_class_confidence, id_of_class_with_max_confidence, |
| | additional_element x num_masks] |
| | """ |
| | num_classes = prediction.shape[2] - 5 - num_masks |
| |
|
| | np_box_corner = np.zeros(prediction.shape) |
| | if box_format == "xywh": |
| | np_box_corner[:, :, 0] = prediction[:, :, 0] - prediction[:, :, 2] / 2 |
| | np_box_corner[:, :, 1] = prediction[:, :, 1] - prediction[:, :, 3] / 2 |
| | np_box_corner[:, :, 2] = prediction[:, :, 0] + prediction[:, :, 2] / 2 |
| | np_box_corner[:, :, 3] = prediction[:, :, 1] + prediction[:, :, 3] / 2 |
| | prediction[:, :, :4] = np_box_corner[:, :, :4] |
| | elif box_format == "xyxy": |
| | pass |
| | else: |
| | raise ValueError( |
| | "box_format must be either 'xywh' or 'xyxy', got {}".format(box_format) |
| | ) |
| |
|
| | batch_predictions = [] |
| | for np_image_i, np_image_pred in enumerate(prediction): |
| | filtered_predictions = [] |
| | np_conf_mask = (np_image_pred[:, 4] >= conf_thresh).squeeze() |
| |
|
| | np_image_pred = np_image_pred[np_conf_mask] |
| | if np_image_pred.shape[0] == 0: |
| | batch_predictions.append(filtered_predictions) |
| | continue |
| | np_class_conf = np.max(np_image_pred[:, 5 : num_classes + 5], 1) |
| | np_class_pred = np.argmax(np_image_pred[:, 5 : num_classes + 5], 1) |
| | np_class_conf = np.expand_dims(np_class_conf, axis=1) |
| | np_class_pred = np.expand_dims(np_class_pred, axis=1) |
| | np_mask_pred = np_image_pred[:, 5 + num_classes :] |
| | np_detections = np.append( |
| | np.append( |
| | np.append(np_image_pred[:, :5], np_class_conf, axis=1), |
| | np_class_pred, |
| | axis=1, |
| | ), |
| | np_mask_pred, |
| | axis=1, |
| | ) |
| |
|
| | np_unique_labels = np.unique(np_detections[:, 6]) |
| |
|
| | if class_agnostic: |
| | np_detections_class = sorted( |
| | np_detections, key=lambda row: row[4], reverse=True |
| | ) |
| | filtered_predictions.extend( |
| | non_max_suppression_fast(np.array(np_detections_class), iou_thresh) |
| | ) |
| | else: |
| | for c in np_unique_labels: |
| | np_detections_class = np_detections[np_detections[:, 6] == c] |
| | np_detections_class = sorted( |
| | np_detections_class, key=lambda row: row[4], reverse=True |
| | ) |
| | filtered_predictions.extend( |
| | non_max_suppression_fast(np.array(np_detections_class), iou_thresh) |
| | ) |
| | filtered_predictions = sorted( |
| | filtered_predictions, key=lambda row: row[4], reverse=True |
| | ) |
| | batch_predictions.append(filtered_predictions[:max_detections]) |
| | return batch_predictions |
| |
|
| |
|
| | |
| | def non_max_suppression_fast(boxes, overlapThresh): |
| | """Applies non-maximum suppression to bounding boxes. |
| | |
| | Args: |
| | boxes (np.ndarray): Array of bounding boxes with confidence scores. |
| | overlapThresh (float): Overlap threshold for suppression. |
| | |
| | Returns: |
| | list: List of bounding boxes after non-maximum suppression. |
| | """ |
| | |
| | if len(boxes) == 0: |
| | return [] |
| | |
| | |
| | if boxes.dtype.kind == "i": |
| | boxes = boxes.astype("float") |
| | |
| | pick = [] |
| | |
| | x1 = boxes[:, 0] |
| | y1 = boxes[:, 1] |
| | x2 = boxes[:, 2] |
| | y2 = boxes[:, 3] |
| | conf = boxes[:, 4] |
| | |
| | |
| | area = (x2 - x1 + 1) * (y2 - y1 + 1) |
| | idxs = np.argsort(conf) |
| | |
| | |
| | while len(idxs) > 0: |
| | |
| | |
| | last = len(idxs) - 1 |
| | i = idxs[last] |
| | pick.append(i) |
| | |
| | |
| | |
| | xx1 = np.maximum(x1[i], x1[idxs[:last]]) |
| | yy1 = np.maximum(y1[i], y1[idxs[:last]]) |
| | xx2 = np.minimum(x2[i], x2[idxs[:last]]) |
| | yy2 = np.minimum(y2[i], y2[idxs[:last]]) |
| | |
| | w = np.maximum(0, xx2 - xx1 + 1) |
| | h = np.maximum(0, yy2 - yy1 + 1) |
| | |
| | overlap = (w * h) / area[idxs[:last]] |
| | |
| | idxs = np.delete( |
| | idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0])) |
| | ) |
| | |
| | |
| | return boxes[pick].astype("float") |
| |
|