1 /*
2 * Copyright (c) 2016-2020 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/NEHarrisCorners.h"
25
26 #include "arm_compute/core/Error.h"
27 #include "arm_compute/core/TensorInfo.h"
28 #include "arm_compute/core/Validate.h"
29 #include "arm_compute/runtime/Array.h"
30 #include "arm_compute/runtime/NEON/NEScheduler.h"
31 #include "arm_compute/runtime/NEON/functions/NESobel3x3.h"
32 #include "arm_compute/runtime/NEON/functions/NESobel5x5.h"
33 #include "arm_compute/runtime/NEON/functions/NESobel7x7.h"
34 #include "arm_compute/runtime/TensorAllocator.h"
35 #include "src/core/NEON/kernels/NEFillBorderKernel.h"
36 #include "src/core/NEON/kernels/NEFillBorderKernel.h"
37 #include "src/core/NEON/kernels/NEHarrisCornersKernel.h"
38 #include "src/core/NEON/kernels/NESobel5x5Kernel.h"
39 #include "src/core/NEON/kernels/NESobel7x7Kernel.h"
40 #include "support/MemorySupport.h"
41
42 #include <cmath>
43 #include <utility>
44
45 namespace arm_compute
46 {
47 NEHarrisCorners::~NEHarrisCorners() = default;
48
NEHarrisCorners(std::shared_ptr<IMemoryManager> memory_manager)49 NEHarrisCorners::NEHarrisCorners(std::shared_ptr<IMemoryManager> memory_manager) // NOLINT
50 : _memory_group(std::move(memory_manager)),
51 _sobel(),
52 _harris_score(),
53 _non_max_suppr(),
54 _candidates(),
55 _sort_euclidean(),
56 _border_gx(),
57 _border_gy(),
58 _gx(),
59 _gy(),
60 _score(),
61 _nonmax(),
62 _corners_list(),
63 _num_corner_candidates(0)
64 {
65 }
66
configure(IImage * input,float threshold,float min_dist,float sensitivity,int32_t gradient_size,int32_t block_size,KeyPointArray * corners,BorderMode border_mode,uint8_t constant_border_value)67 void NEHarrisCorners::configure(IImage *input, float threshold, float min_dist,
68 float sensitivity, int32_t gradient_size, int32_t block_size, KeyPointArray *corners,
69 BorderMode border_mode, uint8_t constant_border_value)
70 {
71 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(input);
72 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8);
73 ARM_COMPUTE_ERROR_ON(!(block_size == 3 || block_size == 5 || block_size == 7));
74
75 const TensorShape shape = input->info()->tensor_shape();
76 TensorInfo tensor_info_gxgy;
77
78 if(gradient_size < 7)
79 {
80 tensor_info_gxgy.init(shape, Format::S16);
81 }
82 else
83 {
84 tensor_info_gxgy.init(shape, Format::S32);
85 }
86
87 _gx.allocator()->init(tensor_info_gxgy);
88 _gy.allocator()->init(tensor_info_gxgy);
89
90 // Manage intermediate buffers
91 _memory_group.manage(&_gx);
92 _memory_group.manage(&_gy);
93
94 TensorInfo tensor_info_score(shape, Format::F32);
95 _score.allocator()->init(tensor_info_score);
96 _nonmax.allocator()->init(tensor_info_score);
97
98 _corners_list.resize(shape.x() * shape.y());
99
100 // Set/init Sobel kernel accordingly with gradient_size
101 switch(gradient_size)
102 {
103 case 3:
104 {
105 auto k = arm_compute::support::cpp14::make_unique<NESobel3x3>();
106 k->configure(input, &_gx, &_gy, border_mode, constant_border_value);
107 _sobel = std::move(k);
108 break;
109 }
110 case 5:
111 {
112 auto k = arm_compute::support::cpp14::make_unique<NESobel5x5>();
113 k->configure(input, &_gx, &_gy, border_mode, constant_border_value);
114 _sobel = std::move(k);
115 break;
116 }
117 case 7:
118 {
119 auto k = arm_compute::support::cpp14::make_unique<NESobel7x7>();
120 k->configure(input, &_gx, &_gy, border_mode, constant_border_value);
121 _sobel = std::move(k);
122 break;
123 }
124 default:
125 ARM_COMPUTE_ERROR("Gradient size not implemented");
126 }
127
128 // Normalization factor
129 const float norm_factor = 1.0f / (255.0f * pow(4.0f, gradient_size / 2) * block_size);
130
131 // Manage intermediate buffers
132 _memory_group.manage(&_score);
133
134 // Set/init Harris Score kernel accordingly with block_size
135 switch(block_size)
136 {
137 case 3:
138 {
139 auto k = arm_compute::support::cpp14::make_unique<NEHarrisScoreKernel<3>>();
140 k->configure(&_gx, &_gy, &_score, norm_factor, threshold, sensitivity, border_mode == BorderMode::UNDEFINED);
141 _harris_score = std::move(k);
142 }
143 break;
144 case 5:
145 {
146 auto k = arm_compute::support::cpp14::make_unique<NEHarrisScoreKernel<5>>();
147 k->configure(&_gx, &_gy, &_score, norm_factor, threshold, sensitivity, border_mode == BorderMode::UNDEFINED);
148 _harris_score = std::move(k);
149 }
150 break;
151 case 7:
152 {
153 auto k = arm_compute::support::cpp14::make_unique<NEHarrisScoreKernel<7>>();
154 k->configure(&_gx, &_gy, &_score, norm_factor, threshold, sensitivity, border_mode == BorderMode::UNDEFINED);
155 _harris_score = std::move(k);
156 }
157 default:
158 break;
159 }
160
161 // Configure border filling before harris score
162 _border_gx = arm_compute::support::cpp14::make_unique<NEFillBorderKernel>();
163 _border_gy = arm_compute::support::cpp14::make_unique<NEFillBorderKernel>();
164 _border_gx->configure(&_gx, _harris_score->border_size(), border_mode, constant_border_value);
165 _border_gy->configure(&_gy, _harris_score->border_size(), border_mode, constant_border_value);
166
167 // Allocate once all the configure methods have been called
168 _gx.allocator()->allocate();
169 _gy.allocator()->allocate();
170
171 // Manage intermediate buffers
172 _memory_group.manage(&_nonmax);
173
174 // Init non-maxima suppression function
175 _non_max_suppr.configure(&_score, &_nonmax, border_mode);
176
177 // Allocate once all the configure methods have been called
178 _score.allocator()->allocate();
179
180 // Init corner candidates kernel
181 _candidates.configure(&_nonmax, _corners_list.data(), &_num_corner_candidates);
182
183 // Allocate once all the configure methods have been called
184 _nonmax.allocator()->allocate();
185
186 // Init euclidean distance
187 _sort_euclidean.configure(_corners_list.data(), corners, &_num_corner_candidates, min_dist);
188 }
189
run()190 void NEHarrisCorners::run()
191 {
192 ARM_COMPUTE_ERROR_ON_MSG(_sobel == nullptr, "Unconfigured function");
193
194 MemoryGroupResourceScope scope_mg(_memory_group);
195
196 // Init to 0 number of corner candidates
197 _num_corner_candidates = 0;
198
199 // Run Sobel kernel
200 _sobel->run();
201
202 // Fill border before harris score kernel
203 NEScheduler::get().schedule(_border_gx.get(), Window::DimZ);
204 NEScheduler::get().schedule(_border_gy.get(), Window::DimZ);
205
206 // Run harris score kernel
207 NEScheduler::get().schedule(_harris_score.get(), Window::DimY);
208
209 // Run non-maxima suppression
210 _non_max_suppr.run();
211
212 // Run corner candidate kernel
213 NEScheduler::get().schedule(&_candidates, Window::DimY);
214
215 // Run sort & euclidean distance
216 NEScheduler::get().schedule(&_sort_euclidean, Window::DimY);
217 }
218 } // namespace arm_compute
219