iterative closest points
CEV ICP algorithm library
Loading...
Searching...
No Matches
feature_aware.cpp
Go to the documentation of this file.
1
6#include <Eigen/Core>
7#include <Eigen/SVD>
8#include <Eigen/Dense>
9
10/* #name Feature-Aware */
11/* #register feature_aware */
12
13/* #desc Builds on top of \ref trimmed_icp. In addition to matching points based on a point-to-point
14distance criteria, matches them based on a local "feature vector."
15*/
16
18
19namespace icp {
20 FeatureAware::FeatureAware(double overlap_rate, double feature_weight, int symmetric_neighbors)
21 : ICP(),
22 overlap_rate(overlap_rate),
23 symmetric_neighbors(symmetric_neighbors),
24 feature_weight(feature_weight),
25 neighbor_weight(1 - feature_weight) {}
26
27 /* #conf "overlap_rate" A `double` between `0.0` and `1.0` for the overlap rate. The default is
28 * `1.0`. */
29 /* #conf "feature_weight" A `double` between `0.0` and `1.0` with default value `0.7`. Decides
30 * how much to weight feature cost versus point-to-point cost. */
31 /* #conf "symmetric_neighbors" An `int` with default value `10`. Decides how many neighbors to
32 * use on each side of a point when constructing the feature vector. */
34 : FeatureAware(config.get<double>("overlap_rate", 0.9),
35 config.get<double>("feature_weight", 0.7),
36 config.get<int>("symmetric_neighbors", 10)) {}
37
39
41 a_current.resize(a.size());
42 a_features.resize(a.size());
43 b_features.resize(b.size());
44
45 b_cm = get_centroid(b);
46
47 compute_features(a, get_centroid(a), a_features);
48 compute_features(b, b_cm, b_features);
49
50 norm_feature_dists = compute_norm_dists(a_features, b_features);
51
52 compute_matches();
53 }
54
56 const size_t n = a.size();
57
58 for (size_t i = 0; i < n; i++) {
59 a_current[i] = transform.apply_to(a[i]);
60 }
61
62 /*
63 #step
64 Matching Step:
65
66 Matches are computed based on a weighted average of the point-to-point cost metric and
67 the feature cost metric (with weight `feature_weight` given to the feature vector
68 cost metric).
69
70 The feature vector for each point is computed as follows. Let \f$ p_i \f$ be the i-th
71 point in the scan, ordered by angle from the scan origin (LiDAR center), and let \f$ c
72 \f$ be the centroid of the scan. Then, we can define \f$ q_i = p_i - c \f$. Also, let \f$
73 n \f$ be the number of `symmetric_neighbors`. The feature vector for \f$ p_k \f$ is then
74 \f$ f_k = \begin{bmatrix} |q_{k - n}| - |q_k| & \ldots & |q_{k - 1}| - |q_k| & |q_{k+1}|
75 - |q_k| & \ldots & |q_{k + n}| - |q_k| \end{bmatrix} \f$. The feature cost metric between
76 two feature vectors \f$ f_a \f$ and \f$ f_b \f$ is simply \f$ |f_a - f_b| \f$.
77 */
78 compute_matches();
79
80 /*
81 #step
82 Trimming Step: see \ref trimmed_icp for details.
83 */
84 std::sort(matches.begin(), matches.end(),
85 [](const auto& a, const auto& b) { return a.cost < b.cost; });
86 size_t new_n = static_cast<size_t>(overlap_rate * n);
87 new_n = std::max<size_t>(new_n, 1); // TODO: bad for scans with 0 points
88
89 // yeah, i know this is inefficient. we'll get back to it later.
90 std::vector<icp::Vector> trimmed_current(new_n);
91 std::vector<icp::Vector> trimmed_b(new_n);
92 for (size_t i = 0; i < new_n; i++) {
93 trimmed_current[i] = a_current[matches[i].point];
94 trimmed_b[i] = b[matches[i].pair];
95 }
96
97 icp::Vector trimmed_cm = get_centroid(trimmed_current);
98 icp::Vector trimmed_b_cm = get_centroid(trimmed_b);
99
100 /* #step SVD: see \ref vanilla_icp for details. */
101 Matrix N = Matrix::Zero();
102 for (size_t i = 0; i < new_n; i++) {
103 N += (trimmed_current[i] - trimmed_cm) * (trimmed_b[i] - trimmed_b_cm).transpose();
104 }
105
106 auto svd = N.jacobiSvd(Eigen::ComputeFullU | Eigen::ComputeFullV);
107 Matrix U = svd.matrixU();
108 Matrix V = svd.matrixV();
109 Matrix R = V * U.transpose();
110
111 /* #step Reflection Handling: see \ref vanilla_icp for details. */
112 if (R.determinant() < 0) {
113 V = V * Eigen::DiagonalMatrix<double, 2>(1, -1);
114 R = V * U.transpose();
115 }
116
117 /* #step Transformation Step: see \ref vanilla_icp for details. */
118 RBTransform step(trimmed_b_cm - R * trimmed_cm, R);
119
121 }
122
123 void FeatureAware::compute_matches() {
124 const size_t n = a.size();
125 const size_t m = b.size();
126
127 Eigen::MatrixXd norm_dists = compute_norm_dists(a_current, b);
128
129 for (size_t i = 0; i < n; i++) {
130 matches[i].point = i;
131 matches[i].cost = std::numeric_limits<double>::infinity();
132 for (size_t j = 0; j < m; j++) {
133 double dist = norm_dists(i, j);
134 double feature_dist = norm_feature_dists(i, j);
135 double cost = neighbor_weight * dist + feature_weight * feature_dist;
136
137 if (cost < matches[i].cost) {
138 matches[i].cost = cost;
139 matches[i].pair = j;
140 }
141 }
142 }
143 }
144
145 void FeatureAware::compute_features(const std::vector<Vector>& points, Vector cm,
146 std::vector<FeatureVector>& features) {
147 for (size_t i = 0; i < points.size(); i++) {
148 Vector p = points[i];
149 double cm_dist_p = (p - cm).norm();
150
151 FeatureVector feature(2 * symmetric_neighbors);
152 feature.setZero();
153
154 size_t lower = std::max((size_t)0, i - symmetric_neighbors);
155 for (size_t j = lower; j < i; j++) {
156 Vector q = points[j];
157 double cm_dist_q = (q - cm).norm();
158 feature[j - lower] = cm_dist_q - cm_dist_p;
159 }
160
161 size_t upper = std::min(points.size() - 1, i + symmetric_neighbors);
162 for (size_t j = i + 1; j <= upper; j++) {
163 Vector q = points[j];
164 double cm_dist_q = (q - cm).norm();
165 feature[j - i - 1 + symmetric_neighbors] = cm_dist_q - cm_dist_p;
166 }
167
168 features[i] = feature;
169 }
170 }
171}
FeatureAware(double overlap_rate, double feature_weight, int symmetric_neighbors)
void setup() override
Per-method setup code.
void iterate() override
Perform one iteration of ICP for the point clouds a and b provided with ICP::begin.
Configuration for ICP instances.
Definition icp.h:81
Interface for iterative closest points.
Definition icp.h:49
RBTransform transform
The current point cloud transformation that is being optimized.
Definition icp.h:59
std::vector< Vector > a
The source point cloud.
Definition icp.h:62
std::vector< Vector > b
The destination point cloud.
Definition icp.h:65
std::vector< Match > matches
The pairing of each point in a to its closest in b.
Definition icp.h:68
Definition driver.h:12
Eigen::Vector2d Vector
Definition geo.h:15
Eigen::Matrix2d Matrix
Definition geo.h:16
Vector get_centroid(const std::vector< Vector > &points)
Definition geo.cpp:11
Rigid-body transformation.
Definition geo.h:19
RBTransform and_then(const RBTransform &next) const
Definition geo.h:36
Vector apply_to(Vector v) const
Definition geo.h:32