cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
sift3d_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 // Use radius as base scale
43 m_base_scale = radius;
44 return 0;
45}
46
47template<typename DataType, typename KNN>
49{
50 m_enable_parallel = enable;
51}
52
53template<typename DataType, typename KNN>
55 std::vector<std::vector<data_type>>& scale_space,
56 std::size_t start_idx,
57 std::size_t end_idx)
58{
59 for (std::size_t scale_idx = 0; scale_idx < m_num_scales; ++scale_idx) {
60 const data_type current_scale = m_base_scale * std::pow(m_scale_factor, scale_idx);
61
62 for (std::size_t i = start_idx; i < end_idx; ++i) {
63 const auto& query_point = m_cloud->points[i];
64
65 // Find neighbors within the current scale radius
66 std::vector<std::size_t> neighbor_indices;
67 std::vector<data_type> neighbor_distances;
68
69 m_knn->radius_neighbors(query_point, current_scale * 3.0, neighbor_indices, neighbor_distances);
70
71 if (neighbor_indices.size() < 3) {
72 scale_space[scale_idx][i] = 0.0;
73 continue;
74 }
75
76 // Compute local density using Gaussian weighting
77 data_type density_current = 0.0;
78 data_type density_prev = 0.0;
79
80 const data_type sigma_current = current_scale;
81 const data_type sigma_prev = current_scale / m_scale_factor;
82
83 for (std::size_t j = 0; j < neighbor_indices.size(); ++j) {
84 const data_type dist = neighbor_distances[j];
85
86 // Gaussian weights
87 const data_type weight_current = std::exp(-0.5 * (dist * dist) / (sigma_current * sigma_current));
88 const data_type weight_prev = std::exp(-0.5 * (dist * dist) / (sigma_prev * sigma_prev));
89
90 density_current += weight_current;
91 density_prev += weight_prev;
92 }
93
94 // Difference of Gaussians
95 scale_space[scale_idx][i] = density_current - density_prev;
96 }
97 }
98}
99
100template<typename DataType, typename KNN>
101std::vector<std::vector<typename sift3d_keypoint_extractor_t<DataType, KNN>::data_type>>
102sift3d_keypoint_extractor_t<DataType, KNN>::build_scale_space()
103{
104 if (!m_cloud || !m_knn) {
105 return {};
106 }
107
108 const std::size_t num_points = m_cloud->size();
109 std::vector<std::vector<data_type>> scale_space(m_num_scales);
110
111 for (auto& scale_level : scale_space) {
112 scale_level.resize(num_points, 0.0);
113 }
114
115 if (m_enable_parallel && num_points > k_parallel_threshold) {
116 // Parallel computation
117 const std::size_t num_threads = toolbox::base::thread_pool_singleton_t::instance().get_thread_count();
118 const std::size_t chunk_size = (num_points + num_threads - 1) / num_threads;
119
120 std::vector<std::future<void>> futures;
121 for (std::size_t t = 0; t < num_threads; ++t) {
122 const std::size_t start_idx = t * chunk_size;
123 const std::size_t end_idx = std::min(start_idx + chunk_size, num_points);
124
125 if (start_idx < end_idx) {
126 futures.emplace_back(
128 [this, &scale_space, start_idx, end_idx]() {
129 compute_scale_space_range(scale_space, start_idx, end_idx);
130 }
131 )
132 );
133 }
134 }
135
136 // Wait for all threads to complete
137 for (auto& future : futures) {
138 future.wait();
139 }
140 } else {
141 // Sequential computation
142 compute_scale_space_range(scale_space, 0, num_points);
143 }
144
145 return scale_space;
146}
147
148template<typename DataType, typename KNN>
149std::vector<typename sift3d_keypoint_extractor_t<DataType, KNN>::ScaleSpacePoint>
150sift3d_keypoint_extractor_t<DataType, KNN>::find_scale_space_extrema(
151 const std::vector<std::vector<data_type>>& scale_space)
152{
153 const std::size_t num_points = m_cloud->size();
154 std::vector<ScaleSpacePoint> extrema;
155
156 // Check for extrema across scales (excluding first and last scale)
157 for (std::size_t scale_idx = 1; scale_idx < m_num_scales - 1; ++scale_idx) {
158 for (std::size_t point_idx = 0; point_idx < num_points; ++point_idx) {
159 const data_type current_value = scale_space[scale_idx][point_idx];
160
161 // Check if it's a local extremum across scales
162 const data_type prev_scale_value = scale_space[scale_idx - 1][point_idx];
163 const data_type next_scale_value = scale_space[scale_idx + 1][point_idx];
164
165 bool is_max = (current_value > prev_scale_value) && (current_value > next_scale_value);
166 bool is_min = (current_value < prev_scale_value) && (current_value < next_scale_value);
167
168 if (!is_max && !is_min) {
169 continue;
170 }
171
172 // Check spatial neighbors at the same scale
173 const auto& query_point = m_cloud->points[point_idx];
174 std::vector<std::size_t> neighbor_indices;
175 std::vector<data_type> neighbor_distances;
176
177 const data_type search_radius = m_base_scale * std::pow(m_scale_factor, scale_idx);
178 m_knn->radius_neighbors(query_point, search_radius, neighbor_indices, neighbor_distances);
179
180 bool is_spatial_extremum = true;
181 for (const auto& neighbor_idx : neighbor_indices) {
182 if (neighbor_idx == point_idx) continue;
183
184 const data_type neighbor_value = scale_space[scale_idx][neighbor_idx];
185
186 if (is_max && neighbor_value > current_value) {
187 is_spatial_extremum = false;
188 break;
189 }
190 if (is_min && neighbor_value < current_value) {
191 is_spatial_extremum = false;
192 break;
193 }
194 }
195
196 if (is_spatial_extremum && std::abs(current_value) > m_contrast_threshold) {
197 extrema.push_back(ScaleSpacePoint{point_idx, scale_idx, current_value, true});
198 }
199 }
200 }
201
202 return extrema;
203}
204
205template<typename DataType, typename KNN>
207sift3d_keypoint_extractor_t<DataType, KNN>::refine_keypoints(
208 const std::vector<ScaleSpacePoint>& extrema)
209{
210 indices_vector refined_keypoints;
211
212 for (const auto& extremum : extrema) {
213 if (extremum.is_extremum && std::abs(extremum.response) > m_contrast_threshold) {
214 refined_keypoints.push_back(extremum.point_idx);
215 }
216 }
217
218 // Remove duplicates
219 std::sort(refined_keypoints.begin(), refined_keypoints.end());
220 refined_keypoints.erase(std::unique(refined_keypoints.begin(), refined_keypoints.end()),
221 refined_keypoints.end());
222
223 return refined_keypoints;
224}
225
226template<typename DataType, typename KNN>
228sift3d_keypoint_extractor_t<DataType, KNN>::remove_edge_responses(
229 const indices_vector& keypoint_indices)
230{
231 indices_vector final_keypoints;
232
233 for (const auto& point_idx : keypoint_indices) {
234 const auto& query_point = m_cloud->points[point_idx];
235
236 // Find local neighborhood
237 std::vector<std::size_t> neighbor_indices;
238 std::vector<data_type> neighbor_distances;
239
240 m_knn->kneighbors(query_point, m_num_neighbors, neighbor_indices, neighbor_distances);
241
242 if (neighbor_indices.size() < 3) {
243 continue;
244 }
245
246 // Compute local structure tensor
247 Eigen::Vector3d centroid(0, 0, 0);
248 for (const auto& idx : neighbor_indices) {
249 const auto& p = m_cloud->points[idx];
250 centroid += Eigen::Vector3d(static_cast<double>(p.x),
251 static_cast<double>(p.y),
252 static_cast<double>(p.z));
253 }
254 centroid /= static_cast<double>(neighbor_indices.size());
255
256 Eigen::Matrix3d covariance = Eigen::Matrix3d::Zero();
257 for (const auto& idx : neighbor_indices) {
258 const auto& p = m_cloud->points[idx];
259 Eigen::Vector3d point_vec(static_cast<double>(p.x),
260 static_cast<double>(p.y),
261 static_cast<double>(p.z));
262 Eigen::Vector3d diff = point_vec - centroid;
263 covariance += diff * diff.transpose();
264 }
265 covariance /= static_cast<double>(neighbor_indices.size() - 1);
266
267 // Compute eigenvalues
268 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver(covariance);
269 if (eigen_solver.info() != Eigen::Success) {
270 continue;
271 }
272
273 const Eigen::Vector3d& eigenvalues = eigen_solver.eigenvalues();
274
275 // Sort eigenvalues in descending order
276 std::vector<double> sorted_eigenvals = {eigenvalues(2), eigenvalues(1), eigenvalues(0)};
277 std::sort(sorted_eigenvals.rbegin(), sorted_eigenvals.rend());
278
279 const double lambda_max = sorted_eigenvals[0];
280 const double lambda_min = sorted_eigenvals[2];
281
282 // Edge response test: ratio of largest to smallest eigenvalue
283 if (lambda_min > 1e-10) {
284 const double edge_ratio = lambda_max / lambda_min;
285
286 if (edge_ratio < m_edge_threshold) {
287 final_keypoints.push_back(point_idx);
288 }
289 }
290 }
291
292 return final_keypoints;
293}
294
295template<typename DataType, typename KNN>
298{
299 if (!m_cloud || !m_knn) {
300 return {};
301 }
302
303 // Step 1: Build scale space
304 auto scale_space = build_scale_space();
305
306 // Step 2: Find scale-space extrema
307 auto extrema = find_scale_space_extrema(scale_space);
308
309 // Step 3: Refine keypoint locations
310 auto refined_keypoints = refine_keypoints(extrema);
311
312 // Step 4: Remove edge responses
313 return remove_edge_responses(refined_keypoints);
314}
315
316template<typename DataType, typename KNN>
318{
319 keypoint_indices = extract_impl();
320}
321
322template<typename DataType, typename KNN>
325{
326 auto keypoint_indices = extract_impl();
327
328 point_cloud keypoints;
329 keypoints.points.reserve(keypoint_indices.size());
330
331 for (const auto& idx : keypoint_indices) {
332 keypoints.points.push_back(m_cloud->points[idx]);
333 }
334
335 return keypoints;
336}
337
338template<typename DataType, typename KNN>
340{
341 auto keypoint_indices = extract_impl();
342
343 output->points.clear();
344 output->points.reserve(keypoint_indices.size());
345
346 for (const auto& idx : keypoint_indices) {
347 output->points.push_back(m_cloud->points[idx]);
348 }
349}
350
351} // namespace toolbox::pcl
static thread_pool_singleton_t & instance()
获取单例实例/Get the singleton instance
Definition thread_pool_singleton.hpp:23
SIFT 3D (Scale-Invariant Feature Transform) 关键点提取器 / SIFT 3D (Scale-Invariant Feature Transform) keyp...
Definition sift3d_keypoints.hpp:26
std::size_t set_input_impl(const point_cloud &cloud)
Definition sift3d_keypoints_impl.hpp:16
typename base_type::point_cloud point_cloud
Definition sift3d_keypoints.hpp:33
typename base_type::knn_type knn_type
Definition sift3d_keypoints.hpp:32
typename base_type::point_cloud_ptr point_cloud_ptr
Definition sift3d_keypoints.hpp:34
std::size_t set_search_radius_impl(data_type radius)
Definition sift3d_keypoints_impl.hpp:40
typename base_type::data_type data_type
Definition sift3d_keypoints.hpp:31
indices_vector extract_impl()
Definition sift3d_keypoints_impl.hpp:297
typename base_type::indices_vector indices_vector
Definition sift3d_keypoints.hpp:35
point_cloud extract_keypoints_impl()
Definition sift3d_keypoints_impl.hpp:324
void enable_parallel_impl(bool enable)
Definition sift3d_keypoints_impl.hpp:48
std::size_t set_knn_impl(const knn_type &knn)
Definition sift3d_keypoints_impl.hpp:30
Definition base_correspondence_generator.hpp:18