blob: ae70e365c3c35b4d6b23ca44e064987c1866caf3 [file] [log] [blame]
Abe Mbise25a340f2017-12-19 13:00:58 +00001/*
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 "FastCorners.h"
25
26#include "Utils.h"
27#include "tests/validation/Helpers.h"
28#include "tests/validation/reference/NonMaximaSuppression.h"
29
30#include "tests/framework/Asserts.h"
31#include <iomanip>
32
33namespace arm_compute
34{
35namespace test
36{
37namespace validation
38{
39namespace reference
40{
41namespace
42{
43constexpr unsigned int bresenham_radius = 3;
44constexpr unsigned int bresenham_count = 16;
45
46/*
47 Offsets of the 16 pixels in the Bresenham circle of radius 3 centered on P
48 . . . . . . . . .
49 . . . F 0 1 . . .
50 . . E . . . 2 . .
51 . D . . . . . 3 .
52 . C . . P . . 4 .
53 . B . . . . . 5 .
54 . . A . . . 6 . .
55 . . . 9 8 7 . . .
56 . . . . . . . . .
57*/
58const std::array<std::array<int, 2>, 16> circle_offsets =
59{
60 {
61 { { 0, -3 } }, // 0 - pixel #1
62 { { 1, -3 } }, // 1 - pixel #2
63 { { 2, -2 } }, // 2 - pixel #3
64 { { 3, -1 } }, // 3 - pixel #4
65 { { 3, 0 } }, // 4 - pixel #5
66 { { 3, 1 } }, // 5 - pixel #6
67 { { 2, 2 } }, // 6 - pixel #7
68 { { 1, 3 } }, // 7 - pixel #8
69 { { 0, 3 } }, // 8 - pixel #9
70 { { -1, 3 } }, // 9 - pixel #10
71 { { -2, 2 } }, // A - pixel #11
72 { { -3, 1 } }, // B - pixel #12
73 { { -3, 0 } }, // C - pixel #13
74 { { -3, -1 } }, // D - pixel #14
75 { { -2, -2 } }, // E - pixel #15
76 { { -1, -3 } } // F - pixel #16
77 }
78};
79
80/*
81 FAST-9 bit masks for consecutive points surrounding a corner candidate
82
83 // Speed-up rejection of non-corners by checking pixels 1, 9, then 5, 13...
84 const std::array<unsigned int, 16> fast9_order = { { 0, 8, 4, 12, 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15 } };
85*/
86const std::array<uint16_t, 16> fast9_masks =
87{
88 {
89 0x01FF, // 0000 0001 1111 1111
90 0x03FE, // 0000 0011 1111 1110
91 0x07FC, // 0000 0111 1111 1100
92 0x0FF8, // 0000 1111 1111 1000
93 0x1FF0, // 0001 1111 1111 0000
94 0x3FE0, // 0011 1111 1110 0000
95 0x7FC0, // 0111 1111 1100 0000
96 0xFF80, // 1111 1111 1000 0000
97 0xFF01, // 1111 1111 0000 0001
98 0xFE03, // 1111 1110 0000 0011
99 0xFC07, // 1111 1100 0000 0111
100 0xF80F, // 1111 1000 0000 1111
101 0xF01F, // 1111 0000 0001 1111
102 0xE03F, // 1110 0000 0011 1111
103 0xC07F, // 1100 0000 0111 1111
104 0x80FF // 1000 0000 1111 1111
105 }
106};
107
108inline bool in_range(const uint8_t low, const uint8_t high, const uint8_t val)
109{
110 return low <= val && val <= high;
111}
112
113template <typename T, typename F>
114bool is_a_corner(const Coordinates &candidate, const SimpleTensor<T> &src, uint8_t threshold, BorderMode border_mode, T constant_border_value, F intensity_at)
115{
116 const auto intensity_p = tensor_elem_at(src, candidate, border_mode, constant_border_value);
117 const auto thresh_bright = intensity_p + threshold;
118 const auto thresh_dark = intensity_p - threshold;
119
120 // Quicker rejection of non-corner points by checking pixels 1, 9 then 5, 13 around the candidate
121 const auto p1 = intensity_at(candidate, 0);
122 const auto p9 = intensity_at(candidate, 8);
123 const auto p5 = intensity_at(candidate, 4);
124 const auto p13 = intensity_at(candidate, 12);
125
126 if((in_range(thresh_dark, thresh_bright, p1) && in_range(thresh_dark, thresh_bright, p9))
127 || (in_range(thresh_dark, thresh_bright, p5) && in_range(thresh_dark, thresh_bright, p13)))
128 {
129 return false;
130 }
131
132 uint16_t mask_bright = 0;
133 uint16_t mask_dark = 0;
134
135 // Set bits of the brighter/darker pixels mask accordingly
136 for(unsigned int n = 0; n < bresenham_count; ++n)
137 {
138 T intensity_n = intensity_at(candidate, n);
139 mask_bright |= (intensity_n > thresh_bright) << n;
140 mask_dark |= (intensity_n < thresh_dark) << n;
141 }
142
143 // Mark as corner candidate if brighter/darker pixel sequence satisfies any one of the FAST-9 masks
144 const auto found = std::find_if(fast9_masks.begin(), fast9_masks.end(), [&](decltype(fast9_masks[0]) mask)
145 {
146 return (mask_bright & mask) == mask || (mask_dark & mask) == mask;
147 });
148
149 return found != fast9_masks.end();
150}
151} // namespace
152
153template <typename T>
154std::vector<KeyPoint> fast_corners(const SimpleTensor<T> &src, float input_thresh, bool suppress_nonmax, BorderMode border_mode, T constant_border_value)
155{
156 // Get intensity of pixel at given index on the Bresenham circle around a candidate point
157 const auto intensity_at = [&](const Coordinates & point, const unsigned int idx)
158 {
159 const auto offs = circle_offsets[idx];
160 Coordinates px{ point.x() + offs[0], point.y() + offs[1] };
161 return tensor_elem_at(src, px, border_mode, constant_border_value);
162 };
163
164 const auto threshold = static_cast<uint8_t>(input_thresh);
165 std::vector<KeyPoint> corners;
166
167 // 1. Detect potential corners (the segment test)
168 std::vector<Coordinates> corner_candidates;
169 SimpleTensor<float> scores(src.shape(), DataType::F32);
170 ValidRegion valid_region = shape_to_valid_region(src.shape(), BorderMode::UNDEFINED == border_mode, BorderSize(bresenham_radius));
171
172 for(int i = 0; i < src.num_elements(); ++i)
173 {
174 Coordinates candidate = index2coord(src.shape(), i);
175 scores[i] = 0.f;
176 if(!is_in_valid_region(valid_region, candidate))
177 {
178 continue;
179 }
180
181 if(is_a_corner(candidate, src, threshold, border_mode, constant_border_value, intensity_at))
182 {
183 corner_candidates.emplace_back(candidate);
184 scores[i] = 1.f;
185 }
186 }
187
188 // 2. Calculate corner scores if non-maxima suppression
189 // The corner response Cp function is defined as the largest threshold t for which the pixel p remains a corner
190 if(suppress_nonmax)
191 {
192 for(const auto &candidate : corner_candidates)
193 {
194 const auto index = coord2index(scores.shape(), candidate);
195
196#ifdef CALC_CORNER_RESPONSE_BY_ITERATION
197 auto response = threshold;
198 while(is_a_corner(candidate, src, response, border_mode, constant_border_value, intensity_at))
199 {
200 response += 1;
201 }
202 scores[index] = response - 1;
203#else // CALC_CORNER_RESPONSE_BY_ITERATION
204 uint8_t thresh_max = UINT8_MAX;
205 uint8_t thresh_min = threshold;
206 uint8_t response = (thresh_min + thresh_max) / 2;
207
208 while(thresh_max - thresh_min > 1)
209 {
210 response = (thresh_min + thresh_max) / 2;
211 if(is_a_corner(candidate, src, response, border_mode, constant_border_value, intensity_at))
212 {
213 thresh_min = response; // raise threshold
214 }
215 else
216 {
217 thresh_max = response; // lower threshold
218 }
219 }
220 scores[index] = thresh_min;
221#endif // CALC_CORNER_RESPONSE_BY_ITERATION
222 }
223
224 scores = non_maxima_suppression(scores, border_mode, static_cast<float>(constant_border_value));
225 valid_region = shape_to_valid_region(scores.shape(), BorderMode::UNDEFINED == border_mode, BorderSize(bresenham_radius + 1));
226 }
227
228 for(const auto &candidate : corner_candidates)
229 {
230 const auto index = coord2index(scores.shape(), candidate);
231 if(scores[index] > 0.f && is_in_valid_region(valid_region, candidate))
232 {
233 KeyPoint corner;
234 corner.x = candidate.x();
235 corner.y = candidate.y();
236 corner.strength = scores[index];
237 corner.tracking_status = 1;
238 corner.scale = 0.f;
239 corner.orientation = 0.f;
240 corner.error = 0.f;
241 corners.emplace_back(corner);
242 }
243 }
244
245 return corners;
246}
247
248template std::vector<KeyPoint> fast_corners(const SimpleTensor<uint8_t> &src, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value);
249} // namespace reference
250} // namespace validation
251} // namespace test
252} // namespace arm_compute