cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
agast_keypoints_impl.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <algorithm>
6#include <cmath>
7#include <future>
8#include <vector>
9
10namespace toolbox::pcl
11{
12
13template<typename DataType, typename KNN>
15{
16 m_cloud = std::make_shared<point_cloud>(cloud);
17 initialize_test_pattern();
18 return m_cloud->size();
19}
20
21template<typename DataType, typename KNN>
23{
24 m_cloud = cloud;
25 initialize_test_pattern();
26 return m_cloud->size();
27}
28
29template<typename DataType, typename KNN>
31{
32 m_knn = const_cast<knn_type*>(&knn);
33 if (m_cloud) {
34 m_knn->set_input(*m_cloud);
35 }
36 return m_cloud ? m_cloud->size() : 0;
37}
38
39template<typename DataType, typename KNN>
41{
42 m_pattern_radius = radius;
43 initialize_test_pattern();
44 return 0;
45}
46
47template<typename DataType, typename KNN>
49{
50 m_enable_parallel = enable;
51}
52
53template<typename DataType, typename KNN>
55{
56 m_test_pattern.clear();
57 m_test_pattern.reserve(m_num_test_points);
58
59 // Generate test points uniformly distributed on a sphere
60 // Using Fibonacci spiral for uniform distribution
61 const data_type golden_angle = static_cast<data_type>(M_PI * (3.0 - std::sqrt(5.0))); // Golden angle
62
63 for (std::size_t i = 0; i < m_num_test_points; ++i) {
64 const data_type y = 1.0 - (2.0 * i) / (m_num_test_points - 1); // y goes from 1 to -1
65 const data_type radius = std::sqrt(1.0 - y * y);
66 const data_type theta = golden_angle * i;
67
68 TestPoint test_point;
69 test_point.x = std::cos(theta) * radius * m_pattern_radius;
70 test_point.y = y * m_pattern_radius;
71 test_point.z = std::sin(theta) * radius * m_pattern_radius;
72
73 m_test_pattern.push_back(test_point);
74 }
75}
76
77template<typename DataType, typename KNN>
79agast_keypoint_extractor_t<DataType, KNN>::compute_test_value(
80 const point_t<data_type>& center,
81 const TestPoint& test_point)
82{
83 // For 3D AGAST, we use local point density as the test value
84 // Alternative: could use local height, curvature, or other geometric properties
85
86 point_t<data_type> test_location;
87 test_location.x = center.x + test_point.x;
88 test_location.y = center.y + test_point.y;
89 test_location.z = center.z + test_point.z;
90
91 std::vector<std::size_t> neighbor_indices;
92 std::vector<data_type> neighbor_distances;
93
94 // Count points within a small radius around the test location
95 const data_type count_radius = m_pattern_radius * 0.3; // 30% of pattern radius
96 m_knn->radius_neighbors(test_location, count_radius, neighbor_indices, neighbor_distances);
97
98 // Return normalized density
99 return static_cast<data_type>(neighbor_indices.size()) / (count_radius * count_radius * count_radius);
100}
101
102template<typename DataType, typename KNN>
103bool agast_keypoint_extractor_t<DataType, KNN>::is_consecutive_arc(
104 const std::vector<bool>& brighter,
105 const std::vector<bool>& darker)
106{
107 const std::size_t n = brighter.size();
108
109 // Check for consecutive brighter points
110 std::size_t max_consecutive_bright = 0;
111 std::size_t current_consecutive_bright = 0;
112
113 for (std::size_t i = 0; i < 2 * n; ++i) { // Check twice to handle circular nature
114 if (brighter[i % n]) {
115 current_consecutive_bright++;
116 max_consecutive_bright = std::max(max_consecutive_bright, current_consecutive_bright);
117 } else {
118 current_consecutive_bright = 0;
119 }
120 }
121
122 if (max_consecutive_bright >= m_min_arc_length) {
123 return true;
124 }
125
126 // Check for consecutive darker points
127 std::size_t max_consecutive_dark = 0;
128 std::size_t current_consecutive_dark = 0;
129
130 for (std::size_t i = 0; i < 2 * n; ++i) { // Check twice to handle circular nature
131 if (darker[i % n]) {
132 current_consecutive_dark++;
133 max_consecutive_dark = std::max(max_consecutive_dark, current_consecutive_dark);
134 } else {
135 current_consecutive_dark = 0;
136 }
137 }
138
139 return max_consecutive_dark >= m_min_arc_length;
140}
141
142template<typename DataType, typename KNN>
143typename agast_keypoint_extractor_t<DataType, KNN>::AGASTInfo
144agast_keypoint_extractor_t<DataType, KNN>::compute_agast_response(std::size_t point_idx)
145{
146 if (!m_cloud || !m_knn || point_idx >= m_cloud->size() || m_test_pattern.empty()) {
147 return AGASTInfo{0, false};
148 }
149
150 const auto& center_point = m_cloud->points[point_idx];
151
152 // Compute center value
153 const data_type center_value = compute_test_value(center_point, TestPoint{0, 0, 0});
154
155 // Test all pattern points
156 std::vector<bool> brighter(m_test_pattern.size(), false);
157 std::vector<bool> darker(m_test_pattern.size(), false);
158 data_type score = 0;
159
160 for (std::size_t i = 0; i < m_test_pattern.size(); ++i) {
161 const data_type test_value = compute_test_value(center_point, m_test_pattern[i]);
162 const data_type diff = test_value - center_value;
163
164 if (diff > m_threshold) {
165 brighter[i] = true;
166 score += diff;
167 } else if (diff < -m_threshold) {
168 darker[i] = true;
169 score += -diff;
170 }
171 }
172
173 // Check if we have a consecutive arc of similar points
174 const bool is_keypoint = is_consecutive_arc(brighter, darker);
175
176 return AGASTInfo{score, is_keypoint};
177}
178
179template<typename DataType, typename KNN>
180void agast_keypoint_extractor_t<DataType, KNN>::compute_agast_range(
181 std::vector<AGASTInfo>& agast_responses,
182 std::size_t start_idx,
183 std::size_t end_idx)
184{
185 for (std::size_t i = start_idx; i < end_idx; ++i) {
186 agast_responses[i] = compute_agast_response(i);
187 }
188}
189
190template<typename DataType, typename KNN>
191std::vector<typename agast_keypoint_extractor_t<DataType, KNN>::AGASTInfo>
192agast_keypoint_extractor_t<DataType, KNN>::compute_all_agast_responses()
193{
194 if (!m_cloud) {
195 return {};
196 }
197
198 const std::size_t num_points = m_cloud->size();
199 std::vector<AGASTInfo> agast_responses(num_points);
200
201 if (m_enable_parallel && num_points > k_parallel_threshold) {
202 // Parallel computation
203 const std::size_t num_threads = toolbox::base::thread_pool_singleton_t::instance().get_thread_count();
204 const std::size_t chunk_size = (num_points + num_threads - 1) / num_threads;
205
206 std::vector<std::future<void>> futures;
207 for (std::size_t t = 0; t < num_threads; ++t) {
208 const std::size_t start_idx = t * chunk_size;
209 const std::size_t end_idx = std::min(start_idx + chunk_size, num_points);
210
211 if (start_idx < end_idx) {
212 futures.emplace_back(
214 [this, &agast_responses, start_idx, end_idx]() {
215 compute_agast_range(agast_responses, start_idx, end_idx);
216 }
217 )
218 );
219 }
220 }
221
222 // Wait for all threads to complete
223 for (auto& future : futures) {
224 future.wait();
225 }
226 } else {
227 // Sequential computation
228 compute_agast_range(agast_responses, 0, num_points);
229 }
230
231 return agast_responses;
232}
233
234template<typename DataType, typename KNN>
236agast_keypoint_extractor_t<DataType, KNN>::apply_non_maxima_suppression(
237 const std::vector<AGASTInfo>& agast_responses)
238{
239 if (!m_cloud || agast_responses.empty()) {
240 return {};
241 }
242
243 indices_vector keypoints;
244 const std::size_t num_points = m_cloud->size();
245
246 for (std::size_t i = 0; i < num_points; ++i) {
247 const auto& current_response = agast_responses[i];
248
249 // Skip non-keypoints
250 if (!current_response.is_keypoint || current_response.score <= 0) {
251 continue;
252 }
253
254 // Find neighbors within non-maxima suppression radius
255 const auto& query_point = m_cloud->points[i];
256 std::vector<std::size_t> neighbor_indices;
257 std::vector<data_type> neighbor_distances;
258
259 m_knn->radius_neighbors(query_point, m_non_maxima_radius, neighbor_indices, neighbor_distances);
260
261 // Check if current point is local maximum
262 bool is_local_maximum = true;
263 for (const auto& neighbor_idx : neighbor_indices) {
264 if (neighbor_idx != i && neighbor_idx < agast_responses.size()) {
265 if (agast_responses[neighbor_idx].is_keypoint &&
266 agast_responses[neighbor_idx].score > current_response.score) {
267 is_local_maximum = false;
268 break;
269 }
270 }
271 }
272
273 if (is_local_maximum) {
274 keypoints.push_back(i);
275 }
276 }
277
278 return keypoints;
279}
280
281template<typename DataType, typename KNN>
284{
285 if (!m_cloud || !m_knn) {
286 return {};
287 }
288
289 // Compute AGAST responses for all points
290 auto agast_responses = compute_all_agast_responses();
291
292 // Apply non-maxima suppression to find keypoints
293 return apply_non_maxima_suppression(agast_responses);
294}
295
296template<typename DataType, typename KNN>
298{
299 keypoint_indices = extract_impl();
300}
301
302template<typename DataType, typename KNN>
305{
306 auto keypoint_indices = extract_impl();
307
308 point_cloud keypoints;
309 keypoints.points.reserve(keypoint_indices.size());
310
311 for (const auto& idx : keypoint_indices) {
312 keypoints.points.push_back(m_cloud->points[idx]);
313 }
314
315 return keypoints;
316}
317
318template<typename DataType, typename KNN>
320{
321 auto keypoint_indices = extract_impl();
322
323 output->points.clear();
324 output->points.reserve(keypoint_indices.size());
325
326 for (const auto& idx : keypoint_indices) {
327 output->points.push_back(m_cloud->points[idx]);
328 }
329}
330
331} // namespace toolbox::pcl
static thread_pool_singleton_t & instance()
获取单例实例/Get the singleton instance
Definition thread_pool_singleton.hpp:23
AGAST (Adaptive and Generic Accelerated Segment Test) 3D关键点提取器 / AGAST (Adaptive and Generic Accelera...
Definition agast_keypoints.hpp:26
typename base_type::point_cloud_ptr point_cloud_ptr
Definition agast_keypoints.hpp:34
typename base_type::point_cloud point_cloud
Definition agast_keypoints.hpp:33
typename base_type::indices_vector indices_vector
Definition agast_keypoints.hpp:35
typename base_type::knn_type knn_type
Definition agast_keypoints.hpp:32
std::size_t set_input_impl(const point_cloud &cloud)
Definition agast_keypoints_impl.hpp:14
void enable_parallel_impl(bool enable)
Definition agast_keypoints_impl.hpp:48
point_cloud extract_keypoints_impl()
Definition agast_keypoints_impl.hpp:304
indices_vector extract_impl()
Definition agast_keypoints_impl.hpp:283
typename base_type::data_type data_type
Definition agast_keypoints.hpp:31
std::size_t set_knn_impl(const knn_type &knn)
Definition agast_keypoints_impl.hpp:30
std::size_t set_search_radius_impl(data_type radius)
Definition agast_keypoints_impl.hpp:40
Definition base_correspondence_generator.hpp:18