tensorflow/lite/kernels/detection_postprocess.cc
// NonMaxSuppressionSingleClass() prunes out the box locations with high overlap
// before selecting the highest scoring boxes (max_detections in number)
// It assumes all boxes are good in beginning and sorts based on the scores.
// If lower-scoring box has too much overlap with a higher-scoring box,
// we get rid of the lower-scoring box.
// Complexity is O(N^2) pairwise comparison between boxes
NonMaxSuppressionSingleClass() prunes out the box locations with high overlap
before selecting the highest scoring boxes (max_detections in number)
It assumes all boxes are good in beginning and sorts based on the scores.
If lower-scoring box has too much overlap with a higher-scoring box,
we get rid of the lower-scoring box.
Complexity is O(N^2) pairwise comparison between boxes
NonMaxSuppressionSingleClass () prunes out the box locations with high overlap before selecting the highest scoring boxes ( max_detections in number ) It assumes all boxes are good in beginning and sorts based on the scores . If lower - scoring box has too much overlap with a higher - scoring box we get rid of the lower - scoring box . Complexity is O ( N^2 ) pairwise comparison between boxes
TfLiteStatus NonMaxSuppressionSingleClassHelper(
    TfLiteContext* context, TfLiteNode* node, OpData* op_data,
    const std::vector<float>& scores, int max_detections,
    std::vector<int>* selected) {
  const TfLiteTensor* input_box_encodings;
  TF_LITE_ENSURE_OK(context,
                    GetInputSafe(context, node, kInputTensorBoxEncodings,
                                 &input_box_encodings));
  const TfLiteTensor* decoded_boxes =
      &context->tensors[op_data->decoded_boxes_index];
  const int num_boxes = input_box_encodings->dims->data[1];
  const float non_max_suppression_score_threshold =
      op_data->non_max_suppression_score_threshold;
  const float intersection_over_union_threshold =
      op_data->intersection_over_union_threshold;
  TF_LITE_ENSURE(context, (max_detections >= 0));
  TF_LITE_ENSURE(context, (intersection_over_union_threshold > 0.0f) &&
                              (intersection_over_union_threshold <= 1.0f));
  TF_LITE_ENSURE_EQ(context, decoded_boxes->type, kTfLiteFloat32);
  TF_LITE_ENSURE(context, ValidateBoxes(decoded_boxes, num_boxes));
  std::vector<int> keep_indices;
  std::vector<float> keep_scores;
  SelectDetectionsAboveScoreThreshold(
      scores, non_max_suppression_score_threshold, &keep_scores, &keep_indices);
  int num_scores_kept = keep_scores.size();
  std::vector<int> sorted_indices;
  sorted_indices.resize(num_scores_kept);
  DecreasingArgSort(keep_scores.data(), num_scores_kept, sorted_indices.data());
  const int num_boxes_kept = num_scores_kept;
  const int output_size = std::min(num_boxes_kept, max_detections);
  selected->clear();
  int num_active_candidate = num_boxes_kept;
  std::vector<uint8_t> active_box_candidate(num_boxes_kept, 1);
  for (int i = 0; i < num_boxes_kept; ++i) {
    if (num_active_candidate == 0 || selected->size() >= output_size) break;
    if (active_box_candidate[i] == 1) {
      selected->push_back(keep_indices[sorted_indices[i]]);
      active_box_candidate[i] = 0;
      num_active_candidate--;
    } else {
      continue;
    }
    for (int j = i + 1; j < num_boxes_kept; ++j) {
      if (active_box_candidate[j] == 1) {
        TF_LITE_ENSURE_EQ(context, decoded_boxes->type, kTfLiteFloat32);
        float intersection_over_union = ComputeIntersectionOverUnion(
            decoded_boxes, keep_indices[sorted_indices[i]],
            keep_indices[sorted_indices[j]]);
        if (intersection_over_union > intersection_over_union_threshold) {
          active_box_candidate[j] = 0;
          num_active_candidate--;
        }
      }
    }
  }
  return kTfLiteOk;
}
TfLiteStatus NonMaxSuppressionSingleClassHelper ( TfLiteContext * context , TfLiteNode * node , OpData * op_data , const std :: vector < float > & scores , int max_detections , std :: vector < int > * selected ) { const TfLiteTensor * input_box_encodings ; TF_LITE_ENSURE_OK ( context , GetInputSafe ( context , node , kInputTensorBoxEncodings , & input_box_encodings ) ) ; const TfLiteTensor * decoded_boxes = & context -> tensors [ op_data -> decoded_boxes_index ] ; const int num_boxes = input_box_encodings -> dims -> data [ 1 ] ; const float non_max_suppression_score_threshold = op_data -> non_max_suppression_score_threshold ; const float intersection_over_union_threshold = op_data -> intersection_over_union_threshold ; TF_LITE_ENSURE ( context , ( max_detections >= 0 ) ) ; TF_LITE_ENSURE ( context , ( intersection_over_union_threshold > 0.0f ) && ( intersection_over_union_threshold <= 1.0f ) ) ; TF_LITE_ENSURE_EQ ( context , decoded_boxes -> type , kTfLiteFloat32 ) ; TF_LITE_ENSURE ( context , ValidateBoxes ( decoded_boxes , num_boxes ) ) ; std :: vector < int > keep_indices ; std :: vector < float > keep_scores ; SelectDetectionsAboveScoreThreshold ( scores , non_max_suppression_score_threshold , & keep_scores , & keep_indices ) ; int num_scores_kept = keep_scores . size ( ) ; std :: vector < int > sorted_indices ; sorted_indices . resize ( num_scores_kept ) ; DecreasingArgSort ( keep_scores . data ( ) , num_scores_kept , sorted_indices . data ( ) ) ; const int num_boxes_kept = num_scores_kept ; const int output_size = std :: min ( num_boxes_kept , max_detections ) ; selected -> clear ( ) ; int num_active_candidate = num_boxes_kept ; std :: vector < uint8_t > active_box_candidate ( num_boxes_kept , 1 ) ; for ( int i = 0 ; i < num_boxes_kept ; ++ i ) { if ( num_active_candidate == 0 || selected -> size ( ) >= output_size ) break ; if ( active_box_candidate [ i ] == 1 ) { selected -> push_back ( keep_indices [ sorted_indices [ i ] ] ) ; active_box_candidate [ i ] = 0 ; num_active_candidate -- ; } else { continue ; } for ( int j = i + 1 ; j < num_boxes_kept ; ++ j ) { if ( active_box_candidate [ j ] == 1 ) { TF_LITE_ENSURE_EQ ( context , decoded_boxes -> type , kTfLiteFloat32 ) ; float intersection_over_union = ComputeIntersectionOverUnion ( decoded_boxes , keep_indices [ sorted_indices [ i ] ] , keep_indices [ sorted_indices [ j ] ] ) ; if ( intersection_over_union > intersection_over_union_threshold ) { active_box_candidate [ j ] = 0 ; num_active_candidate -- ; } } } } return kTfLiteOk ; }
NonMaxSuppressionSingleClass() prunes out the box locations with high overlap
before selecting the highest scoring boxes (max_detections in number)
It assumes all boxes are good in beginning and sorts based on the scores.
NonMaxSuppressionSingleClass () prunes out the box locations with high overlap before selecting the highest scoring boxes ( max_detections in number ) It assumes all boxes are good in beginning and sorts based on the scores .
// Maximum detections should be positive. // intersection_over_union_threshold should be positive // and should be less than 1. // Validate boxes // threshold scores // TODO(b/177068807): Remove the dynamic allocation and replace it // with temporaries, esp for std::vector<float>