blob: 369ac74edc87f254281dbbad0b37992c69fee664 [file] [log] [blame]
Anthony Barbierf45d5a92018-01-24 16:23:15 +00001/*
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 "HOGDescriptor.h"
25
26#include "Derivative.h"
27#include "Magnitude.h"
28#include "Phase.h"
29
30namespace arm_compute
31{
32namespace test
33{
34namespace validation
35{
36namespace reference
37{
38namespace
39{
40template <typename T>
41void hog_orientation_compute(const SimpleTensor<T> &mag, const SimpleTensor<T> &phase, std::vector<T> &bins, const HOGInfo &hog_info)
42{
43 const size_t num_bins = hog_info.num_bins();
44 const size_t cell_height = hog_info.cell_size().height;
45 const size_t cell_width = hog_info.cell_size().width;
46
47 float phase_scale = (PhaseType::SIGNED == hog_info.phase_type() ? num_bins / 360.0f : num_bins / 180.0f);
48 phase_scale *= (PhaseType::SIGNED == hog_info.phase_type() ? 360.0f / 255.0f : 1.0f);
49
50 int row_idx = 0;
51 for(size_t yc = 0; yc < cell_height; ++yc)
52 {
53 for(size_t xc = 0; xc < cell_height; xc++)
54 {
55 const float mag_value = mag[(row_idx + xc)];
56 const float phase_value = phase[(row_idx + xc)] * phase_scale + 0.5f;
57 const float w1 = phase_value - floor(phase_value);
58
59 // The quantised phase is the histogram index [0, num_bins - 1]
60 // Check limit of histogram index. If hidx == num_bins, hidx = 0
61 const auto hidx = static_cast<unsigned int>(phase_value) % num_bins;
62
63 // Weighted vote between 2 bins
64 bins[hidx] += mag_value * (1.0f - w1);
65 bins[(hidx + 1) % num_bins] += mag_value * w1;
66 }
67
68 row_idx += cell_width;
69 }
70}
71
72template <typename T>
73void hog_block_normalization_compute(SimpleTensor<T> &block, SimpleTensor<T> &desc, const HOGInfo &hog_info, size_t block_idx)
74{
75 const int num_bins_per_block = desc.num_channels();
76 const HOGNormType norm_type = hog_info.normalization_type();
77 const Coordinates id = index2coord(desc.shape(), block_idx);
78
79 float sum = 0.0f;
80
81 // Calculate sum
82 for(int i = 0; i < num_bins_per_block; ++i)
83 {
84 const float val = block[i];
85 sum += (norm_type == HOGNormType::L1_NORM) ? std::fabs(val) : val * val;
86 }
87
88 // Calculate normalization scale
89 float scale = 1.0f / (std::sqrt(sum) + num_bins_per_block * 0.1f);
90
91 if(norm_type == HOGNormType::L2HYS_NORM)
92 {
93 // Reset sum
94 sum = 0.0f;
95 for(int i = 0; i < num_bins_per_block; ++i)
96 {
97 float val = block[i] * scale;
98
99 // Clip scaled input_value if over l2_hyst_threshold
100 val = fmin(val, hog_info.l2_hyst_threshold());
101 sum += val * val;
102 block[i] = val;
103 }
104
105 // We use the same constants of OpenCV
106 scale = 1.0f / (std::sqrt(sum) + 1e-3f);
107 }
108
109 for(int i = 0; i < num_bins_per_block; ++i)
110 {
111 block[i] *= scale;
112 reinterpret_cast<float *>(desc(id))[i] = block[i];
113 }
114}
115} // namespace
116
117template <typename T, typename U, typename V>
118void hog_orientation_binning(const SimpleTensor<T> &mag, const SimpleTensor<U> &phase, SimpleTensor<V> &hog_space, const HOGInfo &hog_info)
119{
120 const size_t cell_width = hog_info.cell_size().width;
121 const size_t cell_height = hog_info.cell_size().height;
122 const size_t shape_width = hog_space.shape().x() * hog_info.cell_size().width;
123 const size_t shape_height = hog_space.shape().y() * hog_info.cell_size().height;
124
125 SimpleTensor<V> mag_cell(TensorShape(cell_width, cell_height), DataType::F32);
126 SimpleTensor<V> phase_cell(TensorShape(cell_width, cell_height), DataType::F32);
127
128 int cell_idx = 0;
129 int y_offset = 0;
130 int x_offset = 0;
131
132 // Traverse shape
133 for(auto sy = cell_height - 1; sy < shape_height; sy += cell_height)
134 {
135 x_offset = 0;
136 for(auto sx = cell_width - 1; sx < shape_width; sx += cell_width)
137 {
138 int row_idx = 0;
139 int elem_idx = 0;
140
141 // Traverse cell
142 for(auto y = 0u; y < cell_height; ++y)
143 {
144 for(auto x = 0u; x < cell_width; ++x)
145 {
146 int shape_idx = x + row_idx + x_offset + y_offset;
147 mag_cell[elem_idx] = mag[shape_idx];
148 phase_cell[elem_idx] = phase[shape_idx];
149 elem_idx++;
150 }
151
152 row_idx += shape_width;
153 }
154
155 // Partition magnitude values into bins based on phase values
156 std::vector<V> bins(hog_info.num_bins());
157 hog_orientation_compute(mag_cell, phase_cell, bins, hog_info);
158
159 for(size_t i = 0; i < hog_info.num_bins(); ++i)
160 {
161 hog_space[cell_idx * hog_info.num_bins() + i] = bins[i];
162 }
163
164 x_offset += cell_width;
165 cell_idx++;
166 }
167
168 y_offset += (cell_height * shape_width);
169 }
170}
171
172template <typename T>
173void hog_block_normalization(SimpleTensor<T> &desc, const SimpleTensor<T> &hog_space, const HOGInfo &hog_info)
174{
175 const Size2D cells_per_block = hog_info.num_cells_per_block();
176 const Size2D cells_per_block_stride = hog_info.num_cells_per_block_stride();
177
178 const size_t block_width = hog_info.block_size().width;
179 const size_t block_height = hog_info.block_size().height;
180 const size_t block_stride_width = hog_info.block_stride().width;
181 const size_t block_stride_height = hog_info.block_stride().height;
182 const size_t shape_width = hog_space.shape().x() * hog_info.cell_size().width;
183 const size_t shape_height = hog_space.shape().y() * hog_info.cell_size().height;
184
185 const size_t num_bins = hog_info.num_bins();
186 const size_t num_channels = cells_per_block.area() * num_bins;
187
188 SimpleTensor<T> block(TensorShape{ 1u, 1u }, DataType::F32, num_channels);
189
190 int block_idx = 0;
191 int block_y_offset = 0;
192
193 // Traverse shape
194 for(auto sy = block_width - 1; sy < shape_height; sy += block_stride_height)
195 {
196 int block_x_offset = 0;
197 for(auto sx = block_height - 1; sx < shape_width; sx += block_stride_width)
198 {
199 int cell_y_offset = 0;
200 int elem_idx = 0;
201
202 // Traverse block
203 for(auto y = 0u; y < cells_per_block.height; ++y)
204 {
205 int cell_x_offset = 0;
206 for(auto x = 0u; x < cells_per_block.width; ++x)
207 {
208 for(auto bin = 0u; bin < num_bins; ++bin)
209 {
210 int idx = bin + cell_x_offset + cell_y_offset + block_x_offset + block_y_offset;
211 block[elem_idx] = hog_space[idx];
212 elem_idx++;
213 }
214
215 cell_x_offset += num_bins;
216 }
217
218 cell_y_offset += hog_space.shape().x() * num_bins;
219 }
220
221 // Normalize block and write to descriptor
222 hog_block_normalization_compute(block, desc, hog_info, block_idx);
223
224 block_x_offset += cells_per_block_stride.width * num_bins;
225 block_idx++;
226 }
227
228 block_y_offset += cells_per_block_stride.height * num_bins * hog_space.shape().x();
229 }
230}
231
232template <typename T, typename U>
233SimpleTensor<T> hog_descriptor(const SimpleTensor<U> &src, BorderMode border_mode, U constant_border_value, const HOGInfo &hog_info)
234{
235 SimpleTensor<int16_t> _mag;
236 SimpleTensor<uint8_t> _phase;
237
238 SimpleTensor<int16_t> grad_x;
239 SimpleTensor<int16_t> grad_y;
240
241 // Create tensor info for HOG descriptor
242 TensorInfo desc_info(hog_info, src.shape().x(), src.shape().y());
243 SimpleTensor<T> desc(desc_info.tensor_shape(), DataType::F32, desc_info.num_channels());
244
245 // Create HOG space tensor (num_cells_x, num_cells_y)
246 TensorShape hog_space_shape(src.shape().x() / hog_info.cell_size().width,
247 src.shape().y() / hog_info.cell_size().height);
248
249 // For each cell a histogram with a num_bins is created
250 TensorInfo info_hog_space(hog_space_shape, hog_info.num_bins(), DataType::F32);
251 SimpleTensor<T> hog_space(info_hog_space.tensor_shape(), DataType::F32, info_hog_space.num_channels());
252
253 // Calculate derivative
254 std::tie(grad_x, grad_y) = derivative<int16_t>(src, border_mode, constant_border_value, GradientDimension::GRAD_XY);
255
256 // Calculate magnitude and phase
257 _mag = magnitude(grad_x, grad_y, MagnitudeType::L2NORM);
258 _phase = phase(grad_x, grad_y, hog_info.phase_type());
259
260 // For each cell create histogram based on magnitude and phase
261 hog_orientation_binning(_mag, _phase, hog_space, hog_info);
262
263 // Normalize histograms based on block size
264 hog_block_normalization(desc, hog_space, hog_info);
265
266 return desc;
267}
268
269template SimpleTensor<float> hog_descriptor(const SimpleTensor<uint8_t> &src, BorderMode border_mode, uint8_t constant_border_value, const HOGInfo &hog_info);
270} // namespace reference
271} // namespace validation
272} // namespace test
273} // namespace arm_compute