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 connect(
ui_->toolButton_edit_priorA, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
355 connect(
ui_->toolButton_edit_priorB, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
356 connect(
ui_->toolButton_remove_priorA, SIGNAL(clicked(
bool)),
this, SLOT(
rejectConstraint()));
357 connect(
ui_->toolButton_remove_priorB, SIGNAL(clicked(
bool)),
this, SLOT(
rejectConstraint()));
359 connect(
ui_->spinBox_mesh_angleTolerance, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
360 connect(
ui_->spinBox_mesh_minClusterSize, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
361 connect(
ui_->spinBox_mesh_fillDepthHoles, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
362 connect(
ui_->spinBox_mesh_depthError, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
363 connect(
ui_->checkBox_mesh_quad, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
364 connect(
ui_->spinBox_mesh_triangleSize, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
365 connect(
ui_->checkBox_showWords, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
366 connect(
ui_->checkBox_showCloud, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
367 connect(
ui_->checkBox_showMesh, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
368 connect(
ui_->checkBox_showScan, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
369 connect(
ui_->checkBox_showMap, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
370 connect(
ui_->checkBox_showGrid, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
371 connect(
ui_->checkBox_showElevation, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
372 connect(
ui_->checkBox_odomFrame_3dview, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
373 connect(
ui_->checkBox_gravity_3dview, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
375 ui_->horizontalSlider_neighbors->setTracking(
false);
376 ui_->horizontalSlider_loops->setTracking(
false);
377 ui_->horizontalSlider_neighbors->setEnabled(
false);
378 ui_->horizontalSlider_loops->setEnabled(
false);
388 ui_->checkBox_showOptimized->setEnabled(
false);
389 connect(
ui_->toolButton_constraint, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
392 ui_->horizontalSlider_iterations->setTracking(
false);
393 ui_->horizontalSlider_iterations->setEnabled(
false);
394 ui_->spinBox_optimizationsFrom->setEnabled(
false);
397 connect(
ui_->spinBox_optimizationsFrom, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
398 connect(
ui_->comboBox_optimizationFlavor, SIGNAL(activated(
int)),
this, SLOT(
updateGraphView()));
399 connect(
ui_->checkBox_spanAllMaps, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
400 connect(
ui_->checkBox_wmState, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
401 connect(
ui_->graphViewer, SIGNAL(mapShownRequested()),
this, SLOT(
updateGraphView()));
402 connect(
ui_->checkBox_ignorePoseCorrection, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
404 connect(
ui_->checkBox_ignoreGlobalLoop, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
405 connect(
ui_->checkBox_ignoreLocalLoopSpace, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
406 connect(
ui_->checkBox_ignoreLocalLoopTime, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
407 connect(
ui_->checkBox_ignoreUserLoop, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
408 connect(
ui_->checkBox_ignoreLandmarks, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
409 connect(
ui_->doubleSpinBox_optimizationScale, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
410 connect(
ui_->checkBox_octomap, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
411 connect(
ui_->checkBox_grid_grid, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
412 connect(
ui_->checkBox_grid_2d, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
413 connect(
ui_->checkBox_grid_elevation, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
414 connect(
ui_->comboBox_octomap_rendering_type, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
updateOctomapView()));
416 connect(
ui_->checkBox_grid_empty, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
417 connect(
ui_->checkBox_grid_frontiers, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
418 connect(
ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
updateConstraintView()));
420 connect(
ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(
double)),
this, SLOT(
update3dView()));
421 connect(
ui_->checkBox_cameraProjection, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
422 connect(
ui_->checkBox_showDisparityInsteadOfRight, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
424 connect(
ui_->spinBox_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
425 connect(
ui_->groupBox_posefiltering, SIGNAL(clicked(
bool)),
this, SLOT(
updateGraphView()));
426 connect(
ui_->doubleSpinBox_posefilteringRadius, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
427 connect(
ui_->doubleSpinBox_posefilteringAngle, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
429 ui_->label_stereo_inliers_name->setStyleSheet(
"QLabel {color : blue; }");
430 ui_->label_stereo_flowOutliers_name->setStyleSheet(
"QLabel {color : red; }");
431 ui_->label_stereo_slopeOutliers_name->setStyleSheet(
"QLabel {color : yellow; }");
432 ui_->label_stereo_disparityOutliers_name->setStyleSheet(
"QLabel {color : magenta; }");
436 connect(
ui_->graphViewer, SIGNAL(configChanged()),
this, SLOT(
configModified()));
437 connect(
ui_->graphicsView_A, SIGNAL(configChanged()),
this, SLOT(
configModified()));
438 connect(
ui_->graphicsView_B, SIGNAL(configChanged()),
this, SLOT(
configModified()));
439 connect(
ui_->comboBox_logger_level, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
configModified()));
440 connect(
ui_->actionVertical_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
441 connect(
ui_->checkBox_alignPosesWithGPS, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
442 connect(
ui_->checkBox_alignPosesWithGroundTruth, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
443 connect(
ui_->checkBox_alignScansCloudsWithGroundTruth, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
444 connect(
ui_->checkBox_ignoreIntermediateNodes, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
445 connect(
ui_->checkBox_ignoreIntermediateNodes, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
446 connect(
ui_->checkBox_timeStats, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
447 connect(
ui_->checkBox_timeStats, SIGNAL(stateChanged(
int)),
this, SLOT(
updateStatistics()));
449 connect(
ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
450 connect(
ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
451 connect(
ui_->checkBox_cameraProjection, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
452 connect(
ui_->checkBox_showDisparityInsteadOfRight, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
453 connect(
ui_->spinBox_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
454 connect(
ui_->groupBox_posefiltering, SIGNAL(clicked(
bool)),
this, SLOT(
configModified()));
455 connect(
ui_->doubleSpinBox_posefilteringRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
456 connect(
ui_->doubleSpinBox_posefilteringAngle, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
458 connect(
ui_->pushButton_applyRotation, SIGNAL(clicked()),
this, SLOT(
updateGraphView()));
460 connect(
ui_->spinBox_icp_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
461 connect(
ui_->doubleSpinBox_icp_maxDepth, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
462 connect(
ui_->doubleSpinBox_icp_minDepth, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
463 connect(
ui_->checkBox_icp_from_depth, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
465 connect(
ui_->doubleSpinBox_detectMore_radius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
466 connect(
ui_->doubleSpinBox_detectMore_radiusMin, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
467 connect(
ui_->doubleSpinBox_detectMore_angle, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
468 connect(
ui_->spinBox_detectMore_iterations, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
469 connect(
ui_->checkBox_detectMore_intraSession, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
470 connect(
ui_->checkBox_detectMore_interSession, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
471 connect(
ui_->checkBox_opt_graph_as_guess, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
473 connect(
ui_->lineEdit_obstacleColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
474 connect(
ui_->lineEdit_groundColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
475 connect(
ui_->lineEdit_emptyColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
476 connect(
ui_->lineEdit_frontierColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
477 connect(
ui_->lineEdit_obstacleColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
478 connect(
ui_->lineEdit_groundColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
479 connect(
ui_->lineEdit_emptyColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
480 connect(
ui_->lineEdit_frontierColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
483 connect(
ui_->toolButton_emptyColor, SIGNAL(clicked(
bool)),
this, SLOT(
selectEmptyColor()));
485 connect(
ui_->spinBox_cropRadius, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
486 connect(
ui_->checkBox_grid_showProbMap, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
487 connect(
ui_->checkBox_grid_showProbMap, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
492 QList<QDockWidget*> dockWidgets = this->findChildren<QDockWidget*>();
493 for(
int i=0;
i<dockWidgets.size(); ++
i)
495 connect(dockWidgets[
i], SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
this, SLOT(
configModified()));
496 connect(dockWidgets[
i]->toggleViewAction(), SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
498 ui_->dockWidget_constraints->installEventFilter(
this);
499 ui_->dockWidget_graphView->installEventFilter(
this);
500 ui_->dockWidget_occupancyGridView->installEventFilter(
this);
501 ui_->dockWidget_stereoView->installEventFilter(
this);
502 ui_->dockWidget_view3d->installEventFilter(
this);
503 ui_->dockWidget_guiparameters->installEventFilter(
this);
504 ui_->dockWidget_coreparameters->installEventFilter(
this);
505 ui_->dockWidget_info->installEventFilter(
this);
506 ui_->dockWidget_statistics->installEventFilter(
this);
513 #ifdef RTABMAP_OCTOMAP
522 qobject_cast<QHBoxLayout *>(
ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::TopToBottom);
526 qobject_cast<QHBoxLayout *>(
ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::LeftToRight);
536 ui_->buttonBox->setVisible(visible);
541 this->setWindowModified(
true);
550 QString privatePath = QDir::homePath() +
"/.rtabmap";
551 if(!QDir(privatePath).exists())
553 QDir::home().mkdir(
".rtabmap");
555 return privatePath +
"/rtabmap.ini";
561 QSettings settings(
path, QSettings::IniFormat);
562 settings.beginGroup(
"DatabaseViewer");
566 bytes = settings.value(
"geometry", QByteArray()).toByteArray();
569 this->restoreGeometry(
bytes);
571 bytes = settings.value(
"state", QByteArray()).toByteArray();
574 this->restoreState(
bytes);
578 ui_->comboBox_logger_level->setCurrentIndex(settings.value(
"loggerLevel",
ui_->comboBox_logger_level->currentIndex()).toInt());
579 ui_->actionVertical_Layout->setChecked(settings.value(
"verticalLayout",
ui_->actionVertical_Layout->isChecked()).toBool());
580 ui_->checkBox_ignoreIntermediateNodes->setChecked(settings.value(
"ignoreIntermediateNodes",
ui_->checkBox_ignoreIntermediateNodes->isChecked()).toBool());
581 ui_->checkBox_timeStats->setChecked(settings.value(
"timeStats",
ui_->checkBox_timeStats->isChecked()).toBool());
584 ui_->graphViewer->loadSettings(settings,
"GraphView");
586 settings.beginGroup(
"optimization");
587 ui_->doubleSpinBox_gainCompensationRadius->setValue(settings.value(
"gainCompensationRadius",
ui_->doubleSpinBox_gainCompensationRadius->value()).toDouble());
588 ui_->doubleSpinBox_voxelSize->setValue(settings.value(
"voxelSize",
ui_->doubleSpinBox_voxelSize->value()).toDouble());
589 ui_->spinBox_decimation->setValue(settings.value(
"decimation",
ui_->spinBox_decimation->value()).toInt());
590 ui_->checkBox_cameraProjection->setChecked(settings.value(
"camProj",
ui_->checkBox_cameraProjection->isChecked()).toBool());
591 ui_->checkBox_showDisparityInsteadOfRight->setChecked(settings.value(
"showDisp",
ui_->checkBox_showDisparityInsteadOfRight->isChecked()).toBool());
594 settings.beginGroup(
"grid");
595 ui_->groupBox_posefiltering->setChecked(settings.value(
"poseFiltering",
ui_->groupBox_posefiltering->isChecked()).toBool());
596 ui_->doubleSpinBox_posefilteringRadius->setValue(settings.value(
"poseFilteringRadius",
ui_->doubleSpinBox_posefilteringRadius->value()).toDouble());
597 ui_->doubleSpinBox_posefilteringAngle->setValue(settings.value(
"poseFilteringAngle",
ui_->doubleSpinBox_posefilteringAngle->value()).toDouble());
598 ui_->lineEdit_obstacleColor->setText(settings.value(
"colorObstacle",
ui_->lineEdit_obstacleColor->text()).toString());
599 ui_->lineEdit_groundColor->setText(settings.value(
"colorGround",
ui_->lineEdit_groundColor->text()).toString());
600 ui_->lineEdit_emptyColor->setText(settings.value(
"colorEmpty",
ui_->lineEdit_emptyColor->text()).toString());
601 ui_->lineEdit_frontierColor->setText(settings.value(
"colorFrontier",
ui_->lineEdit_frontierColor->text()).toString());
602 ui_->spinBox_cropRadius->setValue(settings.value(
"cropRadius",
ui_->spinBox_cropRadius->value()).toInt());
603 ui_->checkBox_grid_showProbMap->setChecked(settings.value(
"probMap",
ui_->checkBox_grid_showProbMap->isChecked()).toBool());
606 settings.beginGroup(
"mesh");
607 ui_->checkBox_mesh_quad->setChecked(settings.value(
"quad",
ui_->checkBox_mesh_quad->isChecked()).toBool());
608 ui_->spinBox_mesh_angleTolerance->setValue(settings.value(
"angleTolerance",
ui_->spinBox_mesh_angleTolerance->value()).toInt());
609 ui_->spinBox_mesh_minClusterSize->setValue(settings.value(
"minClusterSize",
ui_->spinBox_mesh_minClusterSize->value()).toInt());
610 ui_->spinBox_mesh_fillDepthHoles->setValue(settings.value(
"fillDepthHolesSize",
ui_->spinBox_mesh_fillDepthHoles->value()).toInt());
611 ui_->spinBox_mesh_depthError->setValue(settings.value(
"fillDepthHolesError",
ui_->spinBox_mesh_depthError->value()).toInt());
612 ui_->spinBox_mesh_triangleSize->setValue(settings.value(
"triangleSize",
ui_->spinBox_mesh_triangleSize->value()).toInt());
616 ui_->graphicsView_A->loadSettings(settings,
"ImageViewA");
617 ui_->graphicsView_B->loadSettings(settings,
"ImageViewB");
620 settings.beginGroup(
"icp");
621 ui_->spinBox_icp_decimation->setValue(settings.value(
"decimation",
ui_->spinBox_icp_decimation->value()).toInt());
622 ui_->doubleSpinBox_icp_maxDepth->setValue(settings.value(
"maxDepth",
ui_->doubleSpinBox_icp_maxDepth->value()).toDouble());
623 ui_->doubleSpinBox_icp_minDepth->setValue(settings.value(
"minDepth",
ui_->doubleSpinBox_icp_minDepth->value()).toDouble());
624 ui_->checkBox_icp_from_depth->setChecked(settings.value(
"icpFromDepth",
ui_->checkBox_icp_from_depth->isChecked()).toBool());
630 settings.beginGroup(
"Gui");
632 settings.beginGroup(
"PostProcessingDialog");
633 ui_->doubleSpinBox_detectMore_radius->setValue(settings.value(
"cluster_radius",
ui_->doubleSpinBox_detectMore_radius->value()).toDouble());
634 ui_->doubleSpinBox_detectMore_radiusMin->setValue(settings.value(
"cluster_radius_min",
ui_->doubleSpinBox_detectMore_radiusMin->value()).toDouble());
635 ui_->doubleSpinBox_detectMore_angle->setValue(settings.value(
"cluster_angle",
ui_->doubleSpinBox_detectMore_angle->value()).toDouble());
636 ui_->spinBox_detectMore_iterations->setValue(settings.value(
"iterations",
ui_->spinBox_detectMore_iterations->value()).toInt());
637 ui_->checkBox_detectMore_intraSession->setChecked(settings.value(
"intra_session",
ui_->checkBox_detectMore_intraSession->isChecked()).toBool());
638 ui_->checkBox_detectMore_interSession->setChecked(settings.value(
"inter_session",
ui_->checkBox_detectMore_interSession->isChecked()).toBool());
639 ui_->checkBox_opt_graph_as_guess->setChecked(settings.value(
"opt_graph_as_guess",
ui_->checkBox_opt_graph_as_guess->isChecked()).toBool());
646 for(ParametersMap::iterator
iter = parameters.begin();
iter!= parameters.end(); ++
iter)
648 ui_->parameters_toolbox->updateParameter(
iter->first,
iter->second);
655 QSettings settings(
path, QSettings::IniFormat);
656 settings.beginGroup(
"DatabaseViewer");
659 if(!this->isMaximized())
661 settings.setValue(
"geometry", this->saveGeometry());
663 settings.setValue(
"state", this->saveState());
664 settings.setValue(
"maximized", this->isMaximized());
667 settings.setValue(
"loggerLevel",
ui_->comboBox_logger_level->currentIndex());
668 settings.setValue(
"verticalLayout",
ui_->actionVertical_Layout->isChecked());
669 settings.setValue(
"ignoreIntermediateNodes",
ui_->checkBox_ignoreIntermediateNodes->isChecked());
670 settings.setValue(
"timeStats",
ui_->checkBox_timeStats->isChecked());
673 ui_->graphViewer->saveSettings(settings,
"GraphView");
676 settings.beginGroup(
"optimization");
677 settings.setValue(
"gainCompensationRadius",
ui_->doubleSpinBox_gainCompensationRadius->value());
678 settings.setValue(
"voxelSize",
ui_->doubleSpinBox_voxelSize->value());
679 settings.setValue(
"decimation",
ui_->spinBox_decimation->value());
680 settings.setValue(
"camProj",
ui_->checkBox_cameraProjection->isChecked());
681 settings.setValue(
"showDisp",
ui_->checkBox_showDisparityInsteadOfRight->isChecked());
685 settings.beginGroup(
"grid");
686 settings.setValue(
"poseFiltering",
ui_->groupBox_posefiltering->isChecked());
687 settings.setValue(
"poseFilteringRadius",
ui_->doubleSpinBox_posefilteringRadius->value());
688 settings.setValue(
"poseFilteringAngle",
ui_->doubleSpinBox_posefilteringAngle->value());
689 settings.setValue(
"colorObstacle",
ui_->lineEdit_obstacleColor->text());
690 settings.setValue(
"colorGround",
ui_->lineEdit_groundColor->text());
691 settings.setValue(
"colorEmpty",
ui_->lineEdit_emptyColor->text());
692 settings.setValue(
"colorFrontier",
ui_->lineEdit_frontierColor->text());
693 settings.setValue(
"cropRadius",
ui_->spinBox_cropRadius->value());
694 settings.setValue(
"probMap",
ui_->checkBox_grid_showProbMap->isChecked());
697 settings.beginGroup(
"mesh");
698 settings.setValue(
"quad",
ui_->checkBox_mesh_quad->isChecked());
699 settings.setValue(
"angleTolerance",
ui_->spinBox_mesh_angleTolerance->value());
700 settings.setValue(
"minClusterSize",
ui_->spinBox_mesh_minClusterSize->value());
701 settings.setValue(
"fillDepthHolesSize",
ui_->spinBox_mesh_fillDepthHoles->value());
702 settings.setValue(
"fillDepthHolesError",
ui_->spinBox_mesh_depthError->value());
703 settings.setValue(
"triangleSize",
ui_->spinBox_mesh_triangleSize->value());
707 ui_->graphicsView_A->saveSettings(settings,
"ImageViewA");
708 ui_->graphicsView_B->saveSettings(settings,
"ImageViewB");
711 settings.beginGroup(
"icp");
712 settings.setValue(
"decimation",
ui_->spinBox_icp_decimation->value());
713 settings.setValue(
"maxDepth",
ui_->doubleSpinBox_icp_maxDepth->value());
714 settings.setValue(
"minDepth",
ui_->doubleSpinBox_icp_minDepth->value());
715 settings.setValue(
"icpFromDepth",
ui_->checkBox_icp_from_depth->isChecked());
721 settings.beginGroup(
"Gui");
723 settings.beginGroup(
"PostProcessingDialog");
724 settings.setValue(
"cluster_radius",
ui_->doubleSpinBox_detectMore_radius->value());
725 settings.setValue(
"cluster_radius_min",
ui_->doubleSpinBox_detectMore_radiusMin->value());
726 settings.setValue(
"cluster_angle",
ui_->doubleSpinBox_detectMore_angle->value());
727 settings.setValue(
"iterations",
ui_->spinBox_detectMore_iterations->value());
728 settings.setValue(
"intra_session",
ui_->checkBox_detectMore_intraSession->isChecked());
729 settings.setValue(
"inter_session",
ui_->checkBox_detectMore_interSession->isChecked());
730 settings.setValue(
"opt_graph_as_guess",
ui_->checkBox_opt_graph_as_guess->isChecked());
735 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end();)
737 if(!
ui_->parameters_toolbox->getParameterWidget(
iter->first.c_str()))
739 parameters.erase(
iter++);
748 this->setWindowModified(
false);
754 ui_->comboBox_logger_level->setCurrentIndex(1);
755 ui_->checkBox_alignPosesWithGPS->setChecked(
true);
756 ui_->checkBox_alignPosesWithGroundTruth->setChecked(
true);
757 ui_->checkBox_alignScansCloudsWithGroundTruth->setChecked(
false);
758 ui_->checkBox_ignoreIntermediateNodes->setChecked(
false);
759 ui_->checkBox_timeStats->setChecked(
true);
761 ui_->comboBox_optimizationFlavor->setCurrentIndex(0);
762 ui_->checkBox_spanAllMaps->setChecked(
true);
763 ui_->checkBox_wmState->setChecked(
false);
764 ui_->checkBox_ignorePoseCorrection->setChecked(
false);
765 ui_->checkBox_ignoreGlobalLoop->setChecked(
false);
766 ui_->checkBox_ignoreLocalLoopSpace->setChecked(
false);
767 ui_->checkBox_ignoreLocalLoopTime->setChecked(
false);
768 ui_->checkBox_ignoreUserLoop->setChecked(
false);
769 ui_->checkBox_ignoreLandmarks->setChecked(
false);
770 ui_->doubleSpinBox_optimizationScale->setValue(1.0);
771 ui_->doubleSpinBox_gainCompensationRadius->setValue(0.0);
772 ui_->doubleSpinBox_voxelSize->setValue(0.0);
773 ui_->spinBox_decimation->setValue(1);
774 ui_->checkBox_cameraProjection->setChecked(
false);
775 ui_->checkBox_showDisparityInsteadOfRight->setChecked(
false);
777 ui_->groupBox_posefiltering->setChecked(
false);
778 ui_->doubleSpinBox_posefilteringRadius->setValue(0.1);
779 ui_->doubleSpinBox_posefilteringAngle->setValue(30);
780 ui_->checkBox_grid_empty->setChecked(
true);
781 ui_->checkBox_grid_frontiers->setChecked(
false);
782 ui_->checkBox_octomap->setChecked(
false);
783 ui_->lineEdit_obstacleColor->setText(QColor(Qt::red).
name());
784 ui_->lineEdit_groundColor->setText(QColor(Qt::green).
name());
785 ui_->lineEdit_emptyColor->setText(QColor(Qt::yellow).
name());
786 ui_->lineEdit_frontierColor->setText(QColor(Qt::cyan).
name());
787 ui_->spinBox_cropRadius->setValue(1);
788 ui_->checkBox_grid_showProbMap->setChecked(
false);
790 ui_->checkBox_mesh_quad->setChecked(
true);
791 ui_->spinBox_mesh_angleTolerance->setValue(15);
792 ui_->spinBox_mesh_minClusterSize->setValue(0);
793 ui_->spinBox_mesh_fillDepthHoles->setValue(
false);
794 ui_->spinBox_mesh_depthError->setValue(10);
795 ui_->spinBox_mesh_triangleSize->setValue(2);
797 ui_->spinBox_icp_decimation->setValue(1);
798 ui_->doubleSpinBox_icp_maxDepth->setValue(0.0);
799 ui_->doubleSpinBox_icp_minDepth->setValue(0.0);
800 ui_->checkBox_icp_from_depth->setChecked(
false);
802 ui_->doubleSpinBox_detectMore_radius->setValue(1.0);
803 ui_->doubleSpinBox_detectMore_radiusMin->setValue(0.0);
804 ui_->doubleSpinBox_detectMore_angle->setValue(30.0);
805 ui_->spinBox_detectMore_iterations->setValue(5);
806 ui_->checkBox_detectMore_intraSession->setChecked(
true);
807 ui_->checkBox_detectMore_interSession->setChecked(
true);
808 ui_->checkBox_opt_graph_as_guess->setChecked(
true);
813 QString
path = QFileDialog::getOpenFileName(
this, tr(
"Select file"),
pathDatabase_, tr(
"Databases (*.db)"));
822 UDEBUG(
"Open database \"%s\"",
path.toStdString().c_str());
823 if(QFile::exists(
path))
825 if(QFileInfo(
path).isFile())
827 std::string driverType =
"sqlite3";
833 ui_->actionClose_database->setEnabled(
false);
834 ui_->actionOpen_database->setEnabled(
true);
837 QMessageBox::warning(
this,
"Database error", tr(
"Can't open database \"%1\"").
arg(
path));
841 ui_->actionClose_database->setEnabled(
true);
842 ui_->actionOpen_database->setEnabled(
false);
856 uInsert(parameters, overriddenParameters);
858 if(parameters.size())
860 const ParametersMap & currentParameters =
ui_->parameters_toolbox->getParameters();
862 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end(); ++
iter)
864 ParametersMap::const_iterator jter = currentParameters.find(
iter->first);
865 if(jter!=currentParameters.end() &&
866 ui_->parameters_toolbox->getParameterWidget(QString(
iter->first.c_str())) != 0 &&
867 iter->second.compare(jter->second) != 0 &&
868 iter->first.compare(Parameters::kRtabmapWorkingDirectory()) != 0)
870 bool different =
true;
881 differentParameters.insert(*
iter);
882 QString
msg = tr(
"Parameter \"%1\": database=\"%2\" Preferences=\"%3\"")
883 .arg(
iter->first.c_str())
884 .arg(
iter->second.c_str())
885 .arg(jter->second.c_str());
891 if(differentParameters.size())
893 int r = QMessageBox::question(
this,
894 tr(
"Update parameters..."),
895 tr(
"The database is using %1 different parameter(s) than "
896 "those currently set in Core parameters panel. Do you want "
897 "to use database's parameters?").
arg(differentParameters.size()),
898 QMessageBox::Yes | QMessageBox::No,
900 if(r == QMessageBox::Yes)
903 for(rtabmap::ParametersMap::const_iterator
iter = differentParameters.begin();
iter!=differentParameters.end(); ++
iter)
905 ui_->parameters_toolbox->updateParameter(
iter->first,
iter->second);
906 str.push_back(
iter->first.c_str());
914 this->setWindowTitle(
"RTAB-Map Database Viewer - " +
path +
"[*]");
930 QMessageBox::warning(
this,
"Database error", tr(
"Database \"%1\" does not exist.").
arg(
path));
937 this->setWindowTitle(
"RTAB-Map Database Viewer[*]");
942 QMessageBox::StandardButton button = QMessageBox::question(
this,
943 tr(
"Links modified"),
944 tr(
"Some links are modified (%1 added, %2 refined, %3 removed), do you want to save them?")
946 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
947 QMessageBox::Cancel);
949 if(button == QMessageBox::Yes)
958 if(refinedIter->second.from() != refinedIter->second.to())
964 if(
iter->second.from() !=
iter->second.to())
975 if(
iter->second.from() !=
iter->second.to())
984 if(
iter->second.from() !=
iter->second.to())
1002 if(button != QMessageBox::Yes && button != QMessageBox::No)
1011 QMessageBox::StandardButton button = QMessageBox::question(
this,
1012 tr(
"Local occupancy grid maps modified"),
1013 tr(
"%1 occupancy grid maps are modified, do you want to "
1014 "save them? This will overwrite occupancy grids saved in the database.")
1016 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
1017 QMessageBox::Cancel);
1019 if(button == QMessageBox::Yes)
1027 mapIter->second.groundCells,
1028 mapIter->second.obstacleCells,
1029 mapIter->second.emptyCells,
1030 mapIter->second.cellSize,
1031 mapIter->second.viewPoint);
1040 if(button != QMessageBox::Yes && button != QMessageBox::No)
1048 QMessageBox::StandardButton button = QMessageBox::question(
this,
1049 tr(
"Laser scans modified"),
1050 tr(
"%1 laser scans are modified, do you want to "
1051 "save them? This will overwrite laser scans saved in the database.")
1053 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
1054 QMessageBox::Cancel);
1056 if(button == QMessageBox::Yes)
1065 if(button != QMessageBox::Yes && button != QMessageBox::No)
1094 ui_->graphViewer->clearAll();
1096 ui_->menuEdit->setEnabled(
false);
1097 ui_->actionGenerate_3D_map_pcd->setEnabled(
false);
1098 ui_->actionExport->setEnabled(
false);
1099 ui_->actionExtract_images->setEnabled(
false);
1100 ui_->menuExport_poses->setEnabled(
false);
1101 ui_->menuExport_GPS->setEnabled(
false);
1102 ui_->actionPoses_KML->setEnabled(
false);
1103 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
1104 ui_->actionExport_saved_2D_map->setEnabled(
false);
1105 ui_->actionImport_2D_map->setEnabled(
false);
1106 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
1107 ui_->actionView_optimized_mesh->setEnabled(
false);
1108 ui_->actionExport_optimized_mesh->setEnabled(
false);
1109 ui_->actionUpdate_optimized_mesh->setEnabled(
false);
1110 ui_->checkBox_showOptimized->setEnabled(
false);
1111 ui_->toolBox_statistics->clear();
1113 ui_->checkBox_alignPosesWithGPS->setVisible(
false);
1114 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
1115 ui_->checkBox_alignPosesWithGroundTruth->setVisible(
false);
1116 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
1117 ui_->checkBox_alignScansCloudsWithGroundTruth->setVisible(
false);
1118 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
1119 ui_->doubleSpinBox_optimizationScale->setVisible(
false);
1120 ui_->label_scale_title->setVisible(
false);
1121 ui_->label_rmse->setVisible(
false);
1122 ui_->label_rmse_title->setVisible(
false);
1123 ui_->checkBox_ignoreIntermediateNodes->setVisible(
false);
1124 ui_->label_ignoreINtermediateNdoes->setVisible(
false);
1125 ui_->label_alignPosesWithGPS->setVisible(
false);
1126 ui_->label_alignPosesWithGroundTruth->setVisible(
false);
1127 ui_->label_alignScansCloudsWithGroundTruth->setVisible(
false);
1128 ui_->label_optimizeFrom->setText(tr(
"Root"));
1129 ui_->textEdit_info->clear();
1131 ui_->pushButton_refine->setEnabled(
false);
1132 ui_->pushButton_add->setEnabled(
false);
1133 ui_->pushButton_reset->setEnabled(
false);
1134 ui_->pushButton_reject->setEnabled(
false);
1136 ui_->horizontalSlider_loops->setEnabled(
false);
1137 ui_->horizontalSlider_loops->setMaximum(0);
1138 ui_->horizontalSlider_iterations->setEnabled(
false);
1139 ui_->horizontalSlider_iterations->setMaximum(0);
1140 ui_->horizontalSlider_neighbors->setEnabled(
false);
1141 ui_->horizontalSlider_neighbors->setMaximum(0);
1142 ui_->label_constraint->clear();
1143 ui_->label_constraint_opt->clear();
1144 ui_->label_variance->clear();
1145 ui_->lineEdit_covariance->clear();
1146 ui_->label_type->clear();
1147 ui_->label_type_name->clear();
1148 ui_->checkBox_showOptimized->setEnabled(
false);
1150 ui_->horizontalSlider_A->setEnabled(
false);
1151 ui_->horizontalSlider_A->setMaximum(0);
1152 ui_->horizontalSlider_B->setEnabled(
false);
1153 ui_->horizontalSlider_B->setMaximum(0);
1154 ui_->label_idA->setText(
"NaN");
1155 ui_->label_idB->setText(
"NaN");
1168 ui_->graphViewer->clearAll();
1169 ui_->label_loopClosures->clear();
1170 ui_->label_timeOptimization->clear();
1171 ui_->label_pathLength->clear();
1172 ui_->label_poses->clear();
1173 ui_->label_rmse->clear();
1174 ui_->spinBox_optimizationsFrom->setEnabled(
false);
1176 ui_->graphicsView_A->clear();
1177 ui_->graphicsView_B->clear();
1179 ui_->graphicsView_stereo->clear();
1183 ui_->toolBox_statistics->clear();
1199 QString
path = QFileDialog::getOpenFileName(
this, tr(
"Select file"),
pathDatabase_, tr(
"Databases (*.db)"));
1204 QMessageBox::information(
this,
"Database recovery", tr(
"The selected database is already opened, close it first."));
1207 std::string errorMsg;
1209 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1211 progressDialog->show();
1216 QMessageBox::information(
this,
"Database recovery", tr(
"Database \"%1\" recovered! Try opening it again.").
arg(
path));
1220 QMessageBox::warning(
this,
"Database recovery", tr(
"Database recovery failed: \"%1\".").
arg(errorMsg.c_str()));
1230 if(this->isWindowModified())
1232 QMessageBox::Button
b=QMessageBox::question(
this,
1233 tr(
"Database Viewer"),
1234 tr(
"There are unsaved changed settings. Save them?"),
1235 QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard);
1236 if(
b == QMessageBox::Save)
1240 else if(
b != QMessageBox::Discard)
1259 if(event->isAccepted())
1261 ui_->toolBox_statistics->closeFigures();
1272 this->setWindowModified(
false);
1282 if(this->isVisible())
1295 if(this->isVisible())
1304 if((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_S)
1312 if (event->type() == QEvent::Resize && qobject_cast<QDockWidget*>(obj))
1314 this->setWindowModified(
true);
1316 return QWidget::eventFilter(obj, event);
1340 double previousStamp = 0;
1341 std::vector<double> delays(
ids_.size());
1343 std::map<int, Transform> poses;
1344 std::map<int, double> stamps;
1345 std::map<int, Transform> groundTruths;
1346 std::map<int, GPS> gpsValues;
1347 std::map<int, EnvSensors> sensorsValues;
1348 for(
int i=0;
i<
ids_.size();
i+=1+framesIgnored)
1360 if(frameRate == 0 ||
1361 previousStamp == 0 ||
1363 stamp - previousStamp >= 1.0/frameRate)
1365 if(sessionExported < 0 || sessionExported == mapId)
1367 ids.push_back(
ids_[
i]);
1369 if(previousStamp && stamp)
1371 delays[oi++] = stamp - previousStamp;
1373 previousStamp = stamp;
1375 poses.insert(std::make_pair(
ids_[
i], odomPose));
1376 stamps.insert(std::make_pair(
ids_[
i], stamp));
1377 groundTruths.insert(std::make_pair(
ids_[
i], groundTruth));
1378 if(gps.
stamp() > 0.0)
1380 gpsValues.insert(std::make_pair(
ids_[
i], gps));
1384 sensorsValues.insert(std::make_pair(
ids_[
i], sensors));
1388 if(sessionExported >= 0 && mapId > sessionExported)
1399 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1401 progressDialog->show();
1403 UINFO(
"Decompress: rgb=%d depth=%d scan=%d userData=%d",
1409 for(
int i=0;
i<ids.size() && !progressDialog->
isCanceled(); ++
i)
1415 cv::Mat depth, rgb, userData;
1417 data.uncompressDataConst(
1422 cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1);
1425 std::multimap<int, Link> links;
1427 if(links.size() && links.begin()->first <
id)
1429 covariance = links.begin()->second.infMatrix().inv();
1434 if(
data.cameraModels().size())
1440 data.cameraModels(),
1451 data.stereoCameraModels(),
1456 if(groundTruths.find(
id)!=groundTruths.end())
1460 if(gpsValues.find(
id)!=gpsValues.end())
1462 sensorData.
setGPS(gpsValues.at(
id));
1464 if(sensorsValues.find(
id)!=sensorsValues.end())
1473 QApplication::processEvents();
1478 progressDialog->
appendText(tr(
"Average frame rate=%1 Hz (Min=%2, Max=%3)")
1485 UERROR(
"DataRecorder init failed?!");
1490 QMessageBox::warning(
this, tr(
"Cannot export database"), tr(
"An output path must be set!"));
1502 QStringList formats;
1503 formats.push_back(
"id.jpg");
1504 formats.push_back(
"id.png");
1505 formats.push_back(
"timestamp.jpg");
1506 formats.push_back(
"timestamp.png");
1508 QString
format = QInputDialog::getItem(
this, tr(
"Which RGB format?"), tr(
"Format:"), formats, 0,
false, &ok);
1514 QString ext =
format.split(
'.').back();
1515 bool useStamp =
format.split(
'.').front().compare(
"timestamp") == 0;
1516 bool directoriesCreated =
false;
1517 QString
path = QFileDialog::getExistingDirectory(
this, tr(
"Select directory where to save images..."),
pathDatabase_);
1521 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1525 progressDialog->show();
1527 int imagesExported = 0;
1528 for(
int i=0;
i<
ids_.size(); ++
i)
1530 QString
id = QString::number(
ids_.at(
i));
1534 data.uncompressData();
1536 if(!directoriesCreated)
1539 if(!
data.imageRaw().empty() && !
data.rightRaw().empty())
1542 dir.mkdir(QString(
"%1/left").
arg(
path));
1543 dir.mkdir(QString(
"%1/right").
arg(
path));
1544 dir.mkdir(QString(
"%1/calib").
arg(
path));
1545 directoriesCreated =
true;
1547 else if(!
data.imageRaw().empty())
1549 if(!
data.depthRaw().empty())
1552 dir.mkdir(QString(
"%1/rgb").
arg(
path));
1553 dir.mkdir(QString(
"%1/depth").
arg(
path));
1554 dir.mkdir(QString(
"%1/calib").
arg(
path));
1555 directoriesCreated =
true;
1560 if(!
data.imageRaw().empty() && useStamp)
1566 std::vector<float>
v;
1572 UWARN(
"Node %d has null timestamp! Using id instead!",
ids_.at(
i));
1576 id = QString::number(stamp,
'f');
1580 if(!
data.imageRaw().empty())
1582 if(!
data.rightRaw().empty())
1584 if(!cv::imwrite(QString(
"%1/left/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1586 if(!cv::imwrite(QString(
"%1/right/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.rightRaw()))
1588 UINFO(QString(
"Saved left/%1.%2 and right/%1.%2").
arg(
id).
arg(ext).toStdString().
c_str());
1592 UERROR(
"Cannot save calibration file, database name is empty!");
1594 else if(
data.stereoCameraModels().size()>=1 &&
data.stereoCameraModels().front().isValidForProjection())
1596 for(
size_t i=0;
i<
data.stereoCameraModels().
size(); ++
i)
1598 std::string cameraName =
id.toStdString();
1599 if(
data.stereoCameraModels().size()>1)
1605 data.imageRaw().size(),
1606 data.stereoCameraModels()[
i].left().K_raw(),
1607 data.stereoCameraModels()[
i].left().D_raw(),
1608 data.stereoCameraModels()[
i].left().R(),
1609 data.stereoCameraModels()[
i].left().P(),
1610 data.rightRaw().size(),
1611 data.stereoCameraModels()[
i].right().K_raw(),
1612 data.stereoCameraModels()[
i].right().D_raw(),
1613 data.stereoCameraModels()[
i].right().R(),
1614 data.stereoCameraModels()[
i].right().P(),
1615 data.stereoCameraModels()[
i].R(),
1616 data.stereoCameraModels()[
i].T(),
1617 data.stereoCameraModels()[
i].E(),
1618 data.stereoCameraModels()[
i].F(),
1619 data.stereoCameraModels()[
i].left().localTransform());
1620 if(
model.save(
path.toStdString() +
"/calib"))
1622 UINFO(
"Saved stereo calibration \"%s\"", (
path.toStdString()+
"/calib/"+cameraName+
".yaml").c_str());
1626 UERROR(
"Failed saving calibration \"%s\"", (
path.toStdString()+
"/calib/"+cameraName+
".yaml").c_str());
1633 if(!
data.depthRaw().empty())
1635 if(!cv::imwrite(QString(
"%1/rgb/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1638 UWARN(
"Failed saving \"%s\"", QString(
"%1/depth/%2.png").
arg(
path).
arg(
id).toStdString().
c_str());
1639 UINFO(QString(
"Saved rgb/%1.%2 and depth/%1.png").
arg(
id).
arg(ext).toStdString().
c_str());
1643 if(!cv::imwrite(QString(
"%1/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1651 UERROR(
"Cannot save calibration file, database name is empty!");
1653 else if(
data.cameraModels().size() >= 1 &&
data.cameraModels().front().isValidForProjection())
1655 for(
size_t i=0;
i<
data.cameraModels().
size(); ++
i)
1657 std::string cameraName =
id.toStdString();
1658 if(
data.cameraModels().size()>1)
1663 data.imageRaw().size(),
1664 data.cameraModels()[
i].K_raw(),
1665 data.cameraModels()[
i].D_raw(),
1666 data.cameraModels()[
i].R(),
1667 data.cameraModels()[
i].P(),
1668 data.cameraModels()[
i].localTransform());
1669 std::string dirPrefix =
"";
1670 if(!
data.depthRaw().empty())
1672 dirPrefix =
"/calib";
1674 if(
model.save(
path.toStdString()+dirPrefix))
1676 UINFO(
"Saved calibration \"%s\"", (
path.toStdString()+dirPrefix+
"/"+cameraName+
".yaml").c_str());
1680 UERROR(
"Failed saving calibration \"%s\"", (
path.toStdString()+
"/"+cameraName+
".yaml").c_str());
1688 QApplication::processEvents();
1698 QMessageBox::information(
this, tr(
"Exporting"), tr(
"%1 images exported!").
arg(imagesExported));
1710 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1711 int progressSteps = 5;
1712 if(
ui_->graphViewer->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
1716 if(
ui_->textEdit_info->isVisible())
1720 if(
ui_->toolBox_statistics->isVisible())
1725 progressDialog->show();
1728 progressDialog->
appendText(tr(
"Loading all ids..."));
1729 QApplication::processEvents();
1731 QApplication::processEvents();
1733 UINFO(
"Loading all IDs...");
1736 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
1737 ids_ = QList<int>(ids.begin(), ids.end());
1739 ids_ = QList<int>::fromStdList(std::list<int>(ids.begin(), ids.end()));
1752 ui_->checkBox_wmState->setVisible(
false);
1753 ui_->checkBox_alignPosesWithGPS->setVisible(
false);
1754 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
1755 ui_->checkBox_alignPosesWithGroundTruth->setVisible(
false);
1756 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
1757 ui_->checkBox_alignScansCloudsWithGroundTruth->setVisible(
false);
1758 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
1759 ui_->doubleSpinBox_optimizationScale->setVisible(
false);
1760 ui_->label_scale_title->setVisible(
false);
1761 ui_->label_rmse->setVisible(
false);
1762 ui_->label_rmse_title->setVisible(
false);
1763 ui_->checkBox_ignoreIntermediateNodes->setVisible(
false);
1764 ui_->label_ignoreINtermediateNdoes->setVisible(
false);
1765 ui_->label_alignPosesWithGPS->setVisible(
false);
1766 ui_->label_alignPosesWithGroundTruth->setVisible(
false);
1767 ui_->label_alignScansCloudsWithGroundTruth->setVisible(
false);
1768 ui_->toolButton_edit_priorA->setVisible(
false);
1769 ui_->toolButton_edit_priorB->setVisible(
false);
1770 ui_->toolButton_remove_priorA->setVisible(
false);
1771 ui_->toolButton_remove_priorB->setVisible(
false);
1772 ui_->menuEdit->setEnabled(
true);
1773 ui_->actionGenerate_3D_map_pcd->setEnabled(
true);
1774 ui_->actionExport->setEnabled(
true);
1775 ui_->actionExtract_images->setEnabled(
true);
1776 ui_->menuExport_poses->setEnabled(
false);
1777 ui_->menuExport_GPS->setEnabled(
false);
1778 ui_->actionPoses_KML->setEnabled(
false);
1779 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
1780 ui_->actionExport_saved_2D_map->setEnabled(
false);
1781 ui_->actionImport_2D_map->setEnabled(
false);
1782 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
1783 ui_->actionView_optimized_mesh->setEnabled(
false);
1784 ui_->actionExport_optimized_mesh->setEnabled(
false);
1790 ui_->toolBox_statistics->clear();
1791 ui_->label_optimizeFrom->setText(tr(
"Root"));
1793 progressDialog->
appendText(tr(
"%1 ids loaded!").
arg(ids.size()));
1795 progressDialog->
appendText(tr(
"Loading all links..."));
1796 QApplication::processEvents();
1798 QApplication::processEvents();
1800 std::multimap<int, Link> unilinks;
1802 UDEBUG(
"%d total links loaded", (
int)unilinks.size());
1804 std::multimap<int, Link> links;
1805 for(std::multimap<int, Link>::iterator
iter=unilinks.begin();
iter!=unilinks.end(); ++
iter)
1807 links.insert(*
iter);
1810 links.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
1814 progressDialog->
appendText(tr(
"%1 links loaded!").
arg(unilinks.size()));
1816 progressDialog->
appendText(
"Loading Working Memory state...");
1817 QApplication::processEvents();
1819 QApplication::processEvents();
1825 double previousStamp = 0.0;
1829 progressDialog->
appendText(
"Loading Working Memory state... done!");
1831 progressDialog->
appendText(
"Loading info for all nodes...");
1832 QApplication::processEvents();
1834 QApplication::processEvents();
1836 int lastValidNodeId = 0;
1837 for(
int i=0;
i<
ids_.size(); ++
i)
1846 std::vector<float>
v;
1859 lastValidNodeId =
ids_[
i];
1863 if(wmStates.find(
ids_[
i]) != wmStates.end())
1866 ui_->checkBox_wmState->setVisible(
true);
1870 ui_->checkBox_ignoreIntermediateNodes->setVisible(
true);
1871 ui_->label_ignoreINtermediateNdoes->setVisible(
true);
1877 if(!
p.isNull() && !previousPose.
isNull())
1882 if(previousStamp > 0.0 &&
s > 0.0)
1896 for(std::multimap<int, Link>::iterator jter=links.find(
ids_[
i]); jter!=links.end() && jter->first ==
ids_[
i]; ++jter)
1903 std::multimap<int, Link>::iterator invertedLinkIter =
graph::findLink(links, jter->second.to(), jter->second.from(),
false, jter->second.type());
1904 if( jter->second.isValid() &&
1905 ids.find(jter->second.from()) != ids.end() &&
1906 (ids.find(jter->second.to()) != ids.end() || jter->second.to()<0) &&
1908 invertedLinkIter != links.end() &&
1912 if(jter->second.userDataCompressed().cols == 0 &&
1913 invertedLinkIter->second.userDataCompressed().cols != 0)
1915 links_.insert(std::make_pair(invertedLinkIter->second.from(), invertedLinkIter->second));
1919 links_.insert(std::make_pair(
ids_[
i], jter->second));
1929 if(gps.
stamp() > 0.0)
1933 cv::Point3f
p(0.0
f,0.0
f,0.0
f);
1945 progressDialog->
appendText(
"Loading info for all nodes... done!");
1947 progressDialog->
appendText(
"Loading optimized poses and maps...");
1948 QApplication::processEvents();
1950 QApplication::processEvents();
1957 ui_->checkBox_alignPosesWithGPS->setVisible(!
gpsPoses_.empty());
1958 ui_->checkBox_alignPosesWithGPS->setEnabled(!
gpsPoses_.empty());
1959 ui_->label_alignPosesWithGPS->setVisible(!
gpsPoses_.empty());
1969 ui_->menuExport_GPS->setEnabled(
true);
1970 ui_->actionPoses_KML->setEnabled(
true);
1973 float xMin, yMin, cellSize;
1975 ui_->actionEdit_optimized_2D_map->setEnabled(hasMap);
1976 ui_->actionExport_saved_2D_map->setEnabled(hasMap);
1977 ui_->actionImport_2D_map->setEnabled(hasMap);
1982 ui_->actionView_optimized_mesh->setEnabled(
true);
1983 ui_->actionExport_optimized_mesh->setEnabled(
true);
1988 progressDialog->
appendText(
"Loading optimized poses and maps... done!");
1990 QApplication::processEvents();
1992 QApplication::processEvents();
1994 if(
ids_.size() &&
ui_->toolBox_statistics->isVisible())
1996 progressDialog->
appendText(
"Loading statistics...");
1997 QApplication::processEvents();
1999 QApplication::processEvents();
2001 UINFO(
"Update statistics...");
2004 progressDialog->
appendText(
"Loading statistics... done!");
2006 QApplication::processEvents();
2008 QApplication::processEvents();
2012 ui_->textEdit_info->clear();
2013 if(
ui_->textEdit_info->isVisible())
2015 progressDialog->
appendText(
"Update database info...");
2016 QApplication::processEvents();
2018 QApplication::processEvents();
2022 progressDialog->
appendText(
"Update database info... done!");
2024 QApplication::processEvents();
2026 QApplication::processEvents();
2033 bool nullPoses =
odomPoses_.begin()->second.isNull();
2036 if((!
iter->second.isNull() && nullPoses) ||
2037 (
iter->second.isNull() && !nullPoses))
2039 if(
iter->second.isNull())
2043 UWARN(
"Mixed valid and null poses! Ignoring graph...");
2058 ui_->spinBox_optimizationsFrom->setValue(
odomPoses_.begin()->first);
2063 if(lastValidNodeId>0)
2068 std::map<int, rtabmap::Transform> posesOut;
2069 std::multimap<int, rtabmap::Link> linksOut;
2070 UINFO(
"Get connected graph from %d (%d poses, %d links)", lastValidNodeId, (
int)
odomPoses_.size(), (
int)
links_.size());
2078 if(!posesOut.empty())
2080 bool optimizeFromGraphEnd = Parameters::defaultRGBDOptimizeFromGraphEnd();
2082 if(optimizeFromGraphEnd)
2084 ui_->spinBox_optimizationsFrom->setValue(posesOut.rbegin()->first);
2088 ui_->spinBox_optimizationsFrom->setValue(posesOut.lower_bound(1)->first);
2103 if(!
iter->second.transform().isNull())
2110 else if(
iter->second.from()!=
iter->second.to())
2117 UERROR(
"Transform null for link from %d to %d",
iter->first,
iter->second.to());
2123 ui_->horizontalSlider_A->setMinimum(0);
2124 ui_->horizontalSlider_B->setMinimum(0);
2125 ui_->horizontalSlider_A->setMaximum(
ids_.size()-1);
2126 ui_->horizontalSlider_B->setMaximum(
ids_.size()-1);
2127 ui_->horizontalSlider_A->setEnabled(
true);
2128 ui_->horizontalSlider_B->setEnabled(
true);
2129 ui_->horizontalSlider_A->setSliderPosition(0);
2130 ui_->horizontalSlider_B->setSliderPosition(0);
2136 ui_->horizontalSlider_A->setEnabled(
false);
2137 ui_->horizontalSlider_B->setEnabled(
false);
2138 ui_->label_idA->setText(
"NaN");
2139 ui_->label_idB->setText(
"NaN");
2144 ui_->horizontalSlider_neighbors->setMinimum(0);
2146 ui_->horizontalSlider_neighbors->setEnabled(
true);
2147 ui_->horizontalSlider_neighbors->setSliderPosition(0);
2151 ui_->horizontalSlider_neighbors->setEnabled(
false);
2157 if(
ui_->graphViewer->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
2159 progressDialog->
appendText(
"Updating Graph View...");
2160 QApplication::processEvents();
2162 QApplication::processEvents();
2166 progressDialog->
appendText(
"Updating Graph View... done!");
2168 QApplication::processEvents();
2170 QApplication::processEvents();
2178 UINFO(
"Update database info...");
2181 if(
ui_->textEdit_info->toPlainText().isEmpty())
2188 ui_->textEdit_info->append(tr(
"Total odometry length:\t%1 m (approx. as graph has been reduced)").
arg(
infoTotalOdom_));
2195 int lastWordIdId = 0;
2202 ids.insert(lastWordIdId);
2203 std::list<VisualWord *> vws;
2207 wordsDim = vws.front()->getDescriptor().cols;
2208 wordsType = vws.front()->getDescriptor().type();
2214 ui_->textEdit_info->append(tr(
"Total time:\t\t%1").
arg(QDateTime::fromMSecsSinceEpoch(
infoTotalTime_*1000).toUTC().toString(
"hh:mm:ss.zzz")));
2220 ui_->textEdit_info->append(
"");
2224 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"));
2227 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"));
2230 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"));
2233 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"));
2236 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"));
2239 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"));
2242 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"));
2245 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"));
2248 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"));
2251 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"));
2254 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"));
2257 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"));
2258 mem = dbSize - total;
2259 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"));
2260 ui_->textEdit_info->append(
"");
2261 std::set<int> idsWithoutBad;
2263 int infoBadcountInLTM = 0;
2264 int infoBadCountInGraph = 0;
2265 for(
int i=0;
i<
ids_.size(); ++
i)
2267 if(idsWithoutBad.find(
ids_[
i]) == idsWithoutBad.end())
2269 ++infoBadcountInLTM;
2272 ++infoBadCountInGraph;
2276 ui_->textEdit_info->append(tr(
"%1 bad signatures in LTM").
arg(infoBadcountInLTM));
2277 ui_->textEdit_info->append(tr(
"%1 bad signatures in the global graph").
arg(infoBadCountInGraph));
2278 ui_->textEdit_info->append(
"");
2280 QFontMetrics metrics(
ui_->textEdit_info->font());
2281 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2282 int tabW =
ui_->textEdit_info->tabStopDistance();
2284 int tabW =
ui_->textEdit_info->tabStopWidth();
2286 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end(); ++
iter)
2288 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2289 int strW = metrics.horizontalAdvance(QString(
iter->first.c_str()) +
"=");
2291 int strW = metrics.width(QString(
iter->first.c_str()) +
"=");
2293 ui_->textEdit_info->append(tr(
"%1=%2%3")
2295 .
arg(strW < tabW?
"\t\t\t\t":strW < tabW*2?
"\t\t\t":strW < tabW*3?
"\t\t":
"\t")
2296 .arg(
iter->second.c_str()));
2300 ui_->textEdit_info->moveCursor(QTextCursor::Start) ;
2301 ui_->textEdit_info->ensureCursorVisible() ;
2306 ui_->textEdit_info->clear();
2315 ui_->toolBox_statistics->clear();
2316 double firstStamp = 0.0;
2319 std::map<std::string, std::pair<std::vector<qreal>, std::vector<qreal> > > allData;
2320 std::map<std::string, int > allDataOi;
2322 for(std::map<
int, std::pair<std::map<std::string, float>,
double> >::
iterator jter=allStats.begin(); jter!=allStats.end(); ++jter)
2324 double stamp=jter->second.second;
2325 std::map<std::string, float> & statistics = jter->second.first;
2330 for(std::map<std::string, float>::iterator
iter=statistics.begin();
iter!=statistics.end(); ++
iter)
2332 if(allData.find(
iter->first) == allData.end())
2335 allData.insert(std::make_pair(
iter->first, std::make_pair(std::vector<qreal>(allStats.size(), 0.0f), std::vector<qreal>(allStats.size(), 0.0f) )));
2336 allDataOi.insert(std::make_pair(
iter->first, 0));
2339 int & oi = allDataOi.at(
iter->first);
2340 allData.at(
iter->first).first[oi] =
ui_->checkBox_timeStats->isChecked()?qreal(stamp-firstStamp):jter->first;
2341 allData.at(
iter->first).second[oi] =
iter->second;
2346 for(std::map<std::string, std::pair<std::vector<qreal>, std::vector<qreal> > >::
iterator iter=allData.begin();
iter!=allData.end(); ++
iter)
2348 int oi = allDataOi.at(
iter->first);
2349 iter->second.first.resize(oi);
2350 iter->second.second.resize(oi);
2351 ui_->toolBox_statistics->updateStat(
iter->first.c_str(),
iter->second.first,
iter->second.second,
true);
2359 QColor
c = QColorDialog::getColor(
ui_->lineEdit_obstacleColor->text(),
this);
2362 ui_->lineEdit_obstacleColor->setText(
c.name());
2367 QColor
c = QColorDialog::getColor(
ui_->lineEdit_groundColor->text(),
this);
2370 ui_->lineEdit_groundColor->setText(
c.name());
2375 QColor
c = QColorDialog::getColor(
ui_->lineEdit_emptyColor->text(),
this);
2378 ui_->lineEdit_emptyColor->setText(
c.name());
2383 QColor
c = QColorDialog::getColor(
ui_->lineEdit_frontierColor->text(),
this);
2386 ui_->lineEdit_frontierColor->setText(
c.name());
2400 data.uncompressData();
2401 if(!
data.depthRaw().empty())
2454 types.push_back(
"Map's graph (see Graph View)");
2455 types.push_back(
"Odometry");
2458 types.push_back(
"Ground Truth");
2461 QString
type = QInputDialog::getItem(
this, tr(
"Which poses?"), tr(
"Poses:"), types, 0,
false, &ok);
2467 bool groundTruth =
type.compare(
"Ground Truth") == 0;
2471 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No ground truth poses in database?!"));
2477 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
2479 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No graph in database?!"));
2485 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No odometry poses in database?!"));
2493 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No GPS in database?!"));
2497 std::map<int, rtabmap::Transform>
graph;
2513 pcl::PointCloud<pcl::PointXYZ> cloud1, cloud2;
2514 cloud1.resize(
graph.size());
2515 cloud2.resize(
graph.size());
2520 std::map<int, Transform>::iterator iter2 =
graph.find(
iter->first);
2521 if(iter2!=
graph.end())
2525 idFirst =
iter->first;
2527 cloud1[oi] = pcl::PointXYZ(
iter->second.x(),
iter->second.y(),
iter->second.z());
2528 cloud2[oi++] = pcl::PointXYZ(iter2->second.x(), iter2->second.y(), iter2->second.z());
2545 std::map<int, GPS>
values;
2553 double bearing = -(
iter->second.theta()*180.0/
M_PI-90.0);
2564 std::vector<float>
v;
2571 QString output =
pathDatabase_ + QDir::separator() +
"poses.kml";
2572 QString
path = QFileDialog::getSaveFileName(
2576 tr(
"Google Earth file (*.kml)"));
2584 QMessageBox::information(
this,
2585 tr(
"Export poses..."),
2586 tr(
"GPS coordinates saved to \"%1\".")
2591 QMessageBox::information(
this,
2592 tr(
"Export poses..."),
2593 tr(
"Failed to save GPS coordinates to \"%1\"!")
2601 std::map<int, Transform> optimizedPoses;
2617 if((
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked()) ||
2618 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked()))
2621 if(
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
2622 ui_->checkBox_alignPosesWithGPS->isChecked())
2630 float translational_rmse = 0.0f;
2631 float translational_mean = 0.0f;
2632 float translational_median = 0.0f;
2633 float translational_std = 0.0f;
2634 float translational_min = 0.0f;
2635 float translational_max = 0.0f;
2636 float rotational_rmse = 0.0f;
2637 float rotational_mean = 0.0f;
2638 float rotational_median = 0.0f;
2639 float rotational_std = 0.0f;
2640 float rotational_min = 0.0f;
2641 float rotational_max = 0.0f;
2648 translational_median,
2661 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2663 iter->second = gtToMap *
iter->second;
2670 if(optimizedPoses.size())
2672 std::map<int, Transform> localTransforms;
2674 items.push_back(
"Robot (base frame)");
2675 items.push_back(
"Camera");
2676 items.push_back(
"Scan");
2678 QString item = QInputDialog::getItem(
this, tr(
"Export Poses"), tr(
"Frame: "), items, 0,
false, &ok);
2679 if(!ok || item.isEmpty())
2683 if(item.compare(
"Robot (base frame)") != 0)
2685 bool cameraFrame = item.compare(
"Camera") == 0;
2686 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2691 std::vector<CameraModel> models;
2692 std::vector<StereoCameraModel> stereoModels;
2695 if((models.size() == 1 &&
2696 !models.at(0).localTransform().isNull()))
2698 localTransform = models.at(0).localTransform();
2700 else if(stereoModels.size() == 1 &&
2701 !stereoModels[0].localTransform().isNull())
2703 localTransform = stereoModels[0].localTransform();
2705 else if(models.size()>1)
2707 UWARN(
"Multi-camera is not supported (node %d)",
iter->first);
2711 UWARN(
"Calibration not valid for node %d",
iter->first);
2716 UWARN(
"Missing calibration for node %d",
iter->first);
2724 localTransform =
info.localTransform();
2728 UWARN(
"Missing scan info for node %d",
iter->first);
2732 if(!localTransform.
isNull())
2734 localTransforms.insert(std::make_pair(
iter->first, localTransform));
2737 if(localTransforms.empty())
2739 QMessageBox::warning(
this,
2741 tr(
"Could not find any \"%1\" frame, exporting in Robot frame instead.").
arg(item));
2745 std::map<int, Transform> poses;
2746 std::multimap<int, Link> links;
2747 if(localTransforms.empty())
2749 poses = optimizedPoses;
2758 for(std::map<int, Transform>::iterator
iter=localTransforms.begin();
iter!=localTransforms.end(); ++
iter)
2760 poses.insert(std::make_pair(
iter->first, optimizedPoses.at(
iter->first) *
iter->second));
2768 std::multimap<int, Link>::iterator inserted = links.insert(*
iter);
2769 int from =
iter->second.from();
2770 int to =
iter->second.to();
2771 inserted->second.setTransform(localTransforms.at(from).inverse()*
iter->second.transform()*localTransforms.at(to));
2777 if(
format != 4 &&
format != 11 && !poses.empty() && poses.begin()->first<0)
2779 UWARN(
"Only g2o format (4) and RGBD format with ID format can export landmarks, they are ignored with format %d",
format);
2780 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end() &&
iter->first < 0;)
2782 poses.erase(
iter++);
2784 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end();)
2786 if(
iter->second.from() < 0 ||
iter->second.to() < 0)
2788 links.erase(
iter++);
2797 std::map<int, double> stamps;
2800 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
2804 stamps.insert(std::make_pair(
iter->first, 0));
2813 std::vector<float>
v;
2818 stamps.insert(std::make_pair(
iter->first, stamp));
2822 if(stamps.size()!=poses.size())
2824 QMessageBox::warning(
this, tr(
"Export poses..."), tr(
"Poses (%1) and stamps (%2) have not the same size! Cannot export in RGB-D SLAM format.")
2825 .
arg(poses.size()).
arg(stamps.size()));
2831 QString suffix =
odometry?
"_odom":groundTruth?
"_gt":
"";
2832 output = output.arg(suffix);
2834 QString
path = QFileDialog::getSaveFileName(
2838 format == 3?tr(
"TORO file (*.graph)"):
format==4?tr(
"g2o file (*.g2o)"):tr(
"Text file (*.txt)"));
2842 if(QFileInfo(
path).suffix() ==
"")
2862 QMessageBox::information(
this,
2863 tr(
"Export poses..."),
2864 tr(
"%1 saved to \"%2\".")
2870 QMessageBox::information(
this,
2871 tr(
"Export poses..."),
2872 tr(
"Failed to save %1 to \"%2\"!")
2894 QString
path = QFileDialog::getSaveFileName(
2898 format==0?tr(
"Raw format (*.txt)"):tr(
"Google Earth file (*.kml)"));
2906 QMessageBox::information(
this,
2907 tr(
"Export poses..."),
2908 tr(
"GPS coordinates saved to \"%1\".")
2913 QMessageBox::information(
this,
2914 tr(
"Export poses..."),
2915 tr(
"Failed to save GPS coordinates to \"%1\"!")
2926 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
2932 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
2933 tr(
"The database has too old version (%1) to saved "
2940 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
2941 tr(
"The database has modified links and/or modified local "
2942 "occupancy grids, the 2D optimized map cannot be modified."));
2946 float xMin, yMin, cellSize;
2950 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"The database doesn't contain a saved 2D map."));
2955 cv::Mat map8UFlip, map8URotated;
2956 cv::flip(map8U, map8UFlip, 0);
2957 if(!
ui_->graphViewer->isOrientationENU())
2960 cv::transpose(map8UFlip, map8URotated);
2961 cv::flip(map8URotated, map8URotated, 0);
2965 map8URotated = map8UFlip;
2974 if(!
ui_->graphViewer->isOrientationENU())
2977 cv::transpose(mapModified, map8URotated);
2978 cv::flip(map8URotated, map8URotated, 1);
2982 map8URotated = mapModified;
2984 cv::flip(map8URotated, map8UFlip, 0);
2986 UASSERT(map8UFlip.type() == map8U.type());
2987 UASSERT(map8UFlip.cols == map8U.cols);
2988 UASSERT(map8UFlip.rows == map8U.rows);
2995 QMessageBox::information(
this, tr(
"Edit 2D map"), tr(
"Map updated!"));
2998 int cropRadius =
ui_->spinBox_cropRadius->value();
2999 QMessageBox::StandardButton
b = QMessageBox::question(
this,
3000 tr(
"Crop empty space"),
3001 tr(
"Do you want to clear empty space from local occupancy grids and laser scans?\n\n"
3003 " * 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"
3004 " * 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"
3006 " * Cropping the laser scans cannot be reverted after the viewer is closed and changes have been saved.\n\n"
3008 " Crop radius = %1 pixels\n\n"
3009 "Press \"Yes\" to filter only grids.\n"
3010 "Press \"Yes to All\" to filter both grids and laser scans.\n").
arg(cropRadius),
3011 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No, QMessageBox::No);
3012 if(
b == QMessageBox::Yes ||
b == QMessageBox::YesToAll)
3020 progressDialog.show();
3022 progressDialog.
appendText(QString(
"Cropping empty space... %1 scans to filter").
arg(poses.size()));
3023 progressDialog.setMinimumWidth(800);
3024 QApplication::processEvents();
3026 UINFO(
"Cropping empty space... poses=%d cropRadius=%d", poses.size(), cropRadius);
3028 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end() && !progressDialog.
isCanceled(); ++
iter)
3032 cv::Mat gridObstacles;
3039 data.uncompressData(0,0,&scan,0,&gridGround,&gridObstacles,&gridEmpty);
3045 if(!gridObstacles.empty())
3047 cv::Mat filtered = cv::Mat(1, gridObstacles.cols, gridObstacles.type());
3049 for(
int i=0;
i<gridObstacles.cols; ++
i)
3051 const float * ptr = gridObstacles.ptr<
float>(0,
i);
3052 cv::Point3f pt(ptr[0], ptr[1], gridObstacles.channels()==2?0:ptr[2]);
3055 int x =
int((pt.x - xMin) / cellSize + 0.5f);
3056 int y =
int((pt.y - yMin) / cellSize + 0.5f);
3058 if(
x>=0 &&
x<map8S.cols &&
3059 y>=0 &&
y<map8S.rows)
3061 bool obstacleDetected =
false;
3063 for(
int j=-cropRadius;
j<=cropRadius && !obstacleDetected; ++
j)
3065 for(
int k=-cropRadius; k<=cropRadius && !obstacleDetected; ++k)
3067 if(
x+
j>=0 &&
x+
j<map8S.cols &&
3068 y+k>=0 &&
y+k<map8S.rows &&
3069 map8S.at<
unsigned char>(
y+k,
x+
j) == 100)
3071 obstacleDetected =
true;
3076 if(map8S.at<
unsigned char>(
y,
x) != 0 || obstacleDetected)
3085 if(oi != gridObstacles.cols)
3087 progressDialog.
appendText(QString(
"Grid %1 filtered %2 pts -> %3 pts").
arg(
iter->first).
arg(gridObstacles.cols).arg(oi));
3088 UINFO(
"Grid %d filtered %d -> %d",
iter->first, gridObstacles.cols, oi);
3091 cv::Mat newObstacles = cv::Mat(filtered,
cv::Range::all(), cv::Range(0, oi));
3095 value.obstacleCells = newObstacles;
3107 if(
b == QMessageBox::YesToAll && !scan.
isEmpty())
3111 cv::Mat filtered = cv::Mat(1, scan.
size(), scan.
dataType());
3113 for(
int i=0;
i<scan.
size(); ++
i)
3115 const float * ptr = scan.
data().ptr<
float>(0,
i);
3116 cv::Point3f pt(ptr[0], ptr[1], scan.
is2d()?0:ptr[2]);
3119 int x =
int((pt.x - xMin) / cellSize + 0.5f);
3120 int y =
int((pt.y - yMin) / cellSize + 0.5f);
3122 if(
x>=0 &&
x<map8S.cols &&
3123 y>=0 &&
y<map8S.rows)
3125 bool obstacleDetected =
false;
3127 for(
int j=-cropRadius;
j<=cropRadius && !obstacleDetected; ++
j)
3129 for(
int k=-cropRadius; k<=cropRadius && !obstacleDetected; ++k)
3131 if(
x+
j>=0 &&
x+
j<map8S.cols &&
3132 y+k>=0 &&
y+k<map8S.rows &&
3133 map8S.at<
unsigned char>(
y+k,
x+
j) == 100)
3135 obstacleDetected =
true;
3140 if(map8S.at<
unsigned char>(
y,
x) != 0 || obstacleDetected)
3149 if(oi != scan.
size())
3152 UINFO(
"Scan %d filtered %d -> %d",
iter->first, scan.
size(), oi);
3182 QApplication::processEvents();
3202 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3206 float xMin, yMin, cellSize;
3210 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"The database doesn't contain a saved 2D map."));
3216 QString
path = QFileDialog::getSaveFileName(
3224 if(QFileInfo(
path).suffix() ==
"")
3228 cv::imwrite(
path.toStdString(), map8U);
3231 QString yaml =
info.absolutePath() +
"/" +
info.baseName() +
".yaml";
3233 float occupancyThr = Parameters::defaultGridGlobalOccupancyThr();
3234 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridGlobalOccupancyThr(), occupancyThr);
3237 file.open (yaml.toStdString());
3238 file <<
"image: " <<
info.baseName().toStdString() <<
".pgm" << std::endl;
3239 file <<
"resolution: " << cellSize << std::endl;
3240 file <<
"origin: [" << xMin <<
", " << yMin <<
", 0.0]" << std::endl;
3241 file <<
"negate: 0" << std::endl;
3242 file <<
"occupied_thresh: " << occupancyThr << std::endl;
3243 file <<
"free_thresh: 0.196" << std::endl;
3248 QMessageBox::information(
this, tr(
"Export 2D map"), tr(
"Exported %1 and %2!").
arg(
path).
arg(yaml));
3257 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3263 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
3264 tr(
"The database has too old version (%1) to saved "
3271 QMessageBox::warning(
this, tr(
"Cannot import 2D map"),
3272 tr(
"The database has modified links and/or modified local "
3273 "occupancy grids, the 2D optimized map cannot be modified."));
3277 float xMin, yMin, cellSize;
3281 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"The database doesn't contain a saved 2D map."));
3285 QString
path = QFileDialog::getOpenFileName(
3292 cv::Mat map8U = cv::imread(
path.toStdString(), cv::IMREAD_UNCHANGED);
3295 if(mapOrg.cols == map.cols && mapOrg.rows == map8U.rows)
3298 QMessageBox::information(
this, tr(
"Import 2D map"), tr(
"Imported %1!").
arg(
path));
3302 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));
3312 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3318 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
3319 tr(
"The database has too old version (%1) to saved "
3326 QMessageBox::warning(
this, tr(
"Cannot import 2D map"),
3327 tr(
"The database has modified links and/or modified local "
3328 "occupancy grids, the 2D optimized map cannot be modified. Try "
3329 "closing the database and re-open it to save the changes."));
3335 QMessageBox::warning(
this, tr(
"Cannot regenerate 2D map"),
3336 tr(
"Graph is empty, make sure you opened the "
3337 "Graph View and there is a map shown."));
3342 #ifdef RTABMAP_OCTOMAP
3344 types.push_back(
"Default occupancy grid");
3345 types.push_back(
"From OctoMap projection");
3347 QString
type = QInputDialog::getItem(
this, tr(
"Which type?"), tr(
"Type:"), types, 0,
false, &ok);
3355 UINFO(
"Update local maps list...");
3358 float gridCellSize = Parameters::defaultGridCellSize();
3359 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridCellSize(), gridCellSize);
3360 #ifdef RTABMAP_OCTOMAP
3361 if(
type.compare(
"From OctoMap projection") == 0)
3374 map = grid.
getMap(xMin, yMin);
3379 QMessageBox::information(
this, tr(
"Regenerate 2D map"), tr(
"Failed to renegerate the map, resulting map is empty!"));
3389 lastlocalizationPose =
graphes_.back().rbegin()->second;
3394 QMessageBox::information(
this, tr(
"Regenerate 2D map"), tr(
"Map regenerated!"));
3395 ui_->actionEdit_optimized_2D_map->setEnabled(
true);
3396 ui_->actionExport_saved_2D_map->setEnabled(
true);
3397 ui_->actionImport_2D_map->setEnabled(
true);
3405 QMessageBox::warning(
this, tr(
"Cannot view optimized mesh"), tr(
"A database must must loaded first...\nUse File->Open database."));
3409 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
3410 #if PCL_VERSION_COMPARE(>=, 1, 8, 0)
3411 std::vector<std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > > texCoords;
3413 std::vector<std::vector<Eigen::Vector2f> > texCoords;
3417 if(cloudMat.empty())
3419 QMessageBox::warning(
this, tr(
"Cannot view optimized mesh"), tr(
"The database doesn't contain a saved optimized mesh."));
3424 viewer->setWindowFlags(Qt::Window);
3425 viewer->setAttribute(Qt::WA_DeleteOnClose);
3427 if(!textures.empty())
3431 viewer->setWindowTitle(
"Optimized Textured Mesh");
3435 else if(polygons.size() == 1)
3438 viewer->setWindowTitle(
"Optimized Mesh");
3446 viewer->setWindowTitle(
"Optimized Point Cloud");
3457 QMessageBox::warning(
this, tr(
"Cannot export optimized mesh"), tr(
"A database must must loaded first...\nUse File->Open database."));
3461 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
3462 #if PCL_VERSION_COMPARE(>=, 1, 8, 0)
3463 std::vector<std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > > texCoords;
3465 std::vector<std::vector<Eigen::Vector2f> > texCoords;
3469 if(cloudMat.empty())
3471 QMessageBox::warning(
this, tr(
"Cannot export optimized mesh"), tr(
"The database doesn't contain a saved optimized mesh."));
3477 if(!textures.empty())
3480 QString
path = QFileDialog::getSaveFileName(
3484 tr(
"Mesh (*.obj)"));
3488 if(QFileInfo(
path).suffix() ==
"")
3492 QString baseName = QFileInfo(
path).baseName();
3493 if(mesh->tex_materials.size() == 1)
3495 mesh->tex_materials.at(0).tex_file = baseName.toStdString() +
".png";
3496 cv::imwrite((QFileInfo(
path).absoluteDir().absolutePath()+QDir::separator()+baseName).toStdString() +
".png", textures);
3500 for(
unsigned int i=0;
i<mesh->tex_materials.size(); ++
i)
3502 mesh->tex_materials.at(
i).tex_file = (baseName+QDir::separator()+QString::number(
i)+
".png").toStdString();
3503 UASSERT((
i+1)*textures.rows <= (
unsigned int)textures.cols);
3504 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)));
3507 pcl::io::saveOBJFile(
path.toStdString(), *mesh);
3509 QMessageBox::information(
this, tr(
"Export Textured Mesh"), tr(
"Exported %1!").
arg(
path));
3512 else if(polygons.size() == 1)
3515 QString
path = QFileDialog::getSaveFileName(
3519 tr(
"Mesh (*.ply)"));
3523 if(QFileInfo(
path).suffix() ==
"")
3527 pcl::io::savePLYFileBinary(
path.toStdString(), *mesh);
3528 QMessageBox::information(
this, tr(
"Export Mesh"), tr(
"Exported %1!").
arg(
path));
3533 QString
path = QFileDialog::getSaveFileName(
3537 tr(
"Point cloud data (*.ply *.pcd)"));
3541 if(QFileInfo(
path).suffix() ==
"")
3545 bool success =
false;
3547 if(QFileInfo(
path).suffix() ==
"pcd")
3549 success = pcl::io::savePCDFile(
path.toStdString(), *cloud) == 0;
3553 success = pcl::io::savePLYFile(
path.toStdString(), *cloud) == 0;
3557 QMessageBox::information(
this, tr(
"Export Point Cloud"), tr(
"Exported %1!").
arg(
path));
3561 QMessageBox::critical(
this, tr(
"Export Point Cloud"), tr(
"Failed exporting %1!").
arg(
path));
3572 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
3579 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
3581 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
3586 std::map<int, Transform> optimizedPoses;
3587 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
3588 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked()
3597 if(
ui_->groupBox_posefiltering->isChecked())
3600 ui_->doubleSpinBox_posefilteringRadius->value(),
3601 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
3607 if(optimizedPoses.size() > 0)
3613 std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
3614 std::map<int, pcl::PolygonMesh::Ptr> meshes;
3615 std::map<int, pcl::TextureMesh::Ptr> textureMeshes;
3616 std::vector<std::map<int, pcl::PointXY> > textureVertexToPixels;
3622 QMap<int, Signature>(),
3623 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
3624 std::map<int, LaserScan>(),
3626 ui_->parameters_toolbox->getParameters(),
3630 textureVertexToPixels))
3632 if(textureMeshes.size())
3636 cv::Mat globalTextures;
3637 pcl::TextureMeshPtr textureMesh = textureMeshes.at(0);
3638 if(textureMesh->tex_materials.size()>1)
3642 std::map<int, cv::Mat>(),
3643 std::map<
int, std::vector<CameraModel> >(),
3648 textureVertexToPixels,
3661 textureMesh->tex_coordinates,
3663 QMessageBox::information(
this, tr(
"Update Optimized Textured Mesh"), tr(
"Updated!"));
3664 ui_->actionView_optimized_mesh->setEnabled(
true);
3665 ui_->actionExport_optimized_mesh->setEnabled(
true);
3668 else if(meshes.size())
3671 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons(1);
3674 QMessageBox::information(
this, tr(
"Update Optimized Mesh"), tr(
"Updated!"));
3675 ui_->actionView_optimized_mesh->setEnabled(
true);
3676 ui_->actionExport_optimized_mesh->setEnabled(
true);
3679 else if(clouds.size())
3683 QMessageBox::information(
this, tr(
"Update Optimized PointCloud"), tr(
"Updated!"));
3684 ui_->actionView_optimized_mesh->setEnabled(
true);
3685 ui_->actionExport_optimized_mesh->setEnabled(
true);
3690 QMessageBox::critical(
this, tr(
"Update Optimized Mesh"), tr(
"Nothing to save!"));
3697 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
3705 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"A database must must loaded first...\nUse File->Open database."));
3709 QString
path = QFileDialog::getSaveFileName(
this, tr(
"Save File"),
pathDatabase_+
"/Graph.dot", tr(
"Graphiz file (*.dot)"));
3720 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
3724 int id = QInputDialog::getInt(
this, tr(
"Around which location?"), tr(
"Location ID"),
ids_.first(),
ids_.first(),
ids_.last(), 1, &ok);
3728 int margin = QInputDialog::getInt(
this, tr(
"Depth around the location?"), tr(
"Margin"), 4, 1, 100, 1, &ok);
3731 QString
path = QFileDialog::getSaveFileName(
this, tr(
"Save File"),
pathDatabase_+
"/Graph" + QString::number(
id) +
".dot", tr(
"Graphiz file (*.dot)"));
3732 if(!
path.isEmpty() &&
id>0)
3734 std::map<int, int> ids;
3735 std::list<int> curentMarginList;
3736 std::set<int> currentMargin;
3737 std::set<int> nextMargin;
3738 nextMargin.insert(
id);
3740 while((margin == 0 ||
m < margin) && nextMargin.size())
3742 curentMarginList = std::list<int>(nextMargin.rbegin(), nextMargin.rend());
3745 for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end(); ++jter)
3747 if(ids.find(*jter) == ids.end())
3749 std::multimap<int, Link> links;
3750 ids.insert(std::pair<int, int>(*jter,
m));
3756 for(std::multimap<int, Link>::const_iterator
iter=links.begin();
iter!=links.end(); ++
iter)
3764 nextMargin.insert(
iter->first);
3769 if(currentMargin.insert(
iter->first).second)
3771 curentMarginList.push_back(
iter->first);
3783 ids.insert(std::pair<int,int>(
id, 0));
3784 std::set<int> idsSet;
3785 for(std::map<int, int>::iterator
iter = ids.begin();
iter!=ids.end(); ++
iter)
3787 idsSet.insert(idsSet.end(),
iter->first);
3790 UINFO(
"idsSet=%d", idsSet.size());
3795 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for signature %1.").
arg(
id));
3810 progressDialog.show();
3814 plot->setWindowFlags(Qt::Window);
3815 plot->setWindowTitle(
"Local Occupancy Grid Generation Time (ms)");
3816 plot->setAttribute(Qt::WA_DeleteOnClose);
3822 plotCells->setWindowFlags(Qt::Window);
3823 plotCells->setWindowTitle(
"Occupancy Cells");
3824 plotCells->setAttribute(Qt::WA_DeleteOnClose);
3831 double decompressionTime = 0;
3832 double gridCreationTime = 0;
3839 data.uncompressData();
3840 decompressionTime =
timer.ticks()*1000.0;
3853 s.setPose(odomPose);
3854 cv::Mat ground, obstacles,
empty;
3855 cv::Point3f viewpoint;
3858 if(
ui_->checkBox_grid_regenerateFromSavedGrid->isChecked() &&
s.sensorData().gridCellSize() > 0.0f)
3866 if(
s.sensorData().cameraModels().size())
3870 for(
unsigned int i=0;
i<
s.sensorData().cameraModels().
size(); ++
i)
3872 const Transform &
t =
s.sensorData().cameraModels()[
i].localTransform();
3875 viewpoint.x +=
t.
x();
3876 viewpoint.y +=
t.
y();
3877 viewpoint.z +=
t.z();
3888 else if(
s.sensorData().cameraModels().size())
3892 for(
unsigned int i=0;
i<
s.sensorData().stereoCameraModels().
size(); ++
i)
3894 const Transform &
t =
s.sensorData().stereoCameraModels()[
i].localTransform();
3897 viewpoint.x +=
t.
x();
3898 viewpoint.y +=
t.
y();
3899 viewpoint.z +=
t.z();
3919 gridCreationTime =
timer.ticks()*1000.0;
3921 msg = QString(
"Generated local occupancy grid map %1/%2").arg(
i+1).arg((
int)
ids_.size());
3932 decompressionCurve->
addValue(
ids_.at(
i), decompressionTime);
3935 if(
ids_.size() < 50 || (
i+1) % 25 == 0)
3937 QApplication::processEvents();
3958 if(
ids_.size() == 0)
3960 UWARN(
"ids_ is empty!");
3965 idsSet.insert(
ids_.at(
ui_->horizontalSlider_A->value()));
3966 idsSet.insert(
ids_.at(
ui_->horizontalSlider_B->value()));
3967 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
3968 QList<int> ids(idsSet.begin(), idsSet.end());
3970 QList<int> ids = idsSet.toList();
3975 progressDialog.show();
3977 for(
int i =0;
i<ids.size(); ++
i)
3981 data.uncompressData();
3994 s.setPose(odomPose);
3995 cv::Mat ground, obstacles,
empty;
3996 cv::Point3f viewpoint;
3998 if(
ui_->checkBox_grid_regenerateFromSavedGrid->isChecked() &&
s.sensorData().gridCellSize() > 0.0f)
4006 if(
s.sensorData().cameraModels().size())
4010 for(
unsigned int i=0;
i<
s.sensorData().cameraModels().
size(); ++
i)
4012 const Transform &
t =
s.sensorData().cameraModels()[
i].localTransform();
4015 viewpoint.x +=
t.
x();
4016 viewpoint.y +=
t.
y();
4017 viewpoint.z +=
t.z();
4028 else if(
s.sensorData().stereoCameraModels().size())
4032 for(
unsigned int i=0;
i<
s.sensorData().stereoCameraModels().
size(); ++
i)
4034 const Transform &
t =
s.sensorData().stereoCameraModels()[
i].localTransform();
4037 viewpoint.x +=
t.
x();
4038 viewpoint.y +=
t.
y();
4039 viewpoint.z +=
t.z();
4060 msg = QString(
"Generated local occupancy grid map %1/%2 (%3s)").arg(
i+1).arg((
int)ids.size()).arg(
time.ticks());
4065 QApplication::processEvents();
4084 QMessageBox::warning(
this, tr(
"Cannot view 3D map"), tr(
"The database is empty..."));
4091 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4093 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4098 std::map<int, Transform> optimizedPoses;
4099 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
4100 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
4109 if(
ui_->groupBox_posefiltering->isChecked())
4112 ui_->doubleSpinBox_posefilteringRadius->value(),
4113 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
4115 if(optimizedPoses.size() > 0)
4121 QMap<int, Signature>(),
4122 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
4123 std::map<int, LaserScan>(),
4125 ui_->parameters_toolbox->getParameters());
4129 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
4137 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
4144 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4146 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4151 std::map<int, Transform> optimizedPoses;
4152 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
4153 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
4162 if(
ui_->groupBox_posefiltering->isChecked())
4165 ui_->doubleSpinBox_posefilteringRadius->value(),
4166 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
4168 if(optimizedPoses.size() > 0)
4174 QMap<int, Signature>(),
4175 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
4176 std::map<int, LaserScan>(),
4178 ui_->parameters_toolbox->getParameters());
4182 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
4191 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4193 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4198 std::map<int, Transform> optimizedPoses =
graphes_.back();
4201 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4204 progressDialog->setMinimumWidth(800);
4205 progressDialog->show();
4207 const ParametersMap & parameters =
ui_->parameters_toolbox->getParameters();
4208 bool loopCovLimited = Parameters::defaultRGBDLoopCovLimited();
4209 Parameters::parse(parameters, Parameters::kRGBDLoopCovLimited(), loopCovLimited);
4215 int iterations =
ui_->spinBox_detectMore_iterations->value();
4218 std::multimap<int, int> checkedLoopClosures;
4219 std::pair<int, int> lastAdded(0,0);
4220 bool intraSession =
ui_->checkBox_detectMore_intraSession->isChecked();
4221 bool interSession =
ui_->checkBox_detectMore_interSession->isChecked();
4222 bool useOptimizedGraphAsGuess =
ui_->checkBox_opt_graph_as_guess->isChecked();
4223 if(!interSession && !intraSession)
4225 QMessageBox::warning(
this, tr(
"Cannot detect more loop closures"), tr(
"Intra and inter session parameters are disabled! Enable one or both."));
4229 for(
int n=0;
n<iterations; ++
n)
4231 UINFO(
"iteration %d/%d",
n+1, iterations);
4233 std::map<int, Transform>(optimizedPoses.upper_bound(0), optimizedPoses.end()),
4234 ui_->doubleSpinBox_detectMore_radius->value(),
4235 ui_->doubleSpinBox_detectMore_angle->value()*CV_PI/180.0);
4238 progressDialog->
appendText(tr(
"Looking for more loop closures, %1 clusters found.").
arg(clusters.size()));
4239 QApplication::processEvents();
4245 std::set<int> addedLinks;
4247 for(std::multimap<int, int>::iterator
iter=clusters.begin();
iter!= clusters.end() && !progressDialog->
isCanceled(); ++
iter, ++
i)
4249 int from =
iter->first;
4250 int to =
iter->second;
4253 from =
iter->second;
4260 if((interSession && mapIdFrom != mapIdTo) ||
4261 (intraSession && mapIdFrom == mapIdTo))
4267 addedLinks.find(from) == addedLinks.end() &&
4268 addedLinks.find(to) == addedLinks.end())
4271 Transform delta = optimizedPoses.at(from).inverse() * optimizedPoses.at(to);
4272 if(
delta.getNorm() <
ui_->doubleSpinBox_detectMore_radius->value() &&
4273 delta.getNorm() >=
ui_->doubleSpinBox_detectMore_radiusMin->value())
4275 checkedLoopClosures.insert(std::make_pair(from, to));
4278 UINFO(
"Added new loop closure between %d and %d.", from, to);
4280 addedLinks.insert(from);
4281 addedLinks.insert(to);
4282 lastAdded.first = from;
4283 lastAdded.second = to;
4285 progressDialog->
appendText(tr(
"Detected loop closure %1->%2! (%3/%4)").
arg(from).
arg(to).
arg(
i+1).
arg(clusters.size()));
4286 QApplication::processEvents();
4297 QApplication::processEvents();
4300 UINFO(
"Iteration %d/%d: added %d loop closures.",
n+1, iterations, (
int)addedLinks.size()/2);
4301 progressDialog->
appendText(tr(
"Iteration %1/%2: Detected %3 loop closures!").
arg(
n+1).
arg(iterations).
arg(addedLinks.size()/2));
4302 if(addedLinks.size() == 0)
4321 UINFO(
"Total added %d loop closures.", added);
4323 progressDialog->
appendText(tr(
"Total new loop closures detected=%1").
arg(added));
4333 QList<rtabmap::Link> links;
4345 QList<rtabmap::Link> links;
4361 double stddev = QInputDialog::getDouble(
this, tr(
"Linear error"), tr(
"Std deviation (m) 0=inf"), 0.01, 0.0, 9, 4, &ok);
4363 double linearVar = stddev*stddev;
4364 stddev = QInputDialog::getDouble(
this, tr(
"Angular error"), tr(
"Std deviation (deg) 0=inf"), 1, 0.0, 90, 2, &ok)*
M_PI/180.0;
4366 double angularVar = stddev*stddev;
4369 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4372 progressDialog->setMinimumWidth(800);
4373 progressDialog->show();
4375 cv::Mat infMatrix = cv::Mat::eye(6,6,CV_64FC1);
4376 if(linearVar == 0.0)
4378 infMatrix(cv::Range(0,3), cv::Range(0,3)) /= 9999.9;
4382 infMatrix(cv::Range(0,3), cv::Range(0,3)) /= linearVar;
4384 if(angularVar == 0.0)
4386 infMatrix(cv::Range(3,6), cv::Range(3,6)) /= 9999.9;
4390 infMatrix(cv::Range(3,6), cv::Range(3,6)) /= angularVar;
4393 for(
int i=0;
i<links.size(); ++
i)
4395 int from = links[
i].from();
4396 int to = links[
i].to();
4401 UERROR(
"Not found link! (%d->%d)", from, to);
4415 if(
iter->second.to() == currentLink.
to() &&
4416 iter->second.type() == currentLink.
type())
4418 iter->second = currentLink;
4431 QApplication::processEvents();
4440 progressDialog->
appendText(
"Refining links finished!");
4451 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4453 int minId =
iter->second.from()>
iter->second.to()?
iter->second.to():
iter->second.from();
4454 int maxId =
iter->second.from()<
iter->second.to()?
iter->second.to():
iter->second.from();
4455 if(minNodeId == 0 || minNodeId > minId)
4459 if(maxNodeId == 0 || maxNodeId < maxId)
4483 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4487 int from =
iter->second.from();
4488 int to =
iter->second.to();
4492 ((from >= minNodeId && from <= maxNodeId) ||
4493 (to >= minNodeId && to <= maxNodeId))) ||
4495 ((mapFrom >= minMapId && mapFrom <= maxMapId) ||
4496 (mapTo >= minMapId && mapTo <= maxMapId)))) &&
4497 ((intra && mapTo == mapFrom) ||
4498 (inter && mapTo != mapFrom)))
4500 links.push_back(
iter->second);
4506 QMessageBox::warning(
this, tr(
"Refine links"), tr(
"No links found matching the requested parameters."));
4521 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4524 progressDialog->setMinimumWidth(800);
4525 progressDialog->show();
4527 for(
int i=0;
i<links.size(); ++
i)
4529 int from = links[
i].from();
4530 int to = links[
i].to();
4531 if(from > 0 && to > 0)
4538 progressDialog->
appendText(tr(
"Ignored link %1->%2 (landmark)").
arg(from).
arg(to));
4541 QApplication::processEvents();
4550 progressDialog->
appendText(
"Refining links finished!");
4556 if(QMessageBox::question(
this,
4557 tr(
"Reset all changes"),
4558 tr(
"You are about to reset all changes you've made so far, do you want to continue?"),
4559 QMessageBox::Yes | QMessageBox::No,
4560 QMessageBox::No) == QMessageBox::Yes)
4576 static bool updateA =
true;
4597 ui_->label_parentsA,
4598 ui_->label_childrenA,
4602 ui_->graphicsView_A,
4606 ui_->label_optposeA,
4610 ui_->label_gravityA,
4612 ui_->toolButton_edit_priorA,
4613 ui_->toolButton_remove_priorA,
4616 ui_->label_sensorsA,
4624 ui_->label_parentsB,
4625 ui_->label_childrenB,
4629 ui_->graphicsView_B,
4633 ui_->label_optposeB,
4637 ui_->label_gravityB,
4639 ui_->toolButton_edit_priorB,
4640 ui_->toolButton_remove_priorB,
4643 ui_->label_sensorsB,
4648 QLabel * labelIndex,
4649 QLabel * labelParents,
4650 QLabel * labelChildren,
4656 QLabel * labelMapId,
4658 QLabel * labelOptPose,
4659 QLabel * labelVelocity,
4660 QLabel * labelCalib,
4662 QLabel * labelGravity,
4663 QLabel * labelPrior,
4664 QToolButton * editPriorButton,
4665 QToolButton * removePriorButton,
4668 QLabel * labelSensors,
4669 bool updateConstraintView)
4674 labelIndex->setText(QString::number(
value));
4675 labelParents->clear();
4676 labelChildren->clear();
4679 labelMapId->clear();
4681 labelOptPose->clear();
4682 labelVelocity->clear();
4684 labelCalib->clear();
4685 labelScan ->clear();
4686 labelGravity->clear();
4687 labelPrior->clear();
4688 editPriorButton->setVisible(
false);
4689 removePriorButton->setVisible(
false);
4692 labelSensors->clear();
4698 labelId->setText(QString::number(
id));
4708 data.uncompressData();
4709 if(!
data.imageRaw().empty())
4713 if(!
data.depthOrRightRaw().empty())
4715 cv::Mat depth =
data.depthOrRightRaw();
4716 if(!
data.depthRaw().empty())
4718 if(
ui_->spinBox_mesh_fillDepthHoles->value() > 0)
4723 if( !
data.imageRaw().empty() &&
4724 !
data.rightRaw().empty() &&
4725 data.stereoCameraModels().size()==1 &&
4726 data.stereoCameraModels()[0].isValidForProjection() &&
4727 ui_->checkBox_showDisparityInsteadOfRight->isChecked())
4752 if(!imgDepth.empty())
4754 view->setImageDepth(imgDepth);
4757 rect.setWidth(imgDepth.cols);
4758 rect.setHeight(imgDepth.rows);
4767 view->setSceneRect(rect);
4772 std::list<Signature*> signatures;
4775 if(signatures.size() && signatures.front()!=0 && !signatures.front()->getWordsKpts().empty())
4777 std::multimap<int, cv::KeyPoint> keypoints;
4778 for(std::map<int, int>::const_iterator
iter=signatures.front()->getWords().begin();
iter!=signatures.front()->getWords().end(); ++
iter)
4780 keypoints.insert(std::make_pair(
iter->first, signatures.front()->getWordsKpts()[
iter->second]));
4782 view->setFeatures(keypoints,
data.depthOrRightRaw().type() == CV_8UC1?cv::Mat():
data.depthOrRightRaw(), Qt::yellow);
4789 std::vector<float>
v;
4795 label->setText(l.c_str());
4798 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));
4803 labelOptPose->setText(
"<Not in optimized graph>");
4813 stamp->setText(QString::number(
s,
'f'));
4814 stamp->setToolTip(QDateTime::fromMSecsSinceEpoch(
s*1000.0).toString(
"dd.MM.yyyy hh:mm:ss.zzz"));
4818 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]));
4821 std::multimap<int, Link> gravityLink;
4823 if(!gravityLink.empty())
4826 gravityLink.begin()->second.transform().getEulerAngles(
roll,
pitch,
yaw);
4828 labelGravity->setText(QString(
"x=%1 y=%2 z=%3").
arg(
v[0]).
arg(
v[1]).
arg(
v[2]));
4838 std::stringstream
out;
4840 labelPrior->setToolTip(QString(
"%1").
arg(
out.str().c_str()));
4841 removePriorButton->setVisible(
true);
4842 editPriorButton->setToolTip(tr(
"Edit Prior"));
4843 editPriorButton->setText(
"...");
4844 editPriorButton->setVisible(
true);
4846 else if(!odomPose.
isNull())
4848 editPriorButton->setToolTip(tr(
"Add Prior"));
4849 editPriorButton->setText(
"+");
4850 editPriorButton->setVisible(
true);
4855 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()));
4856 labelGps->setToolTip(QDateTime::fromMSecsSinceEpoch(gps.
stamp()*1000.0).toString(
"dd.MM.yyyy hh:mm:ss.zzz"));
4860 labelGt->setText(QString(
"%1").
arg(
g.prettyPrint().c_str()));
4866 for(EnvSensors::iterator
iter=sensors.begin();
iter!=sensors.end(); ++
iter)
4868 if(
iter != sensors.begin())
4870 sensorsStr +=
" | ";
4871 tooltipStr +=
" | ";
4876 sensorsStr +=
uFormat(
"%.1f dbm",
iter->second.value()).c_str();
4877 tooltipStr +=
"Wifi signal strength";
4881 sensorsStr +=
uFormat(
"%.1f \u00B0C",
iter->second.value()).c_str();
4882 tooltipStr +=
"Ambient Temperature";
4886 sensorsStr +=
uFormat(
"%.1f hPa",
iter->second.value()).c_str();
4887 tooltipStr +=
"Ambient Air Pressure";
4891 sensorsStr +=
uFormat(
"%.0f lx",
iter->second.value()).c_str();
4892 tooltipStr +=
"Ambient Light";
4896 sensorsStr +=
uFormat(
"%.0f %%",
iter->second.value()).c_str();
4897 tooltipStr +=
"Ambient Relative Humidity";
4901 sensorsStr +=
uFormat(
"%.2f",
iter->second.value()).c_str();
4902 tooltipStr += QString(
"Type %1").arg((
int)
iter->first);
4906 labelSensors->setText(sensorsStr);
4907 labelSensors->setToolTip(tooltipStr);
4909 if(
data.cameraModels().size() ||
data.stereoCameraModels().size())
4911 std::stringstream calibrationDetails;
4912 if(
data.cameraModels().size())
4914 if(!
data.depthRaw().empty() &&
data.depthRaw().cols!=
data.imageRaw().cols &&
data.imageRaw().cols)
4916 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]")
4917 .
arg(
data.cameraModels().size())
4918 .
arg(
data.cameraModels()[0].imageWidth()>0?
data.cameraModels()[0].imageWidth():
data.imageRaw().cols/
data.cameraModels().size())
4919 .arg(
data.cameraModels()[0].imageHeight()>0?
data.cameraModels()[0].imageHeight():
data.imageRaw().rows)
4920 .arg(
data.cameraModels()[0].fx())
4921 .arg(
data.cameraModels()[0].fy())
4922 .arg(
data.cameraModels()[0].cx())
4923 .arg(
data.cameraModels()[0].cy())
4924 .arg(
data.depthRaw().cols/
data.cameraModels().size())
4925 .arg(
data.depthRaw().rows)
4926 .arg(
data.cameraModels()[0].localTransform().prettyPrint().c_str())
4927 .arg(
data.cameraModels()[0].localTransform().r11()).arg(
data.cameraModels()[0].localTransform().r12()).arg(
data.cameraModels()[0].localTransform().r13()).arg(
data.cameraModels()[0].localTransform().o14())
4928 .arg(
data.cameraModels()[0].localTransform().r21()).arg(
data.cameraModels()[0].localTransform().r22()).arg(
data.cameraModels()[0].localTransform().r23()).arg(
data.cameraModels()[0].localTransform().o24())
4929 .arg(
data.cameraModels()[0].localTransform().r31()).arg(
data.cameraModels()[0].localTransform().r32()).arg(
data.cameraModels()[0].localTransform().r33()).arg(
data.cameraModels()[0].localTransform().o34()));
4933 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]")
4934 .
arg(
data.cameraModels().size())
4935 .
arg(
data.cameraModels()[0].imageWidth()>0?
data.cameraModels()[0].imageWidth():
data.imageRaw().cols/
data.cameraModels().size())
4936 .arg(
data.cameraModels()[0].imageHeight()>0?
data.cameraModels()[0].imageHeight():
data.imageRaw().rows)
4937 .arg(
data.cameraModels()[0].fx())
4938 .arg(
data.cameraModels()[0].fy())
4939 .arg(
data.cameraModels()[0].cx())
4940 .arg(
data.cameraModels()[0].cy())
4941 .arg(
data.cameraModels()[0].localTransform().prettyPrint().c_str())
4942 .arg(
data.cameraModels()[0].localTransform().r11()).arg(
data.cameraModels()[0].localTransform().r12()).arg(
data.cameraModels()[0].localTransform().r13()).arg(
data.cameraModels()[0].localTransform().o14())
4943 .arg(
data.cameraModels()[0].localTransform().r21()).arg(
data.cameraModels()[0].localTransform().r22()).arg(
data.cameraModels()[0].localTransform().r23()).arg(
data.cameraModels()[0].localTransform().o24())
4944 .arg(
data.cameraModels()[0].localTransform().r31()).arg(
data.cameraModels()[0].localTransform().r32()).arg(
data.cameraModels()[0].localTransform().r33()).arg(
data.cameraModels()[0].localTransform().o34()));
4947 for(
unsigned int i=0;
i<
data.cameraModels().
size();++
i)
4949 if(
i!=0) calibrationDetails << std::endl;
4950 calibrationDetails <<
"Id: " <<
i <<
" Size=" <<
data.cameraModels()[
i].imageWidth() <<
"x" <<
data.cameraModels()[
i].imageHeight() << std::endl;
4951 if(
data.cameraModels()[
i].K_raw().total()) calibrationDetails <<
"K=" <<
data.cameraModels()[
i].K_raw() << std::endl;
4952 if(
data.cameraModels()[
i].D_raw().total()) calibrationDetails <<
"D=" <<
data.cameraModels()[
i].D_raw() << std::endl;
4953 if(
data.cameraModels()[
i].R().total()) calibrationDetails <<
"R=" <<
data.cameraModels()[
i].R() << std::endl;
4954 if(
data.cameraModels()[
i].P().total()) calibrationDetails <<
"P=" <<
data.cameraModels()[
i].P() << std::endl;
4958 else if(
data.stereoCameraModels().size())
4961 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]")
4962 .
arg(
data.stereoCameraModels()[0].left().imageWidth()>0?
data.stereoCameraModels()[0].left().imageWidth():
data.imageRaw().cols)
4963 .
arg(
data.stereoCameraModels()[0].left().imageHeight()>0?
data.stereoCameraModels()[0].left().imageHeight():
data.imageRaw().rows)
4964 .arg(
data.stereoCameraModels()[0].left().fx())
4965 .arg(
data.stereoCameraModels()[0].left().fy())
4966 .arg(
data.stereoCameraModels()[0].left().cx())
4967 .arg(
data.stereoCameraModels()[0].left().cy())
4968 .arg(
data.stereoCameraModels()[0].baseline())
4969 .arg(
data.stereoCameraModels()[0].localTransform().prettyPrint().c_str())
4970 .arg(
data.stereoCameraModels()[0].localTransform().r11()).arg(
data.stereoCameraModels()[0].localTransform().r12()).arg(
data.stereoCameraModels()[0].localTransform().r13()).arg(
data.stereoCameraModels()[0].localTransform().o14())
4971 .arg(
data.stereoCameraModels()[0].localTransform().r21()).arg(
data.stereoCameraModels()[0].localTransform().r22()).arg(
data.stereoCameraModels()[0].localTransform().r23()).arg(
data.stereoCameraModels()[0].localTransform().o24())
4972 .arg(
data.stereoCameraModels()[0].localTransform().r31()).arg(
data.stereoCameraModels()[0].localTransform().r32()).arg(
data.stereoCameraModels()[0].localTransform().r33()).arg(
data.stereoCameraModels()[0].localTransform().o34()));
4974 for(
unsigned int i=0;
i<
data.stereoCameraModels().
size();++
i)
4976 calibrationDetails <<
"Id: " <<
i << std::endl;
4977 calibrationDetails <<
" Left:" <<
" Size=" <<
data.stereoCameraModels()[
i].left().imageWidth() <<
"x" <<
data.stereoCameraModels()[
i].left().imageHeight() << std::endl;
4978 if(
data.stereoCameraModels()[
i].left().K_raw().total()) calibrationDetails <<
" K=" <<
data.stereoCameraModels()[
i].left().K_raw() << std::endl;
4979 if(
data.stereoCameraModels()[
i].left().D_raw().total()) calibrationDetails <<
" D=" <<
data.stereoCameraModels()[
i].left().D_raw() << std::endl;
4980 if(
data.stereoCameraModels()[
i].left().R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].left().R() << std::endl;
4981 if(
data.stereoCameraModels()[
i].left().P().total()) calibrationDetails <<
" P=" <<
data.stereoCameraModels()[
i].left().P() << std::endl;
4982 calibrationDetails <<
" Right:" <<
" Size=" <<
data.stereoCameraModels()[
i].right().imageWidth() <<
"x" <<
data.stereoCameraModels()[
i].right().imageHeight() << std::endl;
4983 if(
data.stereoCameraModels()[
i].right().K_raw().total()) calibrationDetails <<
" K=" <<
data.stereoCameraModels()[
i].right().K_raw() << std::endl;
4984 if(
data.stereoCameraModels()[
i].right().D_raw().total()) calibrationDetails <<
" D=" <<
data.stereoCameraModels()[
i].right().D_raw() << std::endl;
4985 if(
data.stereoCameraModels()[
i].right().R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].right().R() << std::endl;
4986 if(
data.stereoCameraModels()[
i].right().P().total()) calibrationDetails <<
" P=" <<
data.stereoCameraModels()[
i].right().P() << std::endl;
4987 if(
data.stereoCameraModels()[
i].R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].R() << std::endl;
4988 if(
data.stereoCameraModels()[
i].T().total()) calibrationDetails <<
" T=" <<
data.stereoCameraModels()[
i].T() << std::endl;
4989 if(
data.stereoCameraModels()[
i].F().total()) calibrationDetails <<
" F=" <<
data.stereoCameraModels()[
i].F() << std::endl;
4990 if(
data.stereoCameraModels()[
i].E().total()) calibrationDetails <<
" E=" <<
data.stereoCameraModels()[
i].E() << std::endl;
4993 labelCalib->setToolTip(calibrationDetails.str().c_str());
4998 labelCalib->setText(
"NA");
5001 if(
data.laserScanRaw().size())
5003 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")
5004 .
arg(
data.laserScanRaw().formatName().c_str())
5005 .
arg(
data.laserScanRaw().size())
5006 .arg(
data.laserScanRaw().maxPoints())
5007 .arg(
data.laserScanRaw().rangeMin())
5008 .arg(
data.laserScanRaw().rangeMax())
5009 .arg(
data.laserScanRaw().angleMin())
5010 .arg(
data.laserScanRaw().angleMax())
5011 .arg(
data.laserScanRaw().angleIncrement())
5012 .arg(
data.laserScanRaw().hasRGB()?1:0)
5013 .arg(
data.laserScanRaw().is2d()?1:0)
5014 .arg(
data.laserScanRaw().hasNormals()?1:0)
5015 .arg(
data.laserScanRaw().hasIntensity()?1:0)
5016 .arg(
data.laserScanRaw().localTransform().prettyPrint().c_str()));
5020 if(!
data.depthOrRightRaw().empty() &&
data.depthOrRightRaw().type() == CV_8UC1)
5027 ui_->graphicsView_stereo->clear();
5041 if(signatures.size() &&
ui_->checkBox_odomFrame_3dview->isChecked())
5048 if(!gravityLink.empty() &&
ui_->checkBox_gravity_3dview->isChecked())
5050 Transform gravityT = gravityLink.begin()->second.transform();
5051 Eigen::Vector3f gravity(0,0,-1);
5054 gravityT = gravityT.
inverse();
5056 gravity = (gravityT.
rotation()*(pose).
rotation().inverse()).toEigen3f()*gravity;
5057 cloudViewer_->
addOrUpdateLine(
"gravity", pose, (pose).
translation()*
Transform(gravity[0], gravity[1], gravity[2], 0, 0, 0)*pose.
rotation().
inverse(), Qt::yellow,
true,
false);
5066 if(
ui_->checkBox_showScan->isChecked() && laserScanRaw.
size())
5083 else if(laserScanRaw.
hasRGB())
5101 if(
ui_->checkBox_showCloud->isChecked() &&
ui_->checkBox_cameraProjection->isChecked() &&
5102 !
data.imageRaw().empty() && !laserScanRaw.
empty() && !laserScanRaw.
is2d())
5105 std::vector<CameraModel> models =
data.cameraModels();
5106 if(!
data.stereoCameraModels().empty())
5109 for(
size_t i=0;
i<
data.stereoCameraModels().
size(); ++
i)
5111 models.push_back(
data.stereoCameraModels()[
i].left());
5115 if(!models.empty() && !models[0].isValidForProjection())
5120 if(!models.empty() && models[0].imageWidth() != 0 && models[0].imageHeight() != 0)
5122 std::map<int, Transform> cameraPoses;
5123 std::map<int, std::vector<CameraModel> > cameraModels;
5125 cameraModels.insert(std::make_pair(
data.id(), models));
5127 std::vector<std::pair< std::pair<int, int>, pcl::PointXY> > pointToPixel;
5133 UASSERT(pointToPixel.empty() || pointToPixel.size() == cloud->size());
5134 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudValidPoints(
new pcl::PointCloud<pcl::PointXYZRGB>);
5135 cloudValidPoints->resize(cloud->size());
5137 for(
size_t i=0;
i<pointToPixel.size(); ++
i)
5139 pcl::PointXYZINormal & pt = cloud->at(
i);
5140 pcl::PointXYZRGB ptColor;
5141 int nodeID = pointToPixel[
i].first.first;
5142 int cameraIndex = pointToPixel[
i].first.second;
5143 if(nodeID>0 && cameraIndex>=0)
5145 cv::Mat image =
data.imageRaw();
5147 int subImageWidth = image.cols / cameraModels.at(nodeID).size();
5148 image = image(
cv::Range::all(), cv::Range(cameraIndex*subImageWidth, (cameraIndex+1)*subImageWidth));
5151 int x = pointToPixel[
i].second.x * (
float)image.cols;
5152 int y = pointToPixel[
i].second.y * (
float)image.rows;
5156 if(image.type()==CV_8UC3)
5158 cv::Vec3b bgr = image.at<cv::Vec3b>(
y,
x);
5165 UASSERT(image.type()==CV_8UC1);
5166 ptColor.r = ptColor.g = ptColor.b = image.at<
unsigned char>(pointToPixel[
i].second.y * image.rows, pointToPixel[
i].second.x * image.cols);
5172 cloudValidPoints->at(oi++) = ptColor;
5175 cloudValidPoints->resize(oi);
5176 if(!cloudValidPoints->empty())
5178 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5180 cloudValidPoints =
util3d::voxelize(cloudValidPoints,
ui_->doubleSpinBox_voxelSize->value());
5187 UWARN(
"Camera projection to scan returned an empty cloud, no visible points from cameras...");
5192 UERROR(
"Node has invalid camera models, camera projection on scan cannot be done!.");
5195 else if(
ui_->checkBox_showCloud->isChecked() ||
ui_->checkBox_showMesh->isChecked())
5197 if(!
data.depthOrRightRaw().empty())
5199 if(!
data.imageRaw().empty())
5201 std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> clouds;
5202 std::vector<pcl::IndicesPtr> allIndices;
5203 if(!
data.depthRaw().empty() &&
data.cameraModels().size()==1)
5205 cv::Mat depth =
data.depthRaw();
5206 if(
ui_->spinBox_mesh_fillDepthHoles->value() > 0)
5210 pcl::IndicesPtr
indices(
new std::vector<int>);
5214 data.cameraModels()[0],
5215 ui_->spinBox_decimation->value(),0,0,
indices.get());
5219 allIndices.push_back(
indices);
5226 UASSERT(clouds.size() == allIndices.size());
5227 for(
size_t i=0;
i<allIndices.size(); ++
i)
5229 if(allIndices[
i]->
size())
5231 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = clouds[
i];
5232 pcl::IndicesPtr
indices = allIndices[
i];
5233 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5238 if(
ui_->checkBox_showMesh->isChecked() && !cloud->is_dense)
5240 Eigen::Vector3f viewpoint(0.0
f,0.0
f,0.0
f);
5241 if(
data.cameraModels().size() && !
data.cameraModels()[0].localTransform().isNull())
5243 viewpoint[0] =
data.cameraModels()[0].localTransform().x();
5244 viewpoint[1] =
data.cameraModels()[0].localTransform().y();
5245 viewpoint[2] =
data.cameraModels()[0].localTransform().z();
5247 else if(
data.stereoCameraModels().size() && !
data.stereoCameraModels()[0].localTransform().isNull())
5249 viewpoint[0] =
data.stereoCameraModels()[0].localTransform().x();
5250 viewpoint[1] =
data.stereoCameraModels()[0].localTransform().y();
5251 viewpoint[2] =
data.stereoCameraModels()[0].localTransform().z();
5255 float(
ui_->spinBox_mesh_angleTolerance->value())*
M_PI/180.0f,
5256 ui_->checkBox_mesh_quad->isChecked(),
5257 ui_->spinBox_mesh_triangleSize->value(),
5260 if(
ui_->spinBox_mesh_minClusterSize->value())
5263 std::vector<std::set<int> > neighbors;
5264 std::vector<std::set<int> > vertexToPolygons;
5271 ui_->spinBox_mesh_minClusterSize->value());
5272 std::vector<pcl::Vertices> filteredPolygons(polygons.size());
5276 for(std::list<int>::iterator jter=
iter->begin(); jter!=
iter->end(); ++jter)
5278 filteredPolygons[oi++] = polygons.at(*jter);
5281 filteredPolygons.resize(oi);
5282 polygons = filteredPolygons;
5287 if(
ui_->checkBox_showCloud->isChecked())
5294 else if(
ui_->checkBox_showCloud->isChecked())
5296 std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> clouds;
5297 std::vector<pcl::IndicesPtr> allIndices;
5300 UASSERT(clouds.size() == allIndices.size());
5301 for(
size_t i=0;
i<allIndices.size(); ++
i)
5303 if(allIndices[
i]->
size())
5305 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = clouds[
i];
5306 pcl::IndicesPtr
indices = allIndices[
i];
5307 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5322 if(
data.cameraModels().size())
5333 if(
ui_->checkBox_showWords->isChecked() &&
5334 !signatures.empty() &&
5335 !(*signatures.begin())->getWords3().empty())
5337 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>);
5338 cloud->resize((*signatures.begin())->getWords3().size());
5340 for(std::multimap<int, int>::const_iterator
iter=(*signatures.begin())->getWords().begin();
5341 iter!=(*signatures.begin())->getWords().end();
5344 const cv::Point3f & pt = (*signatures.begin())->getWords3()[
iter->second];
5345 cloud->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
5360 if(
ui_->checkBox_showMap->isChecked() ||
5361 ui_->checkBox_showGrid->isChecked() ||
5362 ui_->checkBox_showElevation->checkState() != Qt::Unchecked)
5369 else if(!
data.gridGroundCellsRaw().empty() || !
data.gridObstacleCellsRaw().empty())
5371 combinedLocalMaps.
add(
data.id(),
data.gridGroundCellsRaw(),
data.gridObstacleCellsRaw(),
data.gridEmptyCellsRaw(),
data.gridCellSize(),
data.gridViewPoint());
5373 if(!combinedLocalMaps.
empty())
5375 std::map<int, Transform> poses;
5376 poses.insert(std::make_pair(
data.id(), pose));
5378 #ifdef RTABMAP_OCTOMAP
5380 if(
ui_->checkBox_octomap->isChecked() &&
5381 (!combinedLocalMaps.
localGrids().begin()->second.groundCells.empty() || !combinedLocalMaps.
localGrids().begin()->second.obstacleCells.empty()) &&
5382 combinedLocalMaps.
localGrids().begin()->second.is3D() &&
5383 combinedLocalMaps.
localGrids().begin()->second.cellSize > 0.0f)
5393 if(
ui_->checkBox_showMap->isChecked())
5395 float xMin=0.0f, yMin=0.0f;
5397 float gridCellSize = Parameters::defaultGridCellSize();
5401 #ifdef RTABMAP_OCTOMAP
5411 map8S = grid.
getMap(xMin, yMin);
5420 if(
ui_->checkBox_showGrid->isChecked())
5422 #ifdef RTABMAP_OCTOMAP
5425 if(
ui_->comboBox_octomap_rendering_type->currentIndex()== 0)
5427 pcl::IndicesPtr obstacles(
new std::vector<int>);
5428 pcl::IndicesPtr
empty(
new std::vector<int>);
5429 pcl::IndicesPtr ground(
new std::vector<int>);
5430 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = octomap->
createCloud(
ui_->spinBox_grid_depth->value(), obstacles.get(),
empty.get(), ground.get());
5433 pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
5434 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
5438 pcl::PointCloud<pcl::PointXYZRGB>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
5439 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
5445 pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5446 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
5450 pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5451 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
5456 if(
ui_->checkBox_grid_empty->isChecked())
5458 pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5459 pcl::copyPointCloud(*cloud, *
empty, *emptyCloud);
5496 if(
ui_->checkBox_grid_empty->isChecked())
5501 QColor(
ui_->lineEdit_emptyColor->text()));
5507 #ifdef RTABMAP_OCTOMAP
5514 #ifdef RTABMAP_GRIDMAP
5515 if(
ui_->checkBox_showElevation->checkState() != Qt::Unchecked)
5517 GridMap gridMap(&combinedLocalMaps, parameters);
5519 if(combinedLocalMaps.
localGrids().begin()->second.is3D())
5522 if(
ui_->checkBox_showElevation->checkState() == Qt::PartiallyChecked)
5524 float xMin, yMin, gridCellSize;
5525 cv::Mat elevationMap = gridMap.
createHeightMap(xMin, yMin, gridCellSize);
5537 UWARN(
"Local grid is not 3D, cannot generate an elevation map");
5548 if(signatures.size())
5550 UASSERT(signatures.front() != 0 && signatures.size() == 1);
5551 delete signatures.front();
5557 std::multimap<int, rtabmap::Link> links;
5561 QString strParents, strChildren;
5562 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
5567 if(
iter->first <
id)
5569 strChildren.append(QString(
"%1 ").
arg(
iter->first));
5573 strParents.append(QString(
"%1 ").
arg(
iter->first));
5577 labelParents->setText(strParents);
5578 labelChildren->setText(strChildren);
5584 labelMapId->setText(QString::number(mapId));
5598 int from =
ids_.at(
ui_->horizontalSlider_A->value());
5599 int to =
ids_.at(
ui_->horizontalSlider_B->value());
5608 if(
i !=
ui_->horizontalSlider_loops->value())
5610 ui_->horizontalSlider_loops->blockSignals(
true);
5611 ui_->horizontalSlider_loops->setValue(
i);
5612 ui_->horizontalSlider_loops->blockSignals(
false);
5615 ui_->horizontalSlider_neighbors->blockSignals(
true);
5616 ui_->horizontalSlider_neighbors->setValue(0);
5617 ui_->horizontalSlider_neighbors->blockSignals(
false);
5627 if(
i !=
ui_->horizontalSlider_neighbors->value())
5629 ui_->horizontalSlider_neighbors->blockSignals(
true);
5630 ui_->horizontalSlider_neighbors->setValue(
i);
5631 ui_->horizontalSlider_neighbors->blockSignals(
false);
5634 ui_->horizontalSlider_loops->blockSignals(
true);
5635 ui_->horizontalSlider_loops->setValue(0);
5636 ui_->horizontalSlider_loops->blockSignals(
false);
5644 ui_->horizontalSlider_loops->blockSignals(
true);
5645 ui_->horizontalSlider_neighbors->blockSignals(
true);
5646 ui_->horizontalSlider_loops->setValue(0);
5647 ui_->horizontalSlider_neighbors->setValue(0);
5648 ui_->horizontalSlider_loops->blockSignals(
false);
5649 ui_->horizontalSlider_neighbors->blockSignals(
false);
5654 bool constraintViewUpdated =
false;
5658 constraintViewUpdated =
true;
5663 std::map<int, Transform> optimizedPoses =
uValueAt(
graphes_,
ui_->horizontalSlider_iterations->value());
5664 if(optimizedPoses.size() > 0)
5666 std::map<int, Transform>::iterator fromIter = optimizedPoses.find(from);
5667 std::map<int, Transform>::iterator toIter = optimizedPoses.find(to);
5668 if(fromIter != optimizedPoses.end() &&
5669 toIter != optimizedPoses.end())
5671 Link link(from, to,
Link::kUndef, fromIter->second.inverse() * toIter->second);
5673 constraintViewUpdated =
true;
5677 if(!constraintViewUpdated)
5679 ui_->label_constraint->clear();
5680 ui_->label_constraint_opt->clear();
5681 ui_->label_variance->clear();
5682 ui_->lineEdit_covariance->clear();
5683 ui_->label_type->clear();
5684 ui_->label_type_name->clear();
5685 ui_->checkBox_showOptimized->setEnabled(
false);
5695 if(this->parent() == 0)
5703 if(
ui_->horizontalSlider_A->maximum())
5705 int id =
ids_.at(
ui_->horizontalSlider_A->value());
5708 data.uncompressData();
5716 ui_->dockWidget_stereoView->isVisible() &&
5717 !
data->imageRaw().empty() &&
5718 !
data->depthOrRightRaw().empty() &&
5719 data->depthOrRightRaw().type() == CV_8UC1 &&
5720 data->stereoCameraModels().size()==1 &&
5721 data->stereoCameraModels()[0].isValidForProjection())
5724 if(
data->imageRaw().channels() == 3)
5726 cv::cvtColor(
data->imageRaw(), leftMono, CV_BGR2GRAY);
5730 leftMono =
data->imageRaw();
5738 std::vector<cv::KeyPoint> kpts;
5739 uInsert(parameters,
ParametersPair(Parameters::kKpMaxFeatures(), parameters.at(Parameters::kVisMaxFeatures())));
5741 uInsert(parameters,
ParametersPair(Parameters::kKpMinDepth(), parameters.at(Parameters::kVisMinDepth())));
5742 uInsert(parameters,
ParametersPair(Parameters::kKpMaxDepth(), parameters.at(Parameters::kVisMaxDepth())));
5743 uInsert(parameters,
ParametersPair(Parameters::kKpDetectorStrategy(), parameters.at(Parameters::kVisFeatureType())));
5744 uInsert(parameters,
ParametersPair(Parameters::kKpRoiRatios(), parameters.at(Parameters::kVisRoiRatios())));
5745 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixEps(), parameters.at(Parameters::kVisSubPixEps())));
5746 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixIterations(), parameters.at(Parameters::kVisSubPixIterations())));
5747 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixWinSize(), parameters.at(Parameters::kVisSubPixWinSize())));
5752 float timeKpt =
timer.ticks();
5754 std::vector<cv::Point2f> leftCorners;
5755 cv::KeyPoint::convert(kpts, leftCorners);
5758 std::vector<unsigned char> status;
5759 std::vector<cv::Point2f> rightCorners;
5768 float timeStereo =
timer.ticks();
5770 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>);
5771 cloud->resize(kpts.size());
5772 float bad_point = std::numeric_limits<float>::quiet_NaN ();
5773 UASSERT(status.size() == kpts.size());
5776 int flowOutliers= 0;
5777 int slopeOutliers= 0;
5778 int negativeDisparityOutliers = 0;
5779 for(
unsigned int i=0;
i<status.size(); ++
i)
5781 cv::Point3f pt(bad_point, bad_point, bad_point);
5784 float disparity = leftCorners[
i].x - rightCorners[
i].x;
5785 if(disparity > 0.0
f)
5790 data->stereoCameraModels()[0]);
5797 cloud->at(oi++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
5803 ++negativeDisparityOutliers;
5813 UINFO(
"correspondences = %d/%d (%f) (time kpt=%fs stereo=%fs)",
5814 (
int)cloud->size(), (
int)leftCorners.size(),
float(cloud->size())/
float(leftCorners.size()), timeKpt, timeStereo);
5820 ui_->label_stereo_inliers->setNum(inliers);
5821 ui_->label_stereo_flowOutliers->setNum(flowOutliers);
5822 ui_->label_stereo_slopeOutliers->setNum(slopeOutliers);
5823 ui_->label_stereo_disparityOutliers->setNum(negativeDisparityOutliers);
5825 std::vector<cv::KeyPoint> rightKpts;
5826 cv::KeyPoint::convert(rightCorners, rightKpts);
5827 std::vector<cv::DMatch> good_matches(kpts.size());
5828 for(
unsigned int i=0;
i<good_matches.size(); ++
i)
5830 good_matches[
i].trainIdx =
i;
5831 good_matches[
i].queryIdx =
i;
5843 ui_->graphicsView_stereo->clear();
5844 ui_->graphicsView_stereo->setLinesShown(
true);
5845 ui_->graphicsView_stereo->setFeaturesShown(
false);
5846 ui_->graphicsView_stereo->setImageDepthShown(
true);
5849 ui_->graphicsView_stereo->setImageDepth(
data->depthOrRightRaw());
5852 for(
unsigned int i=0;
i<kpts.size(); ++
i)
5854 if(rightKpts[
i].pt.x > 0 && rightKpts[
i].pt.y > 0)
5856 QColor
c = Qt::green;
5861 else if(status[
i] == 100)
5865 else if(status[
i] == 101)
5869 else if(status[
i] == 102)
5873 else if(status[
i] == 110)
5877 ui_->graphicsView_stereo->addLine(
5883 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));
5886 ui_->graphicsView_stereo->update();
5892 int from =
ids_.at(
ui_->horizontalSlider_A->value());
5893 int to =
ids_.at(
ui_->horizontalSlider_B->value());
5896 ui_->graphicsView_A->clearLines();
5897 ui_->graphicsView_A->setFeaturesColor(
ui_->graphicsView_A->getDefaultFeatureColor());
5898 ui_->graphicsView_B->clearLines();
5899 ui_->graphicsView_B->setFeaturesColor(
ui_->graphicsView_B->getDefaultFeatureColor());
5901 const QMultiMap<int, KeypointItem*> & wordsA =
ui_->graphicsView_A->getFeatures();
5902 const QMultiMap<int, KeypointItem*> & wordsB =
ui_->graphicsView_B->getFeatures();
5903 std::set<int> inliersSet(inliers.begin(), inliers.end());
5904 if(wordsA.size() && wordsB.size())
5906 QList<int> ids = wordsA.uniqueKeys();
5907 for(
int i=0;
i<ids.size(); ++
i)
5909 if(ids[
i] > 0 && wordsA.count(ids[
i]) == 1 && wordsB.count(ids[
i]) == 1)
5913 float scaleAX =
ui_->graphicsView_A->viewScale();
5914 float scaleBX =
ui_->graphicsView_B->viewScale();
5916 float marginAX = (
ui_->graphicsView_A->width() -
ui_->graphicsView_A->sceneRect().width()*scaleAX)/2.0
f;
5917 float marginAY = (
ui_->graphicsView_A->height() -
ui_->graphicsView_A->sceneRect().height()*scaleAX)/2.0
f;
5918 float marginBX = (
ui_->graphicsView_B->width() -
ui_->graphicsView_B->sceneRect().width()*scaleBX)/2.0
f;
5919 float marginBY = (
ui_->graphicsView_B->height() -
ui_->graphicsView_B->sceneRect().height()*scaleBX)/2.0
f;
5924 if(
ui_->actionVertical_Layout->isChecked())
5926 deltaY =
ui_->graphicsView_A->height();
5930 deltaX =
ui_->graphicsView_A->width();
5936 QColor cA =
ui_->graphicsView_A->getDefaultMatchingLineColor();
5937 QColor cB =
ui_->graphicsView_B->getDefaultMatchingLineColor();
5938 if(inliersSet.find(ids[
i])!=inliersSet.end())
5940 cA =
ui_->graphicsView_A->getDefaultMatchingFeatureColor();
5941 cB =
ui_->graphicsView_B->getDefaultMatchingFeatureColor();
5942 ui_->graphicsView_A->setFeatureColor(ids[
i],
ui_->graphicsView_A->getDefaultMatchingFeatureColor());
5943 ui_->graphicsView_B->setFeatureColor(ids[
i],
ui_->graphicsView_B->getDefaultMatchingFeatureColor());
5947 ui_->graphicsView_A->setFeatureColor(ids[
i],
ui_->graphicsView_A->getDefaultMatchingLineColor());
5948 ui_->graphicsView_B->setFeatureColor(ids[
i],
ui_->graphicsView_B->getDefaultMatchingLineColor());
5951 ui_->graphicsView_A->addLine(
5954 (kptB->
keypoint().pt.x*scaleBX+marginBX+deltaX-marginAX)/scaleAX,
5955 (kptB->
keypoint().pt.y*scaleBX+marginBY+deltaY-marginAY)/scaleAX,
5958 ui_->graphicsView_B->addLine(
5959 (kptA->
keypoint().pt.x*scaleAX+marginAX-deltaX-marginBX)/scaleBX,
5960 (kptA->
keypoint().pt.y*scaleAX+marginAY-deltaY-marginBY)/scaleBX,
5966 ui_->graphicsView_A->update();
5967 ui_->graphicsView_B->update();
5974 ui_->label_indexA->setText(QString::number(
value));
5977 ui_->label_idA->setText(QString::number(
ids_.at(
value)));
5987 ui_->label_indexB->setText(QString::number(
value));
5990 ui_->label_idB->setText(QString::number(
ids_.at(
value)));
6031 int priorId = sender() ==
ui_->toolButton_edit_priorA?
ids_.at(
ui_->horizontalSlider_A->value()):
6032 sender() ==
ui_->toolButton_edit_priorB?
ids_.at(
ui_->horizontalSlider_B->value()):0;
6049 QMessageBox::warning(
this, tr(
""), tr(
"Node %1 doesn't have odometry pose, cannot add a prior for it!").
arg(priorId));
6064 cv::Mat covBefore = link.
infMatrix().inv();
6066 covBefore.at<
double>(0,0)<9999.0?
std::sqrt(covBefore.at<
double>(0,0)):0.0,
6067 covBefore.at<
double>(5,5)<9999.0?
std::sqrt(covBefore.at<
double>(5,5)):0.0);
6068 if(dialog.exec() == QDialog::Accepted)
6070 cv::Mat covariance = cv::Mat::eye(6, 6, CV_64FC1);
6077 covariance(cv::Range(0,3), cv::Range(0,3)) *= 9999.9;
6085 covariance(cv::Range(3,6), cv::Range(3,6)) *= 9999.9;
6091 if(
iter->second.to() == link.
to() &&
6092 iter->second.type() == link.
type())
6094 iter->second = newLink;
6118 if(dialog.exec() == QDialog::Accepted)
6120 cv::Mat covariance = cv::Mat::eye(6, 6, CV_64FC1);
6127 covariance(cv::Range(0,3), cv::Range(0,3)) *= 9999.9;
6135 covariance(cv::Range(3,6), cv::Range(3,6)) *= 9999.9;
6137 int from = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_A->value());
6138 int to = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_B->value());
6145 if(newLink.
from() < newLink.
to())
6161 bool priorsIgnored = Parameters::defaultOptimizerPriorsIgnored();
6162 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kOptimizerPriorsIgnored(), priorsIgnored);
6165 if(QMessageBox::question(
this,
6166 tr(
"Updating Prior"),
6167 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()),
6168 QMessageBox::Yes | QMessageBox::No,
6169 QMessageBox::Yes) == QMessageBox::Yes)
6171 priorsIgnored =
false;
6172 ui_->parameters_toolbox->updateParameter(Parameters::kOptimizerPriorsIgnored(),
"false");
6175 int indexA =
ui_->horizontalSlider_A->value();
6176 int indexB =
ui_->horizontalSlider_B->value();
6181 if(
ui_->horizontalSlider_A->value() != indexA)
6182 ui_->horizontalSlider_A->setValue(indexA);
6185 if(
ui_->horizontalSlider_B->value() != indexB)
6186 ui_->horizontalSlider_B->setValue(indexB);
6204 ui_->horizontalSlider_neighbors->value() >= 0 &&
ui_->horizontalSlider_neighbors->value() <
neighborLinks_.size())
6209 ui_->horizontalSlider_loops->value() >= 0 &&
ui_->horizontalSlider_loops->value() <
loopLinks_.size())
6223 bool updateImageSliders,
6233 if(iterLink->second.from() == link.
to())
6235 link = iterLink->second.
inverse();
6239 link = iterLink->second;
6242 else if(
ui_->checkBox_ignorePoseCorrection->isChecked())
6270 ui_->label_constraint->clear();
6271 ui_->label_constraint_opt->clear();
6272 ui_->checkBox_showOptimized->setEnabled(
false);
6275 ui_->label_type->setText(QString::number(link.
type()));
6276 ui_->label_type_name->setText(tr(
"(%1)")
6286 ui_->label_variance->setText(QString(
"%1, %2")
6289 std::stringstream
out;
6291 ui_->lineEdit_covariance->setText(
out.str().c_str());
6292 ui_->label_constraint->setText(QString(
"%1").
arg(
t.prettyPrint().c_str()).replace(
" ",
"\n"));
6294 (
int)
graphes_.size()-1 ==
ui_->horizontalSlider_iterations->maximum())
6298 std::map<int, rtabmap::Transform>::iterator iterFrom =
graph.find(link.
from());
6299 std::map<int, rtabmap::Transform>::iterator iterTo =
graph.find(link.
to());
6300 if(iterFrom !=
graph.end() && iterTo !=
graph.end())
6302 ui_->checkBox_showOptimized->setEnabled(
true);
6307 float a = pcl::getAngle3D(Eigen::Vector4f(
v1.x(),
v1.y(),
v1.z(), 0), Eigen::Vector4f(
v2.x(),
v2.y(),
v2.z(), 0));
6308 a = (
a *180.0f) / CV_PI;
6309 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));
6311 if(
ui_->checkBox_showOptimized->isChecked())
6318 if(updateImageSliders)
6320 ui_->horizontalSlider_A->blockSignals(
true);
6321 ui_->horizontalSlider_B->blockSignals(
true);
6327 ui_->horizontalSlider_A->blockSignals(
false);
6328 ui_->horizontalSlider_B->blockSignals(
false);
6332 ui_->label_parentsA,
6333 ui_->label_childrenA,
6337 ui_->graphicsView_A,
6341 ui_->label_optposeA,
6345 ui_->label_gravityA,
6347 ui_->toolButton_edit_priorA,
6348 ui_->toolButton_remove_priorA,
6351 ui_->label_sensorsA,
6357 ui_->label_parentsB,
6358 ui_->label_childrenB,
6362 ui_->graphicsView_B,
6366 ui_->label_optposeB,
6370 ui_->label_gravityB,
6372 ui_->toolButton_edit_priorB,
6373 ui_->toolButton_remove_priorB,
6376 ui_->label_sensorsB,
6385 if(signatureFrom.
id()>0)
6397 if(signatureTo.
id()>0)
6401 else if(link.
to()>0)
6411 if(
ui_->checkBox_odomFrame->isChecked())
6417 std::vector<float>
v;
6433 if(
ui_->checkBox_show3Dclouds->isChecked())
6435 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudFrom, cloudTo;
6436 pcl::IndicesPtr indicesFrom(
new std::vector<int>);
6437 pcl::IndicesPtr indicesTo(
new std::vector<int>);
6447 if(cloudTo.get() && indicesTo->size())
6453 if(
ui_->doubleSpinBox_gainCompensationRadius->value()>0.0 &&
6454 cloudFrom.get() && indicesFrom->size() &&
6455 cloudTo.get() && indicesTo->size())
6460 compensator.
apply(0, cloudFrom, indicesFrom);
6461 compensator.
apply(1, cloudTo, indicesTo);
6462 UINFO(
"Gain compensation time = %fs",
t.ticks());
6465 if(cloudFrom.get() && indicesFrom->size())
6467 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
6469 cloudFrom =
util3d::voxelize(cloudFrom, indicesFrom,
ui_->doubleSpinBox_voxelSize->value());
6473 if(cloudTo.get() && indicesTo->size())
6475 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
6485 if(
ui_->checkBox_show3DWords->isChecked())
6488 ids.push_back(link.
from());
6491 ids.push_back(link.
to());
6493 std::list<Signature*> signatures;
6495 if(signatures.size() == 2 || (link.
to()<0 && signatures.size()==1))
6497 const Signature * sFrom = signatureFrom.
id()>0?&signatureFrom:signatures.front();
6499 if(signatures.size()==2)
6501 sTo = signatureTo.
id()>0?&signatureTo:signatures.back();
6505 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudFrom(
new pcl::PointCloud<pcl::PointXYZ>);
6506 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudTo(
new pcl::PointCloud<pcl::PointXYZ>);
6507 cloudFrom->resize(sFrom->
getWords3().size());
6510 cloudTo->resize(sTo->
getWords3().size());
6515 for(std::multimap<int, int>::const_iterator
iter=sFrom->
getWords().begin();
6520 cloudFrom->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
6526 for(std::multimap<int, int>::const_iterator
iter=sTo->
getWords().begin();
6531 cloudTo->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
6535 if(cloudFrom->size())
6548 if(cloudFrom->size())
6554 UWARN(
"Empty 3D words for node %d", link.
from());
6565 UWARN(
"Empty 3D words for node %d", link.
to());
6572 UERROR(
"Not found signature %d or %d in RAM", link.
from(), link.
to());
6577 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
6587 if(
ui_->checkBox_show2DScans->isChecked())
6592 signatureTo.
id()==0)
6594 std::vector<int> ids;
6596 if(userData.type() == CV_8SC1 &&
6597 userData.rows == 1 &&
6598 userData.cols >= 8 &&
6599 userData.at<
char>(userData.cols-1) == 0 &&
6600 memcmp(userData.data,
"SCANS:", 6) == 0)
6602 std::string scansStr = (
const char *)userData.data;
6603 UINFO(
"Detected \"%s\" in links's user data", scansStr.c_str());
6604 if(!scansStr.empty())
6606 std::list<std::string> strs =
uSplit(scansStr,
':');
6607 if(strs.size() == 2)
6609 std::list<std::string> strIds =
uSplit(strs.rbegin()->c_str(),
';');
6610 for(std::list<std::string>::iterator
iter=strIds.begin();
iter!=strIds.end(); ++
iter)
6612 ids.push_back(atoi(
iter->c_str()));
6613 if(ids.back() == link.
from())
6626 std::map<int, rtabmap::Transform> poses;
6627 for(
unsigned int i=0;
i<ids.size(); ++
i)
6635 UERROR(
"Not found %d node!", ids[
i]);
6643 std::map<int, rtabmap::Transform> posesOut;
6644 std::multimap<int, rtabmap::Link> linksOut;
6652 if(poses.size() != posesOut.size())
6654 UWARN(
"Scan poses input and output are different! %d vs %d", (
int)poses.size(), (
int)posesOut.size());
6658 for(std::map<int, Transform>::iterator
iter=posesOut.begin();
iter!=posesOut.end(); ++
iter)
6660 UDEBUG(
" %d=%s",
iter->first,
iter->second.prettyPrint().c_str());
6663 for(std::multimap<int, Link>::iterator
iter=linksOut.begin();
iter!=linksOut.end(); ++
iter)
6665 UDEBUG(
" %d->%d (type=%s) %s",
iter->second.from(),
iter->second.to(),
iter->second.typeName().c_str(),
iter->second.transform().prettyPrint().c_str());
6668 std::map<int, rtabmap::Transform> finalPoses = optimizer->
optimize(link.
to(), posesOut, linksOut);
6671 UDEBUG(
"Output poses: ");
6672 for(std::map<int, Transform>::iterator
iter=finalPoses.begin();
iter!=finalPoses.end(); ++
iter)
6674 UDEBUG(
" %d=%s",
iter->first,
iter->second.prettyPrint().c_str());
6679 pcl::PointCloud<pcl::PointXYZ>::Ptr assembledScans(
new pcl::PointCloud<pcl::PointXYZ>);
6680 pcl::PointCloud<pcl::PointNormal>::Ptr assembledNormalScans(
new pcl::PointCloud<pcl::PointNormal>);
6681 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledIScans(
new pcl::PointCloud<pcl::PointXYZI>);
6682 pcl::PointCloud<pcl::PointXYZINormal>::Ptr assembledINormalScans(
new pcl::PointCloud<pcl::PointXYZINormal>);
6683 pcl::PointCloud<pcl::PointXYZ>::Ptr
graph(
new pcl::PointCloud<pcl::PointXYZ>);
6684 for(std::map<int, Transform>::iterator
iter=finalPoses.begin();
iter!=finalPoses.end(); ++
iter)
6687 if(
iter->first != link.
to())
6693 data.uncompressDataConst(0, 0, &scan, 0);
6717 if(assembledNormalScans->size())
6722 if(assembledScans->size())
6727 if(assembledINormalScans->size())
6732 if(assembledIScans->size())
6752 pcl::PointCloud<pcl::PointXYZINormal>::Ptr scan;
6759 pcl::PointCloud<pcl::PointNormal>::Ptr scan;
6766 pcl::PointCloud<pcl::PointXYZI>::Ptr scan;
6773 pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
6783 pcl::PointCloud<pcl::PointXYZINormal>::Ptr scan;
6790 pcl::PointCloud<pcl::PointNormal>::Ptr scan;
6797 pcl::PointCloud<pcl::PointXYZI>::Ptr scan;
6804 pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
6840 #if PCL_VERSION_COMPARE(>=, 1, 7, 2)
6861 ui_->pushButton_refine->setEnabled(
false);
6862 ui_->pushButton_reset->setEnabled(
false);
6863 ui_->pushButton_add->setEnabled(
false);
6864 ui_->pushButton_reject->setEnabled(
false);
6865 ui_->toolButton_constraint->setEnabled(
false);
6873 currentLink =
loopLinks_.at(
ui_->horizontalSlider_loops->value());
6874 from = currentLink.
from();
6875 to = currentLink.
to();
6879 from =
ids_.at(
ui_->horizontalSlider_A->value());
6880 to =
ids_.at(
ui_->horizontalSlider_B->value());
6881 if(from!=to && from && to &&
6884 (
ui_->checkBox_enableForAll->isChecked() ||
6891 ui_->pushButton_add->setEnabled(
true);
6894 else if(
ui_->checkBox_enableForAll->isChecked())
6898 UWARN(
"Button \"Add\" cannot be enabled even if \"all\" checkbox is checked, as node %d doesn't have odometry set.", from);
6902 UWARN(
"Button \"Add\" cannot be enabled even if \"all\" checkbox is checked, as node %d doesn't have odometry set.", to);
6910 ((currentLink.
from() == from && currentLink.
to() == to) || (currentLink.
from() == to && currentLink.
to() == from)))
6914 ui_->pushButton_reject->setEnabled(
true);
6921 currentLink =
iter->second;
6922 ui_->pushButton_reset->setEnabled(
true);
6925 ui_->toolButton_constraint->setEnabled(
true);
6936 if(
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked())
6946 if(refPoses.size() ==
graph.size() &&
length >= 100.0f)
6951 UINFO(
"KITTI t_err = %f %%", t_err);
6952 UINFO(
"KITTI r_err = %f deg/m", r_err);
6955 float translational_rmse = 0.0f;
6956 float translational_mean = 0.0f;
6957 float translational_median = 0.0f;
6958 float translational_std = 0.0f;
6959 float translational_min = 0.0f;
6960 float translational_max = 0.0f;
6961 float rotational_rmse = 0.0f;
6962 float rotational_mean = 0.0f;
6963 float rotational_median = 0.0f;
6964 float rotational_std = 0.0f;
6965 float rotational_min = 0.0f;
6966 float rotational_max = 0.0f;
6973 translational_median,
6985 ui_->label_rmse->setNum(translational_rmse);
6986 UINFO(
"translational_rmse=%f", translational_rmse);
6987 UINFO(
"translational_mean=%f", translational_mean);
6988 UINFO(
"translational_median=%f", translational_median);
6989 UINFO(
"translational_std=%f", translational_std);
6990 UINFO(
"translational_min=%f", translational_min);
6991 UINFO(
"translational_max=%f", translational_max);
6993 UINFO(
"rotational_rmse=%f", rotational_rmse);
6994 UINFO(
"rotational_mean=%f", rotational_mean);
6995 UINFO(
"rotational_median=%f", rotational_median);
6996 UINFO(
"rotational_std=%f", rotational_std);
6997 UINFO(
"rotational_min=%f", rotational_min);
6998 UINFO(
"rotational_max=%f", rotational_max);
7000 if(((
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked()) ||
7001 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked())) &&
7006 iter->second = gtToMap *
iter->second;
7011 std::map<int, rtabmap::Transform> graphFiltered;
7012 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
7013 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
7020 graphFiltered =
graph;
7022 if(
ui_->groupBox_posefiltering->isChecked())
7025 ui_->doubleSpinBox_posefilteringRadius->value(),
7026 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
7029 #ifdef RTABMAP_OCTOMAP
7033 if(
ui_->dockWidget_graphView->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
7036 UINFO(
"Update local maps list...");
7037 std::vector<int> ids =
uKeys(graphFiltered);
7038 for(
unsigned int i=0;
i<ids.size(); ++
i)
7045 else if(ids.at(
i)>0)
7049 cv::Mat ground, obstacles,
empty;
7050 if(
data.gridCellSize()>0.0f)
7052 data.uncompressData(0, 0, 0, 0, &ground, &obstacles, &
empty);
7054 localMaps_.
add(ids.at(
i), ground, obstacles,
empty,
data.gridCellSize()>0.0f?
data.gridCellSize():Parameters::defaultGridCellSize(),
data.gridViewPoint());
7055 if(!ground.empty() || !obstacles.empty())
7061 UINFO(
"Update local maps list... done (%d local maps, graph size=%d)", (
int)combinedLocalMaps.
size(), (
int)
graph.size());
7065 float cellSize = Parameters::defaultGridCellSize();
7071 if(!
ui_->checkBox_wmState->isChecked())
7073 bool allNodesAreInWM =
true;
7074 std::map<int, float> colors;
7079 colors.insert(std::make_pair(
iter->first, 1));
7083 allNodesAreInWM =
false;
7086 if(!allNodesAreInWM)
7088 ui_->graphViewer->updatePosterior(colors, 1, 1);
7091 QGraphicsRectItem * rectScaleItem = 0;
7092 ui_->graphViewer->clearMap();
7094 if(
graph.size() && combinedLocalMaps.
size() &&
7095 (
ui_->graphViewer->isGridMapVisible() ||
ui_->dockWidget_occupancyGridView->isVisible()))
7100 #ifdef RTABMAP_OCTOMAP
7101 if(
ui_->checkBox_octomap->isChecked())
7108 if((
ui_->dockWidget_graphView->isVisible() &&
ui_->graphViewer->isGridMapVisible()) ||
7109 (
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_2d->isChecked()))
7111 bool eroded = Parameters::defaultGridGlobalEroded();
7116 #ifdef RTABMAP_OCTOMAP
7117 if(
ui_->checkBox_octomap->isChecked())
7129 grid.
update(graphFiltered);
7130 if(
ui_->checkBox_grid_showProbMap->isChecked())
7136 map = grid.
getMap(xMin, yMin);
7140 ui_->label_timeGrid->setNum(
double(
time.elapsed())/1000.0);
7145 if(
ui_->dockWidget_graphView->isVisible() &&
ui_->graphViewer->isGridMapVisible())
7147 ui_->graphViewer->updateMap(map8U, cellSize, xMin, yMin);
7149 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_2d->isChecked())
7158 int xLast = map.cols;
7159 int yLast = map.rows;
7160 bool firstSet =
false;
7161 bool lastSet =
false;
7162 for(
int x=0;
x<map.cols && (!firstSet || !lastSet); ++
x)
7164 for(
int y=0;
y<map.rows; ++
y)
7167 if(!firstSet && map.at<
char>(
y,
x) != -1)
7173 int opp = map.cols-(
x+1);
7174 if(!lastSet && map.at<
char>(
y, opp) != -1)
7183 for(
int y=0;
y<map.rows && (!firstSet || !lastSet); ++
y)
7185 for(
int x=0;
x<map.cols; ++
x)
7188 if(!firstSet && map.at<
char>(
y,
x) != -1)
7194 int opp = map.rows-(
y+1);
7195 if(!lastSet && map.at<
char>(map.rows-(
y+1),
x) != -1)
7203 if( (xLast > xFirst && yLast > yFirst) &&
7205 xLast < map.cols-50 ||
7207 yLast < map.rows-50))
7209 rectScaleItem =
ui_->graphViewer->scene()->addRect(
7214 rectScaleItem->setTransform(QTransform::fromScale(cellSize*100.0
f, -cellSize*100.0
f),
true);
7215 rectScaleItem->setRotation(90);
7216 rectScaleItem->setPos(-yMin*100.0
f, -xMin*100.0
f);
7222 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_grid->isChecked())
7224 #ifdef RTABMAP_OCTOMAP
7225 if(
ui_->checkBox_octomap->isChecked())
7232 CloudMap cloudMap(&combinedLocalMaps, parameters);
7234 cloudMap.
update(graphFiltered);
7236 const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & groundCells = cloudMap.
getMapGround();
7237 const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & obstacleCells = cloudMap.
getMapObstacles();
7238 const pcl::PointCloud<pcl::PointXYZ>::Ptr & emptyCells = cloudMap.
getMapEmptyCells();
7241 if(groundCells->size())
7246 QColor(
ui_->lineEdit_groundColor->text()));
7249 if(obstacleCells->size())
7254 QColor(
ui_->lineEdit_obstacleColor->text()));
7257 if(
ui_->checkBox_grid_empty->isChecked() && emptyCells->size())
7262 QColor(
ui_->lineEdit_emptyColor->text()));
7270 #ifdef RTABMAP_GRIDMAP
7272 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
7273 ui_->checkBox_grid_elevation->checkState() != Qt::Unchecked)
7275 GridMap gridMap(&combinedLocalMaps, parameters);
7277 gridMap.
update(graphFiltered);
7278 if(
ui_->checkBox_grid_elevation->checkState() == Qt::PartiallyChecked)
7293 ui_->graphViewer->fitInView(
ui_->graphViewer->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
7294 if(rectScaleItem != 0)
7296 ui_->graphViewer->fitInView(rectScaleItem, Qt::KeepAspectRatio);
7297 ui_->graphViewer->scene()->removeItem(rectScaleItem);
7298 delete rectScaleItem;
7301 ui_->graphViewer->update();
7302 ui_->label_iterations->setNum(
value);
7308 std::map<int, rtabmap::Transform>::const_iterator jterA =
graph.find(
iter->first);
7309 std::map<int, rtabmap::Transform>::const_iterator jterB =
graph.find(
iter->second.to());
7310 if(jterA !=
graph.end() && jterB !=
graph.end())
7317 Eigen::Vector3f
vA,
vB;
7333 if(
ui_->horizontalSlider_rotation->isEnabled())
7335 float theta =
float(
ui_->horizontalSlider_rotation->value())*
M_PI/1800.0f;
7336 ui_->graphViewer->setWorldMapRotation(theta);
7337 ui_->label_rotation->setText(QString::number(
float(-
ui_->horizontalSlider_rotation->value())/10.0f,
'f', 1) +
" deg");
7341 ui_->graphViewer->setWorldMapRotation(0);
7347 ui_->label_loopClosures->clear();
7348 ui_->label_poses->clear();
7349 ui_->label_rmse->clear();
7351 if(sender() ==
ui_->checkBox_alignPosesWithGPS &&
ui_->checkBox_alignPosesWithGPS->isChecked())
7353 ui_->checkBox_alignPosesWithGroundTruth->setChecked(
false);
7355 else if(sender() ==
ui_->checkBox_alignPosesWithGroundTruth &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked())
7357 ui_->checkBox_alignPosesWithGPS->setChecked(
false);
7362 int fromId =
ui_->spinBox_optimizationsFrom->value();
7365 QMessageBox::warning(
this, tr(
""), tr(
"Graph optimization from id (%1) for which node is not linked to graph.\n Minimum=%2, Maximum=%3")
7375 std::map<int, rtabmap::Transform> poses =
odomPoses_;
7378 std::map<int, rtabmap::Transform> wmPoses;
7379 std::vector<int> & wmState =
wmStates_.at(fromId);
7380 for(
unsigned int i=0;
i<wmState.size(); ++
i)
7382 std::map<int, rtabmap::Transform>::iterator
iter = poses.find(wmState[
i]);
7383 if(
iter!=poses.end())
7385 wmPoses.insert(*
iter);
7388 if(!wmPoses.empty())
7394 UWARN(
"Empty WM poses!? Ignoring WM state... (root id=%d, wmState=%d)", fromId, wmState.size());
7401 int currentMapId =
mapIds_.at(fromId);
7402 for(std::map<int, rtabmap::Transform>::iterator
iter=poses.begin();
iter!=poses.end();)
7406 poses.erase(
iter++);
7415 ui_->menuExport_poses->setEnabled(
true);
7416 std::multimap<int, rtabmap::Link> links =
links_;
7422 int currentMapId =
mapIds_.at(fromId);
7423 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end();)
7428 links.erase(
iter++);
7438 if(
ui_->checkBox_ignorePoseCorrection->isChecked())
7440 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
7451 iter->second.from(),
7453 iter->second.type(),
7461 double markerPriorsLinearVariance = Parameters::defaultMarkerPriorsVarianceLinear();
7462 double markerPriorsAngularVariance = Parameters::defaultMarkerPriorsVarianceAngular();
7463 std::map<int, Transform> markerPriors;
7465 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceLinear(), markerPriorsLinearVariance);
7466 UASSERT(markerPriorsLinearVariance>0.0
f);
7467 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceAngular(), markerPriorsAngularVariance);
7468 UASSERT(markerPriorsAngularVariance>0.0
f);
7469 std::string markerPriorsStr;
7472 std::list<std::string> strList =
uSplit(markerPriorsStr,
'|');
7473 for(std::list<std::string>::iterator
iter=strList.begin();
iter!=strList.end(); ++
iter)
7475 std::string markerStr = *
iter;
7476 while(!markerStr.empty() && !
uIsDigit(markerStr[0]))
7478 markerStr.erase(markerStr.begin());
7480 if(!markerStr.empty())
7485 if(!
prior.isNull() &&
id>0)
7487 markerPriors.insert(std::make_pair(-
id,
prior));
7488 UDEBUG(
"Added landmark prior %d: %s",
id,
prior.prettyPrint().c_str());
7492 UERROR(
"Failed to parse element \"%s\" in parameter %s", markerStr.c_str(), Parameters::kMarkerPriors().c_str());
7495 else if(!
iter->empty())
7497 UERROR(
"Failed to parse parameter %s, value=\"%s\"", Parameters::kMarkerPriors().
c_str(),
iter->c_str());
7504 int totalNeighbor = 0;
7505 int totalNeighborMerged = 0;
7506 int totalGlobal = 0;
7507 int totalLocalTime = 0;
7508 int totalLocalSpace = 0;
7510 int totalPriors = 0;
7511 int totalLandmarks = 0;
7512 int totalGravity = 0;
7513 std::multimap<int, int> uniqueLinks;
7514 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end();)
7516 bool isUnique =
iter->second.from() ==
iter->second.to();
7519 uniqueLinks.insert(std::make_pair(
iter->second.from(),
iter->second.to()));
7531 ++totalNeighborMerged;
7535 if(
ui_->checkBox_ignoreGlobalLoop->isChecked())
7537 links.erase(
iter++);
7546 if(
ui_->checkBox_ignoreLocalLoopSpace->isChecked())
7548 links.erase(
iter++);
7557 if(
ui_->checkBox_ignoreLocalLoopTime->isChecked())
7559 links.erase(
iter++);
7568 if(
ui_->checkBox_ignoreUserLoop->isChecked())
7570 links.erase(
iter++);
7579 if(
ui_->checkBox_ignoreLandmarks->isChecked())
7581 links.erase(
iter++);
7585 if(poses.find(
iter->second.from()) != poses.end() && poses.find(
iter->second.to()) == poses.end())
7587 poses.insert(std::make_pair(
iter->second.to(), poses.at(
iter->second.from())*
iter->second.transform()));
7594 int markerId =
iter->second.to();
7595 if(markerPriors.find(markerId) != markerPriors.end())
7597 cv::Mat infMatrix = cv::Mat::eye(6, 6, CV_64FC1);
7598 infMatrix(cv::Range(0,3), cv::Range(0,3)) /= markerPriorsLinearVariance;
7599 infMatrix(cv::Range(3,6), cv::Range(3,6)) /= markerPriorsAngularVariance;
7600 links.insert(std::make_pair(markerId,
Link(markerId, markerId,
Link::kPosePrior, markerPriors.at(markerId), infMatrix)));
7601 UDEBUG(
"Added prior %d : %s (variance: lin=%f ang=%f)", markerId, markerPriors.at(markerId).prettyPrint().c_str(),
7602 markerPriorsLinearVariance, markerPriorsAngularVariance);
7625 ui_->label_loopClosures->setText(tr(
"(%1, %2, %3, %4, %5, %6, %7, %8, %9)")
7627 .
arg(totalNeighborMerged)
7629 .
arg(totalLocalSpace)
7630 .
arg(totalLocalTime)
7633 .
arg(totalLandmarks)
7634 .
arg(totalGravity));
7637 if(
ui_->checkBox_ignoreIntermediateNodes->isVisible() &&
7638 ui_->checkBox_ignoreIntermediateNodes->isChecked())
7640 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
7648 std::multimap<int, Link>::iterator uter = links.find(link.
to());
7649 while(uter != links.end() &&
7650 uter->first==link.
to() &&
7651 uter->second.from()>uter->second.to())
7655 if(uter != links.end())
7657 poses.erase(link.
to());
7658 link = link.
merge(uter->second, uter->second.type());
7659 links.erase(uter->first);
7667 iter->second = link;
7672 bool applyRotation = sender() ==
ui_->pushButton_applyRotation;
7675 float xMin, yMin, cellSize;
7679 QMessageBox::StandardButton r = QMessageBox::question(
this,
7680 tr(
"Rotate Optimized Graph"),
7681 tr(
"There is a 2D occupancy grid or mesh already saved in "
7682 "database. Applying rotation will clear them (they can be "
7683 "regenerated later from File menu options). "
7684 "Do you want to continue?"),
7685 QMessageBox::Cancel | QMessageBox::Yes,
7686 QMessageBox::Cancel);
7687 if(r != QMessageBox::Yes)
7689 applyRotation =
false;
7694 std::map<int, Transform> optPoses;
7697 ui_->comboBox_optimizationFlavor->currentIndex() == 2)
7700 if(optPoses.empty())
7702 ui_->comboBox_optimizationFlavor->setCurrentIndex(0);
7703 QMessageBox::warning(
this, tr(
"Optimization Flavor"),
7704 tr(
"There is no local optimized graph in the database, "
7705 "falling back to global iterative optimization."));
7710 ui_->comboBox_optimizationFlavor->currentIndex() != 2)
7712 if(
ui_->horizontalSlider_rotation->value()!=0 && applyRotation)
7714 float theta =
float(-
ui_->horizontalSlider_rotation->value())*
M_PI/1800.0f;
7716 poses.at(fromId) = rotT * poses.at(fromId);
7723 std::map<int, rtabmap::Transform> posesOut;
7724 std::multimap<int, rtabmap::Link> linksOut;
7725 UINFO(
"Get connected graph from %d (%d poses, %d links)", fromId, (
int)poses.size(), (
int)links.size());
7732 UINFO(
"Connected graph of %d poses and %d links", (
int)posesOut.size(), (
int)linksOut.size());
7735 std::map<int, rtabmap::Transform> finalPoses = optimizer->
optimize(fromId, posesOut, linksOut,
ui_->comboBox_optimizationFlavor->currentIndex()==0?&
graphes_:0);
7736 ui_->label_timeOptimization->setNum(
double(
time.elapsed())/1000.0);
7738 if(posesOut.size() && finalPoses.empty())
7740 UWARN(
"Optimization failed... (poses=%d, links=%d).", (
int)posesOut.size(), (
int)linksOut.size());
7743 QMessageBox::warning(
this, tr(
"Graph optimization error!"), tr(
"Graph optimization has failed. See the terminal for potential errors. "
7744 "Give it a try with %1=0 and %2=true.").
arg(Parameters::kOptimizerStrategy().
c_str()).
arg(Parameters::kOptimizerVarianceIgnored().
c_str()));
7748 QMessageBox::warning(
this, tr(
"Graph optimization error!"), tr(
"Graph optimization has failed. See the terminal for potential errors."));
7751 ui_->label_poses->setNum((
int)finalPoses.size());
7755 if(applyRotation && !finalPoses.empty())
7757 ui_->comboBox_optimizationFlavor->setCurrentIndex(2);
7760 if(lastLocalizationPose.
isNull())
7763 lastLocalizationPose = finalPoses.rbegin()->second;
7767 float xMin, yMin, cellSize;
7773 QMessageBox::StandardButton r = QMessageBox::question(
this,
7774 tr(
"Rotate Optimized Graph"),
7775 tr(
"Optimized graph has been rotated and saved back to database. "
7776 "Note that 2D occupancy grid and mesh have been cleared (if set). "
7777 "Do you want to regenerate the 2D occupancy grid now "
7778 "(can be done later from File menu)?"),
7779 QMessageBox::Ignore | QMessageBox::Yes,
7781 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
7782 ui_->actionExport_saved_2D_map->setEnabled(
false);
7783 ui_->actionImport_2D_map->setEnabled(
false);
7784 ui_->actionView_optimized_mesh->setEnabled(
false);
7785 ui_->actionExport_optimized_mesh->setEnabled(
false);
7786 if(r == QMessageBox::Yes)
7795 if(
ui_->comboBox_optimizationFlavor->currentIndex() == 2)
7800 ui_->label_timeOptimization->setNum(0);
7801 ui_->label_poses->setNum((
int)optPoses.size());
7804 ui_->horizontalSlider_rotation->setEnabled(
false);
7805 ui_->pushButton_applyRotation->setEnabled(
false);
7806 ui_->spinBox_optimizationsFrom->setEnabled(
false);
7807 ui_->checkBox_spanAllMaps->setEnabled(
false);
7808 ui_->checkBox_wmState->setEnabled(
false);
7809 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
7810 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
7811 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
7812 ui_->checkBox_ignoreIntermediateNodes->setEnabled(
false);
7817 ui_->pushButton_applyRotation->setEnabled(
true);
7818 ui_->horizontalSlider_rotation->setEnabled(
true);
7819 ui_->spinBox_optimizationsFrom->setEnabled(
true);
7820 ui_->checkBox_spanAllMaps->setEnabled(
true);
7821 ui_->checkBox_wmState->setEnabled(
true);
7822 ui_->checkBox_alignPosesWithGPS->setEnabled(
ui_->checkBox_alignPosesWithGPS->isVisible());
7823 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
ui_->checkBox_alignPosesWithGroundTruth->isVisible());
7824 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
ui_->checkBox_alignScansCloudsWithGroundTruth->isVisible());
7825 ui_->checkBox_ignoreIntermediateNodes->setEnabled(
true);
7831 if(
ui_->doubleSpinBox_optimizationScale->value()!=-1.0)
7836 for(std::map<int, Transform>::iterator jter=
iter->begin(); jter!=
iter->end(); ++jter)
7838 jter->second = jter->second.clone();
7839 jter->second.x() *=
ui_->doubleSpinBox_optimizationScale->value();
7840 jter->second.y() *=
ui_->doubleSpinBox_optimizationScale->value();
7841 jter->second.z() *=
ui_->doubleSpinBox_optimizationScale->value();
7846 ui_->horizontalSlider_iterations->setMaximum((
int)
graphes_.size()-1);
7847 ui_->horizontalSlider_iterations->setValue((
int)
graphes_.size()-1);
7848 ui_->horizontalSlider_iterations->setEnabled(
true);
7849 ui_->spinBox_optimizationsFrom->setEnabled(
true);
7854 ui_->horizontalSlider_iterations->setEnabled(
false);
7855 ui_->spinBox_optimizationsFrom->setEnabled(
false);
7861 if(sender() ==
ui_->checkBox_grid_2d && !
ui_->checkBox_grid_2d->isChecked())
7869 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
7870 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7871 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7872 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7873 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
7874 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7875 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7876 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7885 #ifdef RTABMAP_OCTOMAP
7886 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
7887 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7888 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7889 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7890 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
7891 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7892 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7893 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7895 if(
ui_->checkBox_octomap->isChecked())
7907 if(
ui_->comboBox_octomap_rendering_type->currentIndex()>0)
7913 pcl::IndicesPtr obstacles(
new std::vector<int>);
7914 pcl::IndicesPtr
empty(
new std::vector<int>);
7915 pcl::IndicesPtr ground(
new std::vector<int>);
7916 pcl::IndicesPtr frontiers(
new std::vector<int>);
7917 std::vector<double> prob;
7919 ui_->spinBox_grid_depth->value(),
7929 pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7930 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
7934 pcl::PointCloud<pcl::PointXYZRGB>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7935 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
7941 pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7942 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
7946 pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7947 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
7952 if(
ui_->checkBox_grid_empty->isChecked())
7954 if(prob.size()==cloud->size())
7956 float occThr = Parameters::defaultGridGlobalOccupancyThr();
7957 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridGlobalOccupancyThr(), occThr);
7958 pcl::PointCloud<pcl::PointXYZRGB>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7959 emptyCloud->resize(
empty->size());
7960 for(
unsigned int i=0;
i<
empty->size(); ++
i)
7962 emptyCloud->points.at(
i).x = cloud->points.at(
empty->at(
i)).x;
7963 emptyCloud->points.at(
i).y = cloud->points.at(
empty->at(
i)).y;
7964 emptyCloud->points.at(
i).z = cloud->points.at(
empty->at(
i)).z;
7965 QColor hsv = QColor::fromHsv(
int(prob.at(
empty->at(
i))/occThr*240.0), 255, 255, 255);
7966 QRgb color = hsv.rgb();
7967 emptyCloud->points.at(
i).r = qRed(color);
7968 emptyCloud->points.at(
i).g = qGreen(color);
7969 emptyCloud->points.at(
i).b = qBlue(color);
7977 pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7978 pcl::copyPointCloud(*cloud, *
empty, *emptyCloud);
7985 if(
ui_->checkBox_grid_frontiers->isChecked())
7987 pcl::PointCloud<pcl::PointXYZ>::Ptr frontiersCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7988 pcl::copyPointCloud(*cloud, *frontiers, *frontiersCloud);
7996 if(
ui_->dockWidget_view3d->isVisible() &&
ui_->checkBox_showGrid->isChecked())
8010 link = findIter->second;
8017 link = findIter->second;
8022 if(findIter !=
links_.end())
8024 link = findIter->second;
8038 int from =
ids_.at(
ui_->horizontalSlider_A->value());
8039 int to =
ids_.at(
ui_->horizontalSlider_B->value());
8045 UDEBUG(
"%d -> %d", from, to);
8046 bool switchedIds =
false;
8049 UWARN(
"Cannot refine link to same node");
8057 UERROR(
"Not found link! (%d->%d)", from, to);
8061 UDEBUG(
"%d -> %d (type=%d)", currentLink.
from(), currentLink.
to(), currentLink.
type());
8063 if(
ui_->checkBox_showOptimized->isChecked() &&
8066 (
int)
graphes_.size()-1 ==
ui_->horizontalSlider_iterations->maximum())
8071 std::map<int, rtabmap::Transform>::iterator iterFrom =
graph.find(currentLink.
from());
8072 std::map<int, rtabmap::Transform>::iterator iterTo =
graph.find(currentLink.
to());
8073 if(iterFrom !=
graph.end() && iterTo !=
graph.end())
8080 else if(
ui_->checkBox_ignorePoseCorrection->isChecked() &&
8103 UERROR(
"Signature %d not found!", currentLink.
from());
8113 std::map<int, rtabmap::Transform> scanPoses;
8117 userData.type() == CV_8SC1 &&
8118 userData.rows == 1 &&
8119 userData.cols >= 8 &&
8120 userData.at<
char>(userData.cols-1) == 0 &&
8121 memcmp(userData.data,
"SCANS:", 6) == 0 &&
8122 currentLink.
from() > currentLink.
to())
8124 std::string scansStr = (
const char *)userData.data;
8125 UINFO(
"Detected \"%s\" in links's user data", scansStr.c_str());
8126 if(!scansStr.empty())
8128 std::list<std::string> strs =
uSplit(scansStr,
':');
8129 if(strs.size() == 2)
8131 std::list<std::string> strIds =
uSplit(strs.rbegin()->c_str(),
';');
8132 for(std::list<std::string>::iterator
iter=strIds.begin();
iter!=strIds.end(); ++
iter)
8134 int id = atoi(
iter->c_str());
8141 UERROR(
"Not found %d node!",
id);
8147 if(scanPoses.size()>1)
8153 std::map<int, rtabmap::Transform> posesOut;
8154 std::multimap<int, rtabmap::Link> linksOut;
8162 if(scanPoses.size() != posesOut.size())
8164 UWARN(
"Scan poses input and output are different! %d vs %d", (
int)scanPoses.size(), (
int)posesOut.size());
8165 UWARN(
"Input poses: ");
8166 for(std::map<int, Transform>::iterator
iter=scanPoses.begin();
iter!=scanPoses.end(); ++
iter)
8170 UWARN(
"Input links: ");
8172 for(std::multimap<int, Link>::iterator
iter=modifiedLinks.begin();
iter!=modifiedLinks.end(); ++
iter)
8178 scanPoses = optimizer->
optimize(currentLink.
to(), posesOut, linksOut);
8181 std::map<int, Transform> filteredScanPoses = scanPoses;
8182 float proximityFilteringRadius = 0.0f;
8183 Parameters::parse(parameters, Parameters::kRGBDProximityPathFilteringRadius(), proximityFilteringRadius);
8184 if(scanPoses.size() > 2 && proximityFilteringRadius > 0.0f)
8189 filteredScanPoses.insert(*scanPoses.find(currentLink.
to()));
8196 int maxPoints = fromScan.
size();
8199 UWARN(
"From scan %d is empty!", fromS->
id());
8201 pcl::PointCloud<pcl::PointXYZ>::Ptr assembledToClouds(
new pcl::PointCloud<pcl::PointXYZ>);
8202 pcl::PointCloud<pcl::PointNormal>::Ptr assembledToNormalClouds(
new pcl::PointCloud<pcl::PointNormal>);
8203 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledToIClouds(
new pcl::PointCloud<pcl::PointXYZI>);
8204 pcl::PointCloud<pcl::PointXYZINormal>::Ptr assembledToNormalIClouds(
new pcl::PointCloud<pcl::PointXYZINormal>);
8205 pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledToRGBClouds(
new pcl::PointCloud<pcl::PointXYZRGB>);
8206 pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr assembledToNormalRGBClouds(
new pcl::PointCloud<pcl::PointXYZRGBNormal>);
8207 for(std::map<int, Transform>::const_iterator
iter = filteredScanPoses.begin();
iter!=filteredScanPoses.end(); ++
iter)
8209 if(
iter->first != currentLink.
from())
8213 if(!
data.laserScanCompressed().isEmpty())
8216 data.uncompressData(0, 0, &scan);
8259 if(scan.
size() > maxPoints)
8261 maxPoints = scan.
size();
8266 UWARN(
"scan format of %d is not the same than from scan %d: %d vs %d",
data.id(), fromS->
id(), scan.
format(), fromScan.
format());
8271 UWARN(
"Laser scan not found for signature %d",
iter->first);
8277 if(assembledToNormalClouds->size())
8281 else if(assembledToClouds->size())
8285 else if(assembledToNormalIClouds->size())
8289 else if(assembledToIClouds->size())
8293 else if(assembledToNormalRGBClouds->size())
8298 else if(assembledToRGBClouds->size())
8305 UWARN(
"Assembled scan is empty!");
8321 info.covariance*=100.0;
8329 UERROR(
"Signature %d not found!", currentLink.
to());
8334 bool reextractVisualFeatures =
uStr2Bool(parameters.at(Parameters::kRGBDLoopClosureReextractFeatures()));
8338 reextractVisualFeatures ||
8351 if(reextractVisualFeatures)
8354 fromS->
sensorData().
setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
8361 if(
ui_->checkBox_icp_from_depth->isChecked())
8364 cv::Mat tmpA, tmpB, tmpC, tmpD;
8369 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8370 ui_->doubleSpinBox_icp_maxDepth->value(),
8371 ui_->doubleSpinBox_icp_minDepth->value(),
8373 ui_->parameters_toolbox->getParameters());
8376 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8377 ui_->doubleSpinBox_icp_maxDepth->value(),
8378 ui_->doubleSpinBox_icp_minDepth->value(),
8380 ui_->parameters_toolbox->getParameters());
8382 if(cloudFrom->empty() && cloudTo->empty())
8384 std::string
msg =
"Option to generate scan from depth is checked (GUI Parameters->Refine), but "
8385 "resulting clouds from depth are empty. Transformation estimation will likely "
8386 "fails. Uncheck the parameter to use laser scans.";
8390 QMessageBox::warning(
this,
8392 tr(
"%1").
arg(
msg.c_str()));
8397 UWARN(
"There are laser scans in data, but generate laser scan from "
8398 "depth image option is activated (GUI Parameters->Refine). "
8399 "Ignoring saved laser scans...");
8402 int maxLaserScans = cloudFrom->size();
8416 cv::Mat tmpA, tmpB, tmpC, tmpD;
8421 UINFO(
"Uncompress time: %f s",
timer.ticks());
8423 if(fromS->
id() < toS->
id())
8435 UINFO(
"(%d ->%d) Registration time: %f s", currentLink.
from(), currentLink.
to(),
timer.ticks());
8441 if(
info.covariance.at<
double>(0,0)<=0.0)
8443 info.covariance = cv::Mat::eye(6,6,CV_64FC1)*0.0001;
8457 if(
iter->second.to() == currentLink.
to() &&
8458 iter->second.type() == currentLink.
type())
8460 iter->second = newLink;
8477 if(!silent &&
ui_->dockWidget_constraints->isVisible())
8479 if(toS && fromS->
id() > 0 && toS->
id() > 0)
8482 std::multimap<int, cv::KeyPoint> keypointsFrom;
8483 std::multimap<int, cv::KeyPoint> keypointsTo;
8488 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8495 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8521 if(toS && fromS->
id() > 0 && toS->
id() > 0)
8524 std::multimap<int, cv::KeyPoint> keypointsFrom;
8525 std::multimap<int, cv::KeyPoint> keypointsTo;
8530 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8537 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8553 QMessageBox::warning(
this,
8555 tr(
"Cannot find a transformation between nodes %1 and %2: %3").
arg(currentLink.
from()).
arg(currentLink.
to()).arg(
info.rejectedMsg.c_str()));
8563 int from =
ids_.at(
ui_->horizontalSlider_A->value());
8564 int to =
ids_.at(
ui_->horizontalSlider_B->value());
8570 bool switchedIds =
false;
8573 UWARN(
"Cannot add link to same node");
8583 std::list<Signature*> signatures;
8598 bool loopCovLimited = Parameters::defaultRGBDLoopCovLimited();
8599 Parameters::parse(parameters, Parameters::kRGBDLoopCovLimited(), loopCovLimited);
8609 ids.push_back(from);
8612 if(signatures.size() != 2)
8614 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
8620 fromS = *signatures.begin();
8621 toS = *signatures.rbegin();
8623 bool reextractVisualFeatures =
uStr2Bool(parameters.at(Parameters::kRGBDLoopClosureReextractFeatures()));
8626 reextractVisualFeatures ||
8634 if(reextractVisualFeatures)
8637 fromS->
sensorData().
setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
8646 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8647 ui_->doubleSpinBox_icp_maxDepth->value(),
8648 ui_->doubleSpinBox_icp_minDepth->value(),
8650 ui_->parameters_toolbox->getParameters());
8653 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8654 ui_->doubleSpinBox_icp_maxDepth->value(),
8655 ui_->doubleSpinBox_icp_minDepth->value(),
8657 ui_->parameters_toolbox->getParameters());
8659 if(cloudFrom->empty() && cloudTo->empty())
8661 std::string
msg =
"Option to generate scan from depth is checked (GUI Parameters->Refine), but "
8662 "resulting clouds from depth are empty. Transformation estimation will likely "
8663 "fails. Uncheck the parameter to use laser scans.";
8667 QMessageBox::warning(
this,
8669 tr(
"%1").
arg(
msg.c_str()));
8674 UWARN(
"There are laser scans in data, but generate laser scan from "
8675 "depth image option is activated (GUI Parameters->Refine). Ignoring saved laser scans...");
8678 int maxLaserScans = cloudFrom->size();
8683 else if(!reextractVisualFeatures && fromS->
getWords().empty() && toS->
getWords().empty())
8685 std::string
msg =
uFormat(
"\"%s\" is false and signatures (%d and %d) don't have words, "
8686 "registration will not be possible. Set \"%s\" to true.",
8687 Parameters::kRGBDLoopClosureReextractFeatures().
c_str(),
8690 Parameters::kRGBDLoopClosureReextractFeatures().c_str());
8694 QMessageBox::warning(
this,
8696 tr(
"%1").
arg(
msg.c_str()));
8701 bool guessFromGraphRejected =
false;
8707 std::map<int, Transform> optimizedPoses =
graphes_.back();
8708 if(optimizedPoses.size() > 0)
8710 std::map<int, Transform>::iterator fromIter = optimizedPoses.find(from);
8711 std::map<int, Transform>::iterator toIter = optimizedPoses.find(to);
8712 if(fromIter != optimizedPoses.end() &&
8713 toIter != optimizedPoses.end())
8717 if(QMessageBox::question(
this,
8718 tr(
"Add constraint from optimized graph"),
8719 tr(
"Registration is done without vision (see %1 parameter), "
8720 "do you want to use a guess from the optimized graph?"
8722 "\n\nOtherwise, if "
8723 "the database has images, it is recommended to use %2=2 instead so that "
8724 "the guess can be found visually.")
8725 .
arg(Parameters::kRegStrategy().
c_str()).
arg(Parameters::kRegStrategy().
c_str()),
8726 QMessageBox::Yes | QMessageBox::No,
8727 QMessageBox::Yes) == QMessageBox::Yes)
8729 guess = fromIter->second.
inverse() * toIter->second;
8733 guessFromGraphRejected =
true;
8736 else if(silentlyUseOptimizedGraphAsGuess)
8738 guess = fromIter->second.
inverse() * toIter->second;
8743 if(guess.
isNull() && !silent && !guessFromGraphRejected)
8745 if(QMessageBox::question(
this,
8746 tr(
"Add constraint without guess"),
8747 tr(
"Registration is done without vision (see %1 parameter) and we cannot (or we don't want to) find relative "
8748 "transformation between the nodes with the current graph. Do you want to use an identity "
8749 "transform for ICP guess? "
8751 "\n\nOtherwise, if the database has images, it is recommended to use %2=2 "
8752 "instead so that the guess can be found visually.")
8753 .
arg(Parameters::kRegStrategy().
c_str()).
arg(Parameters::kRegStrategy().
c_str()),
8754 QMessageBox::Yes | QMessageBox::Abort,
8755 QMessageBox::Abort) == QMessageBox::Yes)
8761 guessFromGraphRejected =
true;
8783 cv::Mat information =
info.covariance.inv();
8784 if(odomMaxInf.size() == 6 && information.cols==6 && information.rows==6)
8786 for(
int i=0;
i<6; ++
i)
8788 if(information.at<
double>(
i,
i) > odomMaxInf[
i])
8790 information.at<
double>(
i,
i) = odomMaxInf[
i];
8797 else if(!silent && !guessFromGraphRejected)
8799 QMessageBox::StandardButton button = QMessageBox::warning(
this,
8801 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()),
8802 QMessageBox::Yes | QMessageBox::No,
8804 if(button == QMessageBox::Yes)
8816 bool updateConstraints = newLink.
isValid();
8817 float maxOptimizationError =
uStr2Float(
ui_->parameters_toolbox->getParameters().at(Parameters::kRGBDOptimizeMaxError()));
8819 maxOptimizationError > 0.0f &&
8820 uStr2Int(
ui_->parameters_toolbox->getParameters().at(Parameters::kOptimizerIterations())) > 0.0f)
8822 int fromId = newLink.
from();
8824 linksIn.insert(std::make_pair(newLink.
from(), newLink));
8825 const Link * maxLinearLink = 0;
8826 const Link * maxAngularLink = 0;
8827 float maxLinearErrorRatio = 0.0f;
8828 float maxAngularErrorRatio = 0.0f;
8830 std::map<int, Transform> poses;
8831 std::multimap<int, Link> links;
8839 const std::map<int, Transform> & optimizedPoses =
graphes_.back();
8840 for(std::map<int, Transform>::iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
8842 if(optimizedPoses.find(
iter->first) != optimizedPoses.end())
8844 iter->second = optimizedPoses.at(
iter->first);
8848 UASSERT(poses.find(fromId) != poses.end());
8849 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());
8850 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());
8852 std::map<int, Transform> posesIn = poses;
8853 poses = optimizer->
optimize(fromId, posesIn, links);
8854 if(posesIn.size() && poses.empty())
8856 UWARN(
"Optimization failed... (poses=%d, links=%d).", (
int)posesIn.size(), (
int)links.size());
8861 float maxLinearError = 0.0f;
8862 float maxAngularError = 0.0f;
8866 maxLinearErrorRatio,
8867 maxAngularErrorRatio,
8874 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()));
8875 if(maxLinearErrorRatio > maxOptimizationError)
8877 msg =
uFormat(
"Rejecting edge %d->%d because "
8878 "graph error is too large (abs=%f m) after optimization (ratio %f for edge %d->%d, stddev=%f m). "
8883 maxLinearErrorRatio,
8884 maxLinearLink->
from(),
8885 maxLinearLink->
to(),
8887 Parameters::kRGBDOptimizeMaxError().c_str(),
8888 maxOptimizationError);
8893 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()));
8894 if(maxAngularErrorRatio > maxOptimizationError)
8896 msg =
uFormat(
"Rejecting edge %d->%d because "
8897 "graph error is too large (abs=%f deg) after optimization (ratio %f for edge %d->%d, stddev=%f deg). "
8901 maxAngularError*180.0f/CV_PI,
8902 maxAngularErrorRatio,
8903 maxAngularLink->
from(),
8904 maxAngularLink->
to(),
8906 Parameters::kRGBDOptimizeMaxError().c_str(),
8907 maxOptimizationError);
8913 msg =
uFormat(
"Rejecting edge %d->%d because graph optimization has failed!",
8922 QMessageBox::warning(
this,
8924 tr(
"%1").
arg(
msg.c_str()));
8927 updateConstraints =
false;
8930 if(updateConstraints && silent && !
graphes_.empty() &&
graphes_.back().size() == poses.size())
8936 if(updateConstraints)
8945 if(newLink.
from() < newLink.
to())
8957 if((updateConstraints && newLink.
from() > newLink.
to()) || (!updateConstraints && switchedIds))
8964 if(updateConstraints)
8971 std::multimap<int, cv::KeyPoint> keypointsFrom;
8972 std::multimap<int, cv::KeyPoint> keypointsTo;
8977 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8984 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8991 else if(updateConstraints)
8998 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
9003 return updateConstraints;
9008 int from =
ids_.at(
ui_->horizontalSlider_A->value());
9009 int to =
ids_.at(
ui_->horizontalSlider_B->value());
9012 int position =
ui_->horizontalSlider_loops->value();
9027 UWARN(
"Cannot reset link to same node");
9044 int priorId = sender() ==
ui_->toolButton_remove_priorA?
ids_.at(
ui_->horizontalSlider_A->value()):
9045 sender() ==
ui_->toolButton_remove_priorB?
ids_.at(
ui_->horizontalSlider_B->value()):0;
9047 int from = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_A->value());
9048 int to = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_B->value());
9051 int position =
ui_->horizontalSlider_loops->value();
9064 if(priorId==0 && from == to)
9066 UWARN(
"Cannot reject link to same node");
9070 bool removed =
false;
9073 std::multimap<int, Link>::iterator
iter;
9079 QMessageBox::StandardButton button = QMessageBox::warning(
this, tr(
"Reject link"),
9080 tr(
"Removing the neighbor link %1->%2 will split the graph. Do you want to continue?").
arg(from).
arg(to),
9081 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
9082 if(button != QMessageBox::Yes)
9113 bool priorsIgnored = Parameters::defaultOptimizerPriorsIgnored();
9114 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kOptimizerPriorsIgnored(), priorsIgnored);
9115 int indexA =
ui_->horizontalSlider_A->value();
9116 int indexB =
ui_->horizontalSlider_B->value();
9121 if(
ui_->horizontalSlider_A->value() != indexA)
9122 ui_->horizontalSlider_A->setValue(indexA);
9125 if(
ui_->horizontalSlider_B->value() != indexB)
9126 ui_->horizontalSlider_B->setValue(indexB);
9134 const std::multimap<int, rtabmap::Link> & edgeConstraints)
9138 std::multimap<int, rtabmap::Link> links;
9139 for(std::multimap<int, rtabmap::Link>::const_iterator
iter=edgeConstraints.begin();
9140 iter!=edgeConstraints.end();
9143 std::multimap<int, rtabmap::Link>::iterator findIter;
9148 UDEBUG(
"Removed link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9155 links.insert(*findIter);
9156 UDEBUG(
"Updated link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9160 links.insert(*
iter);
9164 for(std::multimap<int, rtabmap::Link>::const_iterator
iter=
linksAdded_.begin();
9171 links.insert(*findIter);
9172 UDEBUG(
"Added refined link (%d->%d, %d)", findIter->second.from(), findIter->second.to(), findIter->second.type());
9176 UDEBUG(
"Added link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9177 links.insert(*
iter);
9185 UDEBUG(
"%d %d", from, to);
9188 int position =
ui_->horizontalSlider_loops->value();
9189 std::multimap<int, Link> linksSortedByParents;
9190 for(std::multimap<int, rtabmap::Link>::iterator
iter = links.begin();
iter!=links.end(); ++
iter)
9192 if(
iter->second.to() >
iter->second.from())
9194 linksSortedByParents.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
9196 else if(
iter->second.to() !=
iter->second.from())
9198 linksSortedByParents.insert(*
iter);
9201 for(std::multimap<int, rtabmap::Link>::iterator
iter = linksSortedByParents.begin();
iter!=linksSortedByParents.end(); ++
iter)
9203 if(!
iter->second.transform().isNull())
9208 if((
iter->second.from() == from &&
iter->second.to() == to) ||
9209 (
iter->second.to() == from &&
iter->second.from() == to))
9218 UERROR(
"Transform null for link from %d to %d",
iter->first,
iter->second.to());
9229 ui_->horizontalSlider_loops->setMinimum(0);
9230 ui_->horizontalSlider_loops->setMaximum(
loopLinks_.size()-1);
9231 ui_->horizontalSlider_loops->setEnabled(
true);
9232 if(
position !=
ui_->horizontalSlider_loops->value())
9243 ui_->horizontalSlider_loops->setEnabled(
false);
9254 for(QStringList::const_iterator
iter=parametersChanged.constBegin();
9258 QString group =
iter->split(
'/').first();