John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 1 | /* |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 2 | * Copyright (c) 2017-2018 ARM Limited. |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 43 | const Size2D &cell_size = hog_info.cell_size(); |
| 44 | const size_t num_bins = hog_info.num_bins(); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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; |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 50 | for(size_t yc = 0; yc < cell_size.height; ++yc) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 51 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 52 | for(size_t xc = 0; xc < cell_size.width; xc++) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 67 | row_idx += cell_size.width; |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 119 | const Size2D &cell_size = hog_info.cell_size(); |
| 120 | |
| 121 | const size_t num_bins = hog_info.num_bins(); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 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); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 129 | |
| 130 | int cell_idx = 0; |
| 131 | int y_offset = 0; |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 132 | |
| 133 | // Traverse shape |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 134 | for(auto sy = cell_size.height; sy <= shape_height; sy += cell_size.height) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 135 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 136 | int x_offset = 0; |
| 137 | for(auto sx = cell_size.width; sx <= shape_width; sx += cell_size.width) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 138 | { |
| 139 | int row_idx = 0; |
| 140 | int elem_idx = 0; |
| 141 | |
| 142 | // Traverse cell |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 143 | for(auto y = 0u; y < cell_size.height; ++y) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 144 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 145 | for(auto x = 0u; x < cell_size.width; ++x) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 157 | std::vector<V> bins(num_bins); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 158 | hog_orientation_compute(mag_cell, phase_cell, bins, hog_info); |
| 159 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 160 | for(size_t i = 0; i < num_bins; ++i) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 161 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 162 | hog_space[cell_idx * num_bins + i] = bins[i]; |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 163 | } |
| 164 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 165 | x_offset += cell_size.width; |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 166 | cell_idx++; |
| 167 | } |
| 168 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 169 | y_offset += (cell_size.height * shape_width); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 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(); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 181 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 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; |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 185 | |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 186 | // Tensor representing single block |
| 187 | SimpleTensor<T> block(TensorShape{ 1u, 1u }, DataType::F32, cells_per_block.area() * num_bins); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 188 | |
| 189 | int block_idx = 0; |
| 190 | int block_y_offset = 0; |
| 191 | |
| 192 | // Traverse shape |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 193 | for(auto sy = block_size.height; sy <= shape_height; sy += block_stride.height) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 194 | { |
| 195 | int block_x_offset = 0; |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 196 | for(auto sx = block_size.width; sx <= shape_width; sx += block_stride.width) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 204 | for(auto x = 0u; x < num_bins_per_block_x; ++x) |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 205 | { |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 206 | int idx = x + cell_y_offset + block_x_offset + block_y_offset; |
| 207 | block[elem_idx] = hog_space[idx]; |
| 208 | elem_idx++; |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | { |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 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 | |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 246 | // For each cell create histogram based on magnitude and phase |
John Richardson | 36e0075 | 2018-01-29 18:05:35 +0000 | [diff] [blame] | 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); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 251 | |
| 252 | // Normalize histograms based on block size |
| 253 | hog_block_normalization(desc, hog_space, hog_info); |
| 254 | |
| 255 | return desc; |
| 256 | } |
| 257 | |
John Richardson | 684cb0f | 2018-01-09 11:17:00 +0000 | [diff] [blame] | 258 | template void hog_orientation_binning(const SimpleTensor<int16_t> &mag, const SimpleTensor<uint8_t> &phase, SimpleTensor<float> &hog_space, const HOGInfo &hog_info); |
| 259 | template void hog_block_normalization(SimpleTensor<float> &desc, const SimpleTensor<float> &hog_space, const HOGInfo &hog_info); |
John Richardson | 25f2368 | 2017-11-27 14:35:09 +0000 | [diff] [blame] | 260 | template 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 |