Compute Library
 20.08
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"
29 
30 #include <cstddef>
31 #include <ios>
32 #include <list>
33 
34 namespace arm_compute
35 {
36 namespace
37 {
38 Status validate_arguments(const ITensorInfo *input_box_encoding, const ITensorInfo *input_class_score, const ITensorInfo *input_anchors,
39  ITensorInfo *output_boxes, ITensorInfo *output_classes, ITensorInfo *output_scores, ITensorInfo *num_detection,
40  DetectionPostProcessLayerInfo info, const unsigned int kBatchSize, const unsigned int kNumCoordBox)
41 {
42  ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input_box_encoding, input_class_score, input_anchors);
44  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input_box_encoding, input_anchors);
45  ARM_COMPUTE_RETURN_ERROR_ON_MSG(input_box_encoding->num_dimensions() > 3, "The location input tensor shape should be [4, N, kBatchSize].");
46  if(input_box_encoding->num_dimensions() > 2)
47  {
48  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);
49  }
50  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);
51  ARM_COMPUTE_RETURN_ERROR_ON_MSG(input_class_score->dimension(0) != (info.num_classes() + 1),
52  "The first dimension of the input class_prediction should be equal to the number of classes plus one.");
53 
54  ARM_COMPUTE_RETURN_ERROR_ON_MSG(input_anchors->num_dimensions() > 3, "The anchors input tensor shape should be [4, N, kBatchSize].");
55  if(input_anchors->num_dimensions() > 2)
56  {
57  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);
58  }
59  ARM_COMPUTE_RETURN_ERROR_ON_MSG((input_box_encoding->dimension(1) != input_class_score->dimension(1))
60  || (input_box_encoding->dimension(1) != input_anchors->dimension(1)),
61  "The second dimension of the inputs should be the same.");
62  ARM_COMPUTE_RETURN_ERROR_ON_MSG(num_detection->num_dimensions() > 1, "The num_detection output tensor shape should be [M].");
63  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.");
64  ARM_COMPUTE_RETURN_ERROR_ON_MSG(info.max_classes_per_detection() <= 0, "The number of max classes per detection should be positive.");
65 
66  const unsigned int num_detected_boxes = info.max_detections() * info.max_classes_per_detection();
67 
68  // Validate configured outputs
69  if(output_boxes->total_size() != 0)
70  {
71  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output_boxes->tensor_shape(), TensorShape(4U, num_detected_boxes, 1U));
73  }
74  if(output_classes->total_size() != 0)
75  {
76  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output_classes->tensor_shape(), TensorShape(num_detected_boxes, 1U));
78  }
79  if(output_scores->total_size() != 0)
80  {
81  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output_scores->tensor_shape(), TensorShape(num_detected_boxes, 1U));
83  }
84  if(num_detection->total_size() != 0)
85  {
86  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(num_detection->tensor_shape(), TensorShape(1U));
88  }
89 
90  return Status{};
91 }
92 
93 inline void DecodeBoxCorner(BBox &box_centersize, BBox &anchor, Iterator &decoded_it, DetectionPostProcessLayerInfo info)
94 {
95  const float half_factor = 0.5f;
96 
97  // BBox is equavalent to CenterSizeEncoding [y,x,h,w]
98  const float y_center = box_centersize[0] / info.scale_value_y() * anchor[2] + anchor[0];
99  const float x_center = box_centersize[1] / info.scale_value_x() * anchor[3] + anchor[1];
100  const float half_h = half_factor * static_cast<float>(std::exp(box_centersize[2] / info.scale_value_h())) * anchor[2];
101  const float half_w = half_factor * static_cast<float>(std::exp(box_centersize[3] / info.scale_value_w())) * anchor[3];
102 
103  // Box Corner encoding boxes are saved as [xmin, ymin, xmax, ymax]
104  auto decoded_ptr = reinterpret_cast<float *>(decoded_it.ptr());
105  *(decoded_ptr) = x_center - half_w; // xmin
106  *(1 + decoded_ptr) = y_center - half_h; // ymin
107  *(2 + decoded_ptr) = x_center + half_w; // xmax
108  *(3 + decoded_ptr) = y_center + half_h; // ymax
109 }
110 
111 /** Decode a bbox according to a anchors and scale info.
112  *
113  * @param[in] input_box_encoding The input prior bounding boxes.
114  * @param[in] input_anchors The corresponding input variance.
115  * @param[in] info The detection informations
116  * @param[out] decoded_boxes The decoded bboxes.
117  */
118 void DecodeCenterSizeBoxes(const ITensor *input_box_encoding, const ITensor *input_anchors, DetectionPostProcessLayerInfo info, Tensor *decoded_boxes)
119 {
120  const QuantizationInfo &qi_box = input_box_encoding->info()->quantization_info();
121  const QuantizationInfo &qi_anchors = input_anchors->info()->quantization_info();
122  BBox box_centersize{ {} };
123  BBox anchor{ {} };
124 
125  Window win;
126  win.use_tensor_dimensions(input_box_encoding->info()->tensor_shape());
127  win.set_dimension_step(0U, 4U);
128  win.set_dimension_step(1U, 1U);
129  Iterator box_it(input_box_encoding, win);
130  Iterator anchor_it(input_anchors, win);
131  Iterator decoded_it(decoded_boxes, win);
132 
133  if(input_box_encoding->info()->data_type() == DataType::QASYMM8)
134  {
135  execute_window_loop(win, [&](const Coordinates &)
136  {
137  const auto box_ptr = reinterpret_cast<const qasymm8_t *>(box_it.ptr());
138  const auto anchor_ptr = reinterpret_cast<const qasymm8_t *>(anchor_it.ptr());
139  box_centersize = BBox({ dequantize_qasymm8(*box_ptr, qi_box), dequantize_qasymm8(*(box_ptr + 1), qi_box),
140  dequantize_qasymm8(*(2 + box_ptr), qi_box), dequantize_qasymm8(*(3 + box_ptr), qi_box)
141  });
142  anchor = BBox({ dequantize_qasymm8(*anchor_ptr, qi_anchors), dequantize_qasymm8(*(anchor_ptr + 1), qi_anchors),
143  dequantize_qasymm8(*(2 + anchor_ptr), qi_anchors), dequantize_qasymm8(*(3 + anchor_ptr), qi_anchors)
144  });
145  DecodeBoxCorner(box_centersize, anchor, decoded_it, info);
146  },
147  box_it, anchor_it, decoded_it);
148  }
149  else if(input_box_encoding->info()->data_type() == DataType::QASYMM8_SIGNED)
150  {
151  execute_window_loop(win, [&](const Coordinates &)
152  {
153  const auto box_ptr = reinterpret_cast<const qasymm8_signed_t *>(box_it.ptr());
154  const auto anchor_ptr = reinterpret_cast<const qasymm8_signed_t *>(anchor_it.ptr());
155  box_centersize = BBox({ dequantize_qasymm8_signed(*box_ptr, qi_box), dequantize_qasymm8_signed(*(box_ptr + 1), qi_box),
156  dequantize_qasymm8_signed(*(2 + box_ptr), qi_box), dequantize_qasymm8_signed(*(3 + box_ptr), qi_box)
157  });
158  anchor = BBox({ dequantize_qasymm8_signed(*anchor_ptr, qi_anchors), dequantize_qasymm8_signed(*(anchor_ptr + 1), qi_anchors),
159  dequantize_qasymm8_signed(*(2 + anchor_ptr), qi_anchors), dequantize_qasymm8_signed(*(3 + anchor_ptr), qi_anchors)
160  });
161  DecodeBoxCorner(box_centersize, anchor, decoded_it, info);
162  },
163  box_it, anchor_it, decoded_it);
164  }
165  else
166  {
167  execute_window_loop(win, [&](const Coordinates &)
168  {
169  const auto box_ptr = reinterpret_cast<const float *>(box_it.ptr());
170  const auto anchor_ptr = reinterpret_cast<const float *>(anchor_it.ptr());
171  box_centersize = BBox({ *box_ptr, *(box_ptr + 1), *(2 + box_ptr), *(3 + box_ptr) });
172  anchor = BBox({ *anchor_ptr, *(anchor_ptr + 1), *(2 + anchor_ptr), *(3 + anchor_ptr) });
173  DecodeBoxCorner(box_centersize, anchor, decoded_it, info);
174  },
175  box_it, anchor_it, decoded_it);
176  }
177 }
178 
179 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,
180  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,
181  ITensor *num_detection)
182 {
183  // xmin,ymin,xmax,ymax -> ymin,xmin,ymax,xmax
184  unsigned int i = 0;
185  for(; i < num_output; ++i)
186  {
187  const unsigned int box_in_idx = result_idx_boxes_after_nms[sorted_indices[i]];
188  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(0, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(1, box_in_idx))));
189  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(1, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(0, box_in_idx))));
190  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(2, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(3, box_in_idx))));
191  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(3, i)))) = *(reinterpret_cast<float *>(decoded_boxes->ptr_to_element(Coordinates(2, box_in_idx))));
192  *(reinterpret_cast<float *>(output_classes->ptr_to_element(Coordinates(i)))) = static_cast<float>(result_classes_after_nms[sorted_indices[i]]);
193  *(reinterpret_cast<float *>(output_scores->ptr_to_element(Coordinates(i)))) = result_scores_after_nms[sorted_indices[i]];
194  }
195  for(; i < max_detections; ++i)
196  {
197  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(1, i)))) = 0.0f;
198  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(0, i)))) = 0.0f;
199  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(3, i)))) = 0.0f;
200  *(reinterpret_cast<float *>(output_boxes->ptr_to_element(Coordinates(2, i)))) = 0.0f;
201  *(reinterpret_cast<float *>(output_classes->ptr_to_element(Coordinates(i)))) = 0.0f;
202  *(reinterpret_cast<float *>(output_scores->ptr_to_element(Coordinates(i)))) = 0.0f;
203  }
204  *(reinterpret_cast<float *>(num_detection->ptr_to_element(Coordinates(0)))) = num_output;
205 }
206 } // namespace
207 
208 CPPDetectionPostProcessLayer::CPPDetectionPostProcessLayer(std::shared_ptr<IMemoryManager> memory_manager)
209  : _memory_group(std::move(memory_manager)), _nms(), _input_box_encoding(nullptr), _input_scores(nullptr), _input_anchors(nullptr), _output_boxes(nullptr), _output_classes(nullptr),
210  _output_scores(nullptr), _num_detection(nullptr), _info(), _num_boxes(), _num_classes_with_background(), _num_max_detected_boxes(), _dequantize_scores(false), _decoded_boxes(), _decoded_scores(),
211  _selected_indices(), _class_scores(), _input_scores_to_use(nullptr)
212 {
213 }
214 
215 void CPPDetectionPostProcessLayer::configure(const ITensor *input_box_encoding, const ITensor *input_scores, const ITensor *input_anchors,
216  ITensor *output_boxes, ITensor *output_classes, ITensor *output_scores, ITensor *num_detection, DetectionPostProcessLayerInfo info)
217 {
218  ARM_COMPUTE_ERROR_ON_NULLPTR(input_box_encoding, input_scores, input_anchors, output_boxes, output_classes, output_scores);
219  _num_max_detected_boxes = info.max_detections() * info.max_classes_per_detection();
220 
221  auto_init_if_empty(*output_boxes->info(), TensorInfo(TensorShape(_kNumCoordBox, _num_max_detected_boxes, _kBatchSize), 1, DataType::F32));
222  auto_init_if_empty(*output_classes->info(), TensorInfo(TensorShape(_num_max_detected_boxes, _kBatchSize), 1, DataType::F32));
223  auto_init_if_empty(*output_scores->info(), TensorInfo(TensorShape(_num_max_detected_boxes, _kBatchSize), 1, DataType::F32));
224  auto_init_if_empty(*num_detection->info(), TensorInfo(TensorShape(1U), 1, DataType::F32));
225 
226  // Perform validation step
227  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(),
228  num_detection->info(),
229  info, _kBatchSize, _kNumCoordBox));
230 
231  _input_box_encoding = input_box_encoding;
232  _input_scores = input_scores;
233  _input_anchors = input_anchors;
234  _output_boxes = output_boxes;
235  _output_classes = output_classes;
236  _output_scores = output_scores;
237  _num_detection = num_detection;
238  _info = info;
239  _num_boxes = input_box_encoding->info()->dimension(1);
240  _num_classes_with_background = _input_scores->info()->dimension(0);
241  _dequantize_scores = (info.dequantize_scores() && is_data_type_quantized(input_box_encoding->info()->data_type()));
242 
243  auto_init_if_empty(*_decoded_boxes.info(), TensorInfo(TensorShape(_kNumCoordBox, _input_box_encoding->info()->dimension(1), _kBatchSize), 1, DataType::F32));
244  auto_init_if_empty(*_decoded_scores.info(), TensorInfo(TensorShape(_input_scores->info()->dimension(0), _input_scores->info()->dimension(1), _kBatchSize), 1, DataType::F32));
245  auto_init_if_empty(*_selected_indices.info(), TensorInfo(TensorShape(info.use_regular_nms() ? info.detection_per_class() : info.max_detections()), 1, DataType::S32));
246  const unsigned int num_classes_per_box = std::min(info.max_classes_per_detection(), info.num_classes());
247  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));
248 
249  _input_scores_to_use = _dequantize_scores ? &_decoded_scores : _input_scores;
250 
251  // Manage intermediate buffers
252  _memory_group.manage(&_decoded_boxes);
253  _memory_group.manage(&_decoded_scores);
254  _memory_group.manage(&_selected_indices);
255  _memory_group.manage(&_class_scores);
256  _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());
257 
258  // Allocate and reserve intermediate tensors and vectors
259  _decoded_boxes.allocator()->allocate();
260  _decoded_scores.allocator()->allocate();
261  _selected_indices.allocator()->allocate();
262  _class_scores.allocator()->allocate();
263 }
264 
265 Status CPPDetectionPostProcessLayer::validate(const ITensorInfo *input_box_encoding, const ITensorInfo *input_class_score, const ITensorInfo *input_anchors,
266  ITensorInfo *output_boxes, ITensorInfo *output_classes, ITensorInfo *output_scores, ITensorInfo *num_detection, DetectionPostProcessLayerInfo info)
267 {
268  constexpr unsigned int kBatchSize = 1;
269  constexpr unsigned int kNumCoordBox = 4;
270  const TensorInfo _decoded_boxes_info = TensorInfo(TensorShape(kNumCoordBox, input_box_encoding->dimension(1)), 1, DataType::F32);
271  const TensorInfo _decoded_scores_info = TensorInfo(TensorShape(input_box_encoding->dimension(1)), 1, DataType::F32);
272  const TensorInfo _selected_indices_info = TensorInfo(TensorShape(info.max_detections()), 1, DataType::S32);
273 
274  ARM_COMPUTE_RETURN_ON_ERROR(CPPNonMaximumSuppression::validate(&_decoded_boxes_info, &_decoded_scores_info, &_selected_indices_info, info.max_detections(), info.nms_score_threshold(),
275  info.iou_threshold()));
276  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));
277 
278  return Status{};
279 }
280 
282 {
283  const unsigned int num_classes = _info.num_classes();
284  const unsigned int max_detections = _info.max_detections();
285 
286  DecodeCenterSizeBoxes(_input_box_encoding, _input_anchors, _info, &_decoded_boxes);
287 
288  // Decode scores if necessary
289  if(_dequantize_scores)
290  {
291  if(_input_box_encoding->info()->data_type() == DataType::QASYMM8)
292  {
293  for(unsigned int idx_c = 0; idx_c < _num_classes_with_background; ++idx_c)
294  {
295  for(unsigned int idx_b = 0; idx_b < _num_boxes; ++idx_b)
296  {
297  *(reinterpret_cast<float *>(_decoded_scores.ptr_to_element(Coordinates(idx_c, idx_b)))) =
298  dequantize_qasymm8(*(reinterpret_cast<qasymm8_t *>(_input_scores->ptr_to_element(Coordinates(idx_c, idx_b)))), _input_scores->info()->quantization_info());
299  }
300  }
301  }
302  else if(_input_box_encoding->info()->data_type() == DataType::QASYMM8_SIGNED)
303  {
304  for(unsigned int idx_c = 0; idx_c < _num_classes_with_background; ++idx_c)
305  {
306  for(unsigned int idx_b = 0; idx_b < _num_boxes; ++idx_b)
307  {
308  *(reinterpret_cast<float *>(_decoded_scores.ptr_to_element(Coordinates(idx_c, idx_b)))) =
309  dequantize_qasymm8_signed(*(reinterpret_cast<qasymm8_signed_t *>(_input_scores->ptr_to_element(Coordinates(idx_c, idx_b)))), _input_scores->info()->quantization_info());
310  }
311  }
312  }
313  }
314 
315  // Regular NMS
316  if(_info.use_regular_nms())
317  {
318  std::vector<int> result_idx_boxes_after_nms;
319  std::vector<int> result_classes_after_nms;
320  std::vector<float> result_scores_after_nms;
321  std::vector<unsigned int> sorted_indices;
322 
323  for(unsigned int c = 0; c < num_classes; ++c)
324  {
325  // For each boxes get scores of the boxes for the class c
326  for(unsigned int i = 0; i < _num_boxes; ++i)
327  {
328  *(reinterpret_cast<float *>(_class_scores.ptr_to_element(Coordinates(i)))) =
329  *(reinterpret_cast<float *>(_input_scores_to_use->ptr_to_element(Coordinates(c + 1, i)))); // i * _num_classes_with_background + c + 1
330  }
331 
332  // Run Non-maxima Suppression
333  _nms.run();
334 
335  for(unsigned int i = 0; i < _info.detection_per_class(); ++i)
336  {
337  const auto selected_index = *(reinterpret_cast<int *>(_selected_indices.ptr_to_element(Coordinates(i))));
338  if(selected_index == -1)
339  {
340  // Nms will return -1 for all the last M-elements not valid
341  break;
342  }
343  result_idx_boxes_after_nms.emplace_back(selected_index);
344  result_scores_after_nms.emplace_back((reinterpret_cast<float *>(_class_scores.buffer()))[selected_index]);
345  result_classes_after_nms.emplace_back(c);
346  }
347  }
348 
349  // We select the max detection numbers of the highest score of all classes
350  const auto num_selected = result_scores_after_nms.size();
351  const auto num_output = std::min<unsigned int>(max_detections, num_selected);
352 
353  // Sort selected indices based on result scores
354  sorted_indices.resize(num_selected);
355  std::iota(sorted_indices.begin(), sorted_indices.end(), 0);
356  std::partial_sort(sorted_indices.data(),
357  sorted_indices.data() + num_output,
358  sorted_indices.data() + num_selected,
359  [&](unsigned int first, unsigned int second)
360  {
361 
362  return result_scores_after_nms[first] > result_scores_after_nms[second];
363  });
364 
365  SaveOutputs(&_decoded_boxes, result_idx_boxes_after_nms, result_scores_after_nms, result_classes_after_nms, sorted_indices,
366  num_output, max_detections, _output_boxes, _output_classes, _output_scores, _num_detection);
367  }
368  // Fast NMS
369  else
370  {
371  const unsigned int num_classes_per_box = std::min<unsigned int>(_info.max_classes_per_detection(), _info.num_classes());
372  std::vector<float> max_scores;
373  std::vector<int> box_indices;
374  std::vector<int> max_score_classes;
375 
376  for(unsigned int b = 0; b < _num_boxes; ++b)
377  {
378  std::vector<float> box_scores;
379  for(unsigned int c = 0; c < num_classes; ++c)
380  {
381  box_scores.emplace_back(*(reinterpret_cast<float *>(_input_scores_to_use->ptr_to_element(Coordinates(c + 1, b)))));
382  }
383 
384  std::vector<unsigned int> max_score_indices;
385  max_score_indices.resize(_info.num_classes());
386  std::iota(max_score_indices.data(), max_score_indices.data() + _info.num_classes(), 0);
387  std::partial_sort(max_score_indices.data(),
388  max_score_indices.data() + num_classes_per_box,
389  max_score_indices.data() + num_classes,
390  [&](unsigned int first, unsigned int second)
391  {
392  return box_scores[first] > box_scores[second];
393  });
394 
395  for(unsigned int i = 0; i < num_classes_per_box; ++i)
396  {
397  const float score_to_add = box_scores[max_score_indices[i]];
398  *(reinterpret_cast<float *>(_class_scores.ptr_to_element(Coordinates(b * num_classes_per_box + i)))) = score_to_add;
399  max_scores.emplace_back(score_to_add);
400  box_indices.emplace_back(b);
401  max_score_classes.emplace_back(max_score_indices[i]);
402  }
403  }
404 
405  // Run Non-maxima Suppression
406  _nms.run();
407  std::vector<unsigned int> selected_indices;
408  for(unsigned int i = 0; i < max_detections; ++i)
409  {
410  // NMS returns M valid indices, the not valid tail is filled with -1
411  if(*(reinterpret_cast<int *>(_selected_indices.ptr_to_element(Coordinates(i)))) == -1)
412  {
413  // Nms will return -1 for all the last M-elements not valid
414  break;
415  }
416  selected_indices.emplace_back(*(reinterpret_cast<int *>(_selected_indices.ptr_to_element(Coordinates(i)))));
417  }
418  // We select the max detection numbers of the highest score of all classes
419  const auto num_output = std::min<unsigned int>(_info.max_detections(), selected_indices.size());
420 
421  SaveOutputs(&_decoded_boxes, box_indices, max_scores, max_score_classes, selected_indices,
422  num_output, max_detections, _output_boxes, _output_classes, _output_scores, _num_detection);
423  }
424 }
425 } // namespace arm_compute
bool is_data_type_quantized(DataType dt)
Check if a given data type is of quantized type.
Definition: Utils.h:1121
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:1104
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(...)
Definition: Validate.h:545
#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...
#define ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(t, c,...)
Definition: Validate.h:792
1 channel, 1 F32 per channel
CPPDetectionPostProcessLayer(std::shared_ptr< IMemoryManager > memory_manager=nullptr)
Constructor.
Store the tensor'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
bool use_regular_nms() const
Get if use regular nms.
Definition: Types.h:1134
Copyright (c) 2017-2020 Arm Limited.
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...
Definition: Helpers.inl:207
TensorAllocator * allocator()
Return a pointer to the tensor's allocator.
Definition: Tensor.cpp:48
ITensorInfo * info() const override
Interface to be implemented by the child class to return the tensor's metadata.
Definition: Tensor.cpp:33
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.
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(...)
Definition: Validate.h:288
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.
std::array< float, 4 > BBox
Definition: Types.h:929
virtual ITensorInfo * info() const =0
Interface to be implemented by the child class to return the tensor'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:1109
#define ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(...)
Definition: Validate.h:163
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...
#define ARM_COMPUTE_ERROR_ON_NULLPTR(...)
Definition: Validate.h:161
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.
unsigned int detection_per_class() const
Get detection per class.
Definition: Types.h:1114
Status validate_arguments(const ITensorInfo *input, const ITensorInfo *bias, const ITensorInfo *output, const GEMMLowpOutputStageInfo *output_stage)
Detection Output layer info.
Definition: Types.h:1062
unsigned int num_classes() const
Get num classes.
Definition: Types.h:1129
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.
Store the tensor'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:128
void run() override final
Run the kernels contained in the function.
quantized, asymmetric fixed-point 8-bit number signed