Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2016, 2017 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 "arm_compute/runtime/NEON/functions/NEHOGMultiDetection.h" |
| 25 | |
| 26 | #include "arm_compute/core/Error.h" |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 27 | #include "arm_compute/core/TensorInfo.h" |
Moritz Pflanzer | d0ae8b8 | 2017-06-29 14:51:57 +0100 | [diff] [blame] | 28 | #include "arm_compute/core/Validate.h" |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 29 | #include "arm_compute/runtime/NEON/NEScheduler.h" |
| 30 | #include "arm_compute/runtime/Tensor.h" |
Moritz Pflanzer | d0ae8b8 | 2017-06-29 14:51:57 +0100 | [diff] [blame] | 31 | #include "support/ToolchainSupport.h" |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 32 | |
| 33 | using namespace arm_compute; |
| 34 | |
Moritz Pflanzer | f4af76e | 2017-09-06 07:42:43 +0100 | [diff] [blame] | 35 | NEHOGMultiDetection::NEHOGMultiDetection() // NOLINT |
| 36 | : _gradient_kernel(), |
| 37 | _orient_bin_kernel(), |
| 38 | _block_norm_kernel(), |
| 39 | _hog_detect_kernel(), |
| 40 | _non_maxima_kernel(), |
| 41 | _hog_space(), |
| 42 | _hog_norm_space(), |
| 43 | _detection_windows(), |
| 44 | _mag(), |
| 45 | _phase(), |
| 46 | _non_maxima_suppression(false), |
| 47 | _num_orient_bin_kernel(0), |
| 48 | _num_block_norm_kernel(0), |
| 49 | _num_hog_detect_kernel(0) |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 50 | { |
| 51 | } |
| 52 | |
| 53 | void NEHOGMultiDetection::configure(ITensor *input, const IMultiHOG *multi_hog, IDetectionWindowArray *detection_windows, const ISize2DArray *detection_window_strides, BorderMode border_mode, |
| 54 | uint8_t constant_border_value, float threshold, bool non_maxima_suppression, float min_distance) |
| 55 | { |
| 56 | ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8); |
| 57 | ARM_COMPUTE_ERROR_ON_INVALID_MULTI_HOG(multi_hog); |
| 58 | ARM_COMPUTE_ERROR_ON(nullptr == detection_windows); |
| 59 | ARM_COMPUTE_ERROR_ON(detection_window_strides->num_values() != multi_hog->num_models()); |
| 60 | |
| 61 | const size_t width = input->info()->dimension(Window::DimX); |
| 62 | const size_t height = input->info()->dimension(Window::DimY); |
| 63 | const TensorShape &shape_img = input->info()->tensor_shape(); |
| 64 | const size_t num_models = multi_hog->num_models(); |
| 65 | PhaseType phase_type = multi_hog->model(0)->info()->phase_type(); |
| 66 | |
| 67 | size_t prev_num_bins = multi_hog->model(0)->info()->num_bins(); |
| 68 | Size2D prev_cell_size = multi_hog->model(0)->info()->cell_size(); |
| 69 | Size2D prev_block_size = multi_hog->model(0)->info()->block_size(); |
| 70 | Size2D prev_block_stride = multi_hog->model(0)->info()->block_stride(); |
| 71 | |
| 72 | /* Check if NEHOGOrientationBinningKernel and NEHOGBlockNormalizationKernel kernels can be skipped for a specific HOG data-object |
| 73 | * |
| 74 | * 1) NEHOGOrientationBinningKernel and NEHOGBlockNormalizationKernel are skipped if the cell size and the number of bins don't change. |
| 75 | * Since "multi_hog" is sorted,it is enough to check the HOG descriptors at level "ith" and level "(i-1)th |
| 76 | * 2) NEHOGBlockNormalizationKernel is skipped if the cell size, the number of bins and block size do not change. |
| 77 | * Since "multi_hog" is sorted,it is enough to check the HOG descriptors at level "ith" and level "(i-1)th |
| 78 | * |
| 79 | * @note Since the orientation binning and block normalization kernels can be skipped, we need to keep track of the input to process for each kernel |
| 80 | * with "input_orient_bin", "input_hog_detect" and "input_block_norm" |
| 81 | */ |
| 82 | std::vector<size_t> input_orient_bin; |
| 83 | std::vector<size_t> input_hog_detect; |
| 84 | std::vector<std::pair<size_t, size_t>> input_block_norm; |
| 85 | |
| 86 | input_orient_bin.push_back(0); |
| 87 | input_hog_detect.push_back(0); |
| 88 | input_block_norm.emplace_back(0, 0); |
| 89 | |
| 90 | for(size_t i = 1; i < num_models; ++i) |
| 91 | { |
| 92 | size_t cur_num_bins = multi_hog->model(i)->info()->num_bins(); |
| 93 | Size2D cur_cell_size = multi_hog->model(i)->info()->cell_size(); |
| 94 | Size2D cur_block_size = multi_hog->model(i)->info()->block_size(); |
| 95 | Size2D cur_block_stride = multi_hog->model(i)->info()->block_stride(); |
| 96 | |
| 97 | if((cur_num_bins != prev_num_bins) || (cur_cell_size.width != prev_cell_size.width) || (cur_cell_size.height != prev_cell_size.height)) |
| 98 | { |
| 99 | prev_num_bins = cur_num_bins; |
| 100 | prev_cell_size = cur_cell_size; |
| 101 | prev_block_size = cur_block_size; |
| 102 | prev_block_stride = cur_block_stride; |
| 103 | |
| 104 | // Compute orientation binning and block normalization kernels. Update input to process |
| 105 | input_orient_bin.push_back(i); |
| 106 | input_block_norm.emplace_back(i, input_orient_bin.size() - 1); |
| 107 | } |
| 108 | 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) |
| 109 | || (cur_block_stride.height != prev_block_stride.height)) |
| 110 | { |
| 111 | prev_block_size = cur_block_size; |
| 112 | prev_block_stride = cur_block_stride; |
| 113 | |
| 114 | // Compute block normalization kernel. Update input to process |
| 115 | input_block_norm.emplace_back(i, input_orient_bin.size() - 1); |
| 116 | } |
| 117 | |
| 118 | // Update input to process for hog detector kernel |
| 119 | input_hog_detect.push_back(input_block_norm.size() - 1); |
| 120 | } |
| 121 | |
| 122 | _detection_windows = detection_windows; |
| 123 | _non_maxima_suppression = non_maxima_suppression; |
| 124 | _num_orient_bin_kernel = input_orient_bin.size(); // Number of NEHOGOrientationBinningKernel kernels to compute |
| 125 | _num_block_norm_kernel = input_block_norm.size(); // Number of NEHOGBlockNormalizationKernel kernels to compute |
| 126 | _num_hog_detect_kernel = input_hog_detect.size(); // Number of NEHOGDetector functions to compute |
| 127 | |
Moritz Pflanzer | d0ae8b8 | 2017-06-29 14:51:57 +0100 | [diff] [blame] | 128 | _orient_bin_kernel = arm_compute::support::cpp14::make_unique<NEHOGOrientationBinningKernel[]>(_num_orient_bin_kernel); |
| 129 | _block_norm_kernel = arm_compute::support::cpp14::make_unique<NEHOGBlockNormalizationKernel[]>(_num_block_norm_kernel); |
| 130 | _hog_detect_kernel = arm_compute::support::cpp14::make_unique<NEHOGDetector[]>(_num_hog_detect_kernel); |
| 131 | _non_maxima_kernel = arm_compute::support::cpp14::make_unique<CPPDetectionWindowNonMaximaSuppressionKernel>(); |
| 132 | _hog_space = arm_compute::support::cpp14::make_unique<Tensor[]>(_num_orient_bin_kernel); |
| 133 | _hog_norm_space = arm_compute::support::cpp14::make_unique<Tensor[]>(_num_block_norm_kernel); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 134 | |
| 135 | // Allocate tensors for magnitude and phase |
| 136 | TensorInfo info_mag(shape_img, Format::S16); |
| 137 | _mag.allocator()->init(info_mag); |
| 138 | |
| 139 | TensorInfo info_phase(shape_img, Format::U8); |
| 140 | _phase.allocator()->init(info_phase); |
| 141 | |
| 142 | // Initialise gradient kernel |
| 143 | _gradient_kernel.configure(input, &_mag, &_phase, phase_type, border_mode, constant_border_value); |
| 144 | |
| 145 | // Configure NETensor for the HOG space and orientation binning kernel |
| 146 | for(size_t i = 0; i < _num_orient_bin_kernel; ++i) |
| 147 | { |
| 148 | const size_t idx_multi_hog = input_orient_bin[i]; |
| 149 | |
| 150 | // Get the corresponding cell size and number of bins |
| 151 | const Size2D &cell = multi_hog->model(idx_multi_hog)->info()->cell_size(); |
| 152 | const size_t num_bins = multi_hog->model(idx_multi_hog)->info()->num_bins(); |
| 153 | |
| 154 | // Calculate number of cells along the x and y directions for the hog_space |
| 155 | const size_t num_cells_x = width / cell.width; |
| 156 | const size_t num_cells_y = height / cell.height; |
| 157 | |
| 158 | // TensorShape of hog space |
| 159 | TensorShape shape_hog_space = input->info()->tensor_shape(); |
| 160 | shape_hog_space.set(Window::DimX, num_cells_x); |
| 161 | shape_hog_space.set(Window::DimY, num_cells_y); |
| 162 | |
| 163 | // Allocate HOG space |
| 164 | TensorInfo info_space(shape_hog_space, num_bins, DataType::F32); |
| 165 | _hog_space[i].allocator()->init(info_space); |
| 166 | |
| 167 | // Initialise orientation binning kernel |
| 168 | _orient_bin_kernel[i].configure(&_mag, &_phase, _hog_space.get() + i, multi_hog->model(idx_multi_hog)->info()); |
| 169 | } |
| 170 | |
| 171 | // Configure NETensor for the normalized HOG space and block normalization kernel |
| 172 | for(size_t i = 0; i < _num_block_norm_kernel; ++i) |
| 173 | { |
| 174 | const size_t idx_multi_hog = input_block_norm[i].first; |
| 175 | const size_t idx_orient_bin = input_block_norm[i].second; |
| 176 | |
| 177 | // Allocate normalized HOG space |
| 178 | TensorInfo tensor_info(*(multi_hog->model(idx_multi_hog)->info()), width, height); |
| 179 | _hog_norm_space[i].allocator()->init(tensor_info); |
| 180 | |
| 181 | // Initialize block normalization kernel |
| 182 | _block_norm_kernel[i].configure(_hog_space.get() + idx_orient_bin, _hog_norm_space.get() + i, multi_hog->model(idx_multi_hog)->info()); |
| 183 | } |
| 184 | |
| 185 | // Configure HOG detector kernel |
| 186 | for(size_t i = 0; i < _num_hog_detect_kernel; ++i) |
| 187 | { |
| 188 | const size_t idx_block_norm = input_hog_detect[i]; |
| 189 | |
| 190 | _hog_detect_kernel[i].configure(_hog_norm_space.get() + idx_block_norm, multi_hog->model(i), detection_windows, detection_window_strides->at(i), threshold, i); |
| 191 | } |
| 192 | |
| 193 | // Configure non maxima suppression kernel |
| 194 | _non_maxima_kernel->configure(_detection_windows, min_distance); |
| 195 | |
| 196 | // Allocate intermediate tensors |
| 197 | _mag.allocator()->allocate(); |
| 198 | _phase.allocator()->allocate(); |
| 199 | |
| 200 | for(size_t i = 0; i < _num_orient_bin_kernel; ++i) |
| 201 | { |
| 202 | _hog_space[i].allocator()->allocate(); |
| 203 | } |
| 204 | |
| 205 | for(size_t i = 0; i < _num_block_norm_kernel; ++i) |
| 206 | { |
| 207 | _hog_norm_space[i].allocator()->allocate(); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | void NEHOGMultiDetection::run() |
| 212 | { |
| 213 | ARM_COMPUTE_ERROR_ON_MSG(_detection_windows == nullptr, "Unconfigured function"); |
| 214 | |
| 215 | // Reset detection window |
| 216 | _detection_windows->clear(); |
| 217 | |
| 218 | // Run gradient |
| 219 | _gradient_kernel.run(); |
| 220 | |
| 221 | // Run orientation binning kernel |
| 222 | for(size_t i = 0; i < _num_orient_bin_kernel; ++i) |
| 223 | { |
| 224 | NEScheduler::get().schedule(_orient_bin_kernel.get() + i, Window::DimY); |
| 225 | } |
| 226 | |
| 227 | // Run block normalization kernel |
| 228 | for(size_t i = 0; i < _num_block_norm_kernel; ++i) |
| 229 | { |
| 230 | NEScheduler::get().schedule(_block_norm_kernel.get() + i, Window::DimY); |
| 231 | } |
| 232 | |
| 233 | // Run HOG detector kernel |
| 234 | for(size_t i = 0; i < _num_hog_detect_kernel; ++i) |
| 235 | { |
| 236 | _hog_detect_kernel[i].run(); |
| 237 | } |
| 238 | |
| 239 | // Run non-maxima suppression kernel if enabled |
| 240 | if(_non_maxima_suppression) |
| 241 | { |
Moritz Pflanzer | c186b57 | 2017-09-07 09:48:04 +0100 | [diff] [blame] | 242 | NEScheduler::get().schedule(_non_maxima_kernel.get(), Window::DimY); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 243 | } |
| 244 | } |