Compute Library
 21.02
CPPDetectionPostProcessLayer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019-2020 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
25 
26 #include "arm_compute/core/Error.h"
30 
31 #include <cstddef>
32 #include <ios>
33 #include <list>
34 
35 namespace arm_compute
36 {
37 namespace
38 {
39 Status validate_arguments(const ITensorInfo *input_box_encoding, const ITensorInfo *input_class_score, const ITensorInfo *input_anchors,
40  ITensorInfo *output_boxes, ITensorInfo *output_classes, ITensorInfo *output_scores, ITensorInfo *num_detection,
41  DetectionPostProcessLayerInfo info, const unsigned int kBatchSize, const unsigned int kNumCoordBox)
42 {
43  ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input_box_encoding, input_class_score, input_anchors);
45  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input_box_encoding, input_anchors);
46  ARM_COMPUTE_RETURN_ERROR_ON_MSG(input_box_encoding->num_dimensions() > 3, "The location input tensor shape should be [4, N, kBatchSize].");
47  if(input_box_encoding->num_dimensions() > 2)
48  {
49  ARM_COMPUTE_RETURN_ERROR_ON_MSG_VAR(input_box_encoding->dimension(2) != kBatchSize, "The third dimension of the input box_encoding tensor should be equal to %d.", kBatchSize);
50  }
51  ARM_COMPUTE_RETURN_ERROR_ON_MSG_VAR(input_box_encoding->dimension(0) != kNumCoordBox, "The first dimension of the input box_encoding tensor should be equal to %d.", kNumCoordBox);
52  ARM_COMPUTE_RETURN_ERROR_ON_MSG(input_class_score->dimension(0) != (info.num_classes() + 1),
53  "The first dimension of the input class_prediction should be equal to the number of classes plus one.");
54 
55  ARM_COMPUTE_RETURN_ERROR_ON_MSG(input_anchors->num_dimensions() > 3, "The anchors input tensor shape should be [4, N, kBatchSize].");
56  if(input_anchors->num_dimensions() > 2)
57  {
58  ARM_COMPUTE_RETURN_ERROR_ON_MSG_VAR(input_anchors->dimension(0) != kNumCoordBox, "The first dimension of the input anchors tensor should be equal to %d.", kNumCoordBox);
59  }
60  ARM_COMPUTE_RETURN_ERROR_ON_MSG((input_box_encoding->dimension(1) != input_class_score->dimension(1))
61  || (input_box_encoding->dimension(1) != input_anchors->dimension(1)),
62  "The second dimension of the inputs should be the same.");
63  ARM_COMPUTE_RETURN_ERROR_ON_MSG(num_detection->num_dimensions() > 1, "The num_detection output tensor shape should be [M].");
64  ARM_COMPUTE_RETURN_ERROR_ON_MSG((info.iou_threshold() <= 0.0f) || (info.iou_threshold() > 1.0f), "The intersection over union should be positive and less than 1.");
65  ARM_COMPUTE_RETURN_ERROR_ON_MSG(info.max_classes_per_detection() <= 0, "The number of max classes per detection should be positive.");
66 
67  const unsigned int num_detected_boxes = info.max_detections() * info.max_classes_per_detection();
68 
69  // Validate configured outputs
70  if(output_boxes->total_size() != 0)
71  {
72  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output_boxes->tensor_shape(), TensorShape(4U, num_detected_boxes, 1U));
74  }
75  if(output_classes->total_size() != 0)
76  {
77  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output_classes->tensor_shape(), TensorShape(num_detected_boxes, 1U));
79  }
80  if(output_scores->total_size() != 0)
81  {
82  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output_scores->tensor_shape(), TensorShape(num_detected_boxes, 1U));
84  }
85  if(num_detection->total_size() != 0)
86  {
87  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(num_detection->tensor_shape(), TensorShape(1U));
89  }
90 
91  return Status{};
92 }
93 
94 inline void DecodeBoxCorner(BBox &box_centersize, BBox &anchor, Iterator &decoded_it, DetectionPostProcessLayerInfo info)
95 {
96  const float half_factor = 0.5f;
97 
98  // BBox is equavalent to CenterSizeEncoding [y,x,h,w]
99  const float y_center = box_centersize[0] / info.scale_value_y() * anchor[2] + anchor[0];
100  const float x_center = box_centersize[1] / info.scale_value_x() * anchor[3] + anchor[1];
101  const float half_h = half_factor * static_cast<float>(std::exp(box_centersize[2] / info.scale_value_h())) * anchor[2];
102  const float half_w = half_factor * static_cast<float>(std::exp(box_centersize[3] / info.scale_value_w())) * anchor[3];
103 
104  // Box Corner encoding boxes are saved as [xmin, ymin, xmax, ymax]
105  auto decoded_ptr = reinterpret_cast<float *>(decoded_it.ptr());
106  *(decoded_ptr) = x_center - half_w; // xmin
107  *(1 + decoded_ptr) = y_center - half_h; // ymin
108  *(2 + decoded_ptr) = x_center + half_w; // xmax
109  *(3 + decoded_ptr) = y_center + half_h; // ymax
110 }
111 
112 /** Decode a bbox according to a anchors and scale info.
113  *
114  * @param[in] input_box_encoding The input prior bounding boxes.
115  * @param[in] input_anchors The corresponding input variance.
116  * @param[in] info The detection informations
117  * @param[out] decoded_boxes The decoded bboxes.
118  */
119 void DecodeCenterSizeBoxes(const ITensor *input_box_encoding, const ITensor *input_anchors, DetectionPostProcessLayerInfo info, Tensor *decoded_boxes)
120 {
121  const QuantizationInfo &qi_box = input_box_encoding->info()->quantization_info();
122  const QuantizationInfo &qi_anchors = input_anchors->info()->quantization_info();
123  BBox box_centersize{ {} };
124  BBox anchor{ {} };
125 
126  Window win;
127  win.use_tensor_dimensions(input_box_encoding->info()->tensor_shape());
128  win.set_dimension_step(0U, 4U);
129  win.set_dimension_step(1U, 1U);
130  Iterator box_it(input_box_encoding, win);
131  Iterator anchor_it(input_anchors, win);
132  Iterator decoded_it(decoded_boxes, win);
133 
134  if(input_box_encoding->info()->data_type() == DataType::QASYMM8)
135  {
136  execute_window_loop(win, [&](const Coordinates &)
137  {
138  const auto box_ptr = reinterpret_cast<const qasymm8_t *>(box_it.ptr());
139  const auto anchor_ptr = reinterpret_cast<const qasymm8_t *>(anchor_it.ptr());
140  box_centersize = BBox({ dequantize_qasymm8(*box_ptr, qi_box), dequantize_qasymm8(*(box_ptr + 1), qi_box),
141  dequantize_qasymm8(*(2 + box_ptr), qi_box), dequantize_qasymm8(*(3 + box_ptr), qi_box)
142  });
143  anchor = BBox({ dequantize_qasymm8(*anchor_ptr, qi_anchors), dequantize_qasymm8(*(anchor_ptr + 1), qi_anchors),
144  dequantize_qasymm8(*(2 + anchor_ptr), qi_anchors), dequantize_qasymm8(*(3 + anchor_ptr), qi_anchors)
145  });
146  DecodeBoxCorner(box_centersize, anchor, decoded_it, info);
147  },
148  box_it, anchor_it, decoded_it);
149  }
150  else if(input_box_encoding->info()->data_type() == DataType::QASYMM8_SIGNED)
151  {
152  execute_window_loop(win, [&](const Coordinates &)
153  {
154  const auto box_ptr = reinterpret_cast<const qasymm8_signed_t *>(box_it.ptr());
155  const auto anchor_ptr = reinterpret_cast<const qasymm8_signed_t *>(anchor_it.ptr());
156  box_centersize = BBox({ dequantize_qasymm8_signed(*box_ptr, qi_box), dequantize_qasymm8_signed(*(box_ptr + 1), qi_box),
157  dequantize_qasymm8_signed(*(2 + box_ptr), qi_box), dequantize_qasymm8_signed(*(3 + box_ptr), qi_box)
158  });
159  anchor = BBox({ dequantize_qasymm8_signed(*anchor_ptr, qi_anchors), dequantize_qasymm8_signed(*(anchor_ptr + 1), qi_anchors),
160  dequantize_qasymm8_signed(*(2 + anchor_ptr), qi_anchors), dequantize_qasymm8_signed(*(3 + anchor_ptr), qi_anchors)
161  });
162  DecodeBoxCorner(box_centersize, anchor, decoded_it, info);
163  },
164  box_it, anchor_it, decoded_it);
165  }
166  else
167  {
168  execute_window_loop(win, [&](const Coordinates &)
169  {
170  const auto box_ptr = reinterpret_cast<const float *>(box_it.ptr());
171  const auto anchor_ptr = reinterpret_cast<const float *>(anchor_it.ptr());
172  box_centersize = BBox({ *box_ptr, *(box_ptr + 1), *(2 + box_ptr), *(3 + box_ptr) });
173  anchor = BBox({ *anchor_ptr, *(anchor_ptr + 1), *(2 + anchor_ptr), *(3 + anchor_ptr) });
174  DecodeBoxCorner(box_centersize, anchor, decoded_it, info);
175  },
176  box_it, anchor_it, decoded_it);
177  }
178 }
179 
180 void SaveOutputs(const Tensor *decoded_boxes, const std::vector<int> &result_idx_boxes_after_nms, const std::vector<float> &result_scores_after_nms, const std::vector<int> &result_classes_after_nms,
181  std::vector<unsigned int> &sorted_indices, const unsigned int num_output, const unsigned int max_detections, ITensor *output_boxes, ITensor *output_classes, ITensor *output_scores,
182  ITensor *num_detection)
183 {
184  // xmin,ymin,xmax,ymax -> ymin,xmin,ymax,xmax
185  unsigned int i = 0;
186  for(; i < num_output; ++i)
187  {
188  const unsigned int box_in_idx = result_idx_boxes_after_nms[sorted_indices[i]];
189  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(0, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(1, box_in_idx))));
190  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(1, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(0, box_in_idx))));
191  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(2, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(3, box_in_idx))));
192  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(3, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(2, box_in_idx))));
193  *(reinterpret_cast<float *>(output_classes->ptr_to_element(Coordinates(i)))) = static_cast<float>(result_classes_after_nms[sorted_indices[i]]);
194  *(reinterpret_cast<float *>(output_scores->ptr_to_element(Coordinates(i)))) = result_scores_after_nms[sorted_indices[i]];
195  }
196  for(; i < max_detections; ++i)
197  {
198  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(1, i)))) = 0.0f;
199  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(0, i)))) = 0.0f;
200  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(3, i)))) = 0.0f;
201  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(2, i)))) = 0.0f;
202  *(reinterpret_cast<float *>(output_classes->ptr_to_element(Coordinates(i)))) = 0.0f;
203  *(reinterpret_cast<float *>(output_scores->ptr_to_element(Coordinates(i)))) = 0.0f;
204  }
205  *(reinterpret_cast<float *>(num_detection->ptr_to_element(Coordinates(0)))) = num_output;
206 }
207 } // namespace
208 
209 CPPDetectionPostProcessLayer::CPPDetectionPostProcessLayer(std::shared_ptr<IMemoryManager> memory_manager)
210  : _memory_group(std::move(memory_manager)), _nms(), _input_box_encoding(nullptr), _input_scores(nullptr), _input_anchors(nullptr), _output_boxes(nullptr), _output_classes(nullptr),
211  _output_scores(nullptr), _num_detection(nullptr), _info(), _num_boxes(), _num_classes_with_background(), _num_max_detected_boxes(), _dequantize_scores(false), _decoded_boxes(), _decoded_scores(),
212  _selected_indices(), _class_scores(), _input_scores_to_use(nullptr)
213 {
214 }
215 
216 void CPPDetectionPostProcessLayer::configure(const ITensor *input_box_encoding, const ITensor *input_scores, const ITensor *input_anchors,
217  ITensor *output_boxes, ITensor *output_classes, ITensor *output_scores, ITensor *num_detection, DetectionPostProcessLayerInfo info)
218 {
219  ARM_COMPUTE_ERROR_ON_NULLPTR(input_box_encoding, input_scores, input_anchors, output_boxes, output_classes, output_scores);
220  _num_max_detected_boxes = info.max_detections() * info.max_classes_per_detection();
221 
222  auto_init_if_empty(*output_boxes->info(), TensorInfo(TensorShape(_kNumCoordBox, _num_max_detected_boxes, _kBatchSize), 1, DataType::F32));
223  auto_init_if_empty(*output_classes->info(), TensorInfo(TensorShape(_num_max_detected_boxes, _kBatchSize), 1, DataType::F32));
224  auto_init_if_empty(*output_scores->info(), TensorInfo(TensorShape(_num_max_detected_boxes, _kBatchSize), 1, DataType::F32));
225  auto_init_if_empty(*num_detection->info(), TensorInfo(TensorShape(1U), 1, DataType::F32));
226 
227  // Perform validation step
228  ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input_box_encoding->info(), input_scores->info(), input_anchors->info(), output_boxes->info(), output_classes->info(), output_scores->info(),
229  num_detection->info(),
230  info, _kBatchSize, _kNumCoordBox));
231 
232  _input_box_encoding = input_box_encoding;
233  _input_scores = input_scores;
234  _input_anchors = input_anchors;
235  _output_boxes = output_boxes;
236  _output_classes = output_classes;
237  _output_scores = output_scores;
238  _num_detection = num_detection;
239  _info = info;
240  _num_boxes = input_box_encoding->info()->dimension(1);
241  _num_classes_with_background = _input_scores->info()->dimension(0);
242  _dequantize_scores = (info.dequantize_scores() && is_data_type_quantized(input_box_encoding->info()->data_type()));
243 
244  auto_init_if_empty(*_decoded_boxes.info(), TensorInfo(TensorShape(_kNumCoordBox, _input_box_encoding->info()->dimension(1), _kBatchSize), 1, DataType::F32));
245  auto_init_if_empty(*_decoded_scores.info(), TensorInfo(TensorShape(_input_scores->info()->dimension(0), _input_scores->info()->dimension(1), _kBatchSize), 1, DataType::F32));
247  const unsigned int num_classes_per_box = std::min(info.max_classes_per_detection(), info.num_classes());
248  auto_init_if_empty(*_class_scores.info(), TensorInfo(info.use_regular_nms() ? TensorShape(_num_boxes) : TensorShape(_num_boxes * num_classes_per_box), 1, DataType::F32));
249 
250  _input_scores_to_use = _dequantize_scores ? &_decoded_scores : _input_scores;
251 
252  // Manage intermediate buffers
253  _memory_group.manage(&_decoded_boxes);
254  _memory_group.manage(&_decoded_scores);
255  _memory_group.manage(&_selected_indices);
256  _memory_group.manage(&_class_scores);
257  _nms.configure(&_decoded_boxes, &_class_scores, &_selected_indices, info.use_regular_nms() ? info.detection_per_class() : info.max_detections(), info.nms_score_threshold(), info.iou_threshold());
258 
259  // Allocate and reserve intermediate tensors and vectors
260  _decoded_boxes.allocator()->allocate();
261  _decoded_scores.allocator()->allocate();
262  _selected_indices.allocator()->allocate();
263  _class_scores.allocator()->allocate();
264 }
265 
266 Status CPPDetectionPostProcessLayer::validate(const ITensorInfo *input_box_encoding, const ITensorInfo *input_class_score, const ITensorInfo *input_anchors,
267  ITensorInfo *output_boxes, ITensorInfo *output_classes, ITensorInfo *output_scores, ITensorInfo *num_detection, DetectionPostProcessLayerInfo info)
268 {
269  constexpr unsigned int kBatchSize = 1;
270  constexpr unsigned int kNumCoordBox = 4;
271  const TensorInfo _decoded_boxes_info = TensorInfo(TensorShape(kNumCoordBox, input_box_encoding->dimension(1)), 1, DataType::F32);
272  const TensorInfo _decoded_scores_info = TensorInfo(TensorShape(input_box_encoding->dimension(1)), 1, DataType::F32);
273  const TensorInfo _selected_indices_info = TensorInfo(TensorShape(info.max_detections()), 1, DataType::S32);
274 
275  ARM_COMPUTE_RETURN_ON_ERROR(CPPNonMaximumSuppression::validate(&_decoded_boxes_info, &_decoded_scores_info, &_selected_indices_info, info.max_detections(), info.nms_score_threshold(),
276  info.iou_threshold()));
277  ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input_box_encoding, input_class_score, input_anchors, output_boxes, output_classes, output_scores, num_detection, info, kBatchSize, kNumCoordBox));
278 
279  return Status{};
280 }
281 
283 {
284  const unsigned int num_classes = _info.num_classes();
285  const unsigned int max_detections = _info.max_detections();
286 
287  DecodeCenterSizeBoxes(_input_box_encoding, _input_anchors, _info, &_decoded_boxes);
288 
289  // Decode scores if necessary
290  if(_dequantize_scores)
291  {
292  if(_input_box_encoding->info()->data_type() == DataType::QASYMM8)
293  {
294  for(unsigned int idx_c = 0; idx_c < _num_classes_with_background; ++idx_c)
295  {
296  for(unsigned int idx_b = 0; idx_b < _num_boxes; ++idx_b)
297  {
298  *(reinterpret_cast<float *>(_decoded_scores.ptr_to_element(Coordinates(idx_c, idx_b)))) =
299  dequantize_qasymm8(*(reinterpret_cast<qasymm8_t *>(_input_scores->ptr_to_element(Coordinates(idx_c, idx_b)))), _input_scores->info()->quantization_info());
300  }
301  }
302  }
303  else if(_input_box_encoding->info()->data_type() == DataType::QASYMM8_SIGNED)
304  {
305  for(unsigned int idx_c = 0; idx_c < _num_classes_with_background; ++idx_c)
306  {
307  for(unsigned int idx_b = 0; idx_b < _num_boxes; ++idx_b)
308  {
309  *(reinterpret_cast<float *>(_decoded_scores.ptr_to_element(Coordinates(idx_c, idx_b)))) =
310  dequantize_qasymm8_signed(*(reinterpret_cast<qasymm8_signed_t *>(_input_scores->ptr_to_element(Coordinates(idx_c, idx_b)))), _input_scores->info()->quantization_info());
311  }
312  }
313  }
314  }
315 
316  // Regular NMS
317  if(_info.use_regular_nms())
318  {
319  std::vector<int> result_idx_boxes_after_nms;
320  std::vector<int> result_classes_after_nms;
321  std::vector<float> result_scores_after_nms;
322  std::vector<unsigned int> sorted_indices;
323 
324  for(unsigned int c = 0; c < num_classes; ++c)
325  {
326  // For each boxes get scores of the boxes for the class c
327  for(unsigned int i = 0; i < _num_boxes; ++i)
328  {
329  *(reinterpret_cast<float *>(_class_scores.ptr_to_element(Coordinates(i)))) =
330  *(reinterpret_cast<float *>(_input_scores_to_use->ptr_to_element(Coordinates(c + 1, i)))); // i * _num_classes_with_background + c + 1
331  }
332 
333  // Run Non-maxima Suppression
334  _nms.run();
335 
336  for(unsigned int i = 0; i < _info.detection_per_class(); ++i)
337  {
338  const auto selected_index = *(reinterpret_cast<int *>(_selected_indices.ptr_to_element(Coordinates(i))));
339  if(selected_index == -1)
340  {
341  // Nms will return -1 for all the last M-elements not valid
342  break;
343  }
344  result_idx_boxes_after_nms.emplace_back(selected_index);
345  result_scores_after_nms.emplace_back((reinterpret_cast<float *>(_class_scores.buffer()))[selected_index]);
346  result_classes_after_nms.emplace_back(c);
347  }
348  }
349 
350  // We select the max detection numbers of the highest score of all classes
351  const auto num_selected = result_scores_after_nms.size();
352  const auto num_output = std::min<unsigned int>(max_detections, num_selected);
353 
354  // Sort selected indices based on result scores
355  sorted_indices.resize(num_selected);
356  std::iota(sorted_indices.begin(), sorted_indices.end(), 0);
357  std::partial_sort(sorted_indices.data(),
358  sorted_indices.data() + num_output,
359  sorted_indices.data() + num_selected,
360  [&](unsigned int first, unsigned int second)
361  {
362 
363  return result_scores_after_nms[first] > result_scores_after_nms[second];
364  });
365 
366  SaveOutputs(&_decoded_boxes, result_idx_boxes_after_nms, result_scores_after_nms, result_classes_after_nms, sorted_indices,
367  num_output, max_detections, _output_boxes, _output_classes, _output_scores, _num_detection);
368  }
369  // Fast NMS
370  else
371  {
372  const unsigned int num_classes_per_box = std::min<unsigned int>(_info.max_classes_per_detection(), _info.num_classes());
373  std::vector<float> max_scores;
374  std::vector<int> box_indices;
375  std::vector<int> max_score_classes;
376 
377  for(unsigned int b = 0; b < _num_boxes; ++b)
378  {
379  std::vector<float> box_scores;
380  for(unsigned int c = 0; c < num_classes; ++c)
381  {
382  box_scores.emplace_back(*(reinterpret_cast<float *>(_input_scores_to_use->ptr_to_element(Coordinates(c + 1, b)))));
383  }
384 
385  std::vector<unsigned int> max_score_indices;
386  max_score_indices.resize(_info.num_classes());
387  std::iota(max_score_indices.data(), max_score_indices.data() + _info.num_classes(), 0);
388  std::partial_sort(max_score_indices.data(),
389  max_score_indices.data() + num_classes_per_box,
390  max_score_indices.data() + num_classes,
391  [&](unsigned int first, unsigned int second)
392  {
393  return box_scores[first] > box_scores[second];
394  });
395 
396  for(unsigned int i = 0; i < num_classes_per_box; ++i)
397  {
398  const float score_to_add = box_scores[max_score_indices[i]];
399  *(reinterpret_cast<float *>(_class_scores.ptr_to_element(Coordinates(b * num_classes_per_box + i)))) = score_to_add;
400  max_scores.emplace_back(score_to_add);
401  box_indices.emplace_back(b);
402  max_score_classes.emplace_back(max_score_indices[i]);
403  }
404  }
405 
406  // Run Non-maxima Suppression
407  _nms.run();
408  std::vector<unsigned int> selected_indices;
409  for(unsigned int i = 0; i < max_detections; ++i)
410  {
411  // NMS returns M valid indices, the not valid tail is filled with -1
412  if(*(reinterpret_cast<int *>(_selected_indices.ptr_to_element(Coordinates(i)))) == -1)
413  {
414  // Nms will return -1 for all the last M-elements not valid
415  break;
416  }
417  selected_indices.emplace_back(*(reinterpret_cast<int *>(_selected_indices.ptr_to_element(Coordinates(i)))));
418  }
419  // We select the max detection numbers of the highest score of all classes
420  const auto num_output = std::min<unsigned int>(_info.max_detections(), selected_indices.size());
421 
422  SaveOutputs(&_decoded_boxes, box_indices, max_scores, max_score_classes, selected_indices,
423  num_output, max_detections, _output_boxes, _output_classes, _output_scores, _num_detection);
424  }
425 }
426 } // namespace arm_compute
bool is_data_type_quantized(DataType dt)
Check if a given data type is of quantized type.
Definition: Utils.h:1168
uint8_t * ptr_to_element(const Coordinates &id) const
Return a pointer to the element at the passed coordinates.
Definition: ITensor.h:63
Shape of a tensor.
Definition: TensorShape.h:39
float dequantize_qasymm8(uint8_t value, const INFO_TYPE &qinfo)
Dequantize a value given an unsigned 8-bit asymmetric quantization scheme.
virtual size_t dimension(size_t index) const =0
Return the size of the requested dimension.
SimpleTensor< float > b
Definition: DFT.cpp:157
unsigned int max_detections() const
Get max detections.
Definition: Types.h:1137
#define ARM_COMPUTE_RETURN_ERROR_ON_MSG_VAR(cond, msg,...)
If the condition is true, an error is returned.
Definition: Error.h:227
#define ARM_COMPUTE_RETURN_ON_ERROR(status)
Checks if a status contains an error and returns it.
Definition: Error.h:204
virtual DataType data_type() const =0
Data type used for each element of the tensor.
static Status validate(const ITensorInfo *input_box_encoding, const ITensorInfo *input_class_score, const ITensorInfo *input_anchors, ITensorInfo *output_boxes, ITensorInfo *output_classes, ITensorInfo *output_scores, ITensorInfo *num_detection, DetectionPostProcessLayerInfo info=DetectionPostProcessLayerInfo())
Static function to check if given info will lead to a valid configuration of CPPDetectionPostProcessL...
1 channel, 1 F32 per channel
CPPDetectionPostProcessLayer(std::shared_ptr< IMemoryManager > memory_manager=nullptr)
Constructor.
Store the tensor&#39;s metadata.
Definition: ITensorInfo.h:40
#define ARM_COMPUTE_ERROR_THROW_ON(status)
Definition: Error.h:455
Status class.
Definition: Error.h:52
Interface for Neon tensor.
Definition: ITensor.h:36
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(...)
Definition: Validate.h:288
bool use_regular_nms() const
Get if use regular nms.
Definition: Types.h:1167
Copyright (c) 2017-2021 Arm Limited.
float iou_threshold() const
Get intersection over union threshold.
Definition: Types.h:1157
TensorAllocator * allocator()
Return a pointer to the tensor&#39;s allocator.
Definition: Tensor.cpp:48
ITensorInfo * info() const override
Interface to be implemented by the child class to return the tensor&#39;s metadata.
Definition: Tensor.cpp:33
#define ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(...)
Definition: Validate.h:163
1 channel, 1 S32 per channel
void manage(IMemoryManageable *obj) override
Sets a object to be managed by the given memory group.
Definition: MemoryGroup.h:79
void run() override
Run the kernels contained in the function.
quantized, asymmetric fixed-point 8-bit number unsigned
Coordinates of an item.
Definition: Coordinates.h:37
void allocate() override
Allocate size specified by TensorInfo of CPU memory.
float nms_score_threshold() const
Get nms threshold.
Definition: Types.h:1152
bool auto_init_if_empty(ITensorInfo &info, const TensorShape &shape, int num_channels, DataType data_type, QuantizationInfo quantization_info=QuantizationInfo())
Auto initialize the tensor info (shape, number of channels and data type) if the current assignment i...
std::array< float, 4 > BBox
Definition: Types.h:962
bool dequantize_scores() const
Get dequantize_scores value.
Definition: Types.h:1196
virtual ITensorInfo * info() const =0
Interface to be implemented by the child class to return the tensor&#39;s metadata.
virtual QuantizationInfo quantization_info() const =0
Get the quantization settings (scale and offset) of the tensor.
unsigned int max_classes_per_detection() const
Get max_classes per detection.
Definition: Types.h:1142
static Status validate(const ITensorInfo *bboxes, const ITensorInfo *scores, const ITensorInfo *indices, unsigned int max_output_size, const float score_threshold, const float nms_threshold)
Static function to check if given arguments will lead to a valid configuration of CPPNonMaximumSuppre...
ScaleKernelInfo info(interpolation_policy, default_border_mode, PixelValue(), sampling_policy, false)
void configure(const ITensor *input_box_encoding, const ITensor *input_score, const ITensor *input_anchors, ITensor *output_boxes, ITensor *output_classes, ITensor *output_scores, ITensor *num_detection, DetectionPostProcessLayerInfo info=DetectionPostProcessLayerInfo())
Configure the detection output layer CPP function.
uint8_t qasymm8_t
8 bit quantized asymmetric scalar value
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(...)
Definition: Validate.h:545
#define ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(t, c,...)
Definition: Validate.h:792
unsigned int detection_per_class() const
Get detection per class.
Definition: Types.h:1147
Status validate_arguments(const ITensorInfo *input, const ITensorInfo *bias, const ITensorInfo *output, const GEMMLowpOutputStageInfo *output_stage)
int8_t qasymm8_signed_t
8 bit signed quantized asymmetric scalar value
Detection Output layer info.
Definition: Types.h:1095
unsigned int num_classes() const
Get num classes.
Definition: Types.h:1162
uint8_t * buffer() const override
Interface to be implemented by the child class to return a pointer to CPU memory. ...
Definition: Tensor.cpp:43
float dequantize_qasymm8_signed(int8_t value, const INFO_TYPE &qinfo)
Dequantize a value given a signed 8-bit asymmetric quantization scheme.
#define ARM_COMPUTE_RETURN_ERROR_ON_MSG(cond, msg)
If the condition is true, an error is returned.
Definition: Error.h:244
void configure(const ITensor *bboxes, const ITensor *scores, ITensor *indices, unsigned int max_output_size, const float score_threshold, const float nms_threshold)
Configure the function to perform non maximal suppression.
#define ARM_COMPUTE_ERROR_ON_NULLPTR(...)
Definition: Validate.h:161
Store the tensor&#39;s metadata.
Definition: TensorInfo.h:45
void execute_window_loop(const Window &w, L &&lambda_function, Ts &&... iterators)
Iterate through the passed window, automatically adjusting the iterators and calling the lambda_funct...
Definition: Helpers.inl:77
void run() override final
Run the kernels contained in the function.
quantized, asymmetric fixed-point 8-bit number signed