cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
harris3d_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 && m_knn) {
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 // Harris3D uses num_neighbors instead of radius for main computation
43 // But we can use radius for suppression
44 m_suppression_radius = radius;
45 return 0;
46}
47
48template<typename DataType, typename KNN>
50{
51 m_enable_parallel = enable;
52}
53
54template<typename DataType, typename KNN>
57{
58 if (!m_cloud || !m_knn || point_idx >= m_cloud->size()) {
59 return Harris3DInfo{0, false};
60 }
61
62 const auto& query_point = m_cloud->points[point_idx];
63 std::vector<std::size_t> neighbor_indices;
64 std::vector<data_type> neighbor_distances;
65
66 // Find k nearest neighbors
67 m_knn->kneighbors(query_point, m_num_neighbors, neighbor_indices, neighbor_distances);
68
69 if (neighbor_indices.size() < 3) {
70 return Harris3DInfo{0, false};
71 }
72
73 // Compute centroid
74 Eigen::Vector3d centroid(0, 0, 0);
75 for (const auto& idx : neighbor_indices) {
76 const auto& p = m_cloud->points[idx];
77 centroid += Eigen::Vector3d(static_cast<double>(p.x),
78 static_cast<double>(p.y),
79 static_cast<double>(p.z));
80 }
81 centroid /= static_cast<double>(neighbor_indices.size());
82
83 // Compute covariance matrix
84 Eigen::Matrix3d covariance = Eigen::Matrix3d::Zero();
85 for (const auto& idx : neighbor_indices) {
86 const auto& p = m_cloud->points[idx];
87 Eigen::Vector3d point_vec(static_cast<double>(p.x),
88 static_cast<double>(p.y),
89 static_cast<double>(p.z));
90 Eigen::Vector3d diff = point_vec - centroid;
91 covariance += diff * diff.transpose();
92 }
93 covariance /= static_cast<double>(neighbor_indices.size() - 1);
94
95 // Compute eigenvalues and eigenvectors
96 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver(covariance);
97 if (eigen_solver.info() != Eigen::Success) {
98 return Harris3DInfo{0, false};
99 }
100
101 const Eigen::Vector3d& eigenvalues = eigen_solver.eigenvalues();
102 const Eigen::Matrix3d& eigenvectors = eigen_solver.eigenvectors();
103
104 // Sort eigenvalues to get normal direction (smallest eigenvalue)
105 std::vector<std::pair<double, int>> sorted_eigenvals;
106 for (int j = 0; j < 3; ++j) {
107 sorted_eigenvals.emplace_back(eigenvalues(j), j);
108 }
109 std::sort(sorted_eigenvals.begin(), sorted_eigenvals.end());
110
111 // Get normal and tangent vectors
112 const int normal_idx = sorted_eigenvals[0].second;
113 const int tangent1_idx = sorted_eigenvals[1].second;
114 const int tangent2_idx = sorted_eigenvals[2].second;
115
116 const Eigen::Vector3d normal = eigenvectors.col(normal_idx);
117 const Eigen::Vector3d tangent1 = eigenvectors.col(tangent1_idx);
118 const Eigen::Vector3d tangent2 = eigenvectors.col(tangent2_idx);
119
120 // Project points onto tangent plane and compute 2D structure tensor
121 Eigen::Matrix2d structure_tensor = Eigen::Matrix2d::Zero();
122
123 for (const auto& idx : neighbor_indices) {
124 const auto& p = m_cloud->points[idx];
125 Eigen::Vector3d point_vec(static_cast<double>(p.x),
126 static_cast<double>(p.y),
127 static_cast<double>(p.z));
128 Eigen::Vector3d local_vec = point_vec - centroid;
129
130 // Project onto tangent plane
131 double u = local_vec.dot(tangent1);
132 double v = local_vec.dot(tangent2);
133
134 // Add to structure tensor
135 structure_tensor(0, 0) += u * u;
136 structure_tensor(0, 1) += u * v;
137 structure_tensor(1, 0) += u * v;
138 structure_tensor(1, 1) += v * v;
139 }
140
141 structure_tensor /= static_cast<double>(neighbor_indices.size());
142
143 // Compute Harris response: R = det(M) - k * trace(M)^2
144 const double det = structure_tensor.determinant();
145 const double trace = structure_tensor.trace();
146 const double harris_response = det - m_harris_k * trace * trace;
147
148 return Harris3DInfo{static_cast<data_type>(harris_response), true};
149}
150
151template<typename DataType, typename KNN>
152void harris3d_keypoint_extractor_t<DataType, KNN>::compute_harris_range(
153 std::vector<Harris3DInfo>& harris_responses,
154 std::size_t start_idx,
155 std::size_t end_idx)
156{
157 for (std::size_t i = start_idx; i < end_idx; ++i) {
158 harris_responses[i] = compute_harris3d_response(i);
159 }
160}
161
162template<typename DataType, typename KNN>
163std::vector<typename harris3d_keypoint_extractor_t<DataType, KNN>::Harris3DInfo>
164harris3d_keypoint_extractor_t<DataType, KNN>::compute_all_harris_responses()
165{
166 if (!m_cloud) {
167 return {};
168 }
169
170 const std::size_t num_points = m_cloud->size();
171 std::vector<Harris3DInfo> harris_responses(num_points);
172
173 if (m_enable_parallel && num_points > k_parallel_threshold) {
174 // Parallel computation
175 const std::size_t num_threads = toolbox::base::thread_pool_singleton_t::instance().get_thread_count();
176 const std::size_t chunk_size = (num_points + num_threads - 1) / num_threads;
177
178 std::vector<std::future<void>> futures;
179 for (std::size_t t = 0; t < num_threads; ++t) {
180 const std::size_t start_idx = t * chunk_size;
181 const std::size_t end_idx = std::min(start_idx + chunk_size, num_points);
182
183 if (start_idx < end_idx) {
184 futures.emplace_back(
186 [this, &harris_responses, start_idx, end_idx]() {
187 compute_harris_range(harris_responses, start_idx, end_idx);
188 }
189 )
190 );
191 }
192 }
193
194 // Wait for all threads to complete
195 for (auto& future : futures) {
196 future.wait();
197 }
198 } else {
199 // Sequential computation
200 compute_harris_range(harris_responses, 0, num_points);
201 }
202
203 return harris_responses;
204}
205
206template<typename DataType, typename KNN>
208harris3d_keypoint_extractor_t<DataType, KNN>::apply_non_maxima_suppression(
209 const std::vector<Harris3DInfo>& harris_responses)
210{
211 if (!m_cloud || harris_responses.empty()) {
212 return {};
213 }
214
215 indices_vector keypoints;
216 const std::size_t num_points = m_cloud->size();
217
218 for (std::size_t i = 0; i < num_points; ++i) {
219 const auto& current_response = harris_responses[i];
220
221 // Skip invalid points or those below threshold
222 if (!current_response.is_valid || current_response.harris_response < m_threshold) {
223 continue;
224 }
225
226 // Find neighbors within suppression radius
227 const auto& query_point = m_cloud->points[i];
228 std::vector<std::size_t> neighbor_indices;
229 std::vector<data_type> neighbor_distances;
230
231 m_knn->radius_neighbors(query_point, m_suppression_radius, neighbor_indices, neighbor_distances);
232
233 // Check if current point is local maximum
234 bool is_local_maximum = true;
235 for (const auto& neighbor_idx : neighbor_indices) {
236 if (neighbor_idx != i && neighbor_idx < harris_responses.size()) {
237 if (harris_responses[neighbor_idx].is_valid &&
238 harris_responses[neighbor_idx].harris_response > current_response.harris_response) {
239 is_local_maximum = false;
240 break;
241 }
242 }
243 }
244
245 if (is_local_maximum) {
246 keypoints.push_back(i);
247 }
248 }
249
250 return keypoints;
251}
252
253template<typename DataType, typename KNN>
256{
257 if (!m_cloud || !m_knn) {
258 return {};
259 }
260
261 // Compute Harris responses for all points
262 auto harris_responses = compute_all_harris_responses();
263
264 // Apply non-maxima suppression to find keypoints
265 return apply_non_maxima_suppression(harris_responses);
266}
267
268template<typename DataType, typename KNN>
270{
271 keypoint_indices = extract_impl();
272}
273
274template<typename DataType, typename KNN>
277{
278 auto keypoint_indices = extract_impl();
279
280 point_cloud keypoints;
281 keypoints.points.reserve(keypoint_indices.size());
282
283 for (const auto& idx : keypoint_indices) {
284 keypoints.points.push_back(m_cloud->points[idx]);
285 }
286
287 return keypoints;
288}
289
290template<typename DataType, typename KNN>
292{
293 auto keypoint_indices = extract_impl();
294
295 output->points.clear();
296 output->points.reserve(keypoint_indices.size());
297
298 for (const auto& idx : keypoint_indices) {
299 output->points.push_back(m_cloud->points[idx]);
300 }
301}
302
303} // namespace toolbox::pcl
static thread_pool_singleton_t & instance()
获取单例实例/Get the singleton instance
Definition thread_pool_singleton.hpp:23
Harris 3D 关键点提取器 / Harris 3D keypoint extractor.
Definition harris3d_keypoints.hpp:63
typename base_type::data_type data_type
Definition harris3d_keypoints.hpp:68
std::size_t set_input_impl(const point_cloud &cloud)
CRTP实现方法 - 设置输入点云 / CRTP implementation - set input point cloud.
Definition harris3d_keypoints_impl.hpp:16
std::size_t set_search_radius_impl(data_type radius)
CRTP实现方法 - 设置搜索半径 / CRTP implementation - set search radius.
Definition harris3d_keypoints_impl.hpp:40
std::size_t set_knn_impl(const knn_type &knn)
CRTP实现方法 - 设置KNN算法 / CRTP implementation - set KNN algorithm.
Definition harris3d_keypoints_impl.hpp:30
indices_vector extract_impl()
CRTP实现方法 - 提取关键点 / CRTP implementation - extract keypoints.
Definition harris3d_keypoints_impl.hpp:255
point_cloud extract_keypoints_impl()
Definition harris3d_keypoints_impl.hpp:276
typename base_type::indices_vector indices_vector
Definition harris3d_keypoints.hpp:72
typename base_type::knn_type knn_type
Definition harris3d_keypoints.hpp:69
typename base_type::point_cloud point_cloud
Definition harris3d_keypoints.hpp:70
void enable_parallel_impl(bool enable)
CRTP实现方法 - 启用并行处理 / CRTP implementation - enable parallel processing.
Definition harris3d_keypoints_impl.hpp:49
typename base_type::point_cloud_ptr point_cloud_ptr
Definition harris3d_keypoints.hpp:71
Definition base_correspondence_generator.hpp:18