| """ |
| PRIMA: Boosting Animal Mesh Recovery with Biological Priors and Test-Time Adaptation |
| |
| Official implementation of the paper: |
| "PRIMA: Boosting Animal Mesh Recovery with Biological Priors and Test-Time Adaptation" |
| by Xiaohang Yu, Ti Wang, and Mackenzie Weygandt Mathis |
| Licensed under a modified MIT license |
| """ |
|
|
| from __future__ import annotations |
|
|
| |
| |
| |
| |
| |
|
|
| from typing import Iterable |
|
|
| import numpy as np |
|
|
| ANIMAL_COCO_IDS = (15, 16, 17, 18, 19, 21, 22) |
|
|
|
|
| def _box_areas(boxes: np.ndarray) -> np.ndarray: |
| widths = np.maximum(0.0, boxes[:, 2] - boxes[:, 0]) |
| heights = np.maximum(0.0, boxes[:, 3] - boxes[:, 1]) |
| return widths * heights |
|
|
|
|
| def _intersection_areas(box: np.ndarray, boxes: np.ndarray) -> np.ndarray: |
| x1 = np.maximum(box[0], boxes[:, 0]) |
| y1 = np.maximum(box[1], boxes[:, 1]) |
| x2 = np.minimum(box[2], boxes[:, 2]) |
| y2 = np.minimum(box[3], boxes[:, 3]) |
| return np.maximum(0.0, x2 - x1) * np.maximum(0.0, y2 - y1) |
|
|
|
|
| def _suppress_duplicate_boxes( |
| boxes: np.ndarray, |
| scores: np.ndarray, |
| *, |
| iou_threshold: float, |
| containment_threshold: float, |
| ) -> np.ndarray: |
| if len(boxes) <= 1: |
| return np.arange(len(boxes), dtype=np.int64) |
|
|
| boxes = boxes.astype(np.float32, copy=False) |
| scores = scores.astype(np.float32, copy=False) |
| areas = _box_areas(boxes) |
|
|
| contained = np.zeros(len(boxes), dtype=bool) |
| for idx, area in enumerate(areas): |
| if area <= 0: |
| contained[idx] = True |
| continue |
| larger = np.where(areas > area)[0] |
| if len(larger) == 0: |
| continue |
| covered = _intersection_areas(boxes[idx], boxes[larger]) / area |
| if np.any(covered >= containment_threshold): |
| contained[idx] = True |
|
|
| candidates = np.where(~contained)[0] |
| if len(candidates) <= 1: |
| return candidates |
|
|
| order = candidates[np.argsort(scores[candidates])[::-1]] |
| keep = [] |
| while len(order) > 0: |
| current = order[0] |
| keep.append(current) |
| rest = order[1:] |
| if len(rest) == 0: |
| break |
|
|
| inter = _intersection_areas(boxes[current], boxes[rest]) |
| union = areas[current] + areas[rest] - inter |
| iou = np.divide(inter, union, out=np.zeros_like(inter), where=union > 0) |
| order = rest[iou <= iou_threshold] |
|
|
| return np.array(sorted(keep), dtype=np.int64) |
|
|
|
|
| def select_animal_boxes( |
| det_instances, |
| *, |
| animal_class_ids: Iterable[int] = ANIMAL_COCO_IDS, |
| score_threshold: float = 0.7, |
| iou_threshold: float = 0.5, |
| containment_threshold: float = 0.9, |
| ) -> tuple[np.ndarray, int]: |
| """Return filtered animal boxes and the number of duplicate boxes removed.""" |
| class_ids = set(int(class_id) for class_id in animal_class_ids) |
| classes = det_instances.pred_classes.detach().cpu().numpy() |
| scores = det_instances.scores.detach().cpu().numpy() |
|
|
| valid_idx = np.array( |
| [ |
| i |
| for i, (class_id, score) in enumerate(zip(classes, scores)) |
| if int(class_id) in class_ids and float(score) > float(score_threshold) |
| ], |
| dtype=np.int64, |
| ) |
| if len(valid_idx) == 0: |
| return np.zeros((0, 4), dtype=np.float32), 0 |
|
|
| boxes = det_instances.pred_boxes.tensor[valid_idx].detach().cpu().numpy() |
| scores = scores[valid_idx] |
| keep = _suppress_duplicate_boxes( |
| boxes, |
| scores, |
| iou_threshold=iou_threshold, |
| containment_threshold=containment_threshold, |
| ) |
| return boxes[keep], int(len(boxes) - len(keep)) |
|
|