2011-06-05 14:45:39 +00:00
/*
2016-07-17 21:57:10 -04:00
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
2014-08-11 17:00:55 +00:00
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
2011-06-05 14:45:39 +00:00
2012-12-11 18:05:05 +00:00
#include "rtabmap/gui/DatabaseViewer.h"
2014-02-11 23:04:22 +00:00
#include "rtabmap/gui/CloudViewer.h"
2012-12-11 18:05:05 +00:00
#include "ui_DatabaseViewer.h"
2015-03-16 19:51:22 +00:00
#include <QMessageBox>
#include <QFileDialog>
#include <QInputDialog>
#include <QGraphicsLineItem>
2014-09-21 21:14:17 +00:00
#include <QtGui/QCloseEvent>
2015-03-16 19:51:22 +00:00
#include <QGraphicsOpacityEffect>
2011-06-05 14:45:39 +00:00
#include <QtCore/QBuffer>
#include <QtCore/QTextStream>
2015-02-27 15:24:15 -05:00
#include <QtCore/QDateTime>
2015-03-04 17:27:44 -05:00
#include <QtCore/QSettings>
2013-04-30 20:16:01 +00:00
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UConversion.h>
2011-06-05 14:45:39 +00:00
#include <opencv2/core/core_c.h>
2014-02-11 23:04:22 +00:00
#include <opencv2/highgui/highgui.hpp>
2013-04-30 20:16:01 +00:00
#include <rtabmap/utilite/UTimer.h>
2015-06-24 20:20:27 -04:00
#include <rtabmap/utilite/UFile.h>
2011-06-05 14:45:39 +00:00
#include "rtabmap/core/DBDriver.h"
2012-06-24 17:19:34 +00:00
#include "rtabmap/gui/KeypointItem.h"
2016-05-05 18:25:26 -04:00
#include "rtabmap/gui/CloudViewer.h"
2015-08-25 10:32:27 -04:00
#include "rtabmap/utilite/UCv2Qt.h"
2013-12-11 00:12:44 +00:00
#include "rtabmap/core/util3d.h"
2015-05-13 19:54:23 -04:00
#include "rtabmap/core/util3d_transforms.h"
#include "rtabmap/core/util3d_filtering.h"
#include "rtabmap/core/util3d_surface.h"
#include "rtabmap/core/util3d_registration.h"
#include "rtabmap/core/util3d_mapping.h"
#include "rtabmap/core/util2d.h"
2013-12-11 00:12:44 +00:00
#include "rtabmap/core/Signature.h"
2015-10-16 14:54:47 -04:00
#include "rtabmap/core/Memory.h"
2014-11-04 18:58:40 +00:00
#include "rtabmap/core/Features2d.h"
2015-01-23 11:17:42 -05:00
#include "rtabmap/core/Compression.h"
#include "rtabmap/core/Graph.h"
2015-12-17 14:13:19 -05:00
#include "rtabmap/core/Stereo.h"
2015-11-27 11:09:39 -05:00
#include "rtabmap/core/Optimizer.h"
2015-11-22 18:08:32 -05:00
#include "rtabmap/core/RegistrationVis.h"
#include "rtabmap/core/RegistrationIcp.h"
2016-08-25 23:47:01 -04:00
#include "rtabmap/core/OccupancyGrid.h"
2014-01-22 19:49:28 +00:00
#include "rtabmap/gui/DataRecorder.h"
2014-08-18 23:14:04 +00:00
#include "rtabmap/core/SensorData.h"
2016-09-07 12:15:44 -04:00
#include "rtabmap/core/GainCompensator.h"
2014-01-22 19:49:28 +00:00
#include "ExportDialog.h"
2015-08-25 10:32:27 -04:00
#include "rtabmap/gui/ProgressDialog.h"
2015-12-17 14:13:19 -05:00
#include "ParametersToolBox.h"
2013-12-11 00:12:44 +00:00
#include <pcl/io/pcd_io.h>
2015-11-22 18:08:32 -05:00
#include <pcl/io/ply_io.h>
2013-12-11 00:12:44 +00:00
#include <pcl/filters/voxel_grid.h>
#include <pcl/common/transforms.h>
2015-05-13 19:54:23 -04:00
#include <pcl/common/common.h>
2011-06-05 14:45:39 +00:00
2014-08-11 15:23:06 +00:00
namespace rtabmap {
2015-12-21 17:27:05 -05:00
DatabaseViewer :: DatabaseViewer ( const QString & ini , QWidget * parent ) :
2011-06-05 14:45:39 +00:00
QMainWindow ( parent ),
2015-10-16 14:54:47 -04:00
dbDriver_ ( 0 ),
2015-03-04 17:27:44 -05:00
savedMaximized_ ( false ),
2015-12-21 17:27:05 -05:00
firstCall_ ( true ),
iniFilePath_ ( ini )
2011-06-05 14:45:39 +00:00
{
pathDatabase_ = QDir :: homePath () + "/Documents/RTAB-Map" ; //use home directory by default
if ( ! UDirectory :: exists ( pathDatabase_ . toStdString ()))
{
pathDatabase_ = QDir :: homePath ();
}
2012-12-11 18:05:05 +00:00
ui_ = new Ui_DatabaseViewer ();
2011-06-05 14:45:39 +00:00
ui_ -> setupUi ( this );
2015-04-28 13:04:35 -04:00
ui_ -> buttonBox -> setVisible ( false );
connect ( ui_ -> buttonBox -> button ( QDialogButtonBox :: Close ), SIGNAL ( clicked ()), this , SLOT ( close ()));
2011-06-05 14:45:39 +00:00
2015-11-22 18:08:32 -05:00
ui_ -> comboBox_logger_level -> setVisible ( parent == 0 );
ui_ -> label_logger_level -> setVisible ( parent == 0 );
connect ( ui_ -> comboBox_logger_level , SIGNAL ( currentIndexChanged ( int )), this , SLOT ( updateLoggerLevel ()));
2015-12-21 17:27:05 -05:00
connect ( ui_ -> checkBox_verticalLayout , SIGNAL ( stateChanged ( int )), this , SLOT ( setupMainLayout ( int )));
2015-11-22 18:08:32 -05:00
2015-03-04 17:27:44 -05:00
QString title ( "RTAB-Map Database Viewer[*]" );
this -> setWindowTitle ( title );
2014-02-11 23:04:22 +00:00
ui_ -> dockWidget_constraints -> setVisible ( false );
ui_ -> dockWidget_graphView -> setVisible ( false );
2015-12-17 14:13:19 -05:00
ui_ -> dockWidget_guiparameters -> setVisible ( false );
ui_ -> dockWidget_coreparameters -> setVisible ( false );
2015-10-24 17:03:44 -04:00
ui_ -> dockWidget_info -> setVisible ( false );
2014-11-04 18:58:40 +00:00
ui_ -> dockWidget_stereoView -> setVisible ( false );
2015-03-04 17:27:44 -05:00
ui_ -> dockWidget_view3d -> setVisible ( false );
2016-09-20 11:19:34 -04:00
ui_ -> dockWidget_statistics -> setVisible ( false );
2015-03-04 17:27:44 -05:00
2016-05-05 18:25:26 -04:00
// Create cloud viewers
constraintsViewer_ = new CloudViewer ( ui_ -> dockWidgetContents );
cloudViewerA_ = new CloudViewer ( ui_ -> dockWidgetContents_3dviews );
cloudViewerB_ = new CloudViewer ( ui_ -> dockWidgetContents_3dviews );
stereoViewer_ = new CloudViewer ( ui_ -> dockWidgetContents_stereo );
constraintsViewer_ -> setObjectName ( "constraintsViewer" );
cloudViewerA_ -> setObjectName ( "cloudViewerA" );
cloudViewerB_ -> setObjectName ( "cloudViewerB" );
stereoViewer_ -> setObjectName ( "stereoViewer" );
ui_ -> layout_constraintsViewer -> addWidget ( constraintsViewer_ );
ui_ -> horizontalLayout_3dviews -> addWidget ( cloudViewerA_ , 1 );
ui_ -> horizontalLayout_3dviews -> addWidget ( cloudViewerB_ , 1 );
ui_ -> horizontalLayout_stereo -> addWidget ( stereoViewer_ , 1 );
constraintsViewer_ -> setCameraLockZ ( false );
constraintsViewer_ -> setCameraFree ();
2015-03-04 17:27:44 -05:00
2015-06-15 14:44:44 -04:00
ui_ -> graphicsView_stereo -> setAlpha ( 255 );
2015-12-17 14:13:19 -05:00
QSet < QString > ignoredGroups ;
ignoredGroups . insert ( "Rtabmap" );
ignoredGroups . insert ( "Mem" );
ignoredGroups . insert ( "Kp" );
ignoredGroups . insert ( "Odom" );
ignoredGroups . insert ( "OdomBow" );
ignoredGroups . insert ( "OdomFlow" );
ignoredGroups . insert ( "OdomMono" );
ignoredGroups . insert ( "VhEp" );
ignoredGroups . insert ( "StereoBM" );
ignoredGroups . insert ( "RGBD" );
ignoredGroups . insert ( "DbSqlite3" );
ignoredGroups . insert ( "Bayes" );
ui_ -> parameters_toolbox -> setupUi ( ignoredGroups );
2015-03-04 17:27:44 -05:00
this -> readSettings ();
2014-08-11 15:23:06 +00:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_constraints -> toggleViewAction ());
2014-02-11 23:04:22 +00:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_graphView -> toggleViewAction ());
2014-11-04 18:58:40 +00:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_stereoView -> toggleViewAction ());
2015-03-04 17:27:44 -05:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_view3d -> toggleViewAction ());
2015-12-17 14:13:19 -05:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_guiparameters -> toggleViewAction ());
ui_ -> menuView -> addAction ( ui_ -> dockWidget_coreparameters -> toggleViewAction ());
2015-10-24 17:03:44 -04:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_info -> toggleViewAction ());
2016-09-20 11:19:34 -04:00
ui_ -> menuView -> addAction ( ui_ -> dockWidget_statistics -> toggleViewAction ());
2014-02-11 23:04:22 +00:00
connect ( ui_ -> dockWidget_graphView -> toggleViewAction (), SIGNAL ( triggered ()), this , SLOT ( updateGraphView ()));
2015-12-17 14:13:19 -05:00
connect ( ui_ -> parameters_toolbox , SIGNAL ( parametersChanged ( const QStringList & )), this , SLOT ( notifyParametersChanged ( const QStringList & )));
2011-06-05 14:45:39 +00:00
connect ( ui_ -> actionQuit , SIGNAL ( triggered ()), this , SLOT ( close ()));
// connect actions with custom slots
2015-03-04 17:27:44 -05:00
ui_ -> actionSave_config -> setShortcut ( QKeySequence :: Save );
connect ( ui_ -> actionSave_config , SIGNAL ( triggered ()), this , SLOT ( writeSettings ()));
2011-06-05 14:45:39 +00:00
connect ( ui_ -> actionOpen_database , SIGNAL ( triggered ()), this , SLOT ( openDatabase ()));
2014-01-22 19:49:28 +00:00
connect ( ui_ -> actionExport , SIGNAL ( triggered ()), this , SLOT ( exportDatabase ()));
2014-02-11 23:04:22 +00:00
connect ( ui_ -> actionExtract_images , SIGNAL ( triggered ()), this , SLOT ( extractImages ()));
2011-06-05 14:45:39 +00:00
connect ( ui_ -> actionGenerate_graph_dot , SIGNAL ( triggered ()), this , SLOT ( generateGraph ()));
connect ( ui_ -> actionGenerate_local_graph_dot , SIGNAL ( triggered ()), this , SLOT ( generateLocalGraph ()));
2014-02-27 17:00:46 +00:00
connect ( ui_ -> actionGenerate_TORO_graph_graph , SIGNAL ( triggered ()), this , SLOT ( generateTOROGraph ()));
2015-09-02 17:51:43 -04:00
connect ( ui_ -> actionGenerate_g2o_graph_g2o , SIGNAL ( triggered ()), this , SLOT ( generateG2OGraph ()));
2014-02-11 23:04:22 +00:00
connect ( ui_ -> actionView_3D_map , SIGNAL ( triggered ()), this , SLOT ( view3DMap ()));
2015-11-22 18:08:32 -05:00
connect ( ui_ -> actionView_3D_laser_scans , SIGNAL ( triggered ()), this , SLOT ( view3DLaserScans ()));
2013-12-11 00:12:44 +00:00
connect ( ui_ -> actionGenerate_3D_map_pcd , SIGNAL ( triggered ()), this , SLOT ( generate3DMap ()));
2015-11-22 18:08:32 -05:00
connect ( ui_ -> actionExport_3D_laser_scans_ply_pcd , SIGNAL ( triggered ()), this , SLOT ( generate3DLaserScans ()));
2014-08-11 15:23:06 +00:00
connect ( ui_ -> actionDetect_more_loop_closures , SIGNAL ( triggered ()), this , SLOT ( detectMoreLoopClosures ()));
connect ( ui_ -> actionRefine_all_neighbor_links , SIGNAL ( triggered ()), this , SLOT ( refineAllNeighborLinks ()));
connect ( ui_ -> actionRefine_all_loop_closure_links , SIGNAL ( triggered ()), this , SLOT ( refineAllLoopClosureLinks ()));
2014-11-03 19:14:02 +00:00
connect ( ui_ -> actionVisual_Refine_all_neighbor_links , SIGNAL ( triggered ()), this , SLOT ( refineVisuallyAllNeighborLinks ()));
connect ( ui_ -> actionVisual_Refine_all_loop_closure_links , SIGNAL ( triggered ()), this , SLOT ( refineVisuallyAllLoopClosureLinks ()));
2016-08-25 23:47:01 -04:00
connect ( ui_ -> actionRegenerate_local_grid_maps , SIGNAL ( triggered ()), this , SLOT ( regenerateLocalMaps ()));
2016-08-30 21:15:55 -04:00
connect ( ui_ -> actionRegenerate_local_grid_maps_selected , SIGNAL ( triggered ()), this , SLOT ( regenerateCurrentLocalMaps ()));
2015-05-05 17:38:38 -04:00
connect ( ui_ -> actionReset_all_changes , SIGNAL ( triggered ()), this , SLOT ( resetAllChanges ()));
2014-08-11 15:23:06 +00:00
//ICP buttons
connect ( ui_ -> pushButton_refine , SIGNAL ( clicked ()), this , SLOT ( refineConstraint ()));
2014-11-03 19:14:02 +00:00
connect ( ui_ -> pushButton_refineVisually , SIGNAL ( clicked ()), this , SLOT ( refineConstraintVisually ()));
2014-08-11 15:23:06 +00:00
connect ( ui_ -> pushButton_add , SIGNAL ( clicked ()), this , SLOT ( addConstraint ()));
connect ( ui_ -> pushButton_reset , SIGNAL ( clicked ()), this , SLOT ( resetConstraint ()));
connect ( ui_ -> pushButton_reject , SIGNAL ( clicked ()), this , SLOT ( rejectConstraint ()));
ui_ -> pushButton_refine -> setEnabled ( false );
2014-11-03 19:14:02 +00:00
ui_ -> pushButton_refineVisually -> setEnabled ( false );
2014-08-11 15:23:06 +00:00
ui_ -> pushButton_add -> setEnabled ( false );
ui_ -> pushButton_reset -> setEnabled ( false );
ui_ -> pushButton_reject -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
2014-02-27 17:00:46 +00:00
ui_ -> actionGenerate_TORO_graph_graph -> setEnabled ( false );
2015-09-02 17:51:43 -04:00
ui_ -> actionGenerate_g2o_graph_g2o -> setEnabled ( false );
2014-02-27 17:00:46 +00:00
2011-06-05 14:45:39 +00:00
ui_ -> horizontalSlider_A -> setTracking ( false );
ui_ -> horizontalSlider_B -> setTracking ( false );
ui_ -> horizontalSlider_A -> setEnabled ( false );
ui_ -> horizontalSlider_B -> setEnabled ( false );
connect ( ui_ -> horizontalSlider_A , SIGNAL ( valueChanged ( int )), this , SLOT ( sliderAValueChanged ( int )));
connect ( ui_ -> horizontalSlider_B , SIGNAL ( valueChanged ( int )), this , SLOT ( sliderBValueChanged ( int )));
connect ( ui_ -> horizontalSlider_A , SIGNAL ( sliderMoved ( int )), this , SLOT ( sliderAMoved ( int )));
connect ( ui_ -> horizontalSlider_B , SIGNAL ( sliderMoved ( int )), this , SLOT ( sliderBMoved ( int )));
2014-02-11 23:04:22 +00:00
2016-02-15 19:35:24 -05:00
connect ( ui_ -> spinBox_mesh_angleTolerance , SIGNAL ( valueChanged ( int )), this , SLOT ( update3dView ()));
2016-09-03 18:59:43 -04:00
connect ( ui_ -> spinBox_mesh_minClusterSize , SIGNAL ( valueChanged ( int )), this , SLOT ( update3dView ()));
2016-02-15 19:35:24 -05:00
connect ( ui_ -> spinBox_mesh_fillDepthHoles , SIGNAL ( valueChanged ( int )), this , SLOT ( update3dView ()));
connect ( ui_ -> spinBox_mesh_depthError , SIGNAL ( valueChanged ( int )), this , SLOT ( update3dView ()));
connect ( ui_ -> checkBox_mesh_quad , SIGNAL ( toggled ( bool )), this , SLOT ( update3dView ()));
connect ( ui_ -> spinBox_mesh_triangleSize , SIGNAL ( valueChanged ( int )), this , SLOT ( update3dView ()));
2016-09-03 18:59:43 -04:00
connect ( ui_ -> checkBox_showCloud , SIGNAL ( toggled ( bool )), this , SLOT ( update3dView ()));
2016-02-15 19:35:24 -05:00
connect ( ui_ -> checkBox_showMesh , SIGNAL ( toggled ( bool )), this , SLOT ( update3dView ()));
2016-09-03 18:59:43 -04:00
connect ( ui_ -> checkBox_showScan , SIGNAL ( toggled ( bool )), this , SLOT ( update3dView ()));
connect ( ui_ -> checkBox_showMap , SIGNAL ( toggled ( bool )), this , SLOT ( update3dView ()));
connect ( ui_ -> checkBox_showGrid , SIGNAL ( toggled ( bool )), this , SLOT ( update3dView ()));
2016-02-15 19:35:24 -05:00
2014-02-11 23:04:22 +00:00
ui_ -> horizontalSlider_neighbors -> setTracking ( false );
ui_ -> horizontalSlider_loops -> setTracking ( false );
ui_ -> horizontalSlider_neighbors -> setEnabled ( false );
ui_ -> horizontalSlider_loops -> setEnabled ( false );
connect ( ui_ -> horizontalSlider_neighbors , SIGNAL ( valueChanged ( int )), this , SLOT ( sliderNeighborValueChanged ( int )));
connect ( ui_ -> horizontalSlider_loops , SIGNAL ( valueChanged ( int )), this , SLOT ( sliderLoopValueChanged ( int )));
connect ( ui_ -> horizontalSlider_neighbors , SIGNAL ( sliderMoved ( int )), this , SLOT ( sliderNeighborValueChanged ( int )));
connect ( ui_ -> horizontalSlider_loops , SIGNAL ( sliderMoved ( int )), this , SLOT ( sliderLoopValueChanged ( int )));
2014-12-14 16:42:10 -05:00
connect ( ui_ -> checkBox_showOptimized , SIGNAL ( stateChanged ( int )), this , SLOT ( updateConstraintView ()));
2015-10-07 16:54:36 -04:00
connect ( ui_ -> checkBox_show3Dclouds , SIGNAL ( stateChanged ( int )), this , SLOT ( updateConstraintView ()));
connect ( ui_ -> checkBox_show2DScans , SIGNAL ( stateChanged ( int )), this , SLOT ( updateConstraintView ()));
2015-04-23 22:01:41 -04:00
connect ( ui_ -> checkBox_show3DWords , SIGNAL ( stateChanged ( int )), this , SLOT ( updateConstraintView ()));
2014-12-14 16:42:10 -05:00
ui_ -> checkBox_showOptimized -> setEnabled ( false );
2014-02-11 23:04:22 +00:00
ui_ -> horizontalSlider_iterations -> setTracking ( false );
2015-03-04 17:27:44 -05:00
ui_ -> horizontalSlider_iterations -> setEnabled ( false );
ui_ -> spinBox_optimizationsFrom -> setEnabled ( false );
2014-02-11 23:04:22 +00:00
connect ( ui_ -> horizontalSlider_iterations , SIGNAL ( valueChanged ( int )), this , SLOT ( sliderIterationsValueChanged ( int )));
connect ( ui_ -> horizontalSlider_iterations , SIGNAL ( sliderMoved ( int )), this , SLOT ( sliderIterationsValueChanged ( int )));
2015-03-12 17:00:56 -04:00
connect ( ui_ -> spinBox_optimizationsFrom , SIGNAL ( editingFinished ()), this , SLOT ( updateGraphView ()));
2015-05-23 13:36:36 -04:00
connect ( ui_ -> checkBox_spanAllMaps , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGraphView ()));
2015-04-01 14:34:43 -04:00
connect ( ui_ -> checkBox_ignorePoseCorrection , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGraphView ()));
2015-11-22 18:08:32 -05:00
connect ( ui_ -> checkBox_ignorePoseCorrection , SIGNAL ( stateChanged ( int )), this , SLOT ( updateConstraintView ()));
2015-05-23 13:36:36 -04:00
connect ( ui_ -> checkBox_ignoreGlobalLoop , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGraphView ()));
connect ( ui_ -> checkBox_ignoreLocalLoopSpace , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGraphView ()));
connect ( ui_ -> checkBox_ignoreLocalLoopTime , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGraphView ()));
connect ( ui_ -> checkBox_ignoreUserLoop , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGraphView ()));
2015-03-16 18:22:54 -04:00
connect ( ui_ -> spinBox_optimizationDepth , SIGNAL ( editingFinished ()), this , SLOT ( updateGraphView ()));
2015-09-28 15:30:01 -04:00
connect ( ui_ -> checkBox_gridErode , SIGNAL ( stateChanged ( int )), this , SLOT ( updateGrid ()));
2016-09-07 12:15:44 -04:00
connect ( ui_ -> doubleSpinBox_gainCompensationRadius , SIGNAL ( valueChanged ( double )), this , SLOT ( updateConstraintView ()));
2015-03-20 14:03:09 -04:00
connect ( ui_ -> groupBox_posefiltering , SIGNAL ( clicked ( bool )), this , SLOT ( updateGraphView ()));
connect ( ui_ -> doubleSpinBox_posefilteringRadius , SIGNAL ( editingFinished ()), this , SLOT ( updateGraphView ()));
connect ( ui_ -> doubleSpinBox_posefilteringAngle , SIGNAL ( editingFinished ()), this , SLOT ( updateGraphView ()));
2015-03-12 17:00:56 -04:00
2015-03-04 17:27:44 -05:00
connect ( ui_ -> doubleSpinBox_gridCellSize , SIGNAL ( editingFinished ()), this , SLOT ( updateGrid ()));
2014-02-11 23:04:22 +00:00
2015-06-15 14:44:44 -04:00
ui_ -> label_stereo_inliers_name -> setStyleSheet ( "QLabel {color : blue; }" );
ui_ -> label_stereo_flowOutliers_name -> setStyleSheet ( "QLabel {color : red; }" );
ui_ -> label_stereo_slopeOutliers_name -> setStyleSheet ( "QLabel {color : yellow; }" );
ui_ -> label_stereo_disparityOutliers_name -> setStyleSheet ( "QLabel {color : magenta; }" );
2015-03-12 17:00:56 -04:00
2015-03-04 17:27:44 -05:00
// connect configuration changed
connect ( ui_ -> graphViewer , SIGNAL ( configChanged ()), this , SLOT ( configModified ()));
//connect(ui_->graphicsView_A, SIGNAL(configChanged()), this, SLOT(configModified()));
//connect(ui_->graphicsView_B, SIGNAL(configChanged()), this, SLOT(configModified()));
2015-11-22 18:08:32 -05:00
connect ( ui_ -> comboBox_logger_level , SIGNAL ( currentIndexChanged ( int )), this , SLOT ( configModified ()));
2015-12-21 17:27:05 -05:00
connect ( ui_ -> checkBox_verticalLayout , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
2015-03-04 17:27:44 -05:00
// Graph view
2015-05-23 13:36:36 -04:00
connect ( ui_ -> checkBox_spanAllMaps , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
connect ( ui_ -> checkBox_ignorePoseCorrection , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
connect ( ui_ -> checkBox_ignoreGlobalLoop , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
connect ( ui_ -> checkBox_ignoreLocalLoopSpace , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
connect ( ui_ -> checkBox_ignoreLocalLoopTime , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
connect ( ui_ -> checkBox_ignoreUserLoop , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
2015-03-16 18:22:54 -04:00
connect ( ui_ -> spinBox_optimizationDepth , SIGNAL ( valueChanged ( int )), this , SLOT ( configModified ()));
2015-03-20 14:03:09 -04:00
connect ( ui_ -> checkBox_gridErode , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
2016-09-07 12:15:44 -04:00
connect ( ui_ -> doubleSpinBox_gainCompensationRadius , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
2015-03-04 17:27:44 -05:00
connect ( ui_ -> doubleSpinBox_gridCellSize , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
2015-03-12 17:00:56 -04:00
connect ( ui_ -> groupBox_posefiltering , SIGNAL ( clicked ( bool )), this , SLOT ( configModified ()));
connect ( ui_ -> doubleSpinBox_posefilteringRadius , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
connect ( ui_ -> doubleSpinBox_posefilteringAngle , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
2015-12-17 14:13:19 -05:00
2015-03-04 17:27:44 -05:00
connect ( ui_ -> spinBox_icp_decimation , SIGNAL ( valueChanged ( int )), this , SLOT ( configModified ()));
connect ( ui_ -> doubleSpinBox_icp_maxDepth , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
2016-04-12 15:14:04 -04:00
connect ( ui_ -> doubleSpinBox_icp_minDepth , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
2016-08-21 19:33:01 -04:00
connect ( ui_ -> checkBox_icp_from_depth , SIGNAL ( stateChanged ( int )), this , SLOT ( configModified ()));
2015-12-17 14:13:19 -05:00
2015-03-04 17:27:44 -05:00
connect ( ui_ -> doubleSpinBox_detectMore_radius , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
connect ( ui_ -> doubleSpinBox_detectMore_angle , SIGNAL ( valueChanged ( double )), this , SLOT ( configModified ()));
connect ( ui_ -> spinBox_detectMore_iterations , SIGNAL ( valueChanged ( int )), this , SLOT ( configModified ()));
2015-12-11 17:44:15 -05:00
2015-03-04 17:27:44 -05:00
// dockwidget
QList < QDockWidget *> dockWidgets = this -> findChildren < QDockWidget *> ();
for ( int i = 0 ; i < dockWidgets . size (); ++ i )
{
connect ( dockWidgets [ i ], SIGNAL ( dockLocationChanged ( Qt :: DockWidgetArea )), this , SLOT ( configModified ()));
connect ( dockWidgets [ i ] -> toggleViewAction (), SIGNAL ( toggled ( bool )), this , SLOT ( configModified ()));
}
ui_ -> dockWidget_constraints -> installEventFilter ( this );
ui_ -> dockWidget_graphView -> installEventFilter ( this );
ui_ -> dockWidget_stereoView -> installEventFilter ( this );
ui_ -> dockWidget_view3d -> installEventFilter ( this );
2015-12-17 14:13:19 -05:00
ui_ -> dockWidget_guiparameters -> installEventFilter ( this );
ui_ -> dockWidget_coreparameters -> installEventFilter ( this );
2015-10-24 17:03:44 -04:00
ui_ -> dockWidget_info -> installEventFilter ( this );
2016-09-20 11:19:34 -04:00
ui_ -> dockWidget_statistics -> installEventFilter ( this );
2011-06-05 14:45:39 +00:00
}
2012-12-11 18:05:05 +00:00
DatabaseViewer ::~ DatabaseViewer ()
2011-06-05 14:45:39 +00:00
{
delete ui_ ;
2015-10-16 14:54:47 -04:00
if ( dbDriver_ )
2011-06-05 14:45:39 +00:00
{
2015-10-16 14:54:47 -04:00
delete dbDriver_ ;
2011-06-05 14:45:39 +00:00
}
}
2015-12-21 17:27:05 -05:00
void DatabaseViewer :: setupMainLayout ( int vertical )
{
if ( vertical )
{
qobject_cast < QHBoxLayout *> ( ui_ -> horizontalLayout_imageViews -> layout ()) -> setDirection ( QBoxLayout :: TopToBottom );
2016-02-15 19:35:24 -05:00
qobject_cast < QHBoxLayout *> ( ui_ -> horizontalLayout_3dviews -> layout ()) -> setDirection ( QBoxLayout :: TopToBottom );
2015-12-21 17:27:05 -05:00
}
else if ( ! vertical )
{
qobject_cast < QHBoxLayout *> ( ui_ -> horizontalLayout_imageViews -> layout ()) -> setDirection ( QBoxLayout :: LeftToRight );
2016-02-15 19:35:24 -05:00
qobject_cast < QHBoxLayout *> ( ui_ -> horizontalLayout_3dviews -> layout ()) -> setDirection ( QBoxLayout :: LeftToRight );
2015-12-21 17:27:05 -05:00
}
}
2015-04-28 13:04:35 -04:00
void DatabaseViewer :: showCloseButton ( bool visible )
{
ui_ -> buttonBox -> setVisible ( visible );
}
2015-03-04 17:27:44 -05:00
void DatabaseViewer :: configModified ()
{
this -> setWindowModified ( true );
}
QString DatabaseViewer :: getIniFilePath () const
{
2015-12-21 17:27:05 -05:00
if ( ! iniFilePath_ . isEmpty ())
{
return iniFilePath_ ;
}
2015-03-04 17:27:44 -05:00
QString privatePath = QDir :: homePath () + "/.rtabmap" ;
if ( ! QDir ( privatePath ). exists ())
{
QDir :: home (). mkdir ( ".rtabmap" );
}
2016-01-07 19:39:21 -05:00
return privatePath + "/rtabmap.ini" ;
2015-03-04 17:27:44 -05:00
}
void DatabaseViewer :: readSettings ()
{
QString path = getIniFilePath ();
QSettings settings ( path , QSettings :: IniFormat );
settings . beginGroup ( "DatabaseViewer" );
//load window state / geometry
QByteArray bytes ;
bytes = settings . value ( "geometry" , QByteArray ()). toByteArray ();
if ( ! bytes . isEmpty ())
{
this -> restoreGeometry ( bytes );
}
bytes = settings . value ( "state" , QByteArray ()). toByteArray ();
if ( ! bytes . isEmpty ())
{
this -> restoreState ( bytes );
}
savedMaximized_ = settings . value ( "maximized" , false ). toBool ();
2015-11-22 18:08:32 -05:00
ui_ -> comboBox_logger_level -> setCurrentIndex ( settings . value ( "loggerLevel" , ui_ -> comboBox_logger_level -> currentIndex ()). toInt ());
2015-12-21 17:27:05 -05:00
ui_ -> checkBox_verticalLayout -> setChecked ( settings . value ( "verticalLayout" , ui_ -> checkBox_verticalLayout -> isChecked ()). toBool ());
2015-11-22 18:08:32 -05:00
2015-03-04 17:27:44 -05:00
// GraphViewer settings
2015-03-12 17:00:56 -04:00
ui_ -> graphViewer -> loadSettings ( settings , "GraphView" );
settings . beginGroup ( "optimization" );
2015-05-23 13:36:36 -04:00
ui_ -> checkBox_spanAllMaps -> setChecked ( settings . value ( "spanToAllMaps" , ui_ -> checkBox_spanAllMaps -> isChecked ()). toBool ());
2015-04-01 14:34:43 -04:00
ui_ -> checkBox_ignorePoseCorrection -> setChecked ( settings . value ( "ignorePoseCorrection" , ui_ -> checkBox_ignorePoseCorrection -> isChecked ()). toBool ());
2015-05-23 13:36:36 -04:00
ui_ -> checkBox_ignoreGlobalLoop -> setChecked ( settings . value ( "ignoreGlobalLoop" , ui_ -> checkBox_ignoreGlobalLoop -> isChecked ()). toBool ());
ui_ -> checkBox_ignoreLocalLoopSpace -> setChecked ( settings . value ( "ignoreLocalLoopSpace" , ui_ -> checkBox_ignoreLocalLoopSpace -> isChecked ()). toBool ());
ui_ -> checkBox_ignoreLocalLoopTime -> setChecked ( settings . value ( "ignoreLocalLoopTime" , ui_ -> checkBox_ignoreLocalLoopTime -> isChecked ()). toBool ());
ui_ -> checkBox_ignoreUserLoop -> setChecked ( settings . value ( "ignoreUserLoop" , ui_ -> checkBox_ignoreUserLoop -> isChecked ()). toBool ());
2015-03-16 18:22:54 -04:00
ui_ -> spinBox_optimizationDepth -> setValue ( settings . value ( "depth" , ui_ -> spinBox_optimizationDepth -> value ()). toInt ());
2015-03-20 14:03:09 -04:00
ui_ -> checkBox_gridErode -> setChecked ( settings . value ( "erode" , ui_ -> checkBox_gridErode -> isChecked ()). toBool ());
2016-09-07 12:15:44 -04:00
ui_ -> doubleSpinBox_gainCompensationRadius -> setValue ( settings . value ( "gainCompensationRadius" , ui_ -> doubleSpinBox_gainCompensationRadius -> value ()). toDouble ());
2015-03-12 17:00:56 -04:00
settings . endGroup ();
settings . beginGroup ( "grid" );
2015-03-04 17:27:44 -05:00
ui_ -> doubleSpinBox_gridCellSize -> setValue ( settings . value ( "gridCellSize" , ui_ -> doubleSpinBox_gridCellSize -> value ()). toDouble ());
2015-03-12 17:00:56 -04:00
ui_ -> groupBox_posefiltering -> setChecked ( settings . value ( "poseFiltering" , ui_ -> groupBox_posefiltering -> isChecked ()). toBool ());
ui_ -> doubleSpinBox_posefilteringRadius -> setValue ( settings . value ( "poseFilteringRadius" , ui_ -> doubleSpinBox_posefilteringRadius -> value ()). toDouble ());
ui_ -> doubleSpinBox_posefilteringAngle -> setValue ( settings . value ( "poseFilteringAngle" , ui_ -> doubleSpinBox_posefilteringAngle -> value ()). toDouble ());
2015-03-04 17:27:44 -05:00
settings . endGroup ();
2016-02-15 19:35:24 -05:00
settings . beginGroup ( "mesh" );
ui_ -> checkBox_mesh_quad -> setChecked ( settings . value ( "quad" , ui_ -> checkBox_mesh_quad -> isChecked ()). toBool ());
ui_ -> spinBox_mesh_angleTolerance -> setValue ( settings . value ( "angleTolerance" , ui_ -> spinBox_mesh_angleTolerance -> value ()). toInt ());
2016-09-03 18:59:43 -04:00
ui_ -> spinBox_mesh_minClusterSize -> setValue ( settings . value ( "minClusterSize" , ui_ -> spinBox_mesh_minClusterSize -> value ()). toInt ());
2016-02-15 19:35:24 -05:00
ui_ -> spinBox_mesh_fillDepthHoles -> setValue ( settings . value ( "fillDepthHolesSize" , ui_ -> spinBox_mesh_fillDepthHoles -> value ()). toInt ());
ui_ -> spinBox_mesh_depthError -> setValue ( settings . value ( "fillDepthHolesError" , ui_ -> spinBox_mesh_depthError -> value ()). toInt ());
ui_ -> spinBox_mesh_triangleSize -> setValue ( settings . value ( "triangleSize" , ui_ -> spinBox_mesh_triangleSize -> value ()). toInt ());
settings . endGroup ();
2015-03-04 17:27:44 -05:00
// ImageViews
//ui_->graphicsView_A->loadSettings(settings, "ImageViewA");
//ui_->graphicsView_B->loadSettings(settings, "ImageViewB");
// ICP parameters
settings . beginGroup ( "icp" );
ui_ -> spinBox_icp_decimation -> setValue ( settings . value ( "decimation" , ui_ -> spinBox_icp_decimation -> value ()). toInt ());
ui_ -> doubleSpinBox_icp_maxDepth -> setValue ( settings . value ( "maxDepth" , ui_ -> doubleSpinBox_icp_maxDepth -> value ()). toDouble ());
2016-04-12 15:14:04 -04:00
ui_ -> doubleSpinBox_icp_minDepth -> setValue ( settings . value ( "minDepth" , ui_ -> doubleSpinBox_icp_minDepth -> value ()). toDouble ());
2016-08-21 19:33:01 -04:00
ui_ -> checkBox_icp_from_depth -> setChecked ( settings . value ( "icpFromDepth" , ui_ -> checkBox_icp_from_depth -> isChecked ()). toBool ());
2015-03-04 17:27:44 -05:00
settings . endGroup ();
// Visual parameters
settings . beginGroup ( "visual" );
ui_ -> doubleSpinBox_detectMore_radius -> setValue ( settings . value ( "detectMoreRadius" , ui_ -> doubleSpinBox_detectMore_radius -> value ()). toDouble ());
ui_ -> doubleSpinBox_detectMore_angle -> setValue ( settings . value ( "detectMoreAngle" , ui_ -> doubleSpinBox_detectMore_angle -> value ()). toDouble ());
ui_ -> spinBox_detectMore_iterations -> setValue ( settings . value ( "detectMoreIterations" , ui_ -> spinBox_detectMore_iterations -> value ()). toInt ());
settings . endGroup ();
settings . endGroup (); // DatabaseViewer
2015-12-17 14:13:19 -05:00
ParametersMap parameters ;
Parameters :: readINI ( path . toStdString (), parameters );
for ( ParametersMap :: iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
ui_ -> parameters_toolbox -> updateParameter ( iter -> first , iter -> second );
}
2015-03-04 17:27:44 -05:00
}
void DatabaseViewer :: writeSettings ()
{
QString path = getIniFilePath ();
QSettings settings ( path , QSettings :: IniFormat );
settings . beginGroup ( "DatabaseViewer" );
//save window state / geometry
if ( ! this -> isMaximized ())
{
settings . setValue ( "geometry" , this -> saveGeometry ());
}
settings . setValue ( "state" , this -> saveState ());
settings . setValue ( "maximized" , this -> isMaximized ());
savedMaximized_ = this -> isMaximized ();
2015-11-22 18:08:32 -05:00
settings . setValue ( "loggerLevel" , ui_ -> comboBox_logger_level -> currentIndex ());
2015-12-21 17:27:05 -05:00
settings . setValue ( "verticalLayout" , ui_ -> checkBox_verticalLayout -> isChecked ());
2015-11-22 18:08:32 -05:00
2015-03-04 17:27:44 -05:00
// save GraphViewer settings
2015-03-12 17:00:56 -04:00
ui_ -> graphViewer -> saveSettings ( settings , "GraphView" );
// save optimization settings
settings . beginGroup ( "optimization" );
2015-12-17 14:13:19 -05:00
//settings.setValue("iterations", ui_->spinBox_iterations->value());
2015-05-23 13:36:36 -04:00
settings . setValue ( "spanToAllMaps" , ui_ -> checkBox_spanAllMaps -> isChecked ());
2015-12-17 14:13:19 -05:00
//settings.setValue("robust", ui_->checkBox_robust->isChecked());
2015-04-01 14:34:43 -04:00
settings . setValue ( "ignorePoseCorrection" , ui_ -> checkBox_ignorePoseCorrection -> isChecked ());
2015-05-23 13:36:36 -04:00
settings . setValue ( "ignoreGlobalLoop" , ui_ -> checkBox_ignoreGlobalLoop -> isChecked ());
settings . setValue ( "ignoreLocalLoopSpace" , ui_ -> checkBox_ignoreLocalLoopSpace -> isChecked ());
settings . setValue ( "ignoreLocalLoopTime" , ui_ -> checkBox_ignoreLocalLoopTime -> isChecked ());
settings . setValue ( "ignoreUserLoop" , ui_ -> checkBox_ignoreUserLoop -> isChecked ());
2015-12-17 14:13:19 -05:00
//settings.setValue("strategy", ui_->comboBox_graphOptimizer->currentIndex());
//settings.setValue("slam2d", ui_->checkBox_2dslam->isChecked());
2015-03-16 18:22:54 -04:00
settings . setValue ( "depth" , ui_ -> spinBox_optimizationDepth -> value ());
2015-03-20 14:03:09 -04:00
settings . setValue ( "erode" , ui_ -> checkBox_gridErode -> isChecked ());
2016-09-07 12:15:44 -04:00
settings . setValue ( "gainCompensationRadius" , ui_ -> doubleSpinBox_gainCompensationRadius -> value ());
2015-03-12 17:00:56 -04:00
settings . endGroup ();
// save Grid settings
settings . beginGroup ( "grid" );
2015-03-04 17:27:44 -05:00
settings . setValue ( "gridCellSize" , ui_ -> doubleSpinBox_gridCellSize -> value ());
2015-03-12 17:00:56 -04:00
settings . setValue ( "poseFiltering" , ui_ -> groupBox_posefiltering -> isChecked ());
settings . setValue ( "poseFilteringRadius" , ui_ -> doubleSpinBox_posefilteringRadius -> value ());
settings . setValue ( "poseFilteringAngle" , ui_ -> doubleSpinBox_posefilteringAngle -> value ());
2015-03-04 17:27:44 -05:00
settings . endGroup ();
2016-02-15 19:35:24 -05:00
settings . beginGroup ( "mesh" );
settings . setValue ( "quad" , ui_ -> checkBox_mesh_quad -> isChecked ());
settings . setValue ( "angleTolerance" , ui_ -> spinBox_mesh_angleTolerance -> value ());
2016-09-03 18:59:43 -04:00
settings . setValue ( "minClusterSize" , ui_ -> spinBox_mesh_minClusterSize -> value ());
2016-02-15 19:35:24 -05:00
settings . setValue ( "fillDepthHolesSize" , ui_ -> spinBox_mesh_fillDepthHoles -> value ());
settings . setValue ( "fillDepthHolesError" , ui_ -> spinBox_mesh_depthError -> value ());
settings . setValue ( "triangleSize" , ui_ -> spinBox_mesh_triangleSize -> value ());
settings . endGroup ();
2015-03-04 17:27:44 -05:00
// ImageViews
//ui_->graphicsView_A->saveSettings(settings, "ImageViewA");
//ui_->graphicsView_B->saveSettings(settings, "ImageViewB");
// save ICP parameters
settings . beginGroup ( "icp" );
settings . setValue ( "decimation" , ui_ -> spinBox_icp_decimation -> value ());
settings . setValue ( "maxDepth" , ui_ -> doubleSpinBox_icp_maxDepth -> value ());
2016-04-12 15:14:04 -04:00
settings . setValue ( "minDepth" , ui_ -> doubleSpinBox_icp_minDepth -> value ());
2016-08-21 19:33:01 -04:00
settings . setValue ( "icpFromDepth" , ui_ -> checkBox_icp_from_depth -> isChecked ());
2015-03-04 17:27:44 -05:00
settings . endGroup ();
2015-12-17 14:13:19 -05:00
2015-03-04 17:27:44 -05:00
// save Visual parameters
settings . beginGroup ( "visual" );
settings . setValue ( "detectMoreRadius" , ui_ -> doubleSpinBox_detectMore_radius -> value ());
settings . setValue ( "detectMoreAngle" , ui_ -> doubleSpinBox_detectMore_angle -> value ());
settings . setValue ( "detectMoreIterations" , ui_ -> spinBox_detectMore_iterations -> value ());
settings . endGroup ();
settings . endGroup (); // DatabaseViewer
2016-06-13 17:34:17 -04:00
ParametersMap parameters = ui_ -> parameters_toolbox -> getParameters ();
for ( ParametersMap :: iterator iter = parameters . begin (); iter != parameters . end ();)
{
if ( ! ui_ -> parameters_toolbox -> getParameterWidget ( iter -> first . c_str ()))
{
parameters . erase ( iter ++ );
}
else
{
++ iter ;
}
}
2015-12-17 14:13:19 -05:00
Parameters :: writeINI ( path . toStdString (), parameters );
2015-03-04 17:27:44 -05:00
this -> setWindowModified ( false );
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: openDatabase ()
2011-06-05 14:45:39 +00:00
{
2012-12-11 18:05:05 +00:00
QString path = QFileDialog :: getOpenFileName ( this , tr ( "Select file" ), pathDatabase_ , tr ( "Databases (*.db)" ));
if ( ! path . isEmpty ())
2011-06-05 14:45:39 +00:00
{
2012-12-11 18:05:05 +00:00
openDatabase ( path );
2011-06-05 14:45:39 +00:00
}
}
2012-12-11 18:05:05 +00:00
bool DatabaseViewer :: openDatabase ( const QString & path )
{
UDEBUG ( "Open database \" %s \" " , path . toStdString (). c_str ());
if ( QFile :: exists ( path ))
{
2015-10-16 14:54:47 -04:00
if ( dbDriver_ )
2012-12-11 18:05:05 +00:00
{
2015-10-16 14:54:47 -04:00
delete dbDriver_ ;
dbDriver_ = 0 ;
2012-12-11 18:05:05 +00:00
ids_ . clear ();
2014-02-28 22:11:36 +00:00
idToIndex_ . clear ();
neighborLinks_ . clear ();
loopLinks_ . clear ();
graphes_ . clear ();
2015-05-23 13:36:36 -04:00
graphLinks_ . clear ();
2014-02-28 22:11:36 +00:00
poses_ . clear ();
2016-01-07 19:39:21 -05:00
groundTruthPoses_ . clear ();
2015-05-22 15:06:05 -04:00
mapIds_ . clear ();
2014-02-28 22:11:36 +00:00
links_ . clear ();
2014-08-11 15:23:06 +00:00
linksAdded_ . clear ();
linksRefined_ . clear ();
linksRemoved_ . clear ();
2015-03-04 17:27:44 -05:00
localMaps_ . clear ();
2016-08-25 23:47:01 -04:00
generatedLocalMaps_ . clear ();
generatedLocalMapsInfo_ . clear ();
2015-09-02 17:51:43 -04:00
ui_ -> graphViewer -> clearAll ();
2014-02-28 22:11:36 +00:00
ui_ -> actionGenerate_TORO_graph_graph -> setEnabled ( false );
2015-09-02 17:51:43 -04:00
ui_ -> actionGenerate_g2o_graph_g2o -> setEnabled ( false );
2014-12-14 16:42:10 -05:00
ui_ -> checkBox_showOptimized -> setEnabled ( false );
2015-06-24 20:20:27 -04:00
databaseFileName_ . clear ();
2012-12-11 18:05:05 +00:00
}
std :: string driverType = "sqlite3" ;
2015-10-16 14:54:47 -04:00
dbDriver_ = DBDriver :: create ();
2012-12-11 18:05:05 +00:00
2015-10-16 14:54:47 -04:00
if ( ! dbDriver_ -> openConnection ( path . toStdString ()))
2012-12-11 18:05:05 +00:00
{
QMessageBox :: warning ( this , "Database error" , tr ( "Can't open database \" %1 \" " ). arg ( path ));
}
else
{
pathDatabase_ = UDirectory :: getDir ( path . toStdString ()). c_str ();
2015-06-24 20:20:27 -04:00
databaseFileName_ = UFile :: getName ( path . toStdString ());
2016-06-13 17:34:17 -04:00
// look if there are saved parameters
ParametersMap parameters = dbDriver_ -> getLastParameters ();
if ( parameters . size ())
{
const ParametersMap & currentParameters = ui_ -> parameters_toolbox -> getParameters ();
ParametersMap differentParameters ;
for ( ParametersMap :: iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
ParametersMap :: const_iterator jter = currentParameters . find ( iter -> first );
if ( jter != currentParameters . end () &&
ui_ -> parameters_toolbox -> getParameterWidget ( QString ( iter -> first . c_str ())) != 0 &&
iter -> second . compare ( jter -> second ) != 0 &&
iter -> first . compare ( Parameters :: kRtabmapWorkingDirectory ()) != 0 )
{
2016-08-26 12:34:19 -04:00
bool different = true ;
if ( Parameters :: getType ( iter -> first ). compare ( "double" ) == 0 ||
Parameters :: getType ( iter -> first ). compare ( "float" ) == 0 )
{
if ( uStr2Double ( iter -> second ) == uStr2Double ( jter -> second ))
{
different = false ;
}
}
if ( different )
{
differentParameters . insert ( * iter );
QString msg = tr ( "Parameter \" %1 \" : database= \" %2 \" Preferences= \" %3 \" " )
. arg ( iter -> first . c_str ())
. arg ( iter -> second . c_str ())
. arg ( jter -> second . c_str ());
UWARN ( msg . toStdString (). c_str ());
}
2016-06-13 17:34:17 -04:00
}
}
if ( differentParameters . size ())
{
int r = QMessageBox :: question ( this ,
tr ( "Update parameters..." ),
tr ( "The database is using %1 different parameter(s) than "
"those currently set in Core parameters panel. Do you want "
"to use database's parameters?" ). arg ( differentParameters . size ()),
QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: Yes );
if ( r == QMessageBox :: Yes )
{
2016-08-26 12:34:19 -04:00
QStringList str ;
2016-06-13 17:34:17 -04:00
for ( rtabmap :: ParametersMap :: const_iterator iter = differentParameters . begin (); iter != differentParameters . end (); ++ iter )
{
ui_ -> parameters_toolbox -> updateParameter ( iter -> first , iter -> second );
2016-08-26 12:34:19 -04:00
str . push_back ( iter -> first . c_str ());
2016-06-13 17:34:17 -04:00
}
2016-08-26 12:34:19 -04:00
notifyParametersChanged ( str );
2016-06-13 17:34:17 -04:00
}
}
}
2012-12-11 18:05:05 +00:00
updateIds ();
return true ;
}
}
else
{
QMessageBox :: warning ( this , "Database error" , tr ( "Database \" %1 \" does not exist." ). arg ( path ));
}
return false ;
}
2014-09-21 21:14:17 +00:00
void DatabaseViewer :: closeEvent ( QCloseEvent * event )
{
2015-03-04 17:27:44 -05:00
//write settings before quit?
bool save = false ;
if ( this -> isWindowModified ())
{
QMessageBox :: Button b = QMessageBox :: question ( this ,
tr ( "Database Viewer" ),
tr ( "There are unsaved changed settings. Save them?" ),
QMessageBox :: Save | QMessageBox :: Cancel | QMessageBox :: Discard );
if ( b == QMessageBox :: Save )
{
save = true ;
}
else if ( b != QMessageBox :: Discard )
{
event -> ignore ();
return ;
}
}
if ( save )
{
writeSettings ();
}
2016-08-25 23:47:01 -04:00
event -> accept ();
2014-09-21 21:14:17 +00:00
if ( linksAdded_ . size () || linksRefined_ . size () || linksRemoved_ . size ())
{
QMessageBox :: StandardButton button = QMessageBox :: question ( this ,
tr ( "Links modified" ),
tr ( "Some links are modified (%1 added, %2 refined, %3 removed), do you want to save them?" )
. arg ( linksAdded_ . size ()). arg ( linksRefined_ . size ()). arg ( linksRemoved_ . size ()),
QMessageBox :: Cancel | QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: Cancel );
if ( button == QMessageBox :: Yes )
{
// Added links
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = linksAdded_ . begin (); iter != linksAdded_ . end (); ++ iter )
{
2015-02-06 13:52:02 -05:00
std :: multimap < int , rtabmap :: Link >:: iterator refinedIter = rtabmap :: graph :: findLink ( linksRefined_ , iter -> second . from (), iter -> second . to ());
2014-09-21 21:14:17 +00:00
if ( refinedIter != linksRefined_ . end ())
{
2015-10-16 14:54:47 -04:00
dbDriver_ -> addLink ( refinedIter -> second );
dbDriver_ -> addLink ( refinedIter -> second . inverse ());
2014-09-21 21:14:17 +00:00
}
else
{
2015-10-16 14:54:47 -04:00
dbDriver_ -> addLink ( iter -> second );
dbDriver_ -> addLink ( iter -> second . inverse ());
2014-09-21 21:14:17 +00:00
}
}
//Refined links
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = linksRefined_ . begin (); iter != linksRefined_ . end (); ++ iter )
{
if ( ! containsLink ( linksAdded_ , iter -> second . from (), iter -> second . to ()))
{
2015-10-16 14:54:47 -04:00
dbDriver_ -> updateLink ( iter -> second );
dbDriver_ -> updateLink ( iter -> second . inverse ());
2014-09-21 21:14:17 +00:00
}
}
// Rejected links
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = linksRemoved_ . begin (); iter != linksRemoved_ . end (); ++ iter )
{
2015-10-16 14:54:47 -04:00
dbDriver_ -> removeLink ( iter -> second . to (), iter -> second . from ());
dbDriver_ -> removeLink ( iter -> second . from (), iter -> second . to ());
2014-09-21 21:14:17 +00:00
}
2016-08-25 23:47:01 -04:00
linksAdded_ . clear ();
linksRefined_ . clear ();
linksRemoved_ . clear ();
2014-09-21 21:14:17 +00:00
}
2016-08-25 23:47:01 -04:00
if ( button != QMessageBox :: Yes && button != QMessageBox :: No )
2014-09-21 21:14:17 +00:00
{
event -> ignore ();
}
}
2016-08-25 23:47:01 -04:00
if ( event -> isAccepted () && generatedLocalMaps_ . size ())
2014-09-21 21:14:17 +00:00
{
2016-08-25 23:47:01 -04:00
QMessageBox :: StandardButton button = QMessageBox :: question ( this ,
tr ( "Local occupancy grid maps modified" ),
tr ( "%1 occupancy grid maps are modified, do you want to "
"save them? This will overwrite occupancy grids saved in the database." )
. arg ( generatedLocalMaps_ . size ()),
QMessageBox :: Cancel | QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: Cancel );
if ( button == QMessageBox :: Yes )
{
// Rejected links
UASSERT ( generatedLocalMaps_ . size () == generatedLocalMapsInfo_ . size ());
std :: map < int , std :: pair < cv :: Mat , cv :: Mat > >:: iterator mapIter = generatedLocalMaps_ . begin ();
std :: map < int , std :: pair < float , cv :: Point3f > >:: iterator infoIter = generatedLocalMapsInfo_ . begin ();
for (; mapIter != generatedLocalMaps_ . end (); ++ mapIter , ++ infoIter )
{
UASSERT ( mapIter -> first == infoIter -> first );
dbDriver_ -> updateOccupancyGrid (
mapIter -> first ,
mapIter -> second . first ,
mapIter -> second . second ,
infoIter -> second . first ,
infoIter -> second . second );
}
generatedLocalMaps_ . clear ();
generatedLocalMapsInfo_ . clear ();
localMaps_ . clear ();
}
if ( button != QMessageBox :: Yes && button != QMessageBox :: No )
{
event -> ignore ();
}
2014-09-21 21:14:17 +00:00
}
2014-10-28 01:20:57 +00:00
if ( event -> isAccepted ())
{
2016-09-20 11:19:34 -04:00
ui_ -> toolBox_statistics -> closeFigures ();
2015-10-16 14:54:47 -04:00
if ( dbDriver_ )
2014-10-28 01:20:57 +00:00
{
2015-10-16 14:54:47 -04:00
delete dbDriver_ ;
dbDriver_ = 0 ;
2014-10-28 01:20:57 +00:00
}
}
2014-09-21 21:14:17 +00:00
}
2014-10-28 19:57:21 +00:00
void DatabaseViewer :: showEvent ( QShowEvent * anEvent )
{
2015-03-04 17:27:44 -05:00
this -> setWindowModified ( false );
2015-05-05 17:38:38 -04:00
if ( ui_ -> graphViewer -> isVisible () && graphes_ . size () && localMaps_ . size () == 0 )
{
sliderIterationsValueChanged (( int ) graphes_ . size () - 1 );
}
2015-03-04 17:27:44 -05:00
}
void DatabaseViewer :: moveEvent ( QMoveEvent * anEvent )
{
if ( this -> isVisible ())
{
// HACK, there is a move event when the window is shown the first time.
if ( ! firstCall_ )
{
this -> configModified ();
}
firstCall_ = false ;
}
2014-10-28 19:57:21 +00:00
}
2014-08-11 15:23:06 +00:00
void DatabaseViewer :: resizeEvent ( QResizeEvent * anEvent )
{
2015-03-04 17:27:44 -05:00
if ( this -> isVisible ())
{
this -> configModified ();
}
}
bool DatabaseViewer :: eventFilter ( QObject * obj , QEvent * event )
{
if ( event -> type () == QEvent :: Resize && qobject_cast < QDockWidget *> ( obj ))
{
this -> setWindowModified ( true );
}
return QWidget :: eventFilter ( obj , event );
2014-08-11 15:23:06 +00:00
}
2014-01-22 19:49:28 +00:00
void DatabaseViewer :: exportDatabase ()
{
2015-10-16 14:54:47 -04:00
if ( ! dbDriver_ || ids_ . size () == 0 )
2014-01-22 19:49:28 +00:00
{
return ;
}
rtabmap :: ExportDialog dialog ;
if ( dialog . exec ())
{
if ( ! dialog . outputPath (). isEmpty ())
{
int framesIgnored = dialog . framesIgnored ();
2015-05-01 07:30:09 -04:00
double frameRate = dialog . targetFramerate ();
2015-04-16 13:44:54 -04:00
int sessionExported = dialog . sessionExported ();
2014-01-22 19:49:28 +00:00
QString path = dialog . outputPath ();
rtabmap :: DataRecorder recorder ;
2015-04-16 13:44:54 -04:00
QList < int > ids ;
2015-05-01 07:30:09 -04:00
double previousStamp = 0 ;
2015-05-03 18:16:55 -04:00
std :: vector < double > delays ( ids_ . size ());
int oi = 0 ;
2015-05-29 14:46:48 -04:00
std :: map < int , Transform > poses ;
2016-01-07 19:39:21 -05:00
std :: map < int , double > stamps ;
std :: map < int , Transform > groundTruths ;
2015-05-01 07:30:09 -04:00
for ( int i = 0 ; i < ids_ . size (); i += 1 + framesIgnored )
2015-04-16 13:44:54 -04:00
{
2016-01-07 19:39:21 -05:00
Transform odomPose , groundTruth ;
2015-05-01 07:30:09 -04:00
int weight = - 1 ;
int mapId = - 1 ;
std :: string label ;
double stamp = 0 ;
2016-01-07 19:39:21 -05:00
if ( dbDriver_ -> getNodeInfo ( ids_ [ i ], odomPose , mapId , weight , label , stamp , groundTruth ))
2015-04-16 13:44:54 -04:00
{
2015-05-01 07:30:09 -04:00
if ( frameRate == 0 ||
previousStamp == 0 ||
stamp == 0 ||
stamp - previousStamp >= 1.0 / frameRate )
2015-04-16 13:44:54 -04:00
{
2015-05-01 07:30:09 -04:00
if ( sessionExported < 0 || sessionExported == mapId )
2015-04-16 13:44:54 -04:00
{
ids . push_back ( ids_ [ i ]);
2015-05-04 18:17:12 -04:00
if ( previousStamp && stamp )
{
delays [ oi ++ ] = stamp - previousStamp ;
}
previousStamp = stamp ;
2015-05-29 14:46:48 -04:00
poses . insert ( std :: make_pair ( ids_ [ i ], odomPose ));
2016-01-07 19:39:21 -05:00
stamps . insert ( std :: make_pair ( ids_ [ i ], stamp ));
groundTruths . insert ( std :: make_pair ( ids_ [ i ], groundTruth ));
2015-04-16 13:44:54 -04:00
}
2015-05-01 07:30:09 -04:00
}
if ( sessionExported >= 0 && mapId > sessionExported )
{
break ;
2015-04-16 13:44:54 -04:00
}
}
}
2015-05-03 18:16:55 -04:00
delays . resize ( oi );
2014-01-22 19:49:28 +00:00
if ( recorder . init ( path , false ))
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog * progressDialog = new rtabmap :: ProgressDialog ( this );
2015-05-03 18:16:55 -04:00
progressDialog -> setAttribute ( Qt :: WA_DeleteOnClose );
progressDialog -> setMaximumSteps ( ids . size ());
progressDialog -> show ();
2015-10-24 17:03:44 -04:00
UINFO ( "Decompress: rgb=%d depth=%d scan=%d userData=%d" ,
dialog . isRgbExported () ? 1 : 0 ,
dialog . isDepthExported () ? 1 : 0 ,
dialog . isDepth2dExported () ? 1 : 0 ,
dialog . isUserDataExported () ? 1 : 0 );
2014-01-22 19:49:28 +00:00
2015-05-01 07:30:09 -04:00
for ( int i = 0 ; i < ids . size (); ++ i )
2014-01-22 19:49:28 +00:00
{
2015-04-16 13:44:54 -04:00
int id = ids . at ( i );
2014-01-22 19:49:28 +00:00
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( id , data );
2015-10-24 17:03:44 -04:00
cv :: Mat depth , rgb , scan , userData ;
data . uncompressDataConst (
! dialog . isRgbExported () ? 0 :& rgb ,
! dialog . isDepthExported () ? 0 :& depth ,
! dialog . isDepth2dExported () ? 0 :& scan ,
! dialog . isUserDataExported () ? 0 :& userData );
2015-05-29 14:46:48 -04:00
cv :: Mat covariance = cv :: Mat :: eye ( 6 , 6 , CV_64FC1 );
2015-05-06 13:05:32 -04:00
if ( dialog . isOdomExported ())
2015-04-17 18:30:12 -04:00
{
2015-10-16 14:54:47 -04:00
std :: map < int , Link > links ;
dbDriver_ -> loadLinks ( id , links , Link :: kNeighbor );
if ( links . size () && links . begin () -> first < id )
2015-05-29 14:46:48 -04:00
{
2015-10-16 14:54:47 -04:00
covariance = links . begin () -> second . infMatrix (). inv ();
2015-05-29 14:46:48 -04:00
}
2015-04-17 18:30:12 -04:00
}
2015-05-06 13:05:32 -04:00
2015-05-29 14:46:48 -04:00
rtabmap :: SensorData sensorData ;
if ( data . cameraModels (). size ())
{
sensorData = rtabmap :: SensorData (
2015-10-24 17:03:44 -04:00
scan ,
2016-08-21 19:33:01 -04:00
LaserScanInfo ( dialog . isDepth2dExported () ? data . laserScanInfo (). maxPoints () : 0 ,
dialog . isDepth2dExported () ? data . laserScanInfo (). maxRange () : 0 ,
dialog . isDepth2dExported () ? data . laserScanInfo (). localTransform () : Transform :: getIdentity ()),
2015-10-24 17:03:44 -04:00
rgb ,
depth ,
2015-05-29 14:46:48 -04:00
data . cameraModels (),
2016-01-07 19:39:21 -05:00
id ,
stamps . at ( id ),
2015-10-24 17:03:44 -04:00
userData );
2015-05-29 14:46:48 -04:00
}
else
{
sensorData = rtabmap :: SensorData (
2015-10-24 17:03:44 -04:00
scan ,
2016-08-21 19:33:01 -04:00
LaserScanInfo ( dialog . isDepth2dExported () ? data . laserScanInfo (). maxPoints () : 0 ,
dialog . isDepth2dExported () ? data . laserScanInfo (). maxRange () : 0 ,
dialog . isDepth2dExported () ? data . laserScanInfo (). localTransform () : Transform :: getIdentity ()),
2015-10-24 17:03:44 -04:00
rgb ,
depth ,
2015-05-29 14:46:48 -04:00
data . stereoCameraModel (),
2016-01-07 19:39:21 -05:00
id ,
stamps . at ( id ),
2015-10-24 17:03:44 -04:00
userData );
2015-05-29 14:46:48 -04:00
}
2016-01-07 19:39:21 -05:00
sensorData . setGroundTruth ( groundTruths . at ( id ));
2015-05-29 14:46:48 -04:00
recorder . addData ( sensorData , dialog . isOdomExported () ? poses . at ( id ) : Transform (), covariance );
2014-01-22 19:49:28 +00:00
2015-05-03 18:16:55 -04:00
progressDialog -> appendText ( tr ( "Exported node %1" ). arg ( id ));
progressDialog -> incrementStep ();
2014-01-22 19:49:28 +00:00
QApplication :: processEvents ();
}
2015-05-03 18:16:55 -04:00
progressDialog -> setValue ( progressDialog -> maximumSteps ());
if ( delays . size ())
{
progressDialog -> appendText ( tr ( "Average frame rate=%1 Hz (Min=%2, Max=%3)" )
. arg ( 1.0 / uMean ( delays )). arg ( 1.0 / uMax ( delays )). arg ( 1.0 / uMin ( delays )));
}
progressDialog -> appendText ( tr ( "Export finished to \" %1 \" !" ). arg ( path ));
2014-01-22 19:49:28 +00:00
}
else
{
UERROR ( "DataRecorder init failed?!" );
}
}
else
{
QMessageBox :: warning ( this , tr ( "Cannot export database" ), tr ( "An output path must be set!" ));
}
}
}
2014-02-11 23:04:22 +00:00
void DatabaseViewer :: extractImages ()
{
2015-10-16 14:54:47 -04:00
if ( ! dbDriver_ || ids_ . size () == 0 )
2014-02-11 23:04:22 +00:00
{
return ;
}
2016-07-17 17:44:12 -04:00
QStringList formats ;
formats . push_back ( "jpg" );
formats . push_back ( "png" );
bool ok ;
QString ext = QInputDialog :: getItem ( this , tr ( "Which RGB format?" ), tr ( "Format:" ), formats , 0 , false , & ok );
if ( ! ok )
{
return ;
}
2014-02-11 23:04:22 +00:00
QString path = QFileDialog :: getExistingDirectory ( this , tr ( "Select directory where to save images..." ), QDir :: homePath ());
2016-10-25 14:54:02 -04:00
if ( ! path . isEmpty ())
2014-02-11 23:04:22 +00:00
{
2015-06-24 20:20:27 -04:00
if ( ids_ . size ())
{
int id = ids_ . at ( 0 );
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( id , data );
data . uncompressData ();
2015-06-24 20:20:27 -04:00
if ( ! data . imageRaw (). empty () && ! data . rightRaw (). empty ())
{
QDir dir ;
dir . mkdir ( QString ( "%1/left" ). arg ( path ));
dir . mkdir ( QString ( "%1/right" ). arg ( path ));
if ( databaseFileName_ . empty ())
{
UERROR ( "Cannot save calibration file, database name is empty!" );
}
2016-01-19 21:01:56 -05:00
else if ( data . stereoCameraModel (). isValidForProjection ())
2015-06-24 20:20:27 -04:00
{
std :: string cameraName = uSplit ( databaseFileName_ , '.' ). front ();
StereoCameraModel model (
cameraName ,
data . imageRaw (). size (),
data . stereoCameraModel (). left (). K (),
data . stereoCameraModel (). left (). D (),
data . stereoCameraModel (). left (). R (),
data . stereoCameraModel (). left (). P (),
data . rightRaw (). size (),
data . stereoCameraModel (). right (). K (),
data . stereoCameraModel (). right (). D (),
data . stereoCameraModel (). right (). R (),
data . stereoCameraModel (). right (). P (),
data . stereoCameraModel (). R (),
data . stereoCameraModel (). T (),
data . stereoCameraModel (). E (),
data . stereoCameraModel (). F (),
data . stereoCameraModel (). left (). localTransform ());
2015-07-30 14:17:29 -04:00
if ( model . save ( path . toStdString ()))
2015-06-24 20:20:27 -04:00
{
UINFO ( "Saved stereo calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
else
{
UERROR ( "Failed saving calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
}
}
2015-08-04 11:35:55 -04:00
else if ( ! data . imageRaw (). empty ())
{
if ( ! data . depthRaw (). empty ())
{
QDir dir ;
dir . mkdir ( QString ( "%1/rgb" ). arg ( path ));
dir . mkdir ( QString ( "%1/depth" ). arg ( path ));
}
if ( databaseFileName_ . empty ())
{
UERROR ( "Cannot save calibration file, database name is empty!" );
}
else if ( data . cameraModels (). size () > 1 )
{
UERROR ( "Only one camera calibration can be saved at this time (%d detected)" , ( int ) data . cameraModels (). size ());
}
2016-01-19 21:01:56 -05:00
else if ( data . cameraModels (). size () == 1 && data . cameraModels (). front (). isValidForProjection ())
2015-08-04 11:35:55 -04:00
{
std :: string cameraName = uSplit ( databaseFileName_ , '.' ). front ();
CameraModel model ( cameraName ,
data . imageRaw (). size (),
data . cameraModels (). front (). K (),
data . cameraModels (). front (). D (),
data . cameraModels (). front (). R (),
data . cameraModels (). front (). P (),
data . cameraModels (). front (). localTransform ());
if ( model . save ( path . toStdString ()))
{
UINFO ( "Saved calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
else
{
UERROR ( "Failed saving calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
}
}
2015-06-24 20:20:27 -04:00
}
2016-10-23 18:32:07 -04:00
int imagesExported = 0 ;
2015-06-24 20:20:27 -04:00
for ( int i = 0 ; i < ids_ . size (); ++ i )
2014-02-11 23:04:22 +00:00
{
int id = ids_ . at ( i );
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( id , data );
data . uncompressData ();
2015-06-24 20:20:27 -04:00
if ( ! data . imageRaw (). empty () && ! data . rightRaw (). empty ())
2014-02-11 23:04:22 +00:00
{
2016-07-17 17:44:12 -04:00
cv :: imwrite ( QString ( "%1/left/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . imageRaw ());
cv :: imwrite ( QString ( "%1/right/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . rightRaw ());
UINFO ( QString ( "Saved left/%1.%2 and right/%1.%2" ). arg ( id ). arg ( ext ). toStdString (). c_str ());
2016-10-23 18:32:07 -04:00
++ imagesExported ;
2015-06-24 20:20:27 -04:00
}
2015-08-04 11:35:55 -04:00
else if ( ! data . imageRaw (). empty () && ! data . depthRaw (). empty ())
{
2016-07-17 17:44:12 -04:00
cv :: imwrite ( QString ( "%1/rgb/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . imageRaw ());
2015-08-04 11:35:55 -04:00
cv :: imwrite ( QString ( "%1/depth/%2.png" ). arg ( path ). arg ( id ). toStdString (), data . depthRaw ());
2016-07-17 17:44:12 -04:00
UINFO ( QString ( "Saved rgb/%1.%2 and depth/%1.png" ). arg ( id ). arg ( ext ). toStdString (). c_str ());
2016-10-23 18:32:07 -04:00
++ imagesExported ;
2015-08-04 11:35:55 -04:00
}
2015-06-24 20:20:27 -04:00
else if ( ! data . imageRaw (). empty ())
{
2016-07-17 17:44:12 -04:00
cv :: imwrite ( QString ( "%1/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . imageRaw ());
UINFO ( QString ( "Saved %1.%2" ). arg ( id ). arg ( ext ). toStdString (). c_str ());
2016-10-23 18:32:07 -04:00
++ imagesExported ;
2014-02-11 23:04:22 +00:00
}
}
2016-10-23 18:32:07 -04:00
QMessageBox :: information ( this , tr ( "Exporting" ), tr ( "%1 images exported!" ). arg ( imagesExported ));
2014-02-11 23:04:22 +00:00
}
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: updateIds ()
2011-06-05 14:45:39 +00:00
{
2015-10-16 14:54:47 -04:00
if ( ! dbDriver_ )
2011-06-05 14:45:39 +00:00
{
return ;
}
2015-10-24 17:03:44 -04:00
UINFO ( "Loading all IDs..." );
2015-10-16 14:54:47 -04:00
std :: set < int > ids ;
dbDriver_ -> getAllNodeIds ( ids );
2011-06-05 14:45:39 +00:00
ids_ = QList < int >:: fromStdList ( std :: list < int > ( ids . begin (), ids . end ()));
2014-02-11 23:04:22 +00:00
idToIndex_ . clear ();
2015-05-22 15:06:05 -04:00
mapIds_ . clear ();
2015-10-16 14:54:47 -04:00
poses_ . clear ();
2016-01-07 19:39:21 -05:00
groundTruthPoses_ . clear ();
2015-10-16 14:54:47 -04:00
links_ . clear ();
linksAdded_ . clear ();
linksRefined_ . clear ();
linksRemoved_ . clear ();
ui_ -> label_optimizeFrom -> setText ( tr ( "Optimize from" ));
2015-10-24 17:03:44 -04:00
std :: multimap < int , Link > links ;
dbDriver_ -> getAllLinks ( links , true );
UDEBUG ( "%d total links loaded" , ( int ) links . size ());
double totalOdom = 0.0 ;
Transform previousPose ;
int sessions = ids_ . size () ? 1 : 0 ;
2015-10-27 13:39:29 -04:00
double totalTime = 0.0 ;
double previousStamp = 0.0 ;
2015-10-28 16:50:18 -04:00
std :: set < int > idsWithoutBad ;
dbDriver_ -> getAllNodeIds ( idsWithoutBad , false , true );
int badcountInLTM = 0 ;
int badCountInGraph = 0 ;
2016-09-20 11:19:34 -04:00
double firstStamp = 0.0 ;
2014-02-11 23:04:22 +00:00
for ( int i = 0 ; i < ids_ . size (); ++ i )
{
idToIndex_ . insert ( ids_ [ i ], i );
2015-05-22 15:06:05 -04:00
2016-01-07 19:39:21 -05:00
Transform p , g ;
2015-05-22 15:06:05 -04:00
int w ;
std :: string l ;
double s ;
int mapId ;
2016-01-07 19:39:21 -05:00
dbDriver_ -> getNodeInfo ( ids_ [ i ], p , mapId , w , l , s , g );
2015-05-22 15:06:05 -04:00
mapIds_ . insert ( std :: make_pair ( ids_ [ i ], mapId ));
2015-10-16 14:54:47 -04:00
2016-09-20 11:19:34 -04:00
double stamp = 0.0 ;
std :: map < std :: string , float > statistics = dbDriver_ -> getStatistics ( ids_ [ i ], stamp );
if ( firstStamp == 0.0 )
{
firstStamp = stamp ;
}
for ( std :: map < std :: string , float >:: iterator iter = statistics . begin (); iter != statistics . end (); ++ iter )
{
ui_ -> toolBox_statistics -> updateStat ( iter -> first . c_str (), float ( stamp - firstStamp ), iter -> second , true );
}
2015-10-24 17:03:44 -04:00
if ( i > 0 )
2015-10-16 14:54:47 -04:00
{
2015-10-24 17:03:44 -04:00
if ( mapIds_ . at ( ids_ [ i - 1 ]) == mapId )
2015-10-16 14:54:47 -04:00
{
2015-10-24 17:03:44 -04:00
if ( ! p . isNull () && ! previousPose . isNull ())
{
totalOdom += p . getDistance ( previousPose );
}
2015-10-27 13:39:29 -04:00
if ( previousStamp > 0.0 && s > 0.0 )
{
totalTime += s - previousStamp ;
}
2015-10-24 17:03:44 -04:00
}
else
{
++ sessions ;
2015-10-16 14:54:47 -04:00
}
}
2015-10-27 13:39:29 -04:00
previousStamp = s ;
2015-10-24 17:03:44 -04:00
previousPose = p ;
//links
2015-10-26 17:52:02 -04:00
bool addPose = false ;
2015-11-03 12:49:03 -05:00
for ( std :: multimap < int , Link >:: iterator jter = links . find ( ids_ [ i ]); jter != links . end () && jter -> first == ids_ [ i ]; ++ jter )
2015-10-24 17:03:44 -04:00
{
2015-11-03 12:49:03 -05:00
std :: multimap < int , Link >:: iterator invertedLinkIter = graph :: findLink ( links , jter -> second . to (), jter -> second . from (), false );
2015-10-24 17:03:44 -04:00
if ( jter -> second . isValid () && // null transform means a rehearsed location
ids . find ( jter -> second . from ()) != ids . end () &&
ids . find ( jter -> second . to ()) != ids . end () &&
graph :: findLink ( links_ , jter -> second . from (), jter -> second . to ()) == links_ . end () &&
graph :: findLink ( links , jter -> second . from (), jter -> second . to (), false ) != links . end () &&
2015-10-26 12:59:20 -04:00
invertedLinkIter != links . end ())
2015-10-24 17:03:44 -04:00
{
2015-10-26 12:59:20 -04:00
// check if user_data is set in opposite direction
if ( jter -> second . userDataCompressed (). cols == 0 &&
invertedLinkIter -> second . userDataCompressed (). cols != 0 )
{
links_ . insert ( std :: make_pair ( invertedLinkIter -> second . from (), invertedLinkIter -> second ));
}
else
{
links_ . insert ( std :: make_pair ( ids_ [ i ], jter -> second ));
}
2015-10-26 17:52:02 -04:00
addPose = true ;
}
else if ( graph :: findLink ( links_ , jter -> second . from (), jter -> second . to ()) != links_ . end ())
{
addPose = true ;
2015-10-24 17:03:44 -04:00
}
}
2015-10-26 17:52:02 -04:00
if ( addPose )
2015-10-24 17:03:44 -04:00
{
poses_ . insert ( std :: make_pair ( ids_ [ i ], p ));
2016-01-07 19:39:21 -05:00
if ( ! g . isNull ())
{
groundTruthPoses_ . insert ( std :: make_pair ( ids_ [ i ], g ));
}
2015-10-24 17:03:44 -04:00
}
2015-10-28 16:50:18 -04:00
if ( idsWithoutBad . find ( ids_ [ i ]) == idsWithoutBad . end ())
{
++ badcountInLTM ;
if ( addPose )
{
++ badCountInGraph ;
}
}
2014-02-11 23:04:22 +00:00
}
2015-10-24 17:03:44 -04:00
UINFO ( "Loaded %d ids, %d poses and %d links" , ( int ) ids_ . size (), ( int ) poses_ . size (), ( int ) links_ . size ());
2014-02-11 23:04:22 +00:00
2015-10-27 13:39:29 -04:00
UINFO ( "Update database info..." );
ui_ -> textEdit_info -> clear ();
ui_ -> textEdit_info -> append ( tr ( "Version: \t\t %1" ). arg ( dbDriver_ -> getDatabaseVersion (). c_str ()));
ui_ -> textEdit_info -> append ( tr ( "Sessions: \t\t %1" ). arg ( sessions ));
ui_ -> textEdit_info -> append ( tr ( "Total odometry length: \t %1 m" ). arg ( totalOdom ));
ui_ -> textEdit_info -> append ( tr ( "Total time: \t\t %1" ). arg ( QDateTime :: fromMSecsSinceEpoch ( totalTime * 1000 ). toUTC (). toString ( "hh:mm:ss.zzz" )));
ui_ -> textEdit_info -> append ( tr ( "LTM: \t\t %1 nodes and %2 words" ). arg ( ids . size ()). arg ( dbDriver_ -> getTotalDictionarySize ()));
ui_ -> textEdit_info -> append ( tr ( "WM: \t\t %1 nodes and %2 words" ). arg ( dbDriver_ -> getLastNodesSize ()). arg ( dbDriver_ -> getLastDictionarySize ()));
ui_ -> textEdit_info -> append ( tr ( "Global graph: \t %1 poses and %2 links" ). arg ( poses_ . size ()). arg ( links_ . size ()));
2016-01-07 19:39:21 -05:00
ui_ -> textEdit_info -> append ( tr ( "Ground truth: \t %1 poses" ). arg ( groundTruthPoses_ . size ()));
2015-10-27 13:39:29 -04:00
ui_ -> textEdit_info -> append ( "" );
ui_ -> textEdit_info -> append ( tr ( "Database size: \t %1 MB" ). arg ( dbDriver_ -> getMemoryUsed () / 1000000 ));
ui_ -> textEdit_info -> append ( tr ( "Images size: \t %1 MB" ). arg ( dbDriver_ -> getImagesMemoryUsed () / 1000000 ));
ui_ -> textEdit_info -> append ( tr ( "Depths size: \t %1 MB" ). arg ( dbDriver_ -> getDepthImagesMemoryUsed () / 1000000 ));
ui_ -> textEdit_info -> append ( tr ( "Scans size: \t\t %1 MB" ). arg ( dbDriver_ -> getLaserScansMemoryUsed () / 1000000 ));
ui_ -> textEdit_info -> append ( tr ( "User data size: \t %1 bytes" ). arg ( dbDriver_ -> getUserDataMemoryUsed ()));
ui_ -> textEdit_info -> append ( tr ( "Dictionary size: \t %1 bytes" ). arg ( dbDriver_ -> getWordsMemoryUsed ()));
2015-10-28 16:50:18 -04:00
ui_ -> textEdit_info -> append ( "" );
ui_ -> textEdit_info -> append ( tr ( "%1 bad signatures in LTM" ). arg ( badcountInLTM ));
ui_ -> textEdit_info -> append ( tr ( "%1 bad signatures in the global graph" ). arg ( badCountInGraph ));
2016-08-08 14:35:39 -04:00
ui_ -> textEdit_info -> append ( "" );
ParametersMap parameters = dbDriver_ -> getLastParameters ();
QFontMetrics metrics ( ui_ -> textEdit_info -> font ());
int tabW = ui_ -> textEdit_info -> tabStopWidth ();
for ( ParametersMap :: iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
int strW = metrics . width ( QString ( iter -> first . c_str ()) + "=" );
ui_ -> textEdit_info -> append ( tr ( "%1=%2%3" )
. arg ( iter -> first . c_str ())
. arg ( strW < tabW ? " \t\t\t\t " : strW < tabW * 2 ? " \t\t\t " : strW < tabW * 3 ? " \t\t " : " \t " )
. arg ( iter -> second . c_str ()));
}
// move back the cursor at the beginning
ui_ -> textEdit_info -> moveCursor ( QTextCursor :: Start ) ;
ui_ -> textEdit_info -> ensureCursorVisible () ;
2015-10-27 13:39:29 -04:00
2015-10-16 14:54:47 -04:00
if ( ids . size ())
2014-02-11 23:04:22 +00:00
{
2015-01-08 16:12:45 -05:00
if ( poses_ . size ())
{
bool nullPoses = poses_ . begin () -> second . isNull ();
for ( std :: map < int , Transform >:: iterator iter = poses_ . begin (); iter != poses_ . end (); ++ iter )
{
if (( ! iter -> second . isNull () && nullPoses ) ||
( iter -> second . isNull () && ! nullPoses ))
{
2015-02-03 17:20:17 -05:00
if ( iter -> second . isNull ())
{
UWARN ( "Pose %d is null!" , iter -> first );
}
2015-01-08 16:12:45 -05:00
UWARN ( "Mixed valid and null poses! Ignoring graph..." );
poses_ . clear ();
links_ . clear ();
break ;
}
}
if ( nullPoses )
{
poses_ . clear ();
links_ . clear ();
}
2015-10-24 17:03:44 -04:00
if ( poses_ . size ())
{
ui_ -> spinBox_optimizationsFrom -> setRange ( poses_ . begin () -> first , poses_ . rbegin () -> first );
ui_ -> spinBox_optimizationsFrom -> setValue ( poses_ . begin () -> first );
ui_ -> label_optimizeFrom -> setText ( tr ( "Optimize from [%1, %2]" ). arg ( poses_ . begin () -> first ). arg ( poses_ . rbegin () -> first ));
}
2015-01-08 16:12:45 -05:00
}
2014-02-11 23:04:22 +00:00
}
2014-02-27 17:00:46 +00:00
ui_ -> actionGenerate_TORO_graph_graph -> setEnabled ( false );
2015-09-02 17:51:43 -04:00
ui_ -> actionGenerate_g2o_graph_g2o -> setEnabled ( false );
2014-02-27 17:00:46 +00:00
graphes_ . clear ();
2015-05-23 13:36:36 -04:00
graphLinks_ . clear ();
2014-02-11 23:04:22 +00:00
neighborLinks_ . clear ();
loopLinks_ . clear ();
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = links_ . begin (); iter != links_ . end (); ++ iter )
{
if ( ! iter -> second . transform (). isNull ())
{
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () == rtabmap :: Link :: kNeighbor ||
iter -> second . type () == rtabmap :: Link :: kNeighborMerged )
2014-02-11 23:04:22 +00:00
{
neighborLinks_ . append ( iter -> second );
}
else
{
loopLinks_ . append ( iter -> second );
}
}
else
{
UERROR ( "Transform null for link from %d to %d" , iter -> first , iter -> second . to ());
}
}
2011-06-05 14:45:39 +00:00
if ( ids_ . size ())
{
ui_ -> horizontalSlider_A -> setMinimum ( 0 );
ui_ -> horizontalSlider_B -> setMinimum ( 0 );
ui_ -> horizontalSlider_A -> setMaximum ( ids_ . size () - 1 );
ui_ -> horizontalSlider_B -> setMaximum ( ids_ . size () - 1 );
ui_ -> horizontalSlider_A -> setEnabled ( true );
ui_ -> horizontalSlider_B -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
ui_ -> horizontalSlider_A -> setSliderPosition ( 0 );
ui_ -> horizontalSlider_B -> setSliderPosition ( 0 );
sliderAValueChanged ( 0 );
sliderBValueChanged ( 0 );
2011-06-05 14:45:39 +00:00
}
else
{
ui_ -> horizontalSlider_A -> setEnabled ( false );
ui_ -> horizontalSlider_B -> setEnabled ( false );
ui_ -> label_idA -> setText ( "NaN" );
ui_ -> label_idB -> setText ( "NaN" );
}
2014-08-11 15:23:06 +00:00
2014-02-11 23:04:22 +00:00
if ( neighborLinks_ . size ())
{
ui_ -> horizontalSlider_neighbors -> setMinimum ( 0 );
ui_ -> horizontalSlider_neighbors -> setMaximum ( neighborLinks_ . size () - 1 );
ui_ -> horizontalSlider_neighbors -> setEnabled ( true );
ui_ -> horizontalSlider_neighbors -> setSliderPosition ( 0 );
}
else
{
ui_ -> horizontalSlider_neighbors -> setEnabled ( false );
}
2014-08-11 15:23:06 +00:00
2014-10-13 19:10:22 +00:00
if ( ids_ . size ())
{
updateLoopClosuresSlider ();
2015-11-27 15:36:57 -05:00
if ( ui_ -> graphViewer -> isVisible ())
{
updateGraphView ();
}
2014-10-13 19:10:22 +00:00
}
2011-06-05 14:45:39 +00:00
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: generateGraph ()
2011-06-05 14:45:39 +00:00
{
2015-10-16 14:54:47 -04:00
if ( ! dbDriver_ )
2011-06-05 14:45:39 +00:00
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "A database must must loaded first... \n Use File->Open database." ));
return ;
}
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save File" ), pathDatabase_ + "/Graph.dot" , tr ( "Graphiz file (*.dot)" ));
if ( ! path . isEmpty ())
{
2015-10-16 14:54:47 -04:00
dbDriver_ -> generateGraph ( path . toStdString ());
2011-06-05 14:45:39 +00:00
}
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: generateLocalGraph ()
2011-06-05 14:45:39 +00:00
{
2015-10-16 14:54:47 -04:00
if ( ! ids_ . size () || ! dbDriver_ )
2011-06-05 14:45:39 +00:00
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "The database is empty..." ));
return ;
}
bool ok = false ;
int id = QInputDialog :: getInt ( this , tr ( "Around which location?" ), tr ( "Location ID" ), ids_ . first (), ids_ . first (), ids_ . last (), 1 , & ok );
if ( ok )
{
int margin = QInputDialog :: getInt ( this , tr ( "Depth around the location?" ), tr ( "Margin" ), 4 , 1 , 100 , 1 , & ok );
if ( ok )
{
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save File" ), pathDatabase_ + "/Graph" + QString :: number ( id ) + ".dot" , tr ( "Graphiz file (*.dot)" ));
2015-10-16 14:54:47 -04:00
if ( ! path . isEmpty () && id > 0 )
2011-06-05 14:45:39 +00:00
{
2015-10-16 14:54:47 -04:00
std :: map < int , int > ids ;
std :: list < int > curentMarginList ;
std :: set < int > currentMargin ;
std :: set < int > nextMargin ;
nextMargin . insert ( id );
int m = 0 ;
while (( margin == 0 || m < margin ) && nextMargin . size ())
{
curentMarginList = std :: list < int > ( nextMargin . rbegin (), nextMargin . rend ());
nextMargin . clear ();
for ( std :: list < int >:: iterator jter = curentMarginList . begin (); jter != curentMarginList . end (); ++ jter )
{
if ( ids . find ( * jter ) == ids . end ())
{
std :: map < int , Link > links ;
ids . insert ( std :: pair < int , int > ( * jter , m ));
UTimer timer ;
dbDriver_ -> loadLinks ( * jter , links );
// links
for ( std :: map < int , Link >:: const_iterator iter = links . begin (); iter != links . end (); ++ iter )
{
if ( ! uContains ( ids , iter -> first ))
{
UASSERT ( iter -> second . type () != Link :: kUndef );
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () == Link :: kNeighbor ||
iter -> second . type () == Link :: kNeighborMerged )
2015-10-16 14:54:47 -04:00
{
nextMargin . insert ( iter -> first );
}
else
{
// loop closures are on same margin
if ( currentMargin . insert ( iter -> first ). second )
{
curentMarginList . push_back ( iter -> first );
}
}
}
}
}
}
++ m ;
}
2012-06-24 17:19:34 +00:00
if ( ids . size () > 0 )
2011-06-05 14:45:39 +00:00
{
2012-06-24 17:19:34 +00:00
ids . insert ( std :: pair < int , int > ( id , 0 ));
std :: set < int > idsSet ;
for ( std :: map < int , int >:: iterator iter = ids . begin (); iter != ids . end (); ++ iter )
2012-03-03 01:46:30 +00:00
{
2012-06-24 17:19:34 +00:00
idsSet . insert ( idsSet . end (), iter -> first );
UINFO ( "Node %d" , iter -> first );
2012-03-03 01:46:30 +00:00
}
2012-06-24 17:19:34 +00:00
UINFO ( "idsSet=%d" , idsSet . size ());
2015-10-16 14:54:47 -04:00
dbDriver_ -> generateGraph ( path . toStdString (), idsSet );
2012-03-03 01:46:30 +00:00
}
else
{
2012-06-24 17:19:34 +00:00
QMessageBox :: critical ( this , tr ( "Error" ), tr ( "No neighbors found for signature %1." ). arg ( id ));
2011-06-05 14:45:39 +00:00
}
}
}
}
}
2014-02-27 17:00:46 +00:00
void DatabaseViewer :: generateTOROGraph ()
{
2015-11-27 15:36:57 -05:00
if ( graphes_ . empty ())
{
this -> updateGraphView ();
if ( graphes_ . empty () || ui_ -> horizontalSlider_iterations -> maximum () != ( int ) graphes_ . size () - 1 )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "No graph in database?!" ));
return ;
}
}
2015-05-23 13:36:36 -04:00
if ( ! graphes_ . size () || ! graphLinks_ . size ())
2014-02-27 17:00:46 +00:00
{
QMessageBox :: warning ( this , tr ( "Cannot generate a TORO graph" ), tr ( "No poses or no links..." ));
return ;
}
bool ok = false ;
2015-03-25 16:30:05 -04:00
int id = QInputDialog :: getInt ( this , tr ( "Which iteration?" ), tr ( "Iteration (0 -> %1)" ). arg (( int ) graphes_ . size () - 1 ), ( int ) graphes_ . size () - 1 , 0 , ( int ) graphes_ . size () - 1 , 1 , & ok );
2014-02-27 17:00:46 +00:00
if ( ok )
{
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save File" ), pathDatabase_ + "/constraints" + QString :: number ( id ) + ".graph" , tr ( "TORO file (*.graph)" ));
if ( ! path . isEmpty ())
{
2015-12-17 14:13:19 -05:00
bool varianceIgnored = uStr2Bool ( ui_ -> parameters_toolbox -> getParameters (). at ( Parameters :: kOptimizerVarianceIgnored ()));
if ( varianceIgnored )
2015-09-02 17:51:43 -04:00
{
std :: multimap < int , rtabmap :: Link > links = graphLinks_ ;
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = links . begin (); iter != links . end (); ++ iter )
{
2016-07-24 13:23:22 -04:00
// reset to identity covariance
iter -> second = Link ( iter -> second . from (),
iter -> second . to (),
iter -> second . type (),
iter -> second . transform ());
2015-09-02 17:51:43 -04:00
}
2015-11-27 11:09:39 -05:00
graph :: exportPoses ( path . toStdString (), 3 , uValueAt ( graphes_ , id ), links );
2015-09-02 17:51:43 -04:00
}
else
{
2015-11-27 11:09:39 -05:00
graph :: exportPoses ( path . toStdString (), 3 , uValueAt ( graphes_ , id ), graphLinks_ );
2015-09-02 17:51:43 -04:00
}
}
}
}
void DatabaseViewer :: generateG2OGraph ()
{
2015-11-27 15:36:57 -05:00
if ( graphes_ . empty ())
{
this -> updateGraphView ();
if ( graphes_ . empty () || ui_ -> horizontalSlider_iterations -> maximum () != ( int ) graphes_ . size () - 1 )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "No graph in database?!" ));
return ;
}
}
2015-09-02 17:51:43 -04:00
if ( ! graphes_ . size () || ! graphLinks_ . size ())
{
QMessageBox :: warning ( this , tr ( "Cannot generate a g2o graph" ), tr ( "No poses or no links..." ));
return ;
}
bool ok = false ;
int id = QInputDialog :: getInt ( this , tr ( "Which iteration?" ), tr ( "Iteration (0 -> %1)" ). arg (( int ) graphes_ . size () - 1 ), ( int ) graphes_ . size () - 1 , 0 , ( int ) graphes_ . size () - 1 , 1 , & ok );
if ( ok )
{
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save File" ), pathDatabase_ + "/constraints" + QString :: number ( id ) + ".g2o" , tr ( "g2o file (*.g2o)" ));
if ( ! path . isEmpty ())
{
2015-12-17 14:13:19 -05:00
bool varianceIgnored = uStr2Bool ( ui_ -> parameters_toolbox -> getParameters (). at ( Parameters :: kOptimizerVarianceIgnored ()));
bool robust = uStr2Bool ( ui_ -> parameters_toolbox -> getParameters (). at ( Parameters :: kOptimizerRobust ()));
if ( varianceIgnored )
2015-09-02 17:51:43 -04:00
{
std :: multimap < int , rtabmap :: Link > links = graphLinks_ ;
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = links . begin (); iter != links . end (); ++ iter )
{
2016-07-24 13:23:22 -04:00
// reset to identity covariance
iter -> second = Link ( iter -> second . from (),
iter -> second . to (),
iter -> second . type (),
iter -> second . transform ());
2015-09-02 17:51:43 -04:00
}
2015-12-17 14:13:19 -05:00
graph :: exportPoses ( path . toStdString (), 4 , uValueAt ( graphes_ , id ), links , std :: map < int , double > (), robust );
2015-09-02 17:51:43 -04:00
}
else
{
2015-12-17 14:13:19 -05:00
graph :: exportPoses ( path . toStdString (), 4 , uValueAt ( graphes_ , id ), graphLinks_ , std :: map < int , double > (), robust );
2015-09-02 17:51:43 -04:00
}
2014-02-27 17:00:46 +00:00
}
}
}
2016-08-25 23:47:01 -04:00
void DatabaseViewer :: regenerateLocalMaps ()
{
UTimer time ;
OccupancyGrid grid ( ui_ -> parameters_toolbox -> getParameters ());
2016-08-26 11:41:35 -04:00
generatedLocalMaps_ . clear ();
generatedLocalMapsInfo_ . clear ();
2016-08-25 23:47:01 -04:00
rtabmap :: ProgressDialog progressDialog ( this );
progressDialog . setMaximumSteps ( ids_ . size ());
progressDialog . show ();
for ( int i = 0 ; i < ids_ . size (); ++ i )
{
SensorData data ;
dbDriver_ -> getNodeData ( ids_ . at ( i ), data );
data . uncompressData ();
int mapId , weight ;
Transform odomPose , groundTruth ;
std :: string label ;
double stamp ;
QString msg ;
if ( dbDriver_ -> getNodeInfo ( data . id (), odomPose , mapId , weight , label , stamp , groundTruth ))
{
Signature s = data ;
s . setPose ( odomPose );
cv :: Mat ground , obstacles ;
cv :: Point3f viewpoint ;
grid . createLocalMap ( s , ground , obstacles , viewpoint );
uInsert ( generatedLocalMaps_ , std :: make_pair ( data . id (), std :: make_pair ( ground , obstacles )));
uInsert ( generatedLocalMapsInfo_ , std :: make_pair ( data . id (), std :: make_pair ( grid . getCellSize (), viewpoint )));
msg = QString ( "Generated local occupancy grid map %1/%2 (%3s)" ). arg ( i + 1 ). arg (( int ) ids_ . size ()). arg ( time . ticks ());
}
progressDialog . appendText ( msg );
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
progressDialog . setValue ( progressDialog . maximumSteps ());
updateGrid ();
}
2016-08-30 21:15:55 -04:00
void DatabaseViewer :: regenerateCurrentLocalMaps ()
{
UTimer time ;
OccupancyGrid grid ( ui_ -> parameters_toolbox -> getParameters ());
if ( ids_ . size () == 0 )
{
UWARN ( "ids_ is empty!" );
return ;
}
QSet < int > idsSet ;
idsSet . insert ( ids_ . at ( ui_ -> horizontalSlider_A -> value ()));
idsSet . insert ( ids_ . at ( ui_ -> horizontalSlider_B -> value ()));
QList < int > ids = idsSet . toList ();
rtabmap :: ProgressDialog progressDialog ( this );
progressDialog . setMaximumSteps ( ids . size ());
progressDialog . show ();
for ( int i = 0 ; i < ids . size (); ++ i )
{
generatedLocalMaps_ . erase ( ids . at ( i ));
generatedLocalMapsInfo_ . erase ( ids . at ( i ));
SensorData data ;
dbDriver_ -> getNodeData ( ids . at ( i ), data );
data . uncompressData ();
int mapId , weight ;
Transform odomPose , groundTruth ;
std :: string label ;
double stamp ;
QString msg ;
if ( dbDriver_ -> getNodeInfo ( data . id (), odomPose , mapId , weight , label , stamp , groundTruth ))
{
Signature s = data ;
s . setPose ( odomPose );
cv :: Mat ground , obstacles ;
cv :: Point3f viewpoint ;
grid . createLocalMap ( s , ground , obstacles , viewpoint );
uInsert ( generatedLocalMaps_ , std :: make_pair ( data . id (), std :: make_pair ( ground , obstacles )));
uInsert ( generatedLocalMapsInfo_ , std :: make_pair ( data . id (), std :: make_pair ( grid . getCellSize (), viewpoint )));
msg = QString ( "Generated local occupancy grid map %1/%2 (%3s)" ). arg ( i + 1 ). arg (( int ) ids . size ()). arg ( time . ticks ());
}
progressDialog . appendText ( msg );
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
progressDialog . setValue ( progressDialog . maximumSteps ());
updateGrid ();
}
2014-02-11 23:04:22 +00:00
void DatabaseViewer :: view3DMap ()
2014-12-14 16:42:10 -05:00
{
2015-10-16 14:54:47 -04:00
if ( ! ids_ . size () || ! dbDriver_ )
2014-12-14 16:42:10 -05:00
{
QMessageBox :: warning ( this , tr ( "Cannot view 3D map" ), tr ( "The database is empty..." ));
return ;
}
2015-10-24 17:03:44 -04:00
2014-12-14 16:42:10 -05:00
if ( graphes_ . empty ())
{
this -> updateGraphView ();
if ( graphes_ . empty () || ui_ -> horizontalSlider_iterations -> maximum () != ( int ) graphes_ . size () - 1 )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "No graph in database?!" ));
return ;
}
}
bool ok = false ;
QStringList items ;
items . append ( "1" );
items . append ( "2" );
items . append ( "4" );
items . append ( "8" );
items . append ( "16" );
2016-10-30 16:03:23 -04:00
items . append ( "-2" );
items . append ( "-4" );
items . append ( "-8" );
items . append ( "-16" );
QString item = QInputDialog :: getItem ( this , tr ( "Decimation?" ), tr ( "Image decimation. Negative decimation is done from RGB size instead of depth size (if depth is smaller than RGB, it may be interpolated depending of the decimation value)." ), items , 2 , false , & ok );
2014-12-14 16:42:10 -05:00
if ( ok )
{
2016-05-05 18:25:26 -04:00
int decimation = item . toInt ();
double maxDepth = QInputDialog :: getDouble ( this , tr ( "Camera depth?" ), tr ( "Maximum depth (m, 0=no max):" ), 4.0 , 0 , 100 , 2 , & ok );
2014-12-14 16:42:10 -05:00
if ( ok )
{
2015-03-16 18:22:54 -04:00
std :: map < int , Transform > optimizedPoses = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
if ( ui_ -> groupBox_posefiltering -> isChecked ())
{
optimizedPoses = graph :: radiusPosesFiltering ( optimizedPoses ,
ui_ -> doubleSpinBox_posefilteringRadius -> value (),
ui_ -> doubleSpinBox_posefilteringAngle -> value () * CV_PI / 180.0 );
}
2014-12-14 16:42:10 -05:00
if ( optimizedPoses . size () > 0 )
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog progressDialog ( this );
2015-03-25 16:30:05 -04:00
progressDialog . setMaximumSteps (( int ) optimizedPoses . size ());
2014-12-14 16:42:10 -05:00
progressDialog . show ();
// create a window
QDialog * window = new QDialog ( this , Qt :: Window );
window -> setModal ( this -> isModal ());
window -> setWindowTitle ( tr ( "3D Map" ));
window -> setMinimumWidth ( 800 );
window -> setMinimumHeight ( 600 );
rtabmap :: CloudViewer * viewer = new rtabmap :: CloudViewer ( window );
QVBoxLayout * layout = new QVBoxLayout ();
layout -> addWidget ( viewer );
viewer -> setCameraLockZ ( false );
window -> setLayout ( layout );
connect ( window , SIGNAL ( finished ( int )), viewer , SLOT ( clear ()));
window -> show ();
for ( std :: map < int , Transform >:: const_iterator iter = optimizedPoses . begin (); iter != optimizedPoses . end (); ++ iter )
{
rtabmap :: Transform pose = iter -> second ;
if ( ! pose . isNull ())
{
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( iter -> first , data );
data . uncompressData ();
2014-12-14 16:42:10 -05:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ;
2015-05-29 14:46:48 -04:00
UASSERT ( data . imageRaw (). empty () || data . imageRaw (). type () == CV_8UC3 || data . imageRaw (). type () == CV_8UC1 );
UASSERT ( data . depthOrRightRaw (). empty () || data . depthOrRightRaw (). type () == CV_8UC1 || data . depthOrRightRaw (). type () == CV_16UC1 || data . depthOrRightRaw (). type () == CV_32FC1 );
2016-05-31 19:09:49 -04:00
cloud = util3d :: cloudRGBFromSensorData ( data , decimation , maxDepth , 0 , 0 , ui_ -> parameters_toolbox -> getParameters ());
2015-05-29 14:46:48 -04:00
if ( cloud -> size ())
2014-12-14 16:42:10 -05:00
{
2015-05-29 14:46:48 -04:00
QColor color = Qt :: red ;
int mapId , weight ;
2016-01-07 19:39:21 -05:00
Transform odomPose , groundTruth ;
2015-05-29 14:46:48 -04:00
std :: string label ;
double stamp ;
2016-01-07 19:39:21 -05:00
if ( dbDriver_ -> getNodeInfo ( iter -> first , odomPose , mapId , weight , label , stamp , groundTruth ))
2014-12-14 16:42:10 -05:00
{
2015-05-29 14:46:48 -04:00
color = ( Qt :: GlobalColor )( mapId % 12 + 7 );
2014-12-14 16:42:10 -05:00
}
2015-05-29 14:46:48 -04:00
viewer -> addCloud ( uFormat ( "cloud%d" , iter -> first ), cloud , pose , color );
UINFO ( "Generated %d (%d points)" , iter -> first , cloud -> size ());
progressDialog . appendText ( QString ( "Generated %1 (%2 points)" ). arg ( iter -> first ). arg ( cloud -> size ()));
2014-12-14 16:42:10 -05:00
}
else
{
2015-05-29 14:46:48 -04:00
UINFO ( "Empty cloud %d" , iter -> first );
progressDialog . appendText ( QString ( "Empty cloud %1" ). arg ( iter -> first ));
2014-12-14 16:42:10 -05:00
}
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
}
progressDialog . setValue ( progressDialog . maximumSteps ());
}
else
{
QMessageBox :: critical ( this , tr ( "Error" ), tr ( "No neighbors found for node %1." ). arg ( ui_ -> spinBox_optimizationsFrom -> value ()));
}
}
}
}
2015-11-22 18:08:32 -05:00
void DatabaseViewer :: view3DLaserScans ()
{
if ( ! ids_ . size () || ! dbDriver_ )
{
QMessageBox :: warning ( this , tr ( "Cannot view 3D laser scans" ), tr ( "The database is empty..." ));
return ;
}
if ( graphes_ . empty ())
{
this -> updateGraphView ();
if ( graphes_ . empty () || ui_ -> horizontalSlider_iterations -> maximum () != ( int ) graphes_ . size () - 1 )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "No graph in database?!" ));
return ;
}
}
bool ok = false ;
int downsamplingStepSize = QInputDialog :: getInt ( this , tr ( "Downsampling?" ), tr ( "Downsample step size (1 = no filtering)" ), 1 , 1 , 99999 , 1 , & ok );
if ( ok )
{
std :: map < int , Transform > optimizedPoses = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
if ( ui_ -> groupBox_posefiltering -> isChecked ())
{
optimizedPoses = graph :: radiusPosesFiltering ( optimizedPoses ,
ui_ -> doubleSpinBox_posefilteringRadius -> value (),
ui_ -> doubleSpinBox_posefilteringAngle -> value () * CV_PI / 180.0 );
}
if ( optimizedPoses . size () > 0 )
{
rtabmap :: ProgressDialog progressDialog ( this );
progressDialog . setMaximumSteps (( int ) optimizedPoses . size ());
progressDialog . show ();
// create a window
QDialog * window = new QDialog ( this , Qt :: Window );
window -> setModal ( this -> isModal ());
window -> setWindowTitle ( tr ( "3D Laser Scans" ));
window -> setMinimumWidth ( 800 );
window -> setMinimumHeight ( 600 );
rtabmap :: CloudViewer * viewer = new rtabmap :: CloudViewer ( window );
QVBoxLayout * layout = new QVBoxLayout ();
layout -> addWidget ( viewer );
viewer -> setCameraLockZ ( false );
window -> setLayout ( layout );
connect ( window , SIGNAL ( finished ( int )), viewer , SLOT ( clear ()));
window -> show ();
for ( std :: map < int , Transform >:: const_iterator iter = optimizedPoses . begin (); iter != optimizedPoses . end (); ++ iter )
{
rtabmap :: Transform pose = iter -> second ;
if ( ! pose . isNull ())
{
SensorData data ;
dbDriver_ -> getNodeData ( iter -> first , data );
cv :: Mat scan ;
data . uncompressDataConst ( 0 , 0 , & scan );
2016-09-07 17:14:09 -04:00
if ( ! scan . empty ())
2015-11-22 18:08:32 -05:00
{
2016-09-07 17:14:09 -04:00
if ( downsamplingStepSize > 1 )
{
scan = util3d :: downsample ( scan , downsamplingStepSize );
}
2015-11-22 18:08:32 -05:00
QColor color = Qt :: red ;
int mapId , weight ;
2016-01-07 19:39:21 -05:00
Transform odomPose , groundTruth ;
2015-11-22 18:08:32 -05:00
std :: string label ;
double stamp ;
2016-01-07 19:39:21 -05:00
if ( dbDriver_ -> getNodeInfo ( iter -> first , odomPose , mapId , weight , label , stamp , groundTruth ))
2015-11-22 18:08:32 -05:00
{
color = ( Qt :: GlobalColor )( mapId % 12 + 7 );
}
2016-09-07 17:14:09 -04:00
if ( scan . channels () == 6 )
{
pcl :: PointCloud < pcl :: PointNormal >:: Ptr cloud ;
cloud = util3d :: laserScanToPointCloudNormal ( scan , data . laserScanInfo (). localTransform ());
viewer -> addCloud ( uFormat ( "cloud%d" , iter -> first ), cloud , pose , color );
}
else
{
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloud ;
cloud = util3d :: laserScanToPointCloud ( scan , data . laserScanInfo (). localTransform ());
viewer -> addCloud ( uFormat ( "cloud%d" , iter -> first ), cloud , pose , color );
}
UINFO ( "Generated %d (%d points)" , iter -> first , scan . cols );
progressDialog . appendText ( QString ( "Generated %1 (%2 points)" ). arg ( iter -> first ). arg ( scan . cols ));
2015-11-22 18:08:32 -05:00
}
else
{
2016-09-07 17:14:09 -04:00
UINFO ( "Empty scan %d" , iter -> first );
progressDialog . appendText ( QString ( "Empty scan %1" ). arg ( iter -> first ));
2015-11-22 18:08:32 -05:00
}
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
}
progressDialog . setValue ( progressDialog . maximumSteps ());
}
else
{
QMessageBox :: critical ( this , tr ( "Error" ), tr ( "No neighbors found for node %1." ). arg ( ui_ -> spinBox_optimizationsFrom -> value ()));
}
}
}
2014-12-14 16:42:10 -05:00
void DatabaseViewer :: generate3DMap ()
2012-03-03 01:46:30 +00:00
{
2015-10-16 14:54:47 -04:00
if ( ! ids_ . size () || ! dbDriver_ )
2012-03-03 01:46:30 +00:00
{
2013-12-11 00:12:44 +00:00
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "The database is empty..." ));
2012-03-03 01:46:30 +00:00
return ;
}
2015-11-27 15:36:57 -05:00
if ( graphes_ . empty ())
{
this -> updateGraphView ();
if ( graphes_ . empty () || ui_ -> horizontalSlider_iterations -> maximum () != ( int ) graphes_ . size () - 1 )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "No graph in database?!" ));
return ;
}
}
2013-12-11 00:12:44 +00:00
bool ok = false ;
2014-12-14 16:42:10 -05:00
QStringList items ;
items . append ( "1" );
items . append ( "2" );
items . append ( "4" );
items . append ( "8" );
items . append ( "16" );
2016-10-30 16:03:23 -04:00
items . append ( "-2" );
items . append ( "-4" );
items . append ( "-8" );
items . append ( "-16" );
QString item = QInputDialog :: getItem ( this , tr ( "Decimation?" ), tr ( "Image decimation. Negative decimation is done from RGB size instead of depth size (if depth is smaller than RGB, it may be interpolated depending of the decimation value)." ), items , 2 , false , & ok );
2013-12-11 00:12:44 +00:00
if ( ok )
2012-03-03 01:46:30 +00:00
{
2016-05-05 18:25:26 -04:00
int decimation = item . toInt ();
2015-11-22 18:08:32 -05:00
double maxDepth = QInputDialog :: getDouble ( this , tr ( "Camera depth?" ), tr ( "Maximum depth (m, 0=no max):" ), 4.0 , 0 , 100 , 2 , & ok );
2013-12-11 00:12:44 +00:00
if ( ok )
{
2015-11-22 18:08:32 -05:00
QMessageBox :: StandardButton b = QMessageBox :: question (
this ,
tr ( "Assembling?" ),
tr ( "Do you want to assemble all the point clouds (creating only one file with a density of 1pt/cm)?" ),
QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: Yes );
bool assemble = b == QMessageBox :: Yes ;
QString path ;
if ( assemble )
{
path = QFileDialog :: getSaveFileName ( this , tr ( "Save point cloud" ),
pathDatabase_ + QDir :: separator () + "cloud.ply" ,
tr ( "Point Cloud (*.ply *.pcd)" ));
}
else
{
path = QFileDialog :: getExistingDirectory ( this , tr ( "Save directory" ), pathDatabase_ );
}
2014-12-14 16:42:10 -05:00
if ( ! path . isEmpty ())
2013-12-11 00:12:44 +00:00
{
2015-03-16 18:22:54 -04:00
std :: map < int , Transform > optimizedPoses = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
if ( ui_ -> groupBox_posefiltering -> isChecked ())
{
optimizedPoses = graph :: radiusPosesFiltering ( optimizedPoses ,
ui_ -> doubleSpinBox_posefilteringRadius -> value (),
ui_ -> doubleSpinBox_posefilteringAngle -> value () * CV_PI / 180.0 );
}
2014-12-14 16:42:10 -05:00
if ( optimizedPoses . size () > 0 )
2013-12-11 00:12:44 +00:00
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog progressDialog ;
2014-12-14 16:42:10 -05:00
progressDialog . setMaximumSteps (( int ) optimizedPoses . size ());
2014-08-11 15:23:06 +00:00
progressDialog . show ();
2015-11-22 18:08:32 -05:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr assembledCloud ( new pcl :: PointCloud < pcl :: PointXYZRGB > );
2014-12-14 16:42:10 -05:00
for ( std :: map < int , Transform >:: const_iterator iter = optimizedPoses . begin (); iter != optimizedPoses . end (); ++ iter )
2013-12-11 00:12:44 +00:00
{
2014-12-14 16:42:10 -05:00
const rtabmap :: Transform & pose = iter -> second ;
2014-08-11 15:23:06 +00:00
if ( ! pose . isNull ())
2013-12-11 00:12:44 +00:00
{
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( iter -> first , data );
data . uncompressData ();
2014-08-11 15:23:06 +00:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ;
2015-05-29 14:46:48 -04:00
UASSERT ( data . imageRaw (). empty () || data . imageRaw (). type () == CV_8UC3 || data . imageRaw (). type () == CV_8UC1 );
UASSERT ( data . depthOrRightRaw (). empty () || data . depthOrRightRaw (). type () == CV_8UC1 || data . depthOrRightRaw (). type () == CV_16UC1 || data . depthOrRightRaw (). type () == CV_32FC1 );
2016-04-12 15:14:04 -04:00
pcl :: IndicesPtr validIndices ( new std :: vector < int > );
2016-05-31 19:09:49 -04:00
cloud = util3d :: cloudRGBFromSensorData ( data , decimation , maxDepth , 0 , validIndices . get (), ui_ -> parameters_toolbox -> getParameters ());
2015-11-22 18:08:32 -05:00
if ( assemble )
2014-10-13 19:10:22 +00:00
{
2015-11-22 18:08:32 -05:00
if ( cloud -> size ())
{
2016-04-12 15:14:04 -04:00
cloud = util3d :: voxelize ( cloud , validIndices , 0.01 );
if ( cloud -> size ())
2015-11-22 18:08:32 -05:00
{
2016-04-12 15:14:04 -04:00
cloud = rtabmap :: util3d :: transformPointCloud ( cloud , pose );
if ( assembledCloud -> size () == 0 )
{
* assembledCloud = * cloud ;
}
else
{
* assembledCloud += * cloud ;
}
2015-11-22 18:08:32 -05:00
}
}
UINFO ( "Created cloud %d (%d points)" , iter -> first , ( int ) cloud -> size ());
progressDialog . appendText ( QString ( "Created cloud %1 (%2 points)" ). arg ( iter -> first ). arg ( cloud -> size ()));
2014-10-13 19:10:22 +00:00
}
else
{
2015-11-22 18:08:32 -05:00
std :: string name = uFormat ( "%s/node%d.pcd" , path . toStdString (). c_str (), iter -> first );
if ( cloud -> size ())
{
cloud = rtabmap :: util3d :: transformPointCloud ( cloud , pose );
pcl :: io :: savePCDFile ( name , * cloud );
UINFO ( "Saved %s (%d points)" , name . c_str (), cloud -> size ());
progressDialog . appendText ( QString ( "Saved %1 (%2 points)" ). arg ( name . c_str ()). arg ( cloud -> size ()));
}
else
{
UINFO ( "Ignored empty cloud %s" , name . c_str ());
progressDialog . appendText ( QString ( "Ignored empty cloud %1" ). arg ( name . c_str ()));
}
2014-10-13 19:10:22 +00:00
}
2014-08-11 15:23:06 +00:00
progressDialog . incrementStep ();
QApplication :: processEvents ();
2013-12-11 00:12:44 +00:00
}
2014-02-11 23:04:22 +00:00
}
2014-12-14 16:42:10 -05:00
2015-11-22 18:08:32 -05:00
if ( assemble && assembledCloud -> size ())
{
//voxelize by default to 1 cm
progressDialog . appendText ( QString ( "Voxelize assembled cloud (%1 points)" ). arg ( assembledCloud -> size ()));
QApplication :: processEvents ();
assembledCloud = util3d :: voxelize ( assembledCloud , 0.01 );
if ( QFileInfo ( path ). suffix () == "ply" )
{
pcl :: io :: savePLYFile ( path . toStdString (), * assembledCloud );
}
else
{
pcl :: io :: savePCDFile ( path . toStdString (), * assembledCloud );
}
progressDialog . appendText ( QString ( "Saved %1 (%2 points)" ). arg ( path ). arg ( assembledCloud -> size ()));
QApplication :: processEvents ();
}
2014-12-14 16:42:10 -05:00
QMessageBox :: information ( this , tr ( "Finished" ), tr ( "%1 clouds generated to %2." ). arg ( optimizedPoses . size ()). arg ( path ));
2015-11-22 18:08:32 -05:00
progressDialog . setValue ( progressDialog . maximumSteps ());
2014-08-11 15:23:06 +00:00
}
else
{
QMessageBox :: critical ( this , tr ( "Error" ), tr ( "No neighbors found for node %1." ). arg ( ui_ -> spinBox_optimizationsFrom -> value ()));
2014-02-11 23:04:22 +00:00
}
}
}
}
}
2015-11-22 18:08:32 -05:00
void DatabaseViewer :: generate3DLaserScans ()
{
if ( ! ids_ . size () || ! dbDriver_ )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "The database is empty..." ));
return ;
}
bool ok = false ;
int downsamplingStepSize = QInputDialog :: getInt ( this , tr ( "Downsampling?" ), tr ( "Downsample step size (1 = no filtering)" ), 1 , 1 , 99999 , 1 , & ok );
if ( ok )
{
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save point cloud" ),
pathDatabase_ + QDir :: separator () + "cloud.ply" ,
tr ( "Point Cloud (*.ply *.pcd)" ));
if ( ! path . isEmpty ())
{
std :: map < int , Transform > optimizedPoses = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
if ( ui_ -> groupBox_posefiltering -> isChecked ())
{
optimizedPoses = graph :: radiusPosesFiltering ( optimizedPoses ,
ui_ -> doubleSpinBox_posefilteringRadius -> value (),
ui_ -> doubleSpinBox_posefilteringAngle -> value () * CV_PI / 180.0 );
}
if ( optimizedPoses . size () > 0 )
{
rtabmap :: ProgressDialog progressDialog ;
progressDialog . setMaximumSteps (( int ) optimizedPoses . size ());
progressDialog . show ();
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr assembledCloud ( new pcl :: PointCloud < pcl :: PointXYZ > );
for ( std :: map < int , Transform >:: const_iterator iter = optimizedPoses . begin (); iter != optimizedPoses . end (); ++ iter )
{
const rtabmap :: Transform & pose = iter -> second ;
if ( ! pose . isNull ())
{
SensorData data ;
dbDriver_ -> getNodeData ( iter -> first , data );
cv :: Mat scan ;
data . uncompressDataConst ( 0 , 0 , & scan );
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloud ;
UASSERT ( scan . empty () || scan . type () == CV_32FC2 || scan . type () == CV_32FC3 );
if ( downsamplingStepSize > 1 )
{
scan = util3d :: downsample ( scan , downsamplingStepSize );
}
2016-08-24 12:15:32 -04:00
cloud = util3d :: laserScanToPointCloud ( scan , data . laserScanInfo (). localTransform ());
2015-11-22 18:08:32 -05:00
if ( cloud -> size ())
{
cloud = rtabmap :: util3d :: transformPointCloud ( cloud , pose );
if ( assembledCloud -> size () == 0 )
{
* assembledCloud = * cloud ;
}
else
{
* assembledCloud += * cloud ;
}
}
UINFO ( "Created cloud %d (%d points)" , iter -> first , ( int ) cloud -> size ());
progressDialog . appendText ( QString ( "Created cloud %1 (%2 points)" ). arg ( iter -> first ). arg ( cloud -> size ()));
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
}
if ( assembledCloud -> size ())
{
//voxelize by default to 1 cm
progressDialog . appendText ( QString ( "Voxelize assembled cloud (%1 points)" ). arg ( assembledCloud -> size ()));
QApplication :: processEvents ();
assembledCloud = util3d :: voxelize ( assembledCloud , 0.01 );
if ( QFileInfo ( path ). suffix () == "ply" )
{
pcl :: io :: savePLYFile ( path . toStdString (), * assembledCloud );
}
else
{
pcl :: io :: savePCDFile ( path . toStdString (), * assembledCloud );
}
progressDialog . appendText ( QString ( "Saved %1 (%2 points)" ). arg ( path ). arg ( assembledCloud -> size ()));
QApplication :: processEvents ();
}
QMessageBox :: information ( this , tr ( "Finished" ), tr ( "%1 clouds generated to %2." ). arg ( optimizedPoses . size ()). arg ( path ));
progressDialog . setValue ( progressDialog . maximumSteps ());
}
else
{
QMessageBox :: critical ( this , tr ( "Error" ), tr ( "No neighbors found for node %1." ). arg ( ui_ -> spinBox_optimizationsFrom -> value ()));
}
}
}
}
2014-08-11 15:23:06 +00:00
void DatabaseViewer :: detectMoreLoopClosures ()
{
2015-11-27 15:36:57 -05:00
if ( graphes_ . empty ())
{
this -> updateGraphView ();
if ( graphes_ . empty () || ui_ -> horizontalSlider_iterations -> maximum () != ( int ) graphes_ . size () - 1 )
{
QMessageBox :: warning ( this , tr ( "Cannot generate a graph" ), tr ( "No graph in database?!" ));
return ;
}
}
2014-12-14 16:42:10 -05:00
const std :: map < int , Transform > & optimizedPoses = graphes_ . back ();
2014-10-30 14:43:43 +00:00
2015-03-04 17:27:44 -05:00
int iterations = ui_ -> spinBox_detectMore_iterations -> value ();
2014-10-30 14:43:43 +00:00
UASSERT ( iterations > 0 );
2014-08-11 15:23:06 +00:00
int added = 0 ;
2014-10-30 14:43:43 +00:00
for ( int n = 0 ; n < iterations ; ++ n )
2014-08-11 15:23:06 +00:00
{
2014-10-30 14:43:43 +00:00
UINFO ( "iteration %d/%d" , n + 1 , iterations );
2015-02-06 13:52:02 -05:00
std :: multimap < int , int > clusters = rtabmap :: graph :: radiusPosesClustering (
2014-10-30 14:43:43 +00:00
optimizedPoses ,
ui_ -> doubleSpinBox_detectMore_radius -> value (),
ui_ -> doubleSpinBox_detectMore_angle -> value () * CV_PI / 180.0 );
std :: set < int > addedLinks ;
for ( std :: multimap < int , int >:: iterator iter = clusters . begin (); iter != clusters . end (); ++ iter )
2014-08-11 15:23:06 +00:00
{
2014-10-30 14:43:43 +00:00
int from = iter -> first ;
int to = iter -> second ;
if ( from < to )
2014-08-11 15:23:06 +00:00
{
2014-10-30 14:43:43 +00:00
from = iter -> second ;
to = iter -> first ;
}
if ( ! findActiveLink ( from , to ). isValid () && ! containsLink ( linksRemoved_ , from , to ) &&
addedLinks . find ( from ) == addedLinks . end () && addedLinks . find ( to ) == addedLinks . end ())
{
2014-12-14 16:42:10 -05:00
if ( addConstraint ( from , to , true , false ))
2014-10-30 14:43:43 +00:00
{
UINFO ( "Added new loop closure between %d and %d." , from , to );
++ added ;
addedLinks . insert ( from );
addedLinks . insert ( to );
}
2014-08-11 15:23:06 +00:00
}
}
2014-10-30 14:43:43 +00:00
UINFO ( "Iteration %d/%d: added %d loop closures." , n + 1 , iterations , ( int ) addedLinks . size () / 2 );
if ( addedLinks . size () == 0 )
{
break ;
}
2014-08-11 15:23:06 +00:00
}
2014-12-14 16:42:10 -05:00
if ( added )
{
this -> updateGraphView ();
}
2014-10-30 14:43:43 +00:00
UINFO ( "Total added %d loop closures." , added );
2014-08-11 15:23:06 +00:00
}
void DatabaseViewer :: refineAllNeighborLinks ()
{
if ( neighborLinks_ . size ())
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog progressDialog ( this );
2014-08-11 15:23:06 +00:00
progressDialog . setMaximumSteps ( neighborLinks_ . size ());
progressDialog . show ();
for ( int i = 0 ; i < neighborLinks_ . size (); ++ i )
{
int from = neighborLinks_ [ i ]. from ();
int to = neighborLinks_ [ i ]. to ();
2015-05-03 18:16:55 -04:00
this -> refineConstraint ( neighborLinks_ [ i ]. from (), neighborLinks_ [ i ]. to (), true , false );
2014-08-11 15:23:06 +00:00
progressDialog . appendText ( tr ( "Refined link %1->%2 (%3/%4)" ). arg ( from ). arg ( to ). arg ( i + 1 ). arg ( neighborLinks_ . size ()));
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
2014-12-14 16:42:10 -05:00
this -> updateGraphView ();
2014-08-11 15:23:06 +00:00
progressDialog . setValue ( progressDialog . maximumSteps ());
progressDialog . appendText ( "Refining links finished!" );
}
}
void DatabaseViewer :: refineAllLoopClosureLinks ()
{
if ( loopLinks_ . size ())
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog progressDialog ( this );
2014-08-11 15:23:06 +00:00
progressDialog . setMaximumSteps ( loopLinks_ . size ());
progressDialog . show ();
for ( int i = 0 ; i < loopLinks_ . size (); ++ i )
{
int from = loopLinks_ [ i ]. from ();
int to = loopLinks_ [ i ]. to ();
2015-05-03 18:16:55 -04:00
this -> refineConstraint ( loopLinks_ [ i ]. from (), loopLinks_ [ i ]. to (), true , false );
2014-08-11 15:23:06 +00:00
progressDialog . appendText ( tr ( "Refined link %1->%2 (%3/%4)" ). arg ( from ). arg ( to ). arg ( i + 1 ). arg ( loopLinks_ . size ()));
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
2014-12-14 16:42:10 -05:00
this -> updateGraphView ();
2014-08-11 15:23:06 +00:00
progressDialog . setValue ( progressDialog . maximumSteps ());
progressDialog . appendText ( "Refining links finished!" );
}
}
2014-11-03 19:14:02 +00:00
void DatabaseViewer :: refineVisuallyAllNeighborLinks ()
{
if ( neighborLinks_ . size ())
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog progressDialog ( this );
2014-11-03 19:14:02 +00:00
progressDialog . setMaximumSteps ( neighborLinks_ . size ());
progressDialog . show ();
for ( int i = 0 ; i < neighborLinks_ . size (); ++ i )
{
int from = neighborLinks_ [ i ]. from ();
int to = neighborLinks_ [ i ]. to ();
2015-05-03 18:16:55 -04:00
this -> refineConstraintVisually ( neighborLinks_ [ i ]. from (), neighborLinks_ [ i ]. to (), true , false );
2014-11-03 19:14:02 +00:00
progressDialog . appendText ( tr ( "Refined link %1->%2 (%3/%4)" ). arg ( from ). arg ( to ). arg ( i + 1 ). arg ( neighborLinks_ . size ()));
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
2014-12-14 16:42:10 -05:00
this -> updateGraphView ();
2014-11-03 19:14:02 +00:00
progressDialog . setValue ( progressDialog . maximumSteps ());
progressDialog . appendText ( "Refining links finished!" );
}
}
void DatabaseViewer :: refineVisuallyAllLoopClosureLinks ()
{
if ( loopLinks_ . size ())
{
2015-08-25 10:32:27 -04:00
rtabmap :: ProgressDialog progressDialog ( this );
2014-11-03 19:14:02 +00:00
progressDialog . setMaximumSteps ( loopLinks_ . size ());
progressDialog . show ();
for ( int i = 0 ; i < loopLinks_ . size (); ++ i )
{
int from = loopLinks_ [ i ]. from ();
int to = loopLinks_ [ i ]. to ();
2015-05-03 18:16:55 -04:00
this -> refineConstraintVisually ( loopLinks_ [ i ]. from (), loopLinks_ [ i ]. to (), true , false );
2014-11-03 19:14:02 +00:00
progressDialog . appendText ( tr ( "Refined link %1->%2 (%3/%4)" ). arg ( from ). arg ( to ). arg ( i + 1 ). arg ( loopLinks_ . size ()));
progressDialog . incrementStep ();
QApplication :: processEvents ();
}
2014-12-14 16:42:10 -05:00
this -> updateGraphView ();
2014-11-03 19:14:02 +00:00
progressDialog . setValue ( progressDialog . maximumSteps ());
progressDialog . appendText ( "Refining links finished!" );
}
}
2015-05-05 17:38:38 -04:00
void DatabaseViewer :: resetAllChanges ()
{
linksAdded_ . clear ();
linksRefined_ . clear ();
linksRemoved_ . clear ();
2016-08-25 23:47:01 -04:00
generatedLocalMaps_ . clear ();
generatedLocalMapsInfo_ . clear ();
2015-05-05 17:38:38 -04:00
updateLoopClosuresSlider ();
this -> updateGraphView ();
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: sliderAValueChanged ( int value )
2011-06-05 14:45:39 +00:00
{
2012-06-24 17:19:34 +00:00
this -> update ( value ,
ui_ -> label_indexA ,
ui_ -> label_parentsA ,
ui_ -> label_childrenA ,
2015-02-27 15:24:15 -05:00
ui_ -> label_weightA ,
ui_ -> label_labelA ,
ui_ -> label_stampA ,
2012-06-24 17:19:34 +00:00
ui_ -> graphicsView_A ,
2016-05-05 18:25:26 -04:00
cloudViewerA_ ,
2015-05-03 18:16:55 -04:00
ui_ -> label_idA ,
ui_ -> label_mapA ,
2015-05-20 18:07:10 -04:00
ui_ -> label_poseA ,
2016-04-07 12:15:58 -04:00
ui_ -> label_calibA ,
2015-05-03 18:16:55 -04:00
true );
2011-06-05 14:45:39 +00:00
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: sliderBValueChanged ( int value )
2011-06-05 14:45:39 +00:00
{
2012-06-24 17:19:34 +00:00
this -> update ( value ,
ui_ -> label_indexB ,
ui_ -> label_parentsB ,
ui_ -> label_childrenB ,
2015-02-27 15:24:15 -05:00
ui_ -> label_weightB ,
ui_ -> label_labelB ,
ui_ -> label_stampB ,
2012-06-24 17:19:34 +00:00
ui_ -> graphicsView_B ,
2016-05-05 18:25:26 -04:00
cloudViewerB_ ,
2015-05-03 18:16:55 -04:00
ui_ -> label_idB ,
ui_ -> label_mapB ,
2015-05-20 18:07:10 -04:00
ui_ -> label_poseB ,
2016-04-07 12:15:58 -04:00
ui_ -> label_calibB ,
2015-05-03 18:16:55 -04:00
true );
2012-06-24 17:19:34 +00:00
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: update ( int value ,
2012-06-24 17:19:34 +00:00
QLabel * labelIndex ,
QLabel * labelParents ,
QLabel * labelChildren ,
2015-02-27 15:24:15 -05:00
QLabel * weight ,
QLabel * label ,
QLabel * stamp ,
2013-12-11 00:12:44 +00:00
rtabmap :: ImageView * view ,
2015-03-04 17:27:44 -05:00
rtabmap :: CloudViewer * view3D ,
2014-10-14 21:06:30 +00:00
QLabel * labelId ,
2015-05-03 18:16:55 -04:00
QLabel * labelMapId ,
2015-05-20 18:07:10 -04:00
QLabel * labelPose ,
2016-04-07 12:15:58 -04:00
QLabel * labelCalib ,
2014-10-14 21:06:30 +00:00
bool updateConstraintView )
2012-06-24 17:19:34 +00:00
{
UTimer timer ;
labelIndex -> setText ( QString :: number ( value ));
labelParents -> clear ();
labelChildren -> clear ();
2015-02-27 15:24:15 -05:00
weight -> clear ();
label -> clear ();
2015-05-03 18:16:55 -04:00
labelMapId -> clear ();
2015-05-20 18:07:10 -04:00
labelPose -> clear ();
2015-02-27 15:24:15 -05:00
stamp -> clear ();
2016-04-07 12:15:58 -04:00
labelCalib -> clear ();
2014-08-11 15:23:06 +00:00
QRectF rect ;
2011-06-05 14:45:39 +00:00
if ( value >= 0 && value < ids_ . size ())
{
2013-12-11 00:12:44 +00:00
view -> clear ();
2011-06-05 14:45:39 +00:00
int id = ids_ . at ( value );
2014-07-14 19:16:26 +00:00
int mapId = - 1 ;
2012-06-24 17:19:34 +00:00
labelId -> setText ( QString :: number ( id ));
2011-06-05 14:45:39 +00:00
if ( id > 0 )
{
2012-03-03 01:46:30 +00:00
//image
2011-06-05 14:45:39 +00:00
QImage img ;
2013-12-11 00:12:44 +00:00
QImage imgDepth ;
2015-10-16 14:54:47 -04:00
if ( dbDriver_ )
2011-06-05 14:45:39 +00:00
{
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( id , data );
data . uncompressData ();
2015-05-29 14:46:48 -04:00
if ( ! data . imageRaw (). empty ())
2011-06-05 14:45:39 +00:00
{
2016-02-15 19:35:24 -05:00
img = uCvMat2QImage ( ui_ -> label_indexB == labelIndex ? data . imageRaw () : data . imageRaw ());
2011-06-05 14:45:39 +00:00
}
2015-05-29 14:46:48 -04:00
if ( ! data . depthOrRightRaw (). empty ())
2013-12-11 00:12:44 +00:00
{
2016-02-15 19:35:24 -05:00
cv :: Mat depth = data . depthOrRightRaw ();
if ( ! data . depthRaw (). empty ())
{
if ( ui_ -> spinBox_mesh_fillDepthHoles -> value () > 0 )
{
depth = util2d :: fillDepthHoles ( depth , ui_ -> spinBox_mesh_fillDepthHoles -> value (), float ( ui_ -> spinBox_mesh_depthError -> value ()) / 100.0f );
}
}
imgDepth = uCvMat2QImage ( depth );
2013-12-11 00:12:44 +00:00
}
2012-03-03 01:46:30 +00:00
2015-10-16 14:54:47 -04:00
std :: list < int > ids ;
ids . push_back ( id );
std :: list < Signature *> signatures ;
dbDriver_ -> loadSignatures ( ids , signatures );
2015-05-29 14:46:48 -04:00
2015-10-16 14:54:47 -04:00
if ( signatures . size () && signatures . front () != 0 && signatures . front () -> getWords (). size ())
2012-03-03 01:46:30 +00:00
{
2015-10-16 14:54:47 -04:00
view -> setFeatures ( signatures . front () -> getWords (), data . depthOrRightRaw (). type () == CV_8UC1 ? cv :: Mat () : data . depthOrRightRaw (), Qt :: yellow );
2012-03-03 01:46:30 +00:00
}
2014-07-14 19:16:26 +00:00
2016-01-07 19:39:21 -05:00
Transform odomPose , g ;
2015-03-06 16:14:09 -05:00
int w ;
std :: string l ;
double s ;
2016-01-07 19:39:21 -05:00
dbDriver_ -> getNodeInfo ( id , odomPose , mapId , w , l , s , g );
2014-11-04 18:58:40 +00:00
2015-05-29 14:46:48 -04:00
weight -> setNum ( w );
label -> setText ( l . c_str ());
2016-02-15 19:35:24 -05:00
float x , y , z , roll , pitch , yaw ;
odomPose . getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
labelPose -> setText ( QString ( "%1xyz=(%2,%3,%4) \n rpy=(%5,%6,%7)" ). arg ( odomPose . isIdentity () ? "* " : "" ). arg ( x ). arg ( y ). arg ( z ). arg ( roll ). arg ( pitch ). arg ( yaw ));
2015-05-29 14:46:48 -04:00
if ( s != 0.0 )
2015-02-27 15:24:15 -05:00
{
2015-05-29 14:46:48 -04:00
stamp -> setText ( QDateTime :: fromMSecsSinceEpoch ( s * 1000.0 ). toString ( "dd.MM.yyyy hh:mm:ss.zzz" ));
2015-02-27 15:24:15 -05:00
}
2016-04-07 12:15:58 -04:00
if ( data . cameraModels (). size () || data . stereoCameraModel (). isValidForProjection ())
{
if ( data . cameraModels (). size ())
{
if ( ! data . depthRaw (). empty () && data . depthRaw (). cols != data . imageRaw (). cols && data . imageRaw (). cols )
{
labelCalib -> setText ( tr ( "%1 %2x%3 [%8x%9] fx=%4 fy=%5 cx=%6 cy=%7" )
. arg ( data . cameraModels (). size ())
. arg ( data . cameraModels ()[ 0 ]. imageWidth () > 0 ? data . cameraModels ()[ 0 ]. imageWidth () : data . imageRaw (). cols / data . cameraModels (). size ())
. arg ( data . cameraModels ()[ 0 ]. imageHeight () > 0 ? data . cameraModels ()[ 0 ]. imageHeight () : data . imageRaw (). rows )
. arg ( data . cameraModels ()[ 0 ]. fx ())
. arg ( data . cameraModels ()[ 0 ]. fy ())
. arg ( data . cameraModels ()[ 0 ]. cx ())
. arg ( data . cameraModels ()[ 0 ]. cy ())
. arg ( data . depthRaw (). cols / data . cameraModels (). size ())
. arg ( data . depthRaw (). rows ));
}
else
{
labelCalib -> setText ( tr ( "%1 %2x%3 fx=%4 fy=%5 cx=%6 cy=%7" )
. arg ( data . cameraModels (). size ())
. arg ( data . cameraModels ()[ 0 ]. imageWidth () > 0 ? data . cameraModels ()[ 0 ]. imageWidth () : data . imageRaw (). cols / data . cameraModels (). size ())
. arg ( data . cameraModels ()[ 0 ]. imageHeight () > 0 ? data . cameraModels ()[ 0 ]. imageHeight () : data . imageRaw (). rows )
. arg ( data . cameraModels ()[ 0 ]. fx ())
. arg ( data . cameraModels ()[ 0 ]. fy ())
. arg ( data . cameraModels ()[ 0 ]. cx ())
. arg ( data . cameraModels ()[ 0 ]. cy ()));
}
}
else
{
//stereo
labelCalib -> setText ( tr ( "%1x%2 fx=%3 fy=%4 cx=%5 cy=%6 baseline=%7m" )
. arg ( data . stereoCameraModel (). left (). imageWidth () > 0 ? data . stereoCameraModel (). left (). imageWidth () : data . imageRaw (). cols )
. arg ( data . stereoCameraModel (). left (). imageHeight () > 0 ? data . stereoCameraModel (). left (). imageHeight () : data . imageRaw (). rows )
. arg ( data . stereoCameraModel (). left (). fx ())
. arg ( data . stereoCameraModel (). left (). fy ())
. arg ( data . stereoCameraModel (). left (). cx ())
. arg ( data . stereoCameraModel (). left (). cy ())
. arg ( data . stereoCameraModel (). baseline ()));
}
}
else
{
labelCalib -> setText ( "NA" );
}
2015-02-27 15:24:15 -05:00
2014-11-04 18:58:40 +00:00
//stereo
2015-05-29 14:46:48 -04:00
if ( ! data . depthOrRightRaw (). empty () && data . depthOrRightRaw (). type () == CV_8UC1 )
2014-11-04 18:58:40 +00:00
{
this -> updateStereo ( & data );
}
2015-06-15 14:44:44 -04:00
else
{
2016-05-05 18:25:26 -04:00
stereoViewer_ -> clear ();
2015-06-15 14:44:44 -04:00
ui_ -> graphicsView_stereo -> clear ();
}
2015-03-04 17:27:44 -05:00
// 3d view
2016-09-03 18:59:43 -04:00
if ( view3D -> isVisible ())
2015-03-04 17:27:44 -05:00
{
2016-08-30 18:22:29 -04:00
Transform pose = Transform :: getIdentity ();
if ( signatures . size ())
{
float x , y , z , roll , pitch , yaw ;
( * signatures . begin ()) -> getPose (). getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
pose = Transform ( 0 , 0 , z , roll , pitch , 0 );
}
2016-08-05 15:30:38 -04:00
view3D -> removeAllFrustums ();
2016-09-03 18:59:43 -04:00
view3D -> removeCloud ( "mesh" );
view3D -> removeCloud ( "cloud" );
view3D -> removeCloud ( "scan" );
2016-08-26 13:02:00 -04:00
view3D -> removeCloud ( "map" );
2016-08-30 21:15:55 -04:00
view3D -> removeCloud ( "ground" );
view3D -> removeCloud ( "obstacles" );
2016-09-03 18:59:43 -04:00
if ( ui_ -> checkBox_showCloud -> isChecked () || ui_ -> checkBox_showMesh -> isChecked ())
2015-03-04 17:27:44 -05:00
{
2016-09-03 18:59:43 -04:00
if ( ! data . depthOrRightRaw (). empty ())
2015-10-24 17:03:44 -04:00
{
2016-09-03 18:59:43 -04:00
if ( ! data . imageRaw (). empty ())
2016-02-15 19:35:24 -05:00
{
2016-09-03 18:59:43 -04:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ;
if ( ! data . depthRaw (). empty () && data . cameraModels (). size () == 1 )
2016-02-15 19:35:24 -05:00
{
2016-09-03 18:59:43 -04:00
cv :: Mat depth = data . depthRaw ();
if ( ui_ -> spinBox_mesh_fillDepthHoles -> value () > 0 )
{
depth = util2d :: fillDepthHoles ( depth , ui_ -> spinBox_mesh_fillDepthHoles -> value (), float ( ui_ -> spinBox_mesh_depthError -> value ()) / 100.0f );
}
cloud = util3d :: cloudFromDepthRGB (
data . imageRaw (),
depth ,
data . cameraModels ()[ 0 ]);
if ( cloud -> size ())
{
cloud = util3d :: transformPointCloud ( cloud , data . cameraModels ()[ 0 ]. localTransform ());
}
2016-08-05 15:30:38 -04:00
2016-02-15 19:35:24 -05:00
}
else
{
2016-09-03 18:59:43 -04:00
cloud = util3d :: cloudRGBFromSensorData ( data , 1 , 0 , 0 , 0 , ui_ -> parameters_toolbox -> getParameters ());
2016-02-15 19:35:24 -05:00
}
2016-09-03 18:59:43 -04:00
if ( cloud -> size ())
{
if ( ui_ -> checkBox_showMesh -> isChecked () && ! cloud -> is_dense )
{
Eigen :: Vector3f viewpoint ( 0.0f , 0.0f , 0.0f );
if ( data . cameraModels (). size () && ! data . cameraModels ()[ 0 ]. localTransform (). isNull ())
{
viewpoint [ 0 ] = data . cameraModels ()[ 0 ]. localTransform (). x ();
viewpoint [ 1 ] = data . cameraModels ()[ 0 ]. localTransform (). y ();
viewpoint [ 2 ] = data . cameraModels ()[ 0 ]. localTransform (). z ();
}
else if ( ! data . stereoCameraModel (). localTransform (). isNull ())
{
viewpoint [ 0 ] = data . stereoCameraModel (). localTransform (). x ();
viewpoint [ 1 ] = data . stereoCameraModel (). localTransform (). y ();
viewpoint [ 2 ] = data . stereoCameraModel (). localTransform (). z ();
}
std :: vector < pcl :: Vertices > polygons = util3d :: organizedFastMesh (
cloud ,
float ( ui_ -> spinBox_mesh_angleTolerance -> value ()) * M_PI / 180.0f ,
ui_ -> checkBox_mesh_quad -> isChecked (),
ui_ -> spinBox_mesh_triangleSize -> value (),
viewpoint );
if ( ui_ -> spinBox_mesh_minClusterSize -> value ())
{
// filter polygons
std :: vector < std :: set < int > > neighbors ;
std :: vector < std :: set < int > > vertexToPolygons ;
util3d :: createPolygonIndexes ( polygons ,
cloud -> size (),
neighbors ,
vertexToPolygons );
std :: list < std :: list < int > > clusters = util3d :: clusterPolygons (
neighbors ,
ui_ -> spinBox_mesh_minClusterSize -> value ());
std :: vector < pcl :: Vertices > filteredPolygons ( polygons . size ());
int oi = 0 ;
for ( std :: list < std :: list < int > >:: iterator iter = clusters . begin (); iter != clusters . end (); ++ iter )
{
for ( std :: list < int >:: iterator jter = iter -> begin (); jter != iter -> end (); ++ jter )
{
filteredPolygons [ oi ++ ] = polygons . at ( * jter );
}
}
filteredPolygons . resize ( oi );
polygons = filteredPolygons ;
}
view3D -> addCloudMesh ( "mesh" , cloud , polygons , pose );
}
if ( ui_ -> checkBox_showCloud -> isChecked ())
{
view3D -> addCloud ( "cloud" , cloud , pose );
}
}
view3D -> updateCameraFrustums ( pose , data . cameraModels ());
2015-10-24 17:03:44 -04:00
}
2016-09-03 18:59:43 -04:00
else if ( ui_ -> checkBox_showCloud -> isChecked ())
2015-10-24 17:03:44 -04:00
{
2016-09-03 18:59:43 -04:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloud ;
cloud = util3d :: cloudFromSensorData ( data , 1 , 0 , 0 , 0 , ui_ -> parameters_toolbox -> getParameters ());
if ( cloud -> size ())
{
view3D -> addCloud ( "cloud" , cloud , pose );
view3D -> updateCameraFrustum ( pose , data . stereoCameraModel ());
}
2015-10-24 17:03:44 -04:00
}
}
2015-03-04 17:27:44 -05:00
}
2015-05-01 07:30:09 -04:00
//add scan
2016-09-07 17:14:09 -04:00
if ( ui_ -> checkBox_showScan -> isChecked () && data . laserScanRaw (). cols )
2015-05-29 14:46:48 -04:00
{
2016-09-07 17:14:09 -04:00
if ( data . laserScanRaw (). channels () == 6 )
2016-09-03 18:59:43 -04:00
{
2016-09-07 17:14:09 -04:00
pcl :: PointCloud < pcl :: PointNormal >:: Ptr scan = util3d :: laserScanToPointCloudNormal ( data . laserScanRaw (), data . laserScanInfo (). localTransform ());
view3D -> addCloud ( "scan" , scan , pose , Qt :: yellow );
}
else
{
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr scan = util3d :: laserScanToPointCloud ( data . laserScanRaw (), data . laserScanInfo (). localTransform ());
2016-09-03 18:59:43 -04:00
view3D -> addCloud ( "scan" , scan , pose , Qt :: yellow );
}
2015-05-29 14:46:48 -04:00
}
2015-05-01 07:30:09 -04:00
2016-08-26 13:02:00 -04:00
//add occupancy grid
2016-09-03 18:59:43 -04:00
if ( ui_ -> checkBox_showMap -> isChecked () || ui_ -> checkBox_showGrid -> isChecked ())
2016-08-26 13:02:00 -04:00
{
2016-09-03 18:59:43 -04:00
std :: map < int , std :: pair < cv :: Mat , cv :: Mat > > localMaps ;
if ( generatedLocalMaps_ . find ( data . id ()) != generatedLocalMaps_ . end ())
2016-08-26 13:02:00 -04:00
{
2016-09-03 18:59:43 -04:00
localMaps . insert ( * generatedLocalMaps_ . find ( data . id ()));
2016-08-26 13:02:00 -04:00
}
2016-09-03 18:59:43 -04:00
else if ( ! data . gridGroundCellsRaw (). empty () && ! data . gridObstacleCellsRaw (). empty ())
{
localMaps . insert ( std :: make_pair ( data . id (), std :: make_pair ( data . gridGroundCellsRaw (), data . gridObstacleCellsRaw ())));
}
if ( ! localMaps . empty ())
{
std :: map < int , Transform > poses ;
poses . insert ( std :: make_pair ( data . id (), Transform :: getIdentity ()));
2016-08-30 21:15:55 -04:00
2016-09-03 18:59:43 -04:00
if ( ui_ -> checkBox_showMap -> isChecked ())
{
float xMin = 0.0f , yMin = 0.0f ;
cv :: Mat map8S = util3d :: create2DMapFromOccupancyLocalMaps (
poses ,
localMaps ,
ui_ -> doubleSpinBox_gridCellSize -> value (),
xMin , yMin );
if ( ! map8S . empty ())
{
//convert to gray scaled map
cv :: Mat map8U = util3d :: convertMap2Image8U ( map8S );
view3D -> addOccupancyGridMap ( map8U , ui_ -> doubleSpinBox_gridCellSize -> value (), xMin , yMin , 1 );
}
}
if ( ui_ -> checkBox_showGrid -> isChecked ())
{
// occupancy cloud
view3D -> addCloud ( "ground" ,
util3d :: laserScanToPointCloud ( localMaps . begin () -> second . first ),
Transform :: getIdentity (),
Qt :: green );
view3D -> addCloud ( "obstacles" ,
util3d :: laserScanToPointCloud ( localMaps . begin () -> second . second ),
Transform :: getIdentity (),
Qt :: red );
view3D -> setCloudPointSize ( "ground" , 5 );
view3D -> setCloudPointSize ( "obstacles" , 5 );
}
}
2016-08-26 13:02:00 -04:00
}
2015-03-04 17:27:44 -05:00
view3D -> update ();
}
2016-09-03 18:59:43 -04:00
2016-08-30 18:22:29 -04:00
if ( signatures . size ())
{
UASSERT ( signatures . front () != 0 && signatures . size () == 1 );
delete signatures . front ();
signatures . clear ();
}
2011-06-05 14:45:39 +00:00
}
2014-08-11 15:23:06 +00:00
if ( ! img . isNull ())
{
view -> setImage ( img );
rect = img . rect ();
}
else
{
ULOGGER_DEBUG ( "Image is empty" );
}
2012-03-03 01:46:30 +00:00
2016-07-08 12:47:23 -04:00
if ( ! imgDepth . isNull ())
{
view -> setImageDepth ( imgDepth );
2016-09-01 16:42:34 -04:00
if ( img . isNull ())
2016-07-08 12:47:23 -04:00
{
rect = imgDepth . rect ();
}
}
else
{
ULOGGER_DEBUG ( "Image depth is empty" );
}
2012-03-03 01:46:30 +00:00
// loops
2015-10-16 14:54:47 -04:00
std :: map < int , rtabmap :: Link > links ;
dbDriver_ -> loadLinks ( id , links );
if ( links . size ())
2012-03-03 01:46:30 +00:00
{
2014-12-14 16:42:10 -05:00
QString strParents , strChildren ;
2015-10-16 14:54:47 -04:00
for ( std :: map < int , rtabmap :: Link >:: iterator iter = links . begin (); iter != links . end (); ++ iter )
2012-03-03 01:46:30 +00:00
{
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () != Link :: kNeighbor &&
iter -> second . type () != Link :: kNeighborMerged )
2014-12-14 16:42:10 -05:00
{
2015-10-16 14:54:47 -04:00
if ( iter -> first < id )
{
strChildren . append ( QString ( "%1 " ). arg ( iter -> first ));
}
else
{
strParents . append ( QString ( "%1 " ). arg ( iter -> first ));
}
2014-12-14 16:42:10 -05:00
}
2012-03-03 01:46:30 +00:00
}
2014-12-14 16:42:10 -05:00
labelParents -> setText ( strParents );
labelChildren -> setText ( strChildren );
2012-03-03 01:46:30 +00:00
}
2011-06-05 14:45:39 +00:00
}
2014-07-14 19:16:26 +00:00
if ( mapId >= 0 )
{
2015-05-03 18:16:55 -04:00
labelMapId -> setText ( QString :: number ( mapId ));
2014-07-14 19:16:26 +00:00
}
2011-06-05 14:45:39 +00:00
}
else
{
ULOGGER_ERROR ( "Slider index out of range ?" );
}
2014-08-11 15:23:06 +00:00
updateConstraintButtons ();
updateWordsMatching ();
2015-07-24 16:29:45 -04:00
if ( updateConstraintView && ui_ -> dockWidget_constraints -> isVisible ())
2014-08-11 15:23:06 +00:00
{
2014-10-14 21:06:30 +00:00
// update constraint view
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
bool set = false ;
for ( int i = 0 ; i < loopLinks_ . size () || i < neighborLinks_ . size (); ++ i )
2014-08-11 15:23:06 +00:00
{
2014-10-14 21:06:30 +00:00
if ( i < loopLinks_ . size ())
2014-08-11 15:23:06 +00:00
{
2014-10-14 21:06:30 +00:00
if (( loopLinks_ [ i ]. from () == from && loopLinks_ [ i ]. to () == to ) ||
( loopLinks_ [ i ]. from () == to && loopLinks_ [ i ]. to () == from ))
2014-08-11 15:23:06 +00:00
{
2014-10-14 21:06:30 +00:00
if ( i != ui_ -> horizontalSlider_loops -> value ())
{
2014-11-17 22:31:04 +00:00
ui_ -> horizontalSlider_loops -> blockSignals ( true );
2014-10-14 21:06:30 +00:00
ui_ -> horizontalSlider_loops -> setValue ( i );
2014-11-17 22:31:04 +00:00
ui_ -> horizontalSlider_loops -> blockSignals ( false );
2015-07-24 16:29:45 -04:00
this -> updateConstraintView ( loopLinks_ [ i ]. from () == from ? loopLinks_ . at ( i ) : loopLinks_ . at ( i ). inverse (), false );
2014-10-14 21:06:30 +00:00
}
ui_ -> horizontalSlider_neighbors -> blockSignals ( true );
ui_ -> horizontalSlider_neighbors -> setValue ( 0 );
ui_ -> horizontalSlider_neighbors -> blockSignals ( false );
set = true ;
break ;
}
}
if ( i < neighborLinks_ . size ())
{
if (( neighborLinks_ [ i ]. from () == from && neighborLinks_ [ i ]. to () == to ) ||
( neighborLinks_ [ i ]. from () == to && neighborLinks_ [ i ]. to () == from ))
{
if ( i != ui_ -> horizontalSlider_neighbors -> value ())
{
2014-11-17 22:31:04 +00:00
ui_ -> horizontalSlider_neighbors -> blockSignals ( true );
2014-10-14 21:06:30 +00:00
ui_ -> horizontalSlider_neighbors -> setValue ( i );
2014-11-17 22:31:04 +00:00
ui_ -> horizontalSlider_neighbors -> blockSignals ( false );
2015-07-24 16:29:45 -04:00
this -> updateConstraintView ( neighborLinks_ [ i ]. from () == from ? neighborLinks_ . at ( i ) : neighborLinks_ . at ( i ). inverse (), false );
2014-10-14 21:06:30 +00:00
}
ui_ -> horizontalSlider_loops -> blockSignals ( true );
ui_ -> horizontalSlider_loops -> setValue ( 0 );
ui_ -> horizontalSlider_loops -> blockSignals ( false );
set = true ;
break ;
2014-08-11 15:23:06 +00:00
}
}
}
2014-10-14 21:06:30 +00:00
if ( ! set )
2014-08-11 15:23:06 +00:00
{
2014-10-14 21:06:30 +00:00
ui_ -> horizontalSlider_loops -> blockSignals ( true );
ui_ -> horizontalSlider_neighbors -> blockSignals ( true );
ui_ -> horizontalSlider_loops -> setValue ( 0 );
ui_ -> horizontalSlider_neighbors -> setValue ( 0 );
ui_ -> horizontalSlider_loops -> blockSignals ( false );
ui_ -> horizontalSlider_neighbors -> blockSignals ( false );
2015-07-24 16:29:45 -04:00
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeAllClouds ();
2015-07-24 16:29:45 -04:00
// make a fake link using globally optimized poses
if ( graphes_ . size ())
{
std :: map < int , Transform > optimizedPoses = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
if ( optimizedPoses . size () > 0 )
{
std :: map < int , Transform >:: iterator fromIter = optimizedPoses . find ( from );
std :: map < int , Transform >:: iterator toIter = optimizedPoses . find ( to );
if ( fromIter != optimizedPoses . end () &&
toIter != optimizedPoses . end ())
{
Link link ( from , to , Link :: kUndef , fromIter -> second . inverse () * toIter -> second );
this -> updateConstraintView ( link , false );
}
}
}
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> update ();
2015-07-24 16:29:45 -04:00
2014-08-11 15:23:06 +00:00
}
}
if ( rect . isValid ())
{
view -> setSceneRect ( rect );
}
}
2015-11-22 18:08:32 -05:00
void DatabaseViewer :: updateLoggerLevel ()
{
if ( this -> parent () == 0 )
{
ULogger :: setLevel (( ULogger :: Level ) ui_ -> comboBox_logger_level -> currentIndex ());
}
}
2016-05-05 18:25:26 -04:00
2015-06-15 14:44:44 -04:00
void DatabaseViewer :: updateStereo ()
{
if ( ui_ -> horizontalSlider_A -> maximum ())
{
int id = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( id , data );
data . uncompressData ();
2015-06-15 14:44:44 -04:00
updateStereo ( & data );
}
}
2016-05-05 18:25:26 -04:00
void DatabaseViewer :: updateStereo ( const SensorData * data )
2014-11-04 18:58:40 +00:00
{
2015-05-29 14:46:48 -04:00
if ( data &&
ui_ -> dockWidget_stereoView -> isVisible () &&
! data -> imageRaw (). empty () &&
! data -> depthOrRightRaw (). empty () &&
data -> depthOrRightRaw (). type () == CV_8UC1 &&
2016-01-19 21:01:56 -05:00
data -> stereoCameraModel (). isValidForProjection ())
2014-11-04 18:58:40 +00:00
{
cv :: Mat leftMono ;
2015-05-29 14:46:48 -04:00
if ( data -> imageRaw (). channels () == 3 )
2014-11-04 18:58:40 +00:00
{
2015-05-29 14:46:48 -04:00
cv :: cvtColor ( data -> imageRaw (), leftMono , CV_BGR2GRAY );
2014-11-04 18:58:40 +00:00
}
else
{
2015-05-29 14:46:48 -04:00
leftMono = data -> imageRaw ();
2014-11-04 18:58:40 +00:00
}
UTimer timer ;
2015-12-17 14:13:19 -05:00
ParametersMap parameters = ui_ -> parameters_toolbox -> getParameters ();
bool opticalFlow = uStr2Bool ( parameters . at ( Parameters :: kStereoOpticalFlow ()));
Stereo * stereo = 0 ;
if ( opticalFlow )
{
stereo = new StereoOpticalFlow ( parameters );
}
else
{
stereo = new Stereo ( parameters );
}
2014-11-04 18:58:40 +00:00
// generate kpts
std :: vector < cv :: KeyPoint > kpts ;
2015-12-21 17:27:05 -05:00
uInsert ( parameters , ParametersPair ( Parameters :: kKpMaxFeatures (), parameters . at ( Parameters :: kVisMaxFeatures ())));
2016-03-13 16:58:36 -04:00
uInsert ( parameters , ParametersPair ( Parameters :: kKpMinDepth (), parameters . at ( Parameters :: kVisMinDepth ())));
uInsert ( parameters , ParametersPair ( Parameters :: kKpMaxDepth (), parameters . at ( Parameters :: kVisMaxDepth ())));
uInsert ( parameters , ParametersPair ( Parameters :: kKpDetectorStrategy (), parameters . at ( Parameters :: kVisFeatureType ())));
uInsert ( parameters , ParametersPair ( Parameters :: kKpRoiRatios (), parameters . at ( Parameters :: kVisRoiRatios ())));
uInsert ( parameters , ParametersPair ( Parameters :: kKpSubPixEps (), parameters . at ( Parameters :: kVisSubPixEps ())));
uInsert ( parameters , ParametersPair ( Parameters :: kKpSubPixIterations (), parameters . at ( Parameters :: kVisSubPixIterations ())));
uInsert ( parameters , ParametersPair ( Parameters :: kKpSubPixWinSize (), parameters . at ( Parameters :: kVisSubPixWinSize ())));
2015-12-17 14:13:19 -05:00
Feature2D * kptDetector = Feature2D :: create ( parameters );
2015-12-21 17:27:05 -05:00
kpts = kptDetector -> generateKeypoints ( leftMono );
2014-11-04 18:58:40 +00:00
delete kptDetector ;
float timeKpt = timer . ticks ();
std :: vector < cv :: Point2f > leftCorners ;
cv :: KeyPoint :: convert ( kpts , leftCorners );
2015-12-11 17:44:15 -05:00
// Find features in the new right image
2014-11-04 18:58:40 +00:00
std :: vector < unsigned char > status ;
std :: vector < cv :: Point2f > rightCorners ;
2015-12-17 14:13:19 -05:00
rightCorners = stereo -> computeCorrespondences (
leftMono ,
data -> rightRaw (),
leftCorners ,
status );
delete stereo ;
float timeStereo = timer . ticks ();
2014-11-04 18:58:40 +00:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloud ( new pcl :: PointCloud < pcl :: PointXYZ > );
cloud -> resize ( kpts . size ());
float bad_point = std :: numeric_limits < float >:: quiet_NaN ();
UASSERT ( status . size () == kpts . size ());
int oi = 0 ;
2015-06-15 14:44:44 -04:00
int inliers = 0 ;
int flowOutliers = 0 ;
int slopeOutliers = 0 ;
int negativeDisparityOutliers = 0 ;
2014-11-04 18:58:40 +00:00
for ( unsigned int i = 0 ; i < status . size (); ++ i )
{
2015-12-21 17:27:05 -05:00
cv :: Point3f pt ( bad_point , bad_point , bad_point );
2014-11-04 18:58:40 +00:00
if ( status [ i ])
{
float disparity = leftCorners [ i ]. x - rightCorners [ i ]. x ;
if ( disparity > 0.0f )
{
2015-12-21 17:27:05 -05:00
cv :: Point3f tmpPt = util3d :: projectDisparityTo3D (
2015-12-17 14:13:19 -05:00
leftCorners [ i ],
disparity ,
data -> stereoCameraModel ());
2014-11-04 18:58:40 +00:00
2015-12-21 17:27:05 -05:00
if ( util3d :: isFinite ( tmpPt ))
2016-05-05 18:25:26 -04:00
{
2015-12-21 17:27:05 -05:00
pt = util3d :: transformPoint ( tmpPt , data -> stereoCameraModel (). left (). localTransform ());
2015-12-17 14:13:19 -05:00
status [ i ] = 100 ; //blue
2016-05-05 18:25:26 -04:00
++ inliers ;
2015-12-21 17:27:05 -05:00
cloud -> at ( oi ++ ) = pcl :: PointXYZ ( pt . x , pt . y , pt . z );
2014-11-04 18:58:40 +00:00
}
}
else
{
status [ i ] = 102 ; //magenta
2015-06-15 14:44:44 -04:00
++ negativeDisparityOutliers ;
2014-11-04 18:58:40 +00:00
}
}
2015-06-15 14:44:44 -04:00
else
{
++ flowOutliers ;
}
2014-11-04 18:58:40 +00:00
}
cloud -> resize ( oi );
2015-12-17 14:13:19 -05:00
UINFO ( "correspondences = %d/%d (%f) (time kpt=%fs stereo=%fs)" ,
( int ) cloud -> size (), ( int ) leftCorners . size (), float ( cloud -> size ()) / float ( leftCorners . size ()), timeKpt , timeStereo );
2014-11-04 18:58:40 +00:00
2016-05-05 18:25:26 -04:00
stereoViewer_ -> updateCameraTargetPosition ( Transform :: getIdentity ());
stereoViewer_ -> addCloud ( "stereo" , cloud );
stereoViewer_ -> update ();
2014-11-04 18:58:40 +00:00
2015-06-15 14:44:44 -04:00
ui_ -> label_stereo_inliers -> setNum ( inliers );
ui_ -> label_stereo_flowOutliers -> setNum ( flowOutliers );
ui_ -> label_stereo_slopeOutliers -> setNum ( slopeOutliers );
ui_ -> label_stereo_disparityOutliers -> setNum ( negativeDisparityOutliers );
2014-11-04 18:58:40 +00:00
std :: vector < cv :: KeyPoint > rightKpts ;
cv :: KeyPoint :: convert ( rightCorners , rightKpts );
std :: vector < cv :: DMatch > good_matches ( kpts . size ());
for ( unsigned int i = 0 ; i < good_matches . size (); ++ i )
{
good_matches [ i ]. trainIdx = i ;
good_matches [ i ]. queryIdx = i ;
}
//
//cv::Mat imageMatches;
//cv::drawMatches( leftMono, kpts, data->getDepthRaw(), rightKpts,
// good_matches, imageMatches, cv::Scalar::all(-1), cv::Scalar::all(-1),
// std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
//ui_->graphicsView_stereo->setImage(uCvMat2QImage(imageMatches));
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_stereo -> clear ();
2014-11-04 18:58:40 +00:00
ui_ -> graphicsView_stereo -> setLinesShown ( true );
ui_ -> graphicsView_stereo -> setFeaturesShown ( false );
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_stereo -> setImageDepthShown ( true );
2014-11-04 18:58:40 +00:00
2015-05-29 14:46:48 -04:00
ui_ -> graphicsView_stereo -> setImage ( uCvMat2QImage ( data -> imageRaw ()));
ui_ -> graphicsView_stereo -> setImageDepth ( uCvMat2QImage ( data -> depthOrRightRaw ()));
2014-11-04 18:58:40 +00:00
// Draw lines between corresponding features...
for ( unsigned int i = 0 ; i < kpts . size (); ++ i )
{
2015-12-11 17:44:15 -05:00
if ( rightKpts [ i ]. pt . x > 0 && rightKpts [ i ]. pt . y > 0 )
2014-11-04 18:58:40 +00:00
{
2015-12-11 17:44:15 -05:00
QColor c = Qt :: green ;
if ( status [ i ] == 0 )
{
c = Qt :: red ;
}
else if ( status [ i ] == 100 )
{
c = Qt :: blue ;
}
else if ( status [ i ] == 101 )
{
c = Qt :: yellow ;
}
else if ( status [ i ] == 102 )
{
c = Qt :: magenta ;
}
else if ( status [ i ] == 110 )
{
c = Qt :: cyan ;
}
ui_ -> graphicsView_stereo -> addLine (
kpts [ i ]. pt . x ,
kpts [ i ]. pt . y ,
rightKpts [ i ]. pt . x ,
rightKpts [ i ]. pt . y ,
c ,
QString ( "%1: (%2,%3) -> (%4,%5)" ). arg ( i ). arg ( kpts [ i ]. pt . x ). arg ( kpts [ i ]. pt . y ). arg ( rightKpts [ i ]. pt . x ). arg ( rightKpts [ i ]. pt . y ));
2014-11-04 18:58:40 +00:00
}
}
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_stereo -> update ();
2014-11-04 18:58:40 +00:00
}
}
2014-08-11 15:23:06 +00:00
void DatabaseViewer :: updateWordsMatching ()
{
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
if ( from && to )
{
int alpha = 70 ;
ui_ -> graphicsView_A -> clearLines ();
ui_ -> graphicsView_A -> setFeaturesColor ( QColor ( 255 , 255 , 0 , alpha )); // yellow
ui_ -> graphicsView_B -> clearLines ();
ui_ -> graphicsView_B -> setFeaturesColor ( QColor ( 255 , 255 , 0 , alpha )); // yellow
const QMultiMap < int , KeypointItem *> & wordsA = ui_ -> graphicsView_A -> getFeatures ();
const QMultiMap < int , KeypointItem *> & wordsB = ui_ -> graphicsView_B -> getFeatures ();
if ( wordsA . size () && wordsB . size ())
{
QList < int > ids = wordsA . uniqueKeys ();
for ( int i = 0 ; i < ids . size (); ++ i )
{
if ( wordsA . count ( ids [ i ]) == 1 && wordsB . count ( ids [ i ]) == 1 )
{
// PINK features
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_A -> setFeatureColor ( ids [ i ], Qt :: magenta );
ui_ -> graphicsView_B -> setFeatureColor ( ids [ i ], Qt :: magenta );
2014-08-11 15:23:06 +00:00
// Add lines
// Draw lines between corresponding features...
2015-03-30 10:47:38 -04:00
float scaleX = ui_ -> graphicsView_A -> viewScale ();
2015-12-21 17:27:05 -05:00
float deltaX = 0 ;
2014-08-11 15:23:06 +00:00
float deltaY = 0 ;
2015-12-21 17:27:05 -05:00
if ( ui_ -> checkBox_verticalLayout -> isChecked ())
{
deltaY = ui_ -> graphicsView_A -> height () / scaleX ;
}
else
{
deltaX = ui_ -> graphicsView_A -> width () / scaleX ;
}
2014-08-11 15:23:06 +00:00
const KeypointItem * kptA = wordsA . value ( ids [ i ]);
const KeypointItem * kptB = wordsB . value ( ids [ i ]);
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_A -> addLine (
2014-08-11 15:23:06 +00:00
kptA -> rect (). x () + kptA -> rect (). width () / 2 ,
kptA -> rect (). y () + kptA -> rect (). height () / 2 ,
kptB -> rect (). x () + kptB -> rect (). width () / 2 + deltaX ,
kptB -> rect (). y () + kptB -> rect (). height () / 2 + deltaY ,
2015-03-30 10:47:38 -04:00
Qt :: cyan );
2014-08-11 15:23:06 +00:00
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_B -> addLine (
2014-08-11 15:23:06 +00:00
kptA -> rect (). x () + kptA -> rect (). width () / 2 - deltaX ,
kptA -> rect (). y () + kptA -> rect (). height () / 2 - deltaY ,
kptB -> rect (). x () + kptB -> rect (). width () / 2 ,
kptB -> rect (). y () + kptB -> rect (). height () / 2 ,
2015-03-30 10:47:38 -04:00
Qt :: cyan );
2014-08-11 15:23:06 +00:00
}
}
2015-03-30 10:47:38 -04:00
ui_ -> graphicsView_A -> update ();
ui_ -> graphicsView_B -> update ();
2014-08-11 15:23:06 +00:00
}
}
2011-06-05 14:45:39 +00:00
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: sliderAMoved ( int value )
2011-06-05 14:45:39 +00:00
{
ui_ -> label_indexA -> setText ( QString :: number ( value ));
if ( value >= 0 && value < ids_ . size ())
{
ui_ -> label_idA -> setText ( QString :: number ( ids_ . at ( value )));
}
else
{
ULOGGER_ERROR ( "Slider index out of range ?" );
}
}
2012-12-11 18:05:05 +00:00
void DatabaseViewer :: sliderBMoved ( int value )
2011-06-05 14:45:39 +00:00
{
ui_ -> label_indexB -> setText ( QString :: number ( value ));
if ( value >= 0 && value < ids_ . size ())
{
ui_ -> label_idB -> setText ( QString :: number ( ids_ . at ( value )));
}
else
{
ULOGGER_ERROR ( "Slider index out of range ?" );
}
}
2014-02-11 23:04:22 +00:00
2016-02-15 19:35:24 -05:00
void DatabaseViewer :: update3dView ()
{
if ( ui_ -> dockWidget_view3d -> isVisible ())
{
sliderAValueChanged ( ui_ -> horizontalSlider_A -> value ());
sliderBValueChanged ( ui_ -> horizontalSlider_B -> value ());
}
}
2014-02-11 23:04:22 +00:00
void DatabaseViewer :: sliderNeighborValueChanged ( int value )
{
this -> updateConstraintView ( neighborLinks_ . at ( value ));
}
void DatabaseViewer :: sliderLoopValueChanged ( int value )
{
this -> updateConstraintView ( loopLinks_ . at ( value ));
}
2014-12-14 16:42:10 -05:00
// only called when ui_->checkBox_showOptimized state changed
void DatabaseViewer :: updateConstraintView ()
{
2015-05-03 18:16:55 -04:00
if ( ids_ . size ())
2015-04-23 22:01:41 -04:00
{
2015-05-03 18:16:55 -04:00
Link link = this -> findActiveLink ( ids_ . at ( ui_ -> horizontalSlider_A -> value ()), ids_ . at ( ui_ -> horizontalSlider_B -> value ()));
if ( link . isValid ())
2015-05-01 07:30:09 -04:00
{
2015-10-24 17:03:44 -04:00
if ( link . type () == Link :: kNeighbor ||
link . type () == Link :: kNeighborMerged )
2015-05-03 18:16:55 -04:00
{
this -> updateConstraintView ( neighborLinks_ . at ( ui_ -> horizontalSlider_neighbors -> value ()), false );
}
else
{
this -> updateConstraintView ( loopLinks_ . at ( ui_ -> horizontalSlider_loops -> value ()), false );
}
2015-05-01 07:30:09 -04:00
}
2015-04-23 22:01:41 -04:00
}
2014-12-14 16:42:10 -05:00
}
2015-02-25 17:06:52 -05:00
void DatabaseViewer :: updateConstraintView (
const rtabmap :: Link & linkIn ,
bool updateImageSliders ,
2014-10-14 21:06:30 +00:00
const pcl :: PointCloud < pcl :: PointXYZ >:: Ptr & cloudFrom ,
2014-11-17 22:31:04 +00:00
const pcl :: PointCloud < pcl :: PointXYZ >:: Ptr & cloudTo ,
2015-02-25 17:06:52 -05:00
const pcl :: PointCloud < pcl :: PointXYZ >:: Ptr & scanFrom ,
const pcl :: PointCloud < pcl :: PointXYZ >:: Ptr & scanTo )
2014-02-11 23:04:22 +00:00
{
2015-10-13 12:50:25 -04:00
std :: multimap < int , Link >:: iterator iterLink = rtabmap :: graph :: findLink ( linksRefined_ , linkIn . from (), linkIn . to ());
2014-12-14 16:42:10 -05:00
rtabmap :: Link link = linkIn ;
2016-07-24 13:23:22 -04:00
2015-10-13 12:50:25 -04:00
if ( iterLink != linksRefined_ . end ())
2014-02-11 23:04:22 +00:00
{
2015-10-13 12:50:25 -04:00
link = iterLink -> second ;
2014-08-11 15:23:06 +00:00
}
2015-11-22 18:08:32 -05:00
else if ( ui_ -> checkBox_ignorePoseCorrection -> isChecked ())
{
if ( link . type () == Link :: kNeighbor ||
link . type () == Link :: kNeighborMerged )
{
Transform poseFrom = uValue ( poses_ , link . from (), Transform ());
Transform poseTo = uValue ( poses_ , link . to (), Transform ());
if ( ! poseFrom . isNull () && ! poseTo . isNull ())
{
2016-07-24 13:23:22 -04:00
// recompute raw odom transformation and
// reset to identity covariance
link = Link ( link . from (),
link . to (),
link . type (),
poseFrom . inverse () * poseTo );
2015-11-22 18:08:32 -05:00
}
}
}
2014-12-14 16:42:10 -05:00
rtabmap :: Transform t = link . transform ();
2014-08-11 15:23:06 +00:00
ui_ -> label_constraint -> clear ();
2014-12-14 16:42:10 -05:00
ui_ -> label_constraint_opt -> clear ();
ui_ -> checkBox_showOptimized -> setEnabled ( false );
2015-10-16 14:54:47 -04:00
UASSERT ( ! t . isNull () && dbDriver_ );
2014-08-11 15:23:06 +00:00
2015-10-24 17:03:44 -04:00
ui_ -> label_type -> setText ( tr ( "%1 (%2)" )
. arg ( link . type ())
2015-11-22 18:08:32 -05:00
. arg ( link . type () == Link :: kNeighbor ? "Neighbor" :
2016-11-03 17:04:31 -04:00
link . type () == Link :: kNeighborMerged ? "Merged neighbor" :
2015-10-27 15:56:46 -04:00
link . type () == Link :: kGlobalClosure ? "Loop closure" :
2015-10-24 17:03:44 -04:00
link . type () == Link :: kLocalSpaceClosure ? "Space proximity link" :
link . type () == Link :: kLocalTimeClosure ? "Time proximity link" :
link . type () == Link :: kUserClosure ? "User link" :
link . type () == Link :: kVirtualClosure ? "Virtual link" : "Undefined" ));
2015-05-29 14:46:48 -04:00
ui_ -> label_variance -> setText ( QString ( "%1, %2" )
. arg ( sqrt ( link . rotVariance ()))
. arg ( sqrt ( link . transVariance ())));
2015-02-27 16:55:47 -05:00
ui_ -> label_constraint -> setText ( QString ( "%1" ). arg ( t . prettyPrint (). c_str ()). replace ( " " , " \n " ));
2015-10-24 17:03:44 -04:00
if (( link . type () == Link :: kNeighbor || link . type () == Link :: kNeighborMerged ) &&
2014-12-14 16:42:10 -05:00
graphes_ . size () &&
( int ) graphes_ . size () - 1 == ui_ -> horizontalSlider_iterations -> maximum ())
{
std :: map < int , rtabmap :: Transform > & graph = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
2015-10-24 17:03:44 -04:00
if ( link . type () == Link :: kNeighbor || link . type () == Link :: kNeighborMerged )
2014-12-14 16:42:10 -05:00
{
std :: map < int , rtabmap :: Transform >:: iterator iterFrom = graph . find ( link . from ());
std :: map < int , rtabmap :: Transform >:: iterator iterTo = graph . find ( link . to ());
if ( iterFrom != graph . end () && iterTo != graph . end ())
{
ui_ -> checkBox_showOptimized -> setEnabled ( true );
Transform topt = iterFrom -> second . inverse () * iterTo -> second ;
2015-02-25 17:06:52 -05:00
float diff = topt . getDistance ( t );
2014-12-14 16:42:10 -05:00
Transform v1 = t . rotation () * Transform ( 1 , 0 , 0 , 0 , 0 , 0 );
Transform v2 = topt . rotation () * Transform ( 1 , 0 , 0 , 0 , 0 , 0 );
float a = pcl :: getAngle3D ( Eigen :: Vector4f ( v1 . x (), v1 . y (), v1 . z (), 0 ), Eigen :: Vector4f ( v2 . x (), v2 . y (), v2 . z (), 0 ));
a = ( a * 180.0f ) / CV_PI ;
2015-02-27 16:55:47 -05:00
ui_ -> label_constraint_opt -> setText ( QString ( "%1 \n (error=%2% a=%3)" ). arg ( QString ( topt . prettyPrint (). c_str ()). replace ( " " , " \n " )). arg (( diff / t . getNorm ()) * 100.0f ). arg ( a ));
2014-12-14 16:42:10 -05:00
if ( ui_ -> checkBox_showOptimized -> isChecked ())
{
t = topt ;
}
}
}
}
2014-08-11 15:23:06 +00:00
2014-11-17 22:31:04 +00:00
if ( updateImageSliders )
2014-08-11 15:23:06 +00:00
{
2014-11-17 22:31:04 +00:00
ui_ -> horizontalSlider_A -> blockSignals ( true );
ui_ -> horizontalSlider_B -> blockSignals ( true );
2015-04-25 18:13:31 -04:00
// set from on left and to on right {
ui_ -> horizontalSlider_A -> setValue ( idToIndex_ . value ( link . from ()));
ui_ -> horizontalSlider_B -> setValue ( idToIndex_ . value ( link . to ()));
2014-11-17 22:31:04 +00:00
ui_ -> horizontalSlider_A -> blockSignals ( false );
ui_ -> horizontalSlider_B -> blockSignals ( false );
2015-04-25 18:13:31 -04:00
this -> update ( idToIndex_ . value ( link . from ()),
ui_ -> label_indexA ,
ui_ -> label_parentsA ,
ui_ -> label_childrenA ,
ui_ -> label_weightA ,
ui_ -> label_labelA ,
ui_ -> label_stampA ,
ui_ -> graphicsView_A ,
2016-05-05 18:25:26 -04:00
cloudViewerA_ ,
2015-04-25 18:13:31 -04:00
ui_ -> label_idA ,
2015-05-03 18:16:55 -04:00
ui_ -> label_mapA ,
2015-05-20 18:07:10 -04:00
ui_ -> label_poseA ,
2016-04-07 12:15:58 -04:00
ui_ -> label_calibA ,
2015-04-25 18:13:31 -04:00
false ); // don't update constraints view!
this -> update ( idToIndex_ . value ( link . to ()),
ui_ -> label_indexB ,
ui_ -> label_parentsB ,
ui_ -> label_childrenB ,
ui_ -> label_weightB ,
ui_ -> label_labelB ,
ui_ -> label_stampB ,
ui_ -> graphicsView_B ,
2016-05-05 18:25:26 -04:00
cloudViewerB_ ,
2015-04-25 18:13:31 -04:00
ui_ -> label_idB ,
2015-05-03 18:16:55 -04:00
ui_ -> label_mapB ,
2015-05-20 18:07:10 -04:00
ui_ -> label_poseB ,
2016-04-07 12:15:58 -04:00
ui_ -> label_calibB ,
2015-04-25 18:13:31 -04:00
false ); // don't update constraints view!
2014-08-11 15:23:06 +00:00
}
2014-02-11 23:04:22 +00:00
2016-05-05 18:25:26 -04:00
if ( constraintsViewer_ -> isVisible ())
2014-10-13 19:10:22 +00:00
{
2015-05-29 14:46:48 -04:00
SensorData dataFrom , dataTo ;
2015-02-25 17:06:52 -05:00
2015-10-16 14:54:47 -04:00
dbDriver_ -> getNodeData ( link . from (), dataFrom );
dataFrom . uncompressData ();
2015-05-29 14:46:48 -04:00
UASSERT ( dataFrom . imageRaw (). empty () || dataFrom . imageRaw (). type () == CV_8UC3 || dataFrom . imageRaw (). type () == CV_8UC1 );
UASSERT ( dataFrom . depthOrRightRaw (). empty () || dataFrom . depthOrRightRaw (). type () == CV_8UC1 || dataFrom . depthOrRightRaw (). type () == CV_16UC1 || dataFrom . depthOrRightRaw (). type () == CV_32FC1 );
2015-02-25 17:06:52 -05:00
2015-10-16 14:54:47 -04:00
dbDriver_ -> getNodeData ( link . to (), dataTo );
dataTo . uncompressData ();
2015-05-29 14:46:48 -04:00
UASSERT ( dataTo . imageRaw (). empty () || dataTo . imageRaw (). type () == CV_8UC3 || dataTo . imageRaw (). type () == CV_8UC1 );
UASSERT ( dataTo . depthOrRightRaw (). empty () || dataTo . depthOrRightRaw (). type () == CV_8UC1 || dataTo . depthOrRightRaw (). type () == CV_16UC1 || dataTo . depthOrRightRaw (). type () == CV_32FC1 );
2015-02-25 17:06:52 -05:00
2014-10-30 14:43:43 +00:00
if ( cloudFrom -> size () == 0 && cloudTo -> size () == 0 )
2014-10-13 19:10:22 +00:00
{
2014-10-30 14:43:43 +00:00
//cloud 3d
2015-07-24 16:29:45 -04:00
if ( ui_ -> checkBox_show3Dclouds -> isChecked ())
2014-10-30 14:43:43 +00:00
{
2015-05-29 14:46:48 -04:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloudFrom , cloudTo ;
2016-09-07 12:15:44 -04:00
pcl :: IndicesPtr indicesFrom ( new std :: vector < int > );
pcl :: IndicesPtr indicesTo ( new std :: vector < int > );
2015-10-24 17:03:44 -04:00
if ( ! dataFrom . imageRaw (). empty () && ! dataFrom . depthOrRightRaw (). empty ())
{
2016-09-07 12:15:44 -04:00
cloudFrom = util3d :: cloudRGBFromSensorData ( dataFrom , 1 , 0 , 0 , indicesFrom . get (), ui_ -> parameters_toolbox -> getParameters ());
2015-10-24 17:03:44 -04:00
}
if ( ! dataTo . imageRaw (). empty () && ! dataTo . depthOrRightRaw (). empty ())
{
2016-09-07 12:15:44 -04:00
cloudTo = util3d :: cloudRGBFromSensorData ( dataTo , 1 , 0 , 0 , indicesTo . get (), ui_ -> parameters_toolbox -> getParameters ());
}
if ( cloudTo . get () && cloudTo -> size ())
{
cloudTo = rtabmap :: util3d :: transformPointCloud ( cloudTo , t );
}
// Gain compensation
if ( ui_ -> doubleSpinBox_gainCompensationRadius -> value () > 0.0 &&
cloudFrom . get () && cloudFrom -> size () &&
cloudTo . get () && cloudTo -> size ())
{
UTimer t ;
GainCompensator compensator ( ui_ -> doubleSpinBox_gainCompensationRadius -> value ());
compensator . feed ( cloudFrom , indicesFrom , cloudTo , indicesTo , Transform :: getIdentity ());
compensator . apply ( 0 , cloudFrom , indicesFrom );
compensator . apply ( 1 , cloudTo , indicesTo );
UINFO ( "Gain compensation time = %fs" , t . ticks ());
2015-10-24 17:03:44 -04:00
}
2014-11-07 20:04:04 +00:00
2015-10-24 17:03:44 -04:00
if ( cloudFrom . get () && cloudFrom -> size ())
2014-11-07 20:04:04 +00:00
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "cloud0" , cloudFrom , Transform :: getIdentity (), Qt :: red );
2014-11-07 20:04:04 +00:00
}
2015-10-24 17:03:44 -04:00
if ( cloudTo . get () && cloudTo -> size ())
2014-11-07 20:04:04 +00:00
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "cloud1" , cloudTo , Transform :: getIdentity (), Qt :: cyan );
2014-11-07 20:04:04 +00:00
}
2014-10-30 14:43:43 +00:00
}
2015-10-07 16:54:36 -04:00
else
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "cloud0" );
constraintsViewer_ -> removeCloud ( "cloud1" );
2015-10-07 16:54:36 -04:00
}
2015-07-24 16:29:45 -04:00
if ( ui_ -> checkBox_show3DWords -> isChecked ())
2014-10-30 14:43:43 +00:00
{
2015-10-16 14:54:47 -04:00
std :: list < int > ids ;
ids . push_back ( link . from ());
ids . push_back ( link . to ());
std :: list < Signature *> signatures ;
dbDriver_ -> loadSignatures ( ids , signatures );
if ( signatures . size () == 2 )
2014-11-07 20:04:04 +00:00
{
2015-10-16 14:54:47 -04:00
const Signature * sFrom = signatures . front ();
const Signature * sTo = signatures . back ();
UASSERT ( sFrom && sTo );
2014-11-07 20:04:04 +00:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloudFrom ( new pcl :: PointCloud < pcl :: PointXYZ > );
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloudTo ( new pcl :: PointCloud < pcl :: PointXYZ > );
cloudFrom -> resize ( sFrom -> getWords3 (). size ());
cloudTo -> resize ( sTo -> getWords3 (). size ());
int i = 0 ;
2015-12-21 17:27:05 -05:00
for ( std :: multimap < int , cv :: Point3f >:: const_iterator iter = sFrom -> getWords3 (). begin ();
2014-11-07 20:04:04 +00:00
iter != sFrom -> getWords3 (). end ();
++ iter )
{
2015-12-21 17:27:05 -05:00
cloudFrom -> at ( i ++ ) = pcl :: PointXYZ ( iter -> second . x , iter -> second . y , iter -> second . z );
2014-11-07 20:04:04 +00:00
}
i = 0 ;
2015-12-21 17:27:05 -05:00
for ( std :: multimap < int , cv :: Point3f >:: const_iterator iter = sTo -> getWords3 (). begin ();
2014-11-07 20:04:04 +00:00
iter != sTo -> getWords3 (). end ();
++ iter )
{
2015-12-21 17:27:05 -05:00
cloudTo -> at ( i ++ ) = pcl :: PointXYZ ( iter -> second . x , iter -> second . y , iter -> second . z );
2014-11-07 20:04:04 +00:00
}
2014-10-30 14:43:43 +00:00
2014-11-07 20:04:04 +00:00
if ( cloudFrom -> size ())
{
2015-05-13 19:54:23 -04:00
cloudFrom = rtabmap :: util3d :: removeNaNFromPointCloud ( cloudFrom );
2014-11-07 20:04:04 +00:00
}
if ( cloudTo -> size ())
{
2015-05-13 19:54:23 -04:00
cloudTo = rtabmap :: util3d :: removeNaNFromPointCloud ( cloudTo );
2015-05-01 07:30:09 -04:00
if ( cloudTo -> size ())
{
2015-05-13 19:54:23 -04:00
cloudTo = rtabmap :: util3d :: transformPointCloud ( cloudTo , t );
2015-05-01 07:30:09 -04:00
}
2014-11-07 20:04:04 +00:00
}
2014-10-30 14:43:43 +00:00
2014-11-07 20:04:04 +00:00
if ( cloudFrom -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "words0" , cloudFrom , Transform :: getIdentity (), Qt :: red );
2014-11-07 20:04:04 +00:00
}
else
{
UWARN ( "Empty 3D words for node %d" , link . from ());
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "words0" );
2014-11-07 20:04:04 +00:00
}
if ( cloudTo -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "words1" , cloudTo , Transform :: getIdentity (), Qt :: cyan );
2014-11-07 20:04:04 +00:00
}
else
{
UWARN ( "Empty 3D words for node %d" , link . to ());
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "words1" );
2014-11-07 20:04:04 +00:00
}
}
else
{
UERROR ( "Not found signature %d or %d in RAM" , link . from (), link . to ());
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "words0" );
constraintsViewer_ -> removeCloud ( "words1" );
2014-11-07 20:04:04 +00:00
}
2015-10-16 14:54:47 -04:00
//cleanup
for ( std :: list < Signature *>:: iterator iter = signatures . begin (); iter != signatures . end (); ++ iter )
{
delete * iter ;
}
2014-10-30 14:43:43 +00:00
}
2015-10-07 16:54:36 -04:00
else
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "words0" );
constraintsViewer_ -> removeCloud ( "words1" );
2015-10-07 16:54:36 -04:00
}
2014-10-13 19:10:22 +00:00
}
else
{
2014-10-30 14:43:43 +00:00
if ( cloudFrom -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "cloud0" , cloudFrom , Transform :: getIdentity (), Qt :: red );
2014-10-30 14:43:43 +00:00
}
if ( cloudTo -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "cloud1" , cloudTo , Transform :: getIdentity (), Qt :: cyan );
2015-02-25 17:06:52 -05:00
}
}
if ( scanFrom -> size () == 0 && scanTo -> size () == 0 )
{
2015-07-24 16:29:45 -04:00
if ( ui_ -> checkBox_show2DScans -> isChecked ())
2015-02-25 17:06:52 -05:00
{
2015-07-24 16:29:45 -04:00
//cloud 2d
2015-10-13 12:50:25 -04:00
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "scan2" );
constraintsViewer_ -> removeGraph ( "scan2graph" );
2015-10-26 12:59:20 -04:00
if ( link . type () == Link :: kLocalSpaceClosure &&
! link . userDataCompressed (). empty ())
2015-10-13 12:50:25 -04:00
{
std :: vector < int > ids ;
cv :: Mat userData = link . uncompressUserDataConst ();
if ( userData . type () == CV_8SC1 &&
userData . rows == 1 &&
userData . cols >= 8 && // including null str ending
userData . at < char > ( userData . cols - 1 ) == 0 &&
memcmp ( userData . data , "SCANS:" , 6 ) == 0 )
{
std :: string scansStr = ( const char * ) userData . data ;
2016-05-20 11:44:50 -04:00
UINFO ( "Detected \" %s \" in links's user data" , scansStr . c_str ());
2015-10-13 12:50:25 -04:00
if ( ! scansStr . empty ())
{
std :: list < std :: string > strs = uSplit ( scansStr , ':' );
if ( strs . size () == 2 )
{
std :: list < std :: string > strIds = uSplit ( strs . rbegin () -> c_str (), ';' );
for ( std :: list < std :: string >:: iterator iter = strIds . begin (); iter != strIds . end (); ++ iter )
{
ids . push_back ( atoi ( iter -> c_str ()));
if ( ids . back () == link . from ())
{
ids . pop_back ();
}
}
}
}
}
if ( ids . size ())
{
//add other scans matching
//optimize the path's poses locally
std :: map < int , rtabmap :: Transform > poses ;
for ( unsigned int i = 0 ; i < ids . size (); ++ i )
{
if ( uContains ( poses_ , ids [ i ]))
{
poses . insert ( * poses_ . find ( ids [ i ]));
}
else
{
UERROR ( "Not found %d node!" , ids [ i ]);
}
}
if ( poses . size ())
{
2015-12-17 14:13:19 -05:00
Optimizer * optimizer = Optimizer :: create ( ui_ -> parameters_toolbox -> getParameters ());
2015-11-27 11:09:39 -05:00
2015-10-13 12:50:25 -04:00
UASSERT ( uContains ( poses , link . to ()));
std :: map < int , rtabmap :: Transform > posesOut ;
std :: multimap < int , rtabmap :: Link > linksOut ;
optimizer -> getConnectedGraph (
link . to (),
poses ,
updateLinksWithModifications ( links_ ),
posesOut ,
linksOut );
2016-05-20 11:44:50 -04:00
if ( poses . size () != posesOut . size ())
{
UWARN ( "Scan poses input and output are different! %d vs %d" , ( int ) poses . size (), ( int ) posesOut . size ());
UWARN ( "Input poses: " );
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
UWARN ( " %d" , iter -> first );
}
UWARN ( "Input links: " );
std :: multimap < int , Link > modifiedLinks = updateLinksWithModifications ( links_ );
for ( std :: multimap < int , Link >:: iterator iter = modifiedLinks . begin (); iter != modifiedLinks . end (); ++ iter )
{
UWARN ( " %d->%d" , iter -> second . from (), iter -> second . to ());
}
}
2015-10-13 12:50:25 -04:00
QTime time ;
time . start ();
std :: map < int , rtabmap :: Transform > finalPoses = optimizer -> optimize ( link . to (), posesOut , linksOut );
delete optimizer ;
// transform local poses in loop referential
Transform u = t * finalPoses . at ( link . to ()). inverse ();
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr assembledScans ( new pcl :: PointCloud < pcl :: PointXYZ > );
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr graph ( new pcl :: PointCloud < pcl :: PointXYZ > );
for ( std :: map < int , Transform >:: iterator iter = finalPoses . begin (); iter != finalPoses . end (); ++ iter )
{
iter -> second = u * iter -> second ;
if ( iter -> first != link . to ()) // already added to view
{
//create scan
2015-10-16 14:54:47 -04:00
SensorData data ;
dbDriver_ -> getNodeData ( iter -> first , data );
2015-10-13 12:50:25 -04:00
cv :: Mat scan ;
data . uncompressDataConst ( 0 , 0 , & scan , 0 );
if ( ! scan . empty ())
{
2016-08-24 12:15:32 -04:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr scanCloud = util3d :: laserScanToPointCloud ( scan , data . laserScanInfo (). localTransform ());
2015-10-13 12:50:25 -04:00
if ( assembledScans -> size () == 0 )
{
assembledScans = util3d :: transformPointCloud ( scanCloud , iter -> second );
}
else
{
* assembledScans += * util3d :: transformPointCloud ( scanCloud , iter -> second );
}
}
}
graph -> push_back ( pcl :: PointXYZ ( iter -> second . x (), iter -> second . y (), iter -> second . z ()));
}
if ( assembledScans -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "scan2" , assembledScans , Transform :: getIdentity (), Qt :: cyan );
2015-10-13 12:50:25 -04:00
}
if ( graph -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addOrUpdateGraph ( "scan2graph" , graph , Qt :: cyan );
2015-10-13 12:50:25 -04:00
}
}
}
}
// Added loop closure scans
2016-09-07 17:14:09 -04:00
constraintsViewer_ -> removeCloud ( "scan0" );
constraintsViewer_ -> removeCloud ( "scan1" );
if ( dataFrom . laserScanRaw (). channels () == 6 )
2015-07-24 16:29:45 -04:00
{
2016-09-07 17:14:09 -04:00
pcl :: PointCloud < pcl :: PointNormal >:: Ptr scan ;
scan = rtabmap :: util3d :: laserScanToPointCloudNormal ( dataFrom . laserScanRaw (), dataFrom . laserScanInfo (). localTransform ());
constraintsViewer_ -> addCloud ( "scan0" , scan , Transform :: getIdentity (), Qt :: yellow );
2015-07-24 16:29:45 -04:00
}
else
{
2016-09-07 17:14:09 -04:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr scan ;
scan = rtabmap :: util3d :: laserScanToPointCloud ( dataFrom . laserScanRaw (), dataFrom . laserScanInfo (). localTransform ());
constraintsViewer_ -> addCloud ( "scan0" , scan , Transform :: getIdentity (), Qt :: yellow );
2015-07-24 16:29:45 -04:00
}
2016-09-07 17:14:09 -04:00
if ( dataTo . laserScanRaw (). channels () == 6 )
2015-07-24 16:29:45 -04:00
{
2016-09-07 17:14:09 -04:00
pcl :: PointCloud < pcl :: PointNormal >:: Ptr scan ;
scan = rtabmap :: util3d :: laserScanToPointCloudNormal ( dataTo . laserScanRaw (), t * dataTo . laserScanInfo (). localTransform ());
constraintsViewer_ -> addCloud ( "scan1" , scan , Transform :: getIdentity (), Qt :: magenta );
2015-07-24 16:29:45 -04:00
}
else
{
2016-09-07 17:14:09 -04:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr scan ;
scan = rtabmap :: util3d :: laserScanToPointCloud ( dataTo . laserScanRaw (), t * dataTo . laserScanInfo (). localTransform ());
constraintsViewer_ -> addCloud ( "scan1" , scan , Transform :: getIdentity (), Qt :: magenta );
2015-07-24 16:29:45 -04:00
}
2015-05-03 18:16:55 -04:00
}
2015-10-07 16:54:36 -04:00
else
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "scan0" );
constraintsViewer_ -> removeCloud ( "scan1" );
constraintsViewer_ -> removeCloud ( "scan2" );
2015-10-07 16:54:36 -04:00
}
2015-02-25 17:06:52 -05:00
}
else
{
if ( scanFrom -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "scan0" , scanFrom , Transform :: getIdentity (), Qt :: yellow );
2015-02-25 17:06:52 -05:00
}
2015-05-03 18:16:55 -04:00
else
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "scan0" );
2015-05-03 18:16:55 -04:00
}
2015-02-25 17:06:52 -05:00
if ( scanTo -> size ())
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addCloud ( "scan1" , scanTo , Transform :: getIdentity (), Qt :: magenta );
2014-10-30 14:43:43 +00:00
}
2015-05-03 18:16:55 -04:00
else
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "scan1" );
2015-05-03 18:16:55 -04:00
}
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeCloud ( "scan2" );
2014-10-13 19:10:22 +00:00
}
2014-12-14 16:42:10 -05:00
2015-07-24 16:29:45 -04:00
//update coordinate
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addOrUpdateCoordinate ( "from_coordinate" , Transform :: getIdentity (), 0.2 );
constraintsViewer_ -> addOrUpdateCoordinate ( "to_coordinate" , t , 0.2 );
2016-01-10 13:07:42 -05:00
if ( uContains ( groundTruthPoses_ , link . from ()) && uContains ( groundTruthPoses_ , link . to ()))
{
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> addOrUpdateCoordinate ( "to_coordinate_gt" ,
2016-01-10 13:07:42 -05:00
groundTruthPoses_ . at ( link . from ()). inverse () * groundTruthPoses_ . at ( link . to ()), 0.1 );
}
2015-07-24 16:29:45 -04:00
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> clearTrajectory ();
2014-12-14 16:42:10 -05:00
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> update ();
2014-10-13 19:10:22 +00:00
}
2014-08-11 15:23:06 +00:00
// update buttons
updateConstraintButtons ();
}
void DatabaseViewer :: updateConstraintButtons ()
{
ui_ -> pushButton_refine -> setEnabled ( false );
2014-11-03 19:14:02 +00:00
ui_ -> pushButton_refineVisually -> setEnabled ( false );
2014-08-11 15:23:06 +00:00
ui_ -> pushButton_reset -> setEnabled ( false );
ui_ -> pushButton_add -> setEnabled ( false );
ui_ -> pushButton_reject -> setEnabled ( false );
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
if ( from != to && from && to )
{
if (( ! containsLink ( links_ , from , to ) && ! containsLink ( linksAdded_ , from , to )) ||
containsLink ( linksRemoved_ , from , to ))
{
ui_ -> pushButton_add -> setEnabled ( true );
}
}
Link currentLink = findActiveLink ( from , to );
if ( currentLink . isValid () &&
(( currentLink . from () == from && currentLink . to () == to ) || ( currentLink . from () == to && currentLink . to () == from )))
{
if ( ! containsLink ( linksRemoved_ , from , to ))
{
2015-10-24 17:03:44 -04:00
ui_ -> pushButton_reject -> setEnabled (
currentLink . type () != Link :: kNeighbor &&
currentLink . type () != Link :: kNeighborMerged );
2014-08-11 15:23:06 +00:00
}
//check for modified link
bool modified = false ;
2015-02-06 13:52:02 -05:00
std :: multimap < int , Link >:: iterator iter = rtabmap :: graph :: findLink ( linksRefined_ , currentLink . from (), currentLink . to ());
2014-08-11 15:23:06 +00:00
if ( iter != linksRefined_ . end ())
{
currentLink = iter -> second ;
ui_ -> pushButton_reset -> setEnabled ( true );
modified = true ;
}
if ( ! modified )
{
ui_ -> pushButton_reset -> setEnabled ( false );
}
ui_ -> pushButton_refine -> setEnabled ( true );
2014-11-03 19:14:02 +00:00
ui_ -> pushButton_refineVisually -> setEnabled ( true );
2014-08-11 15:23:06 +00:00
}
2014-02-11 23:04:22 +00:00
}
void DatabaseViewer :: sliderIterationsValueChanged ( int value )
{
2015-10-16 14:54:47 -04:00
if ( dbDriver_ && value >= 0 && value < ( int ) graphes_ . size ())
2014-02-11 23:04:22 +00:00
{
2015-09-28 15:30:01 -04:00
std :: map < int , rtabmap :: Transform > & graph = uValueAt ( graphes_ , value );
2015-10-14 10:03:36 -04:00
std :: map < int , rtabmap :: Transform > graphFiltered = graph ;
if ( ui_ -> groupBox_posefiltering -> isChecked ())
{
graphFiltered = graph :: radiusPosesFiltering ( graph ,
ui_ -> doubleSpinBox_posefilteringRadius -> value (),
ui_ -> doubleSpinBox_posefilteringAngle -> value () * CV_PI / 180.0 );
}
2016-08-25 23:47:01 -04:00
std :: map < int , std :: pair < cv :: Mat , cv :: Mat > > localMaps ;
2015-10-14 10:03:36 -04:00
if ( ui_ -> dockWidget_graphView -> isVisible ())
2014-02-11 23:04:22 +00:00
{
//update scans
2015-03-04 17:27:44 -05:00
UINFO ( "Update local maps list..." );
2015-10-14 10:03:36 -04:00
std :: vector < int > ids = uKeys ( graphFiltered );
2015-09-28 15:30:01 -04:00
for ( unsigned int i = 0 ; i < ids . size (); ++ i )
2014-02-11 23:04:22 +00:00
{
2016-08-25 23:47:01 -04:00
if ( generatedLocalMaps_ . find ( ids [ i ]) != generatedLocalMaps_ . end ())
2014-02-11 23:04:22 +00:00
{
2016-08-25 23:47:01 -04:00
localMaps . insert ( * generatedLocalMaps_ . find ( ids [ i ]));
}
else if ( localMaps_ . find ( ids [ i ]) != localMaps_ . end ())
{
localMaps . insert ( * localMaps_ . find ( ids . at ( i )));
}
else
{
SensorData data ;
dbDriver_ -> getNodeData ( ids . at ( i ), data );
cv :: Mat ground , obstacles ;
data . uncompressData ( 0 , 0 , 0 , 0 , & ground , & obstacles );
localMaps_ . insert ( std :: make_pair ( ids . at ( i ), std :: make_pair ( ground , obstacles )));
2014-02-11 23:04:22 +00:00
}
}
2015-10-14 11:02:36 -04:00
//cleanup
for ( std :: map < int , std :: pair < cv :: Mat , cv :: Mat > >:: iterator iter = localMaps_ . begin (); iter != localMaps_ . end ();)
{
if ( graphFiltered . find ( iter -> first ) == graphFiltered . end ())
{
localMaps_ . erase ( iter ++ );
}
else
{
++ iter ;
}
}
2015-03-04 17:27:44 -05:00
UINFO ( "Update local maps list... done" );
2014-02-11 23:04:22 +00:00
}
2015-09-28 15:30:01 -04:00
2016-01-07 19:39:21 -05:00
ui_ -> graphViewer -> updateGTGraph ( groundTruthPoses_ );
2015-05-23 13:36:36 -04:00
ui_ -> graphViewer -> updateGraph ( graph , graphLinks_ , mapIds_ );
2016-08-25 23:47:01 -04:00
if ( graph . size () && localMaps . size () && ui_ -> graphViewer -> isGridMapVisible ())
2014-07-29 21:48:37 +00:00
{
float xMin , yMin ;
2015-03-04 17:27:44 -05:00
float cell = ui_ -> doubleSpinBox_gridCellSize -> value ();
2015-03-12 17:00:56 -04:00
cv :: Mat map ;
2015-03-20 14:03:09 -04:00
QTime time ;
time . start ();
2016-08-25 23:47:01 -04:00
map = rtabmap :: util3d :: create2DMapFromOccupancyLocalMaps ( graphFiltered , localMaps , cell , xMin , yMin , 0 , ui_ -> checkBox_gridErode -> isChecked ());
2015-03-12 17:00:56 -04:00
if ( ! map . empty ())
{
ui_ -> graphViewer -> updateMap ( rtabmap :: util3d :: convertMap2Image8U ( map ), cell , xMin , yMin );
}
2015-03-20 14:03:09 -04:00
ui_ -> label_timeGrid -> setNum ( double ( time . elapsed ()) / 1000.0 );
2014-07-29 21:48:37 +00:00
}
2015-03-12 17:00:56 -04:00
ui_ -> graphViewer -> update ();
2014-02-11 23:04:22 +00:00
ui_ -> label_iterations -> setNum ( value );
2014-06-09 22:05:52 +00:00
//compute total length (neighbor links)
float length = 0.0f ;
2015-05-23 13:36:36 -04:00
for ( std :: multimap < int , rtabmap :: Link >:: const_iterator iter = graphLinks_ . begin (); iter != graphLinks_ . end (); ++ iter )
2014-06-09 22:05:52 +00:00
{
std :: map < int , rtabmap :: Transform >:: const_iterator jterA = graph . find ( iter -> first );
std :: map < int , rtabmap :: Transform >:: const_iterator jterB = graph . find ( iter -> second . to ());
if ( jterA != graph . end () && jterB != graph . end ())
{
const rtabmap :: Transform & poseA = jterA -> second ;
const rtabmap :: Transform & poseB = jterB -> second ;
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () == rtabmap :: Link :: kNeighbor ||
iter -> second . type () == rtabmap :: Link :: kNeighborMerged )
2014-06-09 22:05:52 +00:00
{
Eigen :: Vector3f vA , vB ;
2016-06-12 13:51:49 -04:00
float x , y , z ;
poseA . getTranslation ( x , y , z );
vA [ 0 ] = x ; vA [ 1 ] = y ; vA [ 2 ] = z ;
poseB . getTranslation ( x , y , z );
vB [ 0 ] = x ; vB [ 1 ] = y ; vB [ 2 ] = z ;
2014-06-09 22:05:52 +00:00
length += ( vB - vA ). norm ();
}
}
}
ui_ -> label_pathLength -> setNum ( length );
2014-02-11 23:04:22 +00:00
}
}
void DatabaseViewer :: updateGraphView ()
{
2015-05-23 13:36:36 -04:00
ui_ -> label_loopClosures -> clear ();
2015-10-24 17:03:44 -04:00
ui_ -> label_poses -> clear ();
2016-01-07 19:39:21 -05:00
2014-12-14 16:42:10 -05:00
if ( poses_ . size ())
2014-02-11 23:04:22 +00:00
{
2015-05-23 13:36:36 -04:00
int fromId = ui_ -> spinBox_optimizationsFrom -> value ();
if ( ! uContains ( poses_ , fromId ))
2014-02-11 23:04:22 +00:00
{
QMessageBox :: warning ( this , tr ( "" ), tr ( "Graph optimization from id (%1) for which node is not linked to graph. \n Minimum=%2, Maximum=%3" )
2015-05-23 13:36:36 -04:00
. arg ( fromId )
2014-02-11 23:04:22 +00:00
. arg ( poses_ . begin () -> first )
. arg ( poses_ . rbegin () -> first ));
return ;
}
graphes_ . clear ();
2015-05-23 13:36:36 -04:00
graphLinks_ . clear ();
std :: map < int , rtabmap :: Transform > poses = poses_ ;
// filter current map if not spanning to all maps
if ( ! ui_ -> checkBox_spanAllMaps -> isChecked () && uContains ( mapIds_ , fromId ) && mapIds_ . at ( fromId ) >= 0 )
{
int currentMapId = mapIds_ . at ( fromId );
for ( std :: map < int , rtabmap :: Transform >:: iterator iter = poses . begin (); iter != poses . end ();)
{
if ( ! uContains ( mapIds_ , iter -> first ) ||
mapIds_ . at ( iter -> first ) != currentMapId )
{
2015-05-23 14:58:11 -04:00
poses . erase ( iter ++ );
2015-05-23 13:36:36 -04:00
}
else
{
++ iter ;
}
}
}
graphes_ . push_back ( poses );
2014-02-11 23:04:22 +00:00
2014-02-27 17:00:46 +00:00
ui_ -> actionGenerate_TORO_graph_graph -> setEnabled ( true );
2015-09-02 17:51:43 -04:00
ui_ -> actionGenerate_g2o_graph_g2o -> setEnabled ( true );
2015-05-23 13:36:36 -04:00
std :: multimap < int , rtabmap :: Link > links = links_ ;
// filter current map if not spanning to all maps
if ( ! ui_ -> checkBox_spanAllMaps -> isChecked () && uContains ( mapIds_ , fromId ) && mapIds_ . at ( fromId ) >= 0 )
{
int currentMapId = mapIds_ . at ( fromId );
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = links . begin (); iter != links . end ();)
{
if ( ! uContains ( mapIds_ , iter -> second . from ()) ||
! uContains ( mapIds_ , iter -> second . to ()) ||
mapIds_ . at ( iter -> second . from ()) != currentMapId ||
mapIds_ . at ( iter -> second . to ()) != currentMapId )
{
2015-05-23 14:58:11 -04:00
links . erase ( iter ++ );
2015-05-23 13:36:36 -04:00
}
else
{
++ iter ;
}
}
}
2015-04-01 14:34:43 -04:00
if ( ui_ -> checkBox_ignorePoseCorrection -> isChecked ())
{
2015-05-23 13:36:36 -04:00
std :: multimap < int , Link > tmp = links ;
2016-07-24 13:23:22 -04:00
std :: multimap < int , Link >:: iterator jter = links . begin ();
for ( std :: multimap < int , Link >:: iterator iter = tmp . begin (); iter != tmp . end (); ++ iter , ++ jter )
2015-04-01 14:34:43 -04:00
{
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () == Link :: kNeighbor ||
iter -> second . type () == Link :: kNeighborMerged )
2015-04-01 14:34:43 -04:00
{
2015-05-23 13:36:36 -04:00
Transform poseFrom = uValue ( poses , iter -> second . from (), Transform ());
Transform poseTo = uValue ( poses , iter -> second . to (), Transform ());
2015-04-01 14:34:43 -04:00
if ( ! poseFrom . isNull () && ! poseTo . isNull ())
{
2016-07-24 13:23:22 -04:00
// reset to identity covariance
iter -> second = Link (
iter -> second . from (),
iter -> second . to (),
iter -> second . type (),
poseFrom . inverse () * poseTo );
2015-04-01 14:34:43 -04:00
}
}
}
links = updateLinksWithModifications ( tmp );
}
else
{
2015-05-23 13:36:36 -04:00
links = updateLinksWithModifications ( links );
2015-04-01 14:34:43 -04:00
}
2015-05-23 13:36:36 -04:00
// filter links
2015-10-24 17:03:44 -04:00
int totalNeighbor = 0 ;
int totalNeighborMerged = 0 ;
2015-05-23 13:36:36 -04:00
int totalGlobal = 0 ;
int totalLocalTime = 0 ;
int totalLocalSpace = 0 ;
int totalUser = 0 ;
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = links . begin (); iter != links . end ();)
{
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () == Link :: kNeighbor )
{
++ totalNeighbor ;
}
else if ( iter -> second . type () == Link :: kNeighborMerged )
{
++ totalNeighborMerged ;
}
else if ( iter -> second . type () == Link :: kGlobalClosure )
2015-05-23 13:36:36 -04:00
{
if ( ui_ -> checkBox_ignoreGlobalLoop -> isChecked ())
{
2015-05-23 14:58:11 -04:00
links . erase ( iter ++ );
2015-05-23 13:36:36 -04:00
continue ;
}
++ totalGlobal ;
}
else if ( iter -> second . type () == Link :: kLocalSpaceClosure )
{
if ( ui_ -> checkBox_ignoreLocalLoopSpace -> isChecked ())
{
2015-05-23 14:58:11 -04:00
links . erase ( iter ++ );
2015-05-23 13:36:36 -04:00
continue ;
}
++ totalLocalSpace ;
}
else if ( iter -> second . type () == Link :: kLocalTimeClosure )
{
if ( ui_ -> checkBox_ignoreLocalLoopTime -> isChecked ())
{
2015-05-23 14:58:11 -04:00
links . erase ( iter ++ );
2015-05-23 13:36:36 -04:00
continue ;
}
++ totalLocalTime ;
}
else if ( iter -> second . type () == Link :: kUserClosure )
{
if ( ui_ -> checkBox_ignoreUserLoop -> isChecked ())
{
2015-05-23 14:58:11 -04:00
links . erase ( iter ++ );
2015-05-23 13:36:36 -04:00
continue ;
}
++ totalUser ;
}
++ iter ;
}
2015-10-24 17:03:44 -04:00
ui_ -> label_loopClosures -> setText ( tr ( "(%1, %2, %3, %4, %5, %6)" )
. arg ( totalNeighbor )
. arg ( totalNeighborMerged )
. arg ( totalGlobal )
. arg ( totalLocalSpace )
. arg ( totalLocalTime )
. arg ( totalUser ));
2015-05-23 13:36:36 -04:00
2015-12-17 14:13:19 -05:00
Optimizer * optimizer = Optimizer :: create ( ui_ -> parameters_toolbox -> getParameters ());
2015-11-27 11:09:39 -05:00
2015-03-12 17:00:56 -04:00
std :: map < int , rtabmap :: Transform > posesOut ;
std :: multimap < int , rtabmap :: Link > linksOut ;
2015-10-24 17:03:44 -04:00
UINFO ( "Get connected graph from %d (%d poses, %d links)" , fromId , ( int ) poses . size (), ( int ) links . size ());
2015-03-12 17:00:56 -04:00
optimizer -> getConnectedGraph (
fromId ,
2015-05-23 13:36:36 -04:00
poses ,
2015-03-12 17:00:56 -04:00
links ,
posesOut ,
2015-03-16 18:22:54 -04:00
linksOut ,
ui_ -> spinBox_optimizationDepth -> value ());
2015-10-14 10:03:36 -04:00
UINFO ( "Connected graph of %d poses and %d links" , ( int ) posesOut . size (), ( int ) linksOut . size ());
2015-03-12 17:00:56 -04:00
QTime time ;
time . start ();
2015-05-23 13:36:36 -04:00
std :: map < int , rtabmap :: Transform > finalPoses = optimizer -> optimize ( fromId , posesOut , linksOut , & graphes_ );
2015-03-12 17:00:56 -04:00
ui_ -> label_timeOptimization -> setNum ( double ( time . elapsed ()) / 1000.0 );
2014-02-11 23:04:22 +00:00
graphes_ . push_back ( finalPoses );
2015-05-23 13:36:36 -04:00
graphLinks_ = linksOut ;
2015-10-24 17:03:44 -04:00
ui_ -> label_poses -> setNum (( int ) finalPoses . size ());
2015-03-12 17:00:56 -04:00
delete optimizer ;
2015-09-02 17:51:43 -04:00
if ( posesOut . size () && finalPoses . empty ())
{
QMessageBox :: warning ( this , tr ( "Graph optimization error!" ), tr ( "Graph optimization has failed. See the terminal for potential errors." ));
}
2016-01-07 19:39:21 -05:00
if ( uContains ( groundTruthPoses_ , fromId ) && uContains ( posesOut , fromId ))
{
// adjust the ground truth to fit the root
Transform t = posesOut . at ( fromId ) * groundTruthPoses_ . at ( fromId ). inverse ();
for ( std :: map < int , Transform >:: iterator iter = groundTruthPoses_ . begin (); iter != groundTruthPoses_ . end (); ++ iter )
{
iter -> second = t * iter -> second ;
}
}
else if ( groundTruthPoses_ . size ())
{
UWARN ( "Could not find ground truth for root node %d" , fromId );
}
2014-02-11 23:04:22 +00:00
}
if ( graphes_ . size ())
{
2015-03-25 16:30:05 -04:00
ui_ -> horizontalSlider_iterations -> setMaximum (( int ) graphes_ . size () - 1 );
ui_ -> horizontalSlider_iterations -> setValue (( int ) graphes_ . size () - 1 );
2015-03-04 17:27:44 -05:00
ui_ -> horizontalSlider_iterations -> setEnabled ( true );
ui_ -> spinBox_optimizationsFrom -> setEnabled ( true );
2015-03-25 16:30:05 -04:00
sliderIterationsValueChanged (( int ) graphes_ . size () - 1 );
2014-02-11 23:04:22 +00:00
}
else
{
2015-03-04 17:27:44 -05:00
ui_ -> horizontalSlider_iterations -> setEnabled ( false );
ui_ -> spinBox_optimizationsFrom -> setEnabled ( false );
}
}
void DatabaseViewer :: updateGrid ()
{
2016-08-26 13:02:00 -04:00
update3dView ();
2016-08-25 23:47:01 -04:00
updateGraphView ();
2014-02-11 23:04:22 +00:00
}
2014-08-11 15:23:06 +00:00
Link DatabaseViewer :: findActiveLink ( int from , int to )
{
Link link ;
2015-02-06 13:52:02 -05:00
std :: multimap < int , Link >:: iterator findIter = rtabmap :: graph :: findLink ( linksRefined_ , from , to );
2014-08-11 15:23:06 +00:00
if ( findIter != linksRefined_ . end ())
{
link = findIter -> second ;
}
else
{
2015-02-06 13:52:02 -05:00
findIter = rtabmap :: graph :: findLink ( linksAdded_ , from , to );
2014-08-11 15:23:06 +00:00
if ( findIter != linksAdded_ . end ())
{
link = findIter -> second ;
}
else if ( ! containsLink ( linksRemoved_ , from , to ))
{
2015-02-06 13:52:02 -05:00
findIter = rtabmap :: graph :: findLink ( links_ , from , to );
2014-08-11 15:23:06 +00:00
if ( findIter != links_ . end ())
{
link = findIter -> second ;
}
}
}
return link ;
}
bool DatabaseViewer :: containsLink ( std :: multimap < int , Link > & links , int from , int to )
{
2015-02-06 13:52:02 -05:00
return rtabmap :: graph :: findLink ( links , from , to ) != links . end ();
2014-08-11 15:23:06 +00:00
}
void DatabaseViewer :: refineConstraint ()
{
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
2015-05-03 18:16:55 -04:00
refineConstraint ( from , to , false , true );
2014-08-11 15:23:06 +00:00
}
2015-05-03 18:16:55 -04:00
void DatabaseViewer :: refineConstraint ( int from , int to , bool silent , bool updateGraph )
2014-08-11 15:23:06 +00:00
{
if ( from == to )
{
UWARN ( "Cannot refine link to same node" );
return ;
}
Link currentLink = findActiveLink ( from , to );
if ( ! currentLink . isValid ())
{
UERROR ( "Not found link! (%d->%d)" , from , to );
return ;
}
2014-12-14 16:42:10 -05:00
Transform t = currentLink . transform ();
if ( ui_ -> checkBox_showOptimized -> isChecked () &&
2015-10-24 17:03:44 -04:00
( currentLink . type () == Link :: kNeighbor || currentLink . type () == Link :: kNeighborMerged ) &&
2014-12-14 16:42:10 -05:00
graphes_ . size () &&
( int ) graphes_ . size () - 1 == ui_ -> horizontalSlider_iterations -> maximum ())
{
std :: map < int , rtabmap :: Transform > & graph = uValueAt ( graphes_ , ui_ -> horizontalSlider_iterations -> value ());
2015-10-24 17:03:44 -04:00
if ( currentLink . type () == Link :: kNeighbor || currentLink . type () == Link :: kNeighborMerged )
2014-12-14 16:42:10 -05:00
{
std :: map < int , rtabmap :: Transform >:: iterator iterFrom = graph . find ( currentLink . from ());
std :: map < int , rtabmap :: Transform >:: iterator iterTo = graph . find ( currentLink . to ());
if ( iterFrom != graph . end () && iterTo != graph . end ())
{
Transform topt = iterFrom -> second . inverse () * iterTo -> second ;
t = topt ;
}
}
}
2015-11-22 18:08:32 -05:00
else if ( ui_ -> checkBox_ignorePoseCorrection -> isChecked () &&
graph :: findLink ( linksRefined_ , from , to ) == linksRefined_ . end ())
{
if ( currentLink . type () == Link :: kNeighbor ||
currentLink . type () == Link :: kNeighborMerged )
{
Transform poseFrom = uValue ( poses_ , currentLink . from (), Transform ());
Transform poseTo = uValue ( poses_ , currentLink . to (), Transform ());
if ( ! poseFrom . isNull () && ! poseTo . isNull ())
{
t = poseFrom . inverse () * poseTo ; // recompute raw odom transformation
}
}
}
2014-08-11 15:23:06 +00:00
2014-09-21 21:14:17 +00:00
Transform transform ;
2016-01-06 17:26:55 -05:00
RegistrationInfo info ;
2014-09-21 21:14:17 +00:00
2015-05-29 14:46:48 -04:00
SensorData dataFrom , dataTo ;
2015-10-16 14:54:47 -04:00
dbDriver_ -> getNodeData ( currentLink . from (), dataFrom );
dbDriver_ -> getNodeData ( currentLink . to (), dataTo );
2014-08-11 15:23:06 +00:00
2015-12-17 14:13:19 -05:00
ParametersMap parameters = ui_ -> parameters_toolbox -> getParameters ();
2015-11-22 18:08:32 -05:00
UTimer timer ;
2016-08-21 19:33:01 -04:00
if ( ui_ -> checkBox_icp_from_depth -> isChecked ())
2014-08-11 15:23:06 +00:00
{
2015-11-22 18:08:32 -05:00
// generate laser scans from depth image
cv :: Mat tmpA , tmpB , tmpC , tmpD ;
dataFrom . uncompressData ( & tmpA , & tmpB , 0 );
dataTo . uncompressData ( & tmpC , & tmpD , 0 );
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloudFrom = util3d :: cloudFromSensorData (
dataFrom ,
2016-10-30 16:03:23 -04:00
ui_ -> spinBox_icp_decimation -> value () == 0 ? 1 : ui_ -> spinBox_icp_decimation -> value (),
2016-04-12 15:14:04 -04:00
ui_ -> doubleSpinBox_icp_maxDepth -> value (),
2016-05-31 19:09:49 -04:00
ui_ -> doubleSpinBox_icp_minDepth -> value (),
0 ,
ui_ -> parameters_toolbox -> getParameters ());
2015-11-22 18:08:32 -05:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloudTo = util3d :: cloudFromSensorData (
dataTo ,
2016-10-30 16:03:23 -04:00
ui_ -> spinBox_icp_decimation -> value () == 0 ? 1 : ui_ -> spinBox_icp_decimation -> value (),
2016-04-12 15:14:04 -04:00
ui_ -> doubleSpinBox_icp_maxDepth -> value (),
2016-05-31 19:09:49 -04:00
ui_ -> doubleSpinBox_icp_minDepth -> value (),
0 ,
ui_ -> parameters_toolbox -> getParameters ());
2015-11-22 18:08:32 -05:00
int maxLaserScans = cloudFrom -> size ();
2016-08-21 19:33:01 -04:00
dataFrom . setLaserScanRaw ( util3d :: laserScanFromPointCloud ( * util3d :: removeNaNFromPointCloud ( cloudFrom ), Transform ()), LaserScanInfo ( maxLaserScans , 0 ));
dataTo . setLaserScanRaw ( util3d :: laserScanFromPointCloud ( * util3d :: removeNaNFromPointCloud ( cloudTo ), Transform ()), LaserScanInfo ( maxLaserScans , 0 ));
2016-01-10 12:57:49 -05:00
if ( ! dataFrom . laserScanCompressed (). empty () || ! dataTo . laserScanCompressed (). empty ())
{
UWARN ( "There are laser scans in data, but generate laser scan from "
"depth image option is activated. Ignoring saved laser scans..." );
}
2014-08-11 15:23:06 +00:00
}
else
{
2015-11-22 18:08:32 -05:00
cv :: Mat tmpA , tmpB ;
dataFrom . uncompressData ( 0 , 0 , & tmpA );
dataTo . uncompressData ( 0 , 0 , & tmpB );
2014-08-11 15:23:06 +00:00
}
2015-11-22 18:08:32 -05:00
UINFO ( "Uncompress time: %f s" , timer . ticks ());
2014-08-11 15:23:06 +00:00
2015-11-22 18:08:32 -05:00
RegistrationIcp registration ( parameters );
2016-01-06 17:26:55 -05:00
transform = registration . computeTransformation ( dataFrom , dataTo , t , & info );
2015-11-22 18:08:32 -05:00
UINFO ( "Icp time: %f s" , timer . ticks ());
if ( ! transform . isNull ())
2014-08-11 15:23:06 +00:00
{
2016-01-06 17:26:55 -05:00
Link newLink ( currentLink . from (), currentLink . to (), currentLink . type (), transform , info . variance , info . variance );
2015-11-22 18:08:32 -05:00
bool updated = false ;
std :: multimap < int , Link >:: iterator iter = linksRefined_ . find ( currentLink . from ());
while ( iter != linksRefined_ . end () && iter -> first == currentLink . from ())
2014-08-11 15:23:06 +00:00
{
2015-11-22 18:08:32 -05:00
if ( iter -> second . to () == currentLink . to () &&
iter -> second . type () == currentLink . type ())
2014-08-11 15:23:06 +00:00
{
2015-11-22 18:08:32 -05:00
iter -> second = newLink ;
updated = true ;
break ;
}
++ iter ;
}
if ( ! updated )
{
linksRefined_ . insert ( std :: make_pair ( newLink . from (), newLink ));
if ( updateGraph )
{
this -> updateGraphView ();
2014-12-14 16:42:10 -05:00
}
2014-08-11 15:23:06 +00:00
}
2015-11-22 18:08:32 -05:00
if ( ui_ -> dockWidget_constraints -> isVisible ())
2014-10-14 21:06:30 +00:00
{
2015-11-22 18:08:32 -05:00
this -> updateConstraintView ( newLink , true );
2014-10-14 21:06:30 +00:00
}
2014-08-11 15:23:06 +00:00
}
2015-11-22 18:08:32 -05:00
2015-05-03 18:16:55 -04:00
else if ( ! silent )
{
QMessageBox :: warning ( this ,
tr ( "Refine link" ),
tr ( "Cannot find a transformation between nodes %1 and %2" ). arg ( from ). arg ( to ));
}
2014-08-11 15:23:06 +00:00
}
2014-11-03 19:14:02 +00:00
void DatabaseViewer :: refineConstraintVisually ()
{
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
2015-05-03 18:16:55 -04:00
refineConstraintVisually ( from , to , false , true );
2014-11-03 19:14:02 +00:00
}
2015-05-03 18:16:55 -04:00
void DatabaseViewer :: refineConstraintVisually ( int from , int to , bool silent , bool updateGraph )
2014-11-03 19:14:02 +00:00
{
2015-12-17 14:13:19 -05:00
UDEBUG ( "" );
2014-11-03 19:14:02 +00:00
if ( from == to )
{
UWARN ( "Cannot refine link to same node" );
return ;
}
Link currentLink = findActiveLink ( from , to );
if ( ! currentLink . isValid ())
{
UERROR ( "Not found link! (%d->%d)" , from , to );
return ;
}
2015-12-17 14:13:19 -05:00
ParametersMap parameters = ui_ -> parameters_toolbox -> getParameters ();
2015-10-16 14:54:47 -04:00
2014-11-03 19:14:02 +00:00
Transform t ;
2016-01-06 17:26:55 -05:00
RegistrationInfo info ;
2015-11-22 18:08:32 -05:00
2015-12-17 14:13:19 -05:00
// Add sensor data to generate features
SensorData dataFrom ;
dbDriver_ -> getNodeData ( from , dataFrom );
dataFrom . uncompressData ();
SensorData dataTo ;
dbDriver_ -> getNodeData ( to , dataTo );
dataTo . uncompressData ();
2014-11-03 19:14:02 +00:00
2015-12-17 14:13:19 -05:00
UDEBUG ( "" );
RegistrationVis reg ( parameters );
2015-12-21 17:27:05 -05:00
Signature fromS ( dataFrom );
Signature toS ( dataTo );
2016-01-06 17:26:55 -05:00
t = reg . computeTransformationMod ( fromS , toS , currentLink . transform (), & info );
2015-12-17 14:13:19 -05:00
UDEBUG ( "" );
2014-11-03 19:14:02 +00:00
2015-12-21 17:27:05 -05:00
if ( ! silent )
{
ui_ -> graphicsView_A -> setFeatures ( fromS . getWords (), dataFrom . depthRaw ());
ui_ -> graphicsView_B -> setFeatures ( toS . getWords (), dataTo . depthRaw ());
updateWordsMatching ();
}
2014-11-03 19:14:02 +00:00
if ( ! t . isNull ())
{
2016-01-06 17:26:55 -05:00
Link newLink ( currentLink . from (), currentLink . to (), currentLink . type (), t , info . variance , info . variance );
2014-11-03 19:14:02 +00:00
bool updated = false ;
std :: multimap < int , Link >:: iterator iter = linksRefined_ . find ( currentLink . from ());
while ( iter != linksRefined_ . end () && iter -> first == currentLink . from ())
{
if ( iter -> second . to () == currentLink . to () &&
iter -> second . type () == currentLink . type ())
{
iter -> second = newLink ;
updated = true ;
break ;
}
++ iter ;
}
if ( ! updated )
{
2015-09-02 17:51:43 -04:00
linksRefined_ . insert ( std :: make_pair ( newLink . from (), newLink ));
2014-12-14 16:42:10 -05:00
if ( updateGraph )
{
this -> updateGraphView ();
}
2014-11-03 19:14:02 +00:00
}
if ( ui_ -> dockWidget_constraints -> isVisible ())
{
2015-12-21 17:27:05 -05:00
this -> updateConstraintView ( newLink , false );
2014-11-03 19:14:02 +00:00
}
}
2015-05-03 18:16:55 -04:00
else if ( ! silent )
{
QMessageBox :: warning ( this ,
tr ( "Add link" ),
2016-01-07 13:59:21 -05:00
tr ( "Cannot find a transformation between nodes %1 and %2: %3" ). arg ( from ). arg ( to ). arg ( info . rejectedMsg . c_str ()));
2015-05-03 18:16:55 -04:00
}
2014-11-03 19:14:02 +00:00
}
2014-08-11 15:23:06 +00:00
void DatabaseViewer :: addConstraint ()
{
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
2014-12-14 16:42:10 -05:00
addConstraint ( from , to , false , true );
2014-08-11 15:23:06 +00:00
}
2014-12-14 16:42:10 -05:00
bool DatabaseViewer :: addConstraint ( int from , int to , bool silent , bool updateGraph )
2014-08-11 15:23:06 +00:00
{
if ( from == to )
{
UWARN ( "Cannot add link to same node" );
return false ;
}
bool updateSlider = false ;
if ( ! containsLink ( linksAdded_ , from , to ) &&
! containsLink ( links_ , from , to ))
{
UASSERT ( ! containsLink ( linksRemoved_ , from , to ));
UASSERT ( ! containsLink ( linksRefined_ , from , to ));
2015-12-17 14:13:19 -05:00
ParametersMap parameters = ui_ -> parameters_toolbox -> getParameters ();
2015-10-16 14:54:47 -04:00
2014-08-11 15:23:06 +00:00
Transform t ;
2016-01-06 17:26:55 -05:00
RegistrationInfo info ;
2015-12-17 14:13:19 -05:00
// Add sensor data to generate features
SensorData dataFrom ;
dbDriver_ -> getNodeData ( from , dataFrom );
dataFrom . uncompressData ();
SensorData dataTo ;
dbDriver_ -> getNodeData ( to , dataTo );
dataTo . uncompressData ();
2015-10-16 14:54:47 -04:00
2015-12-21 17:27:05 -05:00
UDEBUG ( "" );
RegistrationVis reg ( parameters );
Signature fromS ( dataFrom );
Signature toS ( dataTo );
2016-01-06 17:26:55 -05:00
t = reg . computeTransformationMod ( fromS , toS , Transform :: getIdentity (), & info );
2015-12-21 17:27:05 -05:00
UDEBUG ( "" );
2015-12-17 14:13:19 -05:00
if ( ! silent )
{
2015-12-21 17:27:05 -05:00
ui_ -> graphicsView_A -> setFeatures ( fromS . getWords (), dataFrom . depthRaw ());
ui_ -> graphicsView_B -> setFeatures ( toS . getWords (), dataTo . depthRaw ());
2015-12-17 14:13:19 -05:00
updateWordsMatching ();
2014-08-11 15:23:06 +00:00
}
2015-12-21 17:27:05 -05:00
2015-05-03 18:16:55 -04:00
if ( ! t . isNull ())
2014-08-11 15:23:06 +00:00
{
// transform is valid, make a link
2015-04-25 18:13:31 -04:00
if ( from > to )
{
2016-01-06 17:26:55 -05:00
linksAdded_ . insert ( std :: make_pair ( from , Link ( from , to , Link :: kUserClosure , t , info . variance , info . variance )));
2015-04-25 18:13:31 -04:00
}
else
{
2016-01-06 17:26:55 -05:00
linksAdded_ . insert ( std :: make_pair ( to , Link ( to , from , Link :: kUserClosure , t . inverse (), info . variance , info . variance )));
2015-04-25 18:13:31 -04:00
}
2014-08-11 15:23:06 +00:00
updateSlider = true ;
}
2015-05-03 18:16:55 -04:00
else if ( ! silent )
{
QMessageBox :: warning ( this ,
tr ( "Add link" ),
2016-01-07 13:59:21 -05:00
tr ( "Cannot find a transformation between nodes %1 and %2: %3" ). arg ( from ). arg ( to ). arg ( info . rejectedMsg . c_str ()));
2015-05-03 18:16:55 -04:00
}
2014-08-11 15:23:06 +00:00
}
else if ( containsLink ( linksRemoved_ , from , to ))
{
//simply remove from linksRemoved
2015-02-06 13:52:02 -05:00
linksRemoved_ . erase ( rtabmap :: graph :: findLink ( linksRemoved_ , from , to ));
2014-08-11 15:23:06 +00:00
updateSlider = true ;
}
if ( updateSlider )
{
updateLoopClosuresSlider ( from , to );
2014-12-14 16:42:10 -05:00
if ( updateGraph )
{
this -> updateGraphView ();
}
2014-08-11 15:23:06 +00:00
}
return updateSlider ;
}
void DatabaseViewer :: resetConstraint ()
{
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
if ( from < to )
{
int tmp = to ;
to = from ;
from = tmp ;
}
if ( from == to )
{
UWARN ( "Cannot reset link to same node" );
return ;
}
2015-02-06 13:52:02 -05:00
std :: multimap < int , Link >:: iterator iter = rtabmap :: graph :: findLink ( linksRefined_ , from , to );
2014-08-11 15:23:06 +00:00
if ( iter != linksRefined_ . end ())
{
linksRefined_ . erase ( iter );
2014-12-14 16:42:10 -05:00
this -> updateGraphView ();
2014-08-11 15:23:06 +00:00
}
2015-02-06 13:52:02 -05:00
iter = rtabmap :: graph :: findLink ( links_ , from , to );
2014-08-11 15:23:06 +00:00
if ( iter != links_ . end ())
{
this -> updateConstraintView ( iter -> second );
}
2015-02-06 13:52:02 -05:00
iter = rtabmap :: graph :: findLink ( linksAdded_ , from , to );
2014-09-21 21:14:17 +00:00
if ( iter != linksAdded_ . end ())
{
this -> updateConstraintView ( iter -> second );
}
2014-08-11 15:23:06 +00:00
}
void DatabaseViewer :: rejectConstraint ()
{
int from = ids_ . at ( ui_ -> horizontalSlider_A -> value ());
int to = ids_ . at ( ui_ -> horizontalSlider_B -> value ());
if ( from < to )
{
int tmp = to ;
to = from ;
from = tmp ;
}
if ( from == to )
{
UWARN ( "Cannot reject link to same node" );
return ;
}
2014-12-14 16:42:10 -05:00
bool removed = false ;
2014-08-11 15:23:06 +00:00
// find the original one
std :: multimap < int , Link >:: iterator iter ;
2015-02-06 13:52:02 -05:00
iter = rtabmap :: graph :: findLink ( links_ , from , to );
2014-08-11 15:23:06 +00:00
if ( iter != links_ . end ())
{
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () == Link :: kNeighbor || iter -> second . type () == Link :: kNeighborMerged )
2014-08-11 15:23:06 +00:00
{
UWARN ( "Cannot reject neighbor links (%d->%d)" , from , to );
return ;
}
linksRemoved_ . insert ( * iter );
2014-12-14 16:42:10 -05:00
removed = true ;
2014-08-11 15:23:06 +00:00
}
// remove from refined and added
2015-02-06 13:52:02 -05:00
iter = rtabmap :: graph :: findLink ( linksRefined_ , from , to );
2014-08-11 15:23:06 +00:00
if ( iter != linksRefined_ . end ())
{
linksRefined_ . erase ( iter );
2014-12-14 16:42:10 -05:00
removed = true ;
2014-08-11 15:23:06 +00:00
}
2015-02-06 13:52:02 -05:00
iter = rtabmap :: graph :: findLink ( linksAdded_ , from , to );
2014-08-11 15:23:06 +00:00
if ( iter != linksAdded_ . end ())
{
linksAdded_ . erase ( iter );
2014-12-14 16:42:10 -05:00
removed = true ;
}
if ( removed )
{
this -> updateGraphView ();
2014-08-11 15:23:06 +00:00
}
updateLoopClosuresSlider ();
}
std :: multimap < int , rtabmap :: Link > DatabaseViewer :: updateLinksWithModifications (
const std :: multimap < int , rtabmap :: Link > & edgeConstraints )
{
std :: multimap < int , rtabmap :: Link > links ;
for ( std :: multimap < int , rtabmap :: Link >:: const_iterator iter = edgeConstraints . begin ();
iter != edgeConstraints . end ();
++ iter )
{
std :: multimap < int , rtabmap :: Link >:: iterator findIter ;
2015-02-06 13:52:02 -05:00
findIter = rtabmap :: graph :: findLink ( linksRemoved_ , iter -> second . from (), iter -> second . to ());
2014-08-11 15:23:06 +00:00
if ( findIter != linksRemoved_ . end ())
{
if ( ! ( iter -> second . from () == findIter -> second . from () &&
iter -> second . to () == findIter -> second . to () &&
iter -> second . type () == findIter -> second . type ()))
{
UWARN ( "Links (%d->%d,%d) and (%d->%d,%d) are not equal!?" ,
iter -> second . from (), iter -> second . to (), iter -> second . type (),
findIter -> second . from (), findIter -> second . to (), findIter -> second . type ());
}
else
{
//UINFO("Removed link (%d->%d, %d)", iter->second.from(), iter->second.to(), iter->second.type());
continue ; // don't add this link
}
}
2015-02-06 13:52:02 -05:00
findIter = rtabmap :: graph :: findLink ( linksRefined_ , iter -> second . from (), iter -> second . to ());
2014-08-11 15:23:06 +00:00
if ( findIter != linksRefined_ . end ())
{
if ( iter -> second . from () == findIter -> second . from () &&
iter -> second . to () == findIter -> second . to () &&
iter -> second . type () == findIter -> second . type ())
{
links . insert ( * findIter ); // add the refined link
//UINFO("Updated link (%d->%d, %d)", iter->second.from(), iter->second.to(), iter->second.type());
continue ;
}
else
{
UWARN ( "Links (%d->%d,%d) and (%d->%d,%d) are not equal!?" ,
iter -> second . from (), iter -> second . to (), iter -> second . type (),
findIter -> second . from (), findIter -> second . to (), findIter -> second . type ());
}
}
links . insert ( * iter ); // add original link
}
//look for added links
for ( std :: multimap < int , rtabmap :: Link >:: const_iterator iter = linksAdded_ . begin ();
iter != linksAdded_ . end ();
++ iter )
{
//UINFO("Added link (%d->%d, %d)", iter->second.from(), iter->second.to(), iter->second.type());
links . insert ( * iter );
}
return links ;
}
void DatabaseViewer :: updateLoopClosuresSlider ( int from , int to )
{
int size = loopLinks_ . size ();
loopLinks_ . clear ();
std :: multimap < int , Link > links = updateLinksWithModifications ( links_ );
int position = ui_ -> horizontalSlider_loops -> value ();
for ( std :: multimap < int , rtabmap :: Link >:: iterator iter = links . begin (); iter != links . end (); ++ iter )
{
if ( ! iter -> second . transform (). isNull ())
{
2015-10-24 17:03:44 -04:00
if ( iter -> second . type () != rtabmap :: Link :: kNeighbor &&
iter -> second . type () != rtabmap :: Link :: kNeighborMerged )
2014-08-11 15:23:06 +00:00
{
if (( iter -> second . from () == from && iter -> second . to () == to ) ||
( iter -> second . to () == from && iter -> second . from () == to ))
{
position = loopLinks_ . size ();
}
loopLinks_ . append ( iter -> second );
}
}
else
{
UERROR ( "Transform null for link from %d to %d" , iter -> first , iter -> second . to ());
}
}
if ( loopLinks_ . size ())
{
2015-04-23 22:01:41 -04:00
if ( loopLinks_ . size () == 1 )
{
// just to be able to move the cursor of the loop slider
loopLinks_ . push_back ( loopLinks_ . front ());
}
2014-08-11 15:23:06 +00:00
ui_ -> horizontalSlider_loops -> setMinimum ( 0 );
ui_ -> horizontalSlider_loops -> setMaximum ( loopLinks_ . size () - 1 );
ui_ -> horizontalSlider_loops -> setEnabled ( true );
if ( position != ui_ -> horizontalSlider_loops -> value ())
{
ui_ -> horizontalSlider_loops -> setValue ( position );
}
else if ( size != loopLinks_ . size ())
{
this -> updateConstraintView ( loopLinks_ . at ( position ));
}
}
else
{
ui_ -> horizontalSlider_loops -> setEnabled ( false );
2016-05-05 18:25:26 -04:00
constraintsViewer_ -> removeAllClouds ();
constraintsViewer_ -> update ();
2014-08-11 15:23:06 +00:00
updateConstraintButtons ();
}
}
2015-12-17 14:13:19 -05:00
void DatabaseViewer :: notifyParametersChanged ( const QStringList & parametersChanged )
{
bool updateStereo = false ;
bool updateGraphView = false ;
for ( QStringList :: const_iterator iter = parametersChanged . constBegin ();
iter != parametersChanged . constEnd () && ( ! updateStereo || ! updateGraphView );
++ iter )
{
QString group = iter -> split ( '/' ). first ();
if ( ! updateStereo && group == "Stereo" )
{
updateStereo = true ;
continue ;
}
if ( ! updateGraphView && group == "Optimize" )
{
updateGraphView = true ;
continue ;
}
}
if ( updateStereo )
{
this -> updateStereo ();
}
if ( updateGraphView )
{
this -> updateGraphView ();
}
this -> configModified ();
}
2014-08-11 15:23:06 +00:00
} // namespace rtabmap