46 switch (options.methodSelection)
48 case CGridMapAligner::amCorrelation:
49 return AlignPDF_correlation(
50 mm1, mm2, initialEstimationPDF, runningTime, info);
52 case CGridMapAligner::amModifiedRANSAC:
53 case CGridMapAligner::amRobustMatch:
55 return AlignPDF_robustMatch(
56 mm1, mm2, initialEstimationPDF, runningTime, info);
65 bool myVectorOrder(
const pair<size_t, float>& o1,
const pair<size_t, float>& o2)
67 return o1.second < o2.second;
81 options.methodSelection == CGridMapAligner::amRobustMatch ||
82 options.methodSelection == CGridMapAligner::amModifiedRANSAC);
97 std::vector<size_t> idxs1, idxs2;
127 "Metric maps must be of classes COccupancyGridMap2D or "
133 "Different resolutions for m1, m2:\n"
134 "\tres(m1) = %f\n\tres(m2) = %f\n",
155 m_grid_feat_extr.extractFeatures(
156 *m1, *lm1, N1, options.feature_descriptor,
157 options.feature_detector_options);
158 m_grid_feat_extr.extractFeatures(
159 *m2, *lm2, N2, options.feature_descriptor,
160 options.feature_detector_options);
168 const size_t nLM1 = lm1->size();
169 const size_t nLM2 = lm2->size();
173 if (nLM1 < 2 || nLM2 < 2)
185 CImage im1(1, 1, 1), im2(1, 1, 1);
187 unsigned int corrsCount = 0;
188 std::vector<bool> hasCorr(nLM1,
false);
190 const double thres_max = options.threshold_max;
191 const double thres_delta = options.threshold_delta;
194 if (options.debug_show_corrs)
198 std::cerr <<
"Warning: options.debug_show_corrs has no effect "
199 "since MRPT 0.9.1\n";
202 for (
size_t idx1 = 0; idx1 < nLM1; idx1++)
205 vector<pair<size_t, float>> corrs_indiv;
209 vector<float> corrs_indiv_only;
210 corrs_indiv.reserve(nLM2);
211 corrs_indiv_only.reserve(nLM2);
213 for (
size_t idx2 = 0; idx2 < nLM2; idx2++)
217 lm1->landmarks.get(idx1)->features[0]->descriptorDistanceTo(
218 *lm2->landmarks.get(idx2)->features[0]);
220 corrs_indiv.push_back(std::make_pair(idx2, minDist));
221 corrs_indiv_only.push_back(minDist);
231 std::sort(corrs_indiv.begin(), corrs_indiv.end(),
myVectorOrder);
235 const size_t nBestToKeep = corrs_indiv.size();
237 for (
size_t w = 0;
w < nBestToKeep;
w++)
239 if (corrs_indiv[
w].second <= thres_max &&
240 corrs_indiv[
w].second <= (corr_best + thres_delta))
242 idxs1.push_back(idx1);
243 idxs2.push_back(corrs_indiv[
w].
first);
246 idx1, corrs_indiv[
w].
first, corrs_indiv[
w].second));
251 if (options.debug_show_corrs)
253 auxWin->setWindowTitle(
format(
"Corr: %i - rest",
int(idx1)));
255 auxWin->plot(corrs_indiv_only,
".3",
"the_corr");
258 xs[0]=0; xs[1]=corrs_indiv_only.size()+1;
260 ys[0]=ys[1] = corr_best+thres_delta;
261 auxWin->plot(xs,ys,
"g",
"the_thres");
263 if (idx1==0) auxWin->axis_fit();
264 auxWin->waitForKey();
271 if (options.save_feat_coors)
276 std::map<size_t, std::set<size_t>> corrs_by_idx;
277 for (
size_t l = 0; l < idxs1.size(); l++)
278 corrs_by_idx[idxs1[l]].insert(idxs2[l]);
280 for (std::map<
size_t, std::set<size_t>>::
iterator it =
281 corrs_by_idx.begin();
282 it != corrs_by_idx.end(); ++it)
285 lm1->landmarks.get(it->first)
287 ->getFirstDescriptorAsMatrix(descriptor1);
289 im1 =
CImage(descriptor1,
true);
291 const size_t FEAT_W = im1.getWidth();
292 const size_t FEAT_H = im1.getHeight();
293 const size_t nF = it->second.size();
295 CImage img_compose(FEAT_W * 2 + 15, 10 + (5 + FEAT_H) * nF);
298 img_compose.
getHeight() - 1, TColor::black());
305 format(
"grid_feats/_FEAT_MATCH_%03i", (
int)it->first);
307 for (j = 0, it_j = it->second.begin(); j < nF; ++j, ++it_j)
309 fil +=
format(
"_%u",
static_cast<unsigned int>(*it_j));
312 lm2->landmarks.get(*it_j)
314 ->getFirstDescriptorAsMatrix(descriptor2);
315 im2 =
CImage(descriptor2,
true);
317 10 + FEAT_W, 5 + j * (FEAT_H + 5), im2);
327 correspondences.clear();
328 for (it1 = idxs1.begin(), it2 = idxs2.begin(); it1 != idxs1.end();
333 mp.
this_x = lm1->landmarks.get(*it1)->pose_mean.x;
334 mp.
this_y = lm1->landmarks.get(*it1)->pose_mean.y;
335 mp.
this_z = lm1->landmarks.get(*it1)->pose_mean.z;
338 mp.
other_x = lm2->landmarks.get(*it2)->pose_mean.x;
339 mp.
other_y = lm2->landmarks.get(*it2)->pose_mean.y;
340 mp.
other_z = lm2->landmarks.get(*it2)->pose_mean.z;
341 correspondences.push_back(mp);
345 hasCorr[*it1] =
true;
350 outInfo.
goodness = (2.0f * corrsCount) / (nLM1 + nLM2);
360 outInfo.
sog1 = pdf_SOG;
361 outInfo.
sog2 = pdf_SOG;
362 outInfo.
sog3 = pdf_SOG;
369 "[CGridMapAligner] Overall estimation(%u corrs, total: "
370 "%u): (%.03f,%.03f,%.03fdeg)\n",
371 corrsCount, (
unsigned)correspondences.size(),
378 using TMapMatchingsToPoseMode =
381 TMapMatchingsToPoseMode sog_modes;
388 if (options.methodSelection == CGridMapAligner::amRobustMatch)
396 const unsigned int min_inliers =
397 options.ransac_minSetSizeRatio * (nLM1 + nLM2) / 2;
403 options.ransac_mahalanobisDistanceThreshold;
410 options.ransac_prob_good_inliers;
415 correspondences, options.ransac_SOG_sigma_m, tfest_params,
424 size_t nB = pdf_SOG->size();
425 outInfo.
sog1 = pdf_SOG;
429 best_mode.
log_w = -std::numeric_limits<double>::max();
431 for (
size_t n = 0;
n < pdf_SOG->size();
n++)
439 pdf_SOG->push_back(best_mode);
440 outInfo.
sog2 = pdf_SOG;
443 "[CGridMapAligner] amRobustMatch: "
444 << nB <<
" SOG modes reduced to " << pdf_SOG->size()
445 <<
" (most-likely) (min.inliers=" << min_inliers <<
")\n");
455 const size_t nCorrs = all_corrs.size();
464 for (
size_t i = 0; i < nLM1; i++)
465 lm1_pnts.
insertPoint(lm1->landmarks.get(i)->pose_mean);
467 for (
size_t i = 0; i < nLM2; i++)
468 lm2_pnts.
insertPoint(lm2->landmarks.get(i)->pose_mean);
472 const size_t minInliersTOaccept =
473 round(options.ransac_minSetSizeRatio * 0.5 * (nLM1 + nLM2));
475 const unsigned int ransac_min_nSimulations =
477 unsigned int ransac_nSimulations =
480 const double probability_find_good_model = 0.9999;
482 const double chi2_thres_dim1 =
484 const double chi2_thres_dim2 =
490 COV_pnt.get_unsafe(0, 0) = COV_pnt.get_unsafe(1, 1) =
491 square(options.ransac_SOG_sigma_m);
501 const unsigned int max_trials =
502 (nCorrs * (nCorrs - 1) / 2) *
505 unsigned int iter = 0;
507 unsigned int trials = 0;
509 while (iter < ransac_nSimulations &&
523 }
while (idx1 == idx2);
526 if (all_corrs[idx1].this_idx == all_corrs[idx2].this_idx ||
527 all_corrs[idx1].this_idx == all_corrs[idx2].other_idx)
529 if (all_corrs[idx1].other_idx == all_corrs[idx2].this_idx ||
530 all_corrs[idx1].other_idx == all_corrs[idx2].other_idx)
537 const double corrs_dist1 =
539 all_corrs[idx1].this_x, all_corrs[idx1].this_y,
540 all_corrs[idx1].this_z, all_corrs[idx2].this_x,
541 all_corrs[idx2].this_y, all_corrs[idx2].this_z);
543 const double corrs_dist2 =
545 all_corrs[idx1].other_x, all_corrs[idx1].other_y,
546 all_corrs[idx1].other_z, all_corrs[idx2].other_x,
547 all_corrs[idx2].other_y, all_corrs[idx2].other_z);
551 const double corrs_dist_chi2 =
553 (8.0 *
square(options.ransac_SOG_sigma_m) *
556 if (corrs_dist_chi2 > chi2_thres_dim1)
continue;
562 bool is_new_hyp =
true;
565 itOldHyps != sog_modes.end(); ++itOldHyps)
567 if (itOldHyps->first.contains(all_corrs[idx1]) &&
568 itOldHyps->first.contains(all_corrs[idx2]))
571 itOldHyps->second.log_w = std::log(
572 std::exp(itOldHyps->second.log_w) + 1.0);
577 if (!is_new_hyp)
continue;
580 tentativeSubSet.push_back(all_corrs[idx1]);
581 tentativeSubSet.push_back(all_corrs[idx2]);
585 std::vector<bool> used_landmarks1(nLM1,
false);
586 std::vector<bool> used_landmarks2(nLM2,
false);
588 used_landmarks1[all_corrs[idx1].this_idx] =
true;
589 used_landmarks1[all_corrs[idx2].this_idx] =
true;
590 used_landmarks2[all_corrs[idx1].other_idx] =
true;
591 used_landmarks2[all_corrs[idx2].other_idx] =
true;
595 bool keep_incorporating =
true;
604 temptPose.
cov *=
square(options.ransac_SOG_sigma_m);
612 const double ccos = cos(temptPose.
mean.
phi());
613 const double ssin = sin(temptPose.
mean.
phi());
616 Hc.get_unsafe(1, 1) = ccos;
617 Hc.get_unsafe(0, 0) = ccos;
618 Hc.get_unsafe(1, 0) = ssin;
619 Hc.get_unsafe(0, 1) = -ssin;
627 vector<float> matches_dist;
628 std::vector<size_t> matches_idx;
634 #define GRIDMAP_USE_PROD_INTEGRAL
636 #ifdef GRIDMAP_USE_PROD_INTEGRAL
637 double best_pair_value = 0;
639 double best_pair_value =
640 std::numeric_limits<double>::max();
642 double best_pair_d2 =
643 std::numeric_limits<double>::max();
644 pair<size_t, size_t> best_pair_ij;
649 CDisplayWindowPlots
win(
"Matches");
651 for (
size_t j = 0; j < nLM2; j++)
653 if (used_landmarks2[j])
continue;
660 pdf_M2_j.
cov.get_unsafe(0, 0) =
661 pdf_M2_j.
cov.get_unsafe(1, 1) =
662 square(options.ransac_SOG_sigma_m);
667 pdf_M2_j.
cov, 2,
"b-",
668 format(
"M2_%u", (
unsigned)j),
true);
671 static const unsigned int N_KDTREE_SEARCHED = 3;
677 N_KDTREE_SEARCHED, matches_idx, matches_dist);
680 for (
size_t u = 0; u < matches_idx.size(); u++)
682 if (used_landmarks1[matches_idx[u]])
continue;
685 Hq.get_unsafe(0, 2) =
686 -p2_j_local.
x * ssin - p2_j_local.
y * ccos;
687 Hq.get_unsafe(1, 2) =
688 p2_j_local.
x * ccos - p2_j_local.
y * ssin;
691 Hc.multiply_HCHt(COV_pnt, pdf_M1_i.
cov);
693 temptPose.
cov, pdf_M1_i.
cov,
true);
696 lm1_pnts.
getPoint(matches_idx[u], px, py);
703 pdf_M1_i.
cov, 2,
"r-",
704 format(
"M1_%u", (
unsigned)matches_idx[u]),
709 #ifdef GRIDMAP_USE_PROD_INTEGRAL
710 const double prod_ij =
715 if (prod_ij > best_pair_value)
717 const double prod_ij =
719 if (prod_ij < best_pair_value)
725 best_pair_value = prod_ij;
726 best_pair_ij.first = matches_idx[u];
727 best_pair_ij.second = j;
752 keep_incorporating =
false;
755 if (best_pair_d2 < chi2_thres_dim2)
764 float p1_i_localx, p1_i_localy;
765 float p2_j_localx, p2_j_localy;
767 best_pair_ij.first, p1_i_localx,
770 best_pair_ij.second, p2_j_localx,
773 used_landmarks1[best_pair_ij.first] =
true;
774 used_landmarks2[best_pair_ij.second] =
true;
776 tentativeSubSet.push_back(
778 best_pair_ij.first, best_pair_ij.second,
779 p1_i_localx, p1_i_localy, 0,
780 p2_j_localx, p2_j_localy, 0
783 keep_incorporating =
true;
787 }
while (keep_incorporating);
790 const size_t ninliers = tentativeSubSet.size();
791 if (ninliers > minInliersTOaccept)
797 newGauss.
cov = temptPose.
cov;
799 sog_modes[tentativeSubSet] = newGauss;
806 if (largestConsensusCorrs.size() < ninliers)
808 largestConsensusCorrs = tentativeSubSet;
813 const double fracinliers =
815 static_cast<double>(
std::min(nLM1, nLM2));
821 pNoOutliers = std::max(
822 std::numeric_limits<double>::epsilon(),
825 1.0 - std::numeric_limits<double>::epsilon(),
828 ransac_nSimulations =
829 log(1 - probability_find_good_model) /
832 if (ransac_nSimulations < ransac_min_nSimulations)
833 ransac_nSimulations = ransac_min_nSimulations;
847 s != sog_modes.end(); ++
s)
849 cout <<
"SOG mode: " <<
s->second.mean
850 <<
" inliers: " <<
s->first.size() << endl;
851 pdf_SOG->push_back(
s->second);
855 if (pdf_SOG->size() > 0) pdf_SOG->normalizeWeights();
859 size_t nB = pdf_SOG->size();
860 outInfo.
sog1 = pdf_SOG;
863 pdf_SOG->mergeModes(options.maxKLd_for_merge,
false);
864 const double merge_clock_tim = merge_clock.
Tac();
866 outInfo.
sog2 = pdf_SOG;
867 size_t nA = pdf_SOG->size();
870 "[CGridMapAligner] amModifiedRANSAC: %u SOG modes "
871 "merged to %u in %.03fsec\n",
872 (
unsigned int)nB, (
unsigned int)nA, merge_clock_tim));
877 if (options.debug_save_map_pairs)
879 static unsigned int NN = 0;
887 " Largest consensus: %u\n",
888 static_cast<unsigned>(largestConsensusCorrs.size()));
889 CEnhancedMetaFile::LINUX_IMG_WIDTH(
891 CEnhancedMetaFile::LINUX_IMG_HEIGHT(
896 s != sog_modes.end(); ++
s)
898 COccupancyGridMap2D::saveAsEMFTwoMapsWithCorrespondences(
899 format(
"__debug_corrsGrid_%05u.emf", NN), m1, m2,
901 COccupancyGridMap2D::saveAsBitmapTwoMapsWithCorrespondences(
902 format(
"__debug_corrsGrid_%05u.png", NN), m1, m2,
938 i != pdf_SOG->end(); ++cnt)
941 pnts1.get(), pnts2.get(), (i)->mean,
nullptr, &icpInfo);
950 const double icp_maha_dist =
953 cout <<
"ICP " << cnt <<
" log-w: " << i->log_w
954 <<
" Goodness: " << 100 * icpInfo.
goodness
955 <<
" D_M= " << icp_maha_dist << endl;
956 cout <<
" final pos: " << icp_est->getMeanVal() << endl;
957 cout <<
" org pos: " << i->mean << endl;
963 if (icpInfo.
goodness > options.min_ICP_goodness &&
964 icp_maha_dist < options.max_ICP_mahadist)
966 icp_est->getMean((i)->
mean);
972 i = pdf_SOG->erase(i);
978 outInfo.
sog3 = pdf_SOG;
980 pdf_SOG->mergeModes(options.maxKLd_for_merge,
false);
982 "[CGridMapAligner] " << pdf_SOG->size()
983 <<
" SOG modes merged after ICP.");
994 "Refactor `info` so it is polymorphic and can use dynamic_cast<> here");
1003 *runningTime = tictac->
Tac();
1052 float phiResolution =
DEG2RAD(0.2f);
1053 float phiMin = -
M_PIf + 0.5f * phiResolution;
1054 float phiMax =
M_PIf;
1062 CImage map1_img, map2_img;
1063 float currentMaxCorr = -1e6f;
1070 size_t map2_lx = map2_mod.
getSizeX();
1071 size_t map2_ly = map2_mod.
getSizeY();
1072 CMatrix outCrossCorr, bestCrossCorr;
1073 float map2width_2 = 0.5f * (map2_mod.
getXMax() - map2_mod.
getXMin());
1075 #ifdef CORRELATION_SHOW_DEBUG
1076 CDisplayWindow*
win =
new CDisplayWindow(
"corr");
1077 CDisplayWindow* win2 =
new CDisplayWindow(
"map2_mod");
1083 for (phi = phiMin; phi < phiMax; phi += phiResolution)
1088 pivotPt_x - cos(phi) * map2width_2,
1089 pivotPt_y - sin(phi) * map2width_2,
1094 for (
unsigned int cy2 = 0; cy2 < map2_ly; cy2++)
1097 for (
unsigned int cx2 = 0; cx2 < map2_lx; cx2++)
1109 map2_img, outCrossCorr, -1, -1, -1, -1,
1114 float corrPeak = outCrossCorr.maxCoeff();
1115 printf(
"phi = %fdeg \tmax corr=%f\n",
RAD2DEG(phi), corrPeak);
1118 if (corrPeak > currentMaxCorr)
1120 currentMaxCorr = corrPeak;
1121 bestCrossCorr = outCrossCorr;
1125 #ifdef CORRELATION_SHOW_DEBUG
1126 outCrossCorr.adjustRange(0, 1);
1127 CImage aux(outCrossCorr,
true);
1128 win->showImage(aux);
1129 win2->showImage(map2_img);
1130 std::this_thread::sleep_for(5ms);
1137 *runningTime = tictac->Tac();
1141 bestCrossCorr.normalize(0, 1);
1142 CImage aux(bestCrossCorr,
true);
1145 #ifdef CORRELATION_SHOW_DEBUG
1151 CMatrix::Index uMax, vMax;
1152 currentMaxCorr = bestCrossCorr.maxCoeff(&uMax, &vMax);
1154 PDF->mean.x(m1->
idx2x(uMax));
1155 PDF->mean.y(m1->
idx2y(vMax));
1166 CGridMapAligner::TConfigParams::TConfigParams()
1170 feature_detector_options(),
1172 ransac_minSetSizeRatio(0.20f),
1173 ransac_SOG_sigma_m(0.10f),
1175 ransac_chi2_quantile(0.99),
1176 ransac_prob_good_inliers(0.9999),
1177 featsPerSquareMeter(0.015f),
1178 threshold_max(0.15f),
1179 threshold_delta(0.10f),
1180 min_ICP_goodness(0.30f),
1181 max_ICP_mahadist(10.0),
1182 maxKLd_for_merge(0.9),
1184 save_feat_coors(false),
1185 debug_show_corrs(false),
1186 debug_save_map_pairs(false)
1196 "\n----------- [CGridMapAligner::TConfigParams] ------------ \n\n");
1216 feature_detector_options.dumpToTextStream(out);
1228 iniFile.read_enum(section,
"methodSelection", methodSelection);
1231 featsPerSquareMeter,
float,
iniFile, section)
1242 ransac_minSetSizeRatio,
float,
iniFile, section)
1246 ransac_chi2_quantile,
double,
iniFile, section)
1248 ransac_prob_good_inliers,
double,
iniFile, section)
1254 feature_descriptor =
iniFile.read_enum(
1255 section,
"feature_descriptor", feature_descriptor,
true);
1256 feature_detector_options.loadFromConfigFile(
iniFile, section);