blob: ed2269579384f6101b159c9a50e6e1570399e21c [file] [log] [blame]
John Richardson25f23682017-11-27 14:35:09 +00001/*
John Richardson36e00752018-01-29 18:05:35 +00002 * Copyright (c) 2017-2018 ARM Limited.
John Richardson25f23682017-11-27 14:35:09 +00003 *
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{
John Richardson36e00752018-01-29 18:05:35 +000043 const Size2D &cell_size = hog_info.cell_size();
44 const size_t num_bins = hog_info.num_bins();
John Richardson25f23682017-11-27 14:35:09 +000045
46 float phase_scale = (PhaseType::SIGNED == hog_info.phase_type() ? num_bins / 360.0f : num_bins / 180.0f);
47 phase_scale *= (PhaseType::SIGNED == hog_info.phase_type() ? 360.0f / 255.0f : 1.0f);
48
49 int row_idx = 0;
John Richardson36e00752018-01-29 18:05:35 +000050 for(size_t yc = 0; yc < cell_size.height; ++yc)
John Richardson25f23682017-11-27 14:35:09 +000051 {
John Richardson36e00752018-01-29 18:05:35 +000052 for(size_t xc = 0; xc < cell_size.width; xc++)
John Richardson25f23682017-11-27 14:35:09 +000053 {
54 const float mag_value = mag[(row_idx + xc)];
55 const float phase_value = phase[(row_idx + xc)] * phase_scale + 0.5f;
56 const float w1 = phase_value - floor(phase_value);
57
58 // The quantised phase is the histogram index [0, num_bins - 1]
59 // Check limit of histogram index. If hidx == num_bins, hidx = 0
60 const auto hidx = static_cast<unsigned int>(phase_value) % num_bins;
61
62 // Weighted vote between 2 bins
63 bins[hidx] += mag_value * (1.0f - w1);
64 bins[(hidx + 1) % num_bins] += mag_value * w1;
65 }
66
John Richardson36e00752018-01-29 18:05:35 +000067 row_idx += cell_size.width;
John Richardson25f23682017-11-27 14:35:09 +000068 }
69}
70
71template <typename T>
72void hog_block_normalization_compute(SimpleTensor<T> &block, SimpleTensor<T> &desc, const HOGInfo &hog_info, size_t block_idx)
73{
74 const int num_bins_per_block = desc.num_channels();
75 const HOGNormType norm_type = hog_info.normalization_type();
76 const Coordinates id = index2coord(desc.shape(), block_idx);
77
78 float sum = 0.0f;
79
80 // Calculate sum
81 for(int i = 0; i < num_bins_per_block; ++i)
82 {
83 const float val = block[i];
84 sum += (norm_type == HOGNormType::L1_NORM) ? std::fabs(val) : val * val;
85 }
86
87 // Calculate normalization scale
88 float scale = 1.0f / (std::sqrt(sum) + num_bins_per_block * 0.1f);
89
90 if(norm_type == HOGNormType::L2HYS_NORM)
91 {
92 // Reset sum
93 sum = 0.0f;
94 for(int i = 0; i < num_bins_per_block; ++i)
95 {
96 float val = block[i] * scale;
97
98 // Clip scaled input_value if over l2_hyst_threshold
99 val = fmin(val, hog_info.l2_hyst_threshold());
100 sum += val * val;
101 block[i] = val;
102 }
103
104 // We use the same constants of OpenCV
105 scale = 1.0f / (std::sqrt(sum) + 1e-3f);
106 }
107
108 for(int i = 0; i < num_bins_per_block; ++i)
109 {
110 block[i] *= scale;
111 reinterpret_cast<float *>(desc(id))[i] = block[i];
112 }
113}
114} // namespace
115
116template <typename T, typename U, typename V>
117void hog_orientation_binning(const SimpleTensor<T> &mag, const SimpleTensor<U> &phase, SimpleTensor<V> &hog_space, const HOGInfo &hog_info)
118{
John Richardson36e00752018-01-29 18:05:35 +0000119 const Size2D &cell_size = hog_info.cell_size();
120
121 const size_t num_bins = hog_info.num_bins();
John Richardson25f23682017-11-27 14:35:09 +0000122 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
John Richardson36e00752018-01-29 18:05:35 +0000125 TensorShape cell_shape(cell_size.width, cell_size.height);
126
127 SimpleTensor<V> mag_cell(cell_shape, DataType::F32);
128 SimpleTensor<V> phase_cell(cell_shape, DataType::F32);
John Richardson25f23682017-11-27 14:35:09 +0000129
130 int cell_idx = 0;
131 int y_offset = 0;
John Richardson25f23682017-11-27 14:35:09 +0000132
133 // Traverse shape
John Richardson36e00752018-01-29 18:05:35 +0000134 for(auto sy = cell_size.height; sy <= shape_height; sy += cell_size.height)
John Richardson25f23682017-11-27 14:35:09 +0000135 {
John Richardson36e00752018-01-29 18:05:35 +0000136 int x_offset = 0;
137 for(auto sx = cell_size.width; sx <= shape_width; sx += cell_size.width)
John Richardson25f23682017-11-27 14:35:09 +0000138 {
139 int row_idx = 0;
140 int elem_idx = 0;
141
142 // Traverse cell
John Richardson36e00752018-01-29 18:05:35 +0000143 for(auto y = 0u; y < cell_size.height; ++y)
John Richardson25f23682017-11-27 14:35:09 +0000144 {
John Richardson36e00752018-01-29 18:05:35 +0000145 for(auto x = 0u; x < cell_size.width; ++x)
John Richardson25f23682017-11-27 14:35:09 +0000146 {
147 int shape_idx = x + row_idx + x_offset + y_offset;
148 mag_cell[elem_idx] = mag[shape_idx];
149 phase_cell[elem_idx] = phase[shape_idx];
150 elem_idx++;
151 }
152
153 row_idx += shape_width;
154 }
155
156 // Partition magnitude values into bins based on phase values
John Richardson36e00752018-01-29 18:05:35 +0000157 std::vector<V> bins(num_bins);
John Richardson25f23682017-11-27 14:35:09 +0000158 hog_orientation_compute(mag_cell, phase_cell, bins, hog_info);
159
John Richardson36e00752018-01-29 18:05:35 +0000160 for(size_t i = 0; i < num_bins; ++i)
John Richardson25f23682017-11-27 14:35:09 +0000161 {
John Richardson36e00752018-01-29 18:05:35 +0000162 hog_space[cell_idx * num_bins + i] = bins[i];
John Richardson25f23682017-11-27 14:35:09 +0000163 }
164
John Richardson36e00752018-01-29 18:05:35 +0000165 x_offset += cell_size.width;
John Richardson25f23682017-11-27 14:35:09 +0000166 cell_idx++;
167 }
168
John Richardson36e00752018-01-29 18:05:35 +0000169 y_offset += (cell_size.height * shape_width);
John Richardson25f23682017-11-27 14:35:09 +0000170 }
171}
172
173template <typename T>
174void hog_block_normalization(SimpleTensor<T> &desc, const SimpleTensor<T> &hog_space, const HOGInfo &hog_info)
175{
John Richardson36e00752018-01-29 18:05:35 +0000176 const Size2D cells_per_block = hog_info.num_cells_per_block();
177 const Size2D cells_per_block_stride = hog_info.num_cells_per_block_stride();
178 const Size2D &block_size = hog_info.block_size();
179 const Size2D &block_stride = hog_info.block_stride();
180 const size_t num_bins = hog_info.num_bins();
John Richardson25f23682017-11-27 14:35:09 +0000181
John Richardson36e00752018-01-29 18:05:35 +0000182 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 const size_t num_bins_per_block_x = cells_per_block.width * num_bins;
John Richardson25f23682017-11-27 14:35:09 +0000185
John Richardson36e00752018-01-29 18:05:35 +0000186 // Tensor representing single block
187 SimpleTensor<T> block(TensorShape{ 1u, 1u }, DataType::F32, cells_per_block.area() * num_bins);
John Richardson25f23682017-11-27 14:35:09 +0000188
189 int block_idx = 0;
190 int block_y_offset = 0;
191
192 // Traverse shape
John Richardson36e00752018-01-29 18:05:35 +0000193 for(auto sy = block_size.height; sy <= shape_height; sy += block_stride.height)
John Richardson25f23682017-11-27 14:35:09 +0000194 {
195 int block_x_offset = 0;
John Richardson36e00752018-01-29 18:05:35 +0000196 for(auto sx = block_size.width; sx <= shape_width; sx += block_stride.width)
John Richardson25f23682017-11-27 14:35:09 +0000197 {
198 int cell_y_offset = 0;
199 int elem_idx = 0;
200
201 // Traverse block
202 for(auto y = 0u; y < cells_per_block.height; ++y)
203 {
John Richardson36e00752018-01-29 18:05:35 +0000204 for(auto x = 0u; x < num_bins_per_block_x; ++x)
John Richardson25f23682017-11-27 14:35:09 +0000205 {
John Richardson36e00752018-01-29 18:05:35 +0000206 int idx = x + cell_y_offset + block_x_offset + block_y_offset;
207 block[elem_idx] = hog_space[idx];
208 elem_idx++;
John Richardson25f23682017-11-27 14:35:09 +0000209 }
210
211 cell_y_offset += hog_space.shape().x() * num_bins;
212 }
213
214 // Normalize block and write to descriptor
215 hog_block_normalization_compute(block, desc, hog_info, block_idx);
216
217 block_x_offset += cells_per_block_stride.width * num_bins;
218 block_idx++;
219 }
220
221 block_y_offset += cells_per_block_stride.height * num_bins * hog_space.shape().x();
222 }
223}
224
225template <typename T, typename U>
226SimpleTensor<T> hog_descriptor(const SimpleTensor<U> &src, BorderMode border_mode, U constant_border_value, const HOGInfo &hog_info)
227{
John Richardson25f23682017-11-27 14:35:09 +0000228 SimpleTensor<int16_t> grad_x;
229 SimpleTensor<int16_t> grad_y;
230
231 // Create tensor info for HOG descriptor
232 TensorInfo desc_info(hog_info, src.shape().x(), src.shape().y());
233 SimpleTensor<T> desc(desc_info.tensor_shape(), DataType::F32, desc_info.num_channels());
234
235 // Create HOG space tensor (num_cells_x, num_cells_y)
236 TensorShape hog_space_shape(src.shape().x() / hog_info.cell_size().width,
237 src.shape().y() / hog_info.cell_size().height);
238
239 // For each cell a histogram with a num_bins is created
240 TensorInfo info_hog_space(hog_space_shape, hog_info.num_bins(), DataType::F32);
241 SimpleTensor<T> hog_space(info_hog_space.tensor_shape(), DataType::F32, info_hog_space.num_channels());
242
243 // Calculate derivative
244 std::tie(grad_x, grad_y) = derivative<int16_t>(src, border_mode, constant_border_value, GradientDimension::GRAD_XY);
245
John Richardson25f23682017-11-27 14:35:09 +0000246 // For each cell create histogram based on magnitude and phase
John Richardson36e00752018-01-29 18:05:35 +0000247 hog_orientation_binning(magnitude(grad_x, grad_y, MagnitudeType::L2NORM),
248 phase(grad_x, grad_y, hog_info.phase_type()),
249 hog_space,
250 hog_info);
John Richardson25f23682017-11-27 14:35:09 +0000251
252 // Normalize histograms based on block size
253 hog_block_normalization(desc, hog_space, hog_info);
254
255 return desc;
256}
257
John Richardson684cb0f2018-01-09 11:17:00 +0000258template void hog_orientation_binning(const SimpleTensor<int16_t> &mag, const SimpleTensor<uint8_t> &phase, SimpleTensor<float> &hog_space, const HOGInfo &hog_info);
259template void hog_block_normalization(SimpleTensor<float> &desc, const SimpleTensor<float> &hog_space, const HOGInfo &hog_info);
John Richardson25f23682017-11-27 14:35:09 +0000260template SimpleTensor<float> hog_descriptor(const SimpleTensor<uint8_t> &src, BorderMode border_mode, uint8_t constant_border_value, const HOGInfo &hog_info);
261} // namespace reference
262} // namespace validation
263} // namespace test
264} // namespace arm_compute