30 #include "ui_DatabaseViewer.h"
31 #include <QMessageBox>
32 #include <QFileDialog>
33 #include <QInputDialog>
34 #include <QColorDialog>
35 #include <QGraphicsLineItem>
36 #include <QtGui/QCloseEvent>
37 #include <QGraphicsOpacityEffect>
38 #include <QtCore/QBuffer>
39 #include <QtCore/QTextStream>
40 #include <QtCore/QDateTime>
41 #include <QtCore/QSettings>
46 #include <opencv2/core/core_c.h>
47 #include <opencv2/imgproc/types_c.h>
48 #include <opencv2/highgui/highgui.hpp>
90 #include <pcl/io/pcd_io.h>
91 #include <pcl/io/ply_io.h>
92 #include <pcl/io/obj_io.h>
93 #include <pcl/filters/voxel_grid.h>
94 #include <pcl/filters/crop_box.h>
95 #include <pcl/common/transforms.h>
96 #include <pcl/common/common.h>
99 #ifdef RTABMAP_OCTOMAP
103 #ifdef RTABMAP_GRIDMAP
114 editDepthDialog_(new QDialog(
this)),
115 editMapDialog_(new QDialog(
this)),
117 savedMaximized_(
false),
120 infoReducedGraph_(
false),
131 ui_ =
new Ui_DatabaseViewer();
133 ui_->buttonBox->setVisible(
false);
134 connect(
ui_->buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()),
this, SLOT(close()));
136 ui_->comboBox_logger_level->setVisible(parent==0);
137 ui_->label_logger_level->setVisible(parent==0);
138 connect(
ui_->comboBox_logger_level, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
updateLoggerLevel()));
139 connect(
ui_->actionVertical_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
setupMainLayout(
bool)));
144 vLayout->setContentsMargins(0,0,0,0);
145 vLayout->setSpacing(0);
147 QDialogButtonBox * buttonBox =
new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Reset,
Qt::Horizontal,
editDepthDialog_);
148 vLayout->addWidget(buttonBox);
151 connect(buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()),
editDepthArea_, SLOT(resetChanges()));
158 vLayout->setContentsMargins(0,0,0,0);
159 vLayout->setSpacing(0);
161 buttonBox =
new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Reset,
Qt::Horizontal,
editMapDialog_);
162 vLayout->addWidget(buttonBox);
163 connect(buttonBox, SIGNAL(accepted()),
editMapDialog_, SLOT(accept()));
164 connect(buttonBox, SIGNAL(rejected()),
editMapDialog_, SLOT(reject()));
165 connect(buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()),
editMapArea_, SLOT(resetChanges()));
169 QString title(
"RTAB-Map Database Viewer[*]");
170 this->setWindowTitle(title);
172 ui_->dockWidget_constraints->setVisible(
false);
173 ui_->dockWidget_graphView->setVisible(
false);
174 ui_->dockWidget_occupancyGridView->setVisible(
false);
175 ui_->dockWidget_guiparameters->setVisible(
false);
176 ui_->dockWidget_coreparameters->setVisible(
false);
177 ui_->dockWidget_info->setVisible(
false);
178 ui_->dockWidget_stereoView->setVisible(
false);
179 ui_->dockWidget_view3d->setVisible(
false);
180 ui_->dockWidget_statistics->setVisible(
false);
200 ui_->graphicsView_stereo->setAlpha(255);
202 #ifndef RTABMAP_OCTOMAP
203 ui_->checkBox_octomap->setEnabled(
false);
204 ui_->checkBox_octomap->setChecked(
false);
207 #ifndef RTABMAP_GRIDMAP
208 ui_->checkBox_showElevation->setEnabled(
false);
209 ui_->checkBox_showElevation->setChecked(
false);
210 ui_->checkBox_grid_elevation->setEnabled(
false);
211 ui_->checkBox_grid_elevation->setChecked(
false);
242 ui_->parameters_toolbox->setupUi(parameters);
248 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
249 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
250 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
251 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
252 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
253 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
254 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
255 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
257 ui_->menuView->addAction(
ui_->dockWidget_constraints->toggleViewAction());
258 ui_->menuView->addAction(
ui_->dockWidget_graphView->toggleViewAction());
259 ui_->menuView->addAction(
ui_->dockWidget_occupancyGridView->toggleViewAction());
260 ui_->menuView->addAction(
ui_->dockWidget_stereoView->toggleViewAction());
261 ui_->menuView->addAction(
ui_->dockWidget_view3d->toggleViewAction());
262 ui_->menuView->addAction(
ui_->dockWidget_guiparameters->toggleViewAction());
263 ui_->menuView->addAction(
ui_->dockWidget_coreparameters->toggleViewAction());
264 ui_->menuView->addAction(
ui_->dockWidget_info->toggleViewAction());
265 ui_->menuView->addAction(
ui_->dockWidget_statistics->toggleViewAction());
266 connect(
ui_->dockWidget_graphView->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateGraphView()));
267 connect(
ui_->dockWidget_occupancyGridView->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateGraphView()));
268 connect(
ui_->dockWidget_statistics->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateStatistics()));
269 connect(
ui_->dockWidget_info->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateInfo()));
272 connect(
ui_->graphViewer, SIGNAL(linkSelected(
int,
int)),
this , SLOT(
graphLinkSelected(
int,
int)));
274 connect(
ui_->parameters_toolbox, SIGNAL(parametersChanged(
const QStringList &)),
this, SLOT(
notifyParametersChanged(
const QStringList &)));
276 connect(
ui_->actionQuit, SIGNAL(triggered()),
this, SLOT(close()));
278 ui_->actionOpen_database->setEnabled(
true);
279 ui_->actionClose_database->setEnabled(
false);
282 ui_->actionSave_config->setShortcut(QKeySequence::Save);
283 connect(
ui_->actionSave_config, SIGNAL(triggered()),
this, SLOT(
writeSettings()));
284 connect(
ui_->actionOpen_database, SIGNAL(triggered()),
this, SLOT(
openDatabase()));
285 connect(
ui_->actionClose_database, SIGNAL(triggered()),
this, SLOT(
closeDatabase()));
286 connect(
ui_->actionDatabase_recovery, SIGNAL(triggered()),
this, SLOT(
recoverDatabase()));
288 connect(
ui_->actionExtract_images, SIGNAL(triggered()),
this, SLOT(
extractImages()));
289 connect(
ui_->actionEdit_depth_image, SIGNAL(triggered()),
this, SLOT(
editDepthImage()));
290 connect(
ui_->actionGenerate_graph_dot, SIGNAL(triggered()),
this, SLOT(
generateGraph()));
292 connect(
ui_->actionRaw_format_txt, SIGNAL(triggered()),
this , SLOT(
exportPosesRaw()));
296 connect(
ui_->actionKITTI_format_txt, SIGNAL(triggered()),
this , SLOT(
exportPosesKITTI()));
299 connect(
ui_->actionPoses_KML, SIGNAL(triggered()),
this , SLOT(
exportPosesKML()));
300 connect(
ui_->actionGPS_TXT, SIGNAL(triggered()),
this , SLOT(
exportGPS_TXT()));
301 connect(
ui_->actionGPS_KML, SIGNAL(triggered()),
this , SLOT(
exportGPS_KML()));
302 connect(
ui_->actionEdit_optimized_2D_map, SIGNAL(triggered()),
this , SLOT(
editSaved2DMap()));
303 connect(
ui_->actionExport_saved_2D_map, SIGNAL(triggered()),
this , SLOT(
exportSaved2DMap()));
304 connect(
ui_->actionImport_2D_map, SIGNAL(triggered()),
this , SLOT(
import2DMap()));
305 connect(
ui_->actionRegenerate_optimized_2D_map, SIGNAL(triggered()),
this , SLOT(
regenerateSavedMap()));
309 connect(
ui_->actionView_3D_map, SIGNAL(triggered()),
this, SLOT(
view3DMap()));
310 connect(
ui_->actionGenerate_3D_map_pcd, SIGNAL(triggered()),
this, SLOT(
generate3DMap()));
315 connect(
ui_->actionRefine_links, SIGNAL(triggered()),
this, SLOT(
refineLinks()));
318 connect(
ui_->actionReset_all_changes, SIGNAL(triggered()),
this, SLOT(
resetAllChanges()));
323 connect(
ui_->pushButton_add, SIGNAL(clicked()),
this, SLOT(
addConstraint()));
326 ui_->pushButton_refine->setEnabled(
false);
327 ui_->pushButton_add->setEnabled(
false);
328 ui_->pushButton_reset->setEnabled(
false);
329 ui_->pushButton_reject->setEnabled(
false);
331 ui_->menuEdit->setEnabled(
false);
332 ui_->actionGenerate_3D_map_pcd->setEnabled(
false);
333 ui_->actionExport->setEnabled(
false);
334 ui_->actionExtract_images->setEnabled(
false);
335 ui_->menuExport_poses->setEnabled(
false);
336 ui_->menuExport_GPS->setEnabled(
false);
337 ui_->actionPoses_KML->setEnabled(
false);
338 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
339 ui_->actionExport_saved_2D_map->setEnabled(
false);
340 ui_->actionImport_2D_map->setEnabled(
false);
341 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
342 ui_->actionView_optimized_mesh->setEnabled(
false);
343 ui_->actionExport_optimized_mesh->setEnabled(
false);
344 ui_->actionUpdate_optimized_mesh->setEnabled(
false);
346 ui_->horizontalSlider_A->setTracking(
false);
347 ui_->horizontalSlider_B->setTracking(
false);
348 ui_->horizontalSlider_A->setEnabled(
false);
349 ui_->horizontalSlider_B->setEnabled(
false);
352 connect(
ui_->horizontalSlider_A, SIGNAL(sliderMoved(
int)),
this, SLOT(
sliderAMoved(
int)));
353 connect(
ui_->horizontalSlider_B, SIGNAL(sliderMoved(
int)),
this, SLOT(
sliderBMoved(
int)));
354 ui_->spinBox_indexA->setEnabled(
false);
355 ui_->spinBox_indexB->setEnabled(
false);
356 connect(
ui_->spinBox_indexA, SIGNAL(valueChanged(
int)),
ui_->horizontalSlider_A, SLOT(setValue(
int)));
357 connect(
ui_->spinBox_indexB, SIGNAL(valueChanged(
int)),
ui_->horizontalSlider_B, SLOT(setValue(
int)));
359 connect(
ui_->toolButton_edit_priorA, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
360 connect(
ui_->toolButton_edit_priorB, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
361 connect(
ui_->toolButton_remove_priorA, SIGNAL(clicked(
bool)),
this, SLOT(
rejectConstraint()));
362 connect(
ui_->toolButton_remove_priorB, SIGNAL(clicked(
bool)),
this, SLOT(
rejectConstraint()));
364 connect(
ui_->spinBox_mesh_angleTolerance, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
365 connect(
ui_->spinBox_mesh_minClusterSize, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
366 connect(
ui_->spinBox_mesh_fillDepthHoles, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
367 connect(
ui_->spinBox_mesh_depthError, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
368 connect(
ui_->checkBox_mesh_quad, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
369 connect(
ui_->spinBox_mesh_triangleSize, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
370 connect(
ui_->checkBox_showWords, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
371 connect(
ui_->checkBox_showCloud, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
372 connect(
ui_->checkBox_showMesh, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
373 connect(
ui_->checkBox_showScan, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
374 connect(
ui_->checkBox_showMap, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
375 connect(
ui_->checkBox_showGrid, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
376 connect(
ui_->checkBox_showElevation, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
377 connect(
ui_->checkBox_odomFrame_3dview, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
378 connect(
ui_->checkBox_gravity_3dview, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
380 ui_->horizontalSlider_neighbors->setTracking(
false);
381 ui_->horizontalSlider_loops->setTracking(
false);
382 ui_->horizontalSlider_neighbors->setEnabled(
false);
383 ui_->horizontalSlider_loops->setEnabled(
false);
393 ui_->checkBox_showOptimized->setEnabled(
false);
394 connect(
ui_->toolButton_constraint, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
397 ui_->horizontalSlider_iterations->setTracking(
false);
398 ui_->horizontalSlider_iterations->setEnabled(
false);
399 ui_->spinBox_optimizationsFrom->setEnabled(
false);
402 connect(
ui_->spinBox_optimizationsFrom, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
403 connect(
ui_->comboBox_optimizationFlavor, SIGNAL(activated(
int)),
this, SLOT(
updateGraphView()));
404 connect(
ui_->checkBox_spanAllMaps, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
405 connect(
ui_->checkBox_wmState, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
406 connect(
ui_->graphViewer, SIGNAL(mapShownRequested()),
this, SLOT(
updateGraphView()));
407 connect(
ui_->checkBox_ignorePoseCorrection, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
409 connect(
ui_->checkBox_ignoreGlobalLoop, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
410 connect(
ui_->checkBox_ignoreLocalLoopSpace, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
411 connect(
ui_->checkBox_ignoreLocalLoopTime, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
412 connect(
ui_->checkBox_ignoreUserLoop, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
413 connect(
ui_->checkBox_ignoreLandmarks, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
414 connect(
ui_->doubleSpinBox_optimizationScale, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
415 connect(
ui_->checkBox_octomap, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
416 connect(
ui_->checkBox_grid_grid, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
417 connect(
ui_->checkBox_grid_2d, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
418 connect(
ui_->checkBox_grid_elevation, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
419 connect(
ui_->comboBox_octomap_rendering_type, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
updateOctomapView()));
421 connect(
ui_->checkBox_grid_empty, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
422 connect(
ui_->checkBox_grid_frontiers, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
423 connect(
ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
updateConstraintView()));
425 connect(
ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(
double)),
this, SLOT(
update3dView()));
426 connect(
ui_->checkBox_cameraProjection, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
427 connect(
ui_->checkBox_showDisparityInsteadOfRight, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
429 connect(
ui_->spinBox_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
430 connect(
ui_->groupBox_posefiltering, SIGNAL(clicked(
bool)),
this, SLOT(
updateGraphView()));
431 connect(
ui_->doubleSpinBox_posefilteringRadius, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
432 connect(
ui_->doubleSpinBox_posefilteringAngle, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
434 ui_->label_stereo_inliers_name->setStyleSheet(
"QLabel {color : blue; }");
435 ui_->label_stereo_flowOutliers_name->setStyleSheet(
"QLabel {color : red; }");
436 ui_->label_stereo_slopeOutliers_name->setStyleSheet(
"QLabel {color : yellow; }");
437 ui_->label_stereo_disparityOutliers_name->setStyleSheet(
"QLabel {color : magenta; }");
441 connect(
ui_->graphViewer, SIGNAL(configChanged()),
this, SLOT(
configModified()));
442 connect(
ui_->graphicsView_A, SIGNAL(configChanged()),
this, SLOT(
configModified()));
443 connect(
ui_->graphicsView_B, SIGNAL(configChanged()),
this, SLOT(
configModified()));
444 connect(
ui_->comboBox_logger_level, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
configModified()));
445 connect(
ui_->actionVertical_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
446 connect(
ui_->actionConcise_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
447 connect(
ui_->checkBox_alignPosesWithGPS, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
448 connect(
ui_->checkBox_alignPosesWithGroundTruth, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
449 connect(
ui_->checkBox_alignScansCloudsWithGroundTruth, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
450 connect(
ui_->checkBox_ignoreIntermediateNodes, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
451 connect(
ui_->checkBox_ignoreIntermediateNodes, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
452 connect(
ui_->checkBox_timeStats, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
453 connect(
ui_->checkBox_timeStats, SIGNAL(stateChanged(
int)),
this, SLOT(
updateStatistics()));
455 connect(
ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
456 connect(
ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
457 connect(
ui_->checkBox_cameraProjection, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
458 connect(
ui_->checkBox_showDisparityInsteadOfRight, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
459 connect(
ui_->spinBox_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
460 connect(
ui_->groupBox_posefiltering, SIGNAL(clicked(
bool)),
this, SLOT(
configModified()));
461 connect(
ui_->doubleSpinBox_posefilteringRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
462 connect(
ui_->doubleSpinBox_posefilteringAngle, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
464 connect(
ui_->pushButton_applyRotation, SIGNAL(clicked()),
this, SLOT(
updateGraphView()));
466 connect(
ui_->spinBox_icp_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
467 connect(
ui_->doubleSpinBox_icp_maxDepth, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
468 connect(
ui_->doubleSpinBox_icp_minDepth, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
469 connect(
ui_->checkBox_icp_from_depth, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
471 connect(
ui_->doubleSpinBox_detectMore_radius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
472 connect(
ui_->doubleSpinBox_detectMore_radiusMin, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
473 connect(
ui_->doubleSpinBox_detectMore_angle, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
474 connect(
ui_->spinBox_detectMore_iterations, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
475 connect(
ui_->checkBox_detectMore_intraSession, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
476 connect(
ui_->checkBox_detectMore_interSession, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
477 connect(
ui_->checkBox_opt_graph_as_guess, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
479 connect(
ui_->lineEdit_obstacleColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
480 connect(
ui_->lineEdit_groundColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
481 connect(
ui_->lineEdit_emptyColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
482 connect(
ui_->lineEdit_frontierColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
483 connect(
ui_->lineEdit_obstacleColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
484 connect(
ui_->lineEdit_groundColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
485 connect(
ui_->lineEdit_emptyColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
486 connect(
ui_->lineEdit_frontierColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
489 connect(
ui_->toolButton_emptyColor, SIGNAL(clicked(
bool)),
this, SLOT(
selectEmptyColor()));
491 connect(
ui_->spinBox_cropRadius, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
492 connect(
ui_->checkBox_grid_showProbMap, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
493 connect(
ui_->checkBox_grid_showProbMap, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
498 QList<QDockWidget*> dockWidgets = this->findChildren<QDockWidget*>();
499 for(
int i=0;
i<dockWidgets.size(); ++
i)
501 connect(dockWidgets[
i], SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
this, SLOT(
configModified()));
502 connect(dockWidgets[
i]->toggleViewAction(), SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
504 ui_->dockWidget_constraints->installEventFilter(
this);
505 ui_->dockWidget_graphView->installEventFilter(
this);
506 ui_->dockWidget_occupancyGridView->installEventFilter(
this);
507 ui_->dockWidget_stereoView->installEventFilter(
this);
508 ui_->dockWidget_view3d->installEventFilter(
this);
509 ui_->dockWidget_guiparameters->installEventFilter(
this);
510 ui_->dockWidget_coreparameters->installEventFilter(
this);
511 ui_->dockWidget_info->installEventFilter(
this);
512 ui_->dockWidget_statistics->installEventFilter(
this);
519 #ifdef RTABMAP_OCTOMAP
528 qobject_cast<QHBoxLayout *>(
ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::TopToBottom);
532 qobject_cast<QHBoxLayout *>(
ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::LeftToRight);
542 ui_->buttonBox->setVisible(visible);
547 if(
ui_->actionConcise_Layout->isChecked())
549 ui_->graphicsView_B->setVisible(
false);
550 ui_->scrollArea->setVisible(
false);
551 ui_->scrollArea_2->setVisible(
false);
552 ui_->spinBox_indexB->setVisible(
false);
553 ui_->widget_imageControls_B->setVisible(
false);
554 ui_->widget_graphControl->setVisible(
false);
555 ui_->graphicsView_A->clearLines();
556 ui_->graphicsView_B->clearLines();
560 ui_->graphicsView_B->setVisible(
true);
561 ui_->scrollArea->setVisible(
true);
562 ui_->scrollArea_2->setVisible(
true);
563 ui_->spinBox_indexB->setVisible(
true);
564 ui_->widget_imageControls_B->setVisible(
true);
565 ui_->widget_graphControl->setVisible(
true);
568 this->setWindowModified(
true);
577 QString privatePath = QDir::homePath() +
"/.rtabmap";
578 if(!QDir(privatePath).exists())
580 QDir::home().mkdir(
".rtabmap");
582 return privatePath +
"/rtabmap.ini";
588 QSettings settings(
path, QSettings::IniFormat);
589 settings.beginGroup(
"DatabaseViewer");
593 bytes = settings.value(
"geometry", QByteArray()).toByteArray();
596 this->restoreGeometry(
bytes);
598 bytes = settings.value(
"state", QByteArray()).toByteArray();
601 this->restoreState(
bytes);
605 ui_->comboBox_logger_level->setCurrentIndex(settings.value(
"loggerLevel",
ui_->comboBox_logger_level->currentIndex()).toInt());
606 ui_->actionVertical_Layout->setChecked(settings.value(
"verticalLayout",
ui_->actionVertical_Layout->isChecked()).toBool());
607 ui_->actionConcise_Layout->setChecked(settings.value(
"conciseLayout",
ui_->actionConcise_Layout->isChecked()).toBool());
608 ui_->checkBox_ignoreIntermediateNodes->setChecked(settings.value(
"ignoreIntermediateNodes",
ui_->checkBox_ignoreIntermediateNodes->isChecked()).toBool());
609 ui_->checkBox_timeStats->setChecked(settings.value(
"timeStats",
ui_->checkBox_timeStats->isChecked()).toBool());
612 ui_->graphViewer->loadSettings(settings,
"GraphView");
613 ui_->graphViewer->setReferentialVisible(
false);
615 settings.beginGroup(
"optimization");
616 ui_->doubleSpinBox_gainCompensationRadius->setValue(settings.value(
"gainCompensationRadius",
ui_->doubleSpinBox_gainCompensationRadius->value()).toDouble());
617 ui_->doubleSpinBox_voxelSize->setValue(settings.value(
"voxelSize",
ui_->doubleSpinBox_voxelSize->value()).toDouble());
618 ui_->spinBox_decimation->setValue(settings.value(
"decimation",
ui_->spinBox_decimation->value()).toInt());
619 ui_->checkBox_cameraProjection->setChecked(settings.value(
"camProj",
ui_->checkBox_cameraProjection->isChecked()).toBool());
620 ui_->checkBox_showDisparityInsteadOfRight->setChecked(settings.value(
"showDisp",
ui_->checkBox_showDisparityInsteadOfRight->isChecked()).toBool());
623 settings.beginGroup(
"grid");
624 ui_->groupBox_posefiltering->setChecked(settings.value(
"poseFiltering",
ui_->groupBox_posefiltering->isChecked()).toBool());
625 ui_->doubleSpinBox_posefilteringRadius->setValue(settings.value(
"poseFilteringRadius",
ui_->doubleSpinBox_posefilteringRadius->value()).toDouble());
626 ui_->doubleSpinBox_posefilteringAngle->setValue(settings.value(
"poseFilteringAngle",
ui_->doubleSpinBox_posefilteringAngle->value()).toDouble());
627 ui_->lineEdit_obstacleColor->setText(settings.value(
"colorObstacle",
ui_->lineEdit_obstacleColor->text()).toString());
628 ui_->lineEdit_groundColor->setText(settings.value(
"colorGround",
ui_->lineEdit_groundColor->text()).toString());
629 ui_->lineEdit_emptyColor->setText(settings.value(
"colorEmpty",
ui_->lineEdit_emptyColor->text()).toString());
630 ui_->lineEdit_frontierColor->setText(settings.value(
"colorFrontier",
ui_->lineEdit_frontierColor->text()).toString());
631 ui_->spinBox_cropRadius->setValue(settings.value(
"cropRadius",
ui_->spinBox_cropRadius->value()).toInt());
632 ui_->checkBox_grid_showProbMap->setChecked(settings.value(
"probMap",
ui_->checkBox_grid_showProbMap->isChecked()).toBool());
635 settings.beginGroup(
"mesh");
636 ui_->checkBox_mesh_quad->setChecked(settings.value(
"quad",
ui_->checkBox_mesh_quad->isChecked()).toBool());
637 ui_->spinBox_mesh_angleTolerance->setValue(settings.value(
"angleTolerance",
ui_->spinBox_mesh_angleTolerance->value()).toInt());
638 ui_->spinBox_mesh_minClusterSize->setValue(settings.value(
"minClusterSize",
ui_->spinBox_mesh_minClusterSize->value()).toInt());
639 ui_->spinBox_mesh_fillDepthHoles->setValue(settings.value(
"fillDepthHolesSize",
ui_->spinBox_mesh_fillDepthHoles->value()).toInt());
640 ui_->spinBox_mesh_depthError->setValue(settings.value(
"fillDepthHolesError",
ui_->spinBox_mesh_depthError->value()).toInt());
641 ui_->spinBox_mesh_triangleSize->setValue(settings.value(
"triangleSize",
ui_->spinBox_mesh_triangleSize->value()).toInt());
645 ui_->graphicsView_A->loadSettings(settings,
"ImageViewA");
646 ui_->graphicsView_B->loadSettings(settings,
"ImageViewB");
649 settings.beginGroup(
"icp");
650 ui_->spinBox_icp_decimation->setValue(settings.value(
"decimation",
ui_->spinBox_icp_decimation->value()).toInt());
651 ui_->doubleSpinBox_icp_maxDepth->setValue(settings.value(
"maxDepth",
ui_->doubleSpinBox_icp_maxDepth->value()).toDouble());
652 ui_->doubleSpinBox_icp_minDepth->setValue(settings.value(
"minDepth",
ui_->doubleSpinBox_icp_minDepth->value()).toDouble());
653 ui_->checkBox_icp_from_depth->setChecked(settings.value(
"icpFromDepth",
ui_->checkBox_icp_from_depth->isChecked()).toBool());
659 settings.beginGroup(
"Gui");
661 settings.beginGroup(
"PostProcessingDialog");
662 ui_->doubleSpinBox_detectMore_radius->setValue(settings.value(
"cluster_radius",
ui_->doubleSpinBox_detectMore_radius->value()).toDouble());
663 ui_->doubleSpinBox_detectMore_radiusMin->setValue(settings.value(
"cluster_radius_min",
ui_->doubleSpinBox_detectMore_radiusMin->value()).toDouble());
664 ui_->doubleSpinBox_detectMore_angle->setValue(settings.value(
"cluster_angle",
ui_->doubleSpinBox_detectMore_angle->value()).toDouble());
665 ui_->spinBox_detectMore_iterations->setValue(settings.value(
"iterations",
ui_->spinBox_detectMore_iterations->value()).toInt());
666 ui_->checkBox_detectMore_intraSession->setChecked(settings.value(
"intra_session",
ui_->checkBox_detectMore_intraSession->isChecked()).toBool());
667 ui_->checkBox_detectMore_interSession->setChecked(settings.value(
"inter_session",
ui_->checkBox_detectMore_interSession->isChecked()).toBool());
668 ui_->checkBox_opt_graph_as_guess->setChecked(settings.value(
"opt_graph_as_guess",
ui_->checkBox_opt_graph_as_guess->isChecked()).toBool());
675 for(ParametersMap::iterator
iter = parameters.begin();
iter!= parameters.end(); ++
iter)
677 ui_->parameters_toolbox->updateParameter(
iter->first,
iter->second);
684 QSettings settings(
path, QSettings::IniFormat);
685 settings.beginGroup(
"DatabaseViewer");
688 if(!this->isMaximized())
690 settings.setValue(
"geometry", this->saveGeometry());
692 settings.setValue(
"state", this->saveState());
693 settings.setValue(
"maximized", this->isMaximized());
696 settings.setValue(
"loggerLevel",
ui_->comboBox_logger_level->currentIndex());
697 settings.setValue(
"verticalLayout",
ui_->actionVertical_Layout->isChecked());
698 settings.setValue(
"conciseLayout",
ui_->actionConcise_Layout->isChecked());
699 settings.setValue(
"ignoreIntermediateNodes",
ui_->checkBox_ignoreIntermediateNodes->isChecked());
700 settings.setValue(
"timeStats",
ui_->checkBox_timeStats->isChecked());
703 ui_->graphViewer->saveSettings(settings,
"GraphView");
706 settings.beginGroup(
"optimization");
707 settings.setValue(
"gainCompensationRadius",
ui_->doubleSpinBox_gainCompensationRadius->value());
708 settings.setValue(
"voxelSize",
ui_->doubleSpinBox_voxelSize->value());
709 settings.setValue(
"decimation",
ui_->spinBox_decimation->value());
710 settings.setValue(
"camProj",
ui_->checkBox_cameraProjection->isChecked());
711 settings.setValue(
"showDisp",
ui_->checkBox_showDisparityInsteadOfRight->isChecked());
715 settings.beginGroup(
"grid");
716 settings.setValue(
"poseFiltering",
ui_->groupBox_posefiltering->isChecked());
717 settings.setValue(
"poseFilteringRadius",
ui_->doubleSpinBox_posefilteringRadius->value());
718 settings.setValue(
"poseFilteringAngle",
ui_->doubleSpinBox_posefilteringAngle->value());
719 settings.setValue(
"colorObstacle",
ui_->lineEdit_obstacleColor->text());
720 settings.setValue(
"colorGround",
ui_->lineEdit_groundColor->text());
721 settings.setValue(
"colorEmpty",
ui_->lineEdit_emptyColor->text());
722 settings.setValue(
"colorFrontier",
ui_->lineEdit_frontierColor->text());
723 settings.setValue(
"cropRadius",
ui_->spinBox_cropRadius->value());
724 settings.setValue(
"probMap",
ui_->checkBox_grid_showProbMap->isChecked());
727 settings.beginGroup(
"mesh");
728 settings.setValue(
"quad",
ui_->checkBox_mesh_quad->isChecked());
729 settings.setValue(
"angleTolerance",
ui_->spinBox_mesh_angleTolerance->value());
730 settings.setValue(
"minClusterSize",
ui_->spinBox_mesh_minClusterSize->value());
731 settings.setValue(
"fillDepthHolesSize",
ui_->spinBox_mesh_fillDepthHoles->value());
732 settings.setValue(
"fillDepthHolesError",
ui_->spinBox_mesh_depthError->value());
733 settings.setValue(
"triangleSize",
ui_->spinBox_mesh_triangleSize->value());
737 ui_->graphicsView_A->saveSettings(settings,
"ImageViewA");
738 ui_->graphicsView_B->saveSettings(settings,
"ImageViewB");
741 settings.beginGroup(
"icp");
742 settings.setValue(
"decimation",
ui_->spinBox_icp_decimation->value());
743 settings.setValue(
"maxDepth",
ui_->doubleSpinBox_icp_maxDepth->value());
744 settings.setValue(
"minDepth",
ui_->doubleSpinBox_icp_minDepth->value());
745 settings.setValue(
"icpFromDepth",
ui_->checkBox_icp_from_depth->isChecked());
751 settings.beginGroup(
"Gui");
753 settings.beginGroup(
"PostProcessingDialog");
754 settings.setValue(
"cluster_radius",
ui_->doubleSpinBox_detectMore_radius->value());
755 settings.setValue(
"cluster_radius_min",
ui_->doubleSpinBox_detectMore_radiusMin->value());
756 settings.setValue(
"cluster_angle",
ui_->doubleSpinBox_detectMore_angle->value());
757 settings.setValue(
"iterations",
ui_->spinBox_detectMore_iterations->value());
758 settings.setValue(
"intra_session",
ui_->checkBox_detectMore_intraSession->isChecked());
759 settings.setValue(
"inter_session",
ui_->checkBox_detectMore_interSession->isChecked());
760 settings.setValue(
"opt_graph_as_guess",
ui_->checkBox_opt_graph_as_guess->isChecked());
765 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end();)
767 if(!
ui_->parameters_toolbox->getParameterWidget(
iter->first.c_str()))
769 parameters.erase(
iter++);
778 this->setWindowModified(
false);
784 ui_->comboBox_logger_level->setCurrentIndex(1);
785 ui_->checkBox_alignPosesWithGPS->setChecked(
true);
786 ui_->checkBox_alignPosesWithGroundTruth->setChecked(
true);
787 ui_->checkBox_alignScansCloudsWithGroundTruth->setChecked(
false);
788 ui_->checkBox_ignoreIntermediateNodes->setChecked(
false);
789 ui_->checkBox_timeStats->setChecked(
true);
791 ui_->comboBox_optimizationFlavor->setCurrentIndex(0);
792 ui_->checkBox_spanAllMaps->setChecked(
true);
793 ui_->checkBox_wmState->setChecked(
false);
794 ui_->checkBox_ignorePoseCorrection->setChecked(
false);
795 ui_->checkBox_ignoreGlobalLoop->setChecked(
false);
796 ui_->checkBox_ignoreLocalLoopSpace->setChecked(
false);
797 ui_->checkBox_ignoreLocalLoopTime->setChecked(
false);
798 ui_->checkBox_ignoreUserLoop->setChecked(
false);
799 ui_->checkBox_ignoreLandmarks->setChecked(
false);
800 ui_->doubleSpinBox_optimizationScale->setValue(1.0);
801 ui_->doubleSpinBox_gainCompensationRadius->setValue(0.0);
802 ui_->doubleSpinBox_voxelSize->setValue(0.0);
803 ui_->spinBox_decimation->setValue(1);
804 ui_->checkBox_cameraProjection->setChecked(
false);
805 ui_->checkBox_showDisparityInsteadOfRight->setChecked(
false);
807 ui_->groupBox_posefiltering->setChecked(
false);
808 ui_->doubleSpinBox_posefilteringRadius->setValue(0.1);
809 ui_->doubleSpinBox_posefilteringAngle->setValue(30);
810 ui_->checkBox_grid_empty->setChecked(
true);
811 ui_->checkBox_grid_frontiers->setChecked(
false);
812 ui_->checkBox_octomap->setChecked(
false);
813 ui_->lineEdit_obstacleColor->setText(QColor(Qt::red).
name());
814 ui_->lineEdit_groundColor->setText(QColor(Qt::green).
name());
815 ui_->lineEdit_emptyColor->setText(QColor(Qt::yellow).
name());
816 ui_->lineEdit_frontierColor->setText(QColor(Qt::cyan).
name());
817 ui_->spinBox_cropRadius->setValue(1);
818 ui_->checkBox_grid_showProbMap->setChecked(
false);
820 ui_->checkBox_mesh_quad->setChecked(
true);
821 ui_->spinBox_mesh_angleTolerance->setValue(15);
822 ui_->spinBox_mesh_minClusterSize->setValue(0);
823 ui_->spinBox_mesh_fillDepthHoles->setValue(
false);
824 ui_->spinBox_mesh_depthError->setValue(10);
825 ui_->spinBox_mesh_triangleSize->setValue(2);
827 ui_->spinBox_icp_decimation->setValue(1);
828 ui_->doubleSpinBox_icp_maxDepth->setValue(0.0);
829 ui_->doubleSpinBox_icp_minDepth->setValue(0.0);
830 ui_->checkBox_icp_from_depth->setChecked(
false);
832 ui_->doubleSpinBox_detectMore_radius->setValue(1.0);
833 ui_->doubleSpinBox_detectMore_radiusMin->setValue(0.0);
834 ui_->doubleSpinBox_detectMore_angle->setValue(30.0);
835 ui_->spinBox_detectMore_iterations->setValue(5);
836 ui_->checkBox_detectMore_intraSession->setChecked(
true);
837 ui_->checkBox_detectMore_interSession->setChecked(
true);
838 ui_->checkBox_opt_graph_as_guess->setChecked(
true);
843 QString
path = QFileDialog::getOpenFileName(
this, tr(
"Select file"),
pathDatabase_, tr(
"Databases (*.db)"));
852 UDEBUG(
"Open database \"%s\"",
path.toStdString().c_str());
853 if(QFile::exists(
path))
855 if(QFileInfo(
path).isFile())
857 std::string driverType =
"sqlite3";
863 ui_->actionClose_database->setEnabled(
false);
864 ui_->actionOpen_database->setEnabled(
true);
867 QMessageBox::warning(
this,
"Database error", tr(
"Can't open database \"%1\"").
arg(
path));
871 ui_->actionClose_database->setEnabled(
true);
872 ui_->actionOpen_database->setEnabled(
false);
886 uInsert(parameters, overriddenParameters);
888 if(parameters.size())
890 const ParametersMap & currentParameters =
ui_->parameters_toolbox->getParameters();
892 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end(); ++
iter)
894 ParametersMap::const_iterator jter = currentParameters.find(
iter->first);
895 if(jter!=currentParameters.end() &&
896 ui_->parameters_toolbox->getParameterWidget(QString(
iter->first.c_str())) != 0 &&
897 iter->second.compare(jter->second) != 0 &&
898 iter->first.compare(Parameters::kRtabmapWorkingDirectory()) != 0)
900 bool different =
true;
911 differentParameters.insert(*
iter);
912 QString
msg = tr(
"Parameter \"%1\": database=\"%2\" Preferences=\"%3\"")
913 .arg(
iter->first.c_str())
914 .arg(
iter->second.c_str())
915 .arg(jter->second.c_str());
921 if(differentParameters.size())
923 int r = QMessageBox::question(
this,
924 tr(
"Update parameters..."),
925 tr(
"The database is using %1 different parameter(s) than "
926 "those currently set in Core parameters panel. Do you want "
927 "to use database's parameters?").
arg(differentParameters.size()),
928 QMessageBox::Yes | QMessageBox::No,
930 if(r == QMessageBox::Yes)
933 for(rtabmap::ParametersMap::const_iterator
iter = differentParameters.begin();
iter!=differentParameters.end(); ++
iter)
935 ui_->parameters_toolbox->updateParameter(
iter->first,
iter->second);
936 str.push_back(
iter->first.c_str());
944 this->setWindowTitle(
"RTAB-Map Database Viewer - " +
path +
"[*]");
960 QMessageBox::warning(
this,
"Database error", tr(
"Database \"%1\" does not exist.").
arg(
path));
967 this->setWindowTitle(
"RTAB-Map Database Viewer[*]");
972 QMessageBox::StandardButton button = QMessageBox::question(
this,
973 tr(
"Links modified"),
974 tr(
"Some links are modified (%1 added, %2 refined, %3 removed), do you want to save them?")
976 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
977 QMessageBox::Cancel);
979 if(button == QMessageBox::Yes)
988 if(refinedIter->second.from() != refinedIter->second.to())
994 if(
iter->second.from() !=
iter->second.to())
1005 if(
iter->second.from() !=
iter->second.to())
1014 if(
iter->second.from() !=
iter->second.to())
1032 if(button != QMessageBox::Yes && button != QMessageBox::No)
1041 QMessageBox::StandardButton button = QMessageBox::question(
this,
1042 tr(
"Local occupancy grid maps modified"),
1043 tr(
"%1 occupancy grid maps are modified, do you want to "
1044 "save them? This will overwrite occupancy grids saved in the database.")
1046 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
1047 QMessageBox::Cancel);
1049 if(button == QMessageBox::Yes)
1057 mapIter->second.groundCells,
1058 mapIter->second.obstacleCells,
1059 mapIter->second.emptyCells,
1060 mapIter->second.cellSize,
1061 mapIter->second.viewPoint);
1067 if(button != QMessageBox::Yes && button != QMessageBox::No)
1075 QMessageBox::StandardButton button = QMessageBox::question(
this,
1076 tr(
"Laser scans modified"),
1077 tr(
"%1 laser scans are modified, do you want to "
1078 "save them? This will overwrite laser scans saved in the database.")
1080 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
1081 QMessageBox::Cancel);
1083 if(button == QMessageBox::Yes)
1092 if(button != QMessageBox::Yes && button != QMessageBox::No)
1121 ui_->graphViewer->clearAll();
1123 ui_->menuEdit->setEnabled(
false);
1124 ui_->actionGenerate_3D_map_pcd->setEnabled(
false);
1125 ui_->actionExport->setEnabled(
false);
1126 ui_->actionExtract_images->setEnabled(
false);
1127 ui_->menuExport_poses->setEnabled(
false);
1128 ui_->menuExport_GPS->setEnabled(
false);
1129 ui_->actionPoses_KML->setEnabled(
false);
1130 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
1131 ui_->actionExport_saved_2D_map->setEnabled(
false);
1132 ui_->actionImport_2D_map->setEnabled(
false);
1133 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
1134 ui_->actionView_optimized_mesh->setEnabled(
false);
1135 ui_->actionExport_optimized_mesh->setEnabled(
false);
1136 ui_->actionUpdate_optimized_mesh->setEnabled(
false);
1137 ui_->checkBox_showOptimized->setEnabled(
false);
1138 ui_->toolBox_statistics->clear();
1140 ui_->checkBox_alignPosesWithGPS->setVisible(
false);
1141 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
1142 ui_->checkBox_alignPosesWithGroundTruth->setVisible(
false);
1143 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
1144 ui_->checkBox_alignScansCloudsWithGroundTruth->setVisible(
false);
1145 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
1146 ui_->doubleSpinBox_optimizationScale->setVisible(
false);
1147 ui_->label_scale_title->setVisible(
false);
1148 ui_->label_rmse->setVisible(
false);
1149 ui_->label_rmse_title->setVisible(
false);
1150 ui_->checkBox_ignoreIntermediateNodes->setVisible(
false);
1151 ui_->label_ignoreINtermediateNdoes->setVisible(
false);
1152 ui_->label_alignPosesWithGPS->setVisible(
false);
1153 ui_->label_alignPosesWithGroundTruth->setVisible(
false);
1154 ui_->label_alignScansCloudsWithGroundTruth->setVisible(
false);
1155 ui_->label_optimizeFrom->setText(tr(
"Root"));
1156 ui_->textEdit_info->clear();
1158 ui_->pushButton_refine->setEnabled(
false);
1159 ui_->pushButton_add->setEnabled(
false);
1160 ui_->pushButton_reset->setEnabled(
false);
1161 ui_->pushButton_reject->setEnabled(
false);
1163 ui_->horizontalSlider_loops->setEnabled(
false);
1164 ui_->horizontalSlider_loops->setMaximum(0);
1165 ui_->horizontalSlider_iterations->setEnabled(
false);
1166 ui_->horizontalSlider_iterations->setMaximum(0);
1167 ui_->horizontalSlider_neighbors->setEnabled(
false);
1168 ui_->horizontalSlider_neighbors->setMaximum(0);
1169 ui_->label_constraint->clear();
1170 ui_->label_constraint_opt->clear();
1171 ui_->label_variance->clear();
1172 ui_->lineEdit_covariance->clear();
1173 ui_->label_type->clear();
1174 ui_->label_type_name->clear();
1175 ui_->checkBox_showOptimized->setEnabled(
false);
1177 ui_->horizontalSlider_A->setEnabled(
false);
1178 ui_->horizontalSlider_A->setMaximum(0);
1179 ui_->horizontalSlider_B->setEnabled(
false);
1180 ui_->horizontalSlider_B->setMaximum(0);
1181 ui_->label_idA->setText(
"NaN");
1182 ui_->label_idB->setText(
"NaN");
1186 ui_->spinBox_indexA->setEnabled(
false);
1187 ui_->spinBox_indexA->setMaximum(0);
1188 ui_->spinBox_indexB->setEnabled(
false);
1189 ui_->spinBox_indexB->setMaximum(0);
1200 ui_->graphViewer->clearAll();
1201 ui_->label_loopClosures->clear();
1202 ui_->label_timeOptimization->clear();
1203 ui_->label_pathLength->clear();
1204 ui_->label_poses->clear();
1205 ui_->label_rmse->clear();
1206 ui_->spinBox_optimizationsFrom->setEnabled(
false);
1208 ui_->graphicsView_A->clear();
1209 ui_->graphicsView_B->clear();
1211 ui_->graphicsView_stereo->clear();
1215 ui_->toolBox_statistics->clear();
1231 QString
path = QFileDialog::getOpenFileName(
this, tr(
"Select file"),
pathDatabase_, tr(
"Databases (*.db)"));
1236 QMessageBox::information(
this,
"Database recovery", tr(
"The selected database is already opened, close it first."));
1239 std::string errorMsg;
1241 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1243 progressDialog->show();
1248 QMessageBox::information(
this,
"Database recovery", tr(
"Database \"%1\" recovered! Try opening it again.").
arg(
path));
1252 QMessageBox::warning(
this,
"Database recovery", tr(
"Database recovery failed: \"%1\".").
arg(errorMsg.c_str()));
1262 if(this->isWindowModified())
1264 QMessageBox::Button
b=QMessageBox::question(
this,
1265 tr(
"Database Viewer"),
1266 tr(
"There are unsaved changed settings. Save them?"),
1267 QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard);
1268 if(
b == QMessageBox::Save)
1272 else if(
b != QMessageBox::Discard)
1291 if(event->isAccepted())
1293 ui_->toolBox_statistics->closeFigures();
1304 this->setWindowModified(
false);
1314 if(this->isVisible())
1327 if(this->isVisible())
1336 if((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_S)
1344 if (event->type() == QEvent::Resize && qobject_cast<QDockWidget*>(obj))
1346 this->setWindowModified(
true);
1348 return QWidget::eventFilter(obj, event);
1372 double previousStamp = 0;
1373 std::vector<double> delays(
ids_.size());
1375 std::map<int, Transform> poses;
1376 std::map<int, double> stamps;
1377 std::map<int, Transform> groundTruths;
1378 std::map<int, GPS> gpsValues;
1379 std::map<int, EnvSensors> sensorsValues;
1380 for(
int i=0;
i<
ids_.size();
i+=1+framesIgnored)
1392 if(frameRate == 0 ||
1393 previousStamp == 0 ||
1395 stamp - previousStamp >= 1.0/frameRate)
1397 if(sessionExported < 0 || sessionExported == mapId)
1399 ids.push_back(
ids_[
i]);
1401 if(previousStamp && stamp)
1403 delays[oi++] = stamp - previousStamp;
1405 previousStamp = stamp;
1407 poses.insert(std::make_pair(
ids_[
i], odomPose));
1408 stamps.insert(std::make_pair(
ids_[
i], stamp));
1409 groundTruths.insert(std::make_pair(
ids_[
i], groundTruth));
1410 if(gps.
stamp() > 0.0)
1412 gpsValues.insert(std::make_pair(
ids_[
i], gps));
1416 sensorsValues.insert(std::make_pair(
ids_[
i], sensors));
1420 if(sessionExported >= 0 && mapId > sessionExported)
1431 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1433 progressDialog->show();
1435 UINFO(
"Decompress: rgb=%d depth=%d scan=%d userData=%d",
1441 for(
int i=0;
i<ids.size() && !progressDialog->
isCanceled(); ++
i)
1447 cv::Mat depth, rgb, userData;
1449 data.uncompressDataConst(
1454 cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1);
1457 std::multimap<int, Link> links;
1459 if(links.size() && links.begin()->first <
id)
1461 covariance = links.begin()->second.infMatrix().inv();
1466 if(
data.cameraModels().size())
1472 data.cameraModels(),
1483 data.stereoCameraModels(),
1488 if(groundTruths.find(
id)!=groundTruths.end())
1492 if(gpsValues.find(
id)!=gpsValues.end())
1494 sensorData.
setGPS(gpsValues.at(
id));
1496 if(sensorsValues.find(
id)!=sensorsValues.end())
1505 QApplication::processEvents();
1510 progressDialog->
appendText(tr(
"Average frame rate=%1 Hz (Min=%2, Max=%3)")
1517 UERROR(
"DataRecorder init failed?!");
1522 QMessageBox::warning(
this, tr(
"Cannot export database"), tr(
"An output path must be set!"));
1534 QStringList formats;
1535 formats.push_back(
"id.jpg");
1536 formats.push_back(
"id.png");
1537 formats.push_back(
"timestamp.jpg");
1538 formats.push_back(
"timestamp.png");
1540 QString
format = QInputDialog::getItem(
this, tr(
"Which RGB format?"), tr(
"Format:"), formats, 0,
false, &ok);
1546 QString ext =
format.split(
'.').back();
1547 bool useStamp =
format.split(
'.').front().compare(
"timestamp") == 0;
1548 bool directoriesCreated =
false;
1549 QString
path = QFileDialog::getExistingDirectory(
this, tr(
"Select directory where to save images..."),
pathDatabase_);
1553 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1557 progressDialog->show();
1559 int imagesExported = 0;
1560 for(
int i=0;
i<
ids_.size(); ++
i)
1562 QString
id = QString::number(
ids_.at(
i));
1566 data.uncompressData();
1568 if(!directoriesCreated)
1571 if(!
data.imageRaw().empty() && !
data.rightRaw().empty())
1574 dir.mkdir(QString(
"%1/left").
arg(
path));
1575 dir.mkdir(QString(
"%1/right").
arg(
path));
1576 dir.mkdir(QString(
"%1/calib").
arg(
path));
1577 directoriesCreated =
true;
1579 else if(!
data.imageRaw().empty())
1581 if(!
data.depthRaw().empty())
1584 dir.mkdir(QString(
"%1/rgb").
arg(
path));
1585 dir.mkdir(QString(
"%1/depth").
arg(
path));
1586 dir.mkdir(QString(
"%1/calib").
arg(
path));
1587 directoriesCreated =
true;
1592 if(!
data.imageRaw().empty() && useStamp)
1598 std::vector<float>
v;
1604 UWARN(
"Node %d has null timestamp! Using id instead!",
ids_.at(
i));
1608 id = QString::number(stamp,
'f');
1612 if(!
data.imageRaw().empty())
1614 if(!
data.rightRaw().empty())
1616 if(!cv::imwrite(QString(
"%1/left/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1618 if(!cv::imwrite(QString(
"%1/right/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.rightRaw()))
1620 UINFO(QString(
"Saved left/%1.%2 and right/%1.%2").
arg(
id).
arg(ext).toStdString().
c_str());
1624 UERROR(
"Cannot save calibration file, database name is empty!");
1626 else if(
data.stereoCameraModels().size()>=1 &&
data.stereoCameraModels().front().isValidForProjection())
1628 for(
size_t i=0;
i<
data.stereoCameraModels().
size(); ++
i)
1630 std::string cameraName =
id.toStdString();
1631 if(
data.stereoCameraModels().size()>1)
1637 data.imageRaw().size(),
1638 data.stereoCameraModels()[
i].left().K_raw(),
1639 data.stereoCameraModels()[
i].left().D_raw(),
1640 data.stereoCameraModels()[
i].left().R(),
1641 data.stereoCameraModels()[
i].left().P(),
1642 data.rightRaw().size(),
1643 data.stereoCameraModels()[
i].right().K_raw(),
1644 data.stereoCameraModels()[
i].right().D_raw(),
1645 data.stereoCameraModels()[
i].right().R(),
1646 data.stereoCameraModels()[
i].right().P(),
1647 data.stereoCameraModels()[
i].R(),
1648 data.stereoCameraModels()[
i].T(),
1649 data.stereoCameraModels()[
i].E(),
1650 data.stereoCameraModels()[
i].F(),
1651 data.stereoCameraModels()[
i].left().localTransform());
1652 if(
model.save(
path.toStdString() +
"/calib"))
1654 UINFO(
"Saved stereo calibration \"%s\"", (
path.toStdString()+
"/calib/"+cameraName+
".yaml").c_str());
1658 UERROR(
"Failed saving calibration \"%s\"", (
path.toStdString()+
"/calib/"+cameraName+
".yaml").c_str());
1665 if(!
data.depthRaw().empty())
1667 if(!cv::imwrite(QString(
"%1/rgb/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1670 UWARN(
"Failed saving \"%s\"", QString(
"%1/depth/%2.png").
arg(
path).
arg(
id).toStdString().
c_str());
1671 UINFO(QString(
"Saved rgb/%1.%2 and depth/%1.png").
arg(
id).
arg(ext).toStdString().
c_str());
1675 if(!cv::imwrite(QString(
"%1/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1683 UERROR(
"Cannot save calibration file, database name is empty!");
1685 else if(
data.cameraModels().size() >= 1 &&
data.cameraModels().front().isValidForProjection())
1687 for(
size_t i=0;
i<
data.cameraModels().
size(); ++
i)
1689 std::string cameraName =
id.toStdString();
1690 if(
data.cameraModels().size()>1)
1695 data.imageRaw().size(),
1696 data.cameraModels()[
i].K_raw(),
1697 data.cameraModels()[
i].D_raw(),
1698 data.cameraModels()[
i].R(),
1699 data.cameraModels()[
i].P(),
1700 data.cameraModels()[
i].localTransform());
1701 std::string dirPrefix =
"";
1702 if(!
data.depthRaw().empty())
1704 dirPrefix =
"/calib";
1706 if(
model.save(
path.toStdString()+dirPrefix))
1708 UINFO(
"Saved calibration \"%s\"", (
path.toStdString()+dirPrefix+
"/"+cameraName+
".yaml").c_str());
1712 UERROR(
"Failed saving calibration \"%s\"", (
path.toStdString()+
"/"+cameraName+
".yaml").c_str());
1720 QApplication::processEvents();
1730 QMessageBox::information(
this, tr(
"Exporting"), tr(
"%1 images exported!").
arg(imagesExported));
1742 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1743 int progressSteps = 5;
1744 if(
ui_->graphViewer->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
1748 if(
ui_->textEdit_info->isVisible())
1752 if(
ui_->toolBox_statistics->isVisible())
1757 progressDialog->show();
1760 progressDialog->
appendText(tr(
"Loading all ids..."));
1761 QApplication::processEvents();
1763 QApplication::processEvents();
1765 UINFO(
"Loading all IDs...");
1768 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
1769 ids_ = QList<int>(ids.begin(), ids.end());
1771 ids_ = QList<int>::fromStdList(std::list<int>(ids.begin(), ids.end()));
1784 ui_->checkBox_wmState->setVisible(
false);
1785 ui_->checkBox_alignPosesWithGPS->setVisible(
false);
1786 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
1787 ui_->checkBox_alignPosesWithGroundTruth->setVisible(
false);
1788 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
1789 ui_->checkBox_alignScansCloudsWithGroundTruth->setVisible(
false);
1790 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
1791 ui_->doubleSpinBox_optimizationScale->setVisible(
false);
1792 ui_->label_scale_title->setVisible(
false);
1793 ui_->label_rmse->setVisible(
false);
1794 ui_->label_rmse_title->setVisible(
false);
1795 ui_->checkBox_ignoreIntermediateNodes->setVisible(
false);
1796 ui_->label_ignoreINtermediateNdoes->setVisible(
false);
1797 ui_->label_alignPosesWithGPS->setVisible(
false);
1798 ui_->label_alignPosesWithGroundTruth->setVisible(
false);
1799 ui_->label_alignScansCloudsWithGroundTruth->setVisible(
false);
1800 ui_->toolButton_edit_priorA->setVisible(
false);
1801 ui_->toolButton_edit_priorB->setVisible(
false);
1802 ui_->toolButton_remove_priorA->setVisible(
false);
1803 ui_->toolButton_remove_priorB->setVisible(
false);
1804 ui_->menuEdit->setEnabled(
true);
1805 ui_->actionGenerate_3D_map_pcd->setEnabled(
true);
1806 ui_->actionExport->setEnabled(
true);
1807 ui_->actionExtract_images->setEnabled(
true);
1808 ui_->menuExport_poses->setEnabled(
false);
1809 ui_->menuExport_GPS->setEnabled(
false);
1810 ui_->actionPoses_KML->setEnabled(
false);
1811 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
1812 ui_->actionExport_saved_2D_map->setEnabled(
false);
1813 ui_->actionImport_2D_map->setEnabled(
false);
1814 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
1815 ui_->actionView_optimized_mesh->setEnabled(
false);
1816 ui_->actionExport_optimized_mesh->setEnabled(
false);
1822 ui_->toolBox_statistics->clear();
1823 ui_->label_optimizeFrom->setText(tr(
"Root"));
1825 progressDialog->
appendText(tr(
"%1 ids loaded!").
arg(ids.size()));
1827 progressDialog->
appendText(tr(
"Loading all links..."));
1828 QApplication::processEvents();
1830 QApplication::processEvents();
1832 std::multimap<int, Link> unilinks;
1834 UDEBUG(
"%d total links loaded", (
int)unilinks.size());
1836 std::multimap<int, Link> links;
1837 for(std::multimap<int, Link>::iterator
iter=unilinks.begin();
iter!=unilinks.end(); ++
iter)
1839 links.insert(*
iter);
1842 links.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
1846 progressDialog->
appendText(tr(
"%1 links loaded!").
arg(unilinks.size()));
1848 progressDialog->
appendText(
"Loading Working Memory state...");
1849 QApplication::processEvents();
1851 QApplication::processEvents();
1857 double previousStamp = 0.0;
1861 progressDialog->
appendText(
"Loading Working Memory state... done!");
1863 progressDialog->
appendText(
"Loading info for all nodes...");
1864 QApplication::processEvents();
1866 QApplication::processEvents();
1868 int lastValidNodeId = 0;
1869 for(
int i=0;
i<
ids_.size(); ++
i)
1878 std::vector<float>
v;
1891 lastValidNodeId =
ids_[
i];
1895 if(wmStates.find(
ids_[
i]) != wmStates.end())
1901 ui_->checkBox_ignoreIntermediateNodes->setVisible(
true);
1902 ui_->label_ignoreINtermediateNdoes->setVisible(
true);
1908 if(!
p.isNull() && !previousPose.
isNull())
1913 if(previousStamp > 0.0 &&
s > 0.0)
1927 for(std::multimap<int, Link>::iterator jter=links.find(
ids_[
i]); jter!=links.end() && jter->first ==
ids_[
i]; ++jter)
1934 std::multimap<int, Link>::iterator invertedLinkIter =
graph::findLink(links, jter->second.to(), jter->second.from(),
false, jter->second.type());
1935 if( jter->second.isValid() &&
1936 ids.find(jter->second.from()) != ids.end() &&
1937 (ids.find(jter->second.to()) != ids.end() || jter->second.to()<0) &&
1939 invertedLinkIter != links.end() &&
1943 if(jter->second.userDataCompressed().cols == 0 &&
1944 invertedLinkIter->second.userDataCompressed().cols != 0)
1946 links_.insert(std::make_pair(invertedLinkIter->second.from(), invertedLinkIter->second));
1950 links_.insert(std::make_pair(
ids_[
i], jter->second));
1960 if(gps.
stamp() > 0.0)
1964 cv::Point3f
p(0.0
f,0.0
f,0.0
f);
1978 progressDialog->
appendText(
"Loading info for all nodes... done!");
1980 progressDialog->
appendText(
"Loading optimized poses and maps...");
1981 QApplication::processEvents();
1983 QApplication::processEvents();
1990 ui_->checkBox_alignPosesWithGPS->setVisible(!
gpsPoses_.empty());
1991 ui_->checkBox_alignPosesWithGPS->setEnabled(!
gpsPoses_.empty());
1992 ui_->label_alignPosesWithGPS->setVisible(!
gpsPoses_.empty());
2002 ui_->menuExport_GPS->setEnabled(
true);
2003 ui_->actionPoses_KML->setEnabled(
true);
2006 float xMin, yMin, cellSize;
2008 ui_->actionEdit_optimized_2D_map->setEnabled(hasMap);
2009 ui_->actionExport_saved_2D_map->setEnabled(hasMap);
2010 ui_->actionImport_2D_map->setEnabled(hasMap);
2015 ui_->actionView_optimized_mesh->setEnabled(
true);
2016 ui_->actionExport_optimized_mesh->setEnabled(
true);
2021 progressDialog->
appendText(
"Loading optimized poses and maps... done!");
2023 QApplication::processEvents();
2025 QApplication::processEvents();
2027 if(
ids_.size() &&
ui_->toolBox_statistics->isVisible())
2029 progressDialog->
appendText(
"Loading statistics...");
2030 QApplication::processEvents();
2032 QApplication::processEvents();
2034 UINFO(
"Update statistics...");
2037 progressDialog->
appendText(
"Loading statistics... done!");
2039 QApplication::processEvents();
2041 QApplication::processEvents();
2045 ui_->textEdit_info->clear();
2046 if(
ui_->textEdit_info->isVisible())
2048 progressDialog->
appendText(
"Update database info...");
2049 QApplication::processEvents();
2051 QApplication::processEvents();
2055 progressDialog->
appendText(
"Update database info... done!");
2057 QApplication::processEvents();
2059 QApplication::processEvents();
2066 bool nullPoses =
odomPoses_.begin()->second.isNull();
2069 if((!
iter->second.isNull() && nullPoses) ||
2070 (
iter->second.isNull() && !nullPoses))
2072 if(
iter->second.isNull())
2076 UWARN(
"Mixed valid and null poses! Ignoring graph...");
2091 ui_->spinBox_optimizationsFrom->setValue(
odomPoses_.begin()->first);
2096 if(lastValidNodeId>0)
2101 std::map<int, rtabmap::Transform> posesOut;
2102 std::multimap<int, rtabmap::Link> linksOut;
2103 UINFO(
"Get connected graph from %d (%d poses, %d links)", lastValidNodeId, (
int)
odomPoses_.size(), (
int)
links_.size());
2111 if(!posesOut.empty())
2113 bool optimizeFromGraphEnd = Parameters::defaultRGBDOptimizeFromGraphEnd();
2115 if(optimizeFromGraphEnd)
2117 ui_->spinBox_optimizationsFrom->setValue(posesOut.rbegin()->first);
2121 ui_->spinBox_optimizationsFrom->setValue(posesOut.lower_bound(1)->first);
2135 ui_->spinBox_indexA->setMinimum(0);
2136 ui_->spinBox_indexB->setMinimum(0);
2137 ui_->spinBox_indexA->setMaximum(
ids_.size()-1);
2138 ui_->spinBox_indexB->setMaximum(
ids_.size()-1);
2139 ui_->spinBox_indexA->setEnabled(
true);
2140 ui_->spinBox_indexB->setEnabled(
true);
2142 ui_->horizontalSlider_A->setMinimum(0);
2143 ui_->horizontalSlider_B->setMinimum(0);
2144 ui_->horizontalSlider_A->setMaximum(
ids_.size()-1);
2145 ui_->horizontalSlider_B->setMaximum(
ids_.size()-1);
2146 ui_->horizontalSlider_A->setEnabled(
true);
2147 ui_->horizontalSlider_B->setEnabled(
true);
2148 ui_->horizontalSlider_A->setSliderPosition(0);
2149 ui_->horizontalSlider_B->setSliderPosition(0);
2155 ui_->horizontalSlider_A->setEnabled(
false);
2156 ui_->horizontalSlider_B->setEnabled(
false);
2158 ui_->spinBox_indexA->setEnabled(
false);
2159 ui_->spinBox_indexB->setEnabled(
false);
2161 ui_->label_idA->setText(
"NaN");
2162 ui_->label_idB->setText(
"NaN");
2169 if(
ui_->graphViewer->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
2171 progressDialog->
appendText(
"Updating Graph View...");
2172 QApplication::processEvents();
2174 QApplication::processEvents();
2178 progressDialog->
appendText(
"Updating Graph View... done!");
2180 QApplication::processEvents();
2182 QApplication::processEvents();
2190 UINFO(
"Update database info...");
2193 if(
ui_->textEdit_info->toPlainText().isEmpty())
2200 ui_->textEdit_info->append(tr(
"Total odometry length:\t%1 m (approx. as graph has been reduced)").
arg(
infoTotalOdom_));
2207 int lastWordIdId = 0;
2214 ids.insert(lastWordIdId);
2215 std::list<VisualWord *> vws;
2219 wordsDim = vws.front()->getDescriptor().cols;
2220 wordsType = vws.front()->getDescriptor().type();
2226 ui_->textEdit_info->append(tr(
"Total time:\t\t%1").
arg(QDateTime::fromMSecsSinceEpoch(
infoTotalTime_*1000).toUTC().toString(
"hh:mm:ss.zzz")));
2232 ui_->textEdit_info->append(
"");
2236 ui_->textEdit_info->append(tr(
"Database size:\t%1 %2").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes"));
2239 ui_->textEdit_info->append(tr(
"Nodes size:\t\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2242 ui_->textEdit_info->append(tr(
"Links size:\t\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2245 ui_->textEdit_info->append(tr(
"RGB Images size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2248 ui_->textEdit_info->append(tr(
"Depth Images size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2251 ui_->textEdit_info->append(tr(
"Calibrations size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2254 ui_->textEdit_info->append(tr(
"Grids size:\t\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2257 ui_->textEdit_info->append(tr(
"Scans size:\t\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2260 ui_->textEdit_info->append(tr(
"User data size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2263 ui_->textEdit_info->append(tr(
"Dictionary size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2266 ui_->textEdit_info->append(tr(
"Features size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2269 ui_->textEdit_info->append(tr(
"Statistics size:\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2270 mem = dbSize - total;
2271 ui_->textEdit_info->append(tr(
"Other (indexing, unused):\t%1 %2\t%3%").
arg(mem>1000000?mem/1000000:mem>1000?mem/1000:mem).
arg(mem>1000000?
"MB":mem>1000?
"KB":
"Bytes").
arg(dbSize>0?QString::number(
double(mem)/
double(dbSize)*100.0,
'f', 2 ):
"0"));
2272 ui_->textEdit_info->append(
"");
2273 std::set<int> idsWithoutBad;
2275 int infoBadcountInLTM = 0;
2276 int infoBadCountInGraph = 0;
2277 for(
int i=0;
i<
ids_.size(); ++
i)
2279 if(idsWithoutBad.find(
ids_[
i]) == idsWithoutBad.end())
2281 ++infoBadcountInLTM;
2284 ++infoBadCountInGraph;
2288 ui_->textEdit_info->append(tr(
"%1 bad signatures in LTM").
arg(infoBadcountInLTM));
2289 ui_->textEdit_info->append(tr(
"%1 bad signatures in the global graph").
arg(infoBadCountInGraph));
2290 ui_->textEdit_info->append(
"");
2292 QFontMetrics metrics(
ui_->textEdit_info->font());
2293 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2294 int tabW =
ui_->textEdit_info->tabStopDistance();
2296 int tabW =
ui_->textEdit_info->tabStopWidth();
2298 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end(); ++
iter)
2300 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2301 int strW = metrics.horizontalAdvance(QString(
iter->first.c_str()) +
"=");
2303 int strW = metrics.width(QString(
iter->first.c_str()) +
"=");
2305 ui_->textEdit_info->append(tr(
"%1=%2%3")
2307 .
arg(strW < tabW?
"\t\t\t\t":strW < tabW*2?
"\t\t\t":strW < tabW*3?
"\t\t":
"\t")
2308 .arg(
iter->second.c_str()));
2312 ui_->textEdit_info->moveCursor(QTextCursor::Start) ;
2313 ui_->textEdit_info->ensureCursorVisible() ;
2318 ui_->textEdit_info->clear();
2327 ui_->toolBox_statistics->clear();
2328 double firstStamp = 0.0;
2331 std::map<std::string, std::pair<std::vector<qreal>, std::vector<qreal> > > allData;
2332 std::map<std::string, int > allDataOi;
2334 for(std::map<
int, std::pair<std::map<std::string, float>,
double> >::
iterator jter=allStats.begin(); jter!=allStats.end(); ++jter)
2336 double stamp=jter->second.second;
2337 std::map<std::string, float> & statistics = jter->second.first;
2342 for(std::map<std::string, float>::iterator
iter=statistics.begin();
iter!=statistics.end(); ++
iter)
2344 if(allData.find(
iter->first) == allData.end())
2347 allData.insert(std::make_pair(
iter->first, std::make_pair(std::vector<qreal>(allStats.size(), 0.0f), std::vector<qreal>(allStats.size(), 0.0f) )));
2348 allDataOi.insert(std::make_pair(
iter->first, 0));
2351 int & oi = allDataOi.at(
iter->first);
2352 allData.at(
iter->first).first[oi] =
ui_->checkBox_timeStats->isChecked()?qreal(stamp-firstStamp):jter->first;
2353 allData.at(
iter->first).second[oi] =
iter->second;
2358 for(std::map<std::string, std::pair<std::vector<qreal>, std::vector<qreal> > >::
iterator iter=allData.begin();
iter!=allData.end(); ++
iter)
2360 int oi = allDataOi.at(
iter->first);
2361 iter->second.first.resize(oi);
2362 iter->second.second.resize(oi);
2363 ui_->toolBox_statistics->updateStat(
iter->first.c_str(),
iter->second.first,
iter->second.second,
true);
2371 QColor
c = QColorDialog::getColor(
ui_->lineEdit_obstacleColor->text(),
this);
2374 ui_->lineEdit_obstacleColor->setText(
c.name());
2379 QColor
c = QColorDialog::getColor(
ui_->lineEdit_groundColor->text(),
this);
2382 ui_->lineEdit_groundColor->setText(
c.name());
2387 QColor
c = QColorDialog::getColor(
ui_->lineEdit_emptyColor->text(),
this);
2390 ui_->lineEdit_emptyColor->setText(
c.name());
2395 QColor
c = QColorDialog::getColor(
ui_->lineEdit_frontierColor->text(),
this);
2398 ui_->lineEdit_frontierColor->setText(
c.name());
2412 data.uncompressData();
2413 if(!
data.depthRaw().empty())
2467 types.push_back(
"Map's graph (see Graph View)");
2468 types.push_back(
"Odometry");
2471 types.push_back(
"Ground Truth");
2474 QString
type = QInputDialog::getItem(
this, tr(
"Which poses?"), tr(
"Poses:"), types, 0,
false, &ok);
2480 bool groundTruth =
type.compare(
"Ground Truth") == 0;
2484 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No ground truth poses in database?!"));
2490 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
2492 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No graph in database?!"));
2498 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No odometry poses in database?!"));
2504 QMessageBox::warning(
this, tr(
"Cannot export poses in KML format"), tr(
"No GPS in database?!"));
2508 std::map<int, Transform> optimizedPoses;
2523 (
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
2524 ui_->checkBox_alignPosesWithGPS->isChecked()) ||
2528 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked()))
2539 float translational_rmse = 0.0f;
2540 float translational_mean = 0.0f;
2541 float translational_median = 0.0f;
2542 float translational_std = 0.0f;
2543 float translational_min = 0.0f;
2544 float translational_max = 0.0f;
2545 float rotational_rmse = 0.0f;
2546 float rotational_mean = 0.0f;
2547 float rotational_median = 0.0f;
2548 float rotational_std = 0.0f;
2549 float rotational_min = 0.0f;
2550 float rotational_max = 0.0f;
2557 translational_median,
2571 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2573 iter->second = gtToMap *
iter->second;
2575 if(alignToGPS &&
format != 5 && optimizedPoses.find(
gpsValues_.begin()->first)!=optimizedPoses.end())
2579 Transform offset = optimizedPoses.at(originId).translation().inverse();
2580 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2591 std::map<int, GPS>
values;
2593 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2603 std::vector<float>
v;
2610 QString output =
pathDatabase_ + QDir::separator() +
"poses.kml";
2611 QString
path = QFileDialog::getSaveFileName(
2615 tr(
"Google Earth file (*.kml)"));
2623 QMessageBox::information(
this,
2624 tr(
"Export poses..."),
2625 tr(
"GPS coordinates saved to \"%1\".")
2630 QMessageBox::information(
this,
2631 tr(
"Export poses..."),
2632 tr(
"Failed to save GPS coordinates to \"%1\"!")
2639 if(optimizedPoses.size())
2641 std::map<int, Transform> localTransforms;
2643 items.push_back(
"Robot (base frame)");
2644 items.push_back(
"Camera");
2645 items.push_back(
"Scan");
2647 QString item = QInputDialog::getItem(
this, tr(
"Export Poses"), tr(
"Frame: "), items, 0,
false, &ok);
2648 if(!ok || item.isEmpty())
2652 if(item.compare(
"Robot (base frame)") != 0)
2654 bool cameraFrame = item.compare(
"Camera") == 0;
2655 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2660 std::vector<CameraModel> models;
2661 std::vector<StereoCameraModel> stereoModels;
2664 if((models.size() == 1 &&
2665 !models.at(0).localTransform().isNull()))
2667 localTransform = models.at(0).localTransform();
2669 else if(stereoModels.size() == 1 &&
2670 !stereoModels[0].localTransform().isNull())
2672 localTransform = stereoModels[0].localTransform();
2674 else if(models.size()>1)
2676 UWARN(
"Multi-camera is not supported (node %d)",
iter->first);
2680 UWARN(
"Calibration not valid for node %d",
iter->first);
2685 UWARN(
"Missing calibration for node %d",
iter->first);
2693 localTransform =
info.localTransform();
2697 UWARN(
"Missing scan info for node %d",
iter->first);
2701 if(!localTransform.
isNull())
2703 localTransforms.insert(std::make_pair(
iter->first, localTransform));
2706 if(localTransforms.empty())
2708 QMessageBox::warning(
this,
2710 tr(
"Could not find any \"%1\" frame, exporting in Robot frame instead.").
arg(item));
2714 std::map<int, Transform> poses;
2715 std::multimap<int, Link> links;
2716 if(localTransforms.empty())
2718 poses = optimizedPoses;
2727 for(std::map<int, Transform>::iterator
iter=localTransforms.begin();
iter!=localTransforms.end(); ++
iter)
2729 poses.insert(std::make_pair(
iter->first, optimizedPoses.at(
iter->first) *
iter->second));
2737 std::multimap<int, Link>::iterator inserted = links.insert(*
iter);
2738 int from =
iter->second.from();
2739 int to =
iter->second.to();
2740 inserted->second.setTransform(localTransforms.at(from).inverse()*
iter->second.transform()*localTransforms.at(to));
2746 if(
format != 4 &&
format != 11 && !poses.empty() && poses.begin()->first<0)
2748 UWARN(
"Only g2o format (4) and RGBD format with ID format can export landmarks, they are ignored with format %d",
format);
2749 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end() &&
iter->first < 0;)
2751 poses.erase(
iter++);
2753 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end();)
2755 if(
iter->second.from() < 0 ||
iter->second.to() < 0)
2757 links.erase(
iter++);
2766 std::map<int, double> stamps;
2769 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
2773 stamps.insert(std::make_pair(
iter->first, 0));
2782 std::vector<float>
v;
2787 stamps.insert(std::make_pair(
iter->first, stamp));
2791 if(stamps.size()!=poses.size())
2793 QMessageBox::warning(
this, tr(
"Export poses..."), tr(
"Poses (%1) and stamps (%2) have not the same size! Cannot export in RGB-D SLAM format.")
2794 .
arg(poses.size()).
arg(stamps.size()));
2800 QString suffix =
odometry?
"_odom":groundTruth?
"_gt":
"";
2801 output = output.arg(suffix);
2803 QString
path = QFileDialog::getSaveFileName(
2807 format == 3?tr(
"TORO file (*.graph)"):
format==4?tr(
"g2o file (*.g2o)"):tr(
"Text file (*.txt)"));
2811 if(QFileInfo(
path).suffix() ==
"")
2831 QMessageBox::information(
this,
2832 tr(
"Export poses..."),
2833 tr(
"%1 saved to \"%2\".")
2839 QMessageBox::information(
this,
2840 tr(
"Export poses..."),
2841 tr(
"Failed to save %1 to \"%2\"!")
2863 QString
path = QFileDialog::getSaveFileName(
2867 format==0?tr(
"Raw format (*.txt)"):tr(
"Google Earth file (*.kml)"));
2875 QMessageBox::information(
this,
2876 tr(
"Export poses..."),
2877 tr(
"GPS coordinates saved to \"%1\".")
2882 QMessageBox::information(
this,
2883 tr(
"Export poses..."),
2884 tr(
"Failed to save GPS coordinates to \"%1\"!")
2895 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
2901 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
2902 tr(
"The database has too old version (%1) to saved "
2909 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
2910 tr(
"The database has modified links, the 2D optimized map cannot be modified."));
2914 float xMin, yMin, cellSize;
2918 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"The database doesn't contain a saved 2D map."));
2923 cv::Mat map8UFlip, map8URotated;
2924 cv::flip(map8U, map8UFlip, 0);
2925 if(!
ui_->graphViewer->isOrientationENU())
2928 cv::transpose(map8UFlip, map8URotated);
2929 cv::flip(map8URotated, map8URotated, 0);
2933 map8URotated = map8UFlip;
2942 if(!
ui_->graphViewer->isOrientationENU())
2945 cv::transpose(mapModified, map8URotated);
2946 cv::flip(map8URotated, map8URotated, 1);
2950 map8URotated = mapModified;
2952 cv::flip(map8URotated, map8UFlip, 0);
2954 UASSERT(map8UFlip.type() == map8U.type());
2955 UASSERT(map8UFlip.cols == map8U.cols);
2956 UASSERT(map8UFlip.rows == map8U.rows);
2963 QMessageBox::information(
this, tr(
"Edit 2D map"), tr(
"Map updated!"));
2966 int cropRadius =
ui_->spinBox_cropRadius->value();
2967 QMessageBox::StandardButton
b = QMessageBox::question(
this,
2968 tr(
"Crop empty space"),
2969 tr(
"Do you want to clear empty space from local occupancy grids and laser scans?\n\n"
2971 " * If the map needs to be regenerated in the future (e.g., when we re-use the map in SLAM mode), removed obstacles won't reappear.\n"
2972 " * The cropped laser scans will be also used for localization, so if dynamic obstacles have been removed, localization won't try to match them anymore.\n\n"
2974 " * Cropping the laser scans cannot be reverted after the viewer is closed and changes have been saved.\n\n"
2976 " Crop radius = %1 pixels\n\n"
2977 "Press \"Yes\" to filter only grids.\n"
2978 "Press \"Yes to All\" to filter both grids and laser scans.\n").
arg(cropRadius),
2979 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No, QMessageBox::No);
2980 if(
b == QMessageBox::Yes ||
b == QMessageBox::YesToAll)
2988 progressDialog.show();
2990 progressDialog.
appendText(QString(
"Cropping empty space... %1 scans to filter").
arg(poses.size()));
2991 progressDialog.setMinimumWidth(800);
2992 QApplication::processEvents();
2994 UINFO(
"Cropping empty space... poses=%d cropRadius=%d", poses.size(), cropRadius);
2996 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end() && !progressDialog.
isCanceled(); ++
iter)
3000 cv::Mat gridObstacles;
3007 data.uncompressData(0,0,&scan,0,&gridGround,&gridObstacles,&gridEmpty);
3013 if(!gridObstacles.empty())
3015 cv::Mat filtered = cv::Mat(1, gridObstacles.cols, gridObstacles.type());
3017 for(
int i=0;
i<gridObstacles.cols; ++
i)
3019 const float * ptr = gridObstacles.ptr<
float>(0,
i);
3020 cv::Point3f pt(ptr[0], ptr[1], gridObstacles.channels()==2?0:ptr[2]);
3023 int x =
int((pt.x - xMin) / cellSize + 0.5f);
3024 int y =
int((pt.y - yMin) / cellSize + 0.5f);
3026 if(
x>=0 &&
x<map8S.cols &&
3027 y>=0 &&
y<map8S.rows)
3029 bool obstacleDetected =
false;
3031 for(
int j=-cropRadius;
j<=cropRadius && !obstacleDetected; ++
j)
3033 for(
int k=-cropRadius; k<=cropRadius && !obstacleDetected; ++k)
3035 if(
x+
j>=0 &&
x+
j<map8S.cols &&
3036 y+k>=0 &&
y+k<map8S.rows &&
3037 map8S.at<
signed char>(
y+k,
x+
j) == 100)
3039 obstacleDetected =
true;
3044 if(map8S.at<
unsigned char>(
y,
x) != 0 || obstacleDetected)
3053 if(oi != gridObstacles.cols)
3055 progressDialog.
appendText(QString(
"Grid %1 filtered %2 pts -> %3 pts").
arg(
iter->first).
arg(gridObstacles.cols).arg(oi));
3056 UINFO(
"Grid %d filtered %d -> %d",
iter->first, gridObstacles.cols, oi);
3059 cv::Mat newObstacles = cv::Mat(filtered,
cv::Range::all(), cv::Range(0, oi));
3063 value.obstacleCells = newObstacles;
3075 if(
b == QMessageBox::YesToAll && !scan.
isEmpty())
3079 cv::Mat filtered = cv::Mat(1, scan.
size(), scan.
dataType());
3081 for(
int i=0;
i<scan.
size(); ++
i)
3083 const float * ptr = scan.
data().ptr<
float>(0,
i);
3084 cv::Point3f pt(ptr[0], ptr[1], scan.
is2d()?0:ptr[2]);
3087 int x =
int((pt.x - xMin) / cellSize + 0.5f);
3088 int y =
int((pt.y - yMin) / cellSize + 0.5f);
3090 if(
x>=0 &&
x<map8S.cols &&
3091 y>=0 &&
y<map8S.rows)
3093 bool obstacleDetected =
false;
3095 for(
int j=-cropRadius;
j<=cropRadius && !obstacleDetected; ++
j)
3097 for(
int k=-cropRadius; k<=cropRadius && !obstacleDetected; ++k)
3099 if(
x+
j>=0 &&
x+
j<map8S.cols &&
3100 y+k>=0 &&
y+k<map8S.rows &&
3101 map8S.at<
signed char>(
y+k,
x+
j) == 100)
3103 obstacleDetected =
true;
3108 if(map8S.at<
unsigned char>(
y,
x) != 0 || obstacleDetected)
3117 if(oi != scan.
size())
3120 UINFO(
"Scan %d filtered %d -> %d",
iter->first, scan.
size(), oi);
3150 QApplication::processEvents();
3170 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3174 float xMin, yMin, cellSize;
3178 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"The database doesn't contain a saved 2D map."));
3184 QString
path = QFileDialog::getSaveFileName(
3192 if(QFileInfo(
path).suffix() ==
"")
3196 cv::imwrite(
path.toStdString(), map8U);
3199 QString yaml =
info.absolutePath() +
"/" +
info.baseName() +
".yaml";
3201 float occupancyThr = Parameters::defaultGridGlobalOccupancyThr();
3202 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridGlobalOccupancyThr(), occupancyThr);
3205 file.open (yaml.toStdString());
3206 file <<
"image: " <<
info.baseName().toStdString() <<
".pgm" << std::endl;
3207 file <<
"resolution: " << cellSize << std::endl;
3208 file <<
"origin: [" << xMin <<
", " << yMin <<
", 0.0]" << std::endl;
3209 file <<
"negate: 0" << std::endl;
3210 file <<
"occupied_thresh: " << occupancyThr << std::endl;
3211 file <<
"free_thresh: 0.196" << std::endl;
3216 QMessageBox::information(
this, tr(
"Export 2D map"), tr(
"Exported %1 and %2!").
arg(
path).
arg(yaml));
3225 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3231 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
3232 tr(
"The database has too old version (%1) to saved "
3239 QMessageBox::warning(
this, tr(
"Cannot import 2D map"),
3240 tr(
"The database has modified links, the 2D optimized map cannot be modified."));
3244 float xMin, yMin, cellSize;
3248 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"The database doesn't contain a saved 2D map."));
3252 QString
path = QFileDialog::getOpenFileName(
3259 cv::Mat map8U = cv::imread(
path.toStdString(), cv::IMREAD_UNCHANGED);
3262 if(mapOrg.cols == map.cols && mapOrg.rows == map8U.rows)
3265 QMessageBox::information(
this, tr(
"Import 2D map"), tr(
"Imported %1!").
arg(
path));
3269 QMessageBox::warning(
this, tr(
"Import 2D map"), tr(
"Cannot import %1 as its size doesn't match the current saved map. Import 2D Map action should only be used to modify the map saved in the database.").
arg(
path));
3279 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3285 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
3286 tr(
"The database has too old version (%1) to saved "
3293 QMessageBox::warning(
this, tr(
"Cannot import 2D map"),
3294 tr(
"The database has modified links and/or modified local "
3295 "occupancy grids, the 2D optimized map cannot be modified. Try "
3296 "closing the database and re-open it to save the changes."));
3302 QMessageBox::warning(
this, tr(
"Cannot regenerate 2D map"),
3303 tr(
"Graph is empty, make sure you opened the "
3304 "Graph View and there is a map shown."));
3309 #ifdef RTABMAP_OCTOMAP
3311 types.push_back(
"Default occupancy grid");
3312 types.push_back(
"From OctoMap projection");
3314 QString
type = QInputDialog::getItem(
this, tr(
"Which type?"), tr(
"Type:"), types, 0,
false, &ok);
3322 UINFO(
"Update local maps list...");
3325 float gridCellSize = Parameters::defaultGridCellSize();
3326 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridCellSize(), gridCellSize);
3327 #ifdef RTABMAP_OCTOMAP
3328 if(
type.compare(
"From OctoMap projection") == 0)
3341 map = grid.
getMap(xMin, yMin);
3346 QMessageBox::information(
this, tr(
"Regenerate 2D map"), tr(
"Failed to renegerate the map, resulting map is empty!"));
3356 lastlocalizationPose =
graphes_.back().rbegin()->second;
3361 QMessageBox::information(
this, tr(
"Regenerate 2D map"), tr(
"Map regenerated!"));
3362 ui_->actionEdit_optimized_2D_map->setEnabled(
true);
3363 ui_->actionExport_saved_2D_map->setEnabled(
true);
3364 ui_->actionImport_2D_map->setEnabled(
true);
3372 QMessageBox::warning(
this, tr(
"Cannot view optimized mesh"), tr(
"A database must must loaded first...\nUse File->Open database."));
3376 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
3377 #if PCL_VERSION_COMPARE(>=, 1, 8, 0)
3378 std::vector<std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > > texCoords;
3380 std::vector<std::vector<Eigen::Vector2f> > texCoords;
3384 if(cloudMat.empty())
3386 QMessageBox::warning(
this, tr(
"Cannot view optimized mesh"), tr(
"The database doesn't contain a saved optimized mesh."));
3391 viewer->setWindowFlags(Qt::Window);
3392 viewer->setAttribute(Qt::WA_DeleteOnClose);
3394 if(!textures.empty())
3398 viewer->setWindowTitle(
"Optimized Textured Mesh");
3402 else if(polygons.size() == 1)
3405 viewer->setWindowTitle(
"Optimized Mesh");
3413 viewer->setWindowTitle(
"Optimized Point Cloud");
3424 QMessageBox::warning(
this, tr(
"Cannot export optimized mesh"), tr(
"A database must must loaded first...\nUse File->Open database."));
3428 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
3429 #if PCL_VERSION_COMPARE(>=, 1, 8, 0)
3430 std::vector<std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > > texCoords;
3432 std::vector<std::vector<Eigen::Vector2f> > texCoords;
3436 if(cloudMat.empty())
3438 QMessageBox::warning(
this, tr(
"Cannot export optimized mesh"), tr(
"The database doesn't contain a saved optimized mesh."));
3444 if(!textures.empty())
3447 QString
path = QFileDialog::getSaveFileName(
3451 tr(
"Mesh (*.obj)"));
3455 if(QFileInfo(
path).suffix() ==
"")
3459 QString baseName = QFileInfo(
path).baseName();
3460 if(mesh->tex_materials.size() == 1)
3462 mesh->tex_materials.at(0).tex_file = baseName.toStdString() +
".png";
3463 cv::imwrite((QFileInfo(
path).absoluteDir().absolutePath()+QDir::separator()+baseName).toStdString() +
".png", textures);
3467 for(
unsigned int i=0;
i<mesh->tex_materials.size(); ++
i)
3469 mesh->tex_materials.at(
i).tex_file = (baseName+QDir::separator()+QString::number(
i)+
".png").toStdString();
3470 UASSERT((
i+1)*textures.rows <= (
unsigned int)textures.cols);
3471 cv::imwrite((QFileInfo(
path).absoluteDir().absolutePath()+QDir::separator()+baseName+QDir::separator()+QString::number(
i)+
".png").toStdString(), textures(
cv::Range::all(), cv::Range(
i*textures.rows, (
i+1)*textures.rows)));
3476 QMessageBox::information(
this, tr(
"Export Textured Mesh"), tr(
"Exported %1!").
arg(
path));
3479 else if(polygons.size() == 1)
3482 QString
path = QFileDialog::getSaveFileName(
3486 tr(
"Mesh (*.ply)"));
3490 if(QFileInfo(
path).suffix() ==
"")
3494 pcl::io::savePLYFileBinary(
path.toStdString(), *mesh);
3495 QMessageBox::information(
this, tr(
"Export Mesh"), tr(
"Exported %1!").
arg(
path));
3500 QString
path = QFileDialog::getSaveFileName(
3504 tr(
"Point cloud data (*.ply *.pcd)"));
3508 if(QFileInfo(
path).suffix() ==
"")
3512 bool success =
false;
3514 if(QFileInfo(
path).suffix() ==
"pcd")
3516 success = pcl::io::savePCDFile(
path.toStdString(), *cloud) == 0;
3520 success = pcl::io::savePLYFile(
path.toStdString(), *cloud) == 0;
3524 QMessageBox::information(
this, tr(
"Export Point Cloud"), tr(
"Exported %1!").
arg(
path));
3528 QMessageBox::critical(
this, tr(
"Export Point Cloud"), tr(
"Failed exporting %1!").
arg(
path));
3539 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
3546 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
3548 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
3553 std::map<int, Transform> optimizedPoses;
3554 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
3555 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked()
3564 if(
ui_->groupBox_posefiltering->isChecked())
3567 ui_->doubleSpinBox_posefilteringRadius->value(),
3568 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
3574 if(optimizedPoses.size() > 0)
3580 std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
3581 std::map<int, pcl::PolygonMesh::Ptr> meshes;
3582 std::map<int, pcl::TextureMesh::Ptr> textureMeshes;
3583 std::vector<std::map<int, pcl::PointXY> > textureVertexToPixels;
3589 QMap<int, Signature>(),
3590 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
3591 std::map<int, LaserScan>(),
3593 ui_->parameters_toolbox->getParameters(),
3597 textureVertexToPixels))
3599 if(textureMeshes.size())
3603 cv::Mat globalTextures;
3604 pcl::TextureMeshPtr textureMesh = textureMeshes.at(0);
3605 if(textureMesh->tex_materials.size()>1)
3609 std::map<int, cv::Mat>(),
3610 std::map<
int, std::vector<CameraModel> >(),
3615 textureVertexToPixels,
3628 textureMesh->tex_coordinates,
3630 QMessageBox::information(
this, tr(
"Update Optimized Textured Mesh"), tr(
"Updated!"));
3631 ui_->actionView_optimized_mesh->setEnabled(
true);
3632 ui_->actionExport_optimized_mesh->setEnabled(
true);
3635 else if(meshes.size())
3638 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons(1);
3641 QMessageBox::information(
this, tr(
"Update Optimized Mesh"), tr(
"Updated!"));
3642 ui_->actionView_optimized_mesh->setEnabled(
true);
3643 ui_->actionExport_optimized_mesh->setEnabled(
true);
3646 else if(clouds.size())
3650 QMessageBox::information(
this, tr(
"Update Optimized PointCloud"), tr(
"Updated!"));
3651 ui_->actionView_optimized_mesh->setEnabled(
true);
3652 ui_->actionExport_optimized_mesh->setEnabled(
true);
3657 QMessageBox::critical(
this, tr(
"Update Optimized Mesh"), tr(
"Nothing to save!"));
3664 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
3672 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"A database must must loaded first...\nUse File->Open database."));
3676 QString
path = QFileDialog::getSaveFileName(
this, tr(
"Save File"),
pathDatabase_+
"/Graph.dot", tr(
"Graphiz file (*.dot)"));
3687 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
3691 int id = QInputDialog::getInt(
this, tr(
"Around which location?"), tr(
"Location ID"),
ids_.first(),
ids_.first(),
ids_.last(), 1, &ok);
3695 int margin = QInputDialog::getInt(
this, tr(
"Depth around the location?"), tr(
"Margin"), 4, 1, 100, 1, &ok);
3698 QString
path = QFileDialog::getSaveFileName(
this, tr(
"Save File"),
pathDatabase_+
"/Graph" + QString::number(
id) +
".dot", tr(
"Graphiz file (*.dot)"));
3699 if(!
path.isEmpty() &&
id>0)
3701 std::map<int, int> ids;
3702 std::list<int> curentMarginList;
3703 std::set<int> currentMargin;
3704 std::set<int> nextMargin;
3705 nextMargin.insert(
id);
3707 while((margin == 0 ||
m < margin) && nextMargin.size())
3709 curentMarginList = std::list<int>(nextMargin.rbegin(), nextMargin.rend());
3712 for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end(); ++jter)
3714 if(ids.find(*jter) == ids.end())
3716 std::multimap<int, Link> links;
3717 ids.insert(std::pair<int, int>(*jter,
m));
3723 for(std::multimap<int, Link>::const_iterator
iter=links.begin();
iter!=links.end(); ++
iter)
3731 nextMargin.insert(
iter->first);
3736 if(currentMargin.insert(
iter->first).second)
3738 curentMarginList.push_back(
iter->first);
3750 ids.insert(std::pair<int,int>(
id, 0));
3751 std::set<int> idsSet;
3752 for(std::map<int, int>::iterator
iter = ids.begin();
iter!=ids.end(); ++
iter)
3754 idsSet.insert(idsSet.end(),
iter->first);
3757 UINFO(
"idsSet=%d", idsSet.size());
3762 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for signature %1.").
arg(
id));
3777 progressDialog.show();
3781 plot->setWindowFlags(Qt::Window);
3782 plot->setWindowTitle(
"Local Occupancy Grid Generation Time (ms)");
3783 plot->setAttribute(Qt::WA_DeleteOnClose);
3789 plotCells->setWindowFlags(Qt::Window);
3790 plotCells->setWindowTitle(
"Occupancy Cells");
3791 plotCells->setAttribute(Qt::WA_DeleteOnClose);
3798 double decompressionTime = 0;
3799 double gridCreationTime = 0;
3806 data.uncompressData();
3807 decompressionTime =
timer.ticks()*1000.0;
3820 s.setPose(odomPose);
3821 cv::Mat ground, obstacles,
empty;
3822 cv::Point3f viewpoint;
3825 if(
ui_->checkBox_grid_regenerateFromSavedGrid->isChecked() &&
s.sensorData().gridCellSize() > 0.0f)
3833 if(
s.sensorData().cameraModels().size())
3837 for(
unsigned int i=0;
i<
s.sensorData().cameraModels().
size(); ++
i)
3839 const Transform &
t =
s.sensorData().cameraModels()[
i].localTransform();
3842 viewpoint.x +=
t.
x();
3843 viewpoint.y +=
t.
y();
3844 viewpoint.z +=
t.z();
3855 else if(
s.sensorData().cameraModels().size())
3859 for(
unsigned int i=0;
i<
s.sensorData().stereoCameraModels().
size(); ++
i)
3861 const Transform &
t =
s.sensorData().stereoCameraModels()[
i].localTransform();
3864 viewpoint.x +=
t.
x();
3865 viewpoint.y +=
t.
y();
3866 viewpoint.z +=
t.z();
3890 gridCreationTime =
timer.ticks()*1000.0;
3892 msg = QString(
"Generated local occupancy grid map %1/%2").arg(
i+1).arg((
int)
ids_.size());
3903 decompressionCurve->
addValue(
ids_.at(
i), decompressionTime);
3906 if(
ids_.size() < 50 || (
i+1) % 25 == 0)
3908 QApplication::processEvents();
3929 if(
ids_.size() == 0)
3931 UWARN(
"ids_ is empty!");
3936 idsSet.insert(
ids_.at(
ui_->horizontalSlider_A->value()));
3937 idsSet.insert(
ids_.at(
ui_->horizontalSlider_B->value()));
3938 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
3939 QList<int> ids(idsSet.begin(), idsSet.end());
3941 QList<int> ids = idsSet.toList();
3946 progressDialog.show();
3948 for(
int i =0;
i<ids.size(); ++
i)
3952 data.uncompressData();
3965 s.setPose(odomPose);
3966 cv::Mat ground, obstacles,
empty;
3967 cv::Point3f viewpoint;
3969 if(
ui_->checkBox_grid_regenerateFromSavedGrid->isChecked() &&
s.sensorData().gridCellSize() > 0.0f)
3977 if(
s.sensorData().cameraModels().size())
3981 for(
unsigned int i=0;
i<
s.sensorData().cameraModels().
size(); ++
i)
3983 const Transform &
t =
s.sensorData().cameraModels()[
i].localTransform();
3986 viewpoint.x +=
t.
x();
3987 viewpoint.y +=
t.
y();
3988 viewpoint.z +=
t.z();
3999 else if(
s.sensorData().stereoCameraModels().size())
4003 for(
unsigned int i=0;
i<
s.sensorData().stereoCameraModels().
size(); ++
i)
4005 const Transform &
t =
s.sensorData().stereoCameraModels()[
i].localTransform();
4008 viewpoint.x +=
t.
x();
4009 viewpoint.y +=
t.
y();
4010 viewpoint.z +=
t.z();
4031 msg = QString(
"Generated local occupancy grid map %1/%2 (%3s)").arg(
i+1).arg((
int)ids.size()).arg(
time.ticks());
4036 QApplication::processEvents();
4055 QMessageBox::warning(
this, tr(
"Cannot view 3D map"), tr(
"The database is empty..."));
4062 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4064 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4069 std::map<int, Transform> optimizedPoses;
4070 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
4071 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
4080 if(
ui_->groupBox_posefiltering->isChecked())
4083 ui_->doubleSpinBox_posefilteringRadius->value(),
4084 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
4086 if(optimizedPoses.size() > 0)
4092 QMap<int, Signature>(),
4093 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
4094 std::map<int, LaserScan>(),
4096 ui_->parameters_toolbox->getParameters());
4100 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
4108 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
4115 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4117 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4122 std::map<int, Transform> optimizedPoses;
4123 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
4124 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
4134 ui_->checkBox_alignPosesWithGPS->isEnabled() &&
4135 ui_->checkBox_alignPosesWithGPS->isChecked();
4138 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked()))
4149 float translational_rmse = 0.0f;
4150 float translational_mean = 0.0f;
4151 float translational_median = 0.0f;
4152 float translational_std = 0.0f;
4153 float translational_min = 0.0f;
4154 float translational_max = 0.0f;
4155 float rotational_rmse = 0.0f;
4156 float rotational_mean = 0.0f;
4157 float rotational_median = 0.0f;
4158 float rotational_std = 0.0f;
4159 float rotational_min = 0.0f;
4160 float rotational_max = 0.0f;
4167 translational_median,
4181 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
4183 iter->second = gtToMap *
iter->second;
4185 if(alignToGPS && optimizedPoses.find(
gpsValues_.begin()->first)!=optimizedPoses.end())
4189 Transform offset = optimizedPoses.at(originId).translation().inverse();
4190 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
4199 if(
ui_->groupBox_posefiltering->isChecked())
4202 ui_->doubleSpinBox_posefilteringRadius->value(),
4203 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
4205 if(optimizedPoses.size() > 0)
4211 QMap<int, Signature>(),
4212 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
4213 std::map<int, LaserScan>(),
4215 ui_->parameters_toolbox->getParameters());
4219 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
4228 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4230 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4235 std::map<int, Transform> optimizedPoses =
graphes_.back();
4238 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4241 progressDialog->setMinimumWidth(800);
4242 progressDialog->show();
4244 const ParametersMap & parameters =
ui_->parameters_toolbox->getParameters();
4245 bool loopCovLimited = Parameters::defaultRGBDLoopCovLimited();
4246 Parameters::parse(parameters, Parameters::kRGBDLoopCovLimited(), loopCovLimited);
4252 int iterations =
ui_->spinBox_detectMore_iterations->value();
4255 std::multimap<int, int> checkedLoopClosures;
4256 std::pair<int, int> lastAdded(0,0);
4257 bool intraSession =
ui_->checkBox_detectMore_intraSession->isChecked();
4258 bool interSession =
ui_->checkBox_detectMore_interSession->isChecked();
4259 bool useOptimizedGraphAsGuess =
ui_->checkBox_opt_graph_as_guess->isChecked();
4260 if(!interSession && !intraSession)
4262 QMessageBox::warning(
this, tr(
"Cannot detect more loop closures"), tr(
"Intra and inter session parameters are disabled! Enable one or both."));
4266 for(
int n=0;
n<iterations; ++
n)
4268 UINFO(
"iteration %d/%d",
n+1, iterations);
4270 std::map<int, Transform>(optimizedPoses.upper_bound(0), optimizedPoses.end()),
4271 ui_->doubleSpinBox_detectMore_radius->value(),
4272 ui_->doubleSpinBox_detectMore_angle->value()*CV_PI/180.0);
4275 progressDialog->
appendText(tr(
"Looking for more loop closures, %1 clusters found.").
arg(clusters.size()));
4276 QApplication::processEvents();
4282 std::set<int> addedLinks;
4284 for(std::multimap<int, int>::iterator
iter=clusters.begin();
iter!= clusters.end() && !progressDialog->
isCanceled(); ++
iter, ++
i)
4286 int from =
iter->first;
4287 int to =
iter->second;
4290 from =
iter->second;
4297 if((interSession && mapIdFrom != mapIdTo) ||
4298 (intraSession && mapIdFrom == mapIdTo))
4304 addedLinks.find(from) == addedLinks.end() &&
4305 addedLinks.find(to) == addedLinks.end())
4308 Transform delta = optimizedPoses.at(from).inverse() * optimizedPoses.at(to);
4309 if(
delta.getNorm() <
ui_->doubleSpinBox_detectMore_radius->value() &&
4310 delta.getNorm() >=
ui_->doubleSpinBox_detectMore_radiusMin->value())
4312 checkedLoopClosures.insert(std::make_pair(from, to));
4315 UINFO(
"Added new loop closure between %d and %d.", from, to);
4317 addedLinks.insert(from);
4318 addedLinks.insert(to);
4319 lastAdded.first = from;
4320 lastAdded.second = to;
4322 progressDialog->
appendText(tr(
"Detected loop closure %1->%2! (%3/%4)").
arg(from).
arg(to).
arg(
i+1).
arg(clusters.size()));
4323 QApplication::processEvents();
4334 QApplication::processEvents();
4337 UINFO(
"Iteration %d/%d: added %d loop closures.",
n+1, iterations, (
int)addedLinks.size()/2);
4338 progressDialog->
appendText(tr(
"Iteration %1/%2: Detected %3 loop closures!").
arg(
n+1).
arg(iterations).
arg(addedLinks.size()/2));
4339 if(addedLinks.size() == 0)
4358 UINFO(
"Total added %d loop closures.", added);
4360 progressDialog->
appendText(tr(
"Total new loop closures detected=%1").
arg(added));
4367 QList<rtabmap::Link> links;
4368 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4373 links.push_back(
iter->second);
4381 QList<rtabmap::Link> links;
4382 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4387 iter->second.from() !=
iter->second.to())
4389 links.push_back(
iter->second);
4397 QList<rtabmap::Link> links;
4398 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4402 links.push_back(
iter->second);
4412 cv::Mat infMatrix = links.first().infMatrix();
4415 if(dialog.exec() != QDialog::Accepted)
4421 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4424 progressDialog->setMinimumWidth(800);
4425 progressDialog->show();
4429 for(
int i=0;
i<links.size(); ++
i)
4431 int from = links[
i].from();
4432 int to = links[
i].to();
4437 UERROR(
"Not found link! (%d->%d)", from, to);
4451 if(
iter->second.to() == currentLink.
to() &&
4452 iter->second.type() == currentLink.
type())
4454 iter->second = currentLink;
4467 QApplication::processEvents();
4476 progressDialog->
appendText(
"Refining links finished!");
4487 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4491 int minId =
iter->second.from()>
iter->second.to()?
iter->second.to():
iter->second.from();
4492 int maxId =
iter->second.from()<
iter->second.to()?
iter->second.to():
iter->second.from();
4493 if(minNodeId == 0 || minNodeId > minId)
4497 if(maxNodeId == 0 || maxNodeId < maxId)
4522 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4527 int from =
iter->second.from();
4528 int to =
iter->second.to();
4532 ((from >= minNodeId && from <= maxNodeId) ||
4533 (to >= minNodeId && to <= maxNodeId))) ||
4535 ((mapFrom >= minMapId && mapFrom <= maxMapId) ||
4536 (mapTo >= minMapId && mapTo <= maxMapId)))) &&
4537 ((intra && mapTo == mapFrom) ||
4538 (inter && mapTo != mapFrom)))
4540 links.push_back(
iter->second);
4546 QMessageBox::warning(
this, tr(
"Refine links"), tr(
"No links found matching the requested parameters."));
4557 UWARN(
"No links can be refined!");
4565 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4568 progressDialog->setMinimumWidth(800);
4569 progressDialog->show();
4571 for(
int i=0;
i<links.size(); ++
i)
4573 int from = links[
i].from();
4574 int to = links[
i].to();
4575 if(from > 0 && to > 0)
4582 progressDialog->
appendText(tr(
"Ignored link %1->%2 (landmark)").
arg(from).
arg(to));
4585 QApplication::processEvents();
4594 progressDialog->
appendText(
"Refining links finished!");
4600 if(QMessageBox::question(
this,
4601 tr(
"Reset all changes"),
4602 tr(
"You are about to reset all changes you've made so far, do you want to continue?"),
4603 QMessageBox::Yes | QMessageBox::No,
4604 QMessageBox::No) == QMessageBox::Yes)
4621 static bool updateA =
true;
4622 if(updateA ||
ui_->actionConcise_Layout->isChecked())
4626 updateA = !updateA ||
ui_->actionConcise_Layout->isChecked();
4641 ui_->spinBox_indexA,
4642 ui_->label_parentsA,
4643 ui_->label_childrenA,
4647 ui_->graphicsView_A,
4651 ui_->label_optposeA,
4655 ui_->label_gravityA,
4657 ui_->toolButton_edit_priorA,
4658 ui_->toolButton_remove_priorA,
4661 ui_->label_sensorsA,
4668 ui_->spinBox_indexB,
4669 ui_->label_parentsB,
4670 ui_->label_childrenB,
4674 ui_->graphicsView_B,
4678 ui_->label_optposeB,
4682 ui_->label_gravityB,
4684 ui_->toolButton_edit_priorB,
4685 ui_->toolButton_remove_priorB,
4688 ui_->label_sensorsB,
4693 QSpinBox * spinBoxIndex,
4694 QLabel * labelParents,
4695 QLabel * labelChildren,
4701 QLabel * labelMapId,
4703 QLabel * labelOptPose,
4704 QLabel * labelVelocity,
4705 QLabel * labelCalib,
4707 QLabel * labelGravity,
4708 QLabel * labelPrior,
4709 QToolButton * editPriorButton,
4710 QToolButton * removePriorButton,
4713 QLabel * labelSensors,
4714 bool updateConstraintView)
4719 spinBoxIndex->blockSignals(
true);
4720 spinBoxIndex->setValue(
value);
4721 spinBoxIndex->blockSignals(
false);
4722 labelParents->clear();
4723 labelChildren->clear();
4726 labelMapId->clear();
4728 labelOptPose->clear();
4729 labelVelocity->clear();
4731 labelCalib->clear();
4732 labelScan ->clear();
4733 labelGravity->clear();
4734 labelPrior->clear();
4735 editPriorButton->setVisible(
false);
4736 removePriorButton->setVisible(
false);
4739 labelSensors->clear();
4745 labelId->setText(QString::number(
id));
4748 if(
ui_->dockWidget_graphView->isVisible()) {
4749 ui_->graphViewer->highlightNode(
id, spinBoxIndex==
ui_->spinBox_indexB?1:0);
4759 data.uncompressData();
4760 if(!
data.imageRaw().empty())
4764 if(!
data.depthOrRightRaw().empty())
4766 cv::Mat depth =
data.depthOrRightRaw();
4767 if(!
data.depthRaw().empty())
4769 if(
ui_->spinBox_mesh_fillDepthHoles->value() > 0)
4774 if( !
data.imageRaw().empty() &&
4775 !
data.rightRaw().empty() &&
4776 data.stereoCameraModels().size()==1 &&
4777 data.stereoCameraModels()[0].isValidForProjection() &&
4778 ui_->checkBox_showDisparityInsteadOfRight->isChecked())
4803 if(!imgDepth.empty())
4805 view->setImageDepth(imgDepth);
4808 rect.setWidth(imgDepth.cols);
4809 rect.setHeight(imgDepth.rows);
4818 if(
data.cameraModels().size())
4820 for(
unsigned int i=0;
i<
data.cameraModels().
size(); ++
i)
4822 rect.setWidth(rect.width()+
data.cameraModels()[
i].imageWidth());
4823 rect.setHeight(
std::max((
int)rect.height(),
data.cameraModels()[
i].imageHeight()));
4826 else if(
data.stereoCameraModels().size())
4828 for(
unsigned int i=0;
i<
data.cameraModels().
size(); ++
i)
4830 rect.setWidth(rect.width()+
data.stereoCameraModels()[
i].left().imageWidth());
4831 rect.setHeight(
std::max((
int)rect.height(),
data.stereoCameraModels()[
i].left().imageHeight()));
4837 view->setSceneRect(rect);
4842 std::list<Signature*> signatures;
4845 if(signatures.size() && signatures.front()!=0 && !signatures.front()->getWordsKpts().empty())
4847 std::multimap<int, cv::KeyPoint> keypoints;
4848 for(std::map<int, int>::const_iterator
iter=signatures.front()->getWords().begin();
iter!=signatures.front()->getWords().end(); ++
iter)
4850 keypoints.insert(std::make_pair(
iter->first, signatures.front()->getWordsKpts()[
iter->second]));
4852 view->setFeatures(keypoints,
data.depthOrRightRaw().type() == CV_8UC1||
data.depthOrRightRaw().type() == CV_8UC3?cv::Mat():
data.depthOrRightRaw(), Qt::yellow);
4859 std::vector<float>
v;
4865 label->setText(l.c_str());
4868 labelPose->setText(QString(
"%1xyz=(%2,%3,%4)\nrpy=(%5,%6,%7)").
arg(odomPose.
isIdentity()?
"* ":
"").
arg(
x).arg(
y).arg(
z).arg(
roll).arg(
pitch).arg(
yaw));
4873 labelOptPose->setText(
"<Not in optimized graph>");
4883 stamp->setText(QString::number(
s,
'f'));
4884 stamp->setToolTip(QDateTime::fromMSecsSinceEpoch(
s*1000.0).toString(
"dd.MM.yyyy hh:mm:ss.zzz"));
4888 labelVelocity->setText(QString(
"vx=%1 vy=%2 vz=%3 vroll=%4 vpitch=%5 vyaw=%6").
arg(
v[0]).
arg(
v[1]).
arg(
v[2]).
arg(
v[3]).
arg(
v[4]).
arg(
v[5]));
4891 std::multimap<int, Link> gravityLink;
4893 if(!gravityLink.empty())
4895 Eigen::Vector3f
v = gravityLink.begin()->second.transform().inverse().toEigen3f() * -Eigen::Vector3f::UnitZ();
4896 labelGravity->setText(QString(
"x=%1 y=%2 z=%3").
arg(
v[0]).
arg(
v[1]).
arg(
v[2]));
4906 std::stringstream
out;
4908 labelPrior->setToolTip(QString(
"%1").
arg(
out.str().c_str()));
4909 removePriorButton->setVisible(
true);
4910 editPriorButton->setToolTip(tr(
"Edit Prior"));
4911 editPriorButton->setText(
"...");
4912 editPriorButton->setVisible(
true);
4914 else if(!odomPose.
isNull())
4916 editPriorButton->setToolTip(tr(
"Add Prior"));
4917 editPriorButton->setText(
"+");
4918 editPriorButton->setVisible(
true);
4923 labelGps->setText(QString(
"stamp=%1 longitude=%2 latitude=%3 altitude=%4m error=%5m bearing=%6deg").
arg(QString::number(gps.
stamp(),
'f')).
arg(gps.
longitude()).arg(gps.
latitude()).arg(gps.
altitude()).arg(gps.
error()).arg(gps.
bearing()));
4924 labelGps->setToolTip(QDateTime::fromMSecsSinceEpoch(gps.
stamp()*1000.0).toString(
"dd.MM.yyyy hh:mm:ss.zzz"));
4928 labelGt->setText(QString(
"%1").
arg(
g.prettyPrint().c_str()));
4934 for(EnvSensors::iterator
iter=sensors.begin();
iter!=sensors.end(); ++
iter)
4936 if(
iter != sensors.begin())
4938 sensorsStr +=
" | ";
4939 tooltipStr +=
" | ";
4944 sensorsStr +=
uFormat(
"%.1f dbm",
iter->second.value()).c_str();
4945 tooltipStr +=
"Wifi signal strength";
4949 sensorsStr +=
uFormat(
"%.1f \u00B0C",
iter->second.value()).c_str();
4950 tooltipStr +=
"Ambient Temperature";
4954 sensorsStr +=
uFormat(
"%.1f hPa",
iter->second.value()).c_str();
4955 tooltipStr +=
"Ambient Air Pressure";
4959 sensorsStr +=
uFormat(
"%.0f lx",
iter->second.value()).c_str();
4960 tooltipStr +=
"Ambient Light";
4964 sensorsStr +=
uFormat(
"%.0f %%",
iter->second.value()).c_str();
4965 tooltipStr +=
"Ambient Relative Humidity";
4969 sensorsStr +=
uFormat(
"%.2f",
iter->second.value()).c_str();
4970 tooltipStr += QString(
"Type %1").arg((
int)
iter->first);
4974 labelSensors->setText(sensorsStr);
4975 labelSensors->setToolTip(tooltipStr);
4977 if(
data.cameraModels().size() ||
data.stereoCameraModels().size())
4979 std::stringstream calibrationDetails;
4980 if(
data.cameraModels().size())
4982 if(!
data.depthRaw().empty() &&
data.depthRaw().cols!=
data.imageRaw().cols &&
data.imageRaw().cols)
4984 labelCalib->setText(tr(
"%1 %2x%3 [%8x%9] fx=%4 fy=%5 cx=%6 cy=%7 T=%10 [%11 %12 %13 %14; %15 %16 %17 %18; %19 %20 %21 %22]")
4985 .
arg(
data.cameraModels().size())
4986 .
arg(
data.cameraModels()[0].imageWidth()>0?
data.cameraModels()[0].imageWidth():
data.imageRaw().cols/
data.cameraModels().size())
4987 .arg(
data.cameraModels()[0].imageHeight()>0?
data.cameraModels()[0].imageHeight():
data.imageRaw().rows)
4988 .arg(
data.cameraModels()[0].fx())
4989 .arg(
data.cameraModels()[0].fy())
4990 .arg(
data.cameraModels()[0].cx())
4991 .arg(
data.cameraModels()[0].cy())
4992 .arg(
data.depthRaw().cols/
data.cameraModels().size())
4993 .arg(
data.depthRaw().rows)
4994 .arg(
data.cameraModels()[0].localTransform().prettyPrint().c_str())
4995 .arg(
data.cameraModels()[0].localTransform().r11()).arg(
data.cameraModels()[0].localTransform().r12()).arg(
data.cameraModels()[0].localTransform().r13()).arg(
data.cameraModels()[0].localTransform().o14())
4996 .arg(
data.cameraModels()[0].localTransform().r21()).arg(
data.cameraModels()[0].localTransform().r22()).arg(
data.cameraModels()[0].localTransform().r23()).arg(
data.cameraModels()[0].localTransform().o24())
4997 .arg(
data.cameraModels()[0].localTransform().r31()).arg(
data.cameraModels()[0].localTransform().r32()).arg(
data.cameraModels()[0].localTransform().r33()).arg(
data.cameraModels()[0].localTransform().o34()));
5001 labelCalib->setText(tr(
"%1 %2x%3 fx=%4 fy=%5 cx=%6 cy=%7 T=%8 [%9 %10 %11 %12; %13 %14 %15 %16; %17 %18 %19 %20]")
5002 .
arg(
data.cameraModels().size())
5003 .
arg(
data.cameraModels()[0].imageWidth()>0?
data.cameraModels()[0].imageWidth():
data.imageRaw().cols/
data.cameraModels().size())
5004 .arg(
data.cameraModels()[0].imageHeight()>0?
data.cameraModels()[0].imageHeight():
data.imageRaw().rows)
5005 .arg(
data.cameraModels()[0].fx())
5006 .arg(
data.cameraModels()[0].fy())
5007 .arg(
data.cameraModels()[0].cx())
5008 .arg(
data.cameraModels()[0].cy())
5009 .arg(
data.cameraModels()[0].localTransform().prettyPrint().c_str())
5010 .arg(
data.cameraModels()[0].localTransform().r11()).arg(
data.cameraModels()[0].localTransform().r12()).arg(
data.cameraModels()[0].localTransform().r13()).arg(
data.cameraModels()[0].localTransform().o14())
5011 .arg(
data.cameraModels()[0].localTransform().r21()).arg(
data.cameraModels()[0].localTransform().r22()).arg(
data.cameraModels()[0].localTransform().r23()).arg(
data.cameraModels()[0].localTransform().o24())
5012 .arg(
data.cameraModels()[0].localTransform().r31()).arg(
data.cameraModels()[0].localTransform().r32()).arg(
data.cameraModels()[0].localTransform().r33()).arg(
data.cameraModels()[0].localTransform().o34()));
5015 for(
unsigned int i=0;
i<
data.cameraModels().
size();++
i)
5017 if(
i!=0) calibrationDetails << std::endl;
5018 calibrationDetails <<
"Id: " <<
i <<
" Size=" <<
data.cameraModels()[
i].imageWidth() <<
"x" <<
data.cameraModels()[
i].imageHeight() << std::endl;
5019 if(
data.cameraModels()[
i].K_raw().total()) calibrationDetails <<
"K=" <<
data.cameraModels()[
i].K_raw() << std::endl;
5020 if(
data.cameraModels()[
i].D_raw().total()) calibrationDetails <<
"D=" <<
data.cameraModels()[
i].D_raw() << std::endl;
5021 if(
data.cameraModels()[
i].R().total()) calibrationDetails <<
"R=" <<
data.cameraModels()[
i].R() << std::endl;
5022 if(
data.cameraModels()[
i].P().total()) calibrationDetails <<
"P=" <<
data.cameraModels()[
i].P() << std::endl;
5027 else if(
data.stereoCameraModels().size())
5030 labelCalib->setText(tr(
"%1x%2 fx=%3 fy=%4 cx=%5 cy=%6 baseline=%7m T=%8 [%9 %10 %11 %12; %13 %14 %15 %16; %17 %18 %19 %20]")
5031 .arg(
data.stereoCameraModels()[0].left().imageWidth()>0?
data.stereoCameraModels()[0].left().imageWidth():
data.imageRaw().cols)
5032 .arg(
data.stereoCameraModels()[0].left().imageHeight()>0?
data.stereoCameraModels()[0].left().imageHeight():
data.imageRaw().rows)
5033 .arg(
data.stereoCameraModels()[0].left().fx())
5034 .arg(
data.stereoCameraModels()[0].left().fy())
5035 .arg(
data.stereoCameraModels()[0].left().cx())
5036 .arg(
data.stereoCameraModels()[0].left().cy())
5037 .arg(
data.stereoCameraModels()[0].baseline())
5038 .arg(
data.stereoCameraModels()[0].localTransform().prettyPrint().c_str())
5039 .arg(
data.stereoCameraModels()[0].localTransform().r11()).arg(
data.stereoCameraModels()[0].localTransform().r12()).arg(
data.stereoCameraModels()[0].localTransform().r13()).arg(
data.stereoCameraModels()[0].localTransform().o14())
5040 .arg(
data.stereoCameraModels()[0].localTransform().r21()).arg(
data.stereoCameraModels()[0].localTransform().r22()).arg(
data.stereoCameraModels()[0].localTransform().r23()).arg(
data.stereoCameraModels()[0].localTransform().o24())
5041 .arg(
data.stereoCameraModels()[0].localTransform().r31()).arg(
data.stereoCameraModels()[0].localTransform().r32()).arg(
data.stereoCameraModels()[0].localTransform().r33()).arg(
data.stereoCameraModels()[0].localTransform().o34()));
5043 for(
unsigned int i=0;
i<
data.stereoCameraModels().size();++
i)
5045 calibrationDetails <<
"Id: " <<
i << std::endl;
5046 calibrationDetails <<
" Left:" <<
" Size=" <<
data.stereoCameraModels()[
i].left().imageWidth() <<
"x" <<
data.stereoCameraModels()[
i].left().imageHeight() << std::endl;
5047 if(
data.stereoCameraModels()[
i].left().K_raw().total()) calibrationDetails <<
" K=" <<
data.stereoCameraModels()[
i].left().K_raw() << std::endl;
5048 if(
data.stereoCameraModels()[
i].left().D_raw().total()) calibrationDetails <<
" D=" <<
data.stereoCameraModels()[
i].left().D_raw() << std::endl;
5049 if(
data.stereoCameraModels()[
i].left().R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].left().R() << std::endl;
5050 if(
data.stereoCameraModels()[
i].left().P().total()) calibrationDetails <<
" P=" <<
data.stereoCameraModels()[
i].left().P() << std::endl;
5051 calibrationDetails <<
" Right:" <<
" Size=" <<
data.stereoCameraModels()[
i].right().imageWidth() <<
"x" <<
data.stereoCameraModels()[
i].right().imageHeight() << std::endl;
5052 if(
data.stereoCameraModels()[
i].right().K_raw().total()) calibrationDetails <<
" K=" <<
data.stereoCameraModels()[
i].right().K_raw() << std::endl;
5053 if(
data.stereoCameraModels()[
i].right().D_raw().total()) calibrationDetails <<
" D=" <<
data.stereoCameraModels()[
i].right().D_raw() << std::endl;
5054 if(
data.stereoCameraModels()[
i].right().R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].right().R() << std::endl;
5055 if(
data.stereoCameraModels()[
i].right().P().total()) calibrationDetails <<
" P=" <<
data.stereoCameraModels()[
i].right().P() << std::endl;
5056 if(
data.stereoCameraModels()[
i].R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].R() << std::endl;
5057 if(
data.stereoCameraModels()[
i].T().total()) calibrationDetails <<
" T=" <<
data.stereoCameraModels()[
i].T() << std::endl;
5058 if(
data.stereoCameraModels()[
i].F().total()) calibrationDetails <<
" F=" <<
data.stereoCameraModels()[
i].F() << std::endl;
5059 if(
data.stereoCameraModels()[
i].E().total()) calibrationDetails <<
" E=" <<
data.stereoCameraModels()[
i].E() << std::endl;
5063 labelCalib->setToolTip(calibrationDetails.str().c_str());
5068 labelCalib->setText(
"NA");
5071 if(
data.laserScanRaw().size())
5073 labelScan->setText(tr(
"Format=%1 Points=%2 [max=%3] Range=[%4->%5 m] Angle=[%6->%7 rad inc=%8] Has [Color=%9 2D=%10 Normals=%11 Intensity=%12] %13")
5074 .arg(
data.laserScanRaw().formatName().c_str())
5075 .arg(
data.laserScanRaw().size())
5076 .arg(
data.laserScanRaw().maxPoints())
5077 .arg(
data.laserScanRaw().rangeMin())
5078 .arg(
data.laserScanRaw().rangeMax())
5079 .arg(
data.laserScanRaw().angleMin())
5080 .arg(
data.laserScanRaw().angleMax())
5081 .arg(
data.laserScanRaw().angleIncrement())
5082 .arg(
data.laserScanRaw().hasRGB()?1:0)
5083 .arg(
data.laserScanRaw().is2d()?1:0)
5084 .arg(
data.laserScanRaw().hasNormals()?1:0)
5085 .arg(
data.laserScanRaw().hasIntensity()?1:0)
5086 .arg(
data.laserScanRaw().localTransform().prettyPrint().c_str()));
5090 if(!
data.depthOrRightRaw().empty() && (
data.depthOrRightRaw().type() == CV_8UC1 ||
data.depthOrRightRaw().type() == CV_8UC3))
5097 ui_->graphicsView_stereo->clear();
5111 if(signatures.size() &&
ui_->checkBox_odomFrame_3dview->isChecked())
5118 if(!gravityLink.empty() &&
ui_->checkBox_gravity_3dview->isChecked())
5120 Transform gravityT = gravityLink.begin()->second.transform();
5121 Eigen::Vector3f gravity = gravityT.
inverse().
toEigen3f()*-Eigen::Vector3f::UnitZ();
5122 cloudViewer_->
addOrUpdateLine(
"gravity", pose, pose*
Transform(gravity[0], gravity[1], gravity[2], 0, 0, 0), Qt::yellow,
true,
false);
5131 if(
ui_->checkBox_showScan->isChecked() && laserScanRaw.
size())
5148 else if(laserScanRaw.
hasRGB())
5166 if(
ui_->checkBox_showCloud->isChecked() &&
ui_->checkBox_cameraProjection->isChecked() &&
5167 !
data.imageRaw().empty() && !laserScanRaw.
empty() && !laserScanRaw.
is2d())
5170 std::vector<CameraModel> models =
data.cameraModels();
5171 if(!
data.stereoCameraModels().empty())
5174 for(
size_t i=0;
i<
data.stereoCameraModels().
size(); ++
i)
5176 models.push_back(
data.stereoCameraModels()[
i].left());
5180 if(!models.empty() && !models[0].isValidForProjection())
5185 if(!models.empty() && models[0].imageWidth() != 0 && models[0].imageHeight() != 0)
5188 std::map<int, std::vector<CameraModel> > cameraModels;
5190 cameraModels.insert(std::make_pair(
data.id(), models));
5192 std::vector<std::pair< std::pair<int, int>, pcl::PointXY> > pointToPixel;
5198 UASSERT(pointToPixel.empty() || pointToPixel.size() == cloud->size());
5199 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudValidPoints(
new pcl::PointCloud<pcl::PointXYZRGB>);
5200 cloudValidPoints->resize(cloud->size());
5202 for(
size_t i=0;
i<pointToPixel.size(); ++
i)
5204 pcl::PointXYZINormal & pt = cloud->at(
i);
5205 pcl::PointXYZRGB ptColor;
5206 int nodeID = pointToPixel[
i].first.first;
5207 int cameraIndex = pointToPixel[
i].first.second;
5208 if(nodeID>0 && cameraIndex>=0)
5210 cv::Mat image =
data.imageRaw();
5212 int subImageWidth = image.cols / cameraModels.at(nodeID).size();
5213 image = image(
cv::Range::all(), cv::Range(cameraIndex*subImageWidth, (cameraIndex+1)*subImageWidth));
5216 int x = pointToPixel[
i].second.x * (
float)image.cols;
5217 int y = pointToPixel[
i].second.y * (
float)image.rows;
5221 if(image.type()==CV_8UC3)
5223 cv::Vec3b bgr = image.at<cv::Vec3b>(
y,
x);
5230 UASSERT(image.type()==CV_8UC1);
5231 ptColor.r = ptColor.g = ptColor.b = image.at<
unsigned char>(pointToPixel[
i].second.y * image.rows, pointToPixel[
i].second.x * image.cols);
5237 cloudValidPoints->at(oi++) = ptColor;
5240 cloudValidPoints->resize(oi);
5241 if(!cloudValidPoints->empty())
5243 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5245 cloudValidPoints =
util3d::voxelize(cloudValidPoints,
ui_->doubleSpinBox_voxelSize->value());
5252 UWARN(
"Camera projection to scan returned an empty cloud, no visible points from cameras...");
5257 UERROR(
"Node has invalid camera models, camera projection on scan cannot be done!.");
5260 else if(
ui_->checkBox_showCloud->isChecked() ||
ui_->checkBox_showMesh->isChecked())
5262 if(!
data.depthOrRightRaw().empty())
5264 if(!
data.imageRaw().empty())
5266 std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> clouds;
5267 std::vector<pcl::IndicesPtr> allIndices;
5268 if(!
data.depthRaw().empty() &&
data.cameraModels().size()==1)
5270 cv::Mat depth =
data.depthRaw();
5271 if(
ui_->spinBox_mesh_fillDepthHoles->value() > 0)
5275 pcl::IndicesPtr
indices(
new std::vector<int>);
5279 data.cameraModels()[0],
5280 ui_->spinBox_decimation->value(),0,0,
indices.get());
5284 allIndices.push_back(
indices);
5291 UASSERT(clouds.size() == allIndices.size());
5292 for(
size_t i=0;
i<allIndices.size(); ++
i)
5294 if(allIndices[
i]->
size())
5296 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = clouds[
i];
5297 pcl::IndicesPtr
indices = allIndices[
i];
5298 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5303 if(
ui_->checkBox_showMesh->isChecked() && !cloud->is_dense)
5305 Eigen::Vector3f viewpoint(0.0
f,0.0
f,0.0
f);
5306 if(
data.cameraModels().size() && !
data.cameraModels()[0].localTransform().isNull())
5308 viewpoint[0] =
data.cameraModels()[0].localTransform().x();
5309 viewpoint[1] =
data.cameraModels()[0].localTransform().y();
5310 viewpoint[2] =
data.cameraModels()[0].localTransform().z();
5312 else if(
data.stereoCameraModels().size() && !
data.stereoCameraModels()[0].localTransform().isNull())
5314 viewpoint[0] =
data.stereoCameraModels()[0].localTransform().x();
5315 viewpoint[1] =
data.stereoCameraModels()[0].localTransform().y();
5316 viewpoint[2] =
data.stereoCameraModels()[0].localTransform().z();
5320 float(
ui_->spinBox_mesh_angleTolerance->value())*
M_PI/180.0f,
5321 ui_->checkBox_mesh_quad->isChecked(),
5322 ui_->spinBox_mesh_triangleSize->value(),
5325 if(
ui_->spinBox_mesh_minClusterSize->value())
5328 std::vector<std::set<int> > neighbors;
5329 std::vector<std::set<int> > vertexToPolygons;
5336 ui_->spinBox_mesh_minClusterSize->value());
5337 std::vector<pcl::Vertices> filteredPolygons(polygons.size());
5341 for(std::list<int>::iterator jter=
iter->begin(); jter!=
iter->end(); ++jter)
5343 filteredPolygons[oi++] = polygons.at(*jter);
5346 filteredPolygons.resize(oi);
5347 polygons = filteredPolygons;
5352 if(
ui_->checkBox_showCloud->isChecked())
5359 else if(
ui_->checkBox_showCloud->isChecked())
5361 std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> clouds;
5362 std::vector<pcl::IndicesPtr> allIndices;
5365 UASSERT(clouds.size() == allIndices.size());
5366 for(
size_t i=0;
i<allIndices.size(); ++
i)
5368 if(allIndices[
i]->
size())
5370 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = clouds[
i];
5371 pcl::IndicesPtr
indices = allIndices[
i];
5372 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5387 if(
data.cameraModels().size())
5398 if(
ui_->checkBox_showWords->isChecked() &&
5399 !signatures.empty() &&
5400 !(*signatures.begin())->getWords3().empty())
5402 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>);
5403 cloud->resize((*signatures.begin())->getWords3().size());
5405 for(std::multimap<int, int>::const_iterator
iter=(*signatures.begin())->getWords().begin();
5406 iter!=(*signatures.begin())->getWords().end();
5409 const cv::Point3f & pt = (*signatures.begin())->getWords3()[
iter->second];
5410 cloud->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
5425 if(
ui_->checkBox_showMap->isChecked() ||
5426 ui_->checkBox_showGrid->isChecked() ||
5427 ui_->checkBox_showElevation->checkState() != Qt::Unchecked)
5434 else if(!
data.gridGroundCellsRaw().empty() || !
data.gridObstacleCellsRaw().empty())
5436 combinedLocalMaps.
add(
data.id(),
data.gridGroundCellsRaw(),
data.gridObstacleCellsRaw(),
data.gridEmptyCellsRaw(),
data.gridCellSize(),
data.gridViewPoint());
5438 if(!combinedLocalMaps.
empty())
5440 std::map<int, Transform> poses;
5441 poses.insert(std::make_pair(
data.id(), pose));
5443 #ifdef RTABMAP_OCTOMAP
5445 if(
ui_->checkBox_octomap->isChecked() &&
5446 (!combinedLocalMaps.
localGrids().begin()->second.groundCells.empty() || !combinedLocalMaps.
localGrids().begin()->second.obstacleCells.empty()) &&
5447 combinedLocalMaps.
localGrids().begin()->second.is3D() &&
5448 combinedLocalMaps.
localGrids().begin()->second.cellSize > 0.0f)
5458 if(
ui_->checkBox_showMap->isChecked())
5460 float xMin=0.0f, yMin=0.0f;
5462 float gridCellSize = Parameters::defaultGridCellSize();
5466 #ifdef RTABMAP_OCTOMAP
5476 map8S = grid.
getMap(xMin, yMin);
5485 if(
ui_->checkBox_showGrid->isChecked())
5487 #ifdef RTABMAP_OCTOMAP
5490 if(
ui_->comboBox_octomap_rendering_type->currentIndex()== 0)
5492 pcl::IndicesPtr obstacles(
new std::vector<int>);
5493 pcl::IndicesPtr
empty(
new std::vector<int>);
5494 pcl::IndicesPtr ground(
new std::vector<int>);
5495 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = octomap->
createCloud(
ui_->spinBox_grid_depth->value(), obstacles.get(),
empty.get(), ground.get());
5498 pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
5499 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
5503 pcl::PointCloud<pcl::PointXYZRGB>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
5504 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
5510 pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5511 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
5515 pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5516 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
5521 if(
ui_->checkBox_grid_empty->isChecked())
5523 pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5524 pcl::copyPointCloud(*cloud, *
empty, *emptyCloud);
5561 if(
ui_->checkBox_grid_empty->isChecked())
5566 QColor(
ui_->lineEdit_emptyColor->text()));
5572 #ifdef RTABMAP_OCTOMAP
5579 #ifdef RTABMAP_GRIDMAP
5580 if(
ui_->checkBox_showElevation->checkState() != Qt::Unchecked)
5582 GridMap gridMap(&combinedLocalMaps, parameters);
5584 if(combinedLocalMaps.
localGrids().begin()->second.is3D())
5587 if(
ui_->checkBox_showElevation->checkState() == Qt::PartiallyChecked)
5589 float xMin, yMin, gridCellSize;
5590 cv::Mat elevationMap = gridMap.
createHeightMap(xMin, yMin, gridCellSize);
5602 UWARN(
"Local grid is not 3D, cannot generate an elevation map");
5613 if(signatures.size())
5615 UASSERT(signatures.front() != 0 && signatures.size() == 1);
5616 delete signatures.front();
5622 std::multimap<int, rtabmap::Link> links;
5626 QString strParents, strChildren;
5627 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
5632 if(
iter->first <
id)
5634 strChildren.append(QString(
"%1 ").
arg(
iter->first));
5638 strParents.append(QString(
"%1 ").
arg(
iter->first));
5642 labelParents->setText(strParents);
5643 labelChildren->setText(strChildren);
5649 labelMapId->setText(QString::number(mapId));
5663 int from =
ids_.at(
ui_->horizontalSlider_A->value());
5664 int to =
ids_.at(
ui_->horizontalSlider_B->value());
5673 if(
i !=
ui_->horizontalSlider_loops->value())
5675 ui_->horizontalSlider_loops->blockSignals(
true);
5676 ui_->horizontalSlider_loops->setValue(
i);
5677 ui_->horizontalSlider_loops->blockSignals(
false);
5680 ui_->horizontalSlider_neighbors->blockSignals(
true);
5681 ui_->horizontalSlider_neighbors->setValue(0);
5682 ui_->horizontalSlider_neighbors->blockSignals(
false);
5692 if(
i !=
ui_->horizontalSlider_neighbors->value())
5694 ui_->horizontalSlider_neighbors->blockSignals(
true);
5695 ui_->horizontalSlider_neighbors->setValue(
i);
5696 ui_->horizontalSlider_neighbors->blockSignals(
false);
5699 ui_->horizontalSlider_loops->blockSignals(
true);
5700 ui_->horizontalSlider_loops->setValue(0);
5701 ui_->horizontalSlider_loops->blockSignals(
false);
5709 ui_->horizontalSlider_loops->blockSignals(
true);
5710 ui_->horizontalSlider_neighbors->blockSignals(
true);
5711 ui_->horizontalSlider_loops->setValue(0);
5712 ui_->horizontalSlider_neighbors->setValue(0);
5713 ui_->horizontalSlider_loops->blockSignals(
false);
5714 ui_->horizontalSlider_neighbors->blockSignals(
false);
5719 bool constraintViewUpdated =
false;
5723 constraintViewUpdated =
true;
5728 std::map<int, Transform> optimizedPoses =
uValueAt(
graphes_,
ui_->horizontalSlider_iterations->value());
5729 if(optimizedPoses.size() > 0)
5731 std::map<int, Transform>::iterator fromIter = optimizedPoses.find(from);
5732 std::map<int, Transform>::iterator toIter = optimizedPoses.find(to);
5733 if(fromIter != optimizedPoses.end() &&
5734 toIter != optimizedPoses.end())
5736 Link link(from, to,
Link::kUndef, fromIter->second.inverse() * toIter->second);
5738 constraintViewUpdated =
true;
5742 if(!constraintViewUpdated)
5744 ui_->label_constraint->clear();
5745 ui_->label_constraint_opt->clear();
5746 ui_->label_variance->clear();
5747 ui_->lineEdit_covariance->clear();
5748 ui_->label_type->clear();
5749 ui_->label_type_name->clear();
5750 ui_->checkBox_showOptimized->setEnabled(
false);
5760 if(this->parent() == 0)
5768 if(
ui_->horizontalSlider_A->maximum())
5770 int id =
ids_.at(
ui_->horizontalSlider_A->value());
5773 data.uncompressData();
5781 ui_->dockWidget_stereoView->isVisible() &&
5782 !
data->imageRaw().empty() &&
5783 !
data->depthOrRightRaw().empty() &&
5784 (
data->depthOrRightRaw().type() == CV_8UC1 ||
data->depthOrRightRaw().type() == CV_8UC3) &&
5785 data->stereoCameraModels().size()==1 &&
5786 data->stereoCameraModels()[0].isValidForProjection())
5789 if(
data->imageRaw().channels() == 3)
5791 cv::cvtColor(
data->imageRaw(), leftMono, CV_BGR2GRAY);
5795 leftMono =
data->imageRaw();
5798 if(
data->rightRaw().channels() == 3)
5800 cv::cvtColor(
data->rightRaw(), rightMono, CV_BGR2GRAY);
5804 rightMono =
data->rightRaw();
5812 std::vector<cv::KeyPoint> kpts;
5813 uInsert(parameters,
ParametersPair(Parameters::kKpMaxFeatures(), parameters.at(Parameters::kVisMaxFeatures())));
5815 uInsert(parameters,
ParametersPair(Parameters::kKpMinDepth(), parameters.at(Parameters::kVisMinDepth())));
5816 uInsert(parameters,
ParametersPair(Parameters::kKpMaxDepth(), parameters.at(Parameters::kVisMaxDepth())));
5817 uInsert(parameters,
ParametersPair(Parameters::kKpDetectorStrategy(), parameters.at(Parameters::kVisFeatureType())));
5818 uInsert(parameters,
ParametersPair(Parameters::kKpRoiRatios(), parameters.at(Parameters::kVisRoiRatios())));
5819 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixEps(), parameters.at(Parameters::kVisSubPixEps())));
5820 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixIterations(), parameters.at(Parameters::kVisSubPixIterations())));
5821 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixWinSize(), parameters.at(Parameters::kVisSubPixWinSize())));
5826 float timeKpt =
timer.ticks();
5828 std::vector<cv::Point2f> leftCorners;
5829 cv::KeyPoint::convert(kpts, leftCorners);
5832 std::vector<unsigned char> status;
5833 std::vector<cv::Point2f> rightCorners;
5842 float timeStereo =
timer.ticks();
5844 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>);
5845 cloud->resize(kpts.size());
5846 float bad_point = std::numeric_limits<float>::quiet_NaN ();
5847 UASSERT(status.size() == kpts.size());
5850 int flowOutliers= 0;
5851 int slopeOutliers= 0;
5852 int negativeDisparityOutliers = 0;
5853 for(
unsigned int i=0;
i<status.size(); ++
i)
5855 cv::Point3f pt(bad_point, bad_point, bad_point);
5858 float disparity = leftCorners[
i].x - rightCorners[
i].x;
5859 if(disparity > 0.0
f)
5864 data->stereoCameraModels()[0]);
5871 cloud->at(oi++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
5877 ++negativeDisparityOutliers;
5887 UINFO(
"correspondences = %d/%d (%f) (time kpt=%fs stereo=%fs)",
5888 (
int)cloud->size(), (
int)leftCorners.size(),
float(cloud->size())/
float(leftCorners.size()), timeKpt, timeStereo);
5894 ui_->label_stereo_inliers->setNum(inliers);
5895 ui_->label_stereo_flowOutliers->setNum(flowOutliers);
5896 ui_->label_stereo_slopeOutliers->setNum(slopeOutliers);
5897 ui_->label_stereo_disparityOutliers->setNum(negativeDisparityOutliers);
5899 std::vector<cv::KeyPoint> rightKpts;
5900 cv::KeyPoint::convert(rightCorners, rightKpts);
5901 std::vector<cv::DMatch> good_matches(kpts.size());
5902 for(
unsigned int i=0;
i<good_matches.size(); ++
i)
5904 good_matches[
i].trainIdx =
i;
5905 good_matches[
i].queryIdx =
i;
5917 ui_->graphicsView_stereo->clear();
5918 ui_->graphicsView_stereo->setLinesShown(
true);
5919 ui_->graphicsView_stereo->setFeaturesShown(
false);
5920 ui_->graphicsView_stereo->setImageDepthShown(
true);
5923 ui_->graphicsView_stereo->setImageDepth(
data->depthOrRightRaw());
5926 for(
unsigned int i=0;
i<kpts.size(); ++
i)
5928 if(rightKpts[
i].pt.x > 0 && rightKpts[
i].pt.y > 0)
5930 QColor
c = Qt::green;
5935 else if(status[
i] == 100)
5939 else if(status[
i] == 101)
5943 else if(status[
i] == 102)
5947 else if(status[
i] == 110)
5951 ui_->graphicsView_stereo->addLine(
5957 QString(
"%1: (%2,%3) -> (%4,%5) d=%6").
arg(
i).
arg(kpts[
i].pt.x).
arg(kpts[
i].pt.y).arg(rightKpts[
i].pt.x).arg(rightKpts[
i].pt.y).arg(kpts[
i].pt.x - rightKpts[
i].pt.x));
5960 ui_->graphicsView_stereo->update();
5966 if(
ui_->actionConcise_Layout->isChecked()) {
5970 int from =
ids_.at(
ui_->horizontalSlider_A->value());
5971 int to =
ids_.at(
ui_->horizontalSlider_B->value());
5974 ui_->graphicsView_A->clearLines();
5975 ui_->graphicsView_A->setFeaturesColor(
ui_->graphicsView_A->getDefaultFeatureColor());
5976 ui_->graphicsView_B->clearLines();
5977 ui_->graphicsView_B->setFeaturesColor(
ui_->graphicsView_B->getDefaultFeatureColor());
5979 const QMultiMap<int, KeypointItem*> & wordsA =
ui_->graphicsView_A->getFeatures();
5980 const QMultiMap<int, KeypointItem*> & wordsB =
ui_->graphicsView_B->getFeatures();
5981 std::set<int> inliersSet(inliers.begin(), inliers.end());
5982 if(wordsA.size() && wordsB.size())
5984 QList<int> ids = wordsA.uniqueKeys();
5985 for(
int i=0;
i<ids.size(); ++
i)
5987 if(ids[
i] > 0 && wordsA.count(ids[
i]) == 1 && wordsB.count(ids[
i]) == 1)
5991 float scaleAX =
ui_->graphicsView_A->viewScale();
5992 float scaleBX =
ui_->graphicsView_B->viewScale();
5994 float marginAX = (
ui_->graphicsView_A->width() -
ui_->graphicsView_A->sceneRect().width()*scaleAX)/2.0
f;
5995 float marginAY = (
ui_->graphicsView_A->height() -
ui_->graphicsView_A->sceneRect().height()*scaleAX)/2.0
f;
5996 float marginBX = (
ui_->graphicsView_B->width() -
ui_->graphicsView_B->sceneRect().width()*scaleBX)/2.0
f;
5997 float marginBY = (
ui_->graphicsView_B->height() -
ui_->graphicsView_B->sceneRect().height()*scaleBX)/2.0
f;
6002 if(
ui_->actionVertical_Layout->isChecked())
6004 deltaY =
ui_->graphicsView_A->height();
6008 deltaX =
ui_->graphicsView_A->width();
6014 QColor cA =
ui_->graphicsView_A->getDefaultMatchingLineColor();
6015 QColor cB =
ui_->graphicsView_B->getDefaultMatchingLineColor();
6016 if(inliersSet.find(ids[
i])!=inliersSet.end())
6018 cA =
ui_->graphicsView_A->getDefaultMatchingFeatureColor();
6019 cB =
ui_->graphicsView_B->getDefaultMatchingFeatureColor();
6020 ui_->graphicsView_A->setFeatureColor(ids[
i],
ui_->graphicsView_A->getDefaultMatchingFeatureColor());
6021 ui_->graphicsView_B->setFeatureColor(ids[
i],
ui_->graphicsView_B->getDefaultMatchingFeatureColor());
6025 ui_->graphicsView_A->setFeatureColor(ids[
i],
ui_->graphicsView_A->getDefaultMatchingLineColor());
6026 ui_->graphicsView_B->setFeatureColor(ids[
i],
ui_->graphicsView_B->getDefaultMatchingLineColor());
6029 ui_->graphicsView_A->addLine(
6032 (kptB->
keypoint().pt.x*scaleBX+marginBX+deltaX-marginAX)/scaleAX,
6033 (kptB->
keypoint().pt.y*scaleBX+marginBY+deltaY-marginAY)/scaleAX,
6036 ui_->graphicsView_B->addLine(
6037 (kptA->
keypoint().pt.x*scaleAX+marginAX-deltaX-marginBX)/scaleBX,
6038 (kptA->
keypoint().pt.y*scaleAX+marginAY-deltaY-marginBY)/scaleBX,
6044 ui_->graphicsView_A->update();
6045 ui_->graphicsView_B->update();
6052 ui_->spinBox_indexA->blockSignals(
true);
6053 ui_->spinBox_indexA->setValue(
value);
6054 ui_->spinBox_indexA->blockSignals(
false);
6057 ui_->label_idA->setText(QString::number(
ids_.at(
value)));
6067 ui_->spinBox_indexB->blockSignals(
true);
6068 ui_->spinBox_indexB->setValue(
value);
6069 ui_->spinBox_indexB->blockSignals(
false);
6072 ui_->label_idB->setText(QString::number(
ids_.at(
value)));
6113 int priorId = sender() ==
ui_->toolButton_edit_priorA?
ids_.at(
ui_->horizontalSlider_A->value()):
6114 sender() ==
ui_->toolButton_edit_priorB?
ids_.at(
ui_->horizontalSlider_B->value()):0;
6131 QMessageBox::warning(
this, tr(
""), tr(
"Node %1 doesn't have odometry pose, cannot add a prior for it!").
arg(priorId));
6141 link = findIter->second;
6151 cv::Mat covBefore = link.
infMatrix().inv();
6153 if(dialog.exec() == QDialog::Accepted)
6160 if(
iter->second.to() == link.
to() &&
6161 iter->second.type() == link.
type())
6163 iter->second = newLink;
6184 link.
transform(), cv::Mat::eye(6,6,CV_64FC1) * (priorId>0?0.00001:1));
6185 if(dialog.exec() == QDialog::Accepted)
6188 int from = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_A->value());
6189 int to = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_B->value());
6196 if(newLink.
from() < newLink.
to())
6212 bool priorsIgnored = Parameters::defaultOptimizerPriorsIgnored();
6213 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kOptimizerPriorsIgnored(), priorsIgnored);
6216 if(QMessageBox::question(
this,
6217 tr(
"Updating Prior"),
6218 tr(
"Parameter %1 is true, do you want to turn it off to update the graph with the updated prior?").
arg(Parameters::kOptimizerPriorsIgnored().
c_str()),
6219 QMessageBox::Yes | QMessageBox::No,
6220 QMessageBox::Yes) == QMessageBox::Yes)
6222 priorsIgnored =
false;
6223 ui_->parameters_toolbox->updateParameter(Parameters::kOptimizerPriorsIgnored(),
"false");
6226 int indexA =
ui_->horizontalSlider_A->value();
6227 int indexB =
ui_->horizontalSlider_B->value();
6232 if(
ui_->horizontalSlider_A->value() != indexA)
6233 ui_->horizontalSlider_A->setValue(indexA);
6236 if(
ui_->horizontalSlider_B->value() != indexB)
6237 ui_->horizontalSlider_B->setValue(indexB);
6255 ui_->horizontalSlider_neighbors->value() >= 0 &&
ui_->horizontalSlider_neighbors->value() <
neighborLinks_.size())
6260 ui_->horizontalSlider_loops->value() >= 0 &&
ui_->horizontalSlider_loops->value() <
loopLinks_.size())
6274 bool updateImageSliders,
6284 if(iterLink->second.from() == link.
to())
6286 link = iterLink->second.
inverse();
6290 link = iterLink->second;
6293 else if(
ui_->checkBox_ignorePoseCorrection->isChecked())
6321 ui_->label_constraint->clear();
6322 ui_->label_constraint_opt->clear();
6323 ui_->checkBox_showOptimized->setEnabled(
false);
6326 ui_->label_type->setText(QString::number(link.
type()));
6327 ui_->label_type_name->setText(tr(
"(%1)")
6337 ui_->label_variance->setText(QString(
"%1, %2")
6340 std::stringstream
out;
6342 ui_->lineEdit_covariance->setText(
out.str().c_str());
6343 ui_->label_constraint->setText(QString(
"%1").
arg(
t.prettyPrint().c_str()).replace(
" ",
"\n"));
6345 (
int)
graphes_.size()-1 ==
ui_->horizontalSlider_iterations->maximum())
6349 std::map<int, rtabmap::Transform>::iterator iterFrom =
graph.find(link.
from());
6350 std::map<int, rtabmap::Transform>::iterator iterTo =
graph.find(link.
to());
6351 if(iterFrom !=
graph.end() && iterTo !=
graph.end())
6353 ui_->checkBox_showOptimized->setEnabled(
true);
6358 float a = pcl::getAngle3D(Eigen::Vector4f(
v1.x(),
v1.y(),
v1.z(), 0), Eigen::Vector4f(
v2.x(),
v2.y(),
v2.z(), 0));
6359 a = (
a *180.0f) / CV_PI;
6360 ui_->label_constraint_opt->setText(QString(
"%1\n(error=%2% a=%3)").arg(QString(topt.
prettyPrint().c_str()).replace(
" ",
"\n")).arg((
t.getNorm()>0?
diff/
t.getNorm():0)*100.0f).arg(
a));
6362 if(
ui_->checkBox_showOptimized->isChecked())
6369 if(updateImageSliders)
6371 ui_->horizontalSlider_A->blockSignals(
true);
6372 ui_->horizontalSlider_B->blockSignals(
true);
6378 ui_->horizontalSlider_A->blockSignals(
false);
6379 ui_->horizontalSlider_B->blockSignals(
false);
6382 ui_->spinBox_indexA,
6383 ui_->label_parentsA,
6384 ui_->label_childrenA,
6388 ui_->graphicsView_A,
6392 ui_->label_optposeA,
6396 ui_->label_gravityA,
6398 ui_->toolButton_edit_priorA,
6399 ui_->toolButton_remove_priorA,
6402 ui_->label_sensorsA,
6407 ui_->spinBox_indexB,
6408 ui_->label_parentsB,
6409 ui_->label_childrenB,
6413 ui_->graphicsView_B,
6417 ui_->label_optposeB,
6421 ui_->label_gravityB,
6423 ui_->toolButton_edit_priorB,
6424 ui_->toolButton_remove_priorB,
6427 ui_->label_sensorsB,
6436 if(signatureFrom.
id()>0)
6448 if(signatureTo.
id()>0)
6452 else if(link.
to()>0)
6462 if(
ui_->checkBox_odomFrame->isChecked())
6468 std::vector<float>
v;
6484 if(
ui_->checkBox_show3Dclouds->isChecked())
6486 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudFrom, cloudTo;
6487 pcl::IndicesPtr indicesFrom(
new std::vector<int>);
6488 pcl::IndicesPtr indicesTo(
new std::vector<int>);
6498 if(cloudTo.get() && indicesTo->size())
6504 if(
ui_->doubleSpinBox_gainCompensationRadius->value()>0.0 &&
6505 cloudFrom.get() && indicesFrom->size() &&
6506 cloudTo.get() && indicesTo->size())
6511 compensator.
apply(0, cloudFrom, indicesFrom);
6512 compensator.
apply(1, cloudTo, indicesTo);
6513 UINFO(
"Gain compensation time = %fs",
t.ticks());
6516 if(cloudFrom.get() && indicesFrom->size())
6518 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
6520 cloudFrom =
util3d::voxelize(cloudFrom, indicesFrom,
ui_->doubleSpinBox_voxelSize->value());
6524 if(cloudTo.get() && indicesTo->size())
6526 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
6536 if(
ui_->checkBox_show3DWords->isChecked())
6539 ids.push_back(link.
from());
6542 ids.push_back(link.
to());
6544 std::list<Signature*> signatures;
6546 if(signatures.size() == 2 || (link.
to()<0 && signatures.size()==1))
6548 const Signature * sFrom = signatureFrom.
id()>0?&signatureFrom:signatures.front();
6550 if(signatures.size()==2)
6552 sTo = signatureTo.
id()>0?&signatureTo:signatures.back();
6556 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudFrom(
new pcl::PointCloud<pcl::PointXYZ>);
6557 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudTo(
new pcl::PointCloud<pcl::PointXYZ>);
6558 cloudFrom->resize(sFrom->
getWords3().size());
6561 cloudTo->resize(sTo->
getWords3().size());
6566 for(std::multimap<int, int>::const_iterator
iter=sFrom->
getWords().begin();
6571 cloudFrom->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
6577 for(std::multimap<int, int>::const_iterator
iter=sTo->
getWords().begin();
6582 cloudTo->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
6586 if(cloudFrom->size())
6599 if(cloudFrom->size())
6605 UWARN(
"Empty 3D words for node %d", link.
from());
6616 UWARN(
"Empty 3D words for node %d", link.
to());
6623 UERROR(
"Not found signature %d or %d in RAM", link.
from(), link.
to());
6628 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
6638 if(
ui_->checkBox_show2DScans->isChecked())
6643 signatureTo.
id()==0)
6645 std::vector<int> ids;
6647 if(userData.type() == CV_8SC1 &&
6648 userData.rows == 1 &&
6649 userData.cols >= 8 &&
6650 userData.at<
char>(userData.cols-1) == 0 &&
6651 memcmp(userData.data,
"SCANS:", 6) == 0)
6653 std::string scansStr = (
const char *)userData.data;
6654 UINFO(
"Detected \"%s\" in links's user data", scansStr.c_str());
6655 if(!scansStr.empty())
6657 std::list<std::string> strs =
uSplit(scansStr,
':');
6658 if(strs.size() == 2)
6660 std::list<std::string> strIds =
uSplit(strs.rbegin()->c_str(),
';');
6661 for(std::list<std::string>::iterator
iter=strIds.begin();
iter!=strIds.end(); ++
iter)
6663 ids.push_back(atoi(
iter->c_str()));
6664 if(ids.back() == link.
from())
6677 std::map<int, rtabmap::Transform> poses;
6678 for(
unsigned int i=0;
i<ids.size(); ++
i)
6686 UERROR(
"Not found %d node!", ids[
i]);
6694 std::map<int, rtabmap::Transform> posesOut;
6695 std::multimap<int, rtabmap::Link> linksOut;
6703 if(poses.size() != posesOut.size())
6705 UWARN(
"Scan poses input and output are different! %d vs %d", (
int)poses.size(), (
int)posesOut.size());
6709 for(std::map<int, Transform>::iterator
iter=posesOut.begin();
iter!=posesOut.end(); ++
iter)
6711 UDEBUG(
" %d=%s",
iter->first,
iter->second.prettyPrint().c_str());
6714 for(std::multimap<int, Link>::iterator
iter=linksOut.begin();
iter!=linksOut.end(); ++
iter)
6716 UDEBUG(
" %d->%d (type=%s) %s",
iter->second.from(),
iter->second.to(),
iter->second.typeName().c_str(),
iter->second.transform().prettyPrint().c_str());
6719 std::map<int, rtabmap::Transform> finalPoses = optimizer->
optimize(link.
to(), posesOut, linksOut);
6722 UDEBUG(
"Output poses: ");
6723 for(std::map<int, Transform>::iterator
iter=finalPoses.begin();
iter!=finalPoses.end(); ++
iter)
6725 UDEBUG(
" %d=%s",
iter->first,
iter->second.prettyPrint().c_str());
6730 pcl::PointCloud<pcl::PointXYZ>::Ptr assembledScans(
new pcl::PointCloud<pcl::PointXYZ>);
6731 pcl::PointCloud<pcl::PointNormal>::Ptr assembledNormalScans(
new pcl::PointCloud<pcl::PointNormal>);
6732 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledIScans(
new pcl::PointCloud<pcl::PointXYZI>);
6733 pcl::PointCloud<pcl::PointXYZINormal>::Ptr assembledINormalScans(
new pcl::PointCloud<pcl::PointXYZINormal>);
6734 pcl::PointCloud<pcl::PointXYZ>::Ptr
graph(
new pcl::PointCloud<pcl::PointXYZ>);
6735 for(std::map<int, Transform>::iterator
iter=finalPoses.begin();
iter!=finalPoses.end(); ++
iter)
6738 if(
iter->first != link.
to())
6744 data.uncompressDataConst(0, 0, &scan, 0);
6768 if(assembledNormalScans->size())
6773 if(assembledScans->size())
6778 if(assembledINormalScans->size())
6783 if(assembledIScans->size())
6803 pcl::PointCloud<pcl::PointXYZINormal>::Ptr scan;
6810 pcl::PointCloud<pcl::PointNormal>::Ptr scan;
6817 pcl::PointCloud<pcl::PointXYZI>::Ptr scan;
6824 pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
6834 pcl::PointCloud<pcl::PointXYZINormal>::Ptr scan;
6841 pcl::PointCloud<pcl::PointNormal>::Ptr scan;
6848 pcl::PointCloud<pcl::PointXYZI>::Ptr scan;
6855 pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
6891 #if PCL_VERSION_COMPARE(>=, 1, 7, 2)
6912 ui_->pushButton_refine->setEnabled(
false);
6913 ui_->pushButton_reset->setEnabled(
false);
6914 ui_->pushButton_add->setEnabled(
false);
6915 ui_->pushButton_reject->setEnabled(
false);
6916 ui_->toolButton_constraint->setEnabled(
false);
6924 currentLink =
loopLinks_.at(
ui_->horizontalSlider_loops->value());
6925 from = currentLink.
from();
6926 to = currentLink.
to();
6930 from =
ids_.at(
ui_->horizontalSlider_A->value());
6931 to =
ids_.at(
ui_->horizontalSlider_B->value());
6932 if(from!=to && from && to &&
6935 (
ui_->checkBox_enableForAll->isChecked() ||
6942 ui_->pushButton_add->setEnabled(
true);
6945 else if(
ui_->checkBox_enableForAll->isChecked())
6949 UWARN(
"Button \"Add\" cannot be enabled even if \"all\" checkbox is checked, as node %d doesn't have odometry set.", from);
6953 UWARN(
"Button \"Add\" cannot be enabled even if \"all\" checkbox is checked, as node %d doesn't have odometry set.", to);
6961 ((currentLink.
from() == from && currentLink.
to() == to) || (currentLink.
from() == to && currentLink.
to() == from)))
6965 ui_->pushButton_reject->setEnabled(
true);
6972 currentLink =
iter->second;
6973 ui_->pushButton_reset->setEnabled(
true);
6976 ui_->toolButton_constraint->setEnabled(
true);
6987 bool alignToGPS =
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked();
6998 if(refPoses.size() ==
graph.size() &&
length >= 100.0f)
7003 UINFO(
"KITTI t_err = %f %%", t_err);
7004 UINFO(
"KITTI r_err = %f deg/m", r_err);
7007 float translational_rmse = 0.0f;
7008 float translational_mean = 0.0f;
7009 float translational_median = 0.0f;
7010 float translational_std = 0.0f;
7011 float translational_min = 0.0f;
7012 float translational_max = 0.0f;
7013 float rotational_rmse = 0.0f;
7014 float rotational_mean = 0.0f;
7015 float rotational_median = 0.0f;
7016 float rotational_std = 0.0f;
7017 float rotational_min = 0.0f;
7018 float rotational_max = 0.0f;
7025 translational_median,
7038 ui_->label_rmse->setNum(translational_rmse);
7039 UINFO(
"translational_rmse=%f", translational_rmse);
7040 UINFO(
"translational_mean=%f", translational_mean);
7041 UINFO(
"translational_median=%f", translational_median);
7042 UINFO(
"translational_std=%f", translational_std);
7043 UINFO(
"translational_min=%f", translational_min);
7044 UINFO(
"translational_max=%f", translational_max);
7046 UINFO(
"rotational_rmse=%f", rotational_rmse);
7047 UINFO(
"rotational_mean=%f", rotational_mean);
7048 UINFO(
"rotational_median=%f", rotational_median);
7049 UINFO(
"rotational_std=%f", rotational_std);
7050 UINFO(
"rotational_min=%f", rotational_min);
7051 UINFO(
"rotational_max=%f", rotational_max);
7054 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked())) &&
7059 iter->second = gtToMap *
iter->second;
7064 std::map<int, rtabmap::Transform> graphFiltered;
7065 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
7066 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
7073 graphFiltered =
graph;
7075 if(
ui_->groupBox_posefiltering->isChecked())
7078 ui_->doubleSpinBox_posefilteringRadius->value(),
7079 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
7082 #ifdef RTABMAP_OCTOMAP
7086 if(
ui_->dockWidget_graphView->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
7089 UINFO(
"Update local maps list...");
7090 std::vector<int> ids =
uKeys(graphFiltered);
7091 for(
unsigned int i=0;
i<ids.size(); ++
i)
7098 else if(ids.at(
i)>0)
7102 cv::Mat ground, obstacles,
empty;
7103 if(
data.gridCellSize()>0.0f)
7105 data.uncompressData(0, 0, 0, 0, &ground, &obstacles, &
empty);
7107 localMaps_.
add(ids.at(
i), ground, obstacles,
empty,
data.gridCellSize()>0.0f?
data.gridCellSize():Parameters::defaultGridCellSize(),
data.gridViewPoint());
7108 if(!ground.empty() || !obstacles.empty())
7114 UINFO(
"Update local maps list... done (%d local maps, graph size=%d)", (
int)combinedLocalMaps.
size(), (
int)
graph.size());
7118 float cellSize = Parameters::defaultGridCellSize();
7124 if(
ui_->checkBox_wmState->isEnabled() &&
7125 ui_->checkBox_wmState->isChecked() &&
7128 bool allNodesAreInWM =
true;
7129 std::map<int, float> colors;
7134 colors.insert(std::make_pair(
iter->first, 1));
7138 allNodesAreInWM =
false;
7141 if(!allNodesAreInWM)
7143 ui_->graphViewer->updatePosterior(colors, 1, 1);
7146 QGraphicsRectItem * rectScaleItem = 0;
7147 ui_->graphViewer->clearMap();
7149 if(
graph.size() && combinedLocalMaps.
size() &&
7150 (
ui_->graphViewer->isGridMapVisible() ||
ui_->dockWidget_occupancyGridView->isVisible()))
7155 #ifdef RTABMAP_OCTOMAP
7156 if(
ui_->checkBox_octomap->isChecked())
7163 if((
ui_->dockWidget_graphView->isVisible() &&
ui_->graphViewer->isGridMapVisible()) ||
7164 (
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_2d->isChecked()))
7166 bool eroded = Parameters::defaultGridGlobalEroded();
7171 #ifdef RTABMAP_OCTOMAP
7172 if(
ui_->checkBox_octomap->isChecked())
7184 grid.
update(graphFiltered);
7185 if(
ui_->checkBox_grid_showProbMap->isChecked())
7191 map = grid.
getMap(xMin, yMin);
7195 ui_->label_timeGrid->setNum(
double(
time.elapsed())/1000.0);
7200 if(
ui_->dockWidget_graphView->isVisible() &&
ui_->graphViewer->isGridMapVisible())
7202 ui_->graphViewer->updateMap(map8U, cellSize, xMin, yMin);
7204 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_2d->isChecked())
7213 int xLast = map.cols;
7214 int yLast = map.rows;
7215 bool firstSet =
false;
7216 bool lastSet =
false;
7217 for(
int x=0;
x<map.cols && (!firstSet || !lastSet); ++
x)
7219 for(
int y=0;
y<map.rows; ++
y)
7222 if(!firstSet && map.at<
signed char>(
y,
x) != -1)
7228 int opp = map.cols-(
x+1);
7229 if(!lastSet && map.at<
signed char>(
y, opp) != -1)
7238 for(
int y=0;
y<map.rows && (!firstSet || !lastSet); ++
y)
7240 for(
int x=0;
x<map.cols; ++
x)
7243 if(!firstSet && map.at<
signed char>(
y,
x) != -1)
7249 int opp = map.rows-(
y+1);
7250 if(!lastSet && map.at<
signed char>(map.rows-(
y+1),
x) != -1)
7258 if( (xLast > xFirst && yLast > yFirst) &&
7260 xLast < map.cols-50 ||
7262 yLast < map.rows-50))
7264 rectScaleItem =
ui_->graphViewer->scene()->addRect(
7269 rectScaleItem->setTransform(QTransform::fromScale(cellSize*100.0
f, -cellSize*100.0
f),
true);
7270 rectScaleItem->setRotation(90);
7271 rectScaleItem->setPos(-yMin*100.0
f, -xMin*100.0
f);
7277 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_grid->isChecked())
7279 #ifdef RTABMAP_OCTOMAP
7280 if(
ui_->checkBox_octomap->isChecked())
7287 CloudMap cloudMap(&combinedLocalMaps, parameters);
7289 cloudMap.
update(graphFiltered);
7291 const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & groundCells = cloudMap.
getMapGround();
7292 const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & obstacleCells = cloudMap.
getMapObstacles();
7293 const pcl::PointCloud<pcl::PointXYZ>::Ptr & emptyCells = cloudMap.
getMapEmptyCells();
7296 if(groundCells->size())
7301 QColor(
ui_->lineEdit_groundColor->text()));
7304 if(obstacleCells->size())
7309 QColor(
ui_->lineEdit_obstacleColor->text()));
7312 if(
ui_->checkBox_grid_empty->isChecked() && emptyCells->size())
7317 QColor(
ui_->lineEdit_emptyColor->text()));
7325 #ifdef RTABMAP_GRIDMAP
7327 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
7328 ui_->checkBox_grid_elevation->checkState() != Qt::Unchecked)
7330 GridMap gridMap(&combinedLocalMaps, parameters);
7332 gridMap.
update(graphFiltered);
7333 if(
ui_->checkBox_grid_elevation->checkState() == Qt::PartiallyChecked)
7348 ui_->graphViewer->fitInView(
ui_->graphViewer->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
7349 if(rectScaleItem != 0)
7351 ui_->graphViewer->fitInView(rectScaleItem, Qt::KeepAspectRatio);
7352 ui_->graphViewer->scene()->removeItem(rectScaleItem);
7353 delete rectScaleItem;
7356 ui_->graphViewer->update();
7357 ui_->label_iterations->setNum(
value);
7363 std::map<int, rtabmap::Transform>::const_iterator jterA =
graph.find(
iter->first);
7364 std::map<int, rtabmap::Transform>::const_iterator jterB =
graph.find(
iter->second.to());
7365 if(jterA !=
graph.end() && jterB !=
graph.end())
7372 Eigen::Vector3f
vA,
vB;
7388 if(
ui_->horizontalSlider_rotation->isEnabled())
7390 float theta =
float(
ui_->horizontalSlider_rotation->value())*
M_PI/1800.0f;
7391 ui_->graphViewer->setWorldMapRotation(theta);
7392 ui_->label_rotation->setText(QString::number(
float(-
ui_->horizontalSlider_rotation->value())/10.0f,
'f', 1) +
" deg");
7396 ui_->graphViewer->setWorldMapRotation(0);
7402 ui_->label_loopClosures->clear();
7403 ui_->label_poses->clear();
7404 ui_->label_rmse->clear();
7406 if(sender() ==
ui_->checkBox_alignPosesWithGPS &&
ui_->checkBox_alignPosesWithGPS->isChecked())
7408 ui_->checkBox_alignPosesWithGroundTruth->setChecked(
false);
7410 else if(sender() ==
ui_->checkBox_alignPosesWithGroundTruth &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked())
7412 ui_->checkBox_alignPosesWithGPS->setChecked(
false);
7417 int fromId =
ui_->spinBox_optimizationsFrom->value();
7420 QMessageBox::warning(
this, tr(
""), tr(
"Graph optimization from id (%1) for which node is not linked to graph.\n Minimum=%2, Maximum=%3")
7430 std::map<int, rtabmap::Transform> poses =
odomPoses_;
7431 if(
ui_->checkBox_wmState->isEnabled() &&
7432 ui_->checkBox_wmState->isChecked() &&
7435 std::map<int, rtabmap::Transform> wmPoses;
7436 std::vector<int> & wmState =
wmStates_.at(fromId);
7437 for(
unsigned int i=0;
i<wmState.size(); ++
i)
7439 std::map<int, rtabmap::Transform>::iterator
iter = poses.find(wmState[
i]);
7440 if(
iter!=poses.end())
7442 wmPoses.insert(*
iter);
7445 if(!wmPoses.empty())
7451 UWARN(
"Empty WM poses!? Ignoring WM state... (root id=%d, wmState=%d)", fromId, wmState.size());
7458 int currentMapId =
mapIds_.at(fromId);
7459 for(std::map<int, rtabmap::Transform>::iterator
iter=poses.begin();
iter!=poses.end();)
7463 poses.erase(
iter++);
7472 ui_->menuExport_poses->setEnabled(
true);
7473 std::multimap<int, rtabmap::Link> links =
links_;
7478 int currentMapId =
mapIds_.at(fromId);
7479 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end();)
7484 links.erase(
iter++);
7494 if(
ui_->checkBox_ignorePoseCorrection->isChecked())
7496 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
7507 iter->second.from(),
7509 iter->second.type(),
7517 double markerPriorsLinearVariance = Parameters::defaultMarkerPriorsVarianceLinear();
7518 double markerPriorsAngularVariance = Parameters::defaultMarkerPriorsVarianceAngular();
7519 std::map<int, Transform> markerPriors;
7521 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceLinear(), markerPriorsLinearVariance);
7522 UASSERT(markerPriorsLinearVariance>0.0
f);
7523 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceAngular(), markerPriorsAngularVariance);
7524 UASSERT(markerPriorsAngularVariance>0.0
f);
7525 std::string markerPriorsStr;
7528 std::list<std::string> strList =
uSplit(markerPriorsStr,
'|');
7529 for(std::list<std::string>::iterator
iter=strList.begin();
iter!=strList.end(); ++
iter)
7531 std::string markerStr = *
iter;
7532 while(!markerStr.empty() && !
uIsDigit(markerStr[0]))
7534 markerStr.erase(markerStr.begin());
7536 if(!markerStr.empty())
7541 if(!
prior.isNull() &&
id>0)
7543 markerPriors.insert(std::make_pair(-
id,
prior));
7544 UDEBUG(
"Added landmark prior %d: %s",
id,
prior.prettyPrint().c_str());
7548 UERROR(
"Failed to parse element \"%s\" in parameter %s", markerStr.c_str(), Parameters::kMarkerPriors().c_str());
7551 else if(!
iter->empty())
7553 UERROR(
"Failed to parse parameter %s, value=\"%s\"", Parameters::kMarkerPriors().
c_str(),
iter->c_str());
7560 int totalNeighbor = 0;
7561 int totalNeighborMerged = 0;
7562 int totalGlobal = 0;
7563 int totalLocalTime = 0;
7564 int totalLocalSpace = 0;
7566 int totalPriors = 0;
7567 int totalLandmarks = 0;
7568 int totalGravity = 0;
7569 std::multimap<int, int> uniqueLinks;
7570 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end();)
7572 bool isUnique =
iter->second.from() ==
iter->second.to();
7575 uniqueLinks.insert(std::make_pair(
iter->second.from(),
iter->second.to()));
7587 ++totalNeighborMerged;
7591 if(
ui_->checkBox_ignoreGlobalLoop->isChecked())
7593 links.erase(
iter++);
7601 if(
ui_->checkBox_ignoreLocalLoopSpace->isChecked())
7603 links.erase(
iter++);
7611 if(
ui_->checkBox_ignoreLocalLoopTime->isChecked())
7613 links.erase(
iter++);
7621 if(
ui_->checkBox_ignoreUserLoop->isChecked())
7623 links.erase(
iter++);
7631 if(
ui_->checkBox_ignoreLandmarks->isChecked())
7633 links.erase(
iter++);
7637 if(poses.find(
iter->second.from()) != poses.end() && poses.find(
iter->second.to()) == poses.end())
7639 poses.insert(std::make_pair(
iter->second.to(), poses.at(
iter->second.from())*
iter->second.transform()));
7645 int markerId =
iter->second.to();
7646 if(markerPriors.find(markerId) != markerPriors.end())
7648 cv::Mat infMatrix = cv::Mat::eye(6, 6, CV_64FC1);
7649 infMatrix(cv::Range(0,3), cv::Range(0,3)) /= markerPriorsLinearVariance;
7650 infMatrix(cv::Range(3,6), cv::Range(3,6)) /= markerPriorsAngularVariance;
7651 links.insert(std::make_pair(markerId,
Link(markerId, markerId,
Link::kPosePrior, markerPriors.at(markerId), infMatrix)));
7652 UDEBUG(
"Added prior %d : %s (variance: lin=%f ang=%f)", markerId, markerPriors.at(markerId).prettyPrint().c_str(),
7653 markerPriorsLinearVariance, markerPriorsAngularVariance);
7672 ui_->label_loopClosures->setText(tr(
"(%1, %2, %3, %4, %5, %6, %7, %8, %9)")
7674 .
arg(totalNeighborMerged)
7676 .
arg(totalLocalSpace)
7677 .
arg(totalLocalTime)
7680 .
arg(totalLandmarks)
7681 .
arg(totalGravity));
7684 if(
ui_->checkBox_ignoreIntermediateNodes->isVisible() &&
7685 ui_->checkBox_ignoreIntermediateNodes->isEnabled() &&
7686 ui_->checkBox_ignoreIntermediateNodes->isChecked())
7688 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
7696 std::multimap<int, Link>::iterator uter = links.find(link.
to());
7697 while(uter != links.end() &&
7698 uter->first==link.
to() &&
7699 uter->second.from()>uter->second.to())
7703 if(uter != links.end())
7705 poses.erase(link.
to());
7706 link = link.
merge(uter->second, uter->second.type());
7707 links.erase(uter->first);
7715 iter->second = link;
7720 bool applyRotation = sender() ==
ui_->pushButton_applyRotation;
7723 float xMin, yMin, cellSize;
7727 QMessageBox::StandardButton r = QMessageBox::question(
this,
7728 tr(
"Rotate Optimized Graph"),
7729 tr(
"There is a 2D occupancy grid or mesh already saved in "
7730 "database. Applying rotation will clear them (they can be "
7731 "regenerated later from File menu options). "
7732 "Do you want to continue?"),
7733 QMessageBox::Cancel | QMessageBox::Yes,
7734 QMessageBox::Cancel);
7735 if(r != QMessageBox::Yes)
7737 applyRotation =
false;
7742 std::map<int, Transform> optPoses;
7745 ui_->comboBox_optimizationFlavor->currentIndex() == 2)
7748 if(optPoses.empty())
7750 ui_->comboBox_optimizationFlavor->setCurrentIndex(0);
7751 QMessageBox::warning(
this, tr(
"Optimization Flavor"),
7752 tr(
"There is no local optimized graph in the database, "
7753 "falling back to global iterative optimization."));
7758 ui_->comboBox_optimizationFlavor->currentIndex() != 2)
7760 if(
ui_->horizontalSlider_rotation->value()!=0 && applyRotation)
7762 float theta =
float(-
ui_->horizontalSlider_rotation->value())*
M_PI/1800.0f;
7764 poses.at(fromId) = rotT * poses.at(fromId);
7771 std::map<int, rtabmap::Transform> posesOut;
7772 std::multimap<int, rtabmap::Link> linksOut;
7773 UINFO(
"Get connected graph from %d (%d poses, %d links)", fromId, (
int)poses.size(), (
int)links.size());
7780 UINFO(
"Connected graph of %d poses and %d links", (
int)posesOut.size(), (
int)linksOut.size());
7783 std::map<int, rtabmap::Transform> finalPoses = optimizer->
optimize(fromId, posesOut, linksOut,
ui_->comboBox_optimizationFlavor->currentIndex()==0?&
graphes_:0);
7784 ui_->label_timeOptimization->setNum(
double(
time.elapsed())/1000.0);
7786 if(posesOut.size() && finalPoses.empty())
7788 UWARN(
"Optimization failed... (poses=%d, links=%d).", (
int)posesOut.size(), (
int)linksOut.size());
7791 QMessageBox::warning(
this, tr(
"Graph optimization error!"), tr(
"Graph optimization has failed. See the terminal for potential errors. "
7792 "Give it a try with %1=0 and %2=true.").
arg(Parameters::kOptimizerStrategy().
c_str()).
arg(Parameters::kOptimizerVarianceIgnored().
c_str()));
7796 QMessageBox::warning(
this, tr(
"Graph optimization error!"), tr(
"Graph optimization has failed. See the terminal for potential errors."));
7799 ui_->label_poses->setNum((
int)finalPoses.size());
7803 if(applyRotation && !finalPoses.empty())
7805 ui_->comboBox_optimizationFlavor->setCurrentIndex(2);
7808 if(lastLocalizationPose.
isNull())
7811 lastLocalizationPose = finalPoses.rbegin()->second;
7815 float xMin, yMin, cellSize;
7821 QMessageBox::StandardButton r = QMessageBox::question(
this,
7822 tr(
"Rotate Optimized Graph"),
7823 tr(
"Optimized graph has been rotated and saved back to database. "
7824 "Note that 2D occupancy grid and mesh have been cleared (if set). "
7825 "Do you want to regenerate the 2D occupancy grid now "
7826 "(can be done later from File menu)?"),
7827 QMessageBox::Ignore | QMessageBox::Yes,
7829 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
7830 ui_->actionExport_saved_2D_map->setEnabled(
false);
7831 ui_->actionImport_2D_map->setEnabled(
false);
7832 ui_->actionView_optimized_mesh->setEnabled(
false);
7833 ui_->actionExport_optimized_mesh->setEnabled(
false);
7834 if(r == QMessageBox::Yes)
7843 if(
ui_->comboBox_optimizationFlavor->currentIndex() == 2)
7848 ui_->label_timeOptimization->setNum(0);
7849 ui_->label_poses->setNum((
int)optPoses.size());
7852 ui_->horizontalSlider_rotation->setEnabled(
false);
7853 ui_->pushButton_applyRotation->setEnabled(
false);
7854 ui_->spinBox_optimizationsFrom->setEnabled(
false);
7855 ui_->checkBox_spanAllMaps->setEnabled(
false);
7856 ui_->checkBox_wmState->setEnabled(
false);
7857 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
7858 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
7859 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
7860 ui_->checkBox_ignoreIntermediateNodes->setEnabled(
false);
7865 ui_->pushButton_applyRotation->setEnabled(
true);
7866 ui_->horizontalSlider_rotation->setEnabled(
true);
7867 ui_->spinBox_optimizationsFrom->setEnabled(
true);
7868 ui_->checkBox_spanAllMaps->setEnabled(
true);
7869 ui_->checkBox_wmState->setEnabled(
true);
7870 ui_->checkBox_alignPosesWithGPS->setEnabled(
ui_->checkBox_alignPosesWithGPS->isVisible());
7871 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
ui_->checkBox_alignPosesWithGroundTruth->isVisible());
7872 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
ui_->checkBox_alignScansCloudsWithGroundTruth->isVisible());
7873 ui_->checkBox_ignoreIntermediateNodes->setEnabled(
true);
7879 if(
ui_->doubleSpinBox_optimizationScale->value()!=-1.0)
7884 for(std::map<int, Transform>::iterator jter=
iter->begin(); jter!=
iter->end(); ++jter)
7886 jter->second = jter->second.clone();
7887 jter->second.x() *=
ui_->doubleSpinBox_optimizationScale->value();
7888 jter->second.y() *=
ui_->doubleSpinBox_optimizationScale->value();
7889 jter->second.z() *=
ui_->doubleSpinBox_optimizationScale->value();
7894 ui_->horizontalSlider_iterations->setMaximum((
int)
graphes_.size()-1);
7895 ui_->horizontalSlider_iterations->setValue((
int)
graphes_.size()-1);
7896 ui_->horizontalSlider_iterations->setEnabled(
true);
7897 ui_->spinBox_optimizationsFrom->setEnabled(
true);
7902 ui_->horizontalSlider_iterations->setEnabled(
false);
7903 ui_->spinBox_optimizationsFrom->setEnabled(
false);
7909 if(sender() ==
ui_->checkBox_grid_2d && !
ui_->checkBox_grid_2d->isChecked())
7917 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
7918 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7919 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7920 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7921 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
7922 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7923 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7924 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7933 #ifdef RTABMAP_OCTOMAP
7934 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
7935 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7936 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7937 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7938 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
7939 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7940 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7941 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7943 if(
ui_->checkBox_octomap->isChecked())
7955 if(
ui_->comboBox_octomap_rendering_type->currentIndex()>0)
7961 pcl::IndicesPtr obstacles(
new std::vector<int>);
7962 pcl::IndicesPtr
empty(
new std::vector<int>);
7963 pcl::IndicesPtr ground(
new std::vector<int>);
7964 pcl::IndicesPtr frontiers(
new std::vector<int>);
7965 std::vector<double> prob;
7967 ui_->spinBox_grid_depth->value(),
7977 pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7978 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
7982 pcl::PointCloud<pcl::PointXYZRGB>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7983 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
7989 pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7990 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
7994 pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7995 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
8000 if(
ui_->checkBox_grid_empty->isChecked())
8002 if(prob.size()==cloud->size())
8004 float occThr = Parameters::defaultGridGlobalOccupancyThr();
8005 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridGlobalOccupancyThr(), occThr);
8006 pcl::PointCloud<pcl::PointXYZRGB>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
8007 emptyCloud->resize(
empty->size());
8008 for(
unsigned int i=0;
i<
empty->size(); ++
i)
8010 emptyCloud->points.at(
i).x = cloud->points.at(
empty->at(
i)).x;
8011 emptyCloud->points.at(
i).y = cloud->points.at(
empty->at(
i)).y;
8012 emptyCloud->points.at(
i).z = cloud->points.at(
empty->at(
i)).z;
8013 QColor hsv = QColor::fromHsv(
int(prob.at(
empty->at(
i))/occThr*240.0), 255, 255, 255);
8014 QRgb color = hsv.rgb();
8015 emptyCloud->points.at(
i).r = qRed(color);
8016 emptyCloud->points.at(
i).g = qGreen(color);
8017 emptyCloud->points.at(
i).b = qBlue(color);
8025 pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZ>);
8026 pcl::copyPointCloud(*cloud, *
empty, *emptyCloud);
8033 if(
ui_->checkBox_grid_frontiers->isChecked())
8035 pcl::PointCloud<pcl::PointXYZ>::Ptr frontiersCloud(
new pcl::PointCloud<pcl::PointXYZ>);
8036 pcl::copyPointCloud(*cloud, *frontiers, *frontiersCloud);
8044 if(
ui_->dockWidget_view3d->isVisible() &&
ui_->checkBox_showGrid->isChecked())
8058 link = findIter->second;
8065 link = findIter->second;
8070 if(findIter !=
links_.end())
8072 link = findIter->second;
8086 int from =
ids_.at(
ui_->horizontalSlider_A->value());
8087 int to =
ids_.at(
ui_->horizontalSlider_B->value());
8093 UDEBUG(
"%d -> %d", from, to);
8094 bool switchedIds =
false;
8097 UWARN(
"Cannot refine link to same node");
8105 UERROR(
"Not found link! (%d->%d)", from, to);
8109 UDEBUG(
"%d -> %d (type=%d)", currentLink.
from(), currentLink.
to(), currentLink.
type());
8111 if(
ui_->checkBox_showOptimized->isChecked() &&
8114 (
int)
graphes_.size()-1 ==
ui_->horizontalSlider_iterations->maximum())
8119 std::map<int, rtabmap::Transform>::iterator iterFrom =
graph.find(currentLink.
from());
8120 std::map<int, rtabmap::Transform>::iterator iterTo =
graph.find(currentLink.
to());
8121 if(iterFrom !=
graph.end() && iterTo !=
graph.end())
8128 else if(
ui_->checkBox_ignorePoseCorrection->isChecked() &&
8151 UERROR(
"Signature %d not found!", currentLink.
from());
8161 std::map<int, rtabmap::Transform> scanPoses;
8165 userData.type() == CV_8SC1 &&
8166 userData.rows == 1 &&
8167 userData.cols >= 8 &&
8168 userData.at<
char>(userData.cols-1) == 0 &&
8169 memcmp(userData.data,
"SCANS:", 6) == 0 &&
8170 currentLink.
from() > currentLink.
to())
8172 std::string scansStr = (
const char *)userData.data;
8173 UINFO(
"Detected \"%s\" in links's user data", scansStr.c_str());
8174 if(!scansStr.empty())
8176 std::list<std::string> strs =
uSplit(scansStr,
':');
8177 if(strs.size() == 2)
8179 std::list<std::string> strIds =
uSplit(strs.rbegin()->c_str(),
';');
8180 for(std::list<std::string>::iterator
iter=strIds.begin();
iter!=strIds.end(); ++
iter)
8182 int id = atoi(
iter->c_str());
8189 UERROR(
"Not found %d node!",
id);
8195 if(scanPoses.size()>1)
8201 std::map<int, rtabmap::Transform> posesOut;
8202 std::multimap<int, rtabmap::Link> linksOut;
8210 if(scanPoses.size() != posesOut.size())
8212 UWARN(
"Scan poses input and output are different! %d vs %d", (
int)scanPoses.size(), (
int)posesOut.size());
8213 UWARN(
"Input poses: ");
8214 for(std::map<int, Transform>::iterator
iter=scanPoses.begin();
iter!=scanPoses.end(); ++
iter)
8218 UWARN(
"Input links: ");
8220 for(std::multimap<int, Link>::iterator
iter=modifiedLinks.begin();
iter!=modifiedLinks.end(); ++
iter)
8226 scanPoses = optimizer->
optimize(currentLink.
to(), posesOut, linksOut);
8229 std::map<int, Transform> filteredScanPoses = scanPoses;
8230 float proximityFilteringRadius = 0.0f;
8231 Parameters::parse(parameters, Parameters::kRGBDProximityPathFilteringRadius(), proximityFilteringRadius);
8232 if(scanPoses.size() > 2 && proximityFilteringRadius > 0.0f)
8237 filteredScanPoses.insert(*scanPoses.find(currentLink.
to()));
8244 int maxPoints = fromScan.
size();
8247 UWARN(
"From scan %d is empty!", fromS->
id());
8249 pcl::PointCloud<pcl::PointXYZ>::Ptr assembledToClouds(
new pcl::PointCloud<pcl::PointXYZ>);
8250 pcl::PointCloud<pcl::PointNormal>::Ptr assembledToNormalClouds(
new pcl::PointCloud<pcl::PointNormal>);
8251 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledToIClouds(
new pcl::PointCloud<pcl::PointXYZI>);
8252 pcl::PointCloud<pcl::PointXYZINormal>::Ptr assembledToNormalIClouds(
new pcl::PointCloud<pcl::PointXYZINormal>);
8253 pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledToRGBClouds(
new pcl::PointCloud<pcl::PointXYZRGB>);
8254 pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr assembledToNormalRGBClouds(
new pcl::PointCloud<pcl::PointXYZRGBNormal>);
8255 for(std::map<int, Transform>::const_iterator
iter = filteredScanPoses.begin();
iter!=filteredScanPoses.end(); ++
iter)
8257 if(
iter->first != currentLink.
from())
8261 if(!
data.laserScanCompressed().isEmpty())
8264 data.uncompressData(0, 0, &scan);
8307 if(scan.
size() > maxPoints)
8309 maxPoints = scan.
size();
8314 UWARN(
"scan format of %d is not the same than from scan %d: %d vs %d",
data.id(), fromS->
id(), scan.
format(), fromScan.
format());
8319 UWARN(
"Laser scan not found for signature %d",
iter->first);
8325 if(assembledToNormalClouds->size())
8329 else if(assembledToClouds->size())
8333 else if(assembledToNormalIClouds->size())
8337 else if(assembledToIClouds->size())
8341 else if(assembledToNormalRGBClouds->size())
8346 else if(assembledToRGBClouds->size())
8353 UWARN(
"Assembled scan is empty!");
8369 info.covariance*=100.0;
8377 UERROR(
"Signature %d not found!", currentLink.
to());
8382 bool reextractVisualFeatures =
uStr2Bool(parameters.at(Parameters::kRGBDLoopClosureReextractFeatures()));
8386 reextractVisualFeatures ||
8399 if(reextractVisualFeatures)
8402 fromS->
sensorData().
setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
8409 if(
ui_->checkBox_icp_from_depth->isChecked())
8412 cv::Mat tmpA, tmpB, tmpC, tmpD;
8417 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8418 ui_->doubleSpinBox_icp_maxDepth->value(),
8419 ui_->doubleSpinBox_icp_minDepth->value(),
8421 ui_->parameters_toolbox->getParameters());
8424 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8425 ui_->doubleSpinBox_icp_maxDepth->value(),
8426 ui_->doubleSpinBox_icp_minDepth->value(),
8428 ui_->parameters_toolbox->getParameters());
8430 if(cloudFrom->empty() && cloudTo->empty())
8432 std::string
msg =
"Option to generate scan from depth is checked (GUI Parameters->Refine), but "
8433 "resulting clouds from depth are empty. Transformation estimation will likely "
8434 "fails. Uncheck the parameter to use laser scans.";
8438 QMessageBox::warning(
this,
8440 tr(
"%1").
arg(
msg.c_str()));
8445 UWARN(
"There are laser scans in data, but generate laser scan from "
8446 "depth image option is activated (GUI Parameters->Refine). "
8447 "Ignoring saved laser scans...");
8450 int maxLaserScans = cloudFrom->size();
8464 cv::Mat tmpA, tmpB, tmpC, tmpD;
8469 UINFO(
"Uncompress time: %f s",
timer.ticks());
8471 if(fromS->
id() < toS->
id())
8483 UINFO(
"(%d ->%d) Registration time: %f s", currentLink.
from(), currentLink.
to(),
timer.ticks());
8489 if(
info.covariance.at<
double>(0,0)<=0.0)
8491 info.covariance = cv::Mat::eye(6,6,CV_64FC1)*0.0001;
8505 if(
iter->second.to() == currentLink.
to() &&
8506 iter->second.type() == currentLink.
type())
8508 iter->second = newLink;
8525 if(!silent &&
ui_->dockWidget_constraints->isVisible())
8527 if(toS && fromS->
id() > 0 && toS->
id() > 0)
8530 std::multimap<int, cv::KeyPoint> keypointsFrom;
8531 std::multimap<int, cv::KeyPoint> keypointsTo;
8536 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8543 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8569 if(toS && fromS->
id() > 0 && toS->
id() > 0)
8572 std::multimap<int, cv::KeyPoint> keypointsFrom;
8573 std::multimap<int, cv::KeyPoint> keypointsTo;
8578 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8585 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8601 QMessageBox::warning(
this,
8603 tr(
"Cannot find a transformation between nodes %1 and %2: %3").
arg(currentLink.
from()).
arg(currentLink.
to()).arg(
info.rejectedMsg.c_str()));
8611 int from =
ids_.at(
ui_->horizontalSlider_A->value());
8612 int to =
ids_.at(
ui_->horizontalSlider_B->value());
8618 bool switchedIds =
false;
8621 UWARN(
"Cannot add link to same node");
8631 std::list<Signature*> signatures;
8646 bool loopCovLimited = Parameters::defaultRGBDLoopCovLimited();
8647 Parameters::parse(parameters, Parameters::kRGBDLoopCovLimited(), loopCovLimited);
8657 ids.push_back(from);
8660 if(signatures.size() != 2)
8662 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
8668 fromS = *signatures.begin();
8669 toS = *signatures.rbegin();
8671 bool reextractVisualFeatures =
uStr2Bool(parameters.at(Parameters::kRGBDLoopClosureReextractFeatures()));
8674 reextractVisualFeatures ||
8682 if(reextractVisualFeatures)
8685 fromS->
sensorData().
setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
8694 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8695 ui_->doubleSpinBox_icp_maxDepth->value(),
8696 ui_->doubleSpinBox_icp_minDepth->value(),
8698 ui_->parameters_toolbox->getParameters());
8701 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8702 ui_->doubleSpinBox_icp_maxDepth->value(),
8703 ui_->doubleSpinBox_icp_minDepth->value(),
8705 ui_->parameters_toolbox->getParameters());
8707 if(cloudFrom->empty() && cloudTo->empty())
8709 std::string
msg =
"Option to generate scan from depth is checked (GUI Parameters->Refine), but "
8710 "resulting clouds from depth are empty. Transformation estimation will likely "
8711 "fails. Uncheck the parameter to use laser scans.";
8715 QMessageBox::warning(
this,
8717 tr(
"%1").
arg(
msg.c_str()));
8722 UWARN(
"There are laser scans in data, but generate laser scan from "
8723 "depth image option is activated (GUI Parameters->Refine). Ignoring saved laser scans...");
8726 int maxLaserScans = cloudFrom->size();
8731 else if(!reextractVisualFeatures && fromS->
getWords().empty() && toS->
getWords().empty())
8733 std::string
msg =
uFormat(
"\"%s\" is false and signatures (%d and %d) don't have words, "
8734 "registration will not be possible. Set \"%s\" to true.",
8735 Parameters::kRGBDLoopClosureReextractFeatures().
c_str(),
8738 Parameters::kRGBDLoopClosureReextractFeatures().c_str());
8742 QMessageBox::warning(
this,
8744 tr(
"%1").
arg(
msg.c_str()));
8749 bool guessFromGraphRejected =
false;
8755 std::map<int, Transform> optimizedPoses =
graphes_.back();
8756 if(optimizedPoses.size() > 0)
8758 std::map<int, Transform>::iterator fromIter = optimizedPoses.find(from);
8759 std::map<int, Transform>::iterator toIter = optimizedPoses.find(to);
8760 if(fromIter != optimizedPoses.end() &&
8761 toIter != optimizedPoses.end())
8765 if(QMessageBox::question(
this,
8766 tr(
"Add constraint from optimized graph"),
8767 tr(
"Registration is done without vision (see %1 parameter), "
8768 "do you want to use a guess from the optimized graph?"
8770 "\n\nOtherwise, if "
8771 "the database has images, it is recommended to use %2=2 instead so that "
8772 "the guess can be found visually.")
8773 .
arg(Parameters::kRegStrategy().
c_str()).
arg(Parameters::kRegStrategy().
c_str()),
8774 QMessageBox::Yes | QMessageBox::No,
8775 QMessageBox::Yes) == QMessageBox::Yes)
8777 guess = fromIter->second.
inverse() * toIter->second;
8781 guessFromGraphRejected =
true;
8784 else if(silentlyUseOptimizedGraphAsGuess)
8786 guess = fromIter->second.
inverse() * toIter->second;
8791 if(guess.
isNull() && !silent && !guessFromGraphRejected)
8793 if(QMessageBox::question(
this,
8794 tr(
"Add constraint without guess"),
8795 tr(
"Registration is done without vision (see %1 parameter) and we cannot (or we don't want to) find relative "
8796 "transformation between the nodes with the current graph. Do you want to use an identity "
8797 "transform for ICP guess? "
8799 "\n\nOtherwise, if the database has images, it is recommended to use %2=2 "
8800 "instead so that the guess can be found visually.")
8801 .
arg(Parameters::kRegStrategy().
c_str()).
arg(Parameters::kRegStrategy().
c_str()),
8802 QMessageBox::Yes | QMessageBox::Abort,
8803 QMessageBox::Abort) == QMessageBox::Yes)
8809 guessFromGraphRejected =
true;
8831 cv::Mat information =
info.covariance.inv();
8832 if(odomMaxInf.size() == 6 && information.cols==6 && information.rows==6)
8834 for(
int i=0;
i<6; ++
i)
8836 if(information.at<
double>(
i,
i) > odomMaxInf[
i])
8838 information.at<
double>(
i,
i) = odomMaxInf[
i];
8845 else if(!silent && !guessFromGraphRejected)
8847 QMessageBox::StandardButton button = QMessageBox::warning(
this,
8849 tr(
"Cannot find a transformation between nodes %1 and %2: %3\n\nDo you want to add it manually?").
arg(from).
arg(to).
arg(
info.rejectedMsg.c_str()),
8850 QMessageBox::Yes | QMessageBox::No,
8852 if(button == QMessageBox::Yes)
8864 bool updateConstraints = newLink.
isValid();
8865 float maxOptimizationError =
uStr2Float(
ui_->parameters_toolbox->getParameters().at(Parameters::kRGBDOptimizeMaxError()));
8867 maxOptimizationError > 0.0f &&
8868 uStr2Int(
ui_->parameters_toolbox->getParameters().at(Parameters::kOptimizerIterations())) > 0.0f)
8870 int fromId = newLink.
from();
8872 linksIn.insert(std::make_pair(newLink.
from(), newLink));
8873 const Link * maxLinearLink = 0;
8874 const Link * maxAngularLink = 0;
8875 float maxLinearErrorRatio = 0.0f;
8876 float maxAngularErrorRatio = 0.0f;
8878 std::map<int, Transform> poses;
8879 std::multimap<int, Link> links;
8887 const std::map<int, Transform> & optimizedPoses =
graphes_.back();
8888 for(std::map<int, Transform>::iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
8890 if(optimizedPoses.find(
iter->first) != optimizedPoses.end())
8892 iter->second = optimizedPoses.at(
iter->first);
8896 UASSERT(poses.find(fromId) != poses.end());
8897 UASSERT_MSG(poses.find(newLink.
from()) != poses.end(),
uFormat(
"id=%d poses=%d links=%d", newLink.
from(), (
int)poses.size(), (
int)links.size()).c_str());
8898 UASSERT_MSG(poses.find(newLink.
to()) != poses.end(),
uFormat(
"id=%d poses=%d links=%d", newLink.
to(), (
int)poses.size(), (
int)links.size()).c_str());
8900 std::map<int, Transform> posesIn = poses;
8901 poses = optimizer->
optimize(fromId, posesIn, links);
8902 if(posesIn.size() && poses.empty())
8904 UWARN(
"Optimization failed... (poses=%d, links=%d).", (
int)posesIn.size(), (
int)links.size());
8909 float maxLinearError = 0.0f;
8910 float maxAngularError = 0.0f;
8914 maxLinearErrorRatio,
8915 maxAngularErrorRatio,
8922 UINFO(
"Max optimization linear error = %f m (link %d->%d, var=%f, ratio error/std=%f)", maxLinearError, maxLinearLink->
from(), maxLinearLink->
to(), maxLinearLink->
transVariance(), maxLinearError/
sqrt(maxLinearLink->
transVariance()));
8923 if(maxLinearErrorRatio > maxOptimizationError)
8925 msg =
uFormat(
"Rejecting edge %d->%d because "
8926 "graph error is too large (abs=%f m) after optimization (ratio %f for edge %d->%d, stddev=%f m). "
8931 maxLinearErrorRatio,
8932 maxLinearLink->
from(),
8933 maxLinearLink->
to(),
8935 Parameters::kRGBDOptimizeMaxError().c_str(),
8936 maxOptimizationError);
8941 UINFO(
"Max optimization angular error = %f deg (link %d->%d, var=%f, ratio error/std=%f)", maxAngularError*180.0
f/CV_PI, maxAngularLink->
from(), maxAngularLink->
to(), maxAngularLink->
rotVariance(), maxAngularError/
sqrt(maxAngularLink->
rotVariance()));
8942 if(maxAngularErrorRatio > maxOptimizationError)
8944 msg =
uFormat(
"Rejecting edge %d->%d because "
8945 "graph error is too large (abs=%f deg) after optimization (ratio %f for edge %d->%d, stddev=%f deg). "
8949 maxAngularError*180.0f/CV_PI,
8950 maxAngularErrorRatio,
8951 maxAngularLink->
from(),
8952 maxAngularLink->
to(),
8954 Parameters::kRGBDOptimizeMaxError().c_str(),
8955 maxOptimizationError);
8961 msg =
uFormat(
"Rejecting edge %d->%d because graph optimization has failed!",
8970 QMessageBox::warning(
this,
8972 tr(
"%1").
arg(
msg.c_str()));
8975 updateConstraints =
false;
8978 if(updateConstraints && silent && !
graphes_.empty() &&
graphes_.back().size() == poses.size())
8984 if(updateConstraints)
8993 if(newLink.
from() < newLink.
to())
9005 if((updateConstraints && newLink.
from() > newLink.
to()) || (!updateConstraints && switchedIds))
9012 if(updateConstraints)
9019 std::multimap<int, cv::KeyPoint> keypointsFrom;
9020 std::multimap<int, cv::KeyPoint> keypointsTo;
9025 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
9032 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
9039 else if(updateConstraints)
9046 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
9051 return updateConstraints;
9056 int from =
ids_.at(
ui_->horizontalSlider_A->value());
9057 int to =
ids_.at(
ui_->horizontalSlider_B->value());
9060 int position =
ui_->horizontalSlider_loops->value();
9075 UWARN(
"Cannot reset link to same node");
9092 int priorId = sender() ==
ui_->toolButton_remove_priorA?
ids_.at(
ui_->horizontalSlider_A->value()):
9093 sender() ==
ui_->toolButton_remove_priorB?
ids_.at(
ui_->horizontalSlider_B->value()):0;
9095 int from = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_A->value());
9096 int to = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_B->value());
9099 int position =
ui_->horizontalSlider_loops->value();
9112 if(priorId==0 && from == to)
9114 UWARN(
"Cannot reject link to same node");
9118 bool removed =
false;
9121 std::multimap<int, Link>::iterator
iter;
9127 QMessageBox::StandardButton button = QMessageBox::warning(
this, tr(
"Reject link"),
9128 tr(
"Removing the neighbor link %1->%2 will split the graph. Do you want to continue?").
arg(from).
arg(to),
9129 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
9130 if(button != QMessageBox::Yes)
9160 bool priorsIgnored = Parameters::defaultOptimizerPriorsIgnored();
9161 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kOptimizerPriorsIgnored(), priorsIgnored);
9162 int indexA =
ui_->horizontalSlider_A->value();
9163 int indexB =
ui_->horizontalSlider_B->value();
9168 if(
ui_->horizontalSlider_A->value() != indexA)
9169 ui_->horizontalSlider_A->setValue(indexA);
9172 if(
ui_->horizontalSlider_B->value() != indexB)
9173 ui_->horizontalSlider_B->setValue(indexB);
9181 const std::multimap<int, rtabmap::Link> & edgeConstraints)
9185 std::multimap<int, rtabmap::Link> links;
9186 for(std::multimap<int, rtabmap::Link>::const_iterator
iter=edgeConstraints.begin();
9187 iter!=edgeConstraints.end();
9190 std::multimap<int, rtabmap::Link>::iterator findIter;
9195 UDEBUG(
"Removed link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9203 if(
iter->second.from() == findIter->second.to() &&
9204 iter->second.from() !=
iter->second.to())
9206 links.insert(std::make_pair(
iter->second.from(), findIter->second.inverse()));
9210 links.insert(*findIter);
9212 UDEBUG(
"Updated link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9216 links.insert(*
iter);
9220 for(std::multimap<int, rtabmap::Link>::const_iterator
iter=
linksAdded_.begin();
9227 links.insert(*findIter);
9228 links.insert(std::make_pair(findIter->second.to(), findIter->second.inverse()));
9229 UDEBUG(
"Added refined link (%d->%d, %d)", findIter->second.from(), findIter->second.to(), findIter->second.type());
9233 UDEBUG(
"Added link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9234 links.insert(*
iter);
9235 links.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
9243 UDEBUG(
"%d %d", from, to);
9246 int position =
ui_->horizontalSlider_neighbors->value();
9247 std::multimap<int, Link> linksSortedByChildren;
9248 for(std::multimap<int, rtabmap::Link>::iterator
iter = links.begin();
iter!=links.end(); ++
iter)
9250 if(
iter->second.from() <
iter->second.to())
9252 linksSortedByChildren.insert(*
iter);
9256 for(std::multimap<int, rtabmap::Link>::iterator
iter = linksSortedByChildren.begin();
iter!=linksSortedByChildren.end(); ++
iter)
9258 if(!
iter->second.transform().isNull())
9263 if((
iter->second.from() == from &&
iter->second.to() == to) ||
9264 (
iter->second.to() == from &&
iter->second.from() == to))
9273 UERROR(
"Transform null for link from %d to %d",
iter->first,
iter->second.to());
9284 ui_->horizontalSlider_neighbors->setMinimum(0);
9286 ui_->horizontalSlider_neighbors->setEnabled(
true);
9287 if(
position !=
ui_->horizontalSlider_neighbors->value())
9289 ui_->horizontalSlider_neighbors->setValue(
position);
9298 ui_->horizontalSlider_neighbors->setEnabled(
false);
9307 UDEBUG(
"%d %d", from, to);
9310 int position =
ui_->horizontalSlider_loops->value();
9311 std::multimap<int, Link> linksSortedByParents;
9312 for(std::multimap<int, rtabmap::Link>::iterator
iter = links.begin();
iter!=links.end(); ++
iter)
9314 if(
iter->second.to() >
iter->second.from() &&
iter->second.from() < 0)
9316 linksSortedByParents.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
9318 else if(
iter->second.to() <
iter->second.from())
9320 linksSortedByParents.insert(*
iter);
9323 for(std::multimap<int, rtabmap::Link>::iterator
iter = linksSortedByParents.begin();
iter!=linksSortedByParents.end(); ++
iter)
9325 if(!
iter->second.transform().isNull())
9330 if((
iter->second.from() == from &&
iter->second.to() == to) ||
9331 (
iter->second.to() == from &&
iter->second.from() == to))
9340 UERROR(
"Transform null for link from %d to %d",
iter->first,
iter->second.to());
9351 ui_->horizontalSlider_loops->setMinimum(0);
9352 ui_->horizontalSlider_loops->setMaximum(
loopLinks_.size()-1);
9353 ui_->horizontalSlider_loops->setEnabled(
true);
9354 if(
position !=
ui_->horizontalSlider_loops->value())
9365 ui_->horizontalSlider_loops->setEnabled(
false);
9376 for(QStringList::const_iterator
iter=parametersChanged.constBegin();
9380 QString group =
iter->split(
'/').first();