cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
susan_keypoints_impl.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <Eigen/Dense>
6#include <Eigen/Eigenvalues>
7#include <algorithm>
8#include <cmath>
9#include <future>
10#include <vector>
11
12namespace toolbox::pcl
13{
14
15template<typename DataType, typename KNN>
17{
18 m_cloud = std::make_shared<point_cloud>(cloud);
19 return m_cloud->size();
20}
21
22template<typename DataType, typename KNN>
24{
25 m_cloud = cloud;
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->points);
35 }
36 return m_cloud ? m_cloud->size() : 0;
37}
38
39template<typename DataType, typename KNN>
41{
42 m_search_radius = radius;
43 return 0;
44}
45
46template<typename DataType, typename KNN>
48{
49 m_enable_parallel = enable;
50}
51
52template<typename DataType, typename KNN>
53std::vector<typename susan_keypoint_extractor_t<DataType, KNN>::NormalInfo>
55{
56 if (!m_cloud || !m_knn) {
57 return {};
58 }
59
60 const std::size_t num_points = m_cloud->size();
61 std::vector<NormalInfo> normals(num_points);
62
63 // Use PCA to compute normals
64 pca_norm_extractor_t<data_type, knn_type> norm_estimator;
65 norm_estimator.set_input(*m_cloud);
66 norm_estimator.set_num_neighbors(30); // Use fixed number of neighbors for normal estimation
67 norm_estimator.set_knn(*m_knn);
68
69 auto estimated_normals = norm_estimator.extract();
70
71 // Convert to our NormalInfo structure
72 for (std::size_t i = 0; i < num_points && i < estimated_normals.points.size(); ++i) {
73 const auto& n = estimated_normals.points[i];
74 normals[i] = NormalInfo{
75 static_cast<data_type>(n.x),
76 static_cast<data_type>(n.y),
77 static_cast<data_type>(n.z),
78 true
79 };
80
81 // Check for invalid normals
82 if (std::isnan(n.x) || std::isnan(n.y) || std::isnan(n.z)) {
83 normals[i].is_valid = false;
84 }
85 }
86
87 return normals;
88}
89
90template<typename DataType, typename KNN>
91typename susan_keypoint_extractor_t<DataType, KNN>::SUSANInfo
92susan_keypoint_extractor_t<DataType, KNN>::compute_susan_response(
93 std::size_t point_idx,
94 const std::vector<NormalInfo>& normals)
95{
96 if (!m_cloud || !m_knn || point_idx >= m_cloud->size()) {
97 return SUSANInfo{0, false};
98 }
99
100 const auto& query_point = m_cloud->points[point_idx];
101 std::vector<std::size_t> neighbor_indices;
102 std::vector<data_type> neighbor_distances;
103
104 // Find neighbors within search radius
105 m_knn->radius_neighbors(query_point, m_search_radius, neighbor_indices, neighbor_distances);
106
107 if (neighbor_indices.size() < 5) { // Need minimum neighbors
108 return SUSANInfo{0, false};
109 }
110
111 // SUSAN principle: count similar neighbors (USAN - Univalue Segment Assimilating Nucleus)
112 data_type susan_value = 0;
113 const data_type max_distance = m_search_radius;
114
115 if (m_use_normal_similarity && point_idx < normals.size() && normals[point_idx].is_valid) {
116 // Use normal-based similarity
117 const auto& center_normal = normals[point_idx];
118 const data_type center_norm = std::sqrt(
119 center_normal.nx * center_normal.nx +
120 center_normal.ny * center_normal.ny +
121 center_normal.nz * center_normal.nz
122 );
123
124 if (center_norm < 1e-6) {
125 return SUSANInfo{0, false};
126 }
127
128 for (std::size_t i = 0; i < neighbor_indices.size(); ++i) {
129 const std::size_t neighbor_idx = neighbor_indices[i];
130 if (neighbor_idx == point_idx || neighbor_idx >= normals.size() || !normals[neighbor_idx].is_valid) {
131 continue;
132 }
133
134 const auto& neighbor_normal = normals[neighbor_idx];
135
136 // Compute normal similarity (dot product)
137 const data_type dot_product =
138 center_normal.nx * neighbor_normal.nx +
139 center_normal.ny * neighbor_normal.ny +
140 center_normal.nz * neighbor_normal.nz;
141
142 // Angular similarity
143 if (dot_product > m_angular_threshold) {
144 // Geometric similarity (distance-based weight)
145 const data_type distance_ratio = neighbor_distances[i] / max_distance;
146 const data_type geometric_weight = std::exp(-distance_ratio * distance_ratio / (m_geometric_threshold * m_geometric_threshold));
147
148 susan_value += geometric_weight;
149 }
150 }
151 } else {
152 // Use geometric distance similarity only
153 for (std::size_t i = 0; i < neighbor_indices.size(); ++i) {
154 if (neighbor_indices[i] == point_idx) continue;
155
156 const data_type distance_ratio = neighbor_distances[i] / max_distance;
157 const data_type similarity = std::exp(-distance_ratio * distance_ratio / (m_geometric_threshold * m_geometric_threshold));
158
159 susan_value += similarity;
160 }
161 }
162
163 // Normalize by number of neighbors
164 if (neighbor_indices.size() > 1) {
165 susan_value /= static_cast<data_type>(neighbor_indices.size() - 1);
166 }
167
168 return SUSANInfo{susan_value, true};
169}
170
171template<typename DataType, typename KNN>
172void susan_keypoint_extractor_t<DataType, KNN>::compute_susan_range(
173 std::vector<SUSANInfo>& susan_responses,
174 const std::vector<NormalInfo>& normals,
175 std::size_t start_idx,
176 std::size_t end_idx)
177{
178 for (std::size_t i = start_idx; i < end_idx; ++i) {
179 susan_responses[i] = compute_susan_response(i, normals);
180 }
181}
182
183template<typename DataType, typename KNN>
184std::vector<typename susan_keypoint_extractor_t<DataType, KNN>::SUSANInfo>
185susan_keypoint_extractor_t<DataType, KNN>::compute_all_susan_responses(
186 const std::vector<NormalInfo>& normals)
187{
188 if (!m_cloud) {
189 return {};
190 }
191
192 const std::size_t num_points = m_cloud->size();
193 std::vector<SUSANInfo> susan_responses(num_points);
194
195 if (m_enable_parallel && num_points > k_parallel_threshold) {
196 // Parallel computation
197 const std::size_t num_threads = toolbox::base::thread_pool_singleton_t::instance().get_thread_count();
198 const std::size_t chunk_size = (num_points + num_threads - 1) / num_threads;
199
200 std::vector<std::future<void>> futures;
201 for (std::size_t t = 0; t < num_threads; ++t) {
202 const std::size_t start_idx = t * chunk_size;
203 const std::size_t end_idx = std::min(start_idx + chunk_size, num_points);
204
205 if (start_idx < end_idx) {
206 futures.emplace_back(
208 [this, &susan_responses, &normals, start_idx, end_idx]() {
209 compute_susan_range(susan_responses, normals, start_idx, end_idx);
210 }
211 )
212 );
213 }
214 }
215
216 // Wait for all threads to complete
217 for (auto& future : futures) {
218 future.wait();
219 }
220 } else {
221 // Sequential computation
222 compute_susan_range(susan_responses, normals, 0, num_points);
223 }
224
225 return susan_responses;
226}
227
228template<typename DataType, typename KNN>
230susan_keypoint_extractor_t<DataType, KNN>::apply_non_maxima_suppression(
231 const std::vector<SUSANInfo>& susan_responses)
232{
233 if (!m_cloud || susan_responses.empty()) {
234 return {};
235 }
236
237 indices_vector keypoints;
238 const std::size_t num_points = m_cloud->size();
239
240 for (std::size_t i = 0; i < num_points; ++i) {
241 const auto& current_response = susan_responses[i];
242
243 // Skip invalid points or those above threshold (high SUSAN value means flat region)
244 if (!current_response.is_valid || current_response.susan_value > m_susan_threshold) {
245 continue;
246 }
247
248 // Find neighbors within non-maxima suppression radius
249 const auto& query_point = m_cloud->points[i];
250 std::vector<std::size_t> neighbor_indices;
251 std::vector<data_type> neighbor_distances;
252
253 m_knn->radius_neighbors(query_point, m_non_maxima_radius, neighbor_indices, neighbor_distances);
254
255 // Check if current point is local minimum (for SUSAN, lower values indicate features)
256 bool is_local_minimum = true;
257 for (const auto& neighbor_idx : neighbor_indices) {
258 if (neighbor_idx != i && neighbor_idx < susan_responses.size()) {
259 if (susan_responses[neighbor_idx].is_valid &&
260 susan_responses[neighbor_idx].susan_value < current_response.susan_value) {
261 is_local_minimum = false;
262 break;
263 }
264 }
265 }
266
267 if (is_local_minimum) {
268 keypoints.push_back(i);
269 }
270 }
271
272 return keypoints;
273}
274
275template<typename DataType, typename KNN>
278{
279 if (!m_cloud || !m_knn) {
280 return {};
281 }
282
283 // Compute normals if using normal similarity
284 std::vector<NormalInfo> normals;
285 if (m_use_normal_similarity) {
286 normals = compute_normals();
287 }
288
289 // Compute SUSAN responses for all points
290 auto susan_responses = compute_all_susan_responses(normals);
291
292 // Apply non-maxima suppression to find keypoints
293 return apply_non_maxima_suppression(susan_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
SUSAN (Smallest Univalue Segment Assimilating Nucleus) 3D关键点提取器 / SUSAN (Smallest Univalue Segment As...
Definition susan_keypoints.hpp:27
std::size_t set_search_radius_impl(data_type radius)
Definition susan_keypoints_impl.hpp:40
std::size_t set_input_impl(const point_cloud &cloud)
Definition susan_keypoints_impl.hpp:16
indices_vector extract_impl()
Definition susan_keypoints_impl.hpp:277
void enable_parallel_impl(bool enable)
Definition susan_keypoints_impl.hpp:47
typename base_type::data_type data_type
Definition susan_keypoints.hpp:32
point_cloud extract_keypoints_impl()
Definition susan_keypoints_impl.hpp:304
typename base_type::point_cloud point_cloud
Definition susan_keypoints.hpp:34
typename base_type::knn_type knn_type
Definition susan_keypoints.hpp:33
typename base_type::point_cloud_ptr point_cloud_ptr
Definition susan_keypoints.hpp:35
typename base_type::indices_vector indices_vector
Definition susan_keypoints.hpp:36
std::size_t set_knn_impl(const knn_type &knn)
Definition susan_keypoints_impl.hpp:30
Definition base_correspondence_generator.hpp:18