|  | /* | 
|  | * Copyright 2022 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); | 
|  | } |