30 #include "ui_DatabaseViewer.h"
31 #include <QMessageBox>
32 #include <QFileDialog>
33 #include <QInputDialog>
34 #include <QColorDialog>
35 #include <QGraphicsLineItem>
36 #include <QtGui/QCloseEvent>
37 #include <QGraphicsOpacityEffect>
38 #include <QtCore/QBuffer>
39 #include <QtCore/QTextStream>
40 #include <QtCore/QDateTime>
41 #include <QtCore/QSettings>
46 #include <opencv2/core/core_c.h>
47 #include <opencv2/imgproc/types_c.h>
48 #include <opencv2/highgui/highgui.hpp>
90 #include <pcl/io/pcd_io.h>
91 #include <pcl/io/ply_io.h>
92 #include <pcl/io/obj_io.h>
93 #include <pcl/filters/voxel_grid.h>
94 #include <pcl/filters/crop_box.h>
95 #include <pcl/common/transforms.h>
96 #include <pcl/common/common.h>
99 #ifdef RTABMAP_OCTOMAP
103 #ifdef RTABMAP_GRIDMAP
114 editDepthDialog_(new QDialog(
this)),
115 editMapDialog_(new QDialog(
this)),
117 savedMaximized_(
false),
120 infoReducedGraph_(
false),
131 ui_ =
new Ui_DatabaseViewer();
133 ui_->buttonBox->setVisible(
false);
134 connect(
ui_->buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()),
this, SLOT(close()));
136 ui_->comboBox_logger_level->setVisible(parent==0);
137 ui_->label_logger_level->setVisible(parent==0);
138 connect(
ui_->comboBox_logger_level, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
updateLoggerLevel()));
139 connect(
ui_->actionVertical_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
setupMainLayout(
bool)));
144 vLayout->setContentsMargins(0,0,0,0);
145 vLayout->setSpacing(0);
147 QDialogButtonBox * buttonBox =
new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Reset,
Qt::Horizontal,
editDepthDialog_);
148 vLayout->addWidget(buttonBox);
151 connect(buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()),
editDepthArea_, SLOT(resetChanges()));
158 vLayout->setContentsMargins(0,0,0,0);
159 vLayout->setSpacing(0);
161 buttonBox =
new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Reset,
Qt::Horizontal,
editMapDialog_);
162 vLayout->addWidget(buttonBox);
163 connect(buttonBox, SIGNAL(accepted()),
editMapDialog_, SLOT(accept()));
164 connect(buttonBox, SIGNAL(rejected()),
editMapDialog_, SLOT(reject()));
165 connect(buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()),
editMapArea_, SLOT(resetChanges()));
169 QString title(
"RTAB-Map Database Viewer[*]");
170 this->setWindowTitle(title);
172 ui_->dockWidget_constraints->setVisible(
false);
173 ui_->dockWidget_graphView->setVisible(
false);
174 ui_->dockWidget_occupancyGridView->setVisible(
false);
175 ui_->dockWidget_guiparameters->setVisible(
false);
176 ui_->dockWidget_coreparameters->setVisible(
false);
177 ui_->dockWidget_info->setVisible(
false);
178 ui_->dockWidget_stereoView->setVisible(
false);
179 ui_->dockWidget_view3d->setVisible(
false);
180 ui_->dockWidget_statistics->setVisible(
false);
200 ui_->graphicsView_stereo->setAlpha(255);
202 #ifndef RTABMAP_OCTOMAP
203 ui_->checkBox_octomap->setEnabled(
false);
204 ui_->checkBox_octomap->setChecked(
false);
207 #ifndef RTABMAP_GRIDMAP
208 ui_->checkBox_showElevation->setEnabled(
false);
209 ui_->checkBox_showElevation->setChecked(
false);
210 ui_->checkBox_grid_elevation->setEnabled(
false);
211 ui_->checkBox_grid_elevation->setChecked(
false);
242 ui_->parameters_toolbox->setupUi(parameters);
248 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
249 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
250 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
251 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
252 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
253 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
254 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
255 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
257 ui_->menuView->addAction(
ui_->dockWidget_constraints->toggleViewAction());
258 ui_->menuView->addAction(
ui_->dockWidget_graphView->toggleViewAction());
259 ui_->menuView->addAction(
ui_->dockWidget_occupancyGridView->toggleViewAction());
260 ui_->menuView->addAction(
ui_->dockWidget_stereoView->toggleViewAction());
261 ui_->menuView->addAction(
ui_->dockWidget_view3d->toggleViewAction());
262 ui_->menuView->addAction(
ui_->dockWidget_guiparameters->toggleViewAction());
263 ui_->menuView->addAction(
ui_->dockWidget_coreparameters->toggleViewAction());
264 ui_->menuView->addAction(
ui_->dockWidget_info->toggleViewAction());
265 ui_->menuView->addAction(
ui_->dockWidget_statistics->toggleViewAction());
266 connect(
ui_->dockWidget_graphView->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateGraphView()));
267 connect(
ui_->dockWidget_occupancyGridView->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateGraphView()));
268 connect(
ui_->dockWidget_statistics->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateStatistics()));
269 connect(
ui_->dockWidget_info->toggleViewAction(), SIGNAL(triggered()),
this, SLOT(
updateInfo()));
272 connect(
ui_->graphViewer, SIGNAL(linkSelected(
int,
int)),
this , SLOT(
graphLinkSelected(
int,
int)));
274 connect(
ui_->parameters_toolbox, SIGNAL(parametersChanged(
const QStringList &)),
this, SLOT(
notifyParametersChanged(
const QStringList &)));
276 connect(
ui_->actionQuit, SIGNAL(triggered()),
this, SLOT(close()));
278 ui_->actionOpen_database->setEnabled(
true);
279 ui_->actionClose_database->setEnabled(
false);
282 ui_->actionSave_config->setShortcut(QKeySequence::Save);
283 connect(
ui_->actionSave_config, SIGNAL(triggered()),
this, SLOT(
writeSettings()));
284 connect(
ui_->actionOpen_database, SIGNAL(triggered()),
this, SLOT(
openDatabase()));
285 connect(
ui_->actionClose_database, SIGNAL(triggered()),
this, SLOT(
closeDatabase()));
286 connect(
ui_->actionDatabase_recovery, SIGNAL(triggered()),
this, SLOT(
recoverDatabase()));
288 connect(
ui_->actionExtract_images, SIGNAL(triggered()),
this, SLOT(
extractImages()));
289 connect(
ui_->actionEdit_depth_image, SIGNAL(triggered()),
this, SLOT(
editDepthImage()));
290 connect(
ui_->actionGenerate_graph_dot, SIGNAL(triggered()),
this, SLOT(
generateGraph()));
292 connect(
ui_->actionRaw_format_txt, SIGNAL(triggered()),
this , SLOT(
exportPosesRaw()));
296 connect(
ui_->actionKITTI_format_txt, SIGNAL(triggered()),
this , SLOT(
exportPosesKITTI()));
299 connect(
ui_->actionPoses_KML, SIGNAL(triggered()),
this , SLOT(
exportPosesKML()));
300 connect(
ui_->actionGPS_TXT, SIGNAL(triggered()),
this , SLOT(
exportGPS_TXT()));
301 connect(
ui_->actionGPS_KML, SIGNAL(triggered()),
this , SLOT(
exportGPS_KML()));
302 connect(
ui_->actionEdit_optimized_2D_map, SIGNAL(triggered()),
this , SLOT(
editSaved2DMap()));
303 connect(
ui_->actionExport_saved_2D_map, SIGNAL(triggered()),
this , SLOT(
exportSaved2DMap()));
304 connect(
ui_->actionImport_2D_map, SIGNAL(triggered()),
this , SLOT(
import2DMap()));
305 connect(
ui_->actionRegenerate_optimized_2D_map, SIGNAL(triggered()),
this , SLOT(
regenerateSavedMap()));
309 connect(
ui_->actionView_3D_map, SIGNAL(triggered()),
this, SLOT(
view3DMap()));
310 connect(
ui_->actionGenerate_3D_map_pcd, SIGNAL(triggered()),
this, SLOT(
generate3DMap()));
315 connect(
ui_->actionRefine_links, SIGNAL(triggered()),
this, SLOT(
refineLinks()));
318 connect(
ui_->actionReset_all_changes, SIGNAL(triggered()),
this, SLOT(
resetAllChanges()));
323 connect(
ui_->pushButton_add, SIGNAL(clicked()),
this, SLOT(
addConstraint()));
326 ui_->pushButton_refine->setEnabled(
false);
327 ui_->pushButton_add->setEnabled(
false);
328 ui_->pushButton_reset->setEnabled(
false);
329 ui_->pushButton_reject->setEnabled(
false);
331 ui_->menuEdit->setEnabled(
false);
332 ui_->actionGenerate_3D_map_pcd->setEnabled(
false);
333 ui_->actionExport->setEnabled(
false);
334 ui_->actionExtract_images->setEnabled(
false);
335 ui_->menuExport_poses->setEnabled(
false);
336 ui_->menuExport_GPS->setEnabled(
false);
337 ui_->actionPoses_KML->setEnabled(
false);
338 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
339 ui_->actionExport_saved_2D_map->setEnabled(
false);
340 ui_->actionImport_2D_map->setEnabled(
false);
341 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
342 ui_->actionView_optimized_mesh->setEnabled(
false);
343 ui_->actionExport_optimized_mesh->setEnabled(
false);
344 ui_->actionUpdate_optimized_mesh->setEnabled(
false);
346 ui_->horizontalSlider_A->setTracking(
false);
347 ui_->horizontalSlider_B->setTracking(
false);
348 ui_->horizontalSlider_A->setEnabled(
false);
349 ui_->horizontalSlider_B->setEnabled(
false);
352 connect(
ui_->horizontalSlider_A, SIGNAL(sliderMoved(
int)),
this, SLOT(
sliderAMoved(
int)));
353 connect(
ui_->horizontalSlider_B, SIGNAL(sliderMoved(
int)),
this, SLOT(
sliderBMoved(
int)));
354 ui_->spinBox_indexA->setEnabled(
false);
355 ui_->spinBox_indexB->setEnabled(
false);
356 connect(
ui_->spinBox_indexA, SIGNAL(valueChanged(
int)),
ui_->horizontalSlider_A, SLOT(setValue(
int)));
357 connect(
ui_->spinBox_indexB, SIGNAL(valueChanged(
int)),
ui_->horizontalSlider_B, SLOT(setValue(
int)));
359 connect(
ui_->toolButton_edit_priorA, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
360 connect(
ui_->toolButton_edit_priorB, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
361 connect(
ui_->toolButton_remove_priorA, SIGNAL(clicked(
bool)),
this, SLOT(
rejectConstraint()));
362 connect(
ui_->toolButton_remove_priorB, SIGNAL(clicked(
bool)),
this, SLOT(
rejectConstraint()));
364 connect(
ui_->spinBox_mesh_angleTolerance, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
365 connect(
ui_->spinBox_mesh_minClusterSize, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
366 connect(
ui_->spinBox_mesh_fillDepthHoles, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
367 connect(
ui_->spinBox_mesh_depthError, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
368 connect(
ui_->checkBox_mesh_quad, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
369 connect(
ui_->spinBox_mesh_triangleSize, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
370 connect(
ui_->checkBox_showWords, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
371 connect(
ui_->checkBox_showCloud, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
372 connect(
ui_->checkBox_showMesh, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
373 connect(
ui_->checkBox_showScan, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
374 connect(
ui_->checkBox_showMap, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
375 connect(
ui_->checkBox_showGrid, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
376 connect(
ui_->checkBox_showElevation, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
377 connect(
ui_->checkBox_odomFrame_3dview, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
378 connect(
ui_->checkBox_gravity_3dview, SIGNAL(toggled(
bool)),
this, SLOT(
update3dView()));
380 ui_->horizontalSlider_neighbors->setTracking(
false);
381 ui_->horizontalSlider_loops->setTracking(
false);
382 ui_->horizontalSlider_neighbors->setEnabled(
false);
383 ui_->horizontalSlider_loops->setEnabled(
false);
393 ui_->checkBox_showOptimized->setEnabled(
false);
394 connect(
ui_->toolButton_constraint, SIGNAL(clicked(
bool)),
this, SLOT(
editConstraint()));
397 ui_->horizontalSlider_iterations->setTracking(
false);
398 ui_->horizontalSlider_iterations->setEnabled(
false);
399 ui_->spinBox_optimizationsFrom->setEnabled(
false);
402 connect(
ui_->spinBox_optimizationsFrom, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
403 connect(
ui_->comboBox_optimizationFlavor, SIGNAL(activated(
int)),
this, SLOT(
updateGraphView()));
404 connect(
ui_->checkBox_spanAllMaps, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
405 connect(
ui_->checkBox_wmState, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
406 connect(
ui_->graphViewer, SIGNAL(mapShownRequested()),
this, SLOT(
updateGraphView()));
407 connect(
ui_->checkBox_ignorePoseCorrection, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
409 connect(
ui_->checkBox_ignoreGlobalLoop, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
410 connect(
ui_->checkBox_ignoreLocalLoopSpace, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
411 connect(
ui_->checkBox_ignoreLocalLoopTime, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
412 connect(
ui_->checkBox_ignoreUserLoop, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
413 connect(
ui_->checkBox_ignoreLandmarks, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
414 connect(
ui_->doubleSpinBox_optimizationScale, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
415 connect(
ui_->checkBox_octomap, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
416 connect(
ui_->checkBox_grid_grid, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
417 connect(
ui_->checkBox_grid_2d, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
418 connect(
ui_->checkBox_grid_elevation, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
419 connect(
ui_->comboBox_octomap_rendering_type, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
updateOctomapView()));
421 connect(
ui_->checkBox_grid_empty, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
422 connect(
ui_->checkBox_grid_frontiers, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGrid()));
423 connect(
ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
updateConstraintView()));
425 connect(
ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(
double)),
this, SLOT(
update3dView()));
426 connect(
ui_->checkBox_cameraProjection, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
427 connect(
ui_->checkBox_showDisparityInsteadOfRight, SIGNAL(stateChanged(
int)),
this, SLOT(
update3dView()));
429 connect(
ui_->spinBox_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
update3dView()));
430 connect(
ui_->groupBox_posefiltering, SIGNAL(clicked(
bool)),
this, SLOT(
updateGraphView()));
431 connect(
ui_->doubleSpinBox_posefilteringRadius, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
432 connect(
ui_->doubleSpinBox_posefilteringAngle, SIGNAL(editingFinished()),
this, SLOT(
updateGraphView()));
434 ui_->label_stereo_inliers_name->setStyleSheet(
"QLabel {color : blue; }");
435 ui_->label_stereo_flowOutliers_name->setStyleSheet(
"QLabel {color : red; }");
436 ui_->label_stereo_slopeOutliers_name->setStyleSheet(
"QLabel {color : yellow; }");
437 ui_->label_stereo_disparityOutliers_name->setStyleSheet(
"QLabel {color : magenta; }");
441 connect(
ui_->graphViewer, SIGNAL(configChanged()),
this, SLOT(
configModified()));
442 connect(
ui_->graphicsView_A, SIGNAL(configChanged()),
this, SLOT(
configModified()));
443 connect(
ui_->graphicsView_B, SIGNAL(configChanged()),
this, SLOT(
configModified()));
444 connect(
ui_->comboBox_logger_level, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
configModified()));
445 connect(
ui_->actionVertical_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
446 connect(
ui_->actionConcise_Layout, SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
447 connect(
ui_->checkBox_alignPosesWithGPS, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
448 connect(
ui_->checkBox_alignPosesWithGroundTruth, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
449 connect(
ui_->checkBox_alignScansCloudsWithGroundTruth, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
450 connect(
ui_->checkBox_ignoreIntermediateNodes, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
451 connect(
ui_->checkBox_ignoreIntermediateNodes, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
452 connect(
ui_->checkBox_timeStats, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
453 connect(
ui_->checkBox_timeStats, SIGNAL(stateChanged(
int)),
this, SLOT(
updateStatistics()));
455 connect(
ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
456 connect(
ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
457 connect(
ui_->checkBox_cameraProjection, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
458 connect(
ui_->checkBox_showDisparityInsteadOfRight, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
459 connect(
ui_->spinBox_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
460 connect(
ui_->groupBox_posefiltering, SIGNAL(clicked(
bool)),
this, SLOT(
configModified()));
461 connect(
ui_->doubleSpinBox_posefilteringRadius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
462 connect(
ui_->doubleSpinBox_posefilteringAngle, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
464 connect(
ui_->pushButton_applyRotation, SIGNAL(clicked()),
this, SLOT(
updateGraphView()));
466 connect(
ui_->spinBox_icp_decimation, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
467 connect(
ui_->doubleSpinBox_icp_maxDepth, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
468 connect(
ui_->doubleSpinBox_icp_minDepth, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
469 connect(
ui_->checkBox_icp_from_depth, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
471 connect(
ui_->doubleSpinBox_detectMore_radius, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
472 connect(
ui_->doubleSpinBox_detectMore_radiusMin, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
473 connect(
ui_->doubleSpinBox_detectMore_angle, SIGNAL(valueChanged(
double)),
this, SLOT(
configModified()));
474 connect(
ui_->spinBox_detectMore_iterations, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
475 connect(
ui_->checkBox_detectMore_intraSession, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
476 connect(
ui_->checkBox_detectMore_interSession, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
477 connect(
ui_->checkBox_opt_graph_as_guess, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
479 connect(
ui_->lineEdit_obstacleColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
480 connect(
ui_->lineEdit_groundColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
481 connect(
ui_->lineEdit_emptyColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
482 connect(
ui_->lineEdit_frontierColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
configModified()));
483 connect(
ui_->lineEdit_obstacleColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
484 connect(
ui_->lineEdit_groundColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
485 connect(
ui_->lineEdit_emptyColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
486 connect(
ui_->lineEdit_frontierColor, SIGNAL(textChanged(
const QString &)),
this, SLOT(
updateGrid()));
489 connect(
ui_->toolButton_emptyColor, SIGNAL(clicked(
bool)),
this, SLOT(
selectEmptyColor()));
491 connect(
ui_->spinBox_cropRadius, SIGNAL(valueChanged(
int)),
this, SLOT(
configModified()));
492 connect(
ui_->checkBox_grid_showProbMap, SIGNAL(stateChanged(
int)),
this, SLOT(
configModified()));
493 connect(
ui_->checkBox_grid_showProbMap, SIGNAL(stateChanged(
int)),
this, SLOT(
updateGraphView()));
498 QList<QDockWidget*> dockWidgets = this->findChildren<QDockWidget*>();
499 for(
int i=0;
i<dockWidgets.size(); ++
i)
501 connect(dockWidgets[
i], SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
this, SLOT(
configModified()));
502 connect(dockWidgets[
i]->toggleViewAction(), SIGNAL(toggled(
bool)),
this, SLOT(
configModified()));
504 ui_->dockWidget_constraints->installEventFilter(
this);
505 ui_->dockWidget_graphView->installEventFilter(
this);
506 ui_->dockWidget_occupancyGridView->installEventFilter(
this);
507 ui_->dockWidget_stereoView->installEventFilter(
this);
508 ui_->dockWidget_view3d->installEventFilter(
this);
509 ui_->dockWidget_guiparameters->installEventFilter(
this);
510 ui_->dockWidget_coreparameters->installEventFilter(
this);
511 ui_->dockWidget_info->installEventFilter(
this);
512 ui_->dockWidget_statistics->installEventFilter(
this);
519 #ifdef RTABMAP_OCTOMAP
528 qobject_cast<QHBoxLayout *>(
ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::TopToBottom);
532 qobject_cast<QHBoxLayout *>(
ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::LeftToRight);
542 ui_->buttonBox->setVisible(visible);
547 if(
ui_->actionConcise_Layout->isChecked())
549 ui_->graphicsView_B->setVisible(
false);
550 ui_->scrollArea->setVisible(
false);
551 ui_->scrollArea_2->setVisible(
false);
552 ui_->spinBox_indexB->setVisible(
false);
553 ui_->widget_imageControls_B->setVisible(
false);
554 ui_->widget_graphControl->setVisible(
false);
555 ui_->graphicsView_A->clearLines();
556 ui_->graphicsView_B->clearLines();
560 ui_->graphicsView_B->setVisible(
true);
561 ui_->scrollArea->setVisible(
true);
562 ui_->scrollArea_2->setVisible(
true);
563 ui_->spinBox_indexB->setVisible(
true);
564 ui_->widget_imageControls_B->setVisible(
true);
565 ui_->widget_graphControl->setVisible(
true);
568 this->setWindowModified(
true);
577 QString privatePath = QDir::homePath() +
"/.rtabmap";
578 if(!QDir(privatePath).exists())
580 QDir::home().mkdir(
".rtabmap");
582 return privatePath +
"/rtabmap.ini";
588 QSettings settings(
path, QSettings::IniFormat);
589 settings.beginGroup(
"DatabaseViewer");
593 bytes = settings.value(
"geometry", QByteArray()).toByteArray();
596 this->restoreGeometry(
bytes);
598 bytes = settings.value(
"state", QByteArray()).toByteArray();
601 this->restoreState(
bytes);
605 ui_->comboBox_logger_level->setCurrentIndex(settings.value(
"loggerLevel",
ui_->comboBox_logger_level->currentIndex()).toInt());
606 ui_->actionVertical_Layout->setChecked(settings.value(
"verticalLayout",
ui_->actionVertical_Layout->isChecked()).toBool());
607 ui_->actionConcise_Layout->setChecked(settings.value(
"conciseLayout",
ui_->actionConcise_Layout->isChecked()).toBool());
608 ui_->checkBox_ignoreIntermediateNodes->setChecked(settings.value(
"ignoreIntermediateNodes",
ui_->checkBox_ignoreIntermediateNodes->isChecked()).toBool());
609 ui_->checkBox_timeStats->setChecked(settings.value(
"timeStats",
ui_->checkBox_timeStats->isChecked()).toBool());
612 ui_->graphViewer->loadSettings(settings,
"GraphView");
613 ui_->graphViewer->setReferentialVisible(
false);
615 settings.beginGroup(
"optimization");
616 ui_->doubleSpinBox_gainCompensationRadius->setValue(settings.value(
"gainCompensationRadius",
ui_->doubleSpinBox_gainCompensationRadius->value()).toDouble());
617 ui_->doubleSpinBox_voxelSize->setValue(settings.value(
"voxelSize",
ui_->doubleSpinBox_voxelSize->value()).toDouble());
618 ui_->spinBox_decimation->setValue(settings.value(
"decimation",
ui_->spinBox_decimation->value()).toInt());
619 ui_->checkBox_cameraProjection->setChecked(settings.value(
"camProj",
ui_->checkBox_cameraProjection->isChecked()).toBool());
620 ui_->checkBox_showDisparityInsteadOfRight->setChecked(settings.value(
"showDisp",
ui_->checkBox_showDisparityInsteadOfRight->isChecked()).toBool());
623 settings.beginGroup(
"grid");
624 ui_->groupBox_posefiltering->setChecked(settings.value(
"poseFiltering",
ui_->groupBox_posefiltering->isChecked()).toBool());
625 ui_->doubleSpinBox_posefilteringRadius->setValue(settings.value(
"poseFilteringRadius",
ui_->doubleSpinBox_posefilteringRadius->value()).toDouble());
626 ui_->doubleSpinBox_posefilteringAngle->setValue(settings.value(
"poseFilteringAngle",
ui_->doubleSpinBox_posefilteringAngle->value()).toDouble());
627 ui_->lineEdit_obstacleColor->setText(settings.value(
"colorObstacle",
ui_->lineEdit_obstacleColor->text()).toString());
628 ui_->lineEdit_groundColor->setText(settings.value(
"colorGround",
ui_->lineEdit_groundColor->text()).toString());
629 ui_->lineEdit_emptyColor->setText(settings.value(
"colorEmpty",
ui_->lineEdit_emptyColor->text()).toString());
630 ui_->lineEdit_frontierColor->setText(settings.value(
"colorFrontier",
ui_->lineEdit_frontierColor->text()).toString());
631 ui_->spinBox_cropRadius->setValue(settings.value(
"cropRadius",
ui_->spinBox_cropRadius->value()).toInt());
632 ui_->checkBox_grid_showProbMap->setChecked(settings.value(
"probMap",
ui_->checkBox_grid_showProbMap->isChecked()).toBool());
635 settings.beginGroup(
"mesh");
636 ui_->checkBox_mesh_quad->setChecked(settings.value(
"quad",
ui_->checkBox_mesh_quad->isChecked()).toBool());
637 ui_->spinBox_mesh_angleTolerance->setValue(settings.value(
"angleTolerance",
ui_->spinBox_mesh_angleTolerance->value()).toInt());
638 ui_->spinBox_mesh_minClusterSize->setValue(settings.value(
"minClusterSize",
ui_->spinBox_mesh_minClusterSize->value()).toInt());
639 ui_->spinBox_mesh_fillDepthHoles->setValue(settings.value(
"fillDepthHolesSize",
ui_->spinBox_mesh_fillDepthHoles->value()).toInt());
640 ui_->spinBox_mesh_depthError->setValue(settings.value(
"fillDepthHolesError",
ui_->spinBox_mesh_depthError->value()).toInt());
641 ui_->spinBox_mesh_triangleSize->setValue(settings.value(
"triangleSize",
ui_->spinBox_mesh_triangleSize->value()).toInt());
645 ui_->graphicsView_A->loadSettings(settings,
"ImageViewA");
646 ui_->graphicsView_B->loadSettings(settings,
"ImageViewB");
649 settings.beginGroup(
"icp");
650 ui_->spinBox_icp_decimation->setValue(settings.value(
"decimation",
ui_->spinBox_icp_decimation->value()).toInt());
651 ui_->doubleSpinBox_icp_maxDepth->setValue(settings.value(
"maxDepth",
ui_->doubleSpinBox_icp_maxDepth->value()).toDouble());
652 ui_->doubleSpinBox_icp_minDepth->setValue(settings.value(
"minDepth",
ui_->doubleSpinBox_icp_minDepth->value()).toDouble());
653 ui_->checkBox_icp_from_depth->setChecked(settings.value(
"icpFromDepth",
ui_->checkBox_icp_from_depth->isChecked()).toBool());
659 settings.beginGroup(
"Gui");
661 settings.beginGroup(
"PostProcessingDialog");
662 ui_->doubleSpinBox_detectMore_radius->setValue(settings.value(
"cluster_radius",
ui_->doubleSpinBox_detectMore_radius->value()).toDouble());
663 ui_->doubleSpinBox_detectMore_radiusMin->setValue(settings.value(
"cluster_radius_min",
ui_->doubleSpinBox_detectMore_radiusMin->value()).toDouble());
664 ui_->doubleSpinBox_detectMore_angle->setValue(settings.value(
"cluster_angle",
ui_->doubleSpinBox_detectMore_angle->value()).toDouble());
665 ui_->spinBox_detectMore_iterations->setValue(settings.value(
"iterations",
ui_->spinBox_detectMore_iterations->value()).toInt());
666 ui_->checkBox_detectMore_intraSession->setChecked(settings.value(
"intra_session",
ui_->checkBox_detectMore_intraSession->isChecked()).toBool());
667 ui_->checkBox_detectMore_interSession->setChecked(settings.value(
"inter_session",
ui_->checkBox_detectMore_interSession->isChecked()).toBool());
668 ui_->checkBox_opt_graph_as_guess->setChecked(settings.value(
"opt_graph_as_guess",
ui_->checkBox_opt_graph_as_guess->isChecked()).toBool());
675 for(ParametersMap::iterator
iter = parameters.begin();
iter!= parameters.end(); ++
iter)
677 ui_->parameters_toolbox->updateParameter(
iter->first,
iter->second);
684 QSettings settings(
path, QSettings::IniFormat);
685 settings.beginGroup(
"DatabaseViewer");
688 if(!this->isMaximized())
690 settings.setValue(
"geometry", this->saveGeometry());
692 settings.setValue(
"state", this->saveState());
693 settings.setValue(
"maximized", this->isMaximized());
696 settings.setValue(
"loggerLevel",
ui_->comboBox_logger_level->currentIndex());
697 settings.setValue(
"verticalLayout",
ui_->actionVertical_Layout->isChecked());
698 settings.setValue(
"conciseLayout",
ui_->actionConcise_Layout->isChecked());
699 settings.setValue(
"ignoreIntermediateNodes",
ui_->checkBox_ignoreIntermediateNodes->isChecked());
700 settings.setValue(
"timeStats",
ui_->checkBox_timeStats->isChecked());
703 ui_->graphViewer->saveSettings(settings,
"GraphView");
706 settings.beginGroup(
"optimization");
707 settings.setValue(
"gainCompensationRadius",
ui_->doubleSpinBox_gainCompensationRadius->value());
708 settings.setValue(
"voxelSize",
ui_->doubleSpinBox_voxelSize->value());
709 settings.setValue(
"decimation",
ui_->spinBox_decimation->value());
710 settings.setValue(
"camProj",
ui_->checkBox_cameraProjection->isChecked());
711 settings.setValue(
"showDisp",
ui_->checkBox_showDisparityInsteadOfRight->isChecked());
715 settings.beginGroup(
"grid");
716 settings.setValue(
"poseFiltering",
ui_->groupBox_posefiltering->isChecked());
717 settings.setValue(
"poseFilteringRadius",
ui_->doubleSpinBox_posefilteringRadius->value());
718 settings.setValue(
"poseFilteringAngle",
ui_->doubleSpinBox_posefilteringAngle->value());
719 settings.setValue(
"colorObstacle",
ui_->lineEdit_obstacleColor->text());
720 settings.setValue(
"colorGround",
ui_->lineEdit_groundColor->text());
721 settings.setValue(
"colorEmpty",
ui_->lineEdit_emptyColor->text());
722 settings.setValue(
"colorFrontier",
ui_->lineEdit_frontierColor->text());
723 settings.setValue(
"cropRadius",
ui_->spinBox_cropRadius->value());
724 settings.setValue(
"probMap",
ui_->checkBox_grid_showProbMap->isChecked());
727 settings.beginGroup(
"mesh");
728 settings.setValue(
"quad",
ui_->checkBox_mesh_quad->isChecked());
729 settings.setValue(
"angleTolerance",
ui_->spinBox_mesh_angleTolerance->value());
730 settings.setValue(
"minClusterSize",
ui_->spinBox_mesh_minClusterSize->value());
731 settings.setValue(
"fillDepthHolesSize",
ui_->spinBox_mesh_fillDepthHoles->value());
732 settings.setValue(
"fillDepthHolesError",
ui_->spinBox_mesh_depthError->value());
733 settings.setValue(
"triangleSize",
ui_->spinBox_mesh_triangleSize->value());
737 ui_->graphicsView_A->saveSettings(settings,
"ImageViewA");
738 ui_->graphicsView_B->saveSettings(settings,
"ImageViewB");
741 settings.beginGroup(
"icp");
742 settings.setValue(
"decimation",
ui_->spinBox_icp_decimation->value());
743 settings.setValue(
"maxDepth",
ui_->doubleSpinBox_icp_maxDepth->value());
744 settings.setValue(
"minDepth",
ui_->doubleSpinBox_icp_minDepth->value());
745 settings.setValue(
"icpFromDepth",
ui_->checkBox_icp_from_depth->isChecked());
751 settings.beginGroup(
"Gui");
753 settings.beginGroup(
"PostProcessingDialog");
754 settings.setValue(
"cluster_radius",
ui_->doubleSpinBox_detectMore_radius->value());
755 settings.setValue(
"cluster_radius_min",
ui_->doubleSpinBox_detectMore_radiusMin->value());
756 settings.setValue(
"cluster_angle",
ui_->doubleSpinBox_detectMore_angle->value());
757 settings.setValue(
"iterations",
ui_->spinBox_detectMore_iterations->value());
758 settings.setValue(
"intra_session",
ui_->checkBox_detectMore_intraSession->isChecked());
759 settings.setValue(
"inter_session",
ui_->checkBox_detectMore_interSession->isChecked());
760 settings.setValue(
"opt_graph_as_guess",
ui_->checkBox_opt_graph_as_guess->isChecked());
765 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end();)
767 if(!
ui_->parameters_toolbox->getParameterWidget(
iter->first.c_str()))
769 parameters.erase(
iter++);
778 this->setWindowModified(
false);
784 ui_->comboBox_logger_level->setCurrentIndex(1);
785 ui_->checkBox_alignPosesWithGPS->setChecked(
true);
786 ui_->checkBox_alignPosesWithGroundTruth->setChecked(
true);
787 ui_->checkBox_alignScansCloudsWithGroundTruth->setChecked(
false);
788 ui_->checkBox_ignoreIntermediateNodes->setChecked(
false);
789 ui_->checkBox_timeStats->setChecked(
true);
791 ui_->comboBox_optimizationFlavor->setCurrentIndex(0);
792 ui_->checkBox_spanAllMaps->setChecked(
true);
793 ui_->checkBox_wmState->setChecked(
false);
794 ui_->checkBox_ignorePoseCorrection->setChecked(
false);
795 ui_->checkBox_ignoreGlobalLoop->setChecked(
false);
796 ui_->checkBox_ignoreLocalLoopSpace->setChecked(
false);
797 ui_->checkBox_ignoreLocalLoopTime->setChecked(
false);
798 ui_->checkBox_ignoreUserLoop->setChecked(
false);
799 ui_->checkBox_ignoreLandmarks->setChecked(
false);
800 ui_->doubleSpinBox_optimizationScale->setValue(1.0);
801 ui_->doubleSpinBox_gainCompensationRadius->setValue(0.0);
802 ui_->doubleSpinBox_voxelSize->setValue(0.0);
803 ui_->spinBox_decimation->setValue(1);
804 ui_->checkBox_cameraProjection->setChecked(
false);
805 ui_->checkBox_showDisparityInsteadOfRight->setChecked(
false);
807 ui_->groupBox_posefiltering->setChecked(
false);
808 ui_->doubleSpinBox_posefilteringRadius->setValue(0.1);
809 ui_->doubleSpinBox_posefilteringAngle->setValue(30);
810 ui_->checkBox_grid_empty->setChecked(
true);
811 ui_->checkBox_grid_frontiers->setChecked(
false);
812 ui_->checkBox_octomap->setChecked(
false);
813 ui_->lineEdit_obstacleColor->setText(QColor(Qt::red).
name());
814 ui_->lineEdit_groundColor->setText(QColor(Qt::green).
name());
815 ui_->lineEdit_emptyColor->setText(QColor(Qt::yellow).
name());
816 ui_->lineEdit_frontierColor->setText(QColor(Qt::cyan).
name());
817 ui_->spinBox_cropRadius->setValue(1);
818 ui_->checkBox_grid_showProbMap->setChecked(
false);
820 ui_->checkBox_mesh_quad->setChecked(
true);
821 ui_->spinBox_mesh_angleTolerance->setValue(15);
822 ui_->spinBox_mesh_minClusterSize->setValue(0);
823 ui_->spinBox_mesh_fillDepthHoles->setValue(
false);
824 ui_->spinBox_mesh_depthError->setValue(10);
825 ui_->spinBox_mesh_triangleSize->setValue(2);
827 ui_->spinBox_icp_decimation->setValue(1);
828 ui_->doubleSpinBox_icp_maxDepth->setValue(0.0);
829 ui_->doubleSpinBox_icp_minDepth->setValue(0.0);
830 ui_->checkBox_icp_from_depth->setChecked(
false);
832 ui_->doubleSpinBox_detectMore_radius->setValue(1.0);
833 ui_->doubleSpinBox_detectMore_radiusMin->setValue(0.0);
834 ui_->doubleSpinBox_detectMore_angle->setValue(30.0);
835 ui_->spinBox_detectMore_iterations->setValue(5);
836 ui_->checkBox_detectMore_intraSession->setChecked(
true);
837 ui_->checkBox_detectMore_interSession->setChecked(
true);
838 ui_->checkBox_opt_graph_as_guess->setChecked(
true);
843 QString
path = QFileDialog::getOpenFileName(
this, tr(
"Select file"),
pathDatabase_, tr(
"Databases (*.db)"));
852 UDEBUG(
"Open database \"%s\"",
path.toStdString().c_str());
853 if(QFile::exists(
path))
855 if(QFileInfo(
path).isFile())
857 std::string driverType =
"sqlite3";
863 ui_->actionClose_database->setEnabled(
false);
864 ui_->actionOpen_database->setEnabled(
true);
867 QMessageBox::warning(
this,
"Database error", tr(
"Can't open database \"%1\"").
arg(
path));
871 ui_->actionClose_database->setEnabled(
true);
872 ui_->actionOpen_database->setEnabled(
false);
886 uInsert(parameters, overriddenParameters);
888 if(parameters.size())
890 const ParametersMap & currentParameters =
ui_->parameters_toolbox->getParameters();
892 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end(); ++
iter)
894 ParametersMap::const_iterator jter = currentParameters.find(
iter->first);
895 if(jter!=currentParameters.end() &&
896 ui_->parameters_toolbox->getParameterWidget(QString(
iter->first.c_str())) != 0 &&
897 iter->second.compare(jter->second) != 0 &&
898 iter->first.compare(Parameters::kRtabmapWorkingDirectory()) != 0)
900 bool different =
true;
911 differentParameters.insert(*
iter);
912 QString
msg = tr(
"Parameter \"%1\": database=\"%2\" Preferences=\"%3\"")
913 .arg(
iter->first.c_str())
914 .arg(
iter->second.c_str())
915 .arg(jter->second.c_str());
921 if(differentParameters.size())
923 int r = QMessageBox::question(
this,
924 tr(
"Update parameters..."),
925 tr(
"The database is using %1 different parameter(s) than "
926 "those currently set in Core parameters panel. Do you want "
927 "to use database's parameters?").
arg(differentParameters.size()),
928 QMessageBox::Yes | QMessageBox::No,
930 if(r == QMessageBox::Yes)
933 for(rtabmap::ParametersMap::const_iterator
iter = differentParameters.begin();
iter!=differentParameters.end(); ++
iter)
935 ui_->parameters_toolbox->updateParameter(
iter->first,
iter->second);
936 str.push_back(
iter->first.c_str());
944 this->setWindowTitle(
"RTAB-Map Database Viewer - " +
path +
"[*]");
960 QMessageBox::warning(
this,
"Database error", tr(
"Database \"%1\" does not exist.").
arg(
path));
967 this->setWindowTitle(
"RTAB-Map Database Viewer[*]");
972 QMessageBox::StandardButton button = QMessageBox::question(
this,
973 tr(
"Links modified"),
974 tr(
"Some links are modified (%1 added, %2 refined, %3 removed), do you want to save them?")
976 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
977 QMessageBox::Cancel);
979 if(button == QMessageBox::Yes)
988 if(refinedIter->second.from() != refinedIter->second.to())
994 if(
iter->second.from() !=
iter->second.to())
1005 if(
iter->second.from() !=
iter->second.to())
1014 if(
iter->second.from() !=
iter->second.to())
1032 if(button != QMessageBox::Yes && button != QMessageBox::No)
1041 QMessageBox::StandardButton button = QMessageBox::question(
this,
1042 tr(
"Local occupancy grid maps modified"),
1043 tr(
"%1 occupancy grid maps are modified, do you want to "
1044 "save them? This will overwrite occupancy grids saved in the database.")
1046 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
1047 QMessageBox::Cancel);
1049 if(button == QMessageBox::Yes)
1057 mapIter->second.groundCells,
1058 mapIter->second.obstacleCells,
1059 mapIter->second.emptyCells,
1060 mapIter->second.cellSize,
1061 mapIter->second.viewPoint);
1070 if(button != QMessageBox::Yes && button != QMessageBox::No)
1078 QMessageBox::StandardButton button = QMessageBox::question(
this,
1079 tr(
"Laser scans modified"),
1080 tr(
"%1 laser scans are modified, do you want to "
1081 "save them? This will overwrite laser scans saved in the database.")
1083 QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No,
1084 QMessageBox::Cancel);
1086 if(button == QMessageBox::Yes)
1095 if(button != QMessageBox::Yes && button != QMessageBox::No)
1124 ui_->graphViewer->clearAll();
1126 ui_->menuEdit->setEnabled(
false);
1127 ui_->actionGenerate_3D_map_pcd->setEnabled(
false);
1128 ui_->actionExport->setEnabled(
false);
1129 ui_->actionExtract_images->setEnabled(
false);
1130 ui_->menuExport_poses->setEnabled(
false);
1131 ui_->menuExport_GPS->setEnabled(
false);
1132 ui_->actionPoses_KML->setEnabled(
false);
1133 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
1134 ui_->actionExport_saved_2D_map->setEnabled(
false);
1135 ui_->actionImport_2D_map->setEnabled(
false);
1136 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
1137 ui_->actionView_optimized_mesh->setEnabled(
false);
1138 ui_->actionExport_optimized_mesh->setEnabled(
false);
1139 ui_->actionUpdate_optimized_mesh->setEnabled(
false);
1140 ui_->checkBox_showOptimized->setEnabled(
false);
1141 ui_->toolBox_statistics->clear();
1143 ui_->checkBox_alignPosesWithGPS->setVisible(
false);
1144 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
1145 ui_->checkBox_alignPosesWithGroundTruth->setVisible(
false);
1146 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
1147 ui_->checkBox_alignScansCloudsWithGroundTruth->setVisible(
false);
1148 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
1149 ui_->doubleSpinBox_optimizationScale->setVisible(
false);
1150 ui_->label_scale_title->setVisible(
false);
1151 ui_->label_rmse->setVisible(
false);
1152 ui_->label_rmse_title->setVisible(
false);
1153 ui_->checkBox_ignoreIntermediateNodes->setVisible(
false);
1154 ui_->label_ignoreINtermediateNdoes->setVisible(
false);
1155 ui_->label_alignPosesWithGPS->setVisible(
false);
1156 ui_->label_alignPosesWithGroundTruth->setVisible(
false);
1157 ui_->label_alignScansCloudsWithGroundTruth->setVisible(
false);
1158 ui_->label_optimizeFrom->setText(tr(
"Root"));
1159 ui_->textEdit_info->clear();
1161 ui_->pushButton_refine->setEnabled(
false);
1162 ui_->pushButton_add->setEnabled(
false);
1163 ui_->pushButton_reset->setEnabled(
false);
1164 ui_->pushButton_reject->setEnabled(
false);
1166 ui_->horizontalSlider_loops->setEnabled(
false);
1167 ui_->horizontalSlider_loops->setMaximum(0);
1168 ui_->horizontalSlider_iterations->setEnabled(
false);
1169 ui_->horizontalSlider_iterations->setMaximum(0);
1170 ui_->horizontalSlider_neighbors->setEnabled(
false);
1171 ui_->horizontalSlider_neighbors->setMaximum(0);
1172 ui_->label_constraint->clear();
1173 ui_->label_constraint_opt->clear();
1174 ui_->label_variance->clear();
1175 ui_->lineEdit_covariance->clear();
1176 ui_->label_type->clear();
1177 ui_->label_type_name->clear();
1178 ui_->checkBox_showOptimized->setEnabled(
false);
1180 ui_->horizontalSlider_A->setEnabled(
false);
1181 ui_->horizontalSlider_A->setMaximum(0);
1182 ui_->horizontalSlider_B->setEnabled(
false);
1183 ui_->horizontalSlider_B->setMaximum(0);
1184 ui_->label_idA->setText(
"NaN");
1185 ui_->label_idB->setText(
"NaN");
1189 ui_->spinBox_indexA->setEnabled(
false);
1190 ui_->spinBox_indexA->setMaximum(0);
1191 ui_->spinBox_indexB->setEnabled(
false);
1192 ui_->spinBox_indexB->setMaximum(0);
1203 ui_->graphViewer->clearAll();
1204 ui_->label_loopClosures->clear();
1205 ui_->label_timeOptimization->clear();
1206 ui_->label_pathLength->clear();
1207 ui_->label_poses->clear();
1208 ui_->label_rmse->clear();
1209 ui_->spinBox_optimizationsFrom->setEnabled(
false);
1211 ui_->graphicsView_A->clear();
1212 ui_->graphicsView_B->clear();
1214 ui_->graphicsView_stereo->clear();
1218 ui_->toolBox_statistics->clear();
1234 QString
path = QFileDialog::getOpenFileName(
this, tr(
"Select file"),
pathDatabase_, tr(
"Databases (*.db)"));
1239 QMessageBox::information(
this,
"Database recovery", tr(
"The selected database is already opened, close it first."));
1242 std::string errorMsg;
1244 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1246 progressDialog->show();
1251 QMessageBox::information(
this,
"Database recovery", tr(
"Database \"%1\" recovered! Try opening it again.").
arg(
path));
1255 QMessageBox::warning(
this,
"Database recovery", tr(
"Database recovery failed: \"%1\".").
arg(errorMsg.c_str()));
1265 if(this->isWindowModified())
1267 QMessageBox::Button
b=QMessageBox::question(
this,
1268 tr(
"Database Viewer"),
1269 tr(
"There are unsaved changed settings. Save them?"),
1270 QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard);
1271 if(
b == QMessageBox::Save)
1275 else if(
b != QMessageBox::Discard)
1294 if(event->isAccepted())
1296 ui_->toolBox_statistics->closeFigures();
1307 this->setWindowModified(
false);
1317 if(this->isVisible())
1330 if(this->isVisible())
1339 if((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_S)
1347 if (event->type() == QEvent::Resize && qobject_cast<QDockWidget*>(obj))
1349 this->setWindowModified(
true);
1351 return QWidget::eventFilter(obj, event);
1375 double previousStamp = 0;
1376 std::vector<double> delays(
ids_.size());
1378 std::map<int, Transform> poses;
1379 std::map<int, double> stamps;
1380 std::map<int, Transform> groundTruths;
1381 std::map<int, GPS> gpsValues;
1382 std::map<int, EnvSensors> sensorsValues;
1383 for(
int i=0;
i<
ids_.size();
i+=1+framesIgnored)
1395 if(frameRate == 0 ||
1396 previousStamp == 0 ||
1398 stamp - previousStamp >= 1.0/frameRate)
1400 if(sessionExported < 0 || sessionExported == mapId)
1402 ids.push_back(
ids_[
i]);
1404 if(previousStamp && stamp)
1406 delays[oi++] = stamp - previousStamp;
1408 previousStamp = stamp;
1410 poses.insert(std::make_pair(
ids_[
i], odomPose));
1411 stamps.insert(std::make_pair(
ids_[
i], stamp));
1412 groundTruths.insert(std::make_pair(
ids_[
i], groundTruth));
1413 if(gps.
stamp() > 0.0)
1415 gpsValues.insert(std::make_pair(
ids_[
i], gps));
1419 sensorsValues.insert(std::make_pair(
ids_[
i], sensors));
1423 if(sessionExported >= 0 && mapId > sessionExported)
1434 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1436 progressDialog->show();
1438 UINFO(
"Decompress: rgb=%d depth=%d scan=%d userData=%d",
1444 for(
int i=0;
i<ids.size() && !progressDialog->
isCanceled(); ++
i)
1450 cv::Mat depth, rgb, userData;
1452 data.uncompressDataConst(
1457 cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1);
1460 std::multimap<int, Link> links;
1462 if(links.size() && links.begin()->first <
id)
1464 covariance = links.begin()->second.infMatrix().inv();
1469 if(
data.cameraModels().size())
1475 data.cameraModels(),
1486 data.stereoCameraModels(),
1491 if(groundTruths.find(
id)!=groundTruths.end())
1495 if(gpsValues.find(
id)!=gpsValues.end())
1497 sensorData.
setGPS(gpsValues.at(
id));
1499 if(sensorsValues.find(
id)!=sensorsValues.end())
1508 QApplication::processEvents();
1513 progressDialog->
appendText(tr(
"Average frame rate=%1 Hz (Min=%2, Max=%3)")
1520 UERROR(
"DataRecorder init failed?!");
1525 QMessageBox::warning(
this, tr(
"Cannot export database"), tr(
"An output path must be set!"));
1537 QStringList formats;
1538 formats.push_back(
"id.jpg");
1539 formats.push_back(
"id.png");
1540 formats.push_back(
"timestamp.jpg");
1541 formats.push_back(
"timestamp.png");
1543 QString
format = QInputDialog::getItem(
this, tr(
"Which RGB format?"), tr(
"Format:"), formats, 0,
false, &ok);
1549 QString ext =
format.split(
'.').back();
1550 bool useStamp =
format.split(
'.').front().compare(
"timestamp") == 0;
1551 bool directoriesCreated =
false;
1552 QString
path = QFileDialog::getExistingDirectory(
this, tr(
"Select directory where to save images..."),
pathDatabase_);
1556 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1560 progressDialog->show();
1562 int imagesExported = 0;
1563 for(
int i=0;
i<
ids_.size(); ++
i)
1565 QString
id = QString::number(
ids_.at(
i));
1569 data.uncompressData();
1571 if(!directoriesCreated)
1574 if(!
data.imageRaw().empty() && !
data.rightRaw().empty())
1577 dir.mkdir(QString(
"%1/left").
arg(
path));
1578 dir.mkdir(QString(
"%1/right").
arg(
path));
1579 dir.mkdir(QString(
"%1/calib").
arg(
path));
1580 directoriesCreated =
true;
1582 else if(!
data.imageRaw().empty())
1584 if(!
data.depthRaw().empty())
1587 dir.mkdir(QString(
"%1/rgb").
arg(
path));
1588 dir.mkdir(QString(
"%1/depth").
arg(
path));
1589 dir.mkdir(QString(
"%1/calib").
arg(
path));
1590 directoriesCreated =
true;
1595 if(!
data.imageRaw().empty() && useStamp)
1601 std::vector<float>
v;
1607 UWARN(
"Node %d has null timestamp! Using id instead!",
ids_.at(
i));
1611 id = QString::number(stamp,
'f');
1615 if(!
data.imageRaw().empty())
1617 if(!
data.rightRaw().empty())
1619 if(!cv::imwrite(QString(
"%1/left/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1621 if(!cv::imwrite(QString(
"%1/right/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.rightRaw()))
1623 UINFO(QString(
"Saved left/%1.%2 and right/%1.%2").
arg(
id).
arg(ext).toStdString().
c_str());
1627 UERROR(
"Cannot save calibration file, database name is empty!");
1629 else if(
data.stereoCameraModels().size()>=1 &&
data.stereoCameraModels().front().isValidForProjection())
1631 for(
size_t i=0;
i<
data.stereoCameraModels().
size(); ++
i)
1633 std::string cameraName =
id.toStdString();
1634 if(
data.stereoCameraModels().size()>1)
1640 data.imageRaw().size(),
1641 data.stereoCameraModels()[
i].left().K_raw(),
1642 data.stereoCameraModels()[
i].left().D_raw(),
1643 data.stereoCameraModels()[
i].left().R(),
1644 data.stereoCameraModels()[
i].left().P(),
1645 data.rightRaw().size(),
1646 data.stereoCameraModels()[
i].right().K_raw(),
1647 data.stereoCameraModels()[
i].right().D_raw(),
1648 data.stereoCameraModels()[
i].right().R(),
1649 data.stereoCameraModels()[
i].right().P(),
1650 data.stereoCameraModels()[
i].R(),
1651 data.stereoCameraModels()[
i].T(),
1652 data.stereoCameraModels()[
i].E(),
1653 data.stereoCameraModels()[
i].F(),
1654 data.stereoCameraModels()[
i].left().localTransform());
1655 if(
model.save(
path.toStdString() +
"/calib"))
1657 UINFO(
"Saved stereo calibration \"%s\"", (
path.toStdString()+
"/calib/"+cameraName+
".yaml").c_str());
1661 UERROR(
"Failed saving calibration \"%s\"", (
path.toStdString()+
"/calib/"+cameraName+
".yaml").c_str());
1668 if(!
data.depthRaw().empty())
1670 if(!cv::imwrite(QString(
"%1/rgb/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1673 UWARN(
"Failed saving \"%s\"", QString(
"%1/depth/%2.png").
arg(
path).
arg(
id).toStdString().
c_str());
1674 UINFO(QString(
"Saved rgb/%1.%2 and depth/%1.png").
arg(
id).
arg(ext).toStdString().
c_str());
1678 if(!cv::imwrite(QString(
"%1/%2.%3").
arg(
path).
arg(
id).
arg(ext).toStdString(),
data.imageRaw()))
1686 UERROR(
"Cannot save calibration file, database name is empty!");
1688 else if(
data.cameraModels().size() >= 1 &&
data.cameraModels().front().isValidForProjection())
1690 for(
size_t i=0;
i<
data.cameraModels().
size(); ++
i)
1692 std::string cameraName =
id.toStdString();
1693 if(
data.cameraModels().size()>1)
1698 data.imageRaw().size(),
1699 data.cameraModels()[
i].K_raw(),
1700 data.cameraModels()[
i].D_raw(),
1701 data.cameraModels()[
i].R(),
1702 data.cameraModels()[
i].P(),
1703 data.cameraModels()[
i].localTransform());
1704 std::string dirPrefix =
"";
1705 if(!
data.depthRaw().empty())
1707 dirPrefix =
"/calib";
1709 if(
model.save(
path.toStdString()+dirPrefix))
1711 UINFO(
"Saved calibration \"%s\"", (
path.toStdString()+dirPrefix+
"/"+cameraName+
".yaml").c_str());
1715 UERROR(
"Failed saving calibration \"%s\"", (
path.toStdString()+
"/"+cameraName+
".yaml").c_str());
1723 QApplication::processEvents();
1733 QMessageBox::information(
this, tr(
"Exporting"), tr(
"%1 images exported!").
arg(imagesExported));
1745 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
1746 int progressSteps = 5;
1747 if(
ui_->graphViewer->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
1751 if(
ui_->textEdit_info->isVisible())
1755 if(
ui_->toolBox_statistics->isVisible())
1760 progressDialog->show();
1763 progressDialog->
appendText(tr(
"Loading all ids..."));
1764 QApplication::processEvents();
1766 QApplication::processEvents();
1768 UINFO(
"Loading all IDs...");
1771 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
1772 ids_ = QList<int>(ids.begin(), ids.end());
1774 ids_ = QList<int>::fromStdList(std::list<int>(ids.begin(), ids.end()));
1787 ui_->checkBox_wmState->setVisible(
false);
1788 ui_->checkBox_alignPosesWithGPS->setVisible(
false);
1789 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
1790 ui_->checkBox_alignPosesWithGroundTruth->setVisible(
false);
1791 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
1792 ui_->checkBox_alignScansCloudsWithGroundTruth->setVisible(
false);
1793 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
1794 ui_->doubleSpinBox_optimizationScale->setVisible(
false);
1795 ui_->label_scale_title->setVisible(
false);
1796 ui_->label_rmse->setVisible(
false);
1797 ui_->label_rmse_title->setVisible(
false);
1798 ui_->checkBox_ignoreIntermediateNodes->setVisible(
false);
1799 ui_->label_ignoreINtermediateNdoes->setVisible(
false);
1800 ui_->label_alignPosesWithGPS->setVisible(
false);
1801 ui_->label_alignPosesWithGroundTruth->setVisible(
false);
1802 ui_->label_alignScansCloudsWithGroundTruth->setVisible(
false);
1803 ui_->toolButton_edit_priorA->setVisible(
false);
1804 ui_->toolButton_edit_priorB->setVisible(
false);
1805 ui_->toolButton_remove_priorA->setVisible(
false);
1806 ui_->toolButton_remove_priorB->setVisible(
false);
1807 ui_->menuEdit->setEnabled(
true);
1808 ui_->actionGenerate_3D_map_pcd->setEnabled(
true);
1809 ui_->actionExport->setEnabled(
true);
1810 ui_->actionExtract_images->setEnabled(
true);
1811 ui_->menuExport_poses->setEnabled(
false);
1812 ui_->menuExport_GPS->setEnabled(
false);
1813 ui_->actionPoses_KML->setEnabled(
false);
1814 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
1815 ui_->actionExport_saved_2D_map->setEnabled(
false);
1816 ui_->actionImport_2D_map->setEnabled(
false);
1817 ui_->actionRegenerate_optimized_2D_map->setEnabled(
false);
1818 ui_->actionView_optimized_mesh->setEnabled(
false);
1819 ui_->actionExport_optimized_mesh->setEnabled(
false);
1825 ui_->toolBox_statistics->clear();
1826 ui_->label_optimizeFrom->setText(tr(
"Root"));
1828 progressDialog->
appendText(tr(
"%1 ids loaded!").
arg(ids.size()));
1830 progressDialog->
appendText(tr(
"Loading all links..."));
1831 QApplication::processEvents();
1833 QApplication::processEvents();
1835 std::multimap<int, Link> unilinks;
1837 UDEBUG(
"%d total links loaded", (
int)unilinks.size());
1839 std::multimap<int, Link> links;
1840 for(std::multimap<int, Link>::iterator
iter=unilinks.begin();
iter!=unilinks.end(); ++
iter)
1842 links.insert(*
iter);
1845 links.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
1849 progressDialog->
appendText(tr(
"%1 links loaded!").
arg(unilinks.size()));
1851 progressDialog->
appendText(
"Loading Working Memory state...");
1852 QApplication::processEvents();
1854 QApplication::processEvents();
1860 double previousStamp = 0.0;
1864 progressDialog->
appendText(
"Loading Working Memory state... done!");
1866 progressDialog->
appendText(
"Loading info for all nodes...");
1867 QApplication::processEvents();
1869 QApplication::processEvents();
1871 int lastValidNodeId = 0;
1872 for(
int i=0;
i<
ids_.size(); ++
i)
1881 std::vector<float>
v;
1894 lastValidNodeId =
ids_[
i];
1898 if(wmStates.find(
ids_[
i]) != wmStates.end())
1904 ui_->checkBox_ignoreIntermediateNodes->setVisible(
true);
1905 ui_->label_ignoreINtermediateNdoes->setVisible(
true);
1911 if(!
p.isNull() && !previousPose.
isNull())
1916 if(previousStamp > 0.0 &&
s > 0.0)
1930 for(std::multimap<int, Link>::iterator jter=links.find(
ids_[
i]); jter!=links.end() && jter->first ==
ids_[
i]; ++jter)
1937 std::multimap<int, Link>::iterator invertedLinkIter =
graph::findLink(links, jter->second.to(), jter->second.from(),
false, jter->second.type());
1938 if( jter->second.isValid() &&
1939 ids.find(jter->second.from()) != ids.end() &&
1940 (ids.find(jter->second.to()) != ids.end() || jter->second.to()<0) &&
1942 invertedLinkIter != links.end() &&
1946 if(jter->second.userDataCompressed().cols == 0 &&
1947 invertedLinkIter->second.userDataCompressed().cols != 0)
1949 links_.insert(std::make_pair(invertedLinkIter->second.from(), invertedLinkIter->second));
1953 links_.insert(std::make_pair(
ids_[
i], jter->second));
1963 if(gps.
stamp() > 0.0)
1967 cv::Point3f
p(0.0
f,0.0
f,0.0
f);
1981 progressDialog->
appendText(
"Loading info for all nodes... done!");
1983 progressDialog->
appendText(
"Loading optimized poses and maps...");
1984 QApplication::processEvents();
1986 QApplication::processEvents();
1993 ui_->checkBox_alignPosesWithGPS->setVisible(!
gpsPoses_.empty());
1994 ui_->checkBox_alignPosesWithGPS->setEnabled(!
gpsPoses_.empty());
1995 ui_->label_alignPosesWithGPS->setVisible(!
gpsPoses_.empty());
2005 ui_->menuExport_GPS->setEnabled(
true);
2006 ui_->actionPoses_KML->setEnabled(
true);
2009 float xMin, yMin, cellSize;
2011 ui_->actionEdit_optimized_2D_map->setEnabled(hasMap);
2012 ui_->actionExport_saved_2D_map->setEnabled(hasMap);
2013 ui_->actionImport_2D_map->setEnabled(hasMap);
2018 ui_->actionView_optimized_mesh->setEnabled(
true);
2019 ui_->actionExport_optimized_mesh->setEnabled(
true);
2024 progressDialog->
appendText(
"Loading optimized poses and maps... done!");
2026 QApplication::processEvents();
2028 QApplication::processEvents();
2030 if(
ids_.size() &&
ui_->toolBox_statistics->isVisible())
2032 progressDialog->
appendText(
"Loading statistics...");
2033 QApplication::processEvents();
2035 QApplication::processEvents();
2037 UINFO(
"Update statistics...");
2040 progressDialog->
appendText(
"Loading statistics... done!");
2042 QApplication::processEvents();
2044 QApplication::processEvents();
2048 ui_->textEdit_info->clear();
2049 if(
ui_->textEdit_info->isVisible())
2051 progressDialog->
appendText(
"Update database info...");
2052 QApplication::processEvents();
2054 QApplication::processEvents();
2058 progressDialog->
appendText(
"Update database info... done!");
2060 QApplication::processEvents();
2062 QApplication::processEvents();
2069 bool nullPoses =
odomPoses_.begin()->second.isNull();
2072 if((!
iter->second.isNull() && nullPoses) ||
2073 (
iter->second.isNull() && !nullPoses))
2075 if(
iter->second.isNull())
2079 UWARN(
"Mixed valid and null poses! Ignoring graph...");
2094 ui_->spinBox_optimizationsFrom->setValue(
odomPoses_.begin()->first);
2099 if(lastValidNodeId>0)
2104 std::map<int, rtabmap::Transform> posesOut;
2105 std::multimap<int, rtabmap::Link> linksOut;
2106 UINFO(
"Get connected graph from %d (%d poses, %d links)", lastValidNodeId, (
int)
odomPoses_.size(), (
int)
links_.size());
2114 if(!posesOut.empty())
2116 bool optimizeFromGraphEnd = Parameters::defaultRGBDOptimizeFromGraphEnd();
2118 if(optimizeFromGraphEnd)
2120 ui_->spinBox_optimizationsFrom->setValue(posesOut.rbegin()->first);
2124 ui_->spinBox_optimizationsFrom->setValue(posesOut.lower_bound(1)->first);
2138 ui_->spinBox_indexA->setMinimum(0);
2139 ui_->spinBox_indexB->setMinimum(0);
2140 ui_->spinBox_indexA->setMaximum(
ids_.size()-1);
2141 ui_->spinBox_indexB->setMaximum(
ids_.size()-1);
2142 ui_->spinBox_indexA->setEnabled(
true);
2143 ui_->spinBox_indexB->setEnabled(
true);
2145 ui_->horizontalSlider_A->setMinimum(0);
2146 ui_->horizontalSlider_B->setMinimum(0);
2147 ui_->horizontalSlider_A->setMaximum(
ids_.size()-1);
2148 ui_->horizontalSlider_B->setMaximum(
ids_.size()-1);
2149 ui_->horizontalSlider_A->setEnabled(
true);
2150 ui_->horizontalSlider_B->setEnabled(
true);
2151 ui_->horizontalSlider_A->setSliderPosition(0);
2152 ui_->horizontalSlider_B->setSliderPosition(0);
2158 ui_->horizontalSlider_A->setEnabled(
false);
2159 ui_->horizontalSlider_B->setEnabled(
false);
2161 ui_->spinBox_indexA->setEnabled(
false);
2162 ui_->spinBox_indexB->setEnabled(
false);
2164 ui_->label_idA->setText(
"NaN");
2165 ui_->label_idB->setText(
"NaN");
2172 if(
ui_->graphViewer->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
2174 progressDialog->
appendText(
"Updating Graph View...");
2175 QApplication::processEvents();
2177 QApplication::processEvents();
2181 progressDialog->
appendText(
"Updating Graph View... done!");
2183 QApplication::processEvents();
2185 QApplication::processEvents();
2193 UINFO(
"Update database info...");
2196 if(
ui_->textEdit_info->toPlainText().isEmpty())
2203 ui_->textEdit_info->append(tr(
"Total odometry length:\t%1 m (approx. as graph has been reduced)").
arg(
infoTotalOdom_));
2210 int lastWordIdId = 0;
2217 ids.insert(lastWordIdId);
2218 std::list<VisualWord *> vws;
2222 wordsDim = vws.front()->getDescriptor().cols;
2223 wordsType = vws.front()->getDescriptor().type();
2229 ui_->textEdit_info->append(tr(
"Total time:\t\t%1").
arg(QDateTime::fromMSecsSinceEpoch(
infoTotalTime_*1000).toUTC().toString(
"hh:mm:ss.zzz")));
2235 ui_->textEdit_info->append(
"");
2239 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"));
2242 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"));
2245 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"));
2248 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"));
2251 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"));
2254 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"));
2257 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"));
2260 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"));
2263 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"));
2266 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"));
2269 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"));
2272 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"));
2273 mem = dbSize - total;
2274 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"));
2275 ui_->textEdit_info->append(
"");
2276 std::set<int> idsWithoutBad;
2278 int infoBadcountInLTM = 0;
2279 int infoBadCountInGraph = 0;
2280 for(
int i=0;
i<
ids_.size(); ++
i)
2282 if(idsWithoutBad.find(
ids_[
i]) == idsWithoutBad.end())
2284 ++infoBadcountInLTM;
2287 ++infoBadCountInGraph;
2291 ui_->textEdit_info->append(tr(
"%1 bad signatures in LTM").
arg(infoBadcountInLTM));
2292 ui_->textEdit_info->append(tr(
"%1 bad signatures in the global graph").
arg(infoBadCountInGraph));
2293 ui_->textEdit_info->append(
"");
2295 QFontMetrics metrics(
ui_->textEdit_info->font());
2296 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2297 int tabW =
ui_->textEdit_info->tabStopDistance();
2299 int tabW =
ui_->textEdit_info->tabStopWidth();
2301 for(ParametersMap::iterator
iter=parameters.begin();
iter!=parameters.end(); ++
iter)
2303 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2304 int strW = metrics.horizontalAdvance(QString(
iter->first.c_str()) +
"=");
2306 int strW = metrics.width(QString(
iter->first.c_str()) +
"=");
2308 ui_->textEdit_info->append(tr(
"%1=%2%3")
2310 .
arg(strW < tabW?
"\t\t\t\t":strW < tabW*2?
"\t\t\t":strW < tabW*3?
"\t\t":
"\t")
2311 .arg(
iter->second.c_str()));
2315 ui_->textEdit_info->moveCursor(QTextCursor::Start) ;
2316 ui_->textEdit_info->ensureCursorVisible() ;
2321 ui_->textEdit_info->clear();
2330 ui_->toolBox_statistics->clear();
2331 double firstStamp = 0.0;
2334 std::map<std::string, std::pair<std::vector<qreal>, std::vector<qreal> > > allData;
2335 std::map<std::string, int > allDataOi;
2337 for(std::map<
int, std::pair<std::map<std::string, float>,
double> >::
iterator jter=allStats.begin(); jter!=allStats.end(); ++jter)
2339 double stamp=jter->second.second;
2340 std::map<std::string, float> & statistics = jter->second.first;
2345 for(std::map<std::string, float>::iterator
iter=statistics.begin();
iter!=statistics.end(); ++
iter)
2347 if(allData.find(
iter->first) == allData.end())
2350 allData.insert(std::make_pair(
iter->first, std::make_pair(std::vector<qreal>(allStats.size(), 0.0f), std::vector<qreal>(allStats.size(), 0.0f) )));
2351 allDataOi.insert(std::make_pair(
iter->first, 0));
2354 int & oi = allDataOi.at(
iter->first);
2355 allData.at(
iter->first).first[oi] =
ui_->checkBox_timeStats->isChecked()?qreal(stamp-firstStamp):jter->first;
2356 allData.at(
iter->first).second[oi] =
iter->second;
2361 for(std::map<std::string, std::pair<std::vector<qreal>, std::vector<qreal> > >::
iterator iter=allData.begin();
iter!=allData.end(); ++
iter)
2363 int oi = allDataOi.at(
iter->first);
2364 iter->second.first.resize(oi);
2365 iter->second.second.resize(oi);
2366 ui_->toolBox_statistics->updateStat(
iter->first.c_str(),
iter->second.first,
iter->second.second,
true);
2374 QColor
c = QColorDialog::getColor(
ui_->lineEdit_obstacleColor->text(),
this);
2377 ui_->lineEdit_obstacleColor->setText(
c.name());
2382 QColor
c = QColorDialog::getColor(
ui_->lineEdit_groundColor->text(),
this);
2385 ui_->lineEdit_groundColor->setText(
c.name());
2390 QColor
c = QColorDialog::getColor(
ui_->lineEdit_emptyColor->text(),
this);
2393 ui_->lineEdit_emptyColor->setText(
c.name());
2398 QColor
c = QColorDialog::getColor(
ui_->lineEdit_frontierColor->text(),
this);
2401 ui_->lineEdit_frontierColor->setText(
c.name());
2415 data.uncompressData();
2416 if(!
data.depthRaw().empty())
2469 types.push_back(
"Map's graph (see Graph View)");
2470 types.push_back(
"Odometry");
2473 types.push_back(
"Ground Truth");
2476 QString
type = QInputDialog::getItem(
this, tr(
"Which poses?"), tr(
"Poses:"), types, 0,
false, &ok);
2482 bool groundTruth =
type.compare(
"Ground Truth") == 0;
2486 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No ground truth poses in database?!"));
2492 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
2494 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No graph in database?!"));
2500 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No odometry poses in database?!"));
2508 QMessageBox::warning(
this, tr(
"Cannot export poses"), tr(
"No GPS in database?!"));
2512 std::map<int, rtabmap::Transform>
graph;
2528 pcl::PointCloud<pcl::PointXYZ> cloud1, cloud2;
2529 cloud1.resize(
graph.size());
2530 cloud2.resize(
graph.size());
2535 std::map<int, Transform>::iterator iter2 =
graph.find(
iter->first);
2536 if(iter2!=
graph.end())
2540 idFirst =
iter->first;
2542 cloud1[oi] = pcl::PointXYZ(
iter->second.x(),
iter->second.y(),
iter->second.z());
2543 cloud2[oi++] = pcl::PointXYZ(iter2->second.x(), iter2->second.y(), iter2->second.z());
2560 std::map<int, GPS>
values;
2568 double bearing = -(
iter->second.theta()*180.0/
M_PI-90.0);
2579 std::vector<float>
v;
2586 QString output =
pathDatabase_ + QDir::separator() +
"poses.kml";
2587 QString
path = QFileDialog::getSaveFileName(
2591 tr(
"Google Earth file (*.kml)"));
2599 QMessageBox::information(
this,
2600 tr(
"Export poses..."),
2601 tr(
"GPS coordinates saved to \"%1\".")
2606 QMessageBox::information(
this,
2607 tr(
"Export poses..."),
2608 tr(
"Failed to save GPS coordinates to \"%1\"!")
2616 std::map<int, Transform> optimizedPoses;
2632 if((
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked()) ||
2633 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked()))
2636 if(
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
2637 ui_->checkBox_alignPosesWithGPS->isChecked())
2645 float translational_rmse = 0.0f;
2646 float translational_mean = 0.0f;
2647 float translational_median = 0.0f;
2648 float translational_std = 0.0f;
2649 float translational_min = 0.0f;
2650 float translational_max = 0.0f;
2651 float rotational_rmse = 0.0f;
2652 float rotational_mean = 0.0f;
2653 float rotational_median = 0.0f;
2654 float rotational_std = 0.0f;
2655 float rotational_min = 0.0f;
2656 float rotational_max = 0.0f;
2663 translational_median,
2676 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2678 iter->second = gtToMap *
iter->second;
2685 if(optimizedPoses.size())
2687 std::map<int, Transform> localTransforms;
2689 items.push_back(
"Robot (base frame)");
2690 items.push_back(
"Camera");
2691 items.push_back(
"Scan");
2693 QString item = QInputDialog::getItem(
this, tr(
"Export Poses"), tr(
"Frame: "), items, 0,
false, &ok);
2694 if(!ok || item.isEmpty())
2698 if(item.compare(
"Robot (base frame)") != 0)
2700 bool cameraFrame = item.compare(
"Camera") == 0;
2701 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
2706 std::vector<CameraModel> models;
2707 std::vector<StereoCameraModel> stereoModels;
2710 if((models.size() == 1 &&
2711 !models.at(0).localTransform().isNull()))
2713 localTransform = models.at(0).localTransform();
2715 else if(stereoModels.size() == 1 &&
2716 !stereoModels[0].localTransform().isNull())
2718 localTransform = stereoModels[0].localTransform();
2720 else if(models.size()>1)
2722 UWARN(
"Multi-camera is not supported (node %d)",
iter->first);
2726 UWARN(
"Calibration not valid for node %d",
iter->first);
2731 UWARN(
"Missing calibration for node %d",
iter->first);
2739 localTransform =
info.localTransform();
2743 UWARN(
"Missing scan info for node %d",
iter->first);
2747 if(!localTransform.
isNull())
2749 localTransforms.insert(std::make_pair(
iter->first, localTransform));
2752 if(localTransforms.empty())
2754 QMessageBox::warning(
this,
2756 tr(
"Could not find any \"%1\" frame, exporting in Robot frame instead.").
arg(item));
2760 std::map<int, Transform> poses;
2761 std::multimap<int, Link> links;
2762 if(localTransforms.empty())
2764 poses = optimizedPoses;
2773 for(std::map<int, Transform>::iterator
iter=localTransforms.begin();
iter!=localTransforms.end(); ++
iter)
2775 poses.insert(std::make_pair(
iter->first, optimizedPoses.at(
iter->first) *
iter->second));
2783 std::multimap<int, Link>::iterator inserted = links.insert(*
iter);
2784 int from =
iter->second.from();
2785 int to =
iter->second.to();
2786 inserted->second.setTransform(localTransforms.at(from).inverse()*
iter->second.transform()*localTransforms.at(to));
2792 if(
format != 4 &&
format != 11 && !poses.empty() && poses.begin()->first<0)
2794 UWARN(
"Only g2o format (4) and RGBD format with ID format can export landmarks, they are ignored with format %d",
format);
2795 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end() &&
iter->first < 0;)
2797 poses.erase(
iter++);
2799 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end();)
2801 if(
iter->second.from() < 0 ||
iter->second.to() < 0)
2803 links.erase(
iter++);
2812 std::map<int, double> stamps;
2815 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
2819 stamps.insert(std::make_pair(
iter->first, 0));
2828 std::vector<float>
v;
2833 stamps.insert(std::make_pair(
iter->first, stamp));
2837 if(stamps.size()!=poses.size())
2839 QMessageBox::warning(
this, tr(
"Export poses..."), tr(
"Poses (%1) and stamps (%2) have not the same size! Cannot export in RGB-D SLAM format.")
2840 .
arg(poses.size()).
arg(stamps.size()));
2846 QString suffix =
odometry?
"_odom":groundTruth?
"_gt":
"";
2847 output = output.arg(suffix);
2849 QString
path = QFileDialog::getSaveFileName(
2853 format == 3?tr(
"TORO file (*.graph)"):
format==4?tr(
"g2o file (*.g2o)"):tr(
"Text file (*.txt)"));
2857 if(QFileInfo(
path).suffix() ==
"")
2877 QMessageBox::information(
this,
2878 tr(
"Export poses..."),
2879 tr(
"%1 saved to \"%2\".")
2885 QMessageBox::information(
this,
2886 tr(
"Export poses..."),
2887 tr(
"Failed to save %1 to \"%2\"!")
2909 QString
path = QFileDialog::getSaveFileName(
2913 format==0?tr(
"Raw format (*.txt)"):tr(
"Google Earth file (*.kml)"));
2921 QMessageBox::information(
this,
2922 tr(
"Export poses..."),
2923 tr(
"GPS coordinates saved to \"%1\".")
2928 QMessageBox::information(
this,
2929 tr(
"Export poses..."),
2930 tr(
"Failed to save GPS coordinates to \"%1\"!")
2941 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
2947 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
2948 tr(
"The database has too old version (%1) to saved "
2955 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
2956 tr(
"The database has modified links and/or modified local "
2957 "occupancy grids, the 2D optimized map cannot be modified."));
2961 float xMin, yMin, cellSize;
2965 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"The database doesn't contain a saved 2D map."));
2970 cv::Mat map8UFlip, map8URotated;
2971 cv::flip(map8U, map8UFlip, 0);
2972 if(!
ui_->graphViewer->isOrientationENU())
2975 cv::transpose(map8UFlip, map8URotated);
2976 cv::flip(map8URotated, map8URotated, 0);
2980 map8URotated = map8UFlip;
2989 if(!
ui_->graphViewer->isOrientationENU())
2992 cv::transpose(mapModified, map8URotated);
2993 cv::flip(map8URotated, map8URotated, 1);
2997 map8URotated = mapModified;
2999 cv::flip(map8URotated, map8UFlip, 0);
3001 UASSERT(map8UFlip.type() == map8U.type());
3002 UASSERT(map8UFlip.cols == map8U.cols);
3003 UASSERT(map8UFlip.rows == map8U.rows);
3010 QMessageBox::information(
this, tr(
"Edit 2D map"), tr(
"Map updated!"));
3013 int cropRadius =
ui_->spinBox_cropRadius->value();
3014 QMessageBox::StandardButton
b = QMessageBox::question(
this,
3015 tr(
"Crop empty space"),
3016 tr(
"Do you want to clear empty space from local occupancy grids and laser scans?\n\n"
3018 " * 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"
3019 " * 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"
3021 " * Cropping the laser scans cannot be reverted after the viewer is closed and changes have been saved.\n\n"
3023 " Crop radius = %1 pixels\n\n"
3024 "Press \"Yes\" to filter only grids.\n"
3025 "Press \"Yes to All\" to filter both grids and laser scans.\n").
arg(cropRadius),
3026 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No, QMessageBox::No);
3027 if(
b == QMessageBox::Yes ||
b == QMessageBox::YesToAll)
3035 progressDialog.show();
3037 progressDialog.
appendText(QString(
"Cropping empty space... %1 scans to filter").
arg(poses.size()));
3038 progressDialog.setMinimumWidth(800);
3039 QApplication::processEvents();
3041 UINFO(
"Cropping empty space... poses=%d cropRadius=%d", poses.size(), cropRadius);
3043 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end() && !progressDialog.
isCanceled(); ++
iter)
3047 cv::Mat gridObstacles;
3054 data.uncompressData(0,0,&scan,0,&gridGround,&gridObstacles,&gridEmpty);
3060 if(!gridObstacles.empty())
3062 cv::Mat filtered = cv::Mat(1, gridObstacles.cols, gridObstacles.type());
3064 for(
int i=0;
i<gridObstacles.cols; ++
i)
3066 const float * ptr = gridObstacles.ptr<
float>(0,
i);
3067 cv::Point3f pt(ptr[0], ptr[1], gridObstacles.channels()==2?0:ptr[2]);
3070 int x =
int((pt.x - xMin) / cellSize + 0.5f);
3071 int y =
int((pt.y - yMin) / cellSize + 0.5f);
3073 if(
x>=0 &&
x<map8S.cols &&
3074 y>=0 &&
y<map8S.rows)
3076 bool obstacleDetected =
false;
3078 for(
int j=-cropRadius;
j<=cropRadius && !obstacleDetected; ++
j)
3080 for(
int k=-cropRadius; k<=cropRadius && !obstacleDetected; ++k)
3082 if(
x+
j>=0 &&
x+
j<map8S.cols &&
3083 y+k>=0 &&
y+k<map8S.rows &&
3084 map8S.at<
unsigned char>(
y+k,
x+
j) == 100)
3086 obstacleDetected =
true;
3091 if(map8S.at<
unsigned char>(
y,
x) != 0 || obstacleDetected)
3100 if(oi != gridObstacles.cols)
3102 progressDialog.
appendText(QString(
"Grid %1 filtered %2 pts -> %3 pts").
arg(
iter->first).
arg(gridObstacles.cols).arg(oi));
3103 UINFO(
"Grid %d filtered %d -> %d",
iter->first, gridObstacles.cols, oi);
3106 cv::Mat newObstacles = cv::Mat(filtered,
cv::Range::all(), cv::Range(0, oi));
3110 value.obstacleCells = newObstacles;
3122 if(
b == QMessageBox::YesToAll && !scan.
isEmpty())
3126 cv::Mat filtered = cv::Mat(1, scan.
size(), scan.
dataType());
3128 for(
int i=0;
i<scan.
size(); ++
i)
3130 const float * ptr = scan.
data().ptr<
float>(0,
i);
3131 cv::Point3f pt(ptr[0], ptr[1], scan.
is2d()?0:ptr[2]);
3134 int x =
int((pt.x - xMin) / cellSize + 0.5f);
3135 int y =
int((pt.y - yMin) / cellSize + 0.5f);
3137 if(
x>=0 &&
x<map8S.cols &&
3138 y>=0 &&
y<map8S.rows)
3140 bool obstacleDetected =
false;
3142 for(
int j=-cropRadius;
j<=cropRadius && !obstacleDetected; ++
j)
3144 for(
int k=-cropRadius; k<=cropRadius && !obstacleDetected; ++k)
3146 if(
x+
j>=0 &&
x+
j<map8S.cols &&
3147 y+k>=0 &&
y+k<map8S.rows &&
3148 map8S.at<
unsigned char>(
y+k,
x+
j) == 100)
3150 obstacleDetected =
true;
3155 if(map8S.at<
unsigned char>(
y,
x) != 0 || obstacleDetected)
3164 if(oi != scan.
size())
3167 UINFO(
"Scan %d filtered %d -> %d",
iter->first, scan.
size(), oi);
3197 QApplication::processEvents();
3217 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3221 float xMin, yMin, cellSize;
3225 QMessageBox::warning(
this, tr(
"Cannot export 2D map"), tr(
"The database doesn't contain a saved 2D map."));
3231 QString
path = QFileDialog::getSaveFileName(
3239 if(QFileInfo(
path).suffix() ==
"")
3243 cv::imwrite(
path.toStdString(), map8U);
3246 QString yaml =
info.absolutePath() +
"/" +
info.baseName() +
".yaml";
3248 float occupancyThr = Parameters::defaultGridGlobalOccupancyThr();
3249 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridGlobalOccupancyThr(), occupancyThr);
3252 file.open (yaml.toStdString());
3253 file <<
"image: " <<
info.baseName().toStdString() <<
".pgm" << std::endl;
3254 file <<
"resolution: " << cellSize << std::endl;
3255 file <<
"origin: [" << xMin <<
", " << yMin <<
", 0.0]" << std::endl;
3256 file <<
"negate: 0" << std::endl;
3257 file <<
"occupied_thresh: " << occupancyThr << std::endl;
3258 file <<
"free_thresh: 0.196" << std::endl;
3263 QMessageBox::information(
this, tr(
"Export 2D map"), tr(
"Exported %1 and %2!").
arg(
path).
arg(yaml));
3272 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3278 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
3279 tr(
"The database has too old version (%1) to saved "
3286 QMessageBox::warning(
this, tr(
"Cannot import 2D map"),
3287 tr(
"The database has modified links and/or modified local "
3288 "occupancy grids, the 2D optimized map cannot be modified."));
3292 float xMin, yMin, cellSize;
3296 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"The database doesn't contain a saved 2D map."));
3300 QString
path = QFileDialog::getOpenFileName(
3307 cv::Mat map8U = cv::imread(
path.toStdString(), cv::IMREAD_UNCHANGED);
3310 if(mapOrg.cols == map.cols && mapOrg.rows == map8U.rows)
3313 QMessageBox::information(
this, tr(
"Import 2D map"), tr(
"Imported %1!").
arg(
path));
3317 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));
3327 QMessageBox::warning(
this, tr(
"Cannot import 2D map"), tr(
"A database must must loaded first...\nUse File->Open database."));
3333 QMessageBox::warning(
this, tr(
"Cannot edit 2D map"),
3334 tr(
"The database has too old version (%1) to saved "
3341 QMessageBox::warning(
this, tr(
"Cannot import 2D map"),
3342 tr(
"The database has modified links and/or modified local "
3343 "occupancy grids, the 2D optimized map cannot be modified. Try "
3344 "closing the database and re-open it to save the changes."));
3350 QMessageBox::warning(
this, tr(
"Cannot regenerate 2D map"),
3351 tr(
"Graph is empty, make sure you opened the "
3352 "Graph View and there is a map shown."));
3357 #ifdef RTABMAP_OCTOMAP
3359 types.push_back(
"Default occupancy grid");
3360 types.push_back(
"From OctoMap projection");
3362 QString
type = QInputDialog::getItem(
this, tr(
"Which type?"), tr(
"Type:"), types, 0,
false, &ok);
3370 UINFO(
"Update local maps list...");
3373 float gridCellSize = Parameters::defaultGridCellSize();
3374 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridCellSize(), gridCellSize);
3375 #ifdef RTABMAP_OCTOMAP
3376 if(
type.compare(
"From OctoMap projection") == 0)
3389 map = grid.
getMap(xMin, yMin);
3394 QMessageBox::information(
this, tr(
"Regenerate 2D map"), tr(
"Failed to renegerate the map, resulting map is empty!"));
3404 lastlocalizationPose =
graphes_.back().rbegin()->second;
3409 QMessageBox::information(
this, tr(
"Regenerate 2D map"), tr(
"Map regenerated!"));
3410 ui_->actionEdit_optimized_2D_map->setEnabled(
true);
3411 ui_->actionExport_saved_2D_map->setEnabled(
true);
3412 ui_->actionImport_2D_map->setEnabled(
true);
3420 QMessageBox::warning(
this, tr(
"Cannot view optimized mesh"), tr(
"A database must must loaded first...\nUse File->Open database."));
3424 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
3425 #if PCL_VERSION_COMPARE(>=, 1, 8, 0)
3426 std::vector<std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > > texCoords;
3428 std::vector<std::vector<Eigen::Vector2f> > texCoords;
3432 if(cloudMat.empty())
3434 QMessageBox::warning(
this, tr(
"Cannot view optimized mesh"), tr(
"The database doesn't contain a saved optimized mesh."));
3439 viewer->setWindowFlags(Qt::Window);
3440 viewer->setAttribute(Qt::WA_DeleteOnClose);
3442 if(!textures.empty())
3446 viewer->setWindowTitle(
"Optimized Textured Mesh");
3450 else if(polygons.size() == 1)
3453 viewer->setWindowTitle(
"Optimized Mesh");
3461 viewer->setWindowTitle(
"Optimized Point Cloud");
3472 QMessageBox::warning(
this, tr(
"Cannot export optimized mesh"), tr(
"A database must must loaded first...\nUse File->Open database."));
3476 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
3477 #if PCL_VERSION_COMPARE(>=, 1, 8, 0)
3478 std::vector<std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > > texCoords;
3480 std::vector<std::vector<Eigen::Vector2f> > texCoords;
3484 if(cloudMat.empty())
3486 QMessageBox::warning(
this, tr(
"Cannot export optimized mesh"), tr(
"The database doesn't contain a saved optimized mesh."));
3492 if(!textures.empty())
3495 QString
path = QFileDialog::getSaveFileName(
3499 tr(
"Mesh (*.obj)"));
3503 if(QFileInfo(
path).suffix() ==
"")
3507 QString baseName = QFileInfo(
path).baseName();
3508 if(mesh->tex_materials.size() == 1)
3510 mesh->tex_materials.at(0).tex_file = baseName.toStdString() +
".png";
3511 cv::imwrite((QFileInfo(
path).absoluteDir().absolutePath()+QDir::separator()+baseName).toStdString() +
".png", textures);
3515 for(
unsigned int i=0;
i<mesh->tex_materials.size(); ++
i)
3517 mesh->tex_materials.at(
i).tex_file = (baseName+QDir::separator()+QString::number(
i)+
".png").toStdString();
3518 UASSERT((
i+1)*textures.rows <= (
unsigned int)textures.cols);
3519 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)));
3522 pcl::io::saveOBJFile(
path.toStdString(), *mesh);
3524 QMessageBox::information(
this, tr(
"Export Textured Mesh"), tr(
"Exported %1!").
arg(
path));
3527 else if(polygons.size() == 1)
3530 QString
path = QFileDialog::getSaveFileName(
3534 tr(
"Mesh (*.ply)"));
3538 if(QFileInfo(
path).suffix() ==
"")
3542 pcl::io::savePLYFileBinary(
path.toStdString(), *mesh);
3543 QMessageBox::information(
this, tr(
"Export Mesh"), tr(
"Exported %1!").
arg(
path));
3548 QString
path = QFileDialog::getSaveFileName(
3552 tr(
"Point cloud data (*.ply *.pcd)"));
3556 if(QFileInfo(
path).suffix() ==
"")
3560 bool success =
false;
3562 if(QFileInfo(
path).suffix() ==
"pcd")
3564 success = pcl::io::savePCDFile(
path.toStdString(), *cloud) == 0;
3568 success = pcl::io::savePLYFile(
path.toStdString(), *cloud) == 0;
3572 QMessageBox::information(
this, tr(
"Export Point Cloud"), tr(
"Exported %1!").
arg(
path));
3576 QMessageBox::critical(
this, tr(
"Export Point Cloud"), tr(
"Failed exporting %1!").
arg(
path));
3587 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
3594 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
3596 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
3601 std::map<int, Transform> optimizedPoses;
3602 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
3603 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked()
3612 if(
ui_->groupBox_posefiltering->isChecked())
3615 ui_->doubleSpinBox_posefilteringRadius->value(),
3616 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
3622 if(optimizedPoses.size() > 0)
3628 std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
3629 std::map<int, pcl::PolygonMesh::Ptr> meshes;
3630 std::map<int, pcl::TextureMesh::Ptr> textureMeshes;
3631 std::vector<std::map<int, pcl::PointXY> > textureVertexToPixels;
3637 QMap<int, Signature>(),
3638 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
3639 std::map<int, LaserScan>(),
3641 ui_->parameters_toolbox->getParameters(),
3645 textureVertexToPixels))
3647 if(textureMeshes.size())
3651 cv::Mat globalTextures;
3652 pcl::TextureMeshPtr textureMesh = textureMeshes.at(0);
3653 if(textureMesh->tex_materials.size()>1)
3657 std::map<int, cv::Mat>(),
3658 std::map<
int, std::vector<CameraModel> >(),
3663 textureVertexToPixels,
3676 textureMesh->tex_coordinates,
3678 QMessageBox::information(
this, tr(
"Update Optimized Textured Mesh"), tr(
"Updated!"));
3679 ui_->actionView_optimized_mesh->setEnabled(
true);
3680 ui_->actionExport_optimized_mesh->setEnabled(
true);
3683 else if(meshes.size())
3686 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons(1);
3689 QMessageBox::information(
this, tr(
"Update Optimized Mesh"), tr(
"Updated!"));
3690 ui_->actionView_optimized_mesh->setEnabled(
true);
3691 ui_->actionExport_optimized_mesh->setEnabled(
true);
3694 else if(clouds.size())
3698 QMessageBox::information(
this, tr(
"Update Optimized PointCloud"), tr(
"Updated!"));
3699 ui_->actionView_optimized_mesh->setEnabled(
true);
3700 ui_->actionExport_optimized_mesh->setEnabled(
true);
3705 QMessageBox::critical(
this, tr(
"Update Optimized Mesh"), tr(
"Nothing to save!"));
3712 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
3720 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"A database must must loaded first...\nUse File->Open database."));
3724 QString
path = QFileDialog::getSaveFileName(
this, tr(
"Save File"),
pathDatabase_+
"/Graph.dot", tr(
"Graphiz file (*.dot)"));
3735 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
3739 int id = QInputDialog::getInt(
this, tr(
"Around which location?"), tr(
"Location ID"),
ids_.first(),
ids_.first(),
ids_.last(), 1, &ok);
3743 int margin = QInputDialog::getInt(
this, tr(
"Depth around the location?"), tr(
"Margin"), 4, 1, 100, 1, &ok);
3746 QString
path = QFileDialog::getSaveFileName(
this, tr(
"Save File"),
pathDatabase_+
"/Graph" + QString::number(
id) +
".dot", tr(
"Graphiz file (*.dot)"));
3747 if(!
path.isEmpty() &&
id>0)
3749 std::map<int, int> ids;
3750 std::list<int> curentMarginList;
3751 std::set<int> currentMargin;
3752 std::set<int> nextMargin;
3753 nextMargin.insert(
id);
3755 while((margin == 0 ||
m < margin) && nextMargin.size())
3757 curentMarginList = std::list<int>(nextMargin.rbegin(), nextMargin.rend());
3760 for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end(); ++jter)
3762 if(ids.find(*jter) == ids.end())
3764 std::multimap<int, Link> links;
3765 ids.insert(std::pair<int, int>(*jter,
m));
3771 for(std::multimap<int, Link>::const_iterator
iter=links.begin();
iter!=links.end(); ++
iter)
3779 nextMargin.insert(
iter->first);
3784 if(currentMargin.insert(
iter->first).second)
3786 curentMarginList.push_back(
iter->first);
3798 ids.insert(std::pair<int,int>(
id, 0));
3799 std::set<int> idsSet;
3800 for(std::map<int, int>::iterator
iter = ids.begin();
iter!=ids.end(); ++
iter)
3802 idsSet.insert(idsSet.end(),
iter->first);
3805 UINFO(
"idsSet=%d", idsSet.size());
3810 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for signature %1.").
arg(
id));
3825 progressDialog.show();
3829 plot->setWindowFlags(Qt::Window);
3830 plot->setWindowTitle(
"Local Occupancy Grid Generation Time (ms)");
3831 plot->setAttribute(Qt::WA_DeleteOnClose);
3837 plotCells->setWindowFlags(Qt::Window);
3838 plotCells->setWindowTitle(
"Occupancy Cells");
3839 plotCells->setAttribute(Qt::WA_DeleteOnClose);
3846 double decompressionTime = 0;
3847 double gridCreationTime = 0;
3854 data.uncompressData();
3855 decompressionTime =
timer.ticks()*1000.0;
3868 s.setPose(odomPose);
3869 cv::Mat ground, obstacles,
empty;
3870 cv::Point3f viewpoint;
3873 if(
ui_->checkBox_grid_regenerateFromSavedGrid->isChecked() &&
s.sensorData().gridCellSize() > 0.0f)
3881 if(
s.sensorData().cameraModels().size())
3885 for(
unsigned int i=0;
i<
s.sensorData().cameraModels().
size(); ++
i)
3887 const Transform &
t =
s.sensorData().cameraModels()[
i].localTransform();
3890 viewpoint.x +=
t.
x();
3891 viewpoint.y +=
t.
y();
3892 viewpoint.z +=
t.z();
3903 else if(
s.sensorData().cameraModels().size())
3907 for(
unsigned int i=0;
i<
s.sensorData().stereoCameraModels().
size(); ++
i)
3909 const Transform &
t =
s.sensorData().stereoCameraModels()[
i].localTransform();
3912 viewpoint.x +=
t.
x();
3913 viewpoint.y +=
t.
y();
3914 viewpoint.z +=
t.z();
3934 gridCreationTime =
timer.ticks()*1000.0;
3936 msg = QString(
"Generated local occupancy grid map %1/%2").arg(
i+1).arg((
int)
ids_.size());
3947 decompressionCurve->
addValue(
ids_.at(
i), decompressionTime);
3950 if(
ids_.size() < 50 || (
i+1) % 25 == 0)
3952 QApplication::processEvents();
3973 if(
ids_.size() == 0)
3975 UWARN(
"ids_ is empty!");
3980 idsSet.insert(
ids_.at(
ui_->horizontalSlider_A->value()));
3981 idsSet.insert(
ids_.at(
ui_->horizontalSlider_B->value()));
3982 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
3983 QList<int> ids(idsSet.begin(), idsSet.end());
3985 QList<int> ids = idsSet.toList();
3990 progressDialog.show();
3992 for(
int i =0;
i<ids.size(); ++
i)
3996 data.uncompressData();
4009 s.setPose(odomPose);
4010 cv::Mat ground, obstacles,
empty;
4011 cv::Point3f viewpoint;
4013 if(
ui_->checkBox_grid_regenerateFromSavedGrid->isChecked() &&
s.sensorData().gridCellSize() > 0.0f)
4021 if(
s.sensorData().cameraModels().size())
4025 for(
unsigned int i=0;
i<
s.sensorData().cameraModels().
size(); ++
i)
4027 const Transform &
t =
s.sensorData().cameraModels()[
i].localTransform();
4030 viewpoint.x +=
t.
x();
4031 viewpoint.y +=
t.
y();
4032 viewpoint.z +=
t.z();
4043 else if(
s.sensorData().stereoCameraModels().size())
4047 for(
unsigned int i=0;
i<
s.sensorData().stereoCameraModels().
size(); ++
i)
4049 const Transform &
t =
s.sensorData().stereoCameraModels()[
i].localTransform();
4052 viewpoint.x +=
t.
x();
4053 viewpoint.y +=
t.
y();
4054 viewpoint.z +=
t.z();
4075 msg = QString(
"Generated local occupancy grid map %1/%2 (%3s)").arg(
i+1).arg((
int)ids.size()).arg(
time.ticks());
4080 QApplication::processEvents();
4099 QMessageBox::warning(
this, tr(
"Cannot view 3D map"), tr(
"The database is empty..."));
4106 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4108 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4113 std::map<int, Transform> optimizedPoses;
4114 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
4115 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
4124 if(
ui_->groupBox_posefiltering->isChecked())
4127 ui_->doubleSpinBox_posefilteringRadius->value(),
4128 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
4130 if(optimizedPoses.size() > 0)
4136 QMap<int, Signature>(),
4137 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
4138 std::map<int, LaserScan>(),
4140 ui_->parameters_toolbox->getParameters());
4144 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
4152 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"The database is empty..."));
4159 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4161 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4166 std::map<int, Transform> optimizedPoses;
4167 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
4168 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
4177 if(
ui_->groupBox_posefiltering->isChecked())
4180 ui_->doubleSpinBox_posefilteringRadius->value(),
4181 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
4183 if(optimizedPoses.size() > 0)
4189 QMap<int, Signature>(),
4190 std::map<
int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
4191 std::map<int, LaserScan>(),
4193 ui_->parameters_toolbox->getParameters());
4197 QMessageBox::critical(
this, tr(
"Error"), tr(
"No neighbors found for node %1.").
arg(
ui_->spinBox_optimizationsFrom->value()));
4206 if(
graphes_.empty() ||
ui_->horizontalSlider_iterations->maximum() != (
int)
graphes_.size()-1)
4208 QMessageBox::warning(
this, tr(
"Cannot generate a graph"), tr(
"No graph in database?!"));
4213 std::map<int, Transform> optimizedPoses =
graphes_.back();
4216 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4219 progressDialog->setMinimumWidth(800);
4220 progressDialog->show();
4222 const ParametersMap & parameters =
ui_->parameters_toolbox->getParameters();
4223 bool loopCovLimited = Parameters::defaultRGBDLoopCovLimited();
4224 Parameters::parse(parameters, Parameters::kRGBDLoopCovLimited(), loopCovLimited);
4230 int iterations =
ui_->spinBox_detectMore_iterations->value();
4233 std::multimap<int, int> checkedLoopClosures;
4234 std::pair<int, int> lastAdded(0,0);
4235 bool intraSession =
ui_->checkBox_detectMore_intraSession->isChecked();
4236 bool interSession =
ui_->checkBox_detectMore_interSession->isChecked();
4237 bool useOptimizedGraphAsGuess =
ui_->checkBox_opt_graph_as_guess->isChecked();
4238 if(!interSession && !intraSession)
4240 QMessageBox::warning(
this, tr(
"Cannot detect more loop closures"), tr(
"Intra and inter session parameters are disabled! Enable one or both."));
4244 for(
int n=0;
n<iterations; ++
n)
4246 UINFO(
"iteration %d/%d",
n+1, iterations);
4248 std::map<int, Transform>(optimizedPoses.upper_bound(0), optimizedPoses.end()),
4249 ui_->doubleSpinBox_detectMore_radius->value(),
4250 ui_->doubleSpinBox_detectMore_angle->value()*CV_PI/180.0);
4253 progressDialog->
appendText(tr(
"Looking for more loop closures, %1 clusters found.").
arg(clusters.size()));
4254 QApplication::processEvents();
4260 std::set<int> addedLinks;
4262 for(std::multimap<int, int>::iterator
iter=clusters.begin();
iter!= clusters.end() && !progressDialog->
isCanceled(); ++
iter, ++
i)
4264 int from =
iter->first;
4265 int to =
iter->second;
4268 from =
iter->second;
4275 if((interSession && mapIdFrom != mapIdTo) ||
4276 (intraSession && mapIdFrom == mapIdTo))
4282 addedLinks.find(from) == addedLinks.end() &&
4283 addedLinks.find(to) == addedLinks.end())
4286 Transform delta = optimizedPoses.at(from).inverse() * optimizedPoses.at(to);
4287 if(
delta.getNorm() <
ui_->doubleSpinBox_detectMore_radius->value() &&
4288 delta.getNorm() >=
ui_->doubleSpinBox_detectMore_radiusMin->value())
4290 checkedLoopClosures.insert(std::make_pair(from, to));
4293 UINFO(
"Added new loop closure between %d and %d.", from, to);
4295 addedLinks.insert(from);
4296 addedLinks.insert(to);
4297 lastAdded.first = from;
4298 lastAdded.second = to;
4300 progressDialog->
appendText(tr(
"Detected loop closure %1->%2! (%3/%4)").
arg(from).
arg(to).
arg(
i+1).
arg(clusters.size()));
4301 QApplication::processEvents();
4312 QApplication::processEvents();
4315 UINFO(
"Iteration %d/%d: added %d loop closures.",
n+1, iterations, (
int)addedLinks.size()/2);
4316 progressDialog->
appendText(tr(
"Iteration %1/%2: Detected %3 loop closures!").
arg(
n+1).
arg(iterations).
arg(addedLinks.size()/2));
4317 if(addedLinks.size() == 0)
4336 UINFO(
"Total added %d loop closures.", added);
4338 progressDialog->
appendText(tr(
"Total new loop closures detected=%1").
arg(added));
4345 QList<rtabmap::Link> links;
4346 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4351 links.push_back(
iter->second);
4359 QList<rtabmap::Link> links;
4360 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4365 iter->second.from() !=
iter->second.to())
4367 links.push_back(
iter->second);
4375 QList<rtabmap::Link> links;
4376 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4380 links.push_back(
iter->second);
4390 cv::Mat infMatrix = links.first().infMatrix();
4393 if(dialog.exec() != QDialog::Accepted)
4399 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4402 progressDialog->setMinimumWidth(800);
4403 progressDialog->show();
4407 for(
int i=0;
i<links.size(); ++
i)
4409 int from = links[
i].from();
4410 int to = links[
i].to();
4415 UERROR(
"Not found link! (%d->%d)", from, to);
4429 if(
iter->second.to() == currentLink.
to() &&
4430 iter->second.type() == currentLink.
type())
4432 iter->second = currentLink;
4445 QApplication::processEvents();
4454 progressDialog->
appendText(
"Refining links finished!");
4465 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4469 int minId =
iter->second.from()>
iter->second.to()?
iter->second.to():
iter->second.from();
4470 int maxId =
iter->second.from()<
iter->second.to()?
iter->second.to():
iter->second.from();
4471 if(minNodeId == 0 || minNodeId > minId)
4475 if(maxNodeId == 0 || maxNodeId < maxId)
4500 for(std::multimap<int, Link>::iterator
iter=allLinks.begin();
iter!=allLinks.end(); ++
iter)
4505 int from =
iter->second.from();
4506 int to =
iter->second.to();
4510 ((from >= minNodeId && from <= maxNodeId) ||
4511 (to >= minNodeId && to <= maxNodeId))) ||
4513 ((mapFrom >= minMapId && mapFrom <= maxMapId) ||
4514 (mapTo >= minMapId && mapTo <= maxMapId)))) &&
4515 ((intra && mapTo == mapFrom) ||
4516 (inter && mapTo != mapFrom)))
4518 links.push_back(
iter->second);
4524 QMessageBox::warning(
this, tr(
"Refine links"), tr(
"No links found matching the requested parameters."));
4535 UWARN(
"No links can be refined!");
4543 progressDialog->setAttribute(Qt::WA_DeleteOnClose);
4546 progressDialog->setMinimumWidth(800);
4547 progressDialog->show();
4549 for(
int i=0;
i<links.size(); ++
i)
4551 int from = links[
i].from();
4552 int to = links[
i].to();
4553 if(from > 0 && to > 0)
4560 progressDialog->
appendText(tr(
"Ignored link %1->%2 (landmark)").
arg(from).
arg(to));
4563 QApplication::processEvents();
4572 progressDialog->
appendText(
"Refining links finished!");
4578 if(QMessageBox::question(
this,
4579 tr(
"Reset all changes"),
4580 tr(
"You are about to reset all changes you've made so far, do you want to continue?"),
4581 QMessageBox::Yes | QMessageBox::No,
4582 QMessageBox::No) == QMessageBox::Yes)
4599 static bool updateA =
true;
4600 if(updateA ||
ui_->actionConcise_Layout->isChecked())
4604 updateA = !updateA ||
ui_->actionConcise_Layout->isChecked();
4619 ui_->spinBox_indexA,
4620 ui_->label_parentsA,
4621 ui_->label_childrenA,
4625 ui_->graphicsView_A,
4629 ui_->label_optposeA,
4633 ui_->label_gravityA,
4635 ui_->toolButton_edit_priorA,
4636 ui_->toolButton_remove_priorA,
4639 ui_->label_sensorsA,
4646 ui_->spinBox_indexB,
4647 ui_->label_parentsB,
4648 ui_->label_childrenB,
4652 ui_->graphicsView_B,
4656 ui_->label_optposeB,
4660 ui_->label_gravityB,
4662 ui_->toolButton_edit_priorB,
4663 ui_->toolButton_remove_priorB,
4666 ui_->label_sensorsB,
4671 QSpinBox * spinBoxIndex,
4672 QLabel * labelParents,
4673 QLabel * labelChildren,
4679 QLabel * labelMapId,
4681 QLabel * labelOptPose,
4682 QLabel * labelVelocity,
4683 QLabel * labelCalib,
4685 QLabel * labelGravity,
4686 QLabel * labelPrior,
4687 QToolButton * editPriorButton,
4688 QToolButton * removePriorButton,
4691 QLabel * labelSensors,
4692 bool updateConstraintView)
4697 spinBoxIndex->blockSignals(
true);
4698 spinBoxIndex->setValue(
value);
4699 spinBoxIndex->blockSignals(
false);
4700 labelParents->clear();
4701 labelChildren->clear();
4704 labelMapId->clear();
4706 labelOptPose->clear();
4707 labelVelocity->clear();
4709 labelCalib->clear();
4710 labelScan ->clear();
4711 labelGravity->clear();
4712 labelPrior->clear();
4713 editPriorButton->setVisible(
false);
4714 removePriorButton->setVisible(
false);
4717 labelSensors->clear();
4723 labelId->setText(QString::number(
id));
4726 if(
ui_->dockWidget_graphView->isVisible()) {
4727 ui_->graphViewer->highlightNode(
id, spinBoxIndex==
ui_->spinBox_indexB?1:0);
4737 data.uncompressData();
4738 if(!
data.imageRaw().empty())
4742 if(!
data.depthOrRightRaw().empty())
4744 cv::Mat depth =
data.depthOrRightRaw();
4745 if(!
data.depthRaw().empty())
4747 if(
ui_->spinBox_mesh_fillDepthHoles->value() > 0)
4752 if( !
data.imageRaw().empty() &&
4753 !
data.rightRaw().empty() &&
4754 data.stereoCameraModels().size()==1 &&
4755 data.stereoCameraModels()[0].isValidForProjection() &&
4756 ui_->checkBox_showDisparityInsteadOfRight->isChecked())
4781 if(!imgDepth.empty())
4783 view->setImageDepth(imgDepth);
4786 rect.setWidth(imgDepth.cols);
4787 rect.setHeight(imgDepth.rows);
4796 if(
data.cameraModels().size())
4798 for(
unsigned int i=0;
i<
data.cameraModels().
size(); ++
i)
4800 rect.setWidth(rect.width()+
data.cameraModels()[
i].imageWidth());
4801 rect.setHeight(
std::max((
int)rect.height(),
data.cameraModels()[
i].imageHeight()));
4804 else if(
data.stereoCameraModels().size())
4806 for(
unsigned int i=0;
i<
data.cameraModels().
size(); ++
i)
4808 rect.setWidth(rect.width()+
data.stereoCameraModels()[
i].left().imageWidth());
4809 rect.setHeight(
std::max((
int)rect.height(),
data.stereoCameraModels()[
i].left().imageHeight()));
4815 view->setSceneRect(rect);
4820 std::list<Signature*> signatures;
4823 if(signatures.size() && signatures.front()!=0 && !signatures.front()->getWordsKpts().empty())
4825 std::multimap<int, cv::KeyPoint> keypoints;
4826 for(std::map<int, int>::const_iterator
iter=signatures.front()->getWords().begin();
iter!=signatures.front()->getWords().end(); ++
iter)
4828 keypoints.insert(std::make_pair(
iter->first, signatures.front()->getWordsKpts()[
iter->second]));
4830 view->setFeatures(keypoints,
data.depthOrRightRaw().type() == CV_8UC1?cv::Mat():
data.depthOrRightRaw(), Qt::yellow);
4837 std::vector<float>
v;
4843 label->setText(l.c_str());
4846 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));
4851 labelOptPose->setText(
"<Not in optimized graph>");
4861 stamp->setText(QString::number(
s,
'f'));
4862 stamp->setToolTip(QDateTime::fromMSecsSinceEpoch(
s*1000.0).toString(
"dd.MM.yyyy hh:mm:ss.zzz"));
4866 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]));
4869 std::multimap<int, Link> gravityLink;
4871 if(!gravityLink.empty())
4874 gravityLink.begin()->second.transform().getEulerAngles(
roll,
pitch,
yaw);
4876 labelGravity->setText(QString(
"x=%1 y=%2 z=%3").
arg(
v[0]).
arg(
v[1]).
arg(
v[2]));
4886 std::stringstream
out;
4888 labelPrior->setToolTip(QString(
"%1").
arg(
out.str().c_str()));
4889 removePriorButton->setVisible(
true);
4890 editPriorButton->setToolTip(tr(
"Edit Prior"));
4891 editPriorButton->setText(
"...");
4892 editPriorButton->setVisible(
true);
4894 else if(!odomPose.
isNull())
4896 editPriorButton->setToolTip(tr(
"Add Prior"));
4897 editPriorButton->setText(
"+");
4898 editPriorButton->setVisible(
true);
4903 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()));
4904 labelGps->setToolTip(QDateTime::fromMSecsSinceEpoch(gps.
stamp()*1000.0).toString(
"dd.MM.yyyy hh:mm:ss.zzz"));
4908 labelGt->setText(QString(
"%1").
arg(
g.prettyPrint().c_str()));
4914 for(EnvSensors::iterator
iter=sensors.begin();
iter!=sensors.end(); ++
iter)
4916 if(
iter != sensors.begin())
4918 sensorsStr +=
" | ";
4919 tooltipStr +=
" | ";
4924 sensorsStr +=
uFormat(
"%.1f dbm",
iter->second.value()).c_str();
4925 tooltipStr +=
"Wifi signal strength";
4929 sensorsStr +=
uFormat(
"%.1f \u00B0C",
iter->second.value()).c_str();
4930 tooltipStr +=
"Ambient Temperature";
4934 sensorsStr +=
uFormat(
"%.1f hPa",
iter->second.value()).c_str();
4935 tooltipStr +=
"Ambient Air Pressure";
4939 sensorsStr +=
uFormat(
"%.0f lx",
iter->second.value()).c_str();
4940 tooltipStr +=
"Ambient Light";
4944 sensorsStr +=
uFormat(
"%.0f %%",
iter->second.value()).c_str();
4945 tooltipStr +=
"Ambient Relative Humidity";
4949 sensorsStr +=
uFormat(
"%.2f",
iter->second.value()).c_str();
4950 tooltipStr += QString(
"Type %1").arg((
int)
iter->first);
4954 labelSensors->setText(sensorsStr);
4955 labelSensors->setToolTip(tooltipStr);
4957 if(
data.cameraModels().size() ||
data.stereoCameraModels().size())
4959 std::stringstream calibrationDetails;
4960 if(
data.cameraModels().size())
4962 if(!
data.depthRaw().empty() &&
data.depthRaw().cols!=
data.imageRaw().cols &&
data.imageRaw().cols)
4964 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]")
4965 .
arg(
data.cameraModels().size())
4966 .
arg(
data.cameraModels()[0].imageWidth()>0?
data.cameraModels()[0].imageWidth():
data.imageRaw().cols/
data.cameraModels().size())
4967 .arg(
data.cameraModels()[0].imageHeight()>0?
data.cameraModels()[0].imageHeight():
data.imageRaw().rows)
4968 .arg(
data.cameraModels()[0].fx())
4969 .arg(
data.cameraModels()[0].fy())
4970 .arg(
data.cameraModels()[0].cx())
4971 .arg(
data.cameraModels()[0].cy())
4972 .arg(
data.depthRaw().cols/
data.cameraModels().size())
4973 .arg(
data.depthRaw().rows)
4974 .arg(
data.cameraModels()[0].localTransform().prettyPrint().c_str())
4975 .arg(
data.cameraModels()[0].localTransform().r11()).arg(
data.cameraModels()[0].localTransform().r12()).arg(
data.cameraModels()[0].localTransform().r13()).arg(
data.cameraModels()[0].localTransform().o14())
4976 .arg(
data.cameraModels()[0].localTransform().r21()).arg(
data.cameraModels()[0].localTransform().r22()).arg(
data.cameraModels()[0].localTransform().r23()).arg(
data.cameraModels()[0].localTransform().o24())
4977 .arg(
data.cameraModels()[0].localTransform().r31()).arg(
data.cameraModels()[0].localTransform().r32()).arg(
data.cameraModels()[0].localTransform().r33()).arg(
data.cameraModels()[0].localTransform().o34()));
4981 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]")
4982 .
arg(
data.cameraModels().size())
4983 .
arg(
data.cameraModels()[0].imageWidth()>0?
data.cameraModels()[0].imageWidth():
data.imageRaw().cols/
data.cameraModels().size())
4984 .arg(
data.cameraModels()[0].imageHeight()>0?
data.cameraModels()[0].imageHeight():
data.imageRaw().rows)
4985 .arg(
data.cameraModels()[0].fx())
4986 .arg(
data.cameraModels()[0].fy())
4987 .arg(
data.cameraModels()[0].cx())
4988 .arg(
data.cameraModels()[0].cy())
4989 .arg(
data.cameraModels()[0].localTransform().prettyPrint().c_str())
4990 .arg(
data.cameraModels()[0].localTransform().r11()).arg(
data.cameraModels()[0].localTransform().r12()).arg(
data.cameraModels()[0].localTransform().r13()).arg(
data.cameraModels()[0].localTransform().o14())
4991 .arg(
data.cameraModels()[0].localTransform().r21()).arg(
data.cameraModels()[0].localTransform().r22()).arg(
data.cameraModels()[0].localTransform().r23()).arg(
data.cameraModels()[0].localTransform().o24())
4992 .arg(
data.cameraModels()[0].localTransform().r31()).arg(
data.cameraModels()[0].localTransform().r32()).arg(
data.cameraModels()[0].localTransform().r33()).arg(
data.cameraModels()[0].localTransform().o34()));
4995 for(
unsigned int i=0;
i<
data.cameraModels().
size();++
i)
4997 if(
i!=0) calibrationDetails << std::endl;
4998 calibrationDetails <<
"Id: " <<
i <<
" Size=" <<
data.cameraModels()[
i].imageWidth() <<
"x" <<
data.cameraModels()[
i].imageHeight() << std::endl;
4999 if(
data.cameraModels()[
i].K_raw().total()) calibrationDetails <<
"K=" <<
data.cameraModels()[
i].K_raw() << std::endl;
5000 if(
data.cameraModels()[
i].D_raw().total()) calibrationDetails <<
"D=" <<
data.cameraModels()[
i].D_raw() << std::endl;
5001 if(
data.cameraModels()[
i].R().total()) calibrationDetails <<
"R=" <<
data.cameraModels()[
i].R() << std::endl;
5002 if(
data.cameraModels()[
i].P().total()) calibrationDetails <<
"P=" <<
data.cameraModels()[
i].P() << std::endl;
5007 else if(
data.stereoCameraModels().size())
5010 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]")
5011 .arg(
data.stereoCameraModels()[0].left().imageWidth()>0?
data.stereoCameraModels()[0].left().imageWidth():
data.imageRaw().cols)
5012 .arg(
data.stereoCameraModels()[0].left().imageHeight()>0?
data.stereoCameraModels()[0].left().imageHeight():
data.imageRaw().rows)
5013 .arg(
data.stereoCameraModels()[0].left().fx())
5014 .arg(
data.stereoCameraModels()[0].left().fy())
5015 .arg(
data.stereoCameraModels()[0].left().cx())
5016 .arg(
data.stereoCameraModels()[0].left().cy())
5017 .arg(
data.stereoCameraModels()[0].baseline())
5018 .arg(
data.stereoCameraModels()[0].localTransform().prettyPrint().c_str())
5019 .arg(
data.stereoCameraModels()[0].localTransform().r11()).arg(
data.stereoCameraModels()[0].localTransform().r12()).arg(
data.stereoCameraModels()[0].localTransform().r13()).arg(
data.stereoCameraModels()[0].localTransform().o14())
5020 .arg(
data.stereoCameraModels()[0].localTransform().r21()).arg(
data.stereoCameraModels()[0].localTransform().r22()).arg(
data.stereoCameraModels()[0].localTransform().r23()).arg(
data.stereoCameraModels()[0].localTransform().o24())
5021 .arg(
data.stereoCameraModels()[0].localTransform().r31()).arg(
data.stereoCameraModels()[0].localTransform().r32()).arg(
data.stereoCameraModels()[0].localTransform().r33()).arg(
data.stereoCameraModels()[0].localTransform().o34()));
5023 for(
unsigned int i=0;
i<
data.stereoCameraModels().size();++
i)
5025 calibrationDetails <<
"Id: " <<
i << std::endl;
5026 calibrationDetails <<
" Left:" <<
" Size=" <<
data.stereoCameraModels()[
i].left().imageWidth() <<
"x" <<
data.stereoCameraModels()[
i].left().imageHeight() << std::endl;
5027 if(
data.stereoCameraModels()[
i].left().K_raw().total()) calibrationDetails <<
" K=" <<
data.stereoCameraModels()[
i].left().K_raw() << std::endl;
5028 if(
data.stereoCameraModels()[
i].left().D_raw().total()) calibrationDetails <<
" D=" <<
data.stereoCameraModels()[
i].left().D_raw() << std::endl;
5029 if(
data.stereoCameraModels()[
i].left().R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].left().R() << std::endl;
5030 if(
data.stereoCameraModels()[
i].left().P().total()) calibrationDetails <<
" P=" <<
data.stereoCameraModels()[
i].left().P() << std::endl;
5031 calibrationDetails <<
" Right:" <<
" Size=" <<
data.stereoCameraModels()[
i].right().imageWidth() <<
"x" <<
data.stereoCameraModels()[
i].right().imageHeight() << std::endl;
5032 if(
data.stereoCameraModels()[
i].right().K_raw().total()) calibrationDetails <<
" K=" <<
data.stereoCameraModels()[
i].right().K_raw() << std::endl;
5033 if(
data.stereoCameraModels()[
i].right().D_raw().total()) calibrationDetails <<
" D=" <<
data.stereoCameraModels()[
i].right().D_raw() << std::endl;
5034 if(
data.stereoCameraModels()[
i].right().R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].right().R() << std::endl;
5035 if(
data.stereoCameraModels()[
i].right().P().total()) calibrationDetails <<
" P=" <<
data.stereoCameraModels()[
i].right().P() << std::endl;
5036 if(
data.stereoCameraModels()[
i].R().total()) calibrationDetails <<
" R=" <<
data.stereoCameraModels()[
i].R() << std::endl;
5037 if(
data.stereoCameraModels()[
i].T().total()) calibrationDetails <<
" T=" <<
data.stereoCameraModels()[
i].T() << std::endl;
5038 if(
data.stereoCameraModels()[
i].F().total()) calibrationDetails <<
" F=" <<
data.stereoCameraModels()[
i].F() << std::endl;
5039 if(
data.stereoCameraModels()[
i].E().total()) calibrationDetails <<
" E=" <<
data.stereoCameraModels()[
i].E() << std::endl;
5043 labelCalib->setToolTip(calibrationDetails.str().c_str());
5048 labelCalib->setText(
"NA");
5051 if(
data.laserScanRaw().size())
5053 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")
5054 .arg(
data.laserScanRaw().formatName().c_str())
5055 .arg(
data.laserScanRaw().size())
5056 .arg(
data.laserScanRaw().maxPoints())
5057 .arg(
data.laserScanRaw().rangeMin())
5058 .arg(
data.laserScanRaw().rangeMax())
5059 .arg(
data.laserScanRaw().angleMin())
5060 .arg(
data.laserScanRaw().angleMax())
5061 .arg(
data.laserScanRaw().angleIncrement())
5062 .arg(
data.laserScanRaw().hasRGB()?1:0)
5063 .arg(
data.laserScanRaw().is2d()?1:0)
5064 .arg(
data.laserScanRaw().hasNormals()?1:0)
5065 .arg(
data.laserScanRaw().hasIntensity()?1:0)
5066 .arg(
data.laserScanRaw().localTransform().prettyPrint().c_str()));
5070 if(!
data.depthOrRightRaw().empty() &&
data.depthOrRightRaw().type() == CV_8UC1)
5077 ui_->graphicsView_stereo->clear();
5091 if(signatures.size() &&
ui_->checkBox_odomFrame_3dview->isChecked())
5098 if(!gravityLink.empty() &&
ui_->checkBox_gravity_3dview->isChecked())
5100 Transform gravityT = gravityLink.begin()->second.transform();
5101 Eigen::Vector3f gravity(0,0,-1);
5104 gravityT = gravityT.
inverse();
5106 gravity = (gravityT.
rotation()*(pose).
rotation().inverse()).toEigen3f()*gravity;
5107 cloudViewer_->
addOrUpdateLine(
"gravity", pose, (pose).
translation()*
Transform(gravity[0], gravity[1], gravity[2], 0, 0, 0)*pose.
rotation().
inverse(), Qt::yellow,
true,
false);
5116 if(
ui_->checkBox_showScan->isChecked() && laserScanRaw.
size())
5133 else if(laserScanRaw.
hasRGB())
5151 if(
ui_->checkBox_showCloud->isChecked() &&
ui_->checkBox_cameraProjection->isChecked() &&
5152 !
data.imageRaw().empty() && !laserScanRaw.
empty() && !laserScanRaw.
is2d())
5155 std::vector<CameraModel> models =
data.cameraModels();
5156 if(!
data.stereoCameraModels().empty())
5159 for(
size_t i=0;
i<
data.stereoCameraModels().
size(); ++
i)
5161 models.push_back(
data.stereoCameraModels()[
i].left());
5165 if(!models.empty() && !models[0].isValidForProjection())
5170 if(!models.empty() && models[0].imageWidth() != 0 && models[0].imageHeight() != 0)
5173 std::map<int, std::vector<CameraModel> > cameraModels;
5175 cameraModels.insert(std::make_pair(
data.id(), models));
5177 std::vector<std::pair< std::pair<int, int>, pcl::PointXY> > pointToPixel;
5183 UASSERT(pointToPixel.empty() || pointToPixel.size() == cloud->size());
5184 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudValidPoints(
new pcl::PointCloud<pcl::PointXYZRGB>);
5185 cloudValidPoints->resize(cloud->size());
5187 for(
size_t i=0;
i<pointToPixel.size(); ++
i)
5189 pcl::PointXYZINormal & pt = cloud->at(
i);
5190 pcl::PointXYZRGB ptColor;
5191 int nodeID = pointToPixel[
i].first.first;
5192 int cameraIndex = pointToPixel[
i].first.second;
5193 if(nodeID>0 && cameraIndex>=0)
5195 cv::Mat image =
data.imageRaw();
5197 int subImageWidth = image.cols / cameraModels.at(nodeID).size();
5198 image = image(
cv::Range::all(), cv::Range(cameraIndex*subImageWidth, (cameraIndex+1)*subImageWidth));
5201 int x = pointToPixel[
i].second.x * (
float)image.cols;
5202 int y = pointToPixel[
i].second.y * (
float)image.rows;
5206 if(image.type()==CV_8UC3)
5208 cv::Vec3b bgr = image.at<cv::Vec3b>(
y,
x);
5215 UASSERT(image.type()==CV_8UC1);
5216 ptColor.r = ptColor.g = ptColor.b = image.at<
unsigned char>(pointToPixel[
i].second.y * image.rows, pointToPixel[
i].second.x * image.cols);
5222 cloudValidPoints->at(oi++) = ptColor;
5225 cloudValidPoints->resize(oi);
5226 if(!cloudValidPoints->empty())
5228 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5230 cloudValidPoints =
util3d::voxelize(cloudValidPoints,
ui_->doubleSpinBox_voxelSize->value());
5237 UWARN(
"Camera projection to scan returned an empty cloud, no visible points from cameras...");
5242 UERROR(
"Node has invalid camera models, camera projection on scan cannot be done!.");
5245 else if(
ui_->checkBox_showCloud->isChecked() ||
ui_->checkBox_showMesh->isChecked())
5247 if(!
data.depthOrRightRaw().empty())
5249 if(!
data.imageRaw().empty())
5251 std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> clouds;
5252 std::vector<pcl::IndicesPtr> allIndices;
5253 if(!
data.depthRaw().empty() &&
data.cameraModels().size()==1)
5255 cv::Mat depth =
data.depthRaw();
5256 if(
ui_->spinBox_mesh_fillDepthHoles->value() > 0)
5260 pcl::IndicesPtr
indices(
new std::vector<int>);
5264 data.cameraModels()[0],
5265 ui_->spinBox_decimation->value(),0,0,
indices.get());
5269 allIndices.push_back(
indices);
5276 UASSERT(clouds.size() == allIndices.size());
5277 for(
size_t i=0;
i<allIndices.size(); ++
i)
5279 if(allIndices[
i]->
size())
5281 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = clouds[
i];
5282 pcl::IndicesPtr
indices = allIndices[
i];
5283 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5288 if(
ui_->checkBox_showMesh->isChecked() && !cloud->is_dense)
5290 Eigen::Vector3f viewpoint(0.0
f,0.0
f,0.0
f);
5291 if(
data.cameraModels().size() && !
data.cameraModels()[0].localTransform().isNull())
5293 viewpoint[0] =
data.cameraModels()[0].localTransform().x();
5294 viewpoint[1] =
data.cameraModels()[0].localTransform().y();
5295 viewpoint[2] =
data.cameraModels()[0].localTransform().z();
5297 else if(
data.stereoCameraModels().size() && !
data.stereoCameraModels()[0].localTransform().isNull())
5299 viewpoint[0] =
data.stereoCameraModels()[0].localTransform().x();
5300 viewpoint[1] =
data.stereoCameraModels()[0].localTransform().y();
5301 viewpoint[2] =
data.stereoCameraModels()[0].localTransform().z();
5305 float(
ui_->spinBox_mesh_angleTolerance->value())*
M_PI/180.0f,
5306 ui_->checkBox_mesh_quad->isChecked(),
5307 ui_->spinBox_mesh_triangleSize->value(),
5310 if(
ui_->spinBox_mesh_minClusterSize->value())
5313 std::vector<std::set<int> > neighbors;
5314 std::vector<std::set<int> > vertexToPolygons;
5321 ui_->spinBox_mesh_minClusterSize->value());
5322 std::vector<pcl::Vertices> filteredPolygons(polygons.size());
5326 for(std::list<int>::iterator jter=
iter->begin(); jter!=
iter->end(); ++jter)
5328 filteredPolygons[oi++] = polygons.at(*jter);
5331 filteredPolygons.resize(oi);
5332 polygons = filteredPolygons;
5337 if(
ui_->checkBox_showCloud->isChecked())
5344 else if(
ui_->checkBox_showCloud->isChecked())
5346 std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> clouds;
5347 std::vector<pcl::IndicesPtr> allIndices;
5350 UASSERT(clouds.size() == allIndices.size());
5351 for(
size_t i=0;
i<allIndices.size(); ++
i)
5353 if(allIndices[
i]->
size())
5355 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = clouds[
i];
5356 pcl::IndicesPtr
indices = allIndices[
i];
5357 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
5372 if(
data.cameraModels().size())
5383 if(
ui_->checkBox_showWords->isChecked() &&
5384 !signatures.empty() &&
5385 !(*signatures.begin())->getWords3().empty())
5387 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>);
5388 cloud->resize((*signatures.begin())->getWords3().size());
5390 for(std::multimap<int, int>::const_iterator
iter=(*signatures.begin())->getWords().begin();
5391 iter!=(*signatures.begin())->getWords().end();
5394 const cv::Point3f & pt = (*signatures.begin())->getWords3()[
iter->second];
5395 cloud->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
5410 if(
ui_->checkBox_showMap->isChecked() ||
5411 ui_->checkBox_showGrid->isChecked() ||
5412 ui_->checkBox_showElevation->checkState() != Qt::Unchecked)
5419 else if(!
data.gridGroundCellsRaw().empty() || !
data.gridObstacleCellsRaw().empty())
5421 combinedLocalMaps.
add(
data.id(),
data.gridGroundCellsRaw(),
data.gridObstacleCellsRaw(),
data.gridEmptyCellsRaw(),
data.gridCellSize(),
data.gridViewPoint());
5423 if(!combinedLocalMaps.
empty())
5425 std::map<int, Transform> poses;
5426 poses.insert(std::make_pair(
data.id(), pose));
5428 #ifdef RTABMAP_OCTOMAP
5430 if(
ui_->checkBox_octomap->isChecked() &&
5431 (!combinedLocalMaps.
localGrids().begin()->second.groundCells.empty() || !combinedLocalMaps.
localGrids().begin()->second.obstacleCells.empty()) &&
5432 combinedLocalMaps.
localGrids().begin()->second.is3D() &&
5433 combinedLocalMaps.
localGrids().begin()->second.cellSize > 0.0f)
5443 if(
ui_->checkBox_showMap->isChecked())
5445 float xMin=0.0f, yMin=0.0f;
5447 float gridCellSize = Parameters::defaultGridCellSize();
5451 #ifdef RTABMAP_OCTOMAP
5461 map8S = grid.
getMap(xMin, yMin);
5470 if(
ui_->checkBox_showGrid->isChecked())
5472 #ifdef RTABMAP_OCTOMAP
5475 if(
ui_->comboBox_octomap_rendering_type->currentIndex()== 0)
5477 pcl::IndicesPtr obstacles(
new std::vector<int>);
5478 pcl::IndicesPtr
empty(
new std::vector<int>);
5479 pcl::IndicesPtr ground(
new std::vector<int>);
5480 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = octomap->
createCloud(
ui_->spinBox_grid_depth->value(), obstacles.get(),
empty.get(), ground.get());
5483 pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
5484 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
5488 pcl::PointCloud<pcl::PointXYZRGB>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
5489 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
5495 pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5496 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
5500 pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5501 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
5506 if(
ui_->checkBox_grid_empty->isChecked())
5508 pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZ>);
5509 pcl::copyPointCloud(*cloud, *
empty, *emptyCloud);
5546 if(
ui_->checkBox_grid_empty->isChecked())
5551 QColor(
ui_->lineEdit_emptyColor->text()));
5557 #ifdef RTABMAP_OCTOMAP
5564 #ifdef RTABMAP_GRIDMAP
5565 if(
ui_->checkBox_showElevation->checkState() != Qt::Unchecked)
5567 GridMap gridMap(&combinedLocalMaps, parameters);
5569 if(combinedLocalMaps.
localGrids().begin()->second.is3D())
5572 if(
ui_->checkBox_showElevation->checkState() == Qt::PartiallyChecked)
5574 float xMin, yMin, gridCellSize;
5575 cv::Mat elevationMap = gridMap.
createHeightMap(xMin, yMin, gridCellSize);
5587 UWARN(
"Local grid is not 3D, cannot generate an elevation map");
5598 if(signatures.size())
5600 UASSERT(signatures.front() != 0 && signatures.size() == 1);
5601 delete signatures.front();
5607 std::multimap<int, rtabmap::Link> links;
5611 QString strParents, strChildren;
5612 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
5617 if(
iter->first <
id)
5619 strChildren.append(QString(
"%1 ").
arg(
iter->first));
5623 strParents.append(QString(
"%1 ").
arg(
iter->first));
5627 labelParents->setText(strParents);
5628 labelChildren->setText(strChildren);
5634 labelMapId->setText(QString::number(mapId));
5648 int from =
ids_.at(
ui_->horizontalSlider_A->value());
5649 int to =
ids_.at(
ui_->horizontalSlider_B->value());
5658 if(
i !=
ui_->horizontalSlider_loops->value())
5660 ui_->horizontalSlider_loops->blockSignals(
true);
5661 ui_->horizontalSlider_loops->setValue(
i);
5662 ui_->horizontalSlider_loops->blockSignals(
false);
5665 ui_->horizontalSlider_neighbors->blockSignals(
true);
5666 ui_->horizontalSlider_neighbors->setValue(0);
5667 ui_->horizontalSlider_neighbors->blockSignals(
false);
5677 if(
i !=
ui_->horizontalSlider_neighbors->value())
5679 ui_->horizontalSlider_neighbors->blockSignals(
true);
5680 ui_->horizontalSlider_neighbors->setValue(
i);
5681 ui_->horizontalSlider_neighbors->blockSignals(
false);
5684 ui_->horizontalSlider_loops->blockSignals(
true);
5685 ui_->horizontalSlider_loops->setValue(0);
5686 ui_->horizontalSlider_loops->blockSignals(
false);
5694 ui_->horizontalSlider_loops->blockSignals(
true);
5695 ui_->horizontalSlider_neighbors->blockSignals(
true);
5696 ui_->horizontalSlider_loops->setValue(0);
5697 ui_->horizontalSlider_neighbors->setValue(0);
5698 ui_->horizontalSlider_loops->blockSignals(
false);
5699 ui_->horizontalSlider_neighbors->blockSignals(
false);
5704 bool constraintViewUpdated =
false;
5708 constraintViewUpdated =
true;
5713 std::map<int, Transform> optimizedPoses =
uValueAt(
graphes_,
ui_->horizontalSlider_iterations->value());
5714 if(optimizedPoses.size() > 0)
5716 std::map<int, Transform>::iterator fromIter = optimizedPoses.find(from);
5717 std::map<int, Transform>::iterator toIter = optimizedPoses.find(to);
5718 if(fromIter != optimizedPoses.end() &&
5719 toIter != optimizedPoses.end())
5721 Link link(from, to,
Link::kUndef, fromIter->second.inverse() * toIter->second);
5723 constraintViewUpdated =
true;
5727 if(!constraintViewUpdated)
5729 ui_->label_constraint->clear();
5730 ui_->label_constraint_opt->clear();
5731 ui_->label_variance->clear();
5732 ui_->lineEdit_covariance->clear();
5733 ui_->label_type->clear();
5734 ui_->label_type_name->clear();
5735 ui_->checkBox_showOptimized->setEnabled(
false);
5745 if(this->parent() == 0)
5753 if(
ui_->horizontalSlider_A->maximum())
5755 int id =
ids_.at(
ui_->horizontalSlider_A->value());
5758 data.uncompressData();
5766 ui_->dockWidget_stereoView->isVisible() &&
5767 !
data->imageRaw().empty() &&
5768 !
data->depthOrRightRaw().empty() &&
5769 data->depthOrRightRaw().type() == CV_8UC1 &&
5770 data->stereoCameraModels().size()==1 &&
5771 data->stereoCameraModels()[0].isValidForProjection())
5774 if(
data->imageRaw().channels() == 3)
5776 cv::cvtColor(
data->imageRaw(), leftMono, CV_BGR2GRAY);
5780 leftMono =
data->imageRaw();
5788 std::vector<cv::KeyPoint> kpts;
5789 uInsert(parameters,
ParametersPair(Parameters::kKpMaxFeatures(), parameters.at(Parameters::kVisMaxFeatures())));
5791 uInsert(parameters,
ParametersPair(Parameters::kKpMinDepth(), parameters.at(Parameters::kVisMinDepth())));
5792 uInsert(parameters,
ParametersPair(Parameters::kKpMaxDepth(), parameters.at(Parameters::kVisMaxDepth())));
5793 uInsert(parameters,
ParametersPair(Parameters::kKpDetectorStrategy(), parameters.at(Parameters::kVisFeatureType())));
5794 uInsert(parameters,
ParametersPair(Parameters::kKpRoiRatios(), parameters.at(Parameters::kVisRoiRatios())));
5795 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixEps(), parameters.at(Parameters::kVisSubPixEps())));
5796 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixIterations(), parameters.at(Parameters::kVisSubPixIterations())));
5797 uInsert(parameters,
ParametersPair(Parameters::kKpSubPixWinSize(), parameters.at(Parameters::kVisSubPixWinSize())));
5802 float timeKpt =
timer.ticks();
5804 std::vector<cv::Point2f> leftCorners;
5805 cv::KeyPoint::convert(kpts, leftCorners);
5808 std::vector<unsigned char> status;
5809 std::vector<cv::Point2f> rightCorners;
5818 float timeStereo =
timer.ticks();
5820 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZ>);
5821 cloud->resize(kpts.size());
5822 float bad_point = std::numeric_limits<float>::quiet_NaN ();
5823 UASSERT(status.size() == kpts.size());
5826 int flowOutliers= 0;
5827 int slopeOutliers= 0;
5828 int negativeDisparityOutliers = 0;
5829 for(
unsigned int i=0;
i<status.size(); ++
i)
5831 cv::Point3f pt(bad_point, bad_point, bad_point);
5834 float disparity = leftCorners[
i].x - rightCorners[
i].x;
5835 if(disparity > 0.0
f)
5840 data->stereoCameraModels()[0]);
5847 cloud->at(oi++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
5853 ++negativeDisparityOutliers;
5863 UINFO(
"correspondences = %d/%d (%f) (time kpt=%fs stereo=%fs)",
5864 (
int)cloud->size(), (
int)leftCorners.size(),
float(cloud->size())/
float(leftCorners.size()), timeKpt, timeStereo);
5870 ui_->label_stereo_inliers->setNum(inliers);
5871 ui_->label_stereo_flowOutliers->setNum(flowOutliers);
5872 ui_->label_stereo_slopeOutliers->setNum(slopeOutliers);
5873 ui_->label_stereo_disparityOutliers->setNum(negativeDisparityOutliers);
5875 std::vector<cv::KeyPoint> rightKpts;
5876 cv::KeyPoint::convert(rightCorners, rightKpts);
5877 std::vector<cv::DMatch> good_matches(kpts.size());
5878 for(
unsigned int i=0;
i<good_matches.size(); ++
i)
5880 good_matches[
i].trainIdx =
i;
5881 good_matches[
i].queryIdx =
i;
5893 ui_->graphicsView_stereo->clear();
5894 ui_->graphicsView_stereo->setLinesShown(
true);
5895 ui_->graphicsView_stereo->setFeaturesShown(
false);
5896 ui_->graphicsView_stereo->setImageDepthShown(
true);
5899 ui_->graphicsView_stereo->setImageDepth(
data->depthOrRightRaw());
5902 for(
unsigned int i=0;
i<kpts.size(); ++
i)
5904 if(rightKpts[
i].pt.x > 0 && rightKpts[
i].pt.y > 0)
5906 QColor
c = Qt::green;
5911 else if(status[
i] == 100)
5915 else if(status[
i] == 101)
5919 else if(status[
i] == 102)
5923 else if(status[
i] == 110)
5927 ui_->graphicsView_stereo->addLine(
5933 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));
5936 ui_->graphicsView_stereo->update();
5942 if(
ui_->actionConcise_Layout->isChecked()) {
5946 int from =
ids_.at(
ui_->horizontalSlider_A->value());
5947 int to =
ids_.at(
ui_->horizontalSlider_B->value());
5950 ui_->graphicsView_A->clearLines();
5951 ui_->graphicsView_A->setFeaturesColor(
ui_->graphicsView_A->getDefaultFeatureColor());
5952 ui_->graphicsView_B->clearLines();
5953 ui_->graphicsView_B->setFeaturesColor(
ui_->graphicsView_B->getDefaultFeatureColor());
5955 const QMultiMap<int, KeypointItem*> & wordsA =
ui_->graphicsView_A->getFeatures();
5956 const QMultiMap<int, KeypointItem*> & wordsB =
ui_->graphicsView_B->getFeatures();
5957 std::set<int> inliersSet(inliers.begin(), inliers.end());
5958 if(wordsA.size() && wordsB.size())
5960 QList<int> ids = wordsA.uniqueKeys();
5961 for(
int i=0;
i<ids.size(); ++
i)
5963 if(ids[
i] > 0 && wordsA.count(ids[
i]) == 1 && wordsB.count(ids[
i]) == 1)
5967 float scaleAX =
ui_->graphicsView_A->viewScale();
5968 float scaleBX =
ui_->graphicsView_B->viewScale();
5970 float marginAX = (
ui_->graphicsView_A->width() -
ui_->graphicsView_A->sceneRect().width()*scaleAX)/2.0
f;
5971 float marginAY = (
ui_->graphicsView_A->height() -
ui_->graphicsView_A->sceneRect().height()*scaleAX)/2.0
f;
5972 float marginBX = (
ui_->graphicsView_B->width() -
ui_->graphicsView_B->sceneRect().width()*scaleBX)/2.0
f;
5973 float marginBY = (
ui_->graphicsView_B->height() -
ui_->graphicsView_B->sceneRect().height()*scaleBX)/2.0
f;
5978 if(
ui_->actionVertical_Layout->isChecked())
5980 deltaY =
ui_->graphicsView_A->height();
5984 deltaX =
ui_->graphicsView_A->width();
5990 QColor cA =
ui_->graphicsView_A->getDefaultMatchingLineColor();
5991 QColor cB =
ui_->graphicsView_B->getDefaultMatchingLineColor();
5992 if(inliersSet.find(ids[
i])!=inliersSet.end())
5994 cA =
ui_->graphicsView_A->getDefaultMatchingFeatureColor();
5995 cB =
ui_->graphicsView_B->getDefaultMatchingFeatureColor();
5996 ui_->graphicsView_A->setFeatureColor(ids[
i],
ui_->graphicsView_A->getDefaultMatchingFeatureColor());
5997 ui_->graphicsView_B->setFeatureColor(ids[
i],
ui_->graphicsView_B->getDefaultMatchingFeatureColor());
6001 ui_->graphicsView_A->setFeatureColor(ids[
i],
ui_->graphicsView_A->getDefaultMatchingLineColor());
6002 ui_->graphicsView_B->setFeatureColor(ids[
i],
ui_->graphicsView_B->getDefaultMatchingLineColor());
6005 ui_->graphicsView_A->addLine(
6008 (kptB->
keypoint().pt.x*scaleBX+marginBX+deltaX-marginAX)/scaleAX,
6009 (kptB->
keypoint().pt.y*scaleBX+marginBY+deltaY-marginAY)/scaleAX,
6012 ui_->graphicsView_B->addLine(
6013 (kptA->
keypoint().pt.x*scaleAX+marginAX-deltaX-marginBX)/scaleBX,
6014 (kptA->
keypoint().pt.y*scaleAX+marginAY-deltaY-marginBY)/scaleBX,
6020 ui_->graphicsView_A->update();
6021 ui_->graphicsView_B->update();
6028 ui_->spinBox_indexA->blockSignals(
true);
6029 ui_->spinBox_indexA->setValue(
value);
6030 ui_->spinBox_indexA->blockSignals(
false);
6033 ui_->label_idA->setText(QString::number(
ids_.at(
value)));
6043 ui_->spinBox_indexB->blockSignals(
true);
6044 ui_->spinBox_indexB->setValue(
value);
6045 ui_->spinBox_indexB->blockSignals(
false);
6048 ui_->label_idB->setText(QString::number(
ids_.at(
value)));
6089 int priorId = sender() ==
ui_->toolButton_edit_priorA?
ids_.at(
ui_->horizontalSlider_A->value()):
6090 sender() ==
ui_->toolButton_edit_priorB?
ids_.at(
ui_->horizontalSlider_B->value()):0;
6107 QMessageBox::warning(
this, tr(
""), tr(
"Node %1 doesn't have odometry pose, cannot add a prior for it!").
arg(priorId));
6117 link = findIter->second;
6127 cv::Mat covBefore = link.
infMatrix().inv();
6129 if(dialog.exec() == QDialog::Accepted)
6136 if(
iter->second.to() == link.
to() &&
6137 iter->second.type() == link.
type())
6139 iter->second = newLink;
6160 link.
transform(), cv::Mat::eye(6,6,CV_64FC1) * (priorId>0?0.00001:1));
6161 if(dialog.exec() == QDialog::Accepted)
6164 int from = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_A->value());
6165 int to = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_B->value());
6172 if(newLink.
from() < newLink.
to())
6188 bool priorsIgnored = Parameters::defaultOptimizerPriorsIgnored();
6189 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kOptimizerPriorsIgnored(), priorsIgnored);
6192 if(QMessageBox::question(
this,
6193 tr(
"Updating Prior"),
6194 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()),
6195 QMessageBox::Yes | QMessageBox::No,
6196 QMessageBox::Yes) == QMessageBox::Yes)
6198 priorsIgnored =
false;
6199 ui_->parameters_toolbox->updateParameter(Parameters::kOptimizerPriorsIgnored(),
"false");
6202 int indexA =
ui_->horizontalSlider_A->value();
6203 int indexB =
ui_->horizontalSlider_B->value();
6208 if(
ui_->horizontalSlider_A->value() != indexA)
6209 ui_->horizontalSlider_A->setValue(indexA);
6212 if(
ui_->horizontalSlider_B->value() != indexB)
6213 ui_->horizontalSlider_B->setValue(indexB);
6231 ui_->horizontalSlider_neighbors->value() >= 0 &&
ui_->horizontalSlider_neighbors->value() <
neighborLinks_.size())
6236 ui_->horizontalSlider_loops->value() >= 0 &&
ui_->horizontalSlider_loops->value() <
loopLinks_.size())
6250 bool updateImageSliders,
6260 if(iterLink->second.from() == link.
to())
6262 link = iterLink->second.
inverse();
6266 link = iterLink->second;
6269 else if(
ui_->checkBox_ignorePoseCorrection->isChecked())
6297 ui_->label_constraint->clear();
6298 ui_->label_constraint_opt->clear();
6299 ui_->checkBox_showOptimized->setEnabled(
false);
6302 ui_->label_type->setText(QString::number(link.
type()));
6303 ui_->label_type_name->setText(tr(
"(%1)")
6313 ui_->label_variance->setText(QString(
"%1, %2")
6316 std::stringstream
out;
6318 ui_->lineEdit_covariance->setText(
out.str().c_str());
6319 ui_->label_constraint->setText(QString(
"%1").
arg(
t.prettyPrint().c_str()).replace(
" ",
"\n"));
6321 (
int)
graphes_.size()-1 ==
ui_->horizontalSlider_iterations->maximum())
6325 std::map<int, rtabmap::Transform>::iterator iterFrom =
graph.find(link.
from());
6326 std::map<int, rtabmap::Transform>::iterator iterTo =
graph.find(link.
to());
6327 if(iterFrom !=
graph.end() && iterTo !=
graph.end())
6329 ui_->checkBox_showOptimized->setEnabled(
true);
6334 float a = pcl::getAngle3D(Eigen::Vector4f(
v1.x(),
v1.y(),
v1.z(), 0), Eigen::Vector4f(
v2.x(),
v2.y(),
v2.z(), 0));
6335 a = (
a *180.0f) / CV_PI;
6336 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));
6338 if(
ui_->checkBox_showOptimized->isChecked())
6345 if(updateImageSliders)
6347 ui_->horizontalSlider_A->blockSignals(
true);
6348 ui_->horizontalSlider_B->blockSignals(
true);
6354 ui_->horizontalSlider_A->blockSignals(
false);
6355 ui_->horizontalSlider_B->blockSignals(
false);
6358 ui_->spinBox_indexA,
6359 ui_->label_parentsA,
6360 ui_->label_childrenA,
6364 ui_->graphicsView_A,
6368 ui_->label_optposeA,
6372 ui_->label_gravityA,
6374 ui_->toolButton_edit_priorA,
6375 ui_->toolButton_remove_priorA,
6378 ui_->label_sensorsA,
6383 ui_->spinBox_indexB,
6384 ui_->label_parentsB,
6385 ui_->label_childrenB,
6389 ui_->graphicsView_B,
6393 ui_->label_optposeB,
6397 ui_->label_gravityB,
6399 ui_->toolButton_edit_priorB,
6400 ui_->toolButton_remove_priorB,
6403 ui_->label_sensorsB,
6412 if(signatureFrom.
id()>0)
6424 if(signatureTo.
id()>0)
6428 else if(link.
to()>0)
6438 if(
ui_->checkBox_odomFrame->isChecked())
6444 std::vector<float>
v;
6460 if(
ui_->checkBox_show3Dclouds->isChecked())
6462 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudFrom, cloudTo;
6463 pcl::IndicesPtr indicesFrom(
new std::vector<int>);
6464 pcl::IndicesPtr indicesTo(
new std::vector<int>);
6474 if(cloudTo.get() && indicesTo->size())
6480 if(
ui_->doubleSpinBox_gainCompensationRadius->value()>0.0 &&
6481 cloudFrom.get() && indicesFrom->size() &&
6482 cloudTo.get() && indicesTo->size())
6487 compensator.
apply(0, cloudFrom, indicesFrom);
6488 compensator.
apply(1, cloudTo, indicesTo);
6489 UINFO(
"Gain compensation time = %fs",
t.ticks());
6492 if(cloudFrom.get() && indicesFrom->size())
6494 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
6496 cloudFrom =
util3d::voxelize(cloudFrom, indicesFrom,
ui_->doubleSpinBox_voxelSize->value());
6500 if(cloudTo.get() && indicesTo->size())
6502 if(
ui_->doubleSpinBox_voxelSize->value() > 0.0)
6512 if(
ui_->checkBox_show3DWords->isChecked())
6515 ids.push_back(link.
from());
6518 ids.push_back(link.
to());
6520 std::list<Signature*> signatures;
6522 if(signatures.size() == 2 || (link.
to()<0 && signatures.size()==1))
6524 const Signature * sFrom = signatureFrom.
id()>0?&signatureFrom:signatures.front();
6526 if(signatures.size()==2)
6528 sTo = signatureTo.
id()>0?&signatureTo:signatures.back();
6532 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudFrom(
new pcl::PointCloud<pcl::PointXYZ>);
6533 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudTo(
new pcl::PointCloud<pcl::PointXYZ>);
6534 cloudFrom->resize(sFrom->
getWords3().size());
6537 cloudTo->resize(sTo->
getWords3().size());
6542 for(std::multimap<int, int>::const_iterator
iter=sFrom->
getWords().begin();
6547 cloudFrom->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
6553 for(std::multimap<int, int>::const_iterator
iter=sTo->
getWords().begin();
6558 cloudTo->at(
i++) = pcl::PointXYZ(pt.x, pt.y, pt.z);
6562 if(cloudFrom->size())
6575 if(cloudFrom->size())
6581 UWARN(
"Empty 3D words for node %d", link.
from());
6592 UWARN(
"Empty 3D words for node %d", link.
to());
6599 UERROR(
"Not found signature %d or %d in RAM", link.
from(), link.
to());
6604 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
6614 if(
ui_->checkBox_show2DScans->isChecked())
6619 signatureTo.
id()==0)
6621 std::vector<int> ids;
6623 if(userData.type() == CV_8SC1 &&
6624 userData.rows == 1 &&
6625 userData.cols >= 8 &&
6626 userData.at<
char>(userData.cols-1) == 0 &&
6627 memcmp(userData.data,
"SCANS:", 6) == 0)
6629 std::string scansStr = (
const char *)userData.data;
6630 UINFO(
"Detected \"%s\" in links's user data", scansStr.c_str());
6631 if(!scansStr.empty())
6633 std::list<std::string> strs =
uSplit(scansStr,
':');
6634 if(strs.size() == 2)
6636 std::list<std::string> strIds =
uSplit(strs.rbegin()->c_str(),
';');
6637 for(std::list<std::string>::iterator
iter=strIds.begin();
iter!=strIds.end(); ++
iter)
6639 ids.push_back(atoi(
iter->c_str()));
6640 if(ids.back() == link.
from())
6653 std::map<int, rtabmap::Transform> poses;
6654 for(
unsigned int i=0;
i<ids.size(); ++
i)
6662 UERROR(
"Not found %d node!", ids[
i]);
6670 std::map<int, rtabmap::Transform> posesOut;
6671 std::multimap<int, rtabmap::Link> linksOut;
6679 if(poses.size() != posesOut.size())
6681 UWARN(
"Scan poses input and output are different! %d vs %d", (
int)poses.size(), (
int)posesOut.size());
6685 for(std::map<int, Transform>::iterator
iter=posesOut.begin();
iter!=posesOut.end(); ++
iter)
6687 UDEBUG(
" %d=%s",
iter->first,
iter->second.prettyPrint().c_str());
6690 for(std::multimap<int, Link>::iterator
iter=linksOut.begin();
iter!=linksOut.end(); ++
iter)
6692 UDEBUG(
" %d->%d (type=%s) %s",
iter->second.from(),
iter->second.to(),
iter->second.typeName().c_str(),
iter->second.transform().prettyPrint().c_str());
6695 std::map<int, rtabmap::Transform> finalPoses = optimizer->
optimize(link.
to(), posesOut, linksOut);
6698 UDEBUG(
"Output poses: ");
6699 for(std::map<int, Transform>::iterator
iter=finalPoses.begin();
iter!=finalPoses.end(); ++
iter)
6701 UDEBUG(
" %d=%s",
iter->first,
iter->second.prettyPrint().c_str());
6706 pcl::PointCloud<pcl::PointXYZ>::Ptr assembledScans(
new pcl::PointCloud<pcl::PointXYZ>);
6707 pcl::PointCloud<pcl::PointNormal>::Ptr assembledNormalScans(
new pcl::PointCloud<pcl::PointNormal>);
6708 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledIScans(
new pcl::PointCloud<pcl::PointXYZI>);
6709 pcl::PointCloud<pcl::PointXYZINormal>::Ptr assembledINormalScans(
new pcl::PointCloud<pcl::PointXYZINormal>);
6710 pcl::PointCloud<pcl::PointXYZ>::Ptr
graph(
new pcl::PointCloud<pcl::PointXYZ>);
6711 for(std::map<int, Transform>::iterator
iter=finalPoses.begin();
iter!=finalPoses.end(); ++
iter)
6714 if(
iter->first != link.
to())
6720 data.uncompressDataConst(0, 0, &scan, 0);
6744 if(assembledNormalScans->size())
6749 if(assembledScans->size())
6754 if(assembledINormalScans->size())
6759 if(assembledIScans->size())
6779 pcl::PointCloud<pcl::PointXYZINormal>::Ptr scan;
6786 pcl::PointCloud<pcl::PointNormal>::Ptr scan;
6793 pcl::PointCloud<pcl::PointXYZI>::Ptr scan;
6800 pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
6810 pcl::PointCloud<pcl::PointXYZINormal>::Ptr scan;
6817 pcl::PointCloud<pcl::PointNormal>::Ptr scan;
6824 pcl::PointCloud<pcl::PointXYZI>::Ptr scan;
6831 pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
6867 #if PCL_VERSION_COMPARE(>=, 1, 7, 2)
6888 ui_->pushButton_refine->setEnabled(
false);
6889 ui_->pushButton_reset->setEnabled(
false);
6890 ui_->pushButton_add->setEnabled(
false);
6891 ui_->pushButton_reject->setEnabled(
false);
6892 ui_->toolButton_constraint->setEnabled(
false);
6900 currentLink =
loopLinks_.at(
ui_->horizontalSlider_loops->value());
6901 from = currentLink.
from();
6902 to = currentLink.
to();
6906 from =
ids_.at(
ui_->horizontalSlider_A->value());
6907 to =
ids_.at(
ui_->horizontalSlider_B->value());
6908 if(from!=to && from && to &&
6911 (
ui_->checkBox_enableForAll->isChecked() ||
6918 ui_->pushButton_add->setEnabled(
true);
6921 else if(
ui_->checkBox_enableForAll->isChecked())
6925 UWARN(
"Button \"Add\" cannot be enabled even if \"all\" checkbox is checked, as node %d doesn't have odometry set.", from);
6929 UWARN(
"Button \"Add\" cannot be enabled even if \"all\" checkbox is checked, as node %d doesn't have odometry set.", to);
6937 ((currentLink.
from() == from && currentLink.
to() == to) || (currentLink.
from() == to && currentLink.
to() == from)))
6941 ui_->pushButton_reject->setEnabled(
true);
6948 currentLink =
iter->second;
6949 ui_->pushButton_reset->setEnabled(
true);
6952 ui_->toolButton_constraint->setEnabled(
true);
6963 if(
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked())
6973 if(refPoses.size() ==
graph.size() &&
length >= 100.0f)
6978 UINFO(
"KITTI t_err = %f %%", t_err);
6979 UINFO(
"KITTI r_err = %f deg/m", r_err);
6982 float translational_rmse = 0.0f;
6983 float translational_mean = 0.0f;
6984 float translational_median = 0.0f;
6985 float translational_std = 0.0f;
6986 float translational_min = 0.0f;
6987 float translational_max = 0.0f;
6988 float rotational_rmse = 0.0f;
6989 float rotational_mean = 0.0f;
6990 float rotational_median = 0.0f;
6991 float rotational_std = 0.0f;
6992 float rotational_min = 0.0f;
6993 float rotational_max = 0.0f;
7000 translational_median,
7012 ui_->label_rmse->setNum(translational_rmse);
7013 UINFO(
"translational_rmse=%f", translational_rmse);
7014 UINFO(
"translational_mean=%f", translational_mean);
7015 UINFO(
"translational_median=%f", translational_median);
7016 UINFO(
"translational_std=%f", translational_std);
7017 UINFO(
"translational_min=%f", translational_min);
7018 UINFO(
"translational_max=%f", translational_max);
7020 UINFO(
"rotational_rmse=%f", rotational_rmse);
7021 UINFO(
"rotational_mean=%f", rotational_mean);
7022 UINFO(
"rotational_median=%f", rotational_median);
7023 UINFO(
"rotational_std=%f", rotational_std);
7024 UINFO(
"rotational_min=%f", rotational_min);
7025 UINFO(
"rotational_max=%f", rotational_max);
7027 if(((
ui_->checkBox_alignPosesWithGPS->isEnabled() &&
ui_->checkBox_alignPosesWithGPS->isChecked()) ||
7028 (
ui_->checkBox_alignPosesWithGroundTruth->isEnabled() &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked())) &&
7033 iter->second = gtToMap *
iter->second;
7038 std::map<int, rtabmap::Transform> graphFiltered;
7039 if(
ui_->checkBox_alignScansCloudsWithGroundTruth->isEnabled() &&
7040 ui_->checkBox_alignScansCloudsWithGroundTruth->isChecked() &&
7047 graphFiltered =
graph;
7049 if(
ui_->groupBox_posefiltering->isChecked())
7052 ui_->doubleSpinBox_posefilteringRadius->value(),
7053 ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
7056 #ifdef RTABMAP_OCTOMAP
7060 if(
ui_->dockWidget_graphView->isVisible() ||
ui_->dockWidget_occupancyGridView->isVisible())
7063 UINFO(
"Update local maps list...");
7064 std::vector<int> ids =
uKeys(graphFiltered);
7065 for(
unsigned int i=0;
i<ids.size(); ++
i)
7072 else if(ids.at(
i)>0)
7076 cv::Mat ground, obstacles,
empty;
7077 if(
data.gridCellSize()>0.0f)
7079 data.uncompressData(0, 0, 0, 0, &ground, &obstacles, &
empty);
7081 localMaps_.
add(ids.at(
i), ground, obstacles,
empty,
data.gridCellSize()>0.0f?
data.gridCellSize():Parameters::defaultGridCellSize(),
data.gridViewPoint());
7082 if(!ground.empty() || !obstacles.empty())
7088 UINFO(
"Update local maps list... done (%d local maps, graph size=%d)", (
int)combinedLocalMaps.
size(), (
int)
graph.size());
7092 float cellSize = Parameters::defaultGridCellSize();
7098 if(
ui_->checkBox_wmState->isEnabled() &&
7099 ui_->checkBox_wmState->isChecked() &&
7102 bool allNodesAreInWM =
true;
7103 std::map<int, float> colors;
7108 colors.insert(std::make_pair(
iter->first, 1));
7112 allNodesAreInWM =
false;
7115 if(!allNodesAreInWM)
7117 ui_->graphViewer->updatePosterior(colors, 1, 1);
7120 QGraphicsRectItem * rectScaleItem = 0;
7121 ui_->graphViewer->clearMap();
7123 if(
graph.size() && combinedLocalMaps.
size() &&
7124 (
ui_->graphViewer->isGridMapVisible() ||
ui_->dockWidget_occupancyGridView->isVisible()))
7129 #ifdef RTABMAP_OCTOMAP
7130 if(
ui_->checkBox_octomap->isChecked())
7137 if((
ui_->dockWidget_graphView->isVisible() &&
ui_->graphViewer->isGridMapVisible()) ||
7138 (
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_2d->isChecked()))
7140 bool eroded = Parameters::defaultGridGlobalEroded();
7145 #ifdef RTABMAP_OCTOMAP
7146 if(
ui_->checkBox_octomap->isChecked())
7158 grid.
update(graphFiltered);
7159 if(
ui_->checkBox_grid_showProbMap->isChecked())
7165 map = grid.
getMap(xMin, yMin);
7169 ui_->label_timeGrid->setNum(
double(
time.elapsed())/1000.0);
7174 if(
ui_->dockWidget_graphView->isVisible() &&
ui_->graphViewer->isGridMapVisible())
7176 ui_->graphViewer->updateMap(map8U, cellSize, xMin, yMin);
7178 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_2d->isChecked())
7187 int xLast = map.cols;
7188 int yLast = map.rows;
7189 bool firstSet =
false;
7190 bool lastSet =
false;
7191 for(
int x=0;
x<map.cols && (!firstSet || !lastSet); ++
x)
7193 for(
int y=0;
y<map.rows; ++
y)
7196 if(!firstSet && map.at<
signed char>(
y,
x) != -1)
7202 int opp = map.cols-(
x+1);
7203 if(!lastSet && map.at<
signed char>(
y, opp) != -1)
7212 for(
int y=0;
y<map.rows && (!firstSet || !lastSet); ++
y)
7214 for(
int x=0;
x<map.cols; ++
x)
7217 if(!firstSet && map.at<
signed char>(
y,
x) != -1)
7223 int opp = map.rows-(
y+1);
7224 if(!lastSet && map.at<
signed char>(map.rows-(
y+1),
x) != -1)
7232 if( (xLast > xFirst && yLast > yFirst) &&
7234 xLast < map.cols-50 ||
7236 yLast < map.rows-50))
7238 rectScaleItem =
ui_->graphViewer->scene()->addRect(
7243 rectScaleItem->setTransform(QTransform::fromScale(cellSize*100.0
f, -cellSize*100.0
f),
true);
7244 rectScaleItem->setRotation(90);
7245 rectScaleItem->setPos(-yMin*100.0
f, -xMin*100.0
f);
7251 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
ui_->checkBox_grid_grid->isChecked())
7253 #ifdef RTABMAP_OCTOMAP
7254 if(
ui_->checkBox_octomap->isChecked())
7261 CloudMap cloudMap(&combinedLocalMaps, parameters);
7263 cloudMap.
update(graphFiltered);
7265 const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & groundCells = cloudMap.
getMapGround();
7266 const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & obstacleCells = cloudMap.
getMapObstacles();
7267 const pcl::PointCloud<pcl::PointXYZ>::Ptr & emptyCells = cloudMap.
getMapEmptyCells();
7270 if(groundCells->size())
7275 QColor(
ui_->lineEdit_groundColor->text()));
7278 if(obstacleCells->size())
7283 QColor(
ui_->lineEdit_obstacleColor->text()));
7286 if(
ui_->checkBox_grid_empty->isChecked() && emptyCells->size())
7291 QColor(
ui_->lineEdit_emptyColor->text()));
7299 #ifdef RTABMAP_GRIDMAP
7301 if(
ui_->dockWidget_occupancyGridView->isVisible() &&
7302 ui_->checkBox_grid_elevation->checkState() != Qt::Unchecked)
7304 GridMap gridMap(&combinedLocalMaps, parameters);
7306 gridMap.
update(graphFiltered);
7307 if(
ui_->checkBox_grid_elevation->checkState() == Qt::PartiallyChecked)
7322 ui_->graphViewer->fitInView(
ui_->graphViewer->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
7323 if(rectScaleItem != 0)
7325 ui_->graphViewer->fitInView(rectScaleItem, Qt::KeepAspectRatio);
7326 ui_->graphViewer->scene()->removeItem(rectScaleItem);
7327 delete rectScaleItem;
7330 ui_->graphViewer->update();
7331 ui_->label_iterations->setNum(
value);
7337 std::map<int, rtabmap::Transform>::const_iterator jterA =
graph.find(
iter->first);
7338 std::map<int, rtabmap::Transform>::const_iterator jterB =
graph.find(
iter->second.to());
7339 if(jterA !=
graph.end() && jterB !=
graph.end())
7346 Eigen::Vector3f
vA,
vB;
7362 if(
ui_->horizontalSlider_rotation->isEnabled())
7364 float theta =
float(
ui_->horizontalSlider_rotation->value())*
M_PI/1800.0f;
7365 ui_->graphViewer->setWorldMapRotation(theta);
7366 ui_->label_rotation->setText(QString::number(
float(-
ui_->horizontalSlider_rotation->value())/10.0f,
'f', 1) +
" deg");
7370 ui_->graphViewer->setWorldMapRotation(0);
7376 ui_->label_loopClosures->clear();
7377 ui_->label_poses->clear();
7378 ui_->label_rmse->clear();
7380 if(sender() ==
ui_->checkBox_alignPosesWithGPS &&
ui_->checkBox_alignPosesWithGPS->isChecked())
7382 ui_->checkBox_alignPosesWithGroundTruth->setChecked(
false);
7384 else if(sender() ==
ui_->checkBox_alignPosesWithGroundTruth &&
ui_->checkBox_alignPosesWithGroundTruth->isChecked())
7386 ui_->checkBox_alignPosesWithGPS->setChecked(
false);
7391 int fromId =
ui_->spinBox_optimizationsFrom->value();
7394 QMessageBox::warning(
this, tr(
""), tr(
"Graph optimization from id (%1) for which node is not linked to graph.\n Minimum=%2, Maximum=%3")
7404 std::map<int, rtabmap::Transform> poses =
odomPoses_;
7405 if(
ui_->checkBox_wmState->isEnabled() &&
7406 ui_->checkBox_wmState->isChecked() &&
7409 std::map<int, rtabmap::Transform> wmPoses;
7410 std::vector<int> & wmState =
wmStates_.at(fromId);
7411 for(
unsigned int i=0;
i<wmState.size(); ++
i)
7413 std::map<int, rtabmap::Transform>::iterator
iter = poses.find(wmState[
i]);
7414 if(
iter!=poses.end())
7416 wmPoses.insert(*
iter);
7419 if(!wmPoses.empty())
7425 UWARN(
"Empty WM poses!? Ignoring WM state... (root id=%d, wmState=%d)", fromId, wmState.size());
7432 int currentMapId =
mapIds_.at(fromId);
7433 for(std::map<int, rtabmap::Transform>::iterator
iter=poses.begin();
iter!=poses.end();)
7437 poses.erase(
iter++);
7446 ui_->menuExport_poses->setEnabled(
true);
7447 std::multimap<int, rtabmap::Link> links =
links_;
7452 int currentMapId =
mapIds_.at(fromId);
7453 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end();)
7458 links.erase(
iter++);
7468 if(
ui_->checkBox_ignorePoseCorrection->isChecked())
7470 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
7481 iter->second.from(),
7483 iter->second.type(),
7491 double markerPriorsLinearVariance = Parameters::defaultMarkerPriorsVarianceLinear();
7492 double markerPriorsAngularVariance = Parameters::defaultMarkerPriorsVarianceAngular();
7493 std::map<int, Transform> markerPriors;
7495 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceLinear(), markerPriorsLinearVariance);
7496 UASSERT(markerPriorsLinearVariance>0.0
f);
7497 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceAngular(), markerPriorsAngularVariance);
7498 UASSERT(markerPriorsAngularVariance>0.0
f);
7499 std::string markerPriorsStr;
7502 std::list<std::string> strList =
uSplit(markerPriorsStr,
'|');
7503 for(std::list<std::string>::iterator
iter=strList.begin();
iter!=strList.end(); ++
iter)
7505 std::string markerStr = *
iter;
7506 while(!markerStr.empty() && !
uIsDigit(markerStr[0]))
7508 markerStr.erase(markerStr.begin());
7510 if(!markerStr.empty())
7515 if(!
prior.isNull() &&
id>0)
7517 markerPriors.insert(std::make_pair(-
id,
prior));
7518 UDEBUG(
"Added landmark prior %d: %s",
id,
prior.prettyPrint().c_str());
7522 UERROR(
"Failed to parse element \"%s\" in parameter %s", markerStr.c_str(), Parameters::kMarkerPriors().c_str());
7525 else if(!
iter->empty())
7527 UERROR(
"Failed to parse parameter %s, value=\"%s\"", Parameters::kMarkerPriors().
c_str(),
iter->c_str());
7534 int totalNeighbor = 0;
7535 int totalNeighborMerged = 0;
7536 int totalGlobal = 0;
7537 int totalLocalTime = 0;
7538 int totalLocalSpace = 0;
7540 int totalPriors = 0;
7541 int totalLandmarks = 0;
7542 int totalGravity = 0;
7543 std::multimap<int, int> uniqueLinks;
7544 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end();)
7546 bool isUnique =
iter->second.from() ==
iter->second.to();
7549 uniqueLinks.insert(std::make_pair(
iter->second.from(),
iter->second.to()));
7561 ++totalNeighborMerged;
7565 if(
ui_->checkBox_ignoreGlobalLoop->isChecked())
7567 links.erase(
iter++);
7575 if(
ui_->checkBox_ignoreLocalLoopSpace->isChecked())
7577 links.erase(
iter++);
7585 if(
ui_->checkBox_ignoreLocalLoopTime->isChecked())
7587 links.erase(
iter++);
7595 if(
ui_->checkBox_ignoreUserLoop->isChecked())
7597 links.erase(
iter++);
7605 if(
ui_->checkBox_ignoreLandmarks->isChecked())
7607 links.erase(
iter++);
7611 if(poses.find(
iter->second.from()) != poses.end() && poses.find(
iter->second.to()) == poses.end())
7613 poses.insert(std::make_pair(
iter->second.to(), poses.at(
iter->second.from())*
iter->second.transform()));
7619 int markerId =
iter->second.to();
7620 if(markerPriors.find(markerId) != markerPriors.end())
7622 cv::Mat infMatrix = cv::Mat::eye(6, 6, CV_64FC1);
7623 infMatrix(cv::Range(0,3), cv::Range(0,3)) /= markerPriorsLinearVariance;
7624 infMatrix(cv::Range(3,6), cv::Range(3,6)) /= markerPriorsAngularVariance;
7625 links.insert(std::make_pair(markerId,
Link(markerId, markerId,
Link::kPosePrior, markerPriors.at(markerId), infMatrix)));
7626 UDEBUG(
"Added prior %d : %s (variance: lin=%f ang=%f)", markerId, markerPriors.at(markerId).prettyPrint().c_str(),
7627 markerPriorsLinearVariance, markerPriorsAngularVariance);
7646 ui_->label_loopClosures->setText(tr(
"(%1, %2, %3, %4, %5, %6, %7, %8, %9)")
7648 .
arg(totalNeighborMerged)
7650 .
arg(totalLocalSpace)
7651 .
arg(totalLocalTime)
7654 .
arg(totalLandmarks)
7655 .
arg(totalGravity));
7658 if(
ui_->checkBox_ignoreIntermediateNodes->isVisible() &&
7659 ui_->checkBox_ignoreIntermediateNodes->isEnabled() &&
7660 ui_->checkBox_ignoreIntermediateNodes->isChecked())
7662 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
7670 std::multimap<int, Link>::iterator uter = links.find(link.
to());
7671 while(uter != links.end() &&
7672 uter->first==link.
to() &&
7673 uter->second.from()>uter->second.to())
7677 if(uter != links.end())
7679 poses.erase(link.
to());
7680 link = link.
merge(uter->second, uter->second.type());
7681 links.erase(uter->first);
7689 iter->second = link;
7694 bool applyRotation = sender() ==
ui_->pushButton_applyRotation;
7697 float xMin, yMin, cellSize;
7701 QMessageBox::StandardButton r = QMessageBox::question(
this,
7702 tr(
"Rotate Optimized Graph"),
7703 tr(
"There is a 2D occupancy grid or mesh already saved in "
7704 "database. Applying rotation will clear them (they can be "
7705 "regenerated later from File menu options). "
7706 "Do you want to continue?"),
7707 QMessageBox::Cancel | QMessageBox::Yes,
7708 QMessageBox::Cancel);
7709 if(r != QMessageBox::Yes)
7711 applyRotation =
false;
7716 std::map<int, Transform> optPoses;
7719 ui_->comboBox_optimizationFlavor->currentIndex() == 2)
7722 if(optPoses.empty())
7724 ui_->comboBox_optimizationFlavor->setCurrentIndex(0);
7725 QMessageBox::warning(
this, tr(
"Optimization Flavor"),
7726 tr(
"There is no local optimized graph in the database, "
7727 "falling back to global iterative optimization."));
7732 ui_->comboBox_optimizationFlavor->currentIndex() != 2)
7734 if(
ui_->horizontalSlider_rotation->value()!=0 && applyRotation)
7736 float theta =
float(-
ui_->horizontalSlider_rotation->value())*
M_PI/1800.0f;
7738 poses.at(fromId) = rotT * poses.at(fromId);
7745 std::map<int, rtabmap::Transform> posesOut;
7746 std::multimap<int, rtabmap::Link> linksOut;
7747 UINFO(
"Get connected graph from %d (%d poses, %d links)", fromId, (
int)poses.size(), (
int)links.size());
7754 UINFO(
"Connected graph of %d poses and %d links", (
int)posesOut.size(), (
int)linksOut.size());
7757 std::map<int, rtabmap::Transform> finalPoses = optimizer->
optimize(fromId, posesOut, linksOut,
ui_->comboBox_optimizationFlavor->currentIndex()==0?&
graphes_:0);
7758 ui_->label_timeOptimization->setNum(
double(
time.elapsed())/1000.0);
7760 if(posesOut.size() && finalPoses.empty())
7762 UWARN(
"Optimization failed... (poses=%d, links=%d).", (
int)posesOut.size(), (
int)linksOut.size());
7765 QMessageBox::warning(
this, tr(
"Graph optimization error!"), tr(
"Graph optimization has failed. See the terminal for potential errors. "
7766 "Give it a try with %1=0 and %2=true.").
arg(Parameters::kOptimizerStrategy().
c_str()).
arg(Parameters::kOptimizerVarianceIgnored().
c_str()));
7770 QMessageBox::warning(
this, tr(
"Graph optimization error!"), tr(
"Graph optimization has failed. See the terminal for potential errors."));
7773 ui_->label_poses->setNum((
int)finalPoses.size());
7777 if(applyRotation && !finalPoses.empty())
7779 ui_->comboBox_optimizationFlavor->setCurrentIndex(2);
7782 if(lastLocalizationPose.
isNull())
7785 lastLocalizationPose = finalPoses.rbegin()->second;
7789 float xMin, yMin, cellSize;
7795 QMessageBox::StandardButton r = QMessageBox::question(
this,
7796 tr(
"Rotate Optimized Graph"),
7797 tr(
"Optimized graph has been rotated and saved back to database. "
7798 "Note that 2D occupancy grid and mesh have been cleared (if set). "
7799 "Do you want to regenerate the 2D occupancy grid now "
7800 "(can be done later from File menu)?"),
7801 QMessageBox::Ignore | QMessageBox::Yes,
7803 ui_->actionEdit_optimized_2D_map->setEnabled(
false);
7804 ui_->actionExport_saved_2D_map->setEnabled(
false);
7805 ui_->actionImport_2D_map->setEnabled(
false);
7806 ui_->actionView_optimized_mesh->setEnabled(
false);
7807 ui_->actionExport_optimized_mesh->setEnabled(
false);
7808 if(r == QMessageBox::Yes)
7817 if(
ui_->comboBox_optimizationFlavor->currentIndex() == 2)
7822 ui_->label_timeOptimization->setNum(0);
7823 ui_->label_poses->setNum((
int)optPoses.size());
7826 ui_->horizontalSlider_rotation->setEnabled(
false);
7827 ui_->pushButton_applyRotation->setEnabled(
false);
7828 ui_->spinBox_optimizationsFrom->setEnabled(
false);
7829 ui_->checkBox_spanAllMaps->setEnabled(
false);
7830 ui_->checkBox_wmState->setEnabled(
false);
7831 ui_->checkBox_alignPosesWithGPS->setEnabled(
false);
7832 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
false);
7833 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
false);
7834 ui_->checkBox_ignoreIntermediateNodes->setEnabled(
false);
7839 ui_->pushButton_applyRotation->setEnabled(
true);
7840 ui_->horizontalSlider_rotation->setEnabled(
true);
7841 ui_->spinBox_optimizationsFrom->setEnabled(
true);
7842 ui_->checkBox_spanAllMaps->setEnabled(
true);
7843 ui_->checkBox_wmState->setEnabled(
true);
7844 ui_->checkBox_alignPosesWithGPS->setEnabled(
ui_->checkBox_alignPosesWithGPS->isVisible());
7845 ui_->checkBox_alignPosesWithGroundTruth->setEnabled(
ui_->checkBox_alignPosesWithGroundTruth->isVisible());
7846 ui_->checkBox_alignScansCloudsWithGroundTruth->setEnabled(
ui_->checkBox_alignScansCloudsWithGroundTruth->isVisible());
7847 ui_->checkBox_ignoreIntermediateNodes->setEnabled(
true);
7853 if(
ui_->doubleSpinBox_optimizationScale->value()!=-1.0)
7858 for(std::map<int, Transform>::iterator jter=
iter->begin(); jter!=
iter->end(); ++jter)
7860 jter->second = jter->second.clone();
7861 jter->second.x() *=
ui_->doubleSpinBox_optimizationScale->value();
7862 jter->second.y() *=
ui_->doubleSpinBox_optimizationScale->value();
7863 jter->second.z() *=
ui_->doubleSpinBox_optimizationScale->value();
7868 ui_->horizontalSlider_iterations->setMaximum((
int)
graphes_.size()-1);
7869 ui_->horizontalSlider_iterations->setValue((
int)
graphes_.size()-1);
7870 ui_->horizontalSlider_iterations->setEnabled(
true);
7871 ui_->spinBox_optimizationsFrom->setEnabled(
true);
7876 ui_->horizontalSlider_iterations->setEnabled(
false);
7877 ui_->spinBox_optimizationsFrom->setEnabled(
false);
7883 if(sender() ==
ui_->checkBox_grid_2d && !
ui_->checkBox_grid_2d->isChecked())
7891 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
7892 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7893 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7894 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7895 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
7896 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7897 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7898 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7907 #ifdef RTABMAP_OCTOMAP
7908 ui_->comboBox_octomap_rendering_type->setVisible(
ui_->checkBox_octomap->isChecked());
7909 ui_->spinBox_grid_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7910 ui_->checkBox_grid_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7911 ui_->checkBox_grid_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7912 ui_->label_octomap_cubes->setVisible(
ui_->checkBox_octomap->isChecked());
7913 ui_->label_octomap_depth->setVisible(
ui_->checkBox_octomap->isChecked());
7914 ui_->label_octomap_empty->setVisible(!
ui_->checkBox_octomap->isChecked() ||
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7915 ui_->label_octomap_frontiers->setVisible(
ui_->checkBox_octomap->isChecked() &&
ui_->comboBox_octomap_rendering_type->currentIndex()==0);
7917 if(
ui_->checkBox_octomap->isChecked())
7929 if(
ui_->comboBox_octomap_rendering_type->currentIndex()>0)
7935 pcl::IndicesPtr obstacles(
new std::vector<int>);
7936 pcl::IndicesPtr
empty(
new std::vector<int>);
7937 pcl::IndicesPtr ground(
new std::vector<int>);
7938 pcl::IndicesPtr frontiers(
new std::vector<int>);
7939 std::vector<double> prob;
7941 ui_->spinBox_grid_depth->value(),
7951 pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7952 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
7956 pcl::PointCloud<pcl::PointXYZRGB>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7957 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
7963 pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7964 pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
7968 pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(
new pcl::PointCloud<pcl::PointXYZ>);
7969 pcl::copyPointCloud(*cloud, *ground, *groundCloud);
7974 if(
ui_->checkBox_grid_empty->isChecked())
7976 if(prob.size()==cloud->size())
7978 float occThr = Parameters::defaultGridGlobalOccupancyThr();
7979 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kGridGlobalOccupancyThr(), occThr);
7980 pcl::PointCloud<pcl::PointXYZRGB>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
7981 emptyCloud->resize(
empty->size());
7982 for(
unsigned int i=0;
i<
empty->size(); ++
i)
7984 emptyCloud->points.at(
i).x = cloud->points.at(
empty->at(
i)).x;
7985 emptyCloud->points.at(
i).y = cloud->points.at(
empty->at(
i)).y;
7986 emptyCloud->points.at(
i).z = cloud->points.at(
empty->at(
i)).z;
7987 QColor hsv = QColor::fromHsv(
int(prob.at(
empty->at(
i))/occThr*240.0), 255, 255, 255);
7988 QRgb color = hsv.rgb();
7989 emptyCloud->points.at(
i).r = qRed(color);
7990 emptyCloud->points.at(
i).g = qGreen(color);
7991 emptyCloud->points.at(
i).b = qBlue(color);
7999 pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(
new pcl::PointCloud<pcl::PointXYZ>);
8000 pcl::copyPointCloud(*cloud, *
empty, *emptyCloud);
8007 if(
ui_->checkBox_grid_frontiers->isChecked())
8009 pcl::PointCloud<pcl::PointXYZ>::Ptr frontiersCloud(
new pcl::PointCloud<pcl::PointXYZ>);
8010 pcl::copyPointCloud(*cloud, *frontiers, *frontiersCloud);
8018 if(
ui_->dockWidget_view3d->isVisible() &&
ui_->checkBox_showGrid->isChecked())
8032 link = findIter->second;
8039 link = findIter->second;
8044 if(findIter !=
links_.end())
8046 link = findIter->second;
8060 int from =
ids_.at(
ui_->horizontalSlider_A->value());
8061 int to =
ids_.at(
ui_->horizontalSlider_B->value());
8067 UDEBUG(
"%d -> %d", from, to);
8068 bool switchedIds =
false;
8071 UWARN(
"Cannot refine link to same node");
8079 UERROR(
"Not found link! (%d->%d)", from, to);
8083 UDEBUG(
"%d -> %d (type=%d)", currentLink.
from(), currentLink.
to(), currentLink.
type());
8085 if(
ui_->checkBox_showOptimized->isChecked() &&
8088 (
int)
graphes_.size()-1 ==
ui_->horizontalSlider_iterations->maximum())
8093 std::map<int, rtabmap::Transform>::iterator iterFrom =
graph.find(currentLink.
from());
8094 std::map<int, rtabmap::Transform>::iterator iterTo =
graph.find(currentLink.
to());
8095 if(iterFrom !=
graph.end() && iterTo !=
graph.end())
8102 else if(
ui_->checkBox_ignorePoseCorrection->isChecked() &&
8125 UERROR(
"Signature %d not found!", currentLink.
from());
8135 std::map<int, rtabmap::Transform> scanPoses;
8139 userData.type() == CV_8SC1 &&
8140 userData.rows == 1 &&
8141 userData.cols >= 8 &&
8142 userData.at<
char>(userData.cols-1) == 0 &&
8143 memcmp(userData.data,
"SCANS:", 6) == 0 &&
8144 currentLink.
from() > currentLink.
to())
8146 std::string scansStr = (
const char *)userData.data;
8147 UINFO(
"Detected \"%s\" in links's user data", scansStr.c_str());
8148 if(!scansStr.empty())
8150 std::list<std::string> strs =
uSplit(scansStr,
':');
8151 if(strs.size() == 2)
8153 std::list<std::string> strIds =
uSplit(strs.rbegin()->c_str(),
';');
8154 for(std::list<std::string>::iterator
iter=strIds.begin();
iter!=strIds.end(); ++
iter)
8156 int id = atoi(
iter->c_str());
8163 UERROR(
"Not found %d node!",
id);
8169 if(scanPoses.size()>1)
8175 std::map<int, rtabmap::Transform> posesOut;
8176 std::multimap<int, rtabmap::Link> linksOut;
8184 if(scanPoses.size() != posesOut.size())
8186 UWARN(
"Scan poses input and output are different! %d vs %d", (
int)scanPoses.size(), (
int)posesOut.size());
8187 UWARN(
"Input poses: ");
8188 for(std::map<int, Transform>::iterator
iter=scanPoses.begin();
iter!=scanPoses.end(); ++
iter)
8192 UWARN(
"Input links: ");
8194 for(std::multimap<int, Link>::iterator
iter=modifiedLinks.begin();
iter!=modifiedLinks.end(); ++
iter)
8200 scanPoses = optimizer->
optimize(currentLink.
to(), posesOut, linksOut);
8203 std::map<int, Transform> filteredScanPoses = scanPoses;
8204 float proximityFilteringRadius = 0.0f;
8205 Parameters::parse(parameters, Parameters::kRGBDProximityPathFilteringRadius(), proximityFilteringRadius);
8206 if(scanPoses.size() > 2 && proximityFilteringRadius > 0.0f)
8211 filteredScanPoses.insert(*scanPoses.find(currentLink.
to()));
8218 int maxPoints = fromScan.
size();
8221 UWARN(
"From scan %d is empty!", fromS->
id());
8223 pcl::PointCloud<pcl::PointXYZ>::Ptr assembledToClouds(
new pcl::PointCloud<pcl::PointXYZ>);
8224 pcl::PointCloud<pcl::PointNormal>::Ptr assembledToNormalClouds(
new pcl::PointCloud<pcl::PointNormal>);
8225 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledToIClouds(
new pcl::PointCloud<pcl::PointXYZI>);
8226 pcl::PointCloud<pcl::PointXYZINormal>::Ptr assembledToNormalIClouds(
new pcl::PointCloud<pcl::PointXYZINormal>);
8227 pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledToRGBClouds(
new pcl::PointCloud<pcl::PointXYZRGB>);
8228 pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr assembledToNormalRGBClouds(
new pcl::PointCloud<pcl::PointXYZRGBNormal>);
8229 for(std::map<int, Transform>::const_iterator
iter = filteredScanPoses.begin();
iter!=filteredScanPoses.end(); ++
iter)
8231 if(
iter->first != currentLink.
from())
8235 if(!
data.laserScanCompressed().isEmpty())
8238 data.uncompressData(0, 0, &scan);
8281 if(scan.
size() > maxPoints)
8283 maxPoints = scan.
size();
8288 UWARN(
"scan format of %d is not the same than from scan %d: %d vs %d",
data.id(), fromS->
id(), scan.
format(), fromScan.
format());
8293 UWARN(
"Laser scan not found for signature %d",
iter->first);
8299 if(assembledToNormalClouds->size())
8303 else if(assembledToClouds->size())
8307 else if(assembledToNormalIClouds->size())
8311 else if(assembledToIClouds->size())
8315 else if(assembledToNormalRGBClouds->size())
8320 else if(assembledToRGBClouds->size())
8327 UWARN(
"Assembled scan is empty!");
8343 info.covariance*=100.0;
8351 UERROR(
"Signature %d not found!", currentLink.
to());
8356 bool reextractVisualFeatures =
uStr2Bool(parameters.at(Parameters::kRGBDLoopClosureReextractFeatures()));
8360 reextractVisualFeatures ||
8373 if(reextractVisualFeatures)
8376 fromS->
sensorData().
setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
8383 if(
ui_->checkBox_icp_from_depth->isChecked())
8386 cv::Mat tmpA, tmpB, tmpC, tmpD;
8391 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8392 ui_->doubleSpinBox_icp_maxDepth->value(),
8393 ui_->doubleSpinBox_icp_minDepth->value(),
8395 ui_->parameters_toolbox->getParameters());
8398 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8399 ui_->doubleSpinBox_icp_maxDepth->value(),
8400 ui_->doubleSpinBox_icp_minDepth->value(),
8402 ui_->parameters_toolbox->getParameters());
8404 if(cloudFrom->empty() && cloudTo->empty())
8406 std::string
msg =
"Option to generate scan from depth is checked (GUI Parameters->Refine), but "
8407 "resulting clouds from depth are empty. Transformation estimation will likely "
8408 "fails. Uncheck the parameter to use laser scans.";
8412 QMessageBox::warning(
this,
8414 tr(
"%1").
arg(
msg.c_str()));
8419 UWARN(
"There are laser scans in data, but generate laser scan from "
8420 "depth image option is activated (GUI Parameters->Refine). "
8421 "Ignoring saved laser scans...");
8424 int maxLaserScans = cloudFrom->size();
8438 cv::Mat tmpA, tmpB, tmpC, tmpD;
8443 UINFO(
"Uncompress time: %f s",
timer.ticks());
8445 if(fromS->
id() < toS->
id())
8457 UINFO(
"(%d ->%d) Registration time: %f s", currentLink.
from(), currentLink.
to(),
timer.ticks());
8463 if(
info.covariance.at<
double>(0,0)<=0.0)
8465 info.covariance = cv::Mat::eye(6,6,CV_64FC1)*0.0001;
8479 if(
iter->second.to() == currentLink.
to() &&
8480 iter->second.type() == currentLink.
type())
8482 iter->second = newLink;
8499 if(!silent &&
ui_->dockWidget_constraints->isVisible())
8501 if(toS && fromS->
id() > 0 && toS->
id() > 0)
8504 std::multimap<int, cv::KeyPoint> keypointsFrom;
8505 std::multimap<int, cv::KeyPoint> keypointsTo;
8510 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8517 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8543 if(toS && fromS->
id() > 0 && toS->
id() > 0)
8546 std::multimap<int, cv::KeyPoint> keypointsFrom;
8547 std::multimap<int, cv::KeyPoint> keypointsTo;
8552 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
8559 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
8575 QMessageBox::warning(
this,
8577 tr(
"Cannot find a transformation between nodes %1 and %2: %3").
arg(currentLink.
from()).
arg(currentLink.
to()).arg(
info.rejectedMsg.c_str()));
8585 int from =
ids_.at(
ui_->horizontalSlider_A->value());
8586 int to =
ids_.at(
ui_->horizontalSlider_B->value());
8592 bool switchedIds =
false;
8595 UWARN(
"Cannot add link to same node");
8605 std::list<Signature*> signatures;
8620 bool loopCovLimited = Parameters::defaultRGBDLoopCovLimited();
8621 Parameters::parse(parameters, Parameters::kRGBDLoopCovLimited(), loopCovLimited);
8631 ids.push_back(from);
8634 if(signatures.size() != 2)
8636 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
8642 fromS = *signatures.begin();
8643 toS = *signatures.rbegin();
8645 bool reextractVisualFeatures =
uStr2Bool(parameters.at(Parameters::kRGBDLoopClosureReextractFeatures()));
8648 reextractVisualFeatures ||
8656 if(reextractVisualFeatures)
8659 fromS->
sensorData().
setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
8668 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8669 ui_->doubleSpinBox_icp_maxDepth->value(),
8670 ui_->doubleSpinBox_icp_minDepth->value(),
8672 ui_->parameters_toolbox->getParameters());
8675 ui_->spinBox_icp_decimation->value()==0?1:
ui_->spinBox_icp_decimation->value(),
8676 ui_->doubleSpinBox_icp_maxDepth->value(),
8677 ui_->doubleSpinBox_icp_minDepth->value(),
8679 ui_->parameters_toolbox->getParameters());
8681 if(cloudFrom->empty() && cloudTo->empty())
8683 std::string
msg =
"Option to generate scan from depth is checked (GUI Parameters->Refine), but "
8684 "resulting clouds from depth are empty. Transformation estimation will likely "
8685 "fails. Uncheck the parameter to use laser scans.";
8689 QMessageBox::warning(
this,
8691 tr(
"%1").
arg(
msg.c_str()));
8696 UWARN(
"There are laser scans in data, but generate laser scan from "
8697 "depth image option is activated (GUI Parameters->Refine). Ignoring saved laser scans...");
8700 int maxLaserScans = cloudFrom->size();
8705 else if(!reextractVisualFeatures && fromS->
getWords().empty() && toS->
getWords().empty())
8707 std::string
msg =
uFormat(
"\"%s\" is false and signatures (%d and %d) don't have words, "
8708 "registration will not be possible. Set \"%s\" to true.",
8709 Parameters::kRGBDLoopClosureReextractFeatures().
c_str(),
8712 Parameters::kRGBDLoopClosureReextractFeatures().c_str());
8716 QMessageBox::warning(
this,
8718 tr(
"%1").
arg(
msg.c_str()));
8723 bool guessFromGraphRejected =
false;
8729 std::map<int, Transform> optimizedPoses =
graphes_.back();
8730 if(optimizedPoses.size() > 0)
8732 std::map<int, Transform>::iterator fromIter = optimizedPoses.find(from);
8733 std::map<int, Transform>::iterator toIter = optimizedPoses.find(to);
8734 if(fromIter != optimizedPoses.end() &&
8735 toIter != optimizedPoses.end())
8739 if(QMessageBox::question(
this,
8740 tr(
"Add constraint from optimized graph"),
8741 tr(
"Registration is done without vision (see %1 parameter), "
8742 "do you want to use a guess from the optimized graph?"
8744 "\n\nOtherwise, if "
8745 "the database has images, it is recommended to use %2=2 instead so that "
8746 "the guess can be found visually.")
8747 .
arg(Parameters::kRegStrategy().
c_str()).
arg(Parameters::kRegStrategy().
c_str()),
8748 QMessageBox::Yes | QMessageBox::No,
8749 QMessageBox::Yes) == QMessageBox::Yes)
8751 guess = fromIter->second.
inverse() * toIter->second;
8755 guessFromGraphRejected =
true;
8758 else if(silentlyUseOptimizedGraphAsGuess)
8760 guess = fromIter->second.
inverse() * toIter->second;
8765 if(guess.
isNull() && !silent && !guessFromGraphRejected)
8767 if(QMessageBox::question(
this,
8768 tr(
"Add constraint without guess"),
8769 tr(
"Registration is done without vision (see %1 parameter) and we cannot (or we don't want to) find relative "
8770 "transformation between the nodes with the current graph. Do you want to use an identity "
8771 "transform for ICP guess? "
8773 "\n\nOtherwise, if the database has images, it is recommended to use %2=2 "
8774 "instead so that the guess can be found visually.")
8775 .
arg(Parameters::kRegStrategy().
c_str()).
arg(Parameters::kRegStrategy().
c_str()),
8776 QMessageBox::Yes | QMessageBox::Abort,
8777 QMessageBox::Abort) == QMessageBox::Yes)
8783 guessFromGraphRejected =
true;
8805 cv::Mat information =
info.covariance.inv();
8806 if(odomMaxInf.size() == 6 && information.cols==6 && information.rows==6)
8808 for(
int i=0;
i<6; ++
i)
8810 if(information.at<
double>(
i,
i) > odomMaxInf[
i])
8812 information.at<
double>(
i,
i) = odomMaxInf[
i];
8819 else if(!silent && !guessFromGraphRejected)
8821 QMessageBox::StandardButton button = QMessageBox::warning(
this,
8823 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()),
8824 QMessageBox::Yes | QMessageBox::No,
8826 if(button == QMessageBox::Yes)
8838 bool updateConstraints = newLink.
isValid();
8839 float maxOptimizationError =
uStr2Float(
ui_->parameters_toolbox->getParameters().at(Parameters::kRGBDOptimizeMaxError()));
8841 maxOptimizationError > 0.0f &&
8842 uStr2Int(
ui_->parameters_toolbox->getParameters().at(Parameters::kOptimizerIterations())) > 0.0f)
8844 int fromId = newLink.
from();
8846 linksIn.insert(std::make_pair(newLink.
from(), newLink));
8847 const Link * maxLinearLink = 0;
8848 const Link * maxAngularLink = 0;
8849 float maxLinearErrorRatio = 0.0f;
8850 float maxAngularErrorRatio = 0.0f;
8852 std::map<int, Transform> poses;
8853 std::multimap<int, Link> links;
8861 const std::map<int, Transform> & optimizedPoses =
graphes_.back();
8862 for(std::map<int, Transform>::iterator
iter = poses.begin();
iter!=poses.end(); ++
iter)
8864 if(optimizedPoses.find(
iter->first) != optimizedPoses.end())
8866 iter->second = optimizedPoses.at(
iter->first);
8870 UASSERT(poses.find(fromId) != poses.end());
8871 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());
8872 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());
8874 std::map<int, Transform> posesIn = poses;
8875 poses = optimizer->
optimize(fromId, posesIn, links);
8876 if(posesIn.size() && poses.empty())
8878 UWARN(
"Optimization failed... (poses=%d, links=%d).", (
int)posesIn.size(), (
int)links.size());
8883 float maxLinearError = 0.0f;
8884 float maxAngularError = 0.0f;
8888 maxLinearErrorRatio,
8889 maxAngularErrorRatio,
8896 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()));
8897 if(maxLinearErrorRatio > maxOptimizationError)
8899 msg =
uFormat(
"Rejecting edge %d->%d because "
8900 "graph error is too large (abs=%f m) after optimization (ratio %f for edge %d->%d, stddev=%f m). "
8905 maxLinearErrorRatio,
8906 maxLinearLink->
from(),
8907 maxLinearLink->
to(),
8909 Parameters::kRGBDOptimizeMaxError().c_str(),
8910 maxOptimizationError);
8915 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()));
8916 if(maxAngularErrorRatio > maxOptimizationError)
8918 msg =
uFormat(
"Rejecting edge %d->%d because "
8919 "graph error is too large (abs=%f deg) after optimization (ratio %f for edge %d->%d, stddev=%f deg). "
8923 maxAngularError*180.0f/CV_PI,
8924 maxAngularErrorRatio,
8925 maxAngularLink->
from(),
8926 maxAngularLink->
to(),
8928 Parameters::kRGBDOptimizeMaxError().c_str(),
8929 maxOptimizationError);
8935 msg =
uFormat(
"Rejecting edge %d->%d because graph optimization has failed!",
8944 QMessageBox::warning(
this,
8946 tr(
"%1").
arg(
msg.c_str()));
8949 updateConstraints =
false;
8952 if(updateConstraints && silent && !
graphes_.empty() &&
graphes_.back().size() == poses.size())
8958 if(updateConstraints)
8967 if(newLink.
from() < newLink.
to())
8979 if((updateConstraints && newLink.
from() > newLink.
to()) || (!updateConstraints && switchedIds))
8986 if(updateConstraints)
8993 std::multimap<int, cv::KeyPoint> keypointsFrom;
8994 std::multimap<int, cv::KeyPoint> keypointsTo;
8999 keypointsFrom.insert(keypointsFrom.end(), std::make_pair(
iter->first, fromS->
getWordsKpts()[
iter->second]));
9006 keypointsTo.insert(keypointsTo.end(), std::make_pair(
iter->first, toS->
getWordsKpts()[
iter->second]));
9013 else if(updateConstraints)
9020 for(std::list<Signature*>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
9025 return updateConstraints;
9030 int from =
ids_.at(
ui_->horizontalSlider_A->value());
9031 int to =
ids_.at(
ui_->horizontalSlider_B->value());
9034 int position =
ui_->horizontalSlider_loops->value();
9049 UWARN(
"Cannot reset link to same node");
9066 int priorId = sender() ==
ui_->toolButton_remove_priorA?
ids_.at(
ui_->horizontalSlider_A->value()):
9067 sender() ==
ui_->toolButton_remove_priorB?
ids_.at(
ui_->horizontalSlider_B->value()):0;
9069 int from = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_A->value());
9070 int to = priorId>0?priorId:
ids_.at(
ui_->horizontalSlider_B->value());
9073 int position =
ui_->horizontalSlider_loops->value();
9086 if(priorId==0 && from == to)
9088 UWARN(
"Cannot reject link to same node");
9092 bool removed =
false;
9095 std::multimap<int, Link>::iterator
iter;
9101 QMessageBox::StandardButton button = QMessageBox::warning(
this, tr(
"Reject link"),
9102 tr(
"Removing the neighbor link %1->%2 will split the graph. Do you want to continue?").
arg(from).
arg(to),
9103 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
9104 if(button != QMessageBox::Yes)
9134 bool priorsIgnored = Parameters::defaultOptimizerPriorsIgnored();
9135 Parameters::parse(
ui_->parameters_toolbox->getParameters(), Parameters::kOptimizerPriorsIgnored(), priorsIgnored);
9136 int indexA =
ui_->horizontalSlider_A->value();
9137 int indexB =
ui_->horizontalSlider_B->value();
9142 if(
ui_->horizontalSlider_A->value() != indexA)
9143 ui_->horizontalSlider_A->setValue(indexA);
9146 if(
ui_->horizontalSlider_B->value() != indexB)
9147 ui_->horizontalSlider_B->setValue(indexB);
9155 const std::multimap<int, rtabmap::Link> & edgeConstraints)
9159 std::multimap<int, rtabmap::Link> links;
9160 for(std::multimap<int, rtabmap::Link>::const_iterator
iter=edgeConstraints.begin();
9161 iter!=edgeConstraints.end();
9164 std::multimap<int, rtabmap::Link>::iterator findIter;
9169 UDEBUG(
"Removed link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9177 if(
iter->second.from() == findIter->second.to() &&
9178 iter->second.from() !=
iter->second.to())
9180 links.insert(std::make_pair(
iter->second.from(), findIter->second.inverse()));
9184 links.insert(*findIter);
9186 UDEBUG(
"Updated link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9190 links.insert(*
iter);
9194 for(std::multimap<int, rtabmap::Link>::const_iterator
iter=
linksAdded_.begin();
9201 links.insert(*findIter);
9202 links.insert(std::make_pair(findIter->second.to(), findIter->second.inverse()));
9203 UDEBUG(
"Added refined link (%d->%d, %d)", findIter->second.from(), findIter->second.to(), findIter->second.type());
9207 UDEBUG(
"Added link (%d->%d, %d)",
iter->second.from(),
iter->second.to(),
iter->second.type());
9208 links.insert(*
iter);
9209 links.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
9217 UDEBUG(
"%d %d", from, to);
9220 int position =
ui_->horizontalSlider_neighbors->value();
9221 std::multimap<int, Link> linksSortedByChildren;
9222 for(std::multimap<int, rtabmap::Link>::iterator
iter = links.begin();
iter!=links.end(); ++
iter)
9224 if(
iter->second.from() <
iter->second.to())
9226 linksSortedByChildren.insert(*
iter);
9230 for(std::multimap<int, rtabmap::Link>::iterator
iter = linksSortedByChildren.begin();
iter!=linksSortedByChildren.end(); ++
iter)
9232 if(!
iter->second.transform().isNull())
9237 if((
iter->second.from() == from &&
iter->second.to() == to) ||
9238 (
iter->second.to() == from &&
iter->second.from() == to))
9247 UERROR(
"Transform null for link from %d to %d",
iter->first,
iter->second.to());
9258 ui_->horizontalSlider_neighbors->setMinimum(0);
9260 ui_->horizontalSlider_neighbors->setEnabled(
true);
9261 if(
position !=
ui_->horizontalSlider_neighbors->value())
9263 ui_->horizontalSlider_neighbors->setValue(
position);
9272 ui_->horizontalSlider_neighbors->setEnabled(
false);
9281 UDEBUG(
"%d %d", from, to);
9284 int position =
ui_->horizontalSlider_loops->value();
9285 std::multimap<int, Link> linksSortedByParents;
9286 for(std::multimap<int, rtabmap::Link>::iterator
iter = links.begin();
iter!=links.end(); ++
iter)
9288 if(
iter->second.to() >
iter->second.from() &&
iter->second.from() < 0)
9290 linksSortedByParents.insert(std::make_pair(
iter->second.to(),
iter->second.inverse()));
9292 else if(
iter->second.to() <
iter->second.from())
9294 linksSortedByParents.insert(*
iter);
9297 for(std::multimap<int, rtabmap::Link>::iterator
iter = linksSortedByParents.begin();
iter!=linksSortedByParents.end(); ++
iter)
9299 if(!
iter->second.transform().isNull())
9304 if((
iter->second.from() == from &&
iter->second.to() == to) ||
9305 (
iter->second.to() == from &&
iter->second.from() == to))
9314 UERROR(
"Transform null for link from %d to %d",
iter->first,
iter->second.to());
9325 ui_->horizontalSlider_loops->setMinimum(0);
9326 ui_->horizontalSlider_loops->setMaximum(
loopLinks_.size()-1);
9327 ui_->horizontalSlider_loops->setEnabled(
true);
9328 if(
position !=
ui_->horizontalSlider_loops->value())
9339 ui_->horizontalSlider_loops->setEnabled(
false);
9350 for(QStringList::const_iterator
iter=parametersChanged.constBegin();
9354 QString group =
iter->split(
'/').first();