Matcher_Adaptive.cpp
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  * A repertory of multi primitive-to-primitive (MP2P) ICP algorithms in C++
3  * Copyright (C) 2018-2024 Jose Luis Blanco, University of Almeria
4  * See LICENSE for license information.
5  * ------------------------------------------------------------------------- */
15 #include <mrpt/core/exceptions.h>
16 #include <mrpt/core/round.h>
17 #include <mrpt/math/CHistogram.h> // CHistogram
18 #include <mrpt/math/distributions.h> // confidenceIntervalsFromHistogram()
19 #include <mrpt/version.h>
20 
22 
23 using namespace mp2p_icp;
24 
25 void Matcher_Adaptive::initialize(const mrpt::containers::yaml& params)
26 {
28 
29  MCP_LOAD_REQ(params, confidenceInterval);
30  MCP_LOAD_REQ(params, firstToSecondDistanceMax);
31 
32  MCP_LOAD_REQ(params, absoluteMaxSearchDistance);
33  MCP_LOAD_OPT(params, minimumCorrDist);
34 
35  MCP_LOAD_REQ(params, enableDetectPlanes);
36 
37  MCP_LOAD_OPT(params, planeSearchPoints);
38  MCP_LOAD_OPT(params, planeMinimumFoundPoints);
39  MCP_LOAD_OPT(params, planeEigenThreshold);
40  MCP_LOAD_OPT(params, maxPt2PtCorrespondences);
41  MCP_LOAD_OPT(params, planeMinimumDistance);
42 
43  ASSERT_LT_(confidenceInterval, 1.0);
44  ASSERT_GT_(confidenceInterval, 0.0);
45 
47  ASSERT_GE_(planeMinimumFoundPoints, 3);
48 
49  ASSERT_GT_(planeEigenThreshold, 0.0);
50 }
51 
53  const mrpt::maps::CMetricMap& pcGlobalMap,
54  const mrpt::maps::CPointsMap& pcLocal,
55  const mrpt::poses::CPose3D& localPose, MatchState& ms,
56  const layer_name_t& globalName, const layer_name_t& localName,
57  Pairings& out) const
58 {
59  MRPT_START
60 
61  const mrpt::maps::NearestNeighborsCapable& nnGlobal =
62  *mp2p_icp::MapToNN(pcGlobalMap, true /*throw if cannot convert*/);
63 
64  out.potential_pairings += pcLocal.size() * maxPt2PtCorrespondences;
65 
66  // Empty maps? Nothing to do
67  if (pcGlobalMap.isEmpty() || pcLocal.empty()) return;
68 
70  pcLocal, localPose, maxLocalPointsPerLayer_, localPointsSampleSeed_);
71 
72  // Try to do matching only if the bounding boxes have some overlap:
73  if (!pcGlobalMap.boundingBox().intersection(
74  {tl.localMin, tl.localMax},
75  /* threshold?? */ +bounding_box_intersection_check_epsilon_))
76  return;
77 
78  // Prepare output: no correspondences initially:
79  out.paired_pt2pt.reserve(out.paired_pt2pt.size() + pcLocal.size());
80 
81  // Loop for each point in local map:
82  // --------------------------------------------------
83  const float absoluteMaxDistSqr = mrpt::square(absoluteMaxSearchDistance);
84 
85  const auto& lxs = pcLocal.getPointsBufferRef_x();
86  const auto& lys = pcLocal.getPointsBufferRef_y();
87  const auto& lzs = pcLocal.getPointsBufferRef_z();
88 
89  // In order to find the closest association for each global point, we must
90  // first build this temporary list of *potential* associations, indexed by
91  // global point indices, and sorted by errSqr:
92 
93  // List of all 1st and 2nd closest pairings to each
94  matchesPerLocal_.clear();
95  matchesPerLocal_.resize(tl.x_locals.size());
96 
97  // Calculate limits for the histogram:
98  std::optional<float> minSqrErrorForHistogram;
99  std::optional<float> maxSqrErrorForHistogram;
100 
101  const auto lambdaAddPair =
102  [&](const size_t localIdx, const mrpt::math::TPoint3Df& globalPt,
103  const uint64_t globalIdxOrID, const float errSqr)
104  {
105  auto& ps = matchesPerLocal_.at(localIdx);
106  if (ps.size() >= MAX_CORRS_PER_LOCAL) return;
107 
108  mrpt::tfest::TMatchingPair p;
109  p.globalIdx = globalIdxOrID;
110  p.localIdx = localIdx;
111  p.global = globalPt;
112  p.local = {lxs[localIdx], lys[localIdx], lzs[localIdx]};
113  p.errorSquareAfterTransformation = errSqr;
114 
115  ps.push_back(p);
116  };
117 
118  const uint32_t nn_search_max_points =
120 
121  for (size_t i = 0; i < tl.x_locals.size(); i++)
122  {
123  const size_t localIdx = tl.idxs.has_value() ? (*tl.idxs)[i] : i;
124 
126  ms.localPairedBitField.point_layers.at(localName)[localIdx])
127  {
128  // skip, already paired, e.g. by another Matcher in the pipeline
129  // before me:
130  continue;
131  }
132 
133  const float lx = tl.x_locals[i], ly = tl.y_locals[i],
134  lz = tl.z_locals[i];
135 
136  // Use a KD-tree to look for the nearnest neighbor(s) of
137  // (x_local, y_local, z_local) in the global map:
138  if (nn_search_max_points == 1)
139  {
140  neighborSqrDists_.resize(1);
141  neighborIndices_.resize(1);
142  neighborPts_.resize(1);
143 
144  if (!nnGlobal.nn_single_search(
145  {lx, ly, lz}, // Look closest to this guy
147  {
148  neighborPts_.clear();
149  neighborSqrDists_.clear();
150  neighborIndices_.clear();
151  }
152  }
153  else
154  {
155  nnGlobal.nn_radius_search(
156  {lx, ly, lz}, // Look closest to this guy
157  absoluteMaxDistSqr, neighborPts_, neighborSqrDists_,
158  neighborIndices_, nn_search_max_points);
159  }
160 
161  for (size_t k = 0; k < neighborIndices_.size(); k++)
162  {
163  const auto tentativeErrSqr = neighborSqrDists_.at(k);
164 
165  if (tentativeErrSqr > absoluteMaxDistSqr) continue;
166 
167  if (k <= 1)
168  {
169  // keep max of 1st and 2nd closest point errors for the
170  // histogram:
171  if (maxSqrErrorForHistogram)
172  {
173  mrpt::keep_max(*maxSqrErrorForHistogram, tentativeErrSqr);
174  mrpt::keep_min(*minSqrErrorForHistogram, tentativeErrSqr);
175  }
176  else
177  {
178  maxSqrErrorForHistogram = tentativeErrSqr;
179  minSqrErrorForHistogram = tentativeErrSqr;
180  }
181  }
182 
183  lambdaAddPair(
184  localIdx, neighborPts_.at(k), neighborIndices_.at(k),
185  tentativeErrSqr);
186  }
187 
188  } // For each local point
189 
190  // Now, estimate the probability distribution (histogram) of the
191  // 1st/2nd points:
192  mrpt::math::CHistogram hist(
193  *minSqrErrorForHistogram, *maxSqrErrorForHistogram, 50);
194 
195  for (const auto& mspl : matchesPerLocal_)
196  for (size_t i = 0; i < std::min(mspl.size(), 2UL); i++)
197  hist.add(mspl[i].errorSquareAfterTransformation);
198 
199  hist.getHistogramNormalized(histXs_, histValues_);
200 
201  double ci_low = 0, ci_high = 0;
202  mrpt::math::confidenceIntervalsFromHistogram(
203  histXs_, histValues_, ci_low, ci_high, 1.0 - confidenceInterval);
204 
205 #if 0
206  printf("Histograms:\n");
207  for (auto v : histXs_) printf("%.02f ", v);
208  printf("\n");
209  for (auto v : histValues_) printf("%.02f ", v);
210  printf("\n");
211  printf(
212  "[MatcherAdaptive] CI_HIGH: %.03f => threshold=%.03f m nCorrs=%zu\n",
213  ci_high, std::sqrt(ci_high), matchesPerLocal_.size());
214 #endif
215 
216  // Take the confidence interval limit as the definitive maximum squared
217  // distance for correspondences:
218  const double maxCorrDistSqr =
219  std::max(mrpt::square(minimumCorrDist), ci_high);
220 
221  const float maxSqr1to2 = mrpt::square(firstToSecondDistanceMax);
222 
223  // Now, process candidates pairing and store them in `out.paired_pt2pt`:
224  for (const auto& mspl : matchesPerLocal_)
225  {
226  // Check for a potential plane?
227  // minimum: 3 points to be able to fit a plane
228  if (enableDetectPlanes && mspl.size() >= planeMinimumFoundPoints)
229  {
230  kddXs.clear();
231  kddYs.clear();
232  kddZs.clear();
233  for (const auto& p : mspl)
234  {
235  kddXs.push_back(p.global.x);
236  kddYs.push_back(p.global.y);
237  kddZs.push_back(p.global.z);
238  }
239 
241  kddXs.data(), kddYs.data(), kddZs.data(), std::nullopt,
242  kddXs.size());
243 
244  // e0/e2 must be < planeEigenThreshold:
245  if (eig.eigVals[0] < planeEigenThreshold * eig.eigVals[2] &&
246  eig.eigVals[0] < planeEigenThreshold * eig.eigVals[1])
247  {
248  const auto& normal = eig.eigVectors[0];
249  const mrpt::math::TPoint3D planeCentroid = {
250  eig.meanCov.mean.x(), eig.meanCov.mean.y(),
251  eig.meanCov.mean.z()};
252 
253  const auto thePlane = mrpt::math::TPlane(planeCentroid, normal);
254  const double ptPlaneDist =
255  std::abs(thePlane.distance(mspl.at(0).local));
256 
257  if (ptPlaneDist < planeMinimumDistance)
258  {
259  const auto localIdx = mspl.at(0).localIdx;
260 
261  // OK, all conditions pass: add the new pairing:
262  auto& p = out.paired_pt2pl.emplace_back();
263  p.pt_local = {lxs[localIdx], lys[localIdx], lzs[localIdx]};
264  p.pl_global.centroid = planeCentroid;
265 
266  p.pl_global.plane = thePlane;
267 
268  // Mark local point as already paired:
269  ms.localPairedBitField.point_layers[localName].mark_as_set(
270  localIdx);
271 
272  // all good with this local point:
273  continue;
274  }
275  }
276  }
277 
278  for (size_t i = 0;
279  i < std::min<size_t>(mspl.size(), maxPt2PtCorrespondences); i++)
280  {
281  const auto& p = mspl.at(i);
282  const auto globalIdx = p.globalIdx;
283 
284  if (!allowMatchAlreadyMatchedGlobalPoints_ &&
285  ms.globalPairedBitField.point_layers.at(globalName)[globalIdx])
286  continue; // skip, global point already paired.
287 
288  // too large error for the adaptive threshold?
289  if (p.errorSquareAfterTransformation >= maxCorrDistSqr) continue;
290 
291  if (i != 0 &&
292  mspl[i].errorSquareAfterTransformation >
293  mspl[0].errorSquareAfterTransformation * maxSqr1to2)
294  {
295  break;
296  }
297 
298  out.paired_pt2pt.emplace_back(p);
299 
300  // Mark local & global points as already paired:
301  if (!allowMatchAlreadyMatchedGlobalPoints_)
302  {
303  const auto localIdx = p.localIdx;
304  ms.localPairedBitField.point_layers[localName].mark_as_set(
305  localIdx);
306  }
307  }
308  }
309 
310  // Update adaptive maximum range:
311  // absoluteMaxSearchDistance = std::sqrt(ci_high) + minimumCorrDist;
312 
313 // Now, mark global idxs as used. It's done after the loop above
314 // to allow multiple local -> global pairs with the same global.
315 #if 0
316  // Mark local & global points as already paired:
317  ms.globalPairedBitField.point_layers[globalName].mark_as_set(
318  globalIdx);
319 #endif
320 
321  MRPT_END
322 }
mp2p_icp::Matcher_Adaptive::confidenceInterval
double confidenceInterval
Definition: Matcher_Adaptive.h:58
mp2p_icp::Matcher_Adaptive::planeMinimumDistance
double planeMinimumDistance
Definition: Matcher_Adaptive.h:65
mp2p_icp::Matcher_Adaptive::planeSearchPoints
uint32_t planeSearchPoints
Definition: Matcher_Adaptive.h:63
mp2p_icp
Definition: covariance.h:17
mp2p_icp::Matcher_Points_Base::bounding_box_intersection_check_epsilon_
double bounding_box_intersection_check_epsilon_
Definition: Matcher_Points_Base.h:63
mp2p_icp::Matcher_Points_Base::TransformedLocalPointCloud
Definition: Matcher_Points_Base.h:90
mp2p_icp::Matcher
Definition: Matcher.h:72
mp2p_icp::Matcher_Points_Base::TransformedLocalPointCloud::y_locals
mrpt::aligned_std_vector< float > y_locals
Definition: Matcher_Points_Base.h:100
mp2p_icp::Matcher_Adaptive::maxPt2PtCorrespondences
uint32_t maxPt2PtCorrespondences
Definition: Matcher_Adaptive.h:62
mp2p_icp::Pairings
Definition: Pairings.h:94
mp2p_icp::layer_name_t
std::string layer_name_t
Definition: layer_name_t.h:22
mp2p_icp::Matcher_Points_Base::allowMatchAlreadyMatchedPoints_
bool allowMatchAlreadyMatchedPoints_
Definition: Matcher_Points_Base.h:52
mp2p_icp::Matcher_Adaptive::minimumCorrDist
double minimumCorrDist
Definition: Matcher_Adaptive.h:67
Matcher_Adaptive.h
Pointcloud matcher: smart adaptive matcher.
mp2p_icp::estimate_points_eigen
PointCloudEigen estimate_points_eigen(const float *xs, const float *ys, const float *zs, mrpt::optional_ref< const std::vector< size_t >> indices, std::optional< size_t > totalCount=std::nullopt)
Definition: estimate_points_eigen.cpp:19
mp2p_icp::Matcher_Adaptive::planeEigenThreshold
double planeEigenThreshold
Definition: Matcher_Adaptive.h:66
mp2p_icp::Matcher_Adaptive::MAX_CORRS_PER_LOCAL
constexpr static size_t MAX_CORRS_PER_LOCAL
Definition: Matcher_Adaptive.h:76
mp2p_icp::Matcher_Points_Base::TransformedLocalPointCloud::idxs
std::optional< std::vector< std::size_t > > idxs
Definition: Matcher_Points_Base.h:97
mp2p_icp::Matcher_Adaptive::planeMinimumFoundPoints
uint32_t planeMinimumFoundPoints
Definition: Matcher_Adaptive.h:64
mp2p_icp::Matcher_Points_Base::initialize
void initialize(const mrpt::containers::yaml &params) override
Definition: Matcher_Points_Base.cpp:120
mp2p_icp::Matcher_Adaptive::initialize
void initialize(const mrpt::containers::yaml &params) override
Definition: Matcher_Adaptive.cpp:25
mp2p_icp::Matcher_Points_Base::TransformedLocalPointCloud::z_locals
mrpt::aligned_std_vector< float > z_locals
Definition: Matcher_Points_Base.h:100
mp2p_icp::MatchState
Definition: Matcher.h:36
kitti-batch-convert.out
string out
Definition: kitti-batch-convert.py:7
mp2p_icp::Matcher_Adaptive::firstToSecondDistanceMax
double firstToSecondDistanceMax
Definition: Matcher_Adaptive.h:59
mp2p_icp::Matcher_Adaptive::implMatchOneLayer
void implMatchOneLayer(const mrpt::maps::CMetricMap &pcGlobal, const mrpt::maps::CPointsMap &pcLocal, const mrpt::poses::CPose3D &localPose, MatchState &ms, const layer_name_t &globalName, const layer_name_t &localName, Pairings &out) const override
Definition: Matcher_Adaptive.cpp:52
mp2p_icp::Matcher_Points_Base::localPointsSampleSeed_
uint64_t localPointsSampleSeed_
Definition: Matcher_Points_Base.h:48
mp2p_icp::PointCloudEigen::eigVectors
std::array< mrpt::math::TVector3D, 3 > eigVectors
Definition: estimate_points_eigen.h:35
IMPLEMENTS_MRPT_OBJECT
IMPLEMENTS_MRPT_OBJECT(QualityEvaluator_RangeImageSimilarity, QualityEvaluator, mp2p_icp) using namespace mp2p_icp
mp2p_icp::Matcher_Adaptive::enableDetectPlanes
bool enableDetectPlanes
Definition: Matcher_Adaptive.h:61
mp2p_icp::Matcher_Adaptive::absoluteMaxSearchDistance
double absoluteMaxSearchDistance
Definition: Matcher_Adaptive.h:60
mp2p_icp::pointcloud_bitfield_t::point_layers
std::map< layer_name_t, DenseOrSparseBitField > point_layers
Definition: pointcloud_bitfield.h:82
mp2p_icp::Matcher_Adaptive::matchesPerLocal_
std::vector< mrpt::containers::vector_with_small_size_optimization< mrpt::tfest::TMatchingPair, MAX_CORRS_PER_LOCAL > > matchesPerLocal_
Definition: Matcher_Adaptive.h:80
mp2p_icp::Matcher_Adaptive::neighborPts_
std::vector< mrpt::math::TPoint3Df > neighborPts_
Definition: Matcher_Adaptive.h:72
mp2p_icp::Matcher_Adaptive
Definition: Matcher_Adaptive.h:31
mp2p_icp::PointCloudEigen
Output of estimate_points_eigen()
Definition: estimate_points_eigen.h:32
mp2p_icp::PointCloudEigen::meanCov
mrpt::poses::CPointPDFGaussian meanCov
Definition: estimate_points_eigen.h:34
mp2p_icp::Matcher_Adaptive::neighborIndices_
std::vector< size_t > neighborIndices_
Definition: Matcher_Adaptive.h:70
mp2p_icp::Matcher_Points_Base::maxLocalPointsPerLayer_
uint64_t maxLocalPointsPerLayer_
Definition: Matcher_Points_Base.h:47
mp2p_icp::MatchState::localPairedBitField
pointcloud_bitfield_t localPairedBitField
Definition: Matcher.h:46
mp2p_icp::MapToNN
const mrpt::maps::NearestNeighborsCapable * MapToNN(const mrpt::maps::CMetricMap &map, bool throwIfNotImplemented)
Definition: metricmap.cpp:690
mp2p_icp::PointCloudEigen::eigVals
std::array< double, 3 > eigVals
Definition: estimate_points_eigen.h:37
mp2p_icp::Matcher_Adaptive::neighborSqrDists_
std::vector< float > neighborSqrDists_
Definition: Matcher_Adaptive.h:71
mp2p_icp::Matcher_Points_Base::TransformedLocalPointCloud::x_locals
mrpt::aligned_std_vector< float > x_locals
Definition: Matcher_Points_Base.h:100
estimate_points_eigen.h
Calculate eigenvectors and eigenvalues from a set of points.
mp2p_icp::Matcher_Points_Base::transform_local_to_global
static TransformedLocalPointCloud transform_local_to_global(const mrpt::maps::CPointsMap &pcLocal, const mrpt::poses::CPose3D &localPose, const std::size_t maxLocalPoints=0, const uint64_t localPointsSampleSeed=0)
Definition: Matcher_Points_Base.cpp:174


mp2p_icp
Author(s): Jose-Luis Blanco-Claraco
autogenerated on Tue Jul 2 2024 02:47:25