Compute Library
 21.02
HOGMultiDetection.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2018 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  */
24 #include "HOGMultiDetection.h"
25 
26 #include "Derivative.h"
27 #include "HOGDescriptor.h"
28 #include "HOGDetector.h"
29 #include "Magnitude.h"
30 #include "Phase.h"
31 
32 namespace arm_compute
33 {
34 namespace test
35 {
36 namespace validation
37 {
38 namespace reference
39 {
40 namespace
41 {
42 void validate_models(const std::vector<HOGInfo> &models)
43 {
44  ARM_COMPUTE_ERROR_ON(0 == models.size());
45 
46  for(size_t i = 1; i < models.size(); ++i)
47  {
48  ARM_COMPUTE_ERROR_ON_MSG(models[0].phase_type() != models[i].phase_type(),
49  "All HOG parameters must have the same phase type");
50 
51  ARM_COMPUTE_ERROR_ON_MSG(models[0].normalization_type() != models[i].normalization_type(),
52  "All HOG parameters must have the same normalization_type");
53 
54  ARM_COMPUTE_ERROR_ON_MSG((models[0].l2_hyst_threshold() != models[i].l2_hyst_threshold()) && (models[0].normalization_type() == arm_compute::HOGNormType::L2HYS_NORM),
55  "All HOG parameters must have the same l2 hysteresis threshold if you use L2 hysteresis normalization type");
56  }
57 }
58 } // namespace
59 
60 void detection_windows_non_maxima_suppression(std::vector<DetectionWindow> &multi_windows, float min_distance)
61 {
62  const size_t num_candidates = multi_windows.size();
63  size_t num_detections = 0;
64 
65  // Sort by idx_class first and by score second
66  std::sort(multi_windows.begin(), multi_windows.end(), [](const DetectionWindow & lhs, const DetectionWindow & rhs)
67  {
68  if(lhs.idx_class < rhs.idx_class)
69  {
70  return true;
71  }
72  if(rhs.idx_class < lhs.idx_class)
73  {
74  return false;
75  }
76 
77  // idx_classes are equal so compare by score
78  if(lhs.score > rhs.score)
79  {
80  return true;
81  }
82  if(rhs.score > lhs.score)
83  {
84  return false;
85  }
86 
87  return false;
88  });
89 
90  const float min_distance_pow2 = min_distance * min_distance;
91 
92  // Euclidean distance
93  for(size_t i = 0; i < num_candidates; ++i)
94  {
95  if(0.0f != multi_windows.at(i).score)
96  {
97  DetectionWindow cur;
98  cur.x = multi_windows.at(i).x;
99  cur.y = multi_windows.at(i).y;
100  cur.width = multi_windows.at(i).width;
101  cur.height = multi_windows.at(i).height;
102  cur.idx_class = multi_windows.at(i).idx_class;
103  cur.score = multi_windows.at(i).score;
104 
105  // Store window
106  multi_windows.at(num_detections) = cur;
107  ++num_detections;
108 
109  const float xc = cur.x + cur.width * 0.5f;
110  const float yc = cur.y + cur.height * 0.5f;
111 
112  for(size_t k = i + 1; k < (num_candidates) && (cur.idx_class == multi_windows.at(k).idx_class); ++k)
113  {
114  const float xn = multi_windows.at(k).x + multi_windows.at(k).width * 0.5f;
115  const float yn = multi_windows.at(k).y + multi_windows.at(k).height * 0.5f;
116 
117  const float dx = std::fabs(xn - xc);
118  const float dy = std::fabs(yn - yc);
119 
120  if(dx < min_distance && dy < min_distance)
121  {
122  const float d = dx * dx + dy * dy;
123 
124  if(d < min_distance_pow2)
125  {
126  // Invalidate detection window
127  multi_windows.at(k).score = 0.0f;
128  }
129  }
130  }
131  }
132  }
133 
134  multi_windows.resize(num_detections);
135 }
136 
137 template <typename T>
138 std::vector<DetectionWindow> hog_multi_detection(const SimpleTensor<T> &src, BorderMode border_mode, T constant_border_value,
139  const std::vector<HOGInfo> &models, std::vector<std::vector<float>> descriptors,
140  unsigned int max_num_detection_windows, float threshold, bool non_maxima_suppression, float min_distance)
141 {
142  ARM_COMPUTE_ERROR_ON(descriptors.size() != models.size());
143  validate_models(models);
144 
145  const size_t width = src.shape().x();
146  const size_t height = src.shape().y();
147  const size_t num_models = models.size();
148 
149  // Initialize previous values
150  size_t prev_num_bins = models[0].num_bins();
151  Size2D prev_cell_size = models[0].cell_size();
152  Size2D prev_block_size = models[0].block_size();
153  Size2D prev_block_stride = models[0].block_stride();
154 
155  std::vector<size_t> input_orient_bin;
156  std::vector<size_t> input_hog_detect;
157  std::vector<std::pair<size_t, size_t>> input_block_norm;
158 
159  input_orient_bin.push_back(0);
160  input_hog_detect.push_back(0);
161  input_block_norm.emplace_back(0, 0);
162 
163  // Iterate through the number of models and check if orientation binning
164  // and block normalization steps can be skipped
165  for(size_t i = 1; i < num_models; ++i)
166  {
167  size_t cur_num_bins = models[i].num_bins();
168  Size2D cur_cell_size = models[i].cell_size();
169  Size2D cur_block_size = models[i].block_size();
170  Size2D cur_block_stride = models[i].block_stride();
171 
172  // Check if binning and normalization steps are required
173  if((cur_num_bins != prev_num_bins) || (cur_cell_size.width != prev_cell_size.width) || (cur_cell_size.height != prev_cell_size.height))
174  {
175  prev_num_bins = cur_num_bins;
176  prev_cell_size = cur_cell_size;
177  prev_block_size = cur_block_size;
178  prev_block_stride = cur_block_stride;
179 
180  // Compute orientation binning and block normalization. Update input to process
181  input_orient_bin.push_back(i);
182  input_block_norm.emplace_back(i, input_orient_bin.size() - 1);
183  }
184  else if((cur_block_size.width != prev_block_size.width) || (cur_block_size.height != prev_block_size.height) || (cur_block_stride.width != prev_block_stride.width)
185  || (cur_block_stride.height != prev_block_stride.height))
186  {
187  prev_block_size = cur_block_size;
188  prev_block_stride = cur_block_stride;
189 
190  // Compute block normalization. Update input to process
191  input_block_norm.emplace_back(i, input_orient_bin.size() - 1);
192  }
193 
194  // Update input to process for hog detector
195  input_hog_detect.push_back(input_block_norm.size() - 1);
196  }
197 
198  size_t num_orient_bin = input_orient_bin.size();
199  size_t num_block_norm = input_block_norm.size();
200  size_t num_hog_detect = input_hog_detect.size();
201 
202  std::vector<SimpleTensor<float>> hog_spaces(num_orient_bin);
203  std::vector<SimpleTensor<float>> hog_norm_spaces(num_block_norm);
204 
205  // Calculate derivative
206  SimpleTensor<int16_t> grad_x;
207  SimpleTensor<int16_t> grad_y;
208  std::tie(grad_x, grad_y) = derivative<int16_t>(src, border_mode, constant_border_value, GradientDimension::GRAD_XY);
209 
210  // Calculate magnitude and phase
211  SimpleTensor<int16_t> _mag = magnitude(grad_x, grad_y, MagnitudeType::L2NORM);
212  SimpleTensor<uint8_t> _phase = phase(grad_x, grad_y, models[0].phase_type());
213 
214  // Calculate Tensors for the HOG space and orientation binning
215  for(size_t i = 0; i < num_orient_bin; ++i)
216  {
217  const size_t idx_multi_hog = input_orient_bin[i];
218 
219  const size_t num_bins = models[idx_multi_hog].num_bins();
220  const size_t num_cells_x = width / models[idx_multi_hog].cell_size().width;
221  const size_t num_cells_y = height / models[idx_multi_hog].cell_size().height;
222 
223  // TensorShape of hog space
224  TensorShape hog_space_shape(num_cells_x, num_cells_y);
225 
226  // Initialise HOG space
227  TensorInfo info_hog_space(hog_space_shape, num_bins, DataType::F32);
228  hog_spaces.at(i) = SimpleTensor<float>(info_hog_space.tensor_shape(), DataType::F32, info_hog_space.num_channels());
229 
230  // For each cell create histogram based on magnitude and phase
231  hog_orientation_binning(_mag, _phase, hog_spaces[i], models[idx_multi_hog]);
232  }
233 
234  // Calculate Tensors for the normalized HOG space and block normalization
235  for(size_t i = 0; i < num_block_norm; ++i)
236  {
237  const size_t idx_multi_hog = input_block_norm[i].first;
238  const size_t idx_orient_bin = input_block_norm[i].second;
239 
240  // Create tensor info for HOG descriptor
241  TensorInfo tensor_info(models[idx_multi_hog], src.shape().x(), src.shape().y());
242  hog_norm_spaces.at(i) = SimpleTensor<float>(tensor_info.tensor_shape(), DataType::F32, tensor_info.num_channels());
243 
244  // Normalize histograms based on block size
245  hog_block_normalization(hog_norm_spaces[i], hog_spaces[idx_orient_bin], models[idx_multi_hog]);
246  }
247 
248  std::vector<DetectionWindow> multi_windows;
249 
250  // Calculate Detection Windows for HOG detector
251  for(size_t i = 0; i < num_hog_detect; ++i)
252  {
253  const size_t idx_block_norm = input_hog_detect[i];
254 
255  // NOTE: Detection window stride fixed to block stride
256  const Size2D detection_window_stride = models[i].block_stride();
257 
258  std::vector<DetectionWindow> windows = hog_detector(hog_norm_spaces[idx_block_norm], descriptors[i],
259  max_num_detection_windows, models[i], detection_window_stride, threshold, i);
260 
261  multi_windows.insert(multi_windows.end(), windows.begin(), windows.end());
262  }
263 
264  // Suppress Non-maxima detection windows
265  if(non_maxima_suppression)
266  {
267  detection_windows_non_maxima_suppression(multi_windows, min_distance);
268  }
269 
270  return multi_windows;
271 }
272 
273 template std::vector<DetectionWindow> hog_multi_detection(const SimpleTensor<uint8_t> &src, BorderMode border_mode, uint8_t constant_border_value,
274  const std::vector<HOGInfo> &models, std::vector<std::vector<float>> descriptors,
275  unsigned int max_num_detection_windows, float threshold, bool non_maxima_suppression, float min_distance);
276 } // namespace reference
277 } // namespace validation
278 } // namespace test
279 } // namespace arm_compute
BorderMode
Methods available to handle borders.
Definition: Types.h:265
L2-norm followed by clipping.
Shape of a tensor.
Definition: TensorShape.h:39
uint16_t x
Top-left x coordinate.
Definition: Types.h:592
float score
Confidence value for the detection window.
Definition: Types.h:597
size_t num_channels() const override
The number of channels for each tensor element.
Definition: TensorInfo.h:258
void hog_orientation_binning(const SimpleTensor< T > &mag, const SimpleTensor< U > &phase, SimpleTensor< V > &hog_space, const HOGInfo &hog_info)
1 channel, 1 F32 per channel
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
SimpleTensor< uint8_t > phase(const SimpleTensor< T > &gx, const SimpleTensor< T > &gy, PhaseType phase_type)
Definition: Phase.cpp:35
TensorShape shape() const override
Shape of the tensor.
Definition: SimpleTensor.h:320
SimpleTensor< float > src
Definition: DFT.cpp:155
Copyright (c) 2017-2021 Arm Limited.
size_t height
Height of the image region or rectangle.
Definition: Size2D.h:90
uint16_t width
Width of the detection window.
Definition: Types.h:594
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
L2 normalization type.
uint16_t idx_class
Index of the class.
Definition: Types.h:596
uint16_t height
Height of the detection window.
Definition: Types.h:595
Simple tensor object that stores elements in a consecutive chunk of memory.
Definition: SimpleTensor.h:58
std::vector< DetectionWindow > hog_detector(const SimpleTensor< T > &src, const std::vector< T > &descriptor, unsigned int max_num_detection_windows, const HOGInfo &hog_info, const Size2D &detection_window_stride, float threshold, uint16_t idx_class)
Definition: HOGDetector.cpp:48
x and y gradient dimension
size_t width
Width of the image region or rectangle.
Definition: Size2D.h:89
SimpleTensor< T > non_maxima_suppression(const SimpleTensor< T > &src, BorderMode border_mode, T constant_border_value)
Class for specifying the size of an image or rectangle.
Definition: Size2D.h:34
Detection window used for the object detection.
Definition: Types.h:590
uint16_t y
Top-left y coordinate.
Definition: Types.h:593
Store the tensor&#39;s metadata.
Definition: TensorInfo.h:45
void detection_windows_non_maxima_suppression(std::vector< DetectionWindow > &multi_windows, float min_distance)
const TensorShape & tensor_shape() const override
Size for each dimension of the tensor.
Definition: TensorInfo.h:262
SimpleTensor< T > threshold(const SimpleTensor< T > &src, T threshold, T false_value, T true_value, ThresholdType type, T upper)
Definition: Threshold.cpp:35
SimpleTensor< T > magnitude(const SimpleTensor< T > &gx, const SimpleTensor< T > &gy, MagnitudeType magnitude_type)
Definition: Magnitude.cpp:35
std::vector< DetectionWindow > hog_multi_detection(const SimpleTensor< T > &src, BorderMode border_mode, T constant_border_value, const std::vector< HOGInfo > &models, std::vector< std::vector< float >> descriptors, unsigned int max_num_detection_windows, float threshold, bool non_maxima_suppression, float min_distance)
void hog_block_normalization(SimpleTensor< T > &desc, const SimpleTensor< T > &hog_space, const HOGInfo &hog_info)