• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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"
27 #include "tests/validation/Helpers.h"
28 #include "tests/validation/reference/NonMaximaSuppression.h"
29 #include "tests/validation/reference/Sobel.h"
30 
31 namespace arm_compute
32 {
33 namespace test
34 {
35 namespace validation
36 {
37 namespace reference
38 {
39 namespace
40 {
41 template <typename T>
compute_sobel(const SimpleTensor<uint8_t> & src,int gradient_size,int block_size,BorderMode border_mode,uint8_t constant_border_value)42 std::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 
48     std::tie(grad_x, grad_y) = sobel<T>(src, gradient_size, border_mode, constant_border_value, GradientDimension::GRAD_XY);
49 
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 
68 template <typename T, typename U>
harris_corner_detector_impl(const SimpleTensor<U> & src,float threshold,float min_dist,float sensitivity,int gradient_size,int block_size,BorderMode border_mode,U constant_border_value)69 std::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
102         for(int y = block_top_left.y(); y <= block_bottom_right.y(); ++y)
103         {
104             for(int x = block_top_left.x(); x <= block_bottom_right.x(); ++x)
105             {
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 
142         if(is_in_valid_region(suppressed_scores_region, coord) && suppressed_scores[i] != 0.f)
143         {
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 
186 template <typename T>
harris_corner_detector(const SimpleTensor<T> & src,float threshold,float min_dist,float sensitivity,int gradient_size,int block_size,BorderMode border_mode,T constant_border_value)187 std::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 
200 template 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
206