blob: 442f6a18b57ba7b354146bace6d874b7786ea624 [file] [log] [blame]
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +01001/*
2 * Copyright (c) 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 "HarrisCornerDetector.h"
25
26#include "Utils.h"
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +010027#include "tests/validation/Helpers.h"
Georgios Pinitas5a7e7762017-12-01 16:27:29 +000028#include "tests/validation/reference/NonMaximaSuppression.h"
29#include "tests/validation/reference/Sobel.h"
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +010030
31namespace arm_compute
32{
33namespace test
34{
35namespace validation
36{
37namespace reference
38{
39namespace
40{
41template <typename T>
42std::tuple<SimpleTensor<T>, SimpleTensor<T>, float> compute_sobel(const SimpleTensor<uint8_t> &src, int gradient_size, int block_size, BorderMode border_mode, uint8_t constant_border_value)
43{
44 SimpleTensor<T> grad_x;
45 SimpleTensor<T> grad_y;
46 float norm_factor = 0.f;
47
Isabella Gottardi43ce8982017-11-08 11:13:23 +000048 std::tie(grad_x, grad_y) = sobel<T>(src, gradient_size, border_mode, constant_border_value, GradientDimension::GRAD_XY);
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +010049
50 switch(gradient_size)
51 {
52 case 3:
53 norm_factor = 1.f / (4 * 255 * block_size);
54 break;
55 case 5:
56 norm_factor = 1.f / (16 * 255 * block_size);
57 break;
58 case 7:
59 norm_factor = 1.f / (64 * 255 * block_size);
60 break;
61 default:
62 ARM_COMPUTE_ERROR("Gradient size not supported.");
63 }
64
65 return std::make_tuple(grad_x, grad_y, norm_factor);
66}
67
68template <typename T, typename U>
69std::vector<KeyPoint> harris_corner_detector_impl(const SimpleTensor<U> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
70 U constant_border_value)
71{
72 ARM_COMPUTE_ERROR_ON(block_size != 3 && block_size != 5 && block_size != 7);
73
74 SimpleTensor<T> grad_x;
75 SimpleTensor<T> grad_y;
76 float norm_factor = 0.f;
77
78 // Sobel
79 std::tie(grad_x, grad_y, norm_factor) = compute_sobel<T>(src, gradient_size, block_size, border_mode, constant_border_value);
80
81 SimpleTensor<float> scores(src.shape(), DataType::F32);
82 ValidRegion scores_region = shape_to_valid_region(scores.shape(), border_mode == BorderMode::UNDEFINED, BorderSize(gradient_size / 2 + block_size / 2));
83
84 // Calculate scores
85 for(int i = 0; i < scores.num_elements(); ++i)
86 {
87 Coordinates src_coord = index2coord(src.shape(), i);
88 Coordinates block_top_left{ src_coord.x() - block_size / 2, src_coord.y() - block_size / 2 };
89 Coordinates block_bottom_right{ src_coord.x() + block_size / 2, src_coord.y() + block_size / 2 };
90
91 if(!is_in_valid_region(scores_region, src_coord))
92 {
93 scores[i] = 0.f;
94 continue;
95 }
96
97 float Gx2 = 0.f;
98 float Gy2 = 0.f;
99 float Gxy = 0.f;
100
101 // Calculate Gx^2, Gy^2 and Gxy within the given window
Georgios Pinitas424eb5d2017-12-06 19:49:38 +0000102 for(int y = block_top_left.y(); y <= block_bottom_right.y(); ++y)
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +0100103 {
Georgios Pinitas424eb5d2017-12-06 19:49:38 +0000104 for(int x = block_top_left.x(); x <= block_bottom_right.x(); ++x)
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +0100105 {
106 Coordinates block_coord(x, y);
107
108 const float norm_x = tensor_elem_at(grad_x, block_coord, border_mode, static_cast<T>(constant_border_value)) * norm_factor;
109 const float norm_y = tensor_elem_at(grad_y, block_coord, border_mode, static_cast<T>(constant_border_value)) * norm_factor;
110
111 Gx2 += std::pow(norm_x, 2);
112 Gy2 += std::pow(norm_y, 2);
113 Gxy += norm_x * norm_y;
114 }
115 }
116
117 const float trace2 = std::pow(Gx2 + Gy2, 2);
118 const float det = Gx2 * Gy2 - std::pow(Gxy, 2);
119 const float response = det - sensitivity * trace2;
120
121 if(response > threshold)
122 {
123 scores[i] = response;
124 }
125 else
126 {
127 scores[i] = 0.f;
128 }
129 }
130
131 // Suppress non-maxima candidates
132 SimpleTensor<float> suppressed_scores = non_maxima_suppression(scores, border_mode != BorderMode::UNDEFINED ? BorderMode::CONSTANT : BorderMode::UNDEFINED, 0.f);
133 ValidRegion suppressed_scores_region = shape_to_valid_region(suppressed_scores.shape(), border_mode == BorderMode::UNDEFINED, BorderSize(gradient_size / 2 + block_size / 2 + 1));
134
135 // Create vector of candidate corners
136 std::vector<KeyPoint> corner_candidates;
137
138 for(int i = 0; i < suppressed_scores.num_elements(); ++i)
139 {
140 Coordinates coord = index2coord(suppressed_scores.shape(), i);
141
Georgios Pinitas424eb5d2017-12-06 19:49:38 +0000142 if(is_in_valid_region(suppressed_scores_region, coord) && suppressed_scores[i] != 0.f)
Moritz Pflanzer6c6597c2017-09-24 12:09:41 +0100143 {
144 KeyPoint corner;
145 corner.x = coord.x();
146 corner.y = coord.y();
147 corner.tracking_status = 1;
148 corner.strength = suppressed_scores[i];
149 corner.scale = 0.f;
150 corner.orientation = 0.f;
151 corner.error = 0.f;
152
153 corner_candidates.emplace_back(corner);
154 }
155 }
156
157 // Sort descending by strength
158 std::sort(corner_candidates.begin(), corner_candidates.end(), [](const KeyPoint & a, const KeyPoint & b)
159 {
160 return a.strength > b.strength;
161 });
162
163 std::vector<KeyPoint> corners;
164 corners.reserve(corner_candidates.size());
165
166 // Only add corner if there is no stronger within min_dist
167 for(const KeyPoint &point : corner_candidates)
168 {
169 const auto strongest = std::find_if(corners.begin(), corners.end(), [&](const KeyPoint & other)
170 {
171 return std::sqrt((std::pow(point.x - other.x, 2) + std::pow(point.y - other.y, 2))) < min_dist;
172 });
173
174 if(strongest == corners.end())
175 {
176 corners.emplace_back(point);
177 }
178 }
179
180 corners.shrink_to_fit();
181
182 return corners;
183}
184} // namespace
185
186template <typename T>
187std::vector<KeyPoint> harris_corner_detector(const SimpleTensor<T> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
188 T constant_border_value)
189{
190 if(gradient_size < 7)
191 {
192 return harris_corner_detector_impl<int16_t>(src, threshold, min_dist, sensitivity, gradient_size, block_size, border_mode, constant_border_value);
193 }
194 else
195 {
196 return harris_corner_detector_impl<int32_t>(src, threshold, min_dist, sensitivity, gradient_size, block_size, border_mode, constant_border_value);
197 }
198}
199
200template std::vector<KeyPoint> harris_corner_detector(const SimpleTensor<uint8_t> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
201 uint8_t constant_border_value);
202} // namespace reference
203} // namespace validation
204} // namespace test
205} // namespace arm_compute