| /* |
| * Copyright 2023 Google LLC |
| * |
| * 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. |
| */ |
| |
| // NMS (Non-Maximum Suppression) algorithm |
| |
| #include "ssd_postprocess/nms.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #ifndef MIN |
| #define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| #endif |
| #ifndef MAX |
| #define MAX(a, b) (((a) > (b)) ? (a) : (b)) |
| #endif |
| |
| // compute area of one box |
| static float compute_box_area(const BoxCornerEncode* box) { |
| const float width = box->xmax - box->xmin; |
| const float height = box->ymax - box->ymin; |
| return MAX(0.0, width * height); |
| } |
| |
| // compute IOU (intersection over union) of two boxes |
| static float compute_two_boxes_iou(const BoxCornerEncode* box1, |
| const BoxCornerEncode* box2) { |
| const float area1 = compute_box_area(box1); |
| const float area2 = compute_box_area(box2); |
| if (area1 <= 0 || area2 <= 0) return 0.0; |
| |
| BoxCornerEncode intersection_box = {.ymin = MAX(box1->ymin, box2->ymin), |
| .xmin = MAX(box1->xmin, box2->xmin), |
| .ymax = MIN(box1->ymax, box2->ymax), |
| .xmax = MIN(box1->xmax, box2->xmax)}; |
| float intersection_area = compute_box_area(&intersection_box); |
| return intersection_area / (area1 + area2 - intersection_area); |
| } |
| |
| // comparator for qsort |
| static int comparator(const void* p, const void* q) { |
| float x = ((BoxCornerEncode*)p)->score; |
| float y = ((BoxCornerEncode*)q)->score; |
| return (y > x) - (y < x); |
| } |
| |
| // Perform non-maximum suppression algorithm to remove "similar" bounding boxes |
| void nms(Boxes* boxes_in, Boxes* boxes_out, const int max_boxes, |
| const float iou_threshold) { |
| int num_boxes = boxes_in->num_boxes; |
| uint8_t* is_suppressed = (uint8_t*)malloc(num_boxes * sizeof(uint8_t)); |
| memset(is_suppressed, 0, num_boxes * sizeof(uint8_t)); |
| |
| // quick sort from greatest to smallest |
| qsort(boxes_in->box, num_boxes, sizeof(BoxCornerEncode), comparator); |
| |
| for (int i = 0; i < num_boxes; i++) { |
| if (!is_suppressed[i]) { |
| for (int j = i + 1; j < num_boxes; j++) { |
| if (!is_suppressed[j]) { |
| if (compute_two_boxes_iou(&(boxes_in->box[i]), &(boxes_in->box[j])) > |
| iou_threshold) { |
| is_suppressed[j] = 1; |
| } |
| } |
| } |
| } |
| } |
| |
| int ind_out = 0; |
| for (int i = 0; i < num_boxes; i++) { |
| if (ind_out >= max_boxes) break; |
| if (!is_suppressed[i]) { |
| boxes_out->box[ind_out++] = boxes_in->box[i]; |
| } |
| } |
| boxes_out->num_boxes = ind_out; |
| |
| free(is_suppressed); |
| } |