arm_compute v18.02
[platform/upstream/armcl.git] / tests / validation / reference / HOGDescriptor.cpp
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 "HOGDescriptor.h"
25
26 #include "Derivative.h"
27 #include "Magnitude.h"
28 #include "Phase.h"
29
30 namespace arm_compute
31 {
32 namespace test
33 {
34 namespace validation
35 {
36 namespace reference
37 {
38 namespace
39 {
40 template <typename T>
41 void hog_orientation_compute(const SimpleTensor<T> &mag, const SimpleTensor<T> &phase, std::vector<T> &bins, const HOGInfo &hog_info)
42 {
43     const Size2D &cell_size = hog_info.cell_size();
44     const size_t  num_bins  = hog_info.num_bins();
45
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;
50     for(size_t yc = 0; yc < cell_size.height; ++yc)
51     {
52         for(size_t xc = 0; xc < cell_size.width; xc++)
53         {
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
67         row_idx += cell_size.width;
68     }
69 }
70
71 template <typename T>
72 void 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
116 template <typename T, typename U, typename V>
117 void hog_orientation_binning(const SimpleTensor<T> &mag, const SimpleTensor<U> &phase, SimpleTensor<V> &hog_space, const HOGInfo &hog_info)
118 {
119     const Size2D &cell_size = hog_info.cell_size();
120
121     const size_t num_bins     = hog_info.num_bins();
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     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);
129
130     int cell_idx = 0;
131     int y_offset = 0;
132
133     // Traverse shape
134     for(auto sy = cell_size.height; sy <= shape_height; sy += cell_size.height)
135     {
136         int x_offset = 0;
137         for(auto sx = cell_size.width; sx <= shape_width; sx += cell_size.width)
138         {
139             int row_idx  = 0;
140             int elem_idx = 0;
141
142             // Traverse cell
143             for(auto y = 0u; y < cell_size.height; ++y)
144             {
145                 for(auto x = 0u; x < cell_size.width; ++x)
146                 {
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
157             std::vector<V> bins(num_bins);
158             hog_orientation_compute(mag_cell, phase_cell, bins, hog_info);
159
160             for(size_t i = 0; i < num_bins; ++i)
161             {
162                 hog_space[cell_idx * num_bins + i] = bins[i];
163             }
164
165             x_offset += cell_size.width;
166             cell_idx++;
167         }
168
169         y_offset += (cell_size.height * shape_width);
170     }
171 }
172
173 template <typename T>
174 void hog_block_normalization(SimpleTensor<T> &desc, const SimpleTensor<T> &hog_space, const HOGInfo &hog_info)
175 {
176     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();
181
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     const size_t num_bins_per_block_x = cells_per_block.width * num_bins;
185
186     // Tensor representing single block
187     SimpleTensor<T> block(TensorShape{ 1u, 1u }, DataType::F32, cells_per_block.area() * num_bins);
188
189     int block_idx      = 0;
190     int block_y_offset = 0;
191
192     // Traverse shape
193     for(auto sy = block_size.height; sy <= shape_height; sy += block_stride.height)
194     {
195         int block_x_offset = 0;
196         for(auto sx = block_size.width; sx <= shape_width; sx += block_stride.width)
197         {
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             {
204                 for(auto x = 0u; x < num_bins_per_block_x; ++x)
205                 {
206                     int idx         = x + cell_y_offset + block_x_offset + block_y_offset;
207                     block[elem_idx] = hog_space[idx];
208                     elem_idx++;
209                 }
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
225 template <typename T, typename U>
226 SimpleTensor<T> hog_descriptor(const SimpleTensor<U> &src, BorderMode border_mode, U constant_border_value, const HOGInfo &hog_info)
227 {
228     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
246     // For each cell create histogram based on magnitude and phase
247     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);
251
252     // Normalize histograms based on block size
253     hog_block_normalization(desc, hog_space, hog_info);
254
255     return desc;
256 }
257
258 template SimpleTensor<float> hog_descriptor(const SimpleTensor<uint8_t> &src, BorderMode border_mode, uint8_t constant_border_value, const HOGInfo &hog_info);
259 } // namespace reference
260 } // namespace validation
261 } // namespace test
262 } // namespace arm_compute