Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 1 | /* |
Michele Di Giorgio | d9eaf61 | 2020-07-08 11:12:57 +0100 | [diff] [blame] | 2 | * Copyright (c) 2017-2020 Arm Limited. |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [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 "arm_compute/runtime/CL/functions/CLHOGMultiDetection.h" |
| 25 | |
| 26 | #include "arm_compute/core/CL/OpenCL.h" |
| 27 | #include "arm_compute/core/Error.h" |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 28 | #include "arm_compute/core/TensorInfo.h" |
| 29 | #include "arm_compute/runtime/CL/CLArray.h" |
| 30 | #include "arm_compute/runtime/CL/CLScheduler.h" |
| 31 | #include "arm_compute/runtime/CL/CLTensor.h" |
Moritz Pflanzer | c186b57 | 2017-09-07 09:48:04 +0100 | [diff] [blame] | 32 | #include "arm_compute/runtime/Scheduler.h" |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 33 | #include "src/core/CL/kernels/CLFillBorderKernel.h" |
| 34 | #include "src/core/CL/kernels/CLHOGDescriptorKernel.h" |
| 35 | #include "src/core/CL/kernels/CLHOGDetectorKernel.h" |
| 36 | #include "src/core/CL/kernels/CLMagnitudePhaseKernel.h" |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 37 | |
| 38 | using namespace arm_compute; |
| 39 | |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 40 | CLHOGMultiDetection::CLHOGMultiDetection(std::shared_ptr<IMemoryManager> memory_manager) // NOLINT |
| 41 | : _memory_group(std::move(memory_manager)), |
| 42 | _gradient_kernel(), |
Moritz Pflanzer | f4af76e | 2017-09-06 07:42:43 +0100 | [diff] [blame] | 43 | _orient_bin_kernel(), |
| 44 | _block_norm_kernel(), |
| 45 | _hog_detect_kernel(), |
| 46 | _non_maxima_kernel(), |
| 47 | _hog_space(), |
| 48 | _hog_norm_space(), |
| 49 | _detection_windows(), |
| 50 | _mag(), |
| 51 | _phase(), |
| 52 | _non_maxima_suppression(false), |
| 53 | _num_orient_bin_kernel(0), |
| 54 | _num_block_norm_kernel(0), |
| 55 | _num_hog_detect_kernel(0) |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 56 | { |
| 57 | } |
| 58 | |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 59 | CLHOGMultiDetection::~CLHOGMultiDetection() = default; |
| 60 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 61 | void CLHOGMultiDetection::configure(ICLTensor *input, const ICLMultiHOG *multi_hog, ICLDetectionWindowArray *detection_windows, ICLSize2DArray *detection_window_strides, BorderMode border_mode, |
| 62 | uint8_t constant_border_value, float threshold, bool non_maxima_suppression, float min_distance) |
| 63 | { |
Manuel Bottini | 2b84be5 | 2020-04-08 10:15:51 +0100 | [diff] [blame] | 64 | configure(CLKernelLibrary::get().get_compile_context(), input, multi_hog, detection_windows, detection_window_strides, border_mode, constant_border_value, threshold, non_maxima_suppression, |
| 65 | min_distance); |
| 66 | } |
| 67 | |
| 68 | void CLHOGMultiDetection::configure(const CLCompileContext &compile_context, ICLTensor *input, const ICLMultiHOG *multi_hog, ICLDetectionWindowArray *detection_windows, |
| 69 | ICLSize2DArray *detection_window_strides, BorderMode border_mode, |
| 70 | uint8_t constant_border_value, float threshold, bool non_maxima_suppression, float min_distance) |
| 71 | { |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 72 | ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8); |
| 73 | ARM_COMPUTE_ERROR_ON_INVALID_MULTI_HOG(multi_hog); |
| 74 | ARM_COMPUTE_ERROR_ON(nullptr == detection_windows); |
| 75 | ARM_COMPUTE_ERROR_ON(detection_window_strides->num_values() != multi_hog->num_models()); |
| 76 | |
| 77 | const size_t width = input->info()->dimension(Window::DimX); |
| 78 | const size_t height = input->info()->dimension(Window::DimY); |
| 79 | const TensorShape &shape_img = input->info()->tensor_shape(); |
| 80 | const size_t num_models = multi_hog->num_models(); |
| 81 | PhaseType phase_type = multi_hog->model(0)->info()->phase_type(); |
| 82 | |
| 83 | size_t prev_num_bins = multi_hog->model(0)->info()->num_bins(); |
| 84 | Size2D prev_cell_size = multi_hog->model(0)->info()->cell_size(); |
| 85 | Size2D prev_block_size = multi_hog->model(0)->info()->block_size(); |
| 86 | Size2D prev_block_stride = multi_hog->model(0)->info()->block_stride(); |
| 87 | |
| 88 | /* Check if CLHOGOrientationBinningKernel and CLHOGBlockNormalizationKernel kernels can be skipped for a specific HOG data-object |
| 89 | * |
| 90 | * 1) CLHOGOrientationBinningKernel and CLHOGBlockNormalizationKernel are skipped if the cell size and the number of bins don't change. |
| 91 | * Since "multi_hog" is sorted,it is enough to check the HOG descriptors at level "ith" and level "(i-1)th |
| 92 | * 2) CLHOGBlockNormalizationKernel is skipped if the cell size, the number of bins and block size do not change. |
| 93 | * Since "multi_hog" is sorted,it is enough to check the HOG descriptors at level "ith" and level "(i-1)th |
| 94 | * |
| 95 | * @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 |
| 96 | * with "input_orient_bin", "input_hog_detect" and "input_block_norm" |
| 97 | */ |
| 98 | std::vector<size_t> input_orient_bin; |
| 99 | std::vector<size_t> input_hog_detect; |
| 100 | std::vector<std::pair<size_t, size_t>> input_block_norm; |
| 101 | |
| 102 | input_orient_bin.push_back(0); |
| 103 | input_hog_detect.push_back(0); |
| 104 | input_block_norm.emplace_back(0, 0); |
| 105 | |
| 106 | for(size_t i = 1; i < num_models; ++i) |
| 107 | { |
| 108 | size_t cur_num_bins = multi_hog->model(i)->info()->num_bins(); |
| 109 | Size2D cur_cell_size = multi_hog->model(i)->info()->cell_size(); |
| 110 | Size2D cur_block_size = multi_hog->model(i)->info()->block_size(); |
| 111 | Size2D cur_block_stride = multi_hog->model(i)->info()->block_stride(); |
| 112 | |
| 113 | if((cur_num_bins != prev_num_bins) || (cur_cell_size.width != prev_cell_size.width) || (cur_cell_size.height != prev_cell_size.height)) |
| 114 | { |
| 115 | prev_num_bins = cur_num_bins; |
| 116 | prev_cell_size = cur_cell_size; |
| 117 | prev_block_size = cur_block_size; |
| 118 | prev_block_stride = cur_block_stride; |
| 119 | |
| 120 | // Compute orientation binning and block normalization kernels. Update input to process |
| 121 | input_orient_bin.push_back(i); |
| 122 | input_block_norm.emplace_back(i, input_orient_bin.size() - 1); |
| 123 | } |
| 124 | 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) |
| 125 | || (cur_block_stride.height != prev_block_stride.height)) |
| 126 | { |
| 127 | prev_block_size = cur_block_size; |
| 128 | prev_block_stride = cur_block_stride; |
| 129 | |
| 130 | // Compute block normalization kernel. Update input to process |
| 131 | input_block_norm.emplace_back(i, input_orient_bin.size() - 1); |
| 132 | } |
| 133 | |
| 134 | // Update input to process for hog detector kernel |
| 135 | input_hog_detect.push_back(input_block_norm.size() - 1); |
| 136 | } |
| 137 | |
| 138 | _detection_windows = detection_windows; |
| 139 | _non_maxima_suppression = non_maxima_suppression; |
| 140 | _num_orient_bin_kernel = input_orient_bin.size(); // Number of CLHOGOrientationBinningKernel kernels to compute |
| 141 | _num_block_norm_kernel = input_block_norm.size(); // Number of CLHOGBlockNormalizationKernel kernels to compute |
| 142 | _num_hog_detect_kernel = input_hog_detect.size(); // Number of CLHOGDetector functions to compute |
| 143 | |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 144 | _orient_bin_kernel.reserve(_num_orient_bin_kernel); |
| 145 | _block_norm_kernel.reserve(_num_block_norm_kernel); |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 146 | _hog_detect_kernel.resize(_num_hog_detect_kernel); |
| 147 | _hog_space.resize(_num_orient_bin_kernel); |
| 148 | _hog_norm_space.resize(_num_block_norm_kernel); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 149 | |
| 150 | // Allocate tensors for magnitude and phase |
| 151 | TensorInfo info_mag(shape_img, Format::S16); |
| 152 | _mag.allocator()->init(info_mag); |
| 153 | |
| 154 | TensorInfo info_phase(shape_img, Format::U8); |
| 155 | _phase.allocator()->init(info_phase); |
| 156 | |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 157 | // Manage intermediate buffers |
| 158 | _memory_group.manage(&_mag); |
| 159 | _memory_group.manage(&_phase); |
| 160 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 161 | // Initialise gradient kernel |
Manuel Bottini | 2b84be5 | 2020-04-08 10:15:51 +0100 | [diff] [blame] | 162 | _gradient_kernel.configure(compile_context, input, &_mag, &_phase, phase_type, border_mode, constant_border_value); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 163 | |
| 164 | // Configure NETensor for the HOG space and orientation binning kernel |
| 165 | for(size_t i = 0; i < _num_orient_bin_kernel; ++i) |
| 166 | { |
| 167 | const size_t idx_multi_hog = input_orient_bin[i]; |
| 168 | |
| 169 | // Get the corresponding cell size and number of bins |
| 170 | const Size2D &cell = multi_hog->model(idx_multi_hog)->info()->cell_size(); |
| 171 | const size_t num_bins = multi_hog->model(idx_multi_hog)->info()->num_bins(); |
| 172 | |
| 173 | // Calculate number of cells along the x and y directions for the hog_space |
| 174 | const size_t num_cells_x = width / cell.width; |
| 175 | const size_t num_cells_y = height / cell.height; |
| 176 | |
| 177 | // TensorShape of hog space |
| 178 | TensorShape shape_hog_space = input->info()->tensor_shape(); |
| 179 | shape_hog_space.set(Window::DimX, num_cells_x); |
| 180 | shape_hog_space.set(Window::DimY, num_cells_y); |
| 181 | |
| 182 | // Allocate HOG space |
| 183 | TensorInfo info_space(shape_hog_space, num_bins, DataType::F32); |
| 184 | _hog_space[i].allocator()->init(info_space); |
| 185 | |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 186 | // Manage intermediate buffers |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 187 | _memory_group.manage(&_hog_space[i]); |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 188 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 189 | // Initialise orientation binning kernel |
Georgios Pinitas | 40f51a6 | 2020-11-21 03:04:18 +0000 | [diff] [blame] | 190 | _orient_bin_kernel.emplace_back(std::make_unique<CLHOGOrientationBinningKernel>()); |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 191 | _orient_bin_kernel.back()->configure(compile_context, &_mag, &_phase, &_hog_space[i], multi_hog->model(idx_multi_hog)->info()); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 192 | } |
| 193 | |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 194 | // Allocate intermediate tensors |
| 195 | _mag.allocator()->allocate(); |
| 196 | _phase.allocator()->allocate(); |
| 197 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 198 | // Configure CLTensor for the normalized HOG space and block normalization kernel |
| 199 | for(size_t i = 0; i < _num_block_norm_kernel; ++i) |
| 200 | { |
| 201 | const size_t idx_multi_hog = input_block_norm[i].first; |
| 202 | const size_t idx_orient_bin = input_block_norm[i].second; |
| 203 | |
| 204 | // Allocate normalized HOG space |
| 205 | TensorInfo tensor_info(*(multi_hog->model(idx_multi_hog)->info()), width, height); |
| 206 | _hog_norm_space[i].allocator()->init(tensor_info); |
| 207 | |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 208 | // Manage intermediate buffers |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 209 | _memory_group.manage(&_hog_norm_space[i]); |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 210 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 211 | // Initialize block normalization kernel |
Georgios Pinitas | 40f51a6 | 2020-11-21 03:04:18 +0000 | [diff] [blame] | 212 | _block_norm_kernel.emplace_back(std::make_unique<CLHOGBlockNormalizationKernel>()); |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 213 | _block_norm_kernel.back()->configure(compile_context, &_hog_space[idx_orient_bin], &_hog_norm_space[i], multi_hog->model(idx_multi_hog)->info()); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 214 | } |
| 215 | |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 216 | // Allocate intermediate tensors |
| 217 | for(size_t i = 0; i < _num_orient_bin_kernel; ++i) |
| 218 | { |
| 219 | _hog_space[i].allocator()->allocate(); |
| 220 | } |
| 221 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 222 | detection_window_strides->map(CLScheduler::get().queue(), true); |
| 223 | |
| 224 | // Configure HOG detector kernel |
| 225 | for(size_t i = 0; i < _num_hog_detect_kernel; ++i) |
| 226 | { |
| 227 | const size_t idx_block_norm = input_hog_detect[i]; |
| 228 | |
Manuel Bottini | 2b84be5 | 2020-04-08 10:15:51 +0100 | [diff] [blame] | 229 | _hog_detect_kernel[i].configure(compile_context, &_hog_norm_space[idx_block_norm], multi_hog->cl_model(i), detection_windows, detection_window_strides->at(i), threshold, i); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | detection_window_strides->unmap(CLScheduler::get().queue()); |
| 233 | |
| 234 | // Configure non maxima suppression kernel |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 235 | _non_maxima_kernel.configure(_detection_windows, min_distance); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 236 | |
| 237 | // Allocate intermediate tensors |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 238 | for(size_t i = 0; i < _num_block_norm_kernel; ++i) |
| 239 | { |
| 240 | _hog_norm_space[i].allocator()->allocate(); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | void CLHOGMultiDetection::run() |
| 245 | { |
| 246 | ARM_COMPUTE_ERROR_ON_MSG(_detection_windows == nullptr, "Unconfigured function"); |
| 247 | |
Georgios Pinitas | da953f2 | 2019-04-02 17:27:03 +0100 | [diff] [blame] | 248 | MemoryGroupResourceScope scope_mg(_memory_group); |
Georgios Pinitas | 8a94e7c | 2017-09-15 19:06:47 +0100 | [diff] [blame] | 249 | |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 250 | // Reset detection window |
| 251 | _detection_windows->clear(); |
| 252 | |
| 253 | // Run gradient |
| 254 | _gradient_kernel.run(); |
| 255 | |
| 256 | // Run orientation binning kernel |
| 257 | for(size_t i = 0; i < _num_orient_bin_kernel; ++i) |
| 258 | { |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 259 | CLScheduler::get().enqueue(*_orient_bin_kernel[i], false); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | // Run block normalization kernel |
| 263 | for(size_t i = 0; i < _num_block_norm_kernel; ++i) |
| 264 | { |
Sang-Hoon Park | bef7fa2 | 2020-10-21 15:58:54 +0100 | [diff] [blame] | 265 | CLScheduler::get().enqueue(*_block_norm_kernel[i], false); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | // Run HOG detector kernel |
| 269 | for(size_t i = 0; i < _num_hog_detect_kernel; ++i) |
| 270 | { |
| 271 | _hog_detect_kernel[i].run(); |
| 272 | } |
| 273 | |
| 274 | // Run non-maxima suppression kernel if enabled |
| 275 | if(_non_maxima_suppression) |
| 276 | { |
| 277 | // Map detection windows array before computing non maxima suppression |
| 278 | _detection_windows->map(CLScheduler::get().queue(), true); |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 279 | Scheduler::get().schedule(&_non_maxima_kernel, Window::DimY); |
Anthony Barbier | 6ff3b19 | 2017-09-04 18:44:23 +0100 | [diff] [blame] | 280 | _detection_windows->unmap(CLScheduler::get().queue()); |
| 281 | } |
Moritz Pflanzer | f4af76e | 2017-09-06 07:42:43 +0100 | [diff] [blame] | 282 | } |