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
#include "rtabmap/gui/MainWindow.h"
2013-02-04 16:38:00 +00:00
2011-06-05 14:45:39 +00:00
#include "ui_mainWindow.h"
2013-02-04 16:38:00 +00:00
2015-06-26 18:21:32 -04:00
#include "rtabmap/core/CameraRGB.h"
#include "rtabmap/core/CameraStereo.h"
2024-04-14 19:06:04 -07:00
#include "rtabmap/core/Lidar.h"
2018-03-07 19:43:30 -05:00
#include "rtabmap/core/IMUThread.h"
2012-06-24 17:19:34 +00:00
#include "rtabmap/core/DBReader.h"
2013-02-04 16:38:00 +00:00
#include "rtabmap/core/Parameters.h"
#include "rtabmap/core/ParamEvent.h"
2013-12-11 00:12:44 +00:00
#include "rtabmap/core/Signature.h"
2014-10-30 14:43:43 +00:00
#include "rtabmap/core/Memory.h"
2015-10-16 17:43:31 -04:00
#include "rtabmap/core/DBDriver.h"
2015-11-22 18:08:32 -05:00
#include "rtabmap/core/RegistrationVis.h"
2023-12-17 22:44:11 -08:00
#include "rtabmap/core/global_map/OccupancyGrid.h"
2016-09-07 12:15:44 -04:00
#include "rtabmap/core/GainCompensator.h"
2017-10-11 19:36:00 -04:00
#include "rtabmap/core/Recovery.h"
2018-04-30 11:16:26 -04:00
#include "rtabmap/core/util2d.h"
2013-02-04 16:38:00 +00:00
#include "rtabmap/gui/ImageView.h"
2012-06-24 17:19:34 +00:00
#include "rtabmap/gui/KeypointItem.h"
2014-06-23 00:26:14 +00:00
#include "rtabmap/gui/DataRecorder.h"
2014-10-28 01:20:57 +00:00
#include "rtabmap/gui/DatabaseViewer.h"
2015-08-25 10:32:27 -04:00
#include "rtabmap/gui/PdfPlot.h"
#include "rtabmap/gui/StatsToolBox.h"
#include "rtabmap/gui/ProgressDialog.h"
2016-05-05 18:25:26 -04:00
#include "rtabmap/gui/CloudViewer.h"
#include "rtabmap/gui/LoopClosureViewer.h"
2017-10-20 12:09:34 +02:00
#include "rtabmap/gui/ExportCloudsDialog.h"
#include "rtabmap/gui/ExportBundlerDialog.h"
#include "rtabmap/gui/AboutDialog.h"
#include "rtabmap/gui/PostProcessingDialog.h"
#include "rtabmap/gui/DepthCalibrationDialog.h"
#include "rtabmap/gui/RecoveryState.h"
2022-02-12 13:35:23 -05:00
#include "rtabmap/gui/MultiSessionLocWidget.h"
2013-02-04 16:38:00 +00:00
2013-04-30 20:16:01 +00:00
#include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UEventsManager.h>
#include <rtabmap/utilite/UFile.h>
2013-12-11 00:12:44 +00:00
#include <rtabmap/utilite/UConversion.h>
2015-08-25 10:32:27 -04:00
#include "rtabmap/utilite/UPlot.h"
#include "rtabmap/utilite/UCv2Qt.h"
2013-02-04 16:38:00 +00:00
2011-06-05 14:45:39 +00:00
#include <QtGui/QCloseEvent>
#include <QtGui/QPixmap>
#include <QtCore/QDir>
2015-08-10 11:39:40 -04:00
#include <QtCore/QFile>
#include <QtCore/QTextStream>
2011-06-05 14:45:39 +00:00
#include <QtCore/QFileInfo>
2015-03-16 19:51:22 +00:00
#include <QMessageBox>
#include <QFileDialog>
#include <QGraphicsEllipseItem>
#include <QDockWidget>
2011-06-05 14:45:39 +00:00
#include <QtCore/QBuffer>
#include <QtCore/QTimer>
#include <QtCore/QTime>
2015-03-16 19:51:22 +00:00
#include <QActionGroup>
2011-06-05 14:45:39 +00:00
#include <QtGui/QDesktopServices>
#include <QtCore/QStringList>
#include <QtCore/QProcess>
2015-03-16 19:51:22 +00:00
#include <QSplashScreen>
#include <QInputDialog>
2015-03-31 15:45:38 -04:00
#include <QToolButton>
2011-06-05 14:45:39 +00:00
2013-12-11 00:12:44 +00:00
//RGB-D stuff
2014-06-15 03:31:35 +00:00
#include "rtabmap/core/CameraRGBD.h"
2013-12-11 00:12:44 +00:00
#include "rtabmap/core/Odometry.h"
2015-04-23 22:01:41 -04:00
#include "rtabmap/core/OdometryThread.h"
2013-12-11 00:12:44 +00:00
#include "rtabmap/core/OdometryEvent.h"
#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_mapping.h"
#include "rtabmap/core/util3d_surface.h"
#include "rtabmap/core/util3d_registration.h"
2018-10-01 19:33:56 -04:00
#include "rtabmap/core/optimizer/OptimizerCVSBA.h"
2015-01-23 11:17:42 -05:00
#include "rtabmap/core/Graph.h"
2015-11-22 18:08:32 -05:00
#include "rtabmap/core/RegistrationIcp.h"
2013-12-11 00:12:44 +00:00
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/common/transforms.h>
2014-02-13 21:32:46 +00:00
#include <pcl/common/common.h>
2013-12-11 00:12:44 +00:00
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/filters/filter.h>
2014-02-13 21:32:46 +00:00
#include <pcl/search/kdtree.h>
2024-04-14 19:06:04 -07:00
#include <rtabmap/core/SensorCaptureThread.h>
#include <rtabmap/core/SensorEvent.h>
2013-12-11 00:12:44 +00:00
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2023-12-17 22:44:11 -08:00
#include <rtabmap/core/global_map/OctoMap.h>
#endif
#ifdef RTABMAP_GRIDMAP
#include <rtabmap/core/global_map/GridMap.h>
2016-07-15 17:46:13 -04:00
#endif
2019-02-18 18:19:55 -05:00
#ifdef HAVE_OPENCV_ARUCO
#include <opencv2/aruco.hpp>
#endif
2011-06-05 14:45:39 +00:00
#define LOG_FILE_NAME "LogRtabmap.txt"
2011-06-21 21:21:52 +00:00
#define SHARE_SHOW_LOG_FILE "share/rtabmap/showlogs.m"
#define SHARE_GET_PRECISION_RECALL_FILE "share/rtabmap/getPrecisionRecall.m"
2011-06-05 14:45:39 +00:00
#define SHARE_IMPORT_FILE "share/rtabmap/importfile.m"
using namespace rtabmap ;
inline static void initGuiResource () { Q_INIT_RESOURCE ( GuiLib ); }
namespace rtabmap {
2017-10-20 12:09:34 +02:00
MainWindow :: MainWindow ( PreferencesDialog * prefDialog , QWidget * parent , bool showSplashScreen ) :
2011-06-05 14:45:39 +00:00
QMainWindow ( parent ),
_ui ( 0 ),
_state ( kIdle ),
2024-04-14 19:06:04 -07:00
_sensorCapture ( 0 ),
2013-12-11 00:12:44 +00:00
_odomThread ( 0 ),
2018-03-07 19:43:30 -05:00
_imuThread ( 0 ),
2011-06-05 14:45:39 +00:00
_preferencesDialog ( 0 ),
_aboutDialog ( 0 ),
2016-03-14 17:27:53 -04:00
_exportCloudsDialog ( 0 ),
2017-08-07 16:31:49 -04:00
_exportBundlerDialog ( 0 ),
2015-03-29 09:23:00 -04:00
_dataRecorder ( 0 ),
2011-06-05 14:45:39 +00:00
_lastId ( 0 ),
2016-07-17 19:58:32 -04:00
_firstStamp ( 0.0f ),
2012-03-03 01:46:30 +00:00
_processingStatistics ( false ),
2015-08-04 13:33:01 -04:00
_processingDownloadedMap ( false ),
2017-10-11 19:36:00 -04:00
_recovering ( false ),
2013-12-11 00:12:44 +00:00
_odometryReceived ( false ),
2015-02-18 18:22:59 -05:00
_newDatabasePath ( "" ),
_newDatabasePathOutput ( "" ),
2014-10-28 01:20:57 +00:00
_openedDatabasePath ( "" ),
2015-02-18 18:22:59 -05:00
_databaseUpdated ( false ),
2015-01-23 15:16:05 -05:00
_odomImageShow ( true ),
_odomImageDepthShow ( false ),
2015-10-13 12:50:25 -04:00
_savedMaximized ( false ),
_waypointsIndex ( 0 ),
2016-06-12 17:53:35 -04:00
_cachedMemoryUsage ( 0 ),
_createdCloudsMemoryUsage ( 0 ),
2016-08-21 19:33:01 -04:00
_occupancyGrid ( 0 ),
2016-07-15 17:46:13 -04:00
_octomap ( 0 ),
2023-12-17 22:44:11 -08:00
_elevationMap ( 0 ),
2013-12-11 00:12:44 +00:00
_odometryCorrection ( Transform :: getIdentity ()),
2015-03-20 15:54:33 -04:00
_processingOdometry ( false ),
2011-06-05 14:45:39 +00:00
_oneSecondTimer ( 0 ),
_elapsedTime ( 0 ),
2023-03-05 16:34:56 -08:00
_logEventTime ( 0 ),
2011-06-05 14:45:39 +00:00
_posteriorCurve ( 0 ),
2011-07-23 03:55:05 +00:00
_likelihoodCurve ( 0 ),
2012-12-11 18:05:05 +00:00
_rawLikelihoodCurve ( 0 ),
2017-05-10 15:53:02 -04:00
_exportPosesFrame ( 0 ),
2015-03-04 17:27:44 -05:00
_autoScreenCaptureOdomSync ( false ),
2015-09-04 13:46:37 -04:00
_autoScreenCaptureRAM ( false ),
2017-12-11 17:09:04 -05:00
_autoScreenCapturePNG ( false ),
2016-09-19 14:01:06 -04:00
_firstCall ( true ),
_progressCanceled ( false )
2011-06-05 14:45:39 +00:00
{
2016-11-14 19:54:31 -05:00
ULogger :: registerCurrentThread ( "MainWindow" );
2015-01-23 11:17:42 -05:00
UDEBUG ( "" );
2011-06-05 14:45:39 +00:00
initGuiResource ();
2017-10-20 12:09:34 +02:00
QSplashScreen * splash = 0 ;
if ( showSplashScreen )
{
QPixmap pixmap ( ":images/RTAB-Map.png" );
splash = new QSplashScreen ( pixmap );
splash -> show ();
splash -> showMessage ( tr ( "Loading..." ));
QApplication :: processEvents ();
}
2011-06-09 13:57:01 +00:00
2011-06-05 14:45:39 +00:00
// Create dialogs
_aboutDialog = new AboutDialog ( this );
2015-01-23 11:17:42 -05:00
_aboutDialog -> setObjectName ( "AboutDialog" );
2016-03-14 17:27:53 -04:00
_exportCloudsDialog = new ExportCloudsDialog ( this );
_exportCloudsDialog -> setObjectName ( "ExportCloudsDialog" );
2017-08-07 16:31:49 -04:00
_exportBundlerDialog = new ExportBundlerDialog ( this );
_exportBundlerDialog -> setObjectName ( "ExportBundlerDialog" );
2014-10-30 14:43:43 +00:00
_postProcessingDialog = new PostProcessingDialog ( this );
2015-01-14 11:02:17 -05:00
_postProcessingDialog -> setObjectName ( "PostProcessingDialog" );
2016-09-19 14:01:06 -04:00
_depthCalibrationDialog = new DepthCalibrationDialog ( this );
_depthCalibrationDialog -> setObjectName ( "DepthCalibrationDialog" );
2011-06-05 14:45:39 +00:00
_ui = new Ui_mainWindow ();
2016-05-05 18:25:26 -04:00
UDEBUG ( "Setup ui..." );
2011-06-05 14:45:39 +00:00
_ui -> setupUi ( this );
2016-05-05 18:25:26 -04:00
// Add cloud viewers
// Note that we add them here manually because there is a crash issue
2018-10-15 16:58:57 +01:00
// when adding them in a DockWidget of the *.ui file. The cloud viewer is
2016-05-05 18:25:26 -04:00
// created in a widget which is not yet linked to main window when the CloudViewer constructor
2018-10-15 16:58:57 +01:00
// is called (see order in generated ui file). VTK needs to get the top
2016-05-05 18:25:26 -04:00
// level window at the time CloudViewer is created, otherwise it may crash on some systems.
_cloudViewer = new CloudViewer ( _ui -> layout_cloudViewer );
_cloudViewer -> setObjectName ( "widget_cloudViewer" );
_ui -> layout_cloudViewer -> layout () -> addWidget ( _cloudViewer );
_loopClosureViewer = new LoopClosureViewer ( _ui -> layout_loopClosureViewer );
_loopClosureViewer -> setObjectName ( "widget_loopClosureViewer" );
_ui -> layout_loopClosureViewer -> layout () -> addWidget ( _loopClosureViewer );
UDEBUG ( "Setup ui... end" );
2015-01-23 11:17:42 -05:00
QString title ( "RTAB-Map[*]" );
2013-12-11 00:12:44 +00:00
this -> setWindowTitle ( title );
this -> setWindowIconText ( tr ( "RTAB-Map" ));
2015-01-23 11:17:42 -05:00
this -> setObjectName ( "MainWindow" );
2013-12-11 00:12:44 +00:00
2011-06-05 14:45:39 +00:00
//Setup dock widgets position if it is the first time the application is started.
2015-09-04 13:46:37 -04:00
setDefaultViews ();
2011-06-05 14:45:39 +00:00
2014-02-11 23:04:22 +00:00
_ui -> widget_mainWindow -> setVisible ( false );
2011-06-05 14:45:39 +00:00
if ( prefDialog )
{
_preferencesDialog = prefDialog ;
_preferencesDialog -> setParent ( this , Qt :: Dialog );
}
else // Default dialog
{
_preferencesDialog = new PreferencesDialog ( this );
}
2016-05-05 18:25:26 -04:00
2015-01-23 11:17:42 -05:00
_preferencesDialog -> setObjectName ( "PreferencesDialog" );
2011-06-09 12:51:13 +00:00
_preferencesDialog -> init ();
2011-06-05 14:45:39 +00:00
// Restore window geometry
2015-09-04 13:46:37 -04:00
bool statusBarShown = false ;
_preferencesDialog -> loadMainWindowState ( this , _savedMaximized , statusBarShown );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> loadWindowGeometry ( _preferencesDialog );
2016-03-14 17:27:53 -04:00
_preferencesDialog -> loadWindowGeometry ( _exportCloudsDialog );
2017-08-07 16:31:49 -04:00
_preferencesDialog -> loadWindowGeometry ( _exportBundlerDialog );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> loadWindowGeometry ( _postProcessingDialog );
2016-09-19 14:01:06 -04:00
_preferencesDialog -> loadWindowGeometry ( _depthCalibrationDialog );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> loadWindowGeometry ( _aboutDialog );
2011-06-05 14:45:39 +00:00
setupMainLayout ( _preferencesDialog -> isVerticalLayoutUsed ());
2018-02-13 10:16:48 -05:00
ParametersMap parameters = _preferencesDialog -> getAllParameters ();
2024-04-12 17:12:23 -07:00
// Override map resolution for UI
if ( _preferencesDialog -> getGridUIResolution () > 0 )
{
uInsert ( parameters , ParametersPair ( Parameters :: kGridCellSize (), uNumber2Str ( _preferencesDialog -> getGridUIResolution ())));
}
2023-12-17 22:44:11 -08:00
_occupancyGrid = new OccupancyGrid ( & _cachedLocalMaps , parameters );
2016-07-17 14:58:28 -04:00
#ifdef RTABMAP_OCTOMAP
2023-12-17 22:44:11 -08:00
_octomap = new OctoMap ( & _cachedLocalMaps , parameters );
#endif
#ifdef RTABMAP_GRIDMAP
_elevationMap = new GridMap ( & _cachedLocalMaps , parameters );
2016-07-17 14:58:28 -04:00
#endif
2011-06-05 14:45:39 +00:00
// Timer
_oneSecondTimer = new QTimer ( this );
_oneSecondTimer -> setInterval ( 1000 );
2023-03-05 16:34:56 -08:00
_elapsedTime = new QElapsedTimer ();
2011-06-05 14:45:39 +00:00
_ui -> label_elapsedTime -> setText ( "00:00:00" );
connect ( _oneSecondTimer , SIGNAL ( timeout ()), this , SLOT ( updateElapsedTime ()));
2023-03-05 16:34:56 -08:00
_logEventTime = new QElapsedTimer ();
2011-06-05 14:45:39 +00:00
_logEventTime -> start ();
//Graphics scenes
2017-10-20 12:09:34 +02:00
_ui -> imageView_source -> setBackgroundColor ( _ui -> imageView_source -> getDefaultBackgroundColor ());
_ui -> imageView_loopClosure -> setBackgroundColor ( _ui -> imageView_loopClosure -> getDefaultBackgroundColor ());
_ui -> imageView_odometry -> setBackgroundColor ( _ui -> imageView_odometry -> getDefaultBackgroundColor ());
2015-01-23 15:16:05 -05:00
_ui -> imageView_odometry -> setAlpha ( 200 );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> loadWidgetState ( _ui -> imageView_source );
_preferencesDialog -> loadWidgetState ( _ui -> imageView_loopClosure );
_preferencesDialog -> loadWidgetState ( _ui -> imageView_odometry );
2015-01-30 15:30:57 -05:00
_preferencesDialog -> loadWidgetState ( _ui -> graphicsView_graphView );
2011-06-05 14:45:39 +00:00
2014-10-26 21:47:29 +00:00
_posteriorCurve = new PdfPlotCurve ( "Posterior" , & _cachedSignatures , this );
2012-12-11 18:05:05 +00:00
_ui -> posteriorPlot -> addCurve ( _posteriorCurve , false );
2011-06-05 14:45:39 +00:00
_ui -> posteriorPlot -> showLegend ( false );
_ui -> posteriorPlot -> setFixedYAxis ( 0 , 1 );
2012-06-24 17:19:34 +00:00
UPlotCurveThreshold * tc ;
2024-11-03 03:51:05 +08:00
tc = _ui -> posteriorPlot -> addThreshold ( "1 - Loop Thr" , 1.0 - _preferencesDialog -> getLoopThr ());
2020-06-07 11:55:27 -04:00
connect ( this , SIGNAL ( loopClosureThrChanged ( qreal )), tc , SLOT ( setThreshold ( qreal )));
2011-06-05 14:45:39 +00:00
2014-10-26 21:47:29 +00:00
_likelihoodCurve = new PdfPlotCurve ( "Likelihood" , & _cachedSignatures , this );
2012-12-11 18:05:05 +00:00
_ui -> likelihoodPlot -> addCurve ( _likelihoodCurve , false );
2011-06-05 14:45:39 +00:00
_ui -> likelihoodPlot -> showLegend ( false );
2014-10-26 21:47:29 +00:00
_rawLikelihoodCurve = new PdfPlotCurve ( "Likelihood" , & _cachedSignatures , this );
2012-12-11 18:05:05 +00:00
_ui -> rawLikelihoodPlot -> addCurve ( _rawLikelihoodCurve , false );
_ui -> rawLikelihoodPlot -> showLegend ( false );
2012-06-24 17:19:34 +00:00
2022-02-12 13:35:23 -05:00
_multiSessionLocWidget = new MultiSessionLocWidget ( & _cachedSignatures , & _currentMapIds , this );
_ui -> layout_multiSessionLoc -> layout () -> addWidget ( _multiSessionLocWidget );
2017-10-20 12:09:34 +02:00
_progressDialog = new ProgressDialog ( this );
_progressDialog -> setMinimumWidth ( 800 );
connect ( _progressDialog , SIGNAL ( canceled ()), this , SLOT ( cancelProgress ()));
2011-06-05 14:45:39 +00:00
2014-01-22 19:49:28 +00:00
connect ( _ui -> widget_mapVisibility , SIGNAL ( visibilityChanged ( int , bool )), this , SLOT ( updateNodeVisibility ( int , bool )));
2011-06-05 14:45:39 +00:00
//connect stuff
connect ( _ui -> actionExit , SIGNAL ( triggered ()), this , SLOT ( close ()));
qRegisterMetaType < MainWindow :: State > ( "MainWindow::State" );
connect ( this , SIGNAL ( stateChanged ( MainWindow :: State )), this , SLOT ( changeState ( MainWindow :: State )));
connect ( this , SIGNAL ( rtabmapEventInitReceived ( int , const QString & )), this , SLOT ( processRtabmapEventInit ( int , const QString & )));
2013-12-11 00:12:44 +00:00
qRegisterMetaType < rtabmap :: RtabmapEvent3DMap > ( "rtabmap::RtabmapEvent3DMap" );
connect ( this , SIGNAL ( rtabmapEvent3DMapReceived ( const rtabmap :: RtabmapEvent3DMap & )), this , SLOT ( processRtabmapEvent3DMap ( const rtabmap :: RtabmapEvent3DMap & )));
2015-04-17 18:30:12 -04:00
qRegisterMetaType < rtabmap :: RtabmapGlobalPathEvent > ( "rtabmap::RtabmapGlobalPathEvent" );
connect ( this , SIGNAL ( rtabmapGlobalPathEventReceived ( const rtabmap :: RtabmapGlobalPathEvent & )), this , SLOT ( processRtabmapGlobalPathEvent ( const rtabmap :: RtabmapGlobalPathEvent & )));
2015-07-26 15:50:39 -04:00
connect ( this , SIGNAL ( rtabmapLabelErrorReceived ( int , const QString & )), this , SLOT ( processRtabmapLabelErrorEvent ( int , const QString & )));
2015-10-13 12:50:25 -04:00
connect ( this , SIGNAL ( rtabmapGoalStatusEventReceived ( int )), this , SLOT ( processRtabmapGoalStatusEvent ( int )));
2011-06-05 14:45:39 +00:00
// Dock Widget view actions (Menu->Window)
2014-02-11 23:04:22 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_imageView -> toggleViewAction ());
2011-06-05 14:45:39 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_posterior -> toggleViewAction ());
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_likelihood -> toggleViewAction ());
2012-12-11 18:05:05 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_rawlikelihood -> toggleViewAction ());
2011-06-05 14:45:39 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_statsV2 -> toggleViewAction ());
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_console -> toggleViewAction ());
2013-12-11 00:12:44 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_cloudViewer -> toggleViewAction ());
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_loopClosureViewer -> toggleViewAction ());
2014-01-22 19:49:28 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_mapVisibility -> toggleViewAction ());
2014-02-11 23:04:22 +00:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_graphViewer -> toggleViewAction ());
2014-12-02 17:01:24 -05:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_odometry -> toggleViewAction ());
2022-02-12 13:35:23 -05:00
_ui -> menuShow_view -> addAction ( _ui -> dockWidget_multiSessionLoc -> toggleViewAction ());
2011-07-23 03:55:05 +00:00
_ui -> menuShow_view -> addAction ( _ui -> toolBar -> toggleViewAction ());
2015-03-31 15:45:38 -04:00
_ui -> toolBar -> setWindowTitle ( tr ( "File toolbar" ));
_ui -> menuShow_view -> addAction ( _ui -> toolBar_2 -> toggleViewAction ());
_ui -> toolBar_2 -> setWindowTitle ( tr ( "Control toolbar" ));
2013-12-11 00:12:44 +00:00
QAction * a = _ui -> menuShow_view -> addAction ( "Progress dialog" );
2011-06-05 14:45:39 +00:00
a -> setCheckable ( false );
2017-10-20 12:09:34 +02:00
connect ( a , SIGNAL ( triggered ( bool )), _progressDialog , SLOT ( show ()));
2015-09-04 13:46:37 -04:00
QAction * statusBarAction = _ui -> menuShow_view -> addAction ( "Status bar" );
statusBarAction -> setCheckable ( true );
statusBarAction -> setChecked ( statusBarShown );
connect ( statusBarAction , SIGNAL ( toggled ( bool )), this -> statusBar (), SLOT ( setVisible ( bool )));
2011-06-05 14:45:39 +00:00
// connect actions with custom slots
2015-01-23 11:17:42 -05:00
connect ( _ui -> actionSave_GUI_config , SIGNAL ( triggered ()), this , SLOT ( saveConfigGUI ()));
2014-10-28 01:20:57 +00:00
connect ( _ui -> actionNew_database , SIGNAL ( triggered ()), this , SLOT ( newDatabase ()));
connect ( _ui -> actionOpen_database , SIGNAL ( triggered ()), this , SLOT ( openDatabase ()));
connect ( _ui -> actionClose_database , SIGNAL ( triggered ()), this , SLOT ( closeDatabase ()));
connect ( _ui -> actionEdit_database , SIGNAL ( triggered ()), this , SLOT ( editDatabase ()));
2011-06-05 14:45:39 +00:00
connect ( _ui -> actionStart , SIGNAL ( triggered ()), this , SLOT ( startDetection ()));
connect ( _ui -> actionPause , SIGNAL ( triggered ()), this , SLOT ( pauseDetection ()));
connect ( _ui -> actionStop , SIGNAL ( triggered ()), this , SLOT ( stopDetection ()));
connect ( _ui -> actionDump_the_memory , SIGNAL ( triggered ()), this , SLOT ( dumpTheMemory ()));
connect ( _ui -> actionDump_the_prediction_matrix , SIGNAL ( triggered ()), this , SLOT ( dumpThePrediction ()));
2015-04-17 18:30:12 -04:00
connect ( _ui -> actionSend_goal , SIGNAL ( triggered ()), this , SLOT ( sendGoal ()));
2015-10-13 12:50:25 -04:00
connect ( _ui -> actionSend_waypoints , SIGNAL ( triggered ()), this , SLOT ( sendWaypoints ()));
2015-06-21 20:16:56 -04:00
connect ( _ui -> actionCancel_goal , SIGNAL ( triggered ()), this , SLOT ( cancelGoal ()));
2015-07-26 15:50:39 -04:00
connect ( _ui -> actionLabel_current_location , SIGNAL ( triggered ()), this , SLOT ( label ()));
2022-01-20 18:56:38 -05:00
connect ( _ui -> actionRemove_label , SIGNAL ( triggered ()), this , SLOT ( removeLabel ()));
2011-06-05 14:45:39 +00:00
connect ( _ui -> actionClear_cache , SIGNAL ( triggered ()), this , SLOT ( clearTheCache ()));
connect ( _ui -> actionAbout , SIGNAL ( triggered ()), _aboutDialog , SLOT ( exec ()));
2017-08-22 16:47:48 -04:00
connect ( _ui -> actionHelp , SIGNAL ( triggered ()), this , SLOT ( openHelp ()));
2012-12-11 19:41:44 +00:00
connect ( _ui -> actionPrint_loop_closure_IDs_to_console , SIGNAL ( triggered ()), this , SLOT ( printLoopClosureIds ()));
2015-08-01 23:42:36 -04:00
connect ( _ui -> actionGenerate_map , SIGNAL ( triggered ()), this , SLOT ( generateGraphDOT ()));
2015-08-25 19:36:48 -04:00
connect ( _ui -> actionRaw_format_txt , SIGNAL ( triggered ()), this , SLOT ( exportPosesRaw ()));
2015-08-01 23:42:36 -04:00
connect ( _ui -> actionRGBD_SLAM_format_txt , SIGNAL ( triggered ()), this , SLOT ( exportPosesRGBDSLAM ()));
2018-11-08 12:26:37 -05:00
connect ( _ui -> actionRGBD_SLAM_motion_capture_format_txt , SIGNAL ( triggered ()), this , SLOT ( exportPosesRGBDSLAMMotionCapture ()));
2021-04-13 15:49:04 -04:00
connect ( _ui -> actionRGBD_SLAM_ID_format_txt , SIGNAL ( triggered ()), this , SLOT ( exportPosesRGBDSLAMID ()));
2015-08-25 19:36:48 -04:00
connect ( _ui -> actionKITTI_format_txt , SIGNAL ( triggered ()), this , SLOT ( exportPosesKITTI ()));
2015-08-01 23:42:36 -04:00
connect ( _ui -> actionTORO_graph , SIGNAL ( triggered ()), this , SLOT ( exportPosesTORO ()));
2015-09-02 17:51:43 -04:00
connect ( _ui -> actionG2o_g2o , SIGNAL ( triggered ()), this , SLOT ( exportPosesG2O ()));
2011-06-05 14:45:39 +00:00
connect ( _ui -> actionDelete_memory , SIGNAL ( triggered ()), this , SLOT ( deleteMemory ()));
2013-12-11 00:12:44 +00:00
connect ( _ui -> actionDownload_all_clouds , SIGNAL ( triggered ()), this , SLOT ( downloadAllClouds ()));
2014-02-11 23:04:22 +00:00
connect ( _ui -> actionDownload_graph , SIGNAL ( triggered ()), this , SLOT ( downloadPoseGraph ()));
2015-10-16 17:43:31 -04:00
connect ( _ui -> actionUpdate_cache_from_database , SIGNAL ( triggered ()), this , SLOT ( updateCacheFromDatabase ()));
2016-01-07 15:48:18 -05:00
connect ( _ui -> actionAnchor_clouds_to_ground_truth , SIGNAL ( triggered ()), this , SLOT ( anchorCloudsToGroundTruth ()));
2011-06-05 14:45:39 +00:00
connect ( _ui -> menuEdit , SIGNAL ( aboutToShow ()), this , SLOT ( updateEditMenu ()));
2015-06-21 20:47:41 -04:00
connect ( _ui -> actionDefault_views , SIGNAL ( triggered ( bool )), this , SLOT ( setDefaultViews ()));
2011-07-23 03:55:05 +00:00
connect ( _ui -> actionAuto_screen_capture , SIGNAL ( triggered ( bool )), this , SLOT ( selectScreenCaptureFormat ( bool )));
2014-02-11 23:04:22 +00:00
connect ( _ui -> actionScreenshot , SIGNAL ( triggered ()), this , SLOT ( takeScreenshot ()));
2012-06-24 17:19:34 +00:00
connect ( _ui -> action16_9 , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio16_9 ()));
connect ( _ui -> action16_10 , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio16_10 ()));
connect ( _ui -> action4_3 , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio4_3 ()));
connect ( _ui -> action240p , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio240p ()));
connect ( _ui -> action360p , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio360p ()));
connect ( _ui -> action480p , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio480p ()));
connect ( _ui -> action720p , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio720p ()));
connect ( _ui -> action1080p , SIGNAL ( triggered ()), this , SLOT ( setAspectRatio1080p ()));
2015-09-04 13:46:37 -04:00
connect ( _ui -> actionCustom , SIGNAL ( triggered ()), this , SLOT ( setAspectRatioCustom ()));
2014-07-29 14:11:08 +00:00
connect ( _ui -> actionSave_point_cloud , SIGNAL ( triggered ()), this , SLOT ( exportClouds ()));
2014-07-29 00:52:35 +00:00
connect ( _ui -> actionExport_2D_Grid_map_bmp_png , SIGNAL ( triggered ()), this , SLOT ( exportGridMap ()));
2015-10-16 20:11:24 -04:00
connect ( _ui -> actionExport_images_RGB_jpg_Depth_png , SIGNAL ( triggered ()), this , SLOT ( exportImages ()));
2015-08-10 11:39:40 -04:00
connect ( _ui -> actionExport_cameras_in_Bundle_format_out , SIGNAL ( triggered ()), SLOT ( exportBundlerFormat ()));
2016-07-15 17:46:13 -04:00
connect ( _ui -> actionExport_octomap , SIGNAL ( triggered ()), this , SLOT ( exportOctomap ()));
2014-07-29 00:52:35 +00:00
connect ( _ui -> actionView_high_res_point_cloud , SIGNAL ( triggered ()), this , SLOT ( viewClouds ()));
2013-12-11 00:12:44 +00:00
connect ( _ui -> actionReset_Odometry , SIGNAL ( triggered ()), this , SLOT ( resetOdometry ()));
connect ( _ui -> actionTrigger_a_new_map , SIGNAL ( triggered ()), this , SLOT ( triggerNewMap ()));
2014-06-23 00:26:14 +00:00
connect ( _ui -> actionData_recorder , SIGNAL ( triggered ()), this , SLOT ( dataRecorder ()));
2021-11-09 10:16:12 -05:00
connect ( _ui -> actionPost_processing , SIGNAL ( triggered ()), this , SLOT ( showPostProcessingDialog ()));
2016-09-19 14:01:06 -04:00
connect ( _ui -> actionDepth_Calibration , SIGNAL ( triggered ()), this , SLOT ( depthCalibration ()));
2011-06-05 14:45:39 +00:00
2012-03-03 01:46:30 +00:00
_ui -> actionPause -> setShortcut ( Qt :: Key_Space );
2015-01-23 11:17:42 -05:00
_ui -> actionSave_GUI_config -> setShortcut ( QKeySequence :: Save );
2018-10-15 16:58:57 +01:00
// Qt5 issue, we should explicitly add actions not in
2016-05-05 18:25:26 -04:00
// menu bar to have shortcut working
this -> addAction ( _ui -> actionSave_GUI_config );
2013-12-11 00:12:44 +00:00
_ui -> actionReset_Odometry -> setEnabled ( false );
2014-10-30 14:43:43 +00:00
_ui -> actionPost_processing -> setEnabled ( false );
2016-01-07 15:48:18 -05:00
_ui -> actionAnchor_clouds_to_ground_truth -> setEnabled ( false );
2012-03-03 01:46:30 +00:00
2015-03-31 15:45:38 -04:00
QToolButton * toolButton = new QToolButton ( this );
2015-06-26 18:21:32 -04:00
toolButton -> setMenu ( _ui -> menuSelect_source );
2015-03-31 15:45:38 -04:00
toolButton -> setPopupMode ( QToolButton :: InstantPopup );
toolButton -> setIcon ( QIcon ( ":images/kinect_xbox_360.png" ));
toolButton -> setToolTip ( "Select sensor driver" );
_ui -> toolBar -> addWidget ( toolButton ) -> setObjectName ( "toolbar_source" );
2014-06-23 15:19:17 +00:00
#if defined(Q_WS_MAC) || defined(Q_WS_WIN)
2011-06-05 14:45:39 +00:00
connect ( _ui -> actionOpen_working_directory , SIGNAL ( triggered ()), SLOT ( openWorkingDirectory ()));
#else
_ui -> menuEdit -> removeAction ( _ui -> actionOpen_working_directory );
#endif
//Settings menu
2015-06-26 18:21:32 -04:00
connect ( _ui -> actionMore_options , SIGNAL ( triggered ()), this , SLOT ( openPreferencesSource ()));
2014-06-14 17:01:14 +00:00
connect ( _ui -> actionOpenNI_PCL , SIGNAL ( triggered ()), this , SLOT ( selectOpenni ()));
connect ( _ui -> actionOpenNI_PCL_ASUS , SIGNAL ( triggered ()), this , SLOT ( selectOpenni ()));
2014-06-03 04:42:08 +00:00
connect ( _ui -> actionFreenect , SIGNAL ( triggered ()), this , SLOT ( selectFreenect ()));
2014-06-13 03:47:39 +00:00
connect ( _ui -> actionOpenNI_CV , SIGNAL ( triggered ()), this , SLOT ( selectOpenniCv ()));
connect ( _ui -> actionOpenNI_CV_ASUS , SIGNAL ( triggered ()), this , SLOT ( selectOpenniCvAsus ()));
2015-04-06 00:28:04 -04:00
connect ( _ui -> actionOpenNI2 , SIGNAL ( triggered ()), this , SLOT ( selectOpenni2 ()));
connect ( _ui -> actionOpenNI2_kinect , SIGNAL ( triggered ()), this , SLOT ( selectOpenni2 ()));
2021-12-29 15:53:40 -05:00
connect ( _ui -> actionOpenNI2_orbbec , SIGNAL ( triggered ()), this , SLOT ( selectOpenni2 ()));
2015-04-06 00:28:04 -04:00
connect ( _ui -> actionOpenNI2_sense , SIGNAL ( triggered ()), this , SLOT ( selectOpenni2 ()));
connect ( _ui -> actionFreenect2 , SIGNAL ( triggered ()), this , SLOT ( selectFreenect2 ()));
2017-11-06 00:19:12 -05:00
connect ( _ui -> actionKinect_for_Windows_SDK_v2 , SIGNAL ( triggered ()), this , SLOT ( selectK4W2 ()));
2020-05-19 11:03:36 -08:00
connect ( _ui -> actionKinect_for_Azure , SIGNAL ( triggered ()), this , SLOT ( selectK4A ()));
2017-04-19 15:24:42 -04:00
connect ( _ui -> actionRealSense_R200 , SIGNAL ( triggered ()), this , SLOT ( selectRealSense ()));
connect ( _ui -> actionRealSense_ZR300 , SIGNAL ( triggered ()), this , SLOT ( selectRealSense ()));
2019-07-05 11:19:45 -04:00
connect ( _ui -> actionRealSense2_SR300 , SIGNAL ( triggered ()), this , SLOT ( selectRealSense2 ()));
2021-01-07 23:51:06 -05:00
connect ( _ui -> actionRealSense2_D400 , SIGNAL ( triggered ()), this , SLOT ( selectRealSense2 ()));
connect ( _ui -> actionRealSense2_L515 , SIGNAL ( triggered ()), this , SLOT ( selectRealSense2L515 ()));
2015-04-06 00:28:04 -04:00
connect ( _ui -> actionStereoDC1394 , SIGNAL ( triggered ()), this , SLOT ( selectStereoDC1394 ()));
2015-04-07 12:00:10 -04:00
connect ( _ui -> actionStereoFlyCapture2 , SIGNAL ( triggered ()), this , SLOT ( selectStereoFlyCapture2 ()));
2016-05-31 19:09:49 -04:00
connect ( _ui -> actionStereoZed , SIGNAL ( triggered ()), this , SLOT ( selectStereoZed ()));
2021-02-28 10:31:18 -05:00
connect ( _ui -> actionZed_Open_Capture , SIGNAL ( triggered ()), this , SLOT ( selectStereoZedOC ()));
2021-01-07 23:51:06 -05:00
connect ( _ui -> actionStereoTara , SIGNAL ( triggered ()), this , SLOT ( selectStereoTara ()));
2016-05-31 19:09:49 -04:00
connect ( _ui -> actionStereoUsb , SIGNAL ( triggered ()), this , SLOT ( selectStereoUsb ()));
2019-05-09 19:20:26 -04:00
connect ( _ui -> actionRealSense2_T265 , SIGNAL ( triggered ()), this , SLOT ( selectRealSense2Stereo ()));
2020-04-30 00:57:47 -04:00
connect ( _ui -> actionMYNT_EYE_S_SDK , SIGNAL ( triggered ()), this , SLOT ( selectMyntEyeS ()));
2022-10-23 18:15:23 -07:00
connect ( _ui -> actionDepthAI_oakd , SIGNAL ( triggered ()), this , SLOT ( selectDepthAIOAKD ()));
connect ( _ui -> actionDepthAI_oakdlite , SIGNAL ( triggered ()), this , SLOT ( selectDepthAIOAKDLite ()));
2024-03-30 18:08:32 -07:00
connect ( _ui -> actionDepthAI_oakdpro , SIGNAL ( triggered ()), this , SLOT ( selectDepthAIOAKDPro ()));
2024-06-09 11:13:29 -07:00
connect ( _ui -> actionXvisio_SeerSense , SIGNAL ( triggered ()), this , SLOT ( selectXvisioSeerSense ()));
2025-10-07 10:22:16 -07:00
connect ( _ui -> actionOrbbecSDK_astra2 , SIGNAL ( triggered ()), this , SLOT ( selectOrbbecSDK ()));
2024-04-14 19:06:04 -07:00
connect ( _ui -> actionVelodyne_VLP_16 , SIGNAL ( triggered ()), this , SLOT ( selectVLP16 ()));
2015-04-06 00:28:04 -04:00
_ui -> actionFreenect -> setEnabled ( CameraFreenect :: available ());
2024-06-15 23:12:07 +02:00
_ui -> actionOpenNI_PCL -> setEnabled ( CameraOpenni :: available ());
_ui -> actionOpenNI_PCL_ASUS -> setEnabled ( CameraOpenni :: available ());
2014-06-15 03:31:35 +00:00
_ui -> actionOpenNI_CV -> setEnabled ( CameraOpenNICV :: available ());
_ui -> actionOpenNI_CV_ASUS -> setEnabled ( CameraOpenNICV :: available ());
2014-06-13 23:44:38 +00:00
_ui -> actionOpenNI2 -> setEnabled ( CameraOpenNI2 :: available ());
2015-02-20 15:32:39 -05:00
_ui -> actionOpenNI2_kinect -> setEnabled ( CameraOpenNI2 :: available ());
2021-12-29 15:53:40 -05:00
_ui -> actionOpenNI2_orbbec -> setEnabled ( CameraOpenNI2 :: available ());
2015-04-06 00:28:04 -04:00
_ui -> actionOpenNI2_sense -> setEnabled ( CameraOpenNI2 :: available ());
_ui -> actionFreenect2 -> setEnabled ( CameraFreenect2 :: available ());
2017-11-06 00:19:12 -05:00
_ui -> actionKinect_for_Windows_SDK_v2 -> setEnabled ( CameraK4W2 :: available ());
2020-05-19 15:33:32 -04:00
_ui -> actionKinect_for_Azure -> setEnabled ( CameraK4A :: available ());
2017-04-19 15:24:42 -04:00
_ui -> actionRealSense_R200 -> setEnabled ( CameraRealSense :: available ());
_ui -> actionRealSense_ZR300 -> setEnabled ( CameraRealSense :: available ());
2019-07-05 11:19:45 -04:00
_ui -> actionRealSense2_SR300 -> setEnabled ( CameraRealSense2 :: available ());
2021-01-07 23:51:06 -05:00
_ui -> actionRealSense2_D400 -> setEnabled ( CameraRealSense2 :: available ());
2020-12-11 09:56:17 -05:00
_ui -> actionRealSense2_L515 -> setEnabled ( CameraRealSense2 :: available ());
2019-05-09 19:20:26 -04:00
_ui -> actionRealSense2_T265 -> setEnabled ( CameraRealSense2 :: available ());
2015-04-06 00:28:04 -04:00
_ui -> actionStereoDC1394 -> setEnabled ( CameraStereoDC1394 :: available ());
2015-04-07 12:00:10 -04:00
_ui -> actionStereoFlyCapture2 -> setEnabled ( CameraStereoFlyCapture2 :: available ());
2016-05-31 19:09:49 -04:00
_ui -> actionStereoZed -> setEnabled ( CameraStereoZed :: available ());
2021-02-28 10:31:18 -05:00
_ui -> actionZed_Open_Capture -> setEnabled ( CameraStereoZedOC :: available ());
2018-10-31 20:27:07 +05:30
_ui -> actionStereoTara -> setEnabled ( CameraStereoTara :: available ());
2020-04-30 00:57:47 -04:00
_ui -> actionMYNT_EYE_S_SDK -> setEnabled ( CameraMyntEye :: available ());
2022-10-23 18:15:23 -07:00
_ui -> actionDepthAI_oakd -> setEnabled ( CameraDepthAI :: available ());
_ui -> actionDepthAI_oakdlite -> setEnabled ( CameraDepthAI :: available ());
2024-03-30 18:08:32 -07:00
_ui -> actionDepthAI_oakdpro -> setEnabled ( CameraDepthAI :: available ());
2024-06-09 11:13:29 -07:00
_ui -> actionXvisio_SeerSense -> setEnabled ( CameraSeerSense :: available ());
2025-10-07 10:22:16 -07:00
_ui -> actionOrbbecSDK_astra2 -> setEnabled ( CameraOrbbecSDK :: available ());
2015-03-01 19:41:03 -05:00
this -> updateSelectSourceMenu ();
2011-06-05 14:45:39 +00:00
2013-12-11 00:12:44 +00:00
connect ( _ui -> actionPreferences , SIGNAL ( triggered ()), this , SLOT ( openPreferences ()));
QActionGroup * modeGrp = new QActionGroup ( this );
modeGrp -> addAction ( _ui -> actionSLAM_mode );
modeGrp -> addAction ( _ui -> actionLocalization_mode );
_ui -> actionSLAM_mode -> setChecked ( _preferencesDialog -> isSLAMMode ());
_ui -> actionLocalization_mode -> setChecked ( ! _preferencesDialog -> isSLAMMode ());
connect ( _ui -> actionSLAM_mode , SIGNAL ( triggered ()), this , SLOT ( changeMappingMode ()));
connect ( _ui -> actionLocalization_mode , SIGNAL ( triggered ()), this , SLOT ( changeMappingMode ()));
connect ( this , SIGNAL ( mappingModeChanged ( bool )), _preferencesDialog , SLOT ( setSLAMMode ( bool )));
2011-06-05 14:45:39 +00:00
// Settings changed
qRegisterMetaType < PreferencesDialog :: PANEL_FLAGS > ( "PreferencesDialog::PANEL_FLAGS" );
connect ( _preferencesDialog , SIGNAL ( settingsChanged ( PreferencesDialog :: PANEL_FLAGS )), this , SLOT ( applyPrefSettings ( PreferencesDialog :: PANEL_FLAGS )));
qRegisterMetaType < rtabmap :: ParametersMap > ( "rtabmap::ParametersMap" );
connect ( _preferencesDialog , SIGNAL ( settingsChanged ( rtabmap :: ParametersMap )), this , SLOT ( applyPrefSettings ( rtabmap :: ParametersMap )));
2015-01-23 11:17:42 -05:00
// config GUI modified
2015-02-13 16:50:32 -05:00
connect ( _preferencesDialog , SIGNAL ( settingsChanged ( PreferencesDialog :: PANEL_FLAGS )), this , SLOT ( configGUIModified ()));
if ( prefDialog == 0 )
{
connect ( _preferencesDialog , SIGNAL ( settingsChanged ( rtabmap :: ParametersMap )), this , SLOT ( configGUIModified ()));
}
2015-01-23 11:17:42 -05:00
connect ( _ui -> imageView_source , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
connect ( _ui -> imageView_loopClosure , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
connect ( _ui -> imageView_odometry , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2015-01-30 15:30:57 -05:00
connect ( _ui -> graphicsView_graphView , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2016-05-05 18:25:26 -04:00
connect ( _cloudViewer , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2016-03-14 17:27:53 -04:00
connect ( _exportCloudsDialog , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2017-08-07 16:31:49 -04:00
connect ( _exportBundlerDialog , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2015-01-23 11:17:42 -05:00
connect ( _postProcessingDialog , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2016-09-19 14:01:06 -04:00
connect ( _depthCalibrationDialog , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2022-02-12 13:35:23 -05:00
connect ( _multiSessionLocWidget -> getImageView (), SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
2015-01-23 11:17:42 -05:00
connect ( _ui -> toolBar -> toggleViewAction (), SIGNAL ( toggled ( bool )), this , SLOT ( configGUIModified ()));
connect ( _ui -> toolBar , SIGNAL ( orientationChanged ( Qt :: Orientation )), this , SLOT ( configGUIModified ()));
2015-09-04 13:46:37 -04:00
connect ( statusBarAction , SIGNAL ( toggled ( bool )), this , SLOT ( configGUIModified ()));
2015-01-23 11:17:42 -05:00
QList < QDockWidget *> dockWidgets = this -> findChildren < QDockWidget *> ();
for ( int i = 0 ; i < dockWidgets . size (); ++ i )
{
connect ( dockWidgets [ i ], SIGNAL ( dockLocationChanged ( Qt :: DockWidgetArea )), this , SLOT ( configGUIModified ()));
connect ( dockWidgets [ i ] -> toggleViewAction (), SIGNAL ( toggled ( bool )), this , SLOT ( configGUIModified ()));
}
2016-07-08 12:47:23 -04:00
connect ( _ui -> dockWidget_graphViewer -> toggleViewAction (), SIGNAL ( triggered ()), this , SLOT ( updateGraphView ()));
2015-03-04 17:27:44 -05:00
// catch resize events
_ui -> dockWidget_posterior -> installEventFilter ( this );
_ui -> dockWidget_likelihood -> installEventFilter ( this );
_ui -> dockWidget_rawlikelihood -> installEventFilter ( this );
_ui -> dockWidget_statsV2 -> installEventFilter ( this );
_ui -> dockWidget_console -> installEventFilter ( this );
_ui -> dockWidget_loopClosureViewer -> installEventFilter ( this );
_ui -> dockWidget_mapVisibility -> installEventFilter ( this );
_ui -> dockWidget_graphViewer -> installEventFilter ( this );
_ui -> dockWidget_odometry -> installEventFilter ( this );
_ui -> dockWidget_cloudViewer -> installEventFilter ( this );
_ui -> dockWidget_imageView -> installEventFilter ( this );
2022-02-12 13:35:23 -05:00
_ui -> dockWidget_multiSessionLoc -> installEventFilter ( this );
2011-06-05 14:45:39 +00:00
// more connects...
2016-07-17 14:58:28 -04:00
_ui -> doubleSpinBox_stats_imgRate -> setValue ( _preferencesDialog -> getGeneralInputRate ());
_ui -> doubleSpinBox_stats_detectionRate -> setValue ( _preferencesDialog -> getDetectionRate ());
_ui -> doubleSpinBox_stats_timeLimit -> setValue ( _preferencesDialog -> getTimeLimit ());
2011-06-05 14:45:39 +00:00
connect ( _ui -> doubleSpinBox_stats_imgRate , SIGNAL ( editingFinished ()), this , SLOT ( changeImgRateSetting ()));
2013-12-11 00:12:44 +00:00
connect ( _ui -> doubleSpinBox_stats_detectionRate , SIGNAL ( editingFinished ()), this , SLOT ( changeDetectionRateSetting ()));
2011-06-05 14:45:39 +00:00
connect ( _ui -> doubleSpinBox_stats_timeLimit , SIGNAL ( editingFinished ()), this , SLOT ( changeTimeLimitSetting ()));
2012-06-24 17:19:34 +00:00
connect ( this , SIGNAL ( imgRateChanged ( double )), _preferencesDialog , SLOT ( setInputRate ( double )));
2013-12-11 00:12:44 +00:00
connect ( this , SIGNAL ( detectionRateChanged ( double )), _preferencesDialog , SLOT ( setDetectionRate ( double )));
2012-03-03 01:46:30 +00:00
connect ( this , SIGNAL ( timeLimitChanged ( float )), _preferencesDialog , SLOT ( setTimeLimit ( float )));
2011-06-05 14:45:39 +00:00
// Statistics from the detector
qRegisterMetaType < rtabmap :: Statistics > ( "rtabmap::Statistics" );
connect ( this , SIGNAL ( statsReceived ( rtabmap :: Statistics )), this , SLOT ( processStats ( rtabmap :: Statistics )));
2024-04-14 19:06:04 -07:00
qRegisterMetaType < rtabmap :: SensorCaptureInfo > ( "rtabmap::SensorCaptureInfo" );
connect ( this , SIGNAL ( cameraInfoReceived ( rtabmap :: SensorCaptureInfo )), this , SLOT ( processCameraInfo ( rtabmap :: SensorCaptureInfo )));
2015-11-30 18:49:28 -05:00
2015-05-29 14:46:48 -04:00
qRegisterMetaType < rtabmap :: OdometryEvent > ( "rtabmap::OdometryEvent" );
2016-06-15 14:18:26 -04:00
connect ( this , SIGNAL ( odometryReceived ( rtabmap :: OdometryEvent , bool )), this , SLOT ( processOdometry ( rtabmap :: OdometryEvent , bool )));
2013-12-11 00:12:44 +00:00
2015-06-11 16:57:16 -04:00
connect ( this , SIGNAL ( noMoreImagesReceived ()), this , SLOT ( notifyNoMoreImages ()));
2011-06-05 14:45:39 +00:00
// Apply state
this -> changeState ( kIdle );
this -> applyPrefSettings ( PreferencesDialog :: kPanelAll );
2026-04-19 16:08:03 -07:00
applyPrefSettings ( parameters , false );
2011-06-05 14:45:39 +00:00
2016-11-15 09:52:24 -05:00
_ui -> statsToolBox -> setNewFigureMaxItems ( 50 );
2011-06-05 14:45:39 +00:00
_ui -> statsToolBox -> setWorkingDirectory ( _preferencesDialog -> getWorkingDirectory ());
2014-02-11 23:04:22 +00:00
_ui -> graphicsView_graphView -> setWorkingDirectory ( _preferencesDialog -> getWorkingDirectory ());
2017-08-07 16:31:49 -04:00
_exportBundlerDialog -> setWorkingDirectory ( _preferencesDialog -> getWorkingDirectory ());
2016-05-05 18:25:26 -04:00
_cloudViewer -> setBackfaceCulling ( true , false );
_preferencesDialog -> loadWidgetState ( _cloudViewer );
2022-02-12 13:35:23 -05:00
_preferencesDialog -> loadWidgetState ( _multiSessionLocWidget -> getImageView ());
2015-01-14 11:02:17 -05:00
//dialog states
2016-03-14 17:27:53 -04:00
_preferencesDialog -> loadWidgetState ( _exportCloudsDialog );
2017-08-07 16:31:49 -04:00
_preferencesDialog -> loadWidgetState ( _exportBundlerDialog );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> loadWidgetState ( _postProcessingDialog );
2016-09-19 14:01:06 -04:00
_preferencesDialog -> loadWidgetState ( _depthCalibrationDialog );
2011-06-09 13:57:01 +00:00
2014-10-28 01:20:57 +00:00
if ( _ui -> statsToolBox -> findChildren < StatItem *> (). size () == 0 )
{
const std :: map < std :: string , float > & statistics = Statistics :: defaultData ();
for ( std :: map < std :: string , float >:: const_iterator iter = statistics . begin (); iter != statistics . end (); ++ iter )
{
2017-11-30 16:52:03 -05:00
// Don't add Gt panels yet if we don't know if we will receive Gt values.
if ( ! QString (( * iter ). first . c_str ()). contains ( "Gt/" ))
{
_ui -> statsToolBox -> updateStat ( QString (( * iter ). first . c_str ()). replace ( '_' , ' ' ), false );
}
2014-10-28 01:20:57 +00:00
}
}
2015-10-26 12:59:20 -04:00
// Specific MainWindow
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Planning/From/" , false );
_ui -> statsToolBox -> updateStat ( "Planning/Time/ms" , false );
_ui -> statsToolBox -> updateStat ( "Planning/Goal/" , false );
_ui -> statsToolBox -> updateStat ( "Planning/Poses/" , false );
_ui -> statsToolBox -> updateStat ( "Planning/Length/m" , false );
2016-03-02 18:11:19 -05:00
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Camera/Time capturing/ms" , false );
2024-04-14 19:06:04 -07:00
_ui -> statsToolBox -> updateStat ( "Camera/Time deskewing/ms" , false );
2023-09-22 06:46:10 +08:00
_ui -> statsToolBox -> updateStat ( "Camera/Time undistort depth/ms" , false );
_ui -> statsToolBox -> updateStat ( "Camera/Time bilateral filtering/ms" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Camera/Time decimation/ms" , false );
_ui -> statsToolBox -> updateStat ( "Camera/Time disparity/ms" , false );
_ui -> statsToolBox -> updateStat ( "Camera/Time mirroring/ms" , false );
2023-09-22 06:46:10 +08:00
_ui -> statsToolBox -> updateStat ( "Camera/Time histogram equalization/ms" , false );
_ui -> statsToolBox -> updateStat ( "Camera/Time exposure compensation/ms" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Camera/Time scan from depth/ms" , false );
2023-09-22 06:46:10 +08:00
_ui -> statsToolBox -> updateStat ( "Camera/Time total/ms" , false );
2016-03-02 18:11:19 -05:00
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ID/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Features/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Matches/" , false );
2017-01-03 21:05:00 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/MatchesRatio/" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Inliers/" , false );
2019-06-16 18:54:39 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/InliersMeanDistance/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/InliersDistribution/" , false );
2017-01-03 21:05:00 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/InliersRatio/" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPInliersRatio/" , false );
2017-09-11 13:17:53 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPRotation/rad" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/ICPTranslation/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/ICPStructuralComplexity/" , false );
2020-04-14 17:06:53 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPStructuralDistribution/" , false );
2020-03-28 18:48:51 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPCorrespondences/" , false );
2021-06-23 17:13:07 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPRMS/" , false );
2022-02-06 17:41:53 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/StdDevLin/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/StdDevAng/rad" , false );
2017-01-03 21:05:00 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/VarianceLin/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/VarianceAng/" , false );
2024-04-14 19:06:04 -07:00
_ui -> statsToolBox -> updateStat ( "Odometry/TimeDeskewing/ms" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/TimeEstimation/ms" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/TimeFiltering/ms" , false );
2020-05-31 11:22:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/GravityRollError/deg" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/GravityPitchError/deg" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/LocalMapSize/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/LocalScanMapSize/" , false );
2017-01-03 21:05:00 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/LocalKeyFrames/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleOutliers/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleConstraints/" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleTime/ms" , false );
2025-02-21 16:33:29 -08:00
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleAvgInlierDistance/pix" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleMaxKeyFramesForInlier/" , false );
2017-01-03 21:05:00 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/KeyFrameAdded/" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Interval/ms" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Speed/kph" , false );
2020-03-31 11:52:27 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Speed/mph" , false );
2018-11-21 11:57:04 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/Speed/mps" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedGuess/kph" , false );
2020-03-31 11:52:27 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedGuess/mph" , false );
2018-11-21 11:57:04 -05:00
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedGuess/mps" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Distance/m" , false );
2020-03-28 17:07:22 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/T/m" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Tx/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Ty/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Tz/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Troll/deg" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Tpitch/deg" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Tyaw/deg" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Px/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Py/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Pz/m" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Proll/deg" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Ppitch/deg" , false );
_ui -> statsToolBox -> updateStat ( "Odometry/Pyaw/deg" , false );
2016-03-02 18:11:19 -05:00
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "GUI/Refresh odom/ms" , false );
_ui -> statsToolBox -> updateStat ( "GUI/RGB-D cloud/ms" , false );
2017-04-06 12:27:12 -04:00
_ui -> statsToolBox -> updateStat ( "GUI/Graph Update/ms" , false );
#ifdef RTABMAP_OCTOMAP
_ui -> statsToolBox -> updateStat ( "GUI/Octomap Update/ms" , false );
_ui -> statsToolBox -> updateStat ( "GUI/Octomap Rendering/ms" , false );
2023-12-17 22:44:11 -08:00
#endif
#ifdef RTABMAP_GRIDMAP
_ui -> statsToolBox -> updateStat ( "GUI/Elevation Update/ms" , false );
_ui -> statsToolBox -> updateStat ( "GUI/Elevation Rendering/ms" , false );
2017-04-06 12:27:12 -04:00
#endif
_ui -> statsToolBox -> updateStat ( "GUI/Grid Update/ms" , false );
_ui -> statsToolBox -> updateStat ( "GUI/Grid Rendering/ms" , false );
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "GUI/Refresh stats/ms" , false );
_ui -> statsToolBox -> updateStat ( "GUI/Cache Data Size/MB" , false );
_ui -> statsToolBox -> updateStat ( "GUI/Cache Clouds Size/MB" , false );
2016-08-21 19:33:01 -04:00
#ifdef RTABMAP_OCTOMAP
2016-09-20 11:19:34 -04:00
_ui -> statsToolBox -> updateStat ( "GUI/Octomap Size/MB" , false );
2016-08-21 19:33:01 -04:00
#endif
2016-03-02 18:11:19 -05:00
2015-05-03 18:16:55 -04:00
this -> loadFigures ();
connect ( _ui -> statsToolBox , SIGNAL ( figuresSetupChanged ()), this , SLOT ( configGUIModified ()));
2014-10-28 01:20:57 +00:00
2017-10-20 12:09:34 +02:00
if ( splash )
{
splash -> close ();
delete splash ;
}
2014-06-11 15:51:19 +00:00
this -> setFocus ();
2015-01-23 11:17:42 -05:00
UDEBUG ( "" );
2011-06-05 14:45:39 +00:00
}
MainWindow ::~ MainWindow ()
{
2013-12-11 00:12:44 +00:00
UDEBUG ( "" );
2014-10-28 01:20:57 +00:00
this -> stopDetection ();
2011-06-05 14:45:39 +00:00
delete _ui ;
delete _elapsedTime ;
2023-03-05 16:34:56 -08:00
delete _logEventTime ;
2016-08-21 19:33:01 -04:00
#ifdef RTABMAP_OCTOMAP
delete _octomap ;
2023-12-17 22:44:11 -08:00
#endif
#ifdef RTABMAP_GRIDMAP
delete _elevationMap ;
2016-08-21 19:33:01 -04:00
#endif
delete _occupancyGrid ;
2015-12-17 14:13:19 -05:00
UDEBUG ( "" );
2011-06-05 14:45:39 +00:00
}
void MainWindow :: setupMainLayout ( bool vertical )
{
if ( vertical )
{
2014-02-11 23:04:22 +00:00
qobject_cast < QHBoxLayout *> ( _ui -> layout_imageview -> layout ()) -> setDirection ( QBoxLayout :: TopToBottom );
2011-06-05 14:45:39 +00:00
}
else if ( ! vertical )
{
2014-02-11 23:04:22 +00:00
qobject_cast < QHBoxLayout *> ( _ui -> layout_imageview -> layout ()) -> setDirection ( QBoxLayout :: LeftToRight );
2011-06-05 14:45:39 +00:00
}
}
2021-06-22 08:16:39 -04:00
std :: map < int , Transform > MainWindow :: currentVisiblePosesMap () const
2021-06-20 23:05:24 -04:00
{
2025-12-01 19:42:27 -08:00
return ! _ui -> widget_mapVisibility -> isEmpty () ? _ui -> widget_mapVisibility -> getVisiblePoses () : _currentPosesMap ;
2021-06-20 23:05:24 -04:00
}
2017-10-20 12:09:34 +02:00
void MainWindow :: setCloudViewer ( rtabmap :: CloudViewer * cloudViewer )
2018-10-15 16:58:57 +01:00
{
UASSERT ( cloudViewer );
2017-10-20 12:09:34 +02:00
delete _cloudViewer ;
2018-10-15 16:58:57 +01:00
_cloudViewer = cloudViewer ;
2017-10-20 12:09:34 +02:00
_cloudViewer -> setParent ( _ui -> layout_cloudViewer );
_cloudViewer -> setObjectName ( "widget_cloudViewer" );
_ui -> layout_cloudViewer -> layout () -> addWidget ( _cloudViewer );
2018-10-15 16:58:57 +01:00
2017-10-20 12:09:34 +02:00
_cloudViewer -> setBackfaceCulling ( true , false );
_preferencesDialog -> loadWidgetState ( _cloudViewer );
connect ( _cloudViewer , SIGNAL ( configChanged ()), this , SLOT ( configGUIModified ()));
}
void MainWindow :: setLoopClosureViewer ( rtabmap :: LoopClosureViewer * loopClosureViewer )
2018-10-15 16:58:57 +01:00
{
UASSERT ( loopClosureViewer );
delete _loopClosureViewer ;
2017-10-20 12:09:34 +02:00
_loopClosureViewer = loopClosureViewer ;
_loopClosureViewer -> setParent ( _ui -> layout_loopClosureViewer );
_loopClosureViewer -> setObjectName ( "widget_loopClosureViewer" );
_ui -> layout_loopClosureViewer -> layout () -> addWidget ( _loopClosureViewer );
}
2011-06-05 14:45:39 +00:00
void MainWindow :: closeEvent ( QCloseEvent * event )
{
// Try to close all children
/*QList<QMainWindow *> windows = this->findChildren<QMainWindow *>();
for(int i=0; i<windows.size(); i++) {
if(!windows[i]->close()) {
event->setAccepted(false);
return;
}
}*/
2013-12-11 00:12:44 +00:00
UDEBUG ( "" );
2011-06-05 14:45:39 +00:00
bool processStopped = true ;
2013-12-11 00:12:44 +00:00
if ( _state != kIdle && _state != kMonitoring && _state != kMonitoringPaused )
2011-06-05 14:45:39 +00:00
{
this -> stopDetection ();
2014-10-28 01:20:57 +00:00
if ( _state == kInitialized )
{
2015-02-18 18:22:59 -05:00
if ( this -> closeDatabase ())
{
this -> changeState ( kApplicationClosing );
}
2014-10-28 01:20:57 +00:00
}
2011-06-05 14:45:39 +00:00
if ( _state != kIdle )
{
processStopped = false ;
}
}
if ( processStopped )
{
2015-01-23 11:17:42 -05:00
//write settings before quit?
bool save = false ;
if ( this -> isWindowModified ())
{
QMessageBox :: Button b = QMessageBox :: question ( this ,
tr ( "RTAB-Map" ),
2015-02-23 15:48:25 -05:00
tr ( "There are unsaved changed settings. Save them?" ),
2015-01-23 11:17:42 -05:00
QMessageBox :: Save | QMessageBox :: Cancel | QMessageBox :: Discard );
if ( b == QMessageBox :: Save )
{
save = true ;
}
else if ( b != QMessageBox :: Discard )
{
event -> ignore ();
return ;
}
}
if ( save )
{
saveConfigGUI ();
}
2011-06-05 14:45:39 +00:00
_ui -> statsToolBox -> closeFigures ();
2014-02-11 23:04:22 +00:00
_ui -> dockWidget_imageView -> close ();
2011-06-05 14:45:39 +00:00
_ui -> dockWidget_likelihood -> close ();
2012-12-11 18:05:05 +00:00
_ui -> dockWidget_rawlikelihood -> close ();
2011-06-05 14:45:39 +00:00
_ui -> dockWidget_posterior -> close ();
_ui -> dockWidget_statsV2 -> close ();
_ui -> dockWidget_console -> close ();
2013-12-11 00:12:44 +00:00
_ui -> dockWidget_cloudViewer -> close ();
_ui -> dockWidget_loopClosureViewer -> close ();
2014-01-22 19:49:28 +00:00
_ui -> dockWidget_mapVisibility -> close ();
2014-02-11 23:04:22 +00:00
_ui -> dockWidget_graphViewer -> close ();
2014-12-02 17:01:24 -05:00
_ui -> dockWidget_odometry -> close ();
2022-02-12 13:35:23 -05:00
_ui -> dockWidget_multiSessionLoc -> close ();
2011-06-05 14:45:39 +00:00
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2011-06-05 14:45:39 +00:00
{
UERROR ( "Camera must be already deleted here!" );
2024-04-14 19:06:04 -07:00
delete _sensorCapture ;
_sensorCapture = 0 ;
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
delete _imuThread ;
_imuThread = 0 ;
}
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
if ( _odomThread )
{
UERROR ( "OdomThread must be already deleted here!" );
delete _odomThread ;
_odomThread = 0 ;
}
2011-06-05 14:45:39 +00:00
event -> accept ();
}
else
{
event -> ignore ();
}
2013-12-11 00:12:44 +00:00
UDEBUG ( "" );
2011-06-05 14:45:39 +00:00
}
2017-03-12 21:21:09 -04:00
bool MainWindow :: handleEvent ( UEvent * anEvent )
2011-06-05 14:45:39 +00:00
{
2019-10-13 17:40:09 -04:00
if ( anEvent -> getClassName (). compare ( "IMUEvent" ) == 0 )
{
2020-01-22 10:34:21 -05:00
// IMU events are published at high frequency, early exit
2019-10-13 17:40:09 -04:00
return false ;
}
else if ( anEvent -> getClassName (). compare ( "RtabmapEvent" ) == 0 )
2011-06-05 14:45:39 +00:00
{
RtabmapEvent * rtabmapEvent = ( RtabmapEvent * ) anEvent ;
2012-03-03 01:46:30 +00:00
Statistics stats = rtabmapEvent -> getStats ();
2011-06-05 14:45:39 +00:00
int highestHypothesisId = int ( uValue ( stats . data (), Statistics :: kLoopHighest_hypothesis_id (), 0.0f ));
2016-03-11 17:54:01 -05:00
int proximityClosureId = int ( uValue ( stats . data (), Statistics :: kProximitySpace_last_detection_id (), 0.0f ));
2011-06-09 02:56:31 +00:00
bool rejectedHyp = bool ( uValue ( stats . data (), Statistics :: kLoopRejectedHypothesis (), 0.0f ));
2013-12-11 00:12:44 +00:00
float highestHypothesisValue = uValue ( stats . data (), Statistics :: kLoopHighest_hypothesis_value (), 0.0f );
if (( stats . loopClosureId () > 0 &&
_ui -> actionPause_on_match -> isChecked ())
||
( stats . loopClosureId () == 0 &&
highestHypothesisId > 0 &&
highestHypothesisValue >= _preferencesDialog -> getLoopThr () &&
_ui -> actionPause_when_a_loop_hypothesis_is_rejected -> isChecked () &&
rejectedHyp )
||
2016-03-11 17:54:01 -05:00
( proximityClosureId > 0 &&
2013-12-11 00:12:44 +00:00
_ui -> actionPause_on_local_loop_detection -> isChecked ()))
2011-06-05 14:45:39 +00:00
{
2015-08-04 13:33:01 -04:00
if ( _state != kPaused && _state != kMonitoringPaused && ! _processingDownloadedMap )
2011-06-05 14:45:39 +00:00
{
if ( _preferencesDialog -> beepOnPause ())
{
2012-06-24 17:19:34 +00:00
QMetaObject :: invokeMethod ( this , "beep" );
2011-06-05 14:45:39 +00:00
}
this -> pauseDetection ();
}
}
2015-08-04 13:33:01 -04:00
if ( ! _processingDownloadedMap )
{
_processingStatistics = true ;
2018-06-15 12:17:11 -04:00
Q_EMIT statsReceived ( stats );
2015-08-04 13:33:01 -04:00
}
2011-06-05 14:45:39 +00:00
}
else if ( anEvent -> getClassName (). compare ( "RtabmapEventInit" ) == 0 )
{
2017-10-11 19:36:00 -04:00
if ( ! _recovering )
{
RtabmapEventInit * rtabmapEventInit = ( RtabmapEventInit * ) anEvent ;
2018-06-15 12:17:11 -04:00
Q_EMIT rtabmapEventInitReceived (( int ) rtabmapEventInit -> getStatus (), rtabmapEventInit -> getInfo (). c_str ());
2017-10-11 19:36:00 -04:00
}
2013-12-11 00:12:44 +00:00
}
else if ( anEvent -> getClassName (). compare ( "RtabmapEvent3DMap" ) == 0 )
{
RtabmapEvent3DMap * rtabmapEvent3DMap = ( RtabmapEvent3DMap * ) anEvent ;
2018-06-15 12:17:11 -04:00
Q_EMIT rtabmapEvent3DMapReceived ( * rtabmapEvent3DMap );
2011-06-05 14:45:39 +00:00
}
2015-04-17 18:30:12 -04:00
else if ( anEvent -> getClassName (). compare ( "RtabmapGlobalPathEvent" ) == 0 )
{
RtabmapGlobalPathEvent * rtabmapGlobalPathEvent = ( RtabmapGlobalPathEvent * ) anEvent ;
2018-06-15 12:17:11 -04:00
Q_EMIT rtabmapGlobalPathEventReceived ( * rtabmapGlobalPathEvent );
2015-04-17 18:30:12 -04:00
}
2015-07-26 15:50:39 -04:00
else if ( anEvent -> getClassName (). compare ( "RtabmapLabelErrorEvent" ) == 0 )
{
RtabmapLabelErrorEvent * rtabmapLabelErrorEvent = ( RtabmapLabelErrorEvent * ) anEvent ;
2018-06-15 12:17:11 -04:00
Q_EMIT rtabmapLabelErrorReceived ( rtabmapLabelErrorEvent -> id (), QString ( rtabmapLabelErrorEvent -> label (). c_str ()));
2015-07-26 15:50:39 -04:00
}
2015-10-13 12:50:25 -04:00
else if ( anEvent -> getClassName (). compare ( "RtabmapGoalStatusEvent" ) == 0 )
{
2018-06-15 12:17:11 -04:00
Q_EMIT rtabmapGoalStatusEventReceived ( anEvent -> getCode ());
2015-10-13 12:50:25 -04:00
}
2024-04-14 19:06:04 -07:00
else if ( anEvent -> getClassName (). compare ( "SensorEvent" ) == 0 )
2011-06-05 14:45:39 +00:00
{
2024-04-14 19:06:04 -07:00
SensorEvent * sensorEvent = ( SensorEvent * ) anEvent ;
if ( sensorEvent -> getCode () == SensorEvent :: kCodeNoMoreImages )
2011-06-05 14:45:39 +00:00
{
if ( _preferencesDialog -> beepOnPause ())
{
2012-06-24 17:19:34 +00:00
QMetaObject :: invokeMethod ( this , "beep" );
}
2018-06-15 12:17:11 -04:00
Q_EMIT noMoreImagesReceived ();
2012-06-24 17:19:34 +00:00
}
2015-11-30 18:49:28 -05:00
else
{
2024-04-14 19:06:04 -07:00
Q_EMIT cameraInfoReceived ( sensorEvent -> info ());
if ( _odomThread == 0 && ( _sensorCapture -> odomProvided ()) && _preferencesDialog -> isRGBDMode ())
2016-06-24 18:49:34 -04:00
{
2017-05-04 16:13:45 -04:00
OdometryInfo odomInfo ;
2024-04-14 19:06:04 -07:00
odomInfo . reg . covariance = sensorEvent -> info (). odomCovariance ;
2016-06-24 18:49:34 -04:00
if ( ! _processingOdometry && ! _processingStatistics )
{
_processingOdometry = true ; // if we receive too many odometry events!
2024-04-14 19:06:04 -07:00
OdometryEvent tmp ( sensorEvent -> data (), sensorEvent -> info (). odomPose , odomInfo );
2018-06-15 12:17:11 -04:00
Q_EMIT odometryReceived ( tmp , false );
2016-06-24 18:49:34 -04:00
}
else
{
2018-06-14 17:25:35 -04:00
// we receive too many odometry events! ignore them
2016-06-24 18:49:34 -04:00
}
}
2015-11-30 18:49:28 -05:00
}
2012-06-24 17:19:34 +00:00
}
2013-12-11 00:12:44 +00:00
else if ( anEvent -> getClassName (). compare ( "OdometryEvent" ) == 0 )
{
2015-12-17 14:13:19 -05:00
OdometryEvent * odomEvent = ( OdometryEvent * ) anEvent ;
if ( ! _processingOdometry && ! _processingStatistics )
2013-12-11 00:12:44 +00:00
{
2015-12-17 14:13:19 -05:00
_processingOdometry = true ; // if we receive too many odometry events!
2018-06-15 12:17:11 -04:00
Q_EMIT odometryReceived ( * odomEvent , false );
2015-12-17 14:13:19 -05:00
}
else
{
// we receive too many odometry events! just send without data
SensorData data ( cv :: Mat (), odomEvent -> data (). id (), odomEvent -> data (). stamp ());
2016-05-17 16:32:21 -04:00
data . setCameraModels ( odomEvent -> data (). cameraModels ());
2022-07-20 15:20:14 -04:00
data . setStereoCameraModels ( odomEvent -> data (). stereoCameraModels ());
2015-12-17 14:13:19 -05:00
data . setGroundTruth ( odomEvent -> data (). groundTruth ());
2017-05-04 16:13:45 -04:00
OdometryEvent tmp ( data , odomEvent -> pose (), odomEvent -> info (). copyWithoutData ());
2018-06-15 12:17:11 -04:00
Q_EMIT odometryReceived ( tmp , true );
2013-12-11 00:12:44 +00:00
}
}
2011-06-05 14:45:39 +00:00
else if ( anEvent -> getClassName (). compare ( "ULogEvent" ) == 0 )
{
ULogEvent * logEvent = ( ULogEvent * ) anEvent ;
if ( logEvent -> getCode () >= _preferencesDialog -> getGeneralLoggerPauseLevel ())
{
QMetaObject :: invokeMethod ( _ui -> dockWidget_console , "show" );
// The timer prevents multiple calls to pauseDetection() before the state can be changed
2022-02-12 16:46:10 -05:00
if ( _state != kPaused && _state != kMonitoringPaused && _state != kMonitoring && _logEventTime -> elapsed () > 1000 )
2011-06-05 14:45:39 +00:00
{
_logEventTime -> start ();
if ( _preferencesDialog -> beepOnPause ())
{
2012-06-24 17:19:34 +00:00
QMetaObject :: invokeMethod ( this , "beep" );
2011-06-05 14:45:39 +00:00
}
pauseDetection ();
}
}
}
2017-03-12 21:21:09 -04:00
return false ;
2011-06-05 14:45:39 +00:00
}
2024-04-14 19:06:04 -07:00
void MainWindow :: processCameraInfo ( const rtabmap :: SensorCaptureInfo & info )
2015-11-30 18:49:28 -05:00
{
2016-07-17 19:58:32 -04:00
if ( _firstStamp == 0.0 )
{
_firstStamp = info . stamp ;
}
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
{
_ui -> statsToolBox -> updateStat ( "Camera/Time capturing/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeCapture * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2024-04-14 19:06:04 -07:00
_ui -> statsToolBox -> updateStat ( "Camera/Time deskewing/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeDeskewing * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Camera/Time undistort depth/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeUndistortDepth * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Camera/Time bilateral filtering/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeBilateralFiltering * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Camera/Time decimation/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeImageDecimation * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Camera/Time disparity/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeDisparity * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Camera/Time mirroring/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeMirroring * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2023-09-22 06:46:10 +08:00
_ui -> statsToolBox -> updateStat ( "Camera/Time histogram equalization/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeHistogramEqualization * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Camera/Time exposure compensation/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeStereoExposureCompensation * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Camera/Time scan from depth/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeScanFromDepth * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2023-09-22 06:46:10 +08:00
_ui -> statsToolBox -> updateStat ( "Camera/Time total/ms" , _preferencesDialog -> isTimeUsedInFigures () ? info . stamp - _firstStamp :( float ) info . id , info . timeTotal * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
}
2017-10-25 09:48:00 +02:00
2018-06-15 12:17:11 -04:00
Q_EMIT ( cameraInfoProcessed ());
2015-11-30 18:49:28 -05:00
}
2016-06-15 14:18:26 -04:00
void MainWindow :: processOdometry ( const rtabmap :: OdometryEvent & odom , bool dataIgnored )
2013-12-11 00:12:44 +00:00
{
2016-07-17 19:58:32 -04:00
if ( _firstStamp == 0.0 )
{
_firstStamp = odom . data (). stamp ();
}
2016-01-08 19:15:49 -05:00
UDEBUG ( "" );
2015-03-20 15:54:33 -04:00
_processingOdometry = true ;
2014-12-18 16:54:46 -05:00
UTimer time ;
2023-11-19 19:40:20 -08:00
UTimer timeTotal ;
2015-06-11 16:57:16 -04:00
// Process Data
2016-04-29 14:29:55 -04:00
// Set color code as tooltip
if ( _ui -> imageView_odometry -> toolTip (). isEmpty ())
2015-06-11 16:57:16 -04:00
{
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> setToolTip (
"Background Color Code: \n "
" Dark Red = Odometry Lost \n "
2016-06-24 16:02:27 -04:00
" Dark Yellow = Low Inliers \n "
"Feature Color code: \n "
" Green = Inliers \n "
" Yellow = Not matched features from previous frame(s) \n "
" Red = Outliers" );
2016-04-29 14:29:55 -04:00
}
2016-01-14 15:46:54 -05:00
2016-04-29 14:29:55 -04:00
Transform pose = odom . pose ();
bool lost = false ;
bool lostStateChanged = false ;
2015-03-30 10:47:38 -04:00
2016-04-29 14:29:55 -04:00
if ( pose . isNull ())
{
UDEBUG ( "odom lost" ); // use last pose
2016-05-05 18:25:26 -04:00
lostStateChanged = _cloudViewer -> getBackgroundColor () != Qt :: darkRed ;
_cloudViewer -> setBackgroundColor ( Qt :: darkRed );
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> setBackgroundColor ( Qt :: darkRed );
2013-12-11 00:12:44 +00:00
2016-04-29 14:29:55 -04:00
pose = _lastOdomPose ;
lost = true ;
}
2017-09-11 13:17:53 -04:00
else if ( odom . info (). reg . inliers > 0 &&
2016-04-29 14:29:55 -04:00
_preferencesDialog -> getOdomQualityWarnThr () &&
2017-09-11 13:17:53 -04:00
odom . info (). reg . inliers < _preferencesDialog -> getOdomQualityWarnThr ())
2016-04-29 14:29:55 -04:00
{
2017-09-11 13:17:53 -04:00
UDEBUG ( "odom warn, quality(inliers)=%d thr=%d" , odom . info (). reg . inliers , _preferencesDialog -> getOdomQualityWarnThr ());
2016-05-05 18:25:26 -04:00
lostStateChanged = _cloudViewer -> getBackgroundColor () == Qt :: darkRed ;
_cloudViewer -> setBackgroundColor ( Qt :: darkYellow );
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> setBackgroundColor ( Qt :: darkYellow );
}
else
{
UDEBUG ( "odom ok" );
2016-05-05 18:25:26 -04:00
lostStateChanged = _cloudViewer -> getBackgroundColor () == Qt :: darkRed ;
_cloudViewer -> setBackgroundColor ( _cloudViewer -> getDefaultBackgroundColor ());
2017-10-20 12:09:34 +02:00
_ui -> imageView_odometry -> setBackgroundColor ( _ui -> imageView_odometry -> getDefaultBackgroundColor ());
2016-04-29 14:29:55 -04:00
}
2015-06-11 16:57:16 -04:00
2016-04-29 14:29:55 -04:00
if ( ! pose . isNull () && ( _ui -> dockWidget_cloudViewer -> isVisible () || _ui -> graphicsView_graphView -> isVisible ()))
{
_lastOdomPose = pose ;
}
2015-06-11 16:57:16 -04:00
2021-04-02 19:06:03 -04:00
const SensorData * data = & odom . data ();
SensorData rectifiedData ;
if ( ! data -> imageRaw (). empty () &&
(( _ui -> dockWidget_cloudViewer -> isVisible () && _preferencesDialog -> isCloudsShown ( 1 )) ||
( _ui -> dockWidget_odometry -> isVisible () && ( _ui -> imageView_odometry -> isImageShown () || _ui -> imageView_odometry -> isImageDepthShown ()))))
{
// Do we need to rectify images?
ParametersMap allParameters = _preferencesDialog -> getAllParameters ();
bool imagesAlreadyRectified = Parameters :: defaultRtabmapImagesAlreadyRectified ();
Parameters :: parse ( allParameters , Parameters :: kRtabmapImagesAlreadyRectified (), imagesAlreadyRectified );
if ( ! imagesAlreadyRectified )
{
rectifiedData = odom . data ();
if ( data -> cameraModels (). size ())
{
// Note that only RGB image is rectified, the depth image is assumed to be already registered to rectified RGB camera.
UASSERT ( int (( data -> imageRaw (). cols / data -> cameraModels (). size ()) * data -> cameraModels (). size ()) == data -> imageRaw (). cols );
int subImageWidth = data -> imageRaw (). cols / data -> cameraModels (). size ();
cv :: Mat rectifiedImages = data -> imageRaw (). clone ();
bool initRectMaps = _rectCameraModelsOdom . empty () || _rectCameraModelsOdom . size () != data -> cameraModels (). size ();
if ( initRectMaps )
{
_rectCameraModelsOdom . resize ( data -> cameraModels (). size ());
}
for ( unsigned int i = 0 ; i < data -> cameraModels (). size (); ++ i )
{
if ( data -> cameraModels ()[ i ]. isValidForRectification ())
{
if ( initRectMaps )
{
_rectCameraModelsOdom [ i ] = data -> cameraModels ()[ i ];
if ( ! _rectCameraModelsOdom [ i ]. isRectificationMapInitialized ())
{
UWARN ( "Initializing rectification maps for camera %d (only done for the first image received)..." , i );
_rectCameraModelsOdom [ i ]. initRectificationMap ();
UWARN ( "Initializing rectification maps for camera %d (only done for the first image received)... done!" , i );
}
}
UASSERT ( _rectCameraModelsOdom [ i ]. imageWidth () == data -> cameraModels ()[ i ]. imageWidth () &&
_rectCameraModelsOdom [ i ]. imageHeight () == data -> cameraModels ()[ i ]. imageHeight ());
cv :: Mat rectifiedImage = _rectCameraModelsOdom [ i ]. rectifyImage ( cv :: Mat ( data -> imageRaw (), cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data -> imageRaw (). rows )));
rectifiedImage . copyTo ( cv :: Mat ( rectifiedImages , cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data -> imageRaw (). rows )));
}
else
{
UWARN ( "Camera %d of data %d is not valid for rectification (%dx%d)." ,
i , data -> id (),
data -> cameraModels ()[ i ]. imageWidth (),
data -> cameraModels ()[ i ]. imageHeight ());
}
}
rectifiedData . setRGBDImage ( rectifiedImages , data -> depthOrRightRaw (), data -> cameraModels ());
}
2022-07-20 15:20:14 -04:00
else if ( ! data -> rightRaw (). empty () && data -> stereoCameraModels (). size ())
2021-04-02 19:06:03 -04:00
{
2022-07-20 15:20:14 -04:00
UASSERT ( int (( data -> imageRaw (). cols / data -> stereoCameraModels (). size ()) * data -> stereoCameraModels (). size ()) == data -> imageRaw (). cols );
int subImageWidth = data -> imageRaw (). cols / data -> stereoCameraModels (). size ();
cv :: Mat rectifiedLeftImages = data -> imageRaw (). clone ();
cv :: Mat rectifiedRightImages = data -> imageRaw (). clone ();
bool initRectMaps = _rectCameraModelsOdom . empty () || _rectCameraModelsOdom . size () != data -> stereoCameraModels (). size () * 2 ;
if ( initRectMaps )
2021-04-02 19:06:03 -04:00
{
2022-07-20 15:20:14 -04:00
_rectCameraModelsOdom . resize ( data -> stereoCameraModels (). size () * 2 );
}
for ( unsigned int i = 0 ; i < data -> stereoCameraModels (). size (); ++ i )
{
if ( data -> stereoCameraModels ()[ i ]. isValidForRectification ())
2021-04-02 19:06:03 -04:00
{
2022-07-20 15:20:14 -04:00
if ( initRectMaps )
2021-04-02 19:06:03 -04:00
{
2022-07-20 15:20:14 -04:00
_rectCameraModelsOdom [ i * 2 ] = data -> stereoCameraModels ()[ i ]. left ();
_rectCameraModelsOdom [ i * 2 + 1 ] = data -> stereoCameraModels ()[ i ]. right ();
if ( ! _rectCameraModelsOdom [ i * 2 ]. isRectificationMapInitialized ())
{
UWARN ( "Initializing rectification maps for stereo camera %d (only done for the first image received)..." , i );
_rectCameraModelsOdom [ i * 2 ]. initRectificationMap ();
_rectCameraModelsOdom [ i * 2 + 1 ]. initRectificationMap ();
UWARN ( "Initializing rectification maps for stereo camera %d (only done for the first image received)... done!" , i );
}
2021-04-02 19:06:03 -04:00
}
2022-07-20 15:20:14 -04:00
UASSERT ( _rectCameraModelsOdom [ i * 2 ]. imageWidth () == data -> stereoCameraModels ()[ i ]. left (). imageWidth () &&
_rectCameraModelsOdom [ i * 2 ]. imageHeight () == data -> stereoCameraModels ()[ i ]. left (). imageHeight () &&
_rectCameraModelsOdom [ i * 2 + 1 ]. imageWidth () == data -> stereoCameraModels ()[ i ]. right (). imageWidth () &&
_rectCameraModelsOdom [ i * 2 + 1 ]. imageHeight () == data -> stereoCameraModels ()[ i ]. right (). imageHeight ());
cv :: Mat rectifiedLeftImage = _rectCameraModelsOdom [ i * 2 ]. rectifyImage ( cv :: Mat ( data -> imageRaw (), cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data -> imageRaw (). rows )));
cv :: Mat rectifiedRightImage = _rectCameraModelsOdom [ i * 2 + 1 ]. rectifyImage ( cv :: Mat ( data -> rightRaw (), cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data -> rightRaw (). rows )));
rectifiedLeftImage . copyTo ( cv :: Mat ( rectifiedLeftImages , cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data -> imageRaw (). rows )));
rectifiedRightImage . copyTo ( cv :: Mat ( rectifiedRightImages , cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data -> rightRaw (). rows )));
}
else
{
UWARN ( "Stereo camera %d of data %d is not valid for rectification (%dx%d)." ,
i , data -> id (),
data -> stereoCameraModels ()[ i ]. left (). imageWidth (),
data -> stereoCameraModels ()[ i ]. right (). imageHeight ());
2021-04-02 19:06:03 -04:00
}
}
2022-07-20 15:20:14 -04:00
rectifiedData . setStereoImage ( rectifiedLeftImages , rectifiedRightImages , data -> stereoCameraModels ());
2021-04-02 19:06:03 -04:00
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "Time rectification: %fs" , time . ticks ());
2021-04-02 19:06:03 -04:00
data = & rectifiedData ;
}
}
2016-04-29 14:29:55 -04:00
if ( _ui -> dockWidget_cloudViewer -> isVisible ())
{
bool cloudUpdated = false ;
bool scanUpdated = false ;
bool featuresUpdated = false ;
2020-01-22 10:34:21 -05:00
bool filteredGravityUpdated = false ;
bool accelerationUpdated = false ;
2016-04-29 14:29:55 -04:00
if ( ! pose . isNull ())
2015-06-11 16:57:16 -04:00
{
2016-04-29 14:29:55 -04:00
// 3d cloud
2021-05-23 17:21:12 -04:00
if ( ! data -> imageRaw (). empty () &&
2021-04-02 19:06:03 -04:00
! data -> depthOrRightRaw (). empty () &&
2022-07-20 15:20:14 -04:00
( data -> cameraModels (). size () || data -> stereoCameraModels (). size ()) &&
2016-04-29 14:29:55 -04:00
_preferencesDialog -> isCloudsShown ( 1 ))
2015-06-11 16:57:16 -04:00
{
2016-12-01 21:46:09 -05:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ;
pcl :: IndicesPtr indices ( new std :: vector < int > );
2021-04-02 19:06:03 -04:00
2025-06-01 14:14:29 -07:00
cloud = util3d :: cloudRGBFromSensorData (
* data ,
2016-12-01 21:46:09 -05:00
_preferencesDialog -> getCloudDecimation ( 1 ),
_preferencesDialog -> getCloudMaxDepth ( 1 ),
_preferencesDialog -> getCloudMinDepth ( 1 ),
indices . get (),
2017-01-16 01:32:53 -05:00
_preferencesDialog -> getAllParameters (),
2025-06-01 14:14:29 -07:00
_preferencesDialog -> getCloudRoiRatios ( 1 ),
_preferencesDialog -> getCloudConfidenceThr ( 1 ));
2016-12-01 21:46:09 -05:00
if ( indices -> size ())
2015-06-11 16:57:16 -04:00
{
2016-12-01 21:46:09 -05:00
cloud = util3d :: transformPointCloud ( cloud , pose );
2016-04-29 14:29:55 -04:00
2016-12-01 21:46:09 -05:00
if ( _preferencesDialog -> isCloudMeshing ())
2015-06-11 16:57:16 -04:00
{
2016-12-01 21:46:09 -05:00
// we need to extract indices as pcl::OrganizedFastMesh doesn't take indices
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr output ( new pcl :: PointCloud < pcl :: PointXYZRGB > );
output = util3d :: extractIndices ( cloud , indices , false , true );
2015-06-16 17:41:51 -04:00
2016-12-01 21:46:09 -05:00
// Fast organized mesh
Eigen :: Vector3f viewpoint ( 0.0f , 0.0f , 0.0f );
2021-04-02 19:06:03 -04:00
if ( data -> cameraModels (). size () && ! data -> cameraModels ()[ 0 ]. localTransform (). isNull ())
2015-06-16 17:41:51 -04:00
{
2021-04-02 19:06:03 -04:00
viewpoint [ 0 ] = data -> cameraModels ()[ 0 ]. localTransform (). x ();
viewpoint [ 1 ] = data -> cameraModels ()[ 0 ]. localTransform (). y ();
viewpoint [ 2 ] = data -> cameraModels ()[ 0 ]. localTransform (). z ();
2016-12-01 21:46:09 -05:00
}
2022-07-20 15:20:14 -04:00
else if ( data -> stereoCameraModels (). size () && ! data -> stereoCameraModels ()[ 0 ]. localTransform (). isNull ())
2016-12-01 21:46:09 -05:00
{
2022-07-20 15:20:14 -04:00
viewpoint [ 0 ] = data -> stereoCameraModels ()[ 0 ]. localTransform (). x ();
viewpoint [ 1 ] = data -> stereoCameraModels ()[ 0 ]. localTransform (). y ();
viewpoint [ 2 ] = data -> stereoCameraModels ()[ 0 ]. localTransform (). z ();
2016-12-01 21:46:09 -05:00
}
std :: vector < pcl :: Vertices > polygons = util3d :: organizedFastMesh (
output ,
_preferencesDialog -> getCloudMeshingAngle (),
_preferencesDialog -> isCloudMeshingQuad (),
_preferencesDialog -> getCloudMeshingTriangleSize (),
Eigen :: Vector3f ( pose . x (), pose . y (), pose . z ()) + viewpoint );
if ( polygons . size ())
{
2021-04-02 19:06:03 -04:00
if ( _preferencesDialog -> isCloudMeshingTexture () && ! data -> imageRaw (). empty ())
2016-06-02 17:27:12 -04:00
{
2016-12-01 21:46:09 -05:00
pcl :: TextureMesh :: Ptr textureMesh ( new pcl :: TextureMesh );
pcl :: toPCLPointCloud2 ( * cloud , textureMesh -> cloud );
textureMesh -> tex_polygons . push_back ( polygons );
int w = cloud -> width ;
int h = cloud -> height ;
UASSERT ( w > 1 && h > 1 );
textureMesh -> tex_coordinates . resize ( 1 );
2016-12-02 13:43:44 -05:00
int nPoints = ( int )( textureMesh -> cloud . data . size () / textureMesh -> cloud . point_step );
2016-12-01 21:46:09 -05:00
textureMesh -> tex_coordinates [ 0 ]. resize ( nPoints );
for ( int i = 0 ; i < nPoints ; ++ i )
2016-09-07 12:15:44 -04:00
{
2016-12-01 21:46:09 -05:00
//uv
textureMesh -> tex_coordinates [ 0 ][ i ] = Eigen :: Vector2f (
float ( i % w ) / float ( w ), // u
float ( h - i / w ) / float ( h )); // v
2016-09-07 12:15:44 -04:00
}
2016-12-01 21:46:09 -05:00
pcl :: TexMaterial mesh_material ;
mesh_material . tex_d = 1.0f ;
mesh_material . tex_Ns = 75.0f ;
mesh_material . tex_illum = 1 ;
mesh_material . tex_name = "material_odom" ;
2017-01-30 21:23:56 -05:00
mesh_material . tex_file = "" ;
2016-12-01 21:46:09 -05:00
textureMesh -> tex_materials . push_back ( mesh_material );
2021-04-02 19:06:03 -04:00
if ( ! _cloudViewer -> addCloudTextureMesh ( "cloudOdom" , textureMesh , data -> imageRaw (), _odometryCorrection ))
2016-06-02 17:27:12 -04:00
{
UERROR ( "Adding cloudOdom to viewer failed!" );
}
}
2016-12-01 21:46:09 -05:00
else if ( ! _cloudViewer -> addCloudMesh ( "cloudOdom" , output , polygons , _odometryCorrection ))
2016-02-15 19:35:24 -05:00
{
UERROR ( "Adding cloudOdom to viewer failed!" );
}
2015-06-16 17:41:51 -04:00
}
2016-06-02 17:27:12 -04:00
}
2016-12-01 21:46:09 -05:00
else
{
if ( ! _cloudViewer -> addCloud ( "cloudOdom" , cloud , _odometryCorrection ))
{
UERROR ( "Adding cloudOdom to viewer failed!" );
}
}
_cloudViewer -> setCloudVisibility ( "cloudOdom" , true );
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( "cloudOdom" , _preferencesDialog -> getCloudColorScheme ( 1 ));
2016-12-01 21:46:09 -05:00
_cloudViewer -> setCloudOpacity ( "cloudOdom" , _preferencesDialog -> getCloudOpacity ( 1 ));
_cloudViewer -> setCloudPointSize ( "cloudOdom" , _preferencesDialog -> getCloudPointSize ( 1 ));
cloudUpdated = true ;
2016-04-29 14:29:55 -04:00
}
}
if ( _preferencesDialog -> isScansShown ( 1 ))
{
2016-08-08 14:35:39 -04:00
// F2M: scan local map
2018-02-16 19:20:54 -05:00
if ( ! odom . info (). localScanMap . isEmpty ())
2015-06-11 16:57:16 -04:00
{
2016-11-27 14:30:11 -05:00
if ( ! lost )
2015-11-22 18:08:32 -05:00
{
2020-11-28 17:28:34 -05:00
bool scanAlreadyThere = _cloudViewer -> getAddedClouds (). contains ( "scanMapOdom" );
bool scanAdded = false ;
if ( odom . info (). localScanMap . hasIntensity () && odom . info (). localScanMap . hasNormals ())
{
scanAdded = _cloudViewer -> addCloud ( "scanMapOdom" ,
util3d :: laserScanToPointCloudINormal ( odom . info (). localScanMap , odom . info (). localScanMap . localTransform ()),
_odometryCorrection , Qt :: blue );
}
else if ( odom . info (). localScanMap . hasNormals ())
{
scanAdded = _cloudViewer -> addCloud ( "scanMapOdom" ,
util3d :: laserScanToPointCloudNormal ( odom . info (). localScanMap , odom . info (). localScanMap . localTransform ()),
_odometryCorrection , Qt :: blue );
}
else if ( odom . info (). localScanMap . hasIntensity ())
{
scanAdded = _cloudViewer -> addCloud ( "scanMapOdom" ,
util3d :: laserScanToPointCloudI ( odom . info (). localScanMap , odom . info (). localScanMap . localTransform ()),
_odometryCorrection , Qt :: blue );
}
else
{
scanAdded = _cloudViewer -> addCloud ( "scanMapOdom" ,
util3d :: laserScanToPointCloud ( odom . info (). localScanMap , odom . info (). localScanMap . localTransform ()),
_odometryCorrection , Qt :: blue );
}
if ( ! scanAdded )
2016-11-27 14:30:11 -05:00
{
UERROR ( "Adding scanMapOdom to viewer failed!" );
}
else
{
_cloudViewer -> setCloudVisibility ( "scanMapOdom" , true );
2020-11-28 17:28:34 -05:00
_cloudViewer -> setCloudColorIndex ( "scanMapOdom" , scanAlreadyThere && _preferencesDialog -> getScanColorScheme ( 1 ) == 0 && odom . info (). localScanMap . is2d () ? 2 : _preferencesDialog -> getScanColorScheme ( 1 ));
2016-11-27 14:30:11 -05:00
_cloudViewer -> setCloudOpacity ( "scanMapOdom" , _preferencesDialog -> getScanOpacity ( 1 ));
_cloudViewer -> setCloudPointSize ( "scanMapOdom" , _preferencesDialog -> getScanPointSize ( 1 ));
}
2016-03-06 15:11:09 -05:00
}
2016-11-27 14:30:11 -05:00
scanUpdated = true ;
2015-06-11 16:57:16 -04:00
}
2016-04-29 14:29:55 -04:00
// scan cloud
2021-04-02 19:06:03 -04:00
if ( ! data -> laserScanRaw (). isEmpty ())
2016-03-01 17:33:27 -05:00
{
2021-04-02 19:06:03 -04:00
LaserScan scan = data -> laserScanRaw ();
2016-04-29 14:29:55 -04:00
2018-02-17 09:46:08 -05:00
if ( _preferencesDialog -> getDownsamplingStepScan ( 1 ) > 1 ||
_preferencesDialog -> getScanMaxRange ( 1 ) > 0.0f ||
_preferencesDialog -> getScanMinRange ( 1 ) > 0.0f )
2016-03-01 17:33:27 -05:00
{
2018-02-17 09:46:08 -05:00
scan = util3d :: commonFiltering ( scan ,
_preferencesDialog -> getDownsamplingStepScan ( 1 ),
_preferencesDialog -> getScanMinRange ( 1 ),
_preferencesDialog -> getScanMaxRange ( 1 ));
2016-04-29 14:29:55 -04:00
}
2020-11-28 17:28:34 -05:00
bool scanAlreadyThere = _cloudViewer -> getAddedClouds (). contains ( "scanOdom" );
bool scanAdded = false ;
2016-03-01 17:33:27 -05:00
2020-11-28 17:28:34 -05:00
if ( odom . info (). localScanMap . hasIntensity () && odom . info (). localScanMap . hasNormals ())
2016-04-29 14:29:55 -04:00
{
2020-11-28 17:28:34 -05:00
pcl :: PointCloud < pcl :: PointXYZINormal >:: Ptr cloud ;
cloud = util3d :: laserScanToPointCloudINormal ( scan , pose * scan . localTransform ());
if ( _preferencesDialog -> getCloudVoxelSizeScan ( 1 ) > 0.0 )
{
cloud = util3d :: voxelize ( cloud , _preferencesDialog -> getCloudVoxelSizeScan ( 1 ));
}
scanAdded = _cloudViewer -> addCloud ( "scanOdom" , cloud , _odometryCorrection , Qt :: magenta );
}
else if ( odom . info (). localScanMap . hasNormals ())
{
pcl :: PointCloud < pcl :: PointNormal >:: Ptr cloud ;
cloud = util3d :: laserScanToPointCloudNormal ( scan , pose * scan . localTransform ());
if ( _preferencesDialog -> getCloudVoxelSizeScan ( 1 ) > 0.0 )
{
cloud = util3d :: voxelize ( cloud , _preferencesDialog -> getCloudVoxelSizeScan ( 1 ));
}
scanAdded = _cloudViewer -> addCloud ( "scanOdom" , cloud , _odometryCorrection , Qt :: magenta );
}
else if ( odom . info (). localScanMap . hasIntensity ())
{
pcl :: PointCloud < pcl :: PointXYZI >:: Ptr cloud ;
cloud = util3d :: laserScanToPointCloudI ( scan , pose * scan . localTransform ());
if ( _preferencesDialog -> getCloudVoxelSizeScan ( 1 ) > 0.0 )
{
cloud = util3d :: voxelize ( cloud , _preferencesDialog -> getCloudVoxelSizeScan ( 1 ));
}
scanAdded = _cloudViewer -> addCloud ( "scanOdom" , cloud , _odometryCorrection , Qt :: magenta );
}
else
{
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloud ;
cloud = util3d :: laserScanToPointCloud ( scan , pose * scan . localTransform ());
if ( _preferencesDialog -> getCloudVoxelSizeScan ( 1 ) > 0.0 )
{
cloud = util3d :: voxelize ( cloud , _preferencesDialog -> getCloudVoxelSizeScan ( 1 ));
}
scanAdded = _cloudViewer -> addCloud ( "scanOdom" , cloud , _odometryCorrection , Qt :: magenta );
2016-04-29 14:29:55 -04:00
}
2016-03-01 17:33:27 -05:00
2020-11-28 17:28:34 -05:00
if ( ! scanAdded )
2016-04-29 14:29:55 -04:00
{
UERROR ( "Adding scanOdom to viewer failed!" );
}
else
{
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( "scanOdom" , true );
2020-11-28 17:28:34 -05:00
_cloudViewer -> setCloudColorIndex ( "scanOdom" , scanAlreadyThere && _preferencesDialog -> getScanColorScheme ( 1 ) == 0 && scan . is2d () ? 2 : _preferencesDialog -> getScanColorScheme ( 1 ));
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudOpacity ( "scanOdom" , _preferencesDialog -> getScanOpacity ( 1 ));
_cloudViewer -> setCloudPointSize ( "scanOdom" , _preferencesDialog -> getScanPointSize ( 1 ));
2016-04-29 14:29:55 -04:00
scanUpdated = true ;
2016-03-01 17:33:27 -05:00
}
}
2015-06-11 16:57:16 -04:00
}
2016-04-29 14:29:55 -04:00
// 3d features
2016-11-27 14:30:11 -05:00
if ( _preferencesDialog -> isFeaturesShown ( 1 ) && ! odom . info (). localMap . empty ())
2015-11-26 13:37:37 -05:00
{
2016-11-27 14:30:11 -05:00
if ( ! lost )
2016-04-29 14:29:55 -04:00
{
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ( new pcl :: PointCloud < pcl :: PointXYZRGB > );
cloud -> resize ( odom . info (). localMap . size ());
int i = 0 ;
for ( std :: map < int , cv :: Point3f >:: const_iterator iter = odom . info (). localMap . begin (); iter != odom . info (). localMap . end (); ++ iter )
{
2018-03-07 19:43:30 -05:00
// filter very far features from current location
2019-03-06 12:35:54 -05:00
if ( uNormSquared ( iter -> second . x - odom . pose (). x (), iter -> second . y - odom . pose (). y (), iter -> second . z - odom . pose (). z ()) < 100 * 100 )
2017-05-22 11:48:01 -04:00
{
2018-03-07 19:43:30 -05:00
( * cloud )[ i ]. x = iter -> second . x ;
( * cloud )[ i ]. y = iter -> second . y ;
( * cloud )[ i ]. z = iter -> second . z ;
// green = inlier, yellow = outliers
bool inlier = odom . info (). words . find ( iter -> first ) != odom . info (). words . end ();
( * cloud )[ i ]. r = inlier ? 0 : 255 ;
( * cloud )[ i ]. g = 255 ;
( * cloud )[ i ]. b = 0 ;
if ( ! _preferencesDialog -> isOdomOnlyInliersShown () || inlier )
{
++ i ;
}
2017-05-22 11:48:01 -04:00
}
2016-04-29 14:29:55 -04:00
}
2017-05-22 11:48:01 -04:00
cloud -> resize ( i );
2016-04-29 14:29:55 -04:00
2016-05-05 18:25:26 -04:00
_cloudViewer -> addCloud ( "featuresOdom" , cloud , _odometryCorrection );
_cloudViewer -> setCloudVisibility ( "featuresOdom" , true );
_cloudViewer -> setCloudPointSize ( "featuresOdom" , _preferencesDialog -> getFeaturesPointSize ( 1 ));
2016-04-29 14:29:55 -04:00
}
2016-11-27 14:30:11 -05:00
featuresUpdated = true ;
2016-03-01 17:33:27 -05:00
}
2017-11-30 16:52:03 -05:00
if ( _preferencesDialog -> isFrustumsShown ( 1 ))
{
QMap < std :: string , Transform > addedFrustums = _cloudViewer -> getAddedFrustums ();
for ( QMap < std :: string , Transform >:: iterator iter = addedFrustums . begin (); iter != addedFrustums . end (); ++ iter )
{
std :: list < std :: string > splitted = uSplitNumChar ( iter . key ());
if ( splitted . size () == 2 )
{
int id = std :: atoi ( splitted . back (). c_str ());
2022-07-20 15:20:14 -04:00
id -= id % 10 ;
2017-11-30 16:52:03 -05:00
if ( splitted . front (). compare ( "f_odom_" ) == 0 &&
odom . info (). localBundlePoses . find ( id ) == odom . info (). localBundlePoses . end ())
{
_cloudViewer -> removeFrustum ( iter . key ());
}
}
}
for ( std :: map < int , Transform >:: const_iterator iter = odom . info (). localBundlePoses . begin (); iter != odom . info (). localBundlePoses . end (); ++ iter )
{
2022-07-20 15:20:14 -04:00
std :: string frustumId = uFormat ( "f_odom_%d" , iter -> first * 10 );
2017-11-30 16:52:03 -05:00
if ( _cloudViewer -> getAddedFrustums (). contains ( frustumId ))
{
2022-07-20 15:20:14 -04:00
for ( size_t i = 0 ; i < 10 ; ++ i )
{
std :: string subFrustumId = uFormat ( "f_odom_%d" , iter -> first * 10 + i );
_cloudViewer -> updateFrustumPose ( subFrustumId , _odometryCorrection * iter -> second );
}
2017-11-30 16:52:03 -05:00
}
else if ( odom . info (). localBundleModels . find ( iter -> first ) != odom . info (). localBundleModels . end ())
{
2022-07-20 15:20:14 -04:00
const std :: vector < CameraModel > & models = odom . info (). localBundleModels . at ( iter -> first );
for ( size_t i = 0 ; i < models . size (); ++ i )
2017-11-30 16:52:03 -05:00
{
2022-07-20 15:20:14 -04:00
Transform t = models [ i ]. localTransform ();
if ( ! t . isNull ())
{
QColor color = Qt :: yellow ;
std :: string subFrustumId = uFormat ( "f_odom_%d" , iter -> first * 10 + i );
_cloudViewer -> addOrUpdateFrustum ( subFrustumId , _odometryCorrection * iter -> second , t , _cloudViewer -> getFrustumScale (), color , models [ i ]. fovX (), models [ i ]. fovY ());
}
2017-11-30 16:52:03 -05:00
}
}
}
2021-06-23 17:13:07 -04:00
}
if ( _preferencesDialog -> isIMUGravityShown ( 1 ) &&
( data -> imu (). orientation (). val [ 0 ] != 0 ||
data -> imu (). orientation (). val [ 1 ] != 0 ||
data -> imu (). orientation (). val [ 2 ] != 0 ||
data -> imu (). orientation (). val [ 3 ] != 0 ))
{
Eigen :: Vector3f gravity ( 0 , 0 , - _preferencesDialog -> getIMUGravityLength ( 1 ));
Transform orientation ( 0 , 0 , 0 , data -> imu (). orientation ()[ 0 ], data -> imu (). orientation ()[ 1 ], data -> imu (). orientation ()[ 2 ], data -> imu (). orientation ()[ 3 ]);
gravity = ( orientation * data -> imu (). localTransform (). inverse () * ( _odometryCorrection * pose ). rotation (). inverse ()). toEigen3f () * gravity ;
_cloudViewer -> addOrUpdateLine ( "odom_imu_orientation" , _odometryCorrection * pose , ( _odometryCorrection * pose ). translation () * Transform ( gravity [ 0 ], gravity [ 1 ], gravity [ 2 ], 0 , 0 , 0 ) * pose . rotation (). inverse (), Qt :: yellow , true , true );
filteredGravityUpdated = true ;
}
if ( _preferencesDialog -> isIMUAccShown () &&
( data -> imu (). linearAcceleration (). val [ 0 ] != 0 ||
data -> imu (). linearAcceleration (). val [ 1 ] != 0 ||
data -> imu (). linearAcceleration (). val [ 2 ] != 0 ))
{
Eigen :: Vector3f gravity (
- data -> imu (). linearAcceleration (). val [ 0 ],
- data -> imu (). linearAcceleration (). val [ 1 ],
- data -> imu (). linearAcceleration (). val [ 2 ]);
gravity = gravity . normalized () * _preferencesDialog -> getIMUGravityLength ( 1 );
gravity = data -> imu (). localTransform (). toEigen3f () * gravity ;
_cloudViewer -> addOrUpdateLine ( "odom_imu_acc" , _odometryCorrection * pose , _odometryCorrection * pose * Transform ( gravity [ 0 ], gravity [ 1 ], gravity [ 2 ], 0 , 0 , 0 ), Qt :: red , true , true );
accelerationUpdated = true ;
2017-11-30 16:52:03 -05:00
}
2015-06-11 16:57:16 -04:00
}
2016-06-15 14:18:26 -04:00
if ( ! dataIgnored )
2015-06-16 17:41:51 -04:00
{
2016-06-15 14:18:26 -04:00
if ( ! cloudUpdated && _cloudViewer -> getAddedClouds (). contains ( "cloudOdom" ))
{
_cloudViewer -> setCloudVisibility ( "cloudOdom" , false );
}
if ( ! scanUpdated && _cloudViewer -> getAddedClouds (). contains ( "scanOdom" ))
{
_cloudViewer -> setCloudVisibility ( "scanOdom" , false );
}
if ( ! scanUpdated && _cloudViewer -> getAddedClouds (). contains ( "scanMapOdom" ))
{
_cloudViewer -> setCloudVisibility ( "scanMapOdom" , false );
}
if ( ! featuresUpdated && _cloudViewer -> getAddedClouds (). contains ( "featuresOdom" ))
{
_cloudViewer -> setCloudVisibility ( "featuresOdom" , false );
}
2020-01-22 10:34:21 -05:00
if ( ! filteredGravityUpdated && _cloudViewer -> getAddedLines (). find ( "odom_imu_orientation" ) != _cloudViewer -> getAddedLines (). end ())
{
_cloudViewer -> removeLine ( "odom_imu_orientation" );
}
if ( ! accelerationUpdated && _cloudViewer -> getAddedLines (). find ( "odom_imu_acc" ) != _cloudViewer -> getAddedLines (). end ())
{
_cloudViewer -> removeLine ( "odom_imu_acc" );
}
2016-04-29 14:29:55 -04:00
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "Time 3D Rendering: %fs" , time . ticks ());
2016-04-29 14:29:55 -04:00
}
if ( ! odom . pose (). isNull ())
{
2016-11-27 14:30:11 -05:00
_odometryReceived = true ;
2016-04-29 14:29:55 -04:00
// update camera position
2025-11-16 18:56:55 -08:00
if ( _cloudViewer -> isVisible ())
2016-05-17 16:32:21 -04:00
{
2025-11-16 18:56:55 -08:00
if ( data -> cameraModels (). size () && data -> cameraModels ()[ 0 ]. isValidForProjection ())
2021-01-22 12:51:47 -05:00
{
2025-11-16 18:56:55 -08:00
_cloudViewer -> updateCameraFrustums ( _odometryCorrection * odom . pose (), data -> cameraModels ());
}
else if ( data -> stereoCameraModels (). size () && data -> stereoCameraModels ()[ 0 ]. isValidForProjection ())
{
_cloudViewer -> updateCameraFrustums ( _odometryCorrection * odom . pose (), data -> stereoCameraModels ());
}
else if ( ! data -> laserScanRaw (). isEmpty () ||
! data -> laserScanCompressed (). isEmpty ())
{
Transform scanLocalTransform ;
if ( ! data -> laserScanRaw (). isEmpty ())
{
scanLocalTransform = data -> laserScanRaw (). localTransform ();
}
else
{
scanLocalTransform = data -> laserScanCompressed (). localTransform ();
}
//fake frustum
CameraModel model (
2 ,
2 ,
2 ,
1.5 ,
scanLocalTransform * CameraModel :: opticalRotation (),
0 ,
cv :: Size ( 4 , 3 ));
_cloudViewer -> updateCameraFrustum ( _odometryCorrection * odom . pose (), model );
}
#if PCL_VERSION_COMPARE(>=, 1, 7, 2)
if ( _preferencesDialog -> isFramesShown ())
{
_cloudViewer -> addOrUpdateLine ( "odom_to_base_link" , _odometryCorrection , _odometryCorrection * odom . pose (), qRgb ( 255 , 128 , 0 ), true , false );
2021-01-22 12:51:47 -05:00
}
else
{
2025-11-16 18:56:55 -08:00
_cloudViewer -> removeLine ( "odom_to_base_link" );
2021-01-22 12:51:47 -05:00
}
2020-03-10 10:30:42 -04:00
#endif
2025-11-16 18:56:55 -08:00
_cloudViewer -> updateCameraTargetPosition ( _odometryCorrection * odom . pose ());
UDEBUG ( "Time Update Pose: %fs" , time . ticks ());
_cloudViewer -> refreshView ();
}
if ( _ui -> graphicsView_graphView -> isVisible ())
2016-04-29 14:29:55 -04:00
{
_ui -> graphicsView_graphView -> updateReferentialPosition ( _odometryCorrection * odom . pose ());
_ui -> graphicsView_graphView -> update ();
2021-05-23 17:21:12 -04:00
UDEBUG ( "Time Update graphview: %fs" , time . ticks ());
2016-04-29 14:29:55 -04:00
}
2025-11-16 18:56:55 -08:00
}
2016-04-29 14:29:55 -04:00
if ( _ui -> dockWidget_odometry -> isVisible () &&
2021-04-02 19:06:03 -04:00
! data -> imageRaw (). empty ())
2016-04-29 14:29:55 -04:00
{
if ( _ui -> imageView_odometry -> isFeaturesShown ())
{
2021-03-09 16:00:41 -05:00
if ( odom . info (). type == ( int ) Odometry :: kTypeF2M || odom . info (). type == ( int ) Odometry :: kTypeORBSLAM )
2015-06-11 16:57:16 -04:00
{
2017-05-22 11:48:01 -04:00
if ( _preferencesDialog -> isOdomOnlyInliersShown ())
{
std :: multimap < int , cv :: KeyPoint > kpInliers ;
2017-09-11 13:17:53 -04:00
for ( unsigned int i = 0 ; i < odom . info (). reg . inliersIDs . size (); ++ i )
2017-05-22 11:48:01 -04:00
{
2017-09-11 13:17:53 -04:00
kpInliers . insert ( * odom . info (). words . find ( odom . info (). reg . inliersIDs [ i ]));
2017-05-22 11:48:01 -04:00
}
_ui -> imageView_odometry -> setFeatures (
kpInliers ,
2021-04-02 19:06:03 -04:00
data -> depthRaw (),
2017-05-22 11:48:01 -04:00
Qt :: green );
}
else
{
_ui -> imageView_odometry -> setFeatures (
odom . info (). words ,
2021-04-02 19:06:03 -04:00
data -> depthRaw (),
2017-05-22 11:48:01 -04:00
Qt :: yellow );
}
2016-04-29 14:29:55 -04:00
}
2017-05-30 18:29:49 -04:00
else if ( odom . info (). type == ( int ) Odometry :: kTypeF2F ||
odom . info (). type == ( int ) Odometry :: kTypeViso2 ||
2018-07-18 16:15:41 -04:00
odom . info (). type == ( int ) Odometry :: kTypeFovis ||
2019-10-13 14:48:11 -04:00
odom . info (). type == ( int ) Odometry :: kTypeMSCKF ||
2025-09-14 19:29:28 -07:00
odom . info (). type == ( int ) Odometry :: kTypeVINSFusion ||
2021-03-28 23:51:02 -04:00
odom . info (). type == ( int ) Odometry :: kTypeOpenVINS )
2016-04-29 14:29:55 -04:00
{
std :: vector < cv :: KeyPoint > kpts ;
2017-05-30 18:29:49 -04:00
cv :: KeyPoint :: convert ( odom . info (). newCorners , kpts , 7 );
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> setFeatures (
kpts ,
2021-04-02 19:06:03 -04:00
data -> depthRaw (),
2016-04-29 14:29:55 -04:00
Qt :: red );
2015-06-11 16:57:16 -04:00
}
}
2017-06-05 19:04:23 -04:00
//detect if it is OdometryMono initialization
2016-04-29 14:29:55 -04:00
bool monoInitialization = false ;
2017-06-05 19:04:23 -04:00
if ( _preferencesDialog -> getOdomStrategy () == 6 && odom . info (). type == ( int ) Odometry :: kTypeF2F )
2017-05-30 18:29:49 -04:00
{
monoInitialization = true ;
}
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> clearLines ();
if ( lost && ! monoInitialization )
{
if ( lostStateChanged )
2015-06-11 16:57:16 -04:00
{
2016-04-29 14:29:55 -04:00
// save state
_odomImageShow = _ui -> imageView_odometry -> isImageShown ();
_odomImageDepthShow = _ui -> imageView_odometry -> isImageDepthShown ();
}
2021-04-02 19:06:03 -04:00
_ui -> imageView_odometry -> setImageDepth ( data -> imageRaw ());
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> setImageShown ( true );
_ui -> imageView_odometry -> setImageDepthShown ( true );
}
else
{
if ( lostStateChanged )
{
// restore state
_ui -> imageView_odometry -> setImageShown ( _odomImageShow );
_ui -> imageView_odometry -> setImageDepthShown ( _odomImageDepthShow );
2015-06-11 16:57:16 -04:00
}
2021-04-02 19:06:03 -04:00
_ui -> imageView_odometry -> setImage ( uCvMat2QImage ( data -> imageRaw ()));
if ( _ui -> imageView_odometry -> isImageDepthShown () && ! data -> depthOrRightRaw (). empty ())
2015-07-28 23:48:50 -04:00
{
2025-06-01 14:14:29 -07:00
_ui -> imageView_odometry -> setImageDepth ( data -> depthOrRightRaw (), data -> depthConfidenceRaw ());
2015-07-28 23:48:50 -04:00
}
2018-07-19 14:10:36 -04:00
if ( odom . info (). type == ( int ) Odometry :: kTypeF2M ||
2021-03-09 16:00:41 -05:00
odom . info (). type == ( int ) Odometry :: kTypeORBSLAM ||
2019-10-13 14:48:11 -04:00
odom . info (). type == ( int ) Odometry :: kTypeMSCKF ||
2021-03-28 23:51:02 -04:00
odom . info (). type == ( int ) Odometry :: kTypeOpenVINS )
2015-06-11 16:57:16 -04:00
{
2017-05-22 11:48:01 -04:00
if ( _ui -> imageView_odometry -> isFeaturesShown () && ! _preferencesDialog -> isOdomOnlyInliersShown ())
2015-06-11 16:57:16 -04:00
{
2017-09-11 13:17:53 -04:00
for ( unsigned int i = 0 ; i < odom . info (). reg . matchesIDs . size (); ++ i )
2015-06-11 16:57:16 -04:00
{
2017-09-11 13:17:53 -04:00
_ui -> imageView_odometry -> setFeatureColor ( odom . info (). reg . matchesIDs [ i ], Qt :: red ); // outliers
2016-04-29 14:29:55 -04:00
}
2017-09-11 13:17:53 -04:00
for ( unsigned int i = 0 ; i < odom . info (). reg . inliersIDs . size (); ++ i )
2016-04-29 14:29:55 -04:00
{
2017-09-11 13:17:53 -04:00
_ui -> imageView_odometry -> setFeatureColor ( odom . info (). reg . inliersIDs [ i ], Qt :: green ); // inliers
2015-06-11 16:57:16 -04:00
}
}
2016-04-29 14:29:55 -04:00
}
2017-05-30 18:29:49 -04:00
if (( odom . info (). type == ( int ) Odometry :: kTypeF2F ||
odom . info (). type == ( int ) Odometry :: kTypeViso2 ||
2025-09-14 19:29:28 -07:00
odom . info (). type == ( int ) Odometry :: kTypeVINSFusion ||
2017-05-30 18:29:49 -04:00
odom . info (). type == ( int ) Odometry :: kTypeFovis ) && odom . info (). refCorners . size ())
2016-04-29 14:29:55 -04:00
{
if ( _ui -> imageView_odometry -> isFeaturesShown () || _ui -> imageView_odometry -> isLinesShown ())
2015-06-11 16:57:16 -04:00
{
2016-04-29 14:29:55 -04:00
//draw lines
UASSERT ( odom . info (). refCorners . size () == odom . info (). newCorners . size ());
std :: set < int > inliers ( odom . info (). cornerInliers . begin (), odom . info (). cornerInliers . end ());
2023-10-25 14:54:20 -07:00
int subImageWidth = 0 ;
if ( data -> cameraModels (). size () > 1 || data -> stereoCameraModels (). size () > 1 )
{
subImageWidth = data -> cameraModels (). size () ? data -> cameraModels ()[ 0 ]. imageWidth () : data -> stereoCameraModels ()[ 0 ]. left (). imageWidth ();
}
2016-04-29 14:29:55 -04:00
for ( unsigned int i = 0 ; i < odom . info (). refCorners . size (); ++ i )
2015-06-11 16:57:16 -04:00
{
2016-04-29 14:29:55 -04:00
if ( _ui -> imageView_odometry -> isFeaturesShown () && inliers . find ( i ) != inliers . end ())
2015-06-11 16:57:16 -04:00
{
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> setFeatureColor ( i , Qt :: green ); // inliers
}
if ( _ui -> imageView_odometry -> isLinesShown ())
{
2023-10-25 14:54:20 -07:00
// just draw lines in same camera
if ( subImageWidth == 0 || int ( odom . info (). refCorners [ i ]. x / subImageWidth ) == int ( odom . info (). newCorners [ i ]. x / subImageWidth ))
{
_ui -> imageView_odometry -> addLine (
odom . info (). newCorners [ i ]. x ,
odom . info (). newCorners [ i ]. y ,
odom . info (). refCorners [ i ]. x ,
odom . info (). refCorners [ i ]. y ,
inliers . find ( i ) != inliers . end () ? Qt :: blue : Qt :: yellow );
}
2015-06-11 16:57:16 -04:00
}
}
}
}
}
2021-04-02 19:06:03 -04:00
if ( ! data -> imageRaw (). empty ())
2015-06-11 16:57:16 -04:00
{
2021-04-02 19:06:03 -04:00
_ui -> imageView_odometry -> setSceneRect ( QRectF ( 0 , 0 ,( float ) data -> imageRaw (). cols , ( float ) data -> imageRaw (). rows ));
2015-06-11 16:57:16 -04:00
}
2016-04-29 14:29:55 -04:00
_ui -> imageView_odometry -> update ();
2021-05-23 17:21:12 -04:00
UDEBUG ( "Time update imageview: %fs" , time . ticks ());
2016-04-29 14:29:55 -04:00
}
if ( _ui -> actionAuto_screen_capture -> isChecked () && _autoScreenCaptureOdomSync )
{
2017-12-11 17:09:04 -05:00
this -> captureScreen ( _autoScreenCaptureRAM , _autoScreenCapturePNG );
2013-12-11 00:12:44 +00:00
}
2015-03-30 10:47:38 -04:00
2015-06-11 16:57:16 -04:00
//Process info
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
2020-05-31 11:22:12 -04:00
{
2025-10-09 14:44:13 -07:00
UASSERT ( odom . info (). reg . covariance . total () == 36 && odom . info (). reg . covariance . type () == CV_64FC1 );
2024-02-07 14:43:16 -08:00
double linVar = uMax3 ( odom . info (). reg . covariance . at < double > ( 0 , 0 ), odom . info (). reg . covariance . at < double > ( 1 , 1 ) >= 9999 ? 0 : odom . info (). reg . covariance . at < double > ( 1 , 1 ), odom . info (). reg . covariance . at < double > ( 2 , 2 ) >= 9999 ? 0 : odom . info (). reg . covariance . at < double > ( 2 , 2 ));
double angVar = uMax3 ( odom . info (). reg . covariance . at < double > ( 3 , 3 ) >= 9999 ? 0 : odom . info (). reg . covariance . at < double > ( 3 , 3 ), odom . info (). reg . covariance . at < double > ( 4 , 4 ) >= 9999 ? 0 : odom . info (). reg . covariance . at < double > ( 4 , 4 ), odom . info (). reg . covariance . at < double > ( 5 , 5 ));
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Inliers/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . inliers , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/InliersMeanDistance/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . inliersMeanDistance , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/InliersDistribution/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . inliersDistribution , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/InliersRatio/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). features <= 0 ? 0.0f : float ( odom . info (). reg . inliers ) / float ( odom . info (). features ), _preferencesDialog -> isCacheSavedInFigures ());
2025-04-14 09:52:41 -07:00
for ( size_t i = 0 ; i < odom . info (). reg . matchesPerCam . size (); ++ i )
{
_ui -> statsToolBox -> updateStat ( QString ( "Odometry/matchesCam%1/" ). arg ( i ), _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . matchesPerCam [ i ], _preferencesDialog -> isCacheSavedInFigures ());
}
for ( size_t i = 0 ; i < odom . info (). reg . inliersPerCam . size (); ++ i )
{
_ui -> statsToolBox -> updateStat ( QString ( "Odometry/inliersCam%1/" ). arg ( i ), _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . inliersPerCam [ i ], _preferencesDialog -> isCacheSavedInFigures ());
}
if ( odom . info (). reg . matchesPerCam . size () == odom . info (). reg . inliersPerCam . size ())
{
for ( size_t i = 0 ; i < odom . info (). reg . matchesPerCam . size (); ++ i )
{
_ui -> statsToolBox -> updateStat ( QString ( "Odometry/inliersRatioCam%1/" ). arg ( i ), _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). reg . matchesPerCam [ i ] > 0 ? ( float ) odom . info (). reg . inliersPerCam [ i ] / ( float ) odom . info (). reg . matchesPerCam [ i ] : 0.0f , _preferencesDialog -> isCacheSavedInFigures ());
}
}
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPInliersRatio/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpInliersRatio , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/ICPRotation/rad" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpRotation , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/ICPTranslation/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpTranslation , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/ICPStructuralComplexity/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpStructuralComplexity , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/ICPStructuralDistribution/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpStructuralDistribution , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/ICPCorrespondences/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpCorrespondences , _preferencesDialog -> isCacheSavedInFigures ());
2021-06-23 17:13:07 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/ICPRMS/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . icpRMS , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Matches/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). reg . matches , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/MatchesRatio/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). features <= 0 ? 0.0f : float ( odom . info (). reg . matches ) / float ( odom . info (). features ), _preferencesDialog -> isCacheSavedInFigures ());
2024-02-07 14:43:16 -08:00
_ui -> statsToolBox -> updateStat ( "Odometry/StdDevLin/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), sqrt (( float ) linVar ), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/VarianceLin/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) linVar , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/StdDevAng/rad" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), sqrt (( float ) angVar ), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/VarianceAng/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) angVar , _preferencesDialog -> isCacheSavedInFigures ());
2024-04-14 19:06:04 -07:00
_ui -> statsToolBox -> updateStat ( "Odometry/TimeDeskewing/ms" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). timeDeskewing * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/TimeEstimation/ms" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). timeEstimation * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
if ( odom . info (). timeParticleFiltering > 0.0f )
2021-03-13 18:42:13 -05:00
{
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/TimeFiltering/ms" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). timeParticleFiltering * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2021-03-13 18:42:13 -05:00
}
2021-05-23 17:21:12 -04:00
if ( odom . info (). gravityRollError > 0.0f || odom . info (). gravityPitchError > 0.0f )
2020-03-31 11:52:27 -04:00
{
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/GravityRollError/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). gravityRollError * 180 / M_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/GravityPitchError/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). gravityPitchError * 180 / M_PI , _preferencesDialog -> isCacheSavedInFigures ());
}
_ui -> statsToolBox -> updateStat ( "Odometry/Features/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). features , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/LocalMapSize/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localMapSize , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/LocalScanMapSize/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localScanMapSize , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/LocalKeyFrames/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localKeyFrames , _preferencesDialog -> isCacheSavedInFigures ());
if ( odom . info (). localBundleTime > 0.0f )
{
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleOutliers/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localBundleOutliers , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleConstraints/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localBundleConstraints , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleTime/ms" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localBundleTime * 1000.0f , _preferencesDialog -> isCacheSavedInFigures ());
2025-02-21 16:33:29 -08:00
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleAvgInlierDistance/pix" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localBundleAvgInlierDistance , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/localBundleMaxKeyFramesForInlier/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localBundleMaxKeyFramesForInlier , _preferencesDialog -> isCacheSavedInFigures ());
2025-04-14 09:52:41 -07:00
for ( size_t i = 0 ; i < odom . info (). localBundleOutliersPerCam . size (); ++ i )
{
_ui -> statsToolBox -> updateStat ( QString ( "Odometry/localBundleOutliersCam%1/" ). arg ( i ), _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). localBundleOutliersPerCam [ i ], _preferencesDialog -> isCacheSavedInFigures ());
}
2021-05-23 17:21:12 -04:00
}
_ui -> statsToolBox -> updateStat ( "Odometry/KeyFrameAdded/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) odom . info (). keyFrameAdded ? 1.0f : 0.0f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/ID/" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), ( float ) data -> id (), _preferencesDialog -> isCacheSavedInFigures ());
Transform odomT ;
float dist = 0.0f , x , y , z , roll , pitch , yaw ;
if ( ! odom . info (). transform . isNull ())
{
odomT = odom . info (). transform ;
odom . info (). transform . getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
dist = odom . info (). transform . getNorm ();
_ui -> statsToolBox -> updateStat ( "Odometry/T/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Tx/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), x , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Ty/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), y , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Tz/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), z , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Troll/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), roll * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Tpitch/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), pitch * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Tyaw/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), yaw * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
2020-03-31 11:52:27 -04:00
}
2021-05-23 17:21:12 -04:00
if ( ! odom . info (). transformFiltered . isNull ())
{
odomT = odom . info (). transformFiltered ;
odom . info (). transformFiltered . getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
dist = odom . info (). transformFiltered . getNorm ();
_ui -> statsToolBox -> updateStat ( "Odometry/TF/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TFx/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), x , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TFy/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), y , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TFz/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), z , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TFroll/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), roll * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TFpitch/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), pitch * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TFyaw/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), yaw * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
}
2017-01-03 21:05:00 -05:00
if ( odom . info (). interval > 0 )
{
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "Odometry/Interval/ms" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). interval * 1000.f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Speed/kph" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval * 3.6f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Speed/mph" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval * 2.237f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Speed/mps" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval , _preferencesDialog -> isCacheSavedInFigures ());
if ( ! odom . info (). guess . isNull ())
{
odom . info (). guess . getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
dist = odom . info (). guess . getNorm ();
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedGuess/kph" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval * 3.6f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedGuess/mph" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval * 2.237f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedGuess/mps" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval , _preferencesDialog -> isCacheSavedInFigures ());
}
2017-01-03 21:05:00 -05:00
}
2015-12-17 14:13:19 -05:00
2021-05-23 17:21:12 -04:00
if ( ! odom . info (). transformGroundTruth . isNull ())
{
if ( ! odomT . isNull ())
{
2025-05-24 13:25:26 -07:00
_ui -> statsToolBox -> updateStat ( "Odometry/TG_error_lin/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). transformGroundTruth . getDistance ( odomT ), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TG_error_ang/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). transformGroundTruth . getAngle ( odomT ) * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
}
2014-12-02 17:01:24 -05:00
2021-05-23 17:21:12 -04:00
odom . info (). transformGroundTruth . getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
dist = odom . info (). transformGroundTruth . getNorm ();
_ui -> statsToolBox -> updateStat ( "Odometry/TG/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TGx/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), x , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TGy/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), y , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TGz/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), z , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TGroll/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), roll * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TGpitch/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), pitch * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/TGyaw/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), yaw * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
if ( odom . info (). interval > 0 )
{
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedG/kph" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval * 3.6f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedG/mph" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval * 2.237f , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/SpeedG/mps" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), dist / odom . info (). interval , _preferencesDialog -> isCacheSavedInFigures ());
}
}
2014-12-18 16:54:46 -05:00
2021-05-23 17:21:12 -04:00
//cumulative pose
if ( ! odom . pose (). isNull ())
{
odom . pose (). getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
_ui -> statsToolBox -> updateStat ( "Odometry/Px/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), x , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Py/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), y , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Pz/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), z , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Proll/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), roll * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Ppitch/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), pitch * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/Pyaw/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), yaw * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
}
if ( ! data -> groundTruth (). isNull ())
{
data -> groundTruth (). getTranslationAndEulerAngles ( x , y , z , roll , pitch , yaw );
_ui -> statsToolBox -> updateStat ( "Odometry/PGx/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), x , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/PGy/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), y , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/PGz/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), z , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/PGroll/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), roll * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/PGpitch/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), pitch * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Odometry/PGyaw/deg" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), yaw * 180.0 / CV_PI , _preferencesDialog -> isCacheSavedInFigures ());
}
if ( odom . info (). distanceTravelled > 0 )
{
_ui -> statsToolBox -> updateStat ( "Odometry/Distance/m" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), odom . info (). distanceTravelled , _preferencesDialog -> isCacheSavedInFigures ());
}
2023-11-19 19:40:20 -08:00
_ui -> statsToolBox -> updateStat ( "GUI/Refresh odom/ms" , _preferencesDialog -> isTimeUsedInFigures () ? data -> stamp () - _firstStamp :( float ) data -> id (), timeTotal . elapsed () * 1000.0 , _preferencesDialog -> isCacheSavedInFigures ());
2021-05-23 17:21:12 -04:00
UDEBUG ( "Time updating Stats toolbox: %fs" , time . ticks ());
}
2015-03-20 15:54:33 -04:00
_processingOdometry = false ;
2017-10-25 09:48:00 +02:00
2018-06-15 12:17:11 -04:00
Q_EMIT ( odometryProcessed ());
2013-12-11 00:12:44 +00:00
}
2011-06-05 14:45:39 +00:00
void MainWindow :: processStats ( const rtabmap :: Statistics & stat )
{
2012-03-03 01:46:30 +00:00
_processingStatistics = true ;
2011-06-05 14:45:39 +00:00
ULOGGER_DEBUG ( "" );
2023-03-05 16:34:56 -08:00
QElapsedTimer time , totalTime ;
2011-06-05 14:45:39 +00:00
time . start ();
2013-12-11 00:12:44 +00:00
totalTime . start ();
2011-06-05 14:45:39 +00:00
//Affichage des stats et images
2014-01-08 16:11:00 +00:00
2016-07-17 19:58:32 -04:00
if ( _firstStamp == 0.0 )
{
_firstStamp = stat . stamp ();
}
2015-05-30 20:05:35 -04:00
int refMapId = - 1 , loopMapId = - 1 ;
2018-11-08 17:39:16 -05:00
if ( stat . getLastSignatureData (). id () == stat . refImageId ())
2015-05-30 20:05:35 -04:00
{
2018-11-08 17:39:16 -05:00
refMapId = stat . getLastSignatureData (). mapId ();
2015-05-30 20:05:35 -04:00
}
2015-07-19 14:26:10 -04:00
int highestHypothesisId = static_cast < float > ( uValue ( stat . data (), Statistics :: kLoopHighest_hypothesis_id (), 0.0f ));
2016-03-11 17:54:01 -05:00
int loopId = stat . loopClosureId () > 0 ? stat . loopClosureId () : stat . proximityDetectionId () > 0 ? stat . proximityDetectionId () : highestHypothesisId ;
2018-12-07 18:29:41 -05:00
if ( loopId > 0 && _cachedSignatures . contains ( loopId ))
2015-05-30 20:05:35 -04:00
{
2015-07-19 14:26:10 -04:00
loopMapId = _cachedSignatures . value ( loopId ). mapId ();
2015-05-30 20:05:35 -04:00
}
2014-01-22 19:49:28 +00:00
2014-01-08 16:11:00 +00:00
_ui -> label_refId -> setText ( QString ( "New ID = %1 [%2]" ). arg ( stat . refImageId ()). arg ( refMapId ));
2011-06-05 14:45:39 +00:00
if ( stat . extended ())
{
2012-12-11 18:05:05 +00:00
float totalTime = static_cast < float > ( uValue ( stat . data (), Statistics :: kTimingTotal (), 0.0f ));
2013-12-11 00:12:44 +00:00
if ( totalTime / 1000.0f > float ( 1.0 / _preferencesDialog -> getDetectionRate ()))
2012-12-11 18:05:05 +00:00
{
2013-12-11 00:12:44 +00:00
UWARN ( "Processing time (%fs) is over detection rate (%fs), real-time problem!" , totalTime / 1000.0f , 1.0 / _preferencesDialog -> getDetectionRate ());
2012-12-11 18:05:05 +00:00
}
2012-06-24 17:19:34 +00:00
UDEBUG ( "" );
2013-12-11 00:12:44 +00:00
bool highestHypothesisIsSaved = ( bool ) uValue ( stat . data (), Statistics :: kLoopHypothesis_reactivated (), 0.0f );
2012-06-24 17:19:34 +00:00
2016-06-12 17:53:35 -04:00
bool smallMovement = ( bool ) uValue ( stat . data (), Statistics :: kMemorySmall_movement (), 0.0f );
2017-09-28 13:34:55 -04:00
bool fastMovement = ( bool ) uValue ( stat . data (), Statistics :: kMemoryFast_movement (), 0.0f );
2021-06-25 12:54:52 -04:00
int rehearsalMerged = ( int ) uValue ( stat . data (), Statistics :: kMemoryRehearsal_merged (), 0.0f );
2014-01-08 16:11:00 +00:00
// update cache
2015-05-30 20:05:35 -04:00
Signature signature ;
2018-11-08 17:39:16 -05:00
if ( stat . getLastSignatureData (). id () == stat . refImageId ())
2015-05-30 20:05:35 -04:00
{
2018-11-08 17:39:16 -05:00
signature = stat . getLastSignatureData ();
2021-06-25 12:54:52 -04:00
}
else if ( rehearsalMerged > 0 &&
rehearsalMerged == stat . getLastSignatureData (). id () &&
_cachedSignatures . contains ( rehearsalMerged ))
{
signature = _cachedSignatures . value ( rehearsalMerged );
}
2020-11-03 16:12:43 -05:00
2021-06-25 12:54:52 -04:00
if ( signature . id () != 0 )
{
2020-11-03 16:12:43 -05:00
// make sure data are uncompressed
// We don't need to uncompress images if we don't show them
2025-11-16 18:56:55 -08:00
bool uncompressImages = ( ! signature . sensorData (). imageCompressed (). empty () &&
(( _ui -> imageView_source -> isVisible () && _ui -> imageView_source -> isImageShown ()) ||
_loopClosureViewer -> isVisible ()))
||
( ! signature . sensorData (). depthOrRightCompressed (). empty () &&
(( _ui -> imageView_loopClosure -> isVisible () && _ui -> imageView_loopClosure -> isImageShown ()) ||
( _cloudViewer -> isVisible () && _preferencesDialog -> isCloudsShown ( 0 ))));
2020-11-05 15:50:05 -05:00
bool uncompressScan = ! signature . sensorData (). laserScanCompressed (). isEmpty () && (
2020-11-03 16:12:43 -05:00
_loopClosureViewer -> isVisible () ||
2020-11-05 15:50:05 -05:00
( _cloudViewer -> isVisible () && _preferencesDialog -> isScansShown ( 0 )));
2020-11-03 16:12:43 -05:00
cv :: Mat tmpRgb , tmpDepth , tmpG , tmpO , tmpE ;
LaserScan tmpScan ;
signature . sensorData (). uncompressData (
uncompressImages ?& tmpRgb : 0 ,
2021-03-12 17:47:36 -05:00
uncompressImages && ! signature . sensorData (). depthOrRightCompressed (). empty () ?& tmpDepth : 0 ,
2020-11-03 16:12:43 -05:00
uncompressScan ?& tmpScan : 0 ,
0 , & tmpG , & tmpO , & tmpE );
2016-06-12 17:53:35 -04:00
2021-06-25 12:54:52 -04:00
if ( stat . getLastSignatureData (). id () == stat . refImageId () &&
uStr2Bool ( _preferencesDialog -> getParameter ( Parameters :: kMemIncrementalMemory ())) &&
2016-12-01 21:46:09 -05:00
signature . getWeight () >= 0 ) // ignore intermediate nodes for the cache
2016-06-12 17:53:35 -04:00
{
2017-09-28 13:34:55 -04:00
if ( smallMovement || fastMovement )
2017-04-06 12:27:12 -04:00
{
2018-12-07 18:29:41 -05:00
_cachedSignatures . insert ( 0 , signature ); // zero means temporary
2017-04-06 12:27:12 -04:00
}
else
{
_cachedSignatures . insert ( signature . id (), signature );
_cachedMemoryUsage += signature . sensorData (). getMemoryUsed ();
2018-07-09 11:33:39 -04:00
unsigned int count = 0 ;
2020-10-05 17:34:32 -04:00
if ( ! signature . getWords3 (). empty ())
2018-07-09 11:33:39 -04:00
{
2020-10-05 17:34:32 -04:00
for ( std :: multimap < int , int >:: const_iterator jter = signature . getWords (). upper_bound ( - 1 ); jter != signature . getWords (). end (); ++ jter )
2018-07-09 11:33:39 -04:00
{
2020-10-05 17:34:32 -04:00
if ( util3d :: isFinite ( signature . getWords3 ()[ jter -> second ]))
{
++ count ;
}
2018-07-09 11:33:39 -04:00
}
}
_cachedWordsCount . insert ( std :: make_pair ( signature . id (), ( float ) count ));
2017-04-06 12:27:12 -04:00
}
2016-06-12 17:53:35 -04:00
}
2015-05-30 20:05:35 -04:00
}
2014-10-01 21:44:46 +00:00
2022-10-28 12:25:48 -07:00
// Add data
for ( std :: map < int , Signature >:: const_iterator iter = stat . getSignaturesData (). begin ();
iter != stat . getSignaturesData (). end ();
++ iter )
{
if ( signature . id () != iter -> first &&
( ! _cachedSignatures . contains ( iter -> first ) ||
( _cachedSignatures . value ( iter -> first ). sensorData (). imageCompressed (). empty () && ! iter -> second . sensorData (). imageCompressed (). empty ())))
{
_cachedSignatures . insert ( iter -> first , iter -> second );
_cachedMemoryUsage += iter -> second . sensorData (). getMemoryUsed ();
unsigned int count = 0 ;
if ( ! iter -> second . getWords3 (). empty ())
{
for ( std :: multimap < int , int >:: const_iterator jter = iter -> second . getWords (). upper_bound ( - 1 ); jter != iter -> second . getWords (). end (); ++ jter )
{
if ( util3d :: isFinite ( iter -> second . getWords3 ()[ jter -> second ]))
{
++ count ;
}
}
}
_cachedWordsCount . insert ( std :: make_pair ( iter -> first , ( float ) count ));
2022-10-30 13:59:08 -07:00
UINFO ( "Added node data %d [map=%d] to cache" , iter -> first , iter -> second . mapId ());
_currentMapIds . insert ( std :: make_pair ( iter -> first , iter -> second . mapId ()));
if ( ! iter -> second . getGroundTruthPose (). isNull ())
{
_currentGTPosesMap . insert ( std :: make_pair ( iter -> first , iter -> second . getGroundTruthPose ()));
}
2022-10-28 12:25:48 -07:00
}
}
2015-06-11 16:57:16 -04:00
// For intermediate empty nodes, keep latest image shown
2026-05-14 09:20:09 -07:00
bool rehearsedSimilarity = ( float ) uValue ( stat . data (), Statistics :: kMemoryRehearsal_id (), 0.0f ) != 0.0f ;
2021-05-16 11:15:05 -04:00
if ( signature . getWeight () >= 0 )
2015-06-11 16:57:16 -04:00
{
_ui -> imageView_source -> clear ();
_ui -> imageView_loopClosure -> clear ();
2025-11-16 18:56:55 -08:00
// To see colors
QRect rect ( 0 , 0 , 640 , 480 ); // default
if ( signature . sensorData (). cameraModels (). size () && signature . sensorData (). cameraModels (). at ( 0 ). imageSize () != cv :: Size ())
2021-05-16 11:15:05 -04:00
{
2025-11-16 18:56:55 -08:00
rect . setWidth ( signature . sensorData (). cameraModels (). at ( 0 ). imageWidth () * signature . sensorData (). cameraModels (). size ());
rect . setHeight ( signature . sensorData (). cameraModels (). at ( 0 ). imageHeight ());
2021-05-16 11:15:05 -04:00
}
2025-11-16 18:56:55 -08:00
else if ( signature . sensorData (). stereoCameraModels (). size () && signature . sensorData (). stereoCameraModels (). at ( 0 ). left (). imageSize () != cv :: Size ())
{
rect . setWidth ( signature . sensorData (). stereoCameraModels (). at ( 0 ). left (). imageWidth () * signature . sensorData (). stereoCameraModels (). size ());
rect . setHeight ( signature . sensorData (). stereoCameraModels (). at ( 0 ). left (). imageHeight ());
}
_ui -> imageView_source -> setSceneRect ( rect );
2021-05-16 11:15:05 -04:00
2017-10-20 12:09:34 +02:00
_ui -> imageView_source -> setBackgroundColor ( _ui -> imageView_source -> getDefaultBackgroundColor ());
_ui -> imageView_loopClosure -> setBackgroundColor ( _ui -> imageView_loopClosure -> getDefaultBackgroundColor ());
2015-06-11 16:57:16 -04:00
_ui -> label_matchId -> clear ();
2017-11-30 16:52:03 -05:00
int proximityTimeDetections = ( int ) uValue ( stat . data (), Statistics :: kProximityTime_detections (), 0.0f );
bool scanMatchingSuccess = ( bool ) uValue ( stat . data (), Statistics :: kNeighborLinkRefiningAccepted (), 0.0f );
_ui -> label_stats_imageNumber -> setText ( QString ( "%1 [%2]" ). arg ( stat . refImageId ()). arg ( refMapId ));
2013-12-11 00:12:44 +00:00
2017-11-30 16:52:03 -05:00
if ( rehearsalMerged > 0 )
2012-06-24 17:19:34 +00:00
{
2017-11-30 16:52:03 -05:00
_ui -> imageView_source -> setBackgroundColor ( Qt :: blue );
2012-06-24 17:19:34 +00:00
}
2017-11-30 16:52:03 -05:00
else if ( proximityTimeDetections > 0 )
2013-06-05 19:25:16 +00:00
{
2017-11-30 16:52:03 -05:00
_ui -> imageView_source -> setBackgroundColor ( Qt :: darkYellow );
2013-06-05 19:25:16 +00:00
}
2017-11-30 16:52:03 -05:00
else if ( scanMatchingSuccess )
2012-06-24 17:19:34 +00:00
{
2017-11-30 16:52:03 -05:00
_ui -> imageView_source -> setBackgroundColor ( Qt :: darkCyan );
2012-06-24 17:19:34 +00:00
}
2017-11-30 16:52:03 -05:00
else if ( rehearsedSimilarity )
2012-06-24 17:19:34 +00:00
{
2017-11-30 16:52:03 -05:00
_ui -> imageView_source -> setBackgroundColor ( Qt :: darkBlue );
}
else if ( smallMovement )
{
_ui -> imageView_source -> setBackgroundColor ( Qt :: gray );
}
else if ( fastMovement )
{
_ui -> imageView_source -> setBackgroundColor ( Qt :: magenta );
}
// Set color code as tooltip
if ( _ui -> label_refId -> toolTip (). isEmpty ())
{
_ui -> label_refId -> setToolTip (
"Background Color Code: \n "
" Blue = Weight Update Merged \n "
" Dark Blue = Weight Update \n "
" Dark Yellow = Proximity Detection in Time \n "
" Dark Cyan = Neighbor Link Refined \n "
" Gray = Small Movement \n "
" Magenta = Fast Movement \n "
"Feature Color code: \n "
" Green = New \n "
" Yellow = New but Not Unique \n "
" Red = In Vocabulary \n "
" Blue = In Vocabulary and in Previous Signature \n "
" Pink = In Vocabulary and in Loop Closure Signature \n "
" Gray = Not Quantized to Vocabulary" );
}
// Set color code as tooltip
if ( _ui -> label_matchId -> toolTip (). isEmpty ())
{
_ui -> label_matchId -> setToolTip (
"Background Color Code: \n "
" Green = Accepted Loop Closure Detection \n "
" Red = Rejected Loop Closure Detection \n "
" Yellow = Proximity Detection in Space \n "
"Feature Color code: \n "
" Red = In Vocabulary \n "
" Pink = In Vocabulary and in Loop Closure Signature \n "
" Gray = Not Quantized to Vocabulary" );
2012-06-24 17:19:34 +00:00
}
2017-11-30 16:52:03 -05:00
int rejectedHyp = bool ( uValue ( stat . data (), Statistics :: kLoopRejectedHypothesis (), 0.0f ));
float highestHypothesisValue = uValue ( stat . data (), Statistics :: kLoopHighest_hypothesis_value (), 0.0f );
2018-12-07 18:29:41 -05:00
int landmarkId = static_cast < int > ( uValue ( stat . data (), Statistics :: kLoopLandmark_detected (), 0.0f ));
int landmarkNodeRef = static_cast < int > ( uValue ( stat . data (), Statistics :: kLoopLandmark_detected_node_ref (), 0.0f ));
2017-11-30 16:52:03 -05:00
int matchId = 0 ;
Signature loopSignature ;
int shownLoopId = 0 ;
2018-12-07 18:29:41 -05:00
if ( highestHypothesisId > 0 || stat . proximityDetectionId () > 0 || landmarkId > 0 )
2013-12-11 00:12:44 +00:00
{
2017-11-30 16:52:03 -05:00
bool show = true ;
if ( stat . loopClosureId () > 0 )
2016-10-18 12:27:14 -04:00
{
2017-11-30 16:52:03 -05:00
_ui -> imageView_loopClosure -> setBackgroundColor ( Qt :: green );
_ui -> label_stats_loopClosuresDetected -> setText ( QString :: number ( _ui -> label_stats_loopClosuresDetected -> text (). toInt () + 1 ));
if ( highestHypothesisIsSaved )
2016-10-18 12:27:14 -04:00
{
2017-11-30 16:52:03 -05:00
_ui -> label_stats_loopClosuresReactivatedDetected -> setText ( QString :: number ( _ui -> label_stats_loopClosuresReactivatedDetected -> text (). toInt () + 1 ));
}
_ui -> label_matchId -> setText ( QString ( "Match ID = %1 [%2]" ). arg ( stat . loopClosureId ()). arg ( loopMapId ));
matchId = stat . loopClosureId ();
}
else if ( stat . proximityDetectionId ())
{
_ui -> imageView_loopClosure -> setBackgroundColor ( Qt :: yellow );
_ui -> label_matchId -> setText ( QString ( "Local match = %1 [%2]" ). arg ( stat . proximityDetectionId ()). arg ( loopMapId ));
matchId = stat . proximityDetectionId ();
}
2018-12-07 18:29:41 -05:00
else if ( landmarkId != 0 )
{
2022-09-19 12:23:18 -07:00
highestHypothesisId = landmarkNodeRef ;
2021-12-10 17:48:36 -05:00
if ( rejectedHyp )
{
show = _preferencesDialog -> imageRejectedShown ();
if ( show )
{
_ui -> imageView_loopClosure -> setBackgroundColor ( Qt :: red );
_ui -> label_stats_loopClosuresRejected -> setText ( QString :: number ( _ui -> label_stats_loopClosuresRejected -> text (). toInt () + 1 ));
_ui -> label_matchId -> setText ( QString ( "Landmark rejected = %1 with %2" ). arg ( landmarkId ). arg ( landmarkNodeRef ));
}
}
else
{
_ui -> imageView_loopClosure -> setBackgroundColor ( QColor ( "orange" ));
_ui -> label_matchId -> setText ( QString ( "Landmark match = %1 with %2" ). arg ( landmarkId ). arg ( landmarkNodeRef ));
matchId = landmarkNodeRef ;
}
2018-12-07 18:29:41 -05:00
}
2017-11-30 16:52:03 -05:00
else if ( rejectedHyp && highestHypothesisValue >= _preferencesDialog -> getLoopThr ())
{
show = _preferencesDialog -> imageRejectedShown () || _preferencesDialog -> imageHighestHypShown ();
if ( show )
{
_ui -> imageView_loopClosure -> setBackgroundColor ( Qt :: red );
_ui -> label_stats_loopClosuresRejected -> setText ( QString :: number ( _ui -> label_stats_loopClosuresRejected -> text (). toInt () + 1 ));
_ui -> label_matchId -> setText ( QString ( "Loop hypothesis %1 rejected!" ). arg ( highestHypothesisId ));
2016-10-18 12:27:14 -04:00
}
}
2017-11-30 16:52:03 -05:00
else
2016-10-18 12:27:14 -04:00
{
2017-11-30 16:52:03 -05:00
show = _preferencesDialog -> imageHighestHypShown ();
if ( show )
{
_ui -> label_matchId -> setText ( QString ( "Highest hypothesis (%1)" ). arg ( highestHypothesisId ));
}
2016-10-18 12:27:14 -04:00
}
2017-11-30 16:52:03 -05:00
if ( show )
2016-10-18 12:27:14 -04:00
{
2018-12-07 18:29:41 -05:00
shownLoopId = matchId > 0 ? matchId : highestHypothesisId ;
2017-11-30 16:52:03 -05:00
QMap < int , Signature >:: iterator iter = _cachedSignatures . find ( shownLoopId );
if ( iter != _cachedSignatures . end ())
{
loopSignature = iter . value ();
2025-11-16 18:56:55 -08:00
if (( _ui -> imageView_loopClosure -> isVisible () && ( _ui -> imageView_loopClosure -> isImageShown () || _ui -> imageView_loopClosure -> isImageDepthShown ())) ||
_loopClosureViewer -> isVisible ())
2020-11-03 16:12:43 -05:00
{
2025-11-16 18:56:55 -08:00
// uncompress after copy to avoid keeping uncompressed data in memory
bool uncompressImages = ! loopSignature . sensorData (). imageCompressed (). empty () && (
( _ui -> imageView_loopClosure -> isVisible () && ( _ui -> imageView_loopClosure -> isImageShown () || _ui -> imageView_loopClosure -> isImageDepthShown ())) ||
( _loopClosureViewer -> isVisible () &&
! loopSignature . sensorData (). depthOrRightCompressed (). empty ()));
bool uncompressScan = _loopClosureViewer -> isVisible () &&
! loopSignature . sensorData (). laserScanCompressed (). isEmpty ();
if ( uncompressImages || uncompressScan )
{
cv :: Mat tmpRGB , tmpDepth ;
LaserScan tmpScan ;
loopSignature . sensorData (). uncompressData (
uncompressImages ?& tmpRGB : 0 ,
uncompressImages ?& tmpDepth : 0 ,
uncompressScan ?& tmpScan : 0 );
}
2020-11-03 16:12:43 -05:00
}
2017-11-30 16:52:03 -05:00
}
2016-10-18 12:27:14 -04:00
}
}
2017-11-30 16:52:03 -05:00
_refIds . push_back ( stat . refImageId ());
_loopClosureIds . push_back ( matchId );
2018-07-26 14:40:16 -04:00
if ( matchId > 0 )
{
_cachedLocalizationsCount [ matchId ] += 1.0f ;
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update detection ui)" , time . restart ());
2017-11-30 16:52:03 -05:00
//update image views
2023-06-16 16:30:07 -07:00
if ( ! signature . sensorData (). imageRaw (). empty () ||
! loopSignature . sensorData (). imageRaw (). empty () ||
signature . getWords (). size ())
2013-12-11 00:12:44 +00:00
{
2025-11-16 18:56:55 -08:00
cv :: Mat refImage = _ui -> imageView_source -> isImageShown () ? signature . sensorData (). imageRaw () : cv :: Mat ();
cv :: Mat loopImage = _ui -> imageView_loopClosure -> isImageShown () ? loopSignature . sensorData (). imageRaw () : cv :: Mat ();
2019-02-18 18:19:55 -05:00
if ( _preferencesDialog -> isMarkerDetection () &&
_preferencesDialog -> isLandmarksShown ())
{
//draw markers
2025-11-16 18:56:55 -08:00
if ( ! signature . getLandmarks (). empty () && ! refImage . empty ())
2019-02-18 18:19:55 -05:00
{
2022-09-24 12:29:42 -07:00
if ( refImage . channels () == 1 )
{
cv :: Mat imgColor ;
cvtColor ( refImage , imgColor , cv :: COLOR_GRAY2BGR );
refImage = imgColor ;
}
else
{
refImage = refImage . clone ();
}
2019-02-18 18:19:55 -05:00
drawLandmarks ( refImage , signature );
}
2025-11-16 18:56:55 -08:00
if ( ! loopSignature . getLandmarks (). empty () && ! loopImage . empty ())
2019-02-18 18:19:55 -05:00
{
2022-09-24 12:29:42 -07:00
if ( loopImage . channels () == 1 )
{
cv :: Mat imgColor ;
cv :: cvtColor ( loopImage , imgColor , cv :: COLOR_GRAY2BGR );
loopImage = imgColor ;
}
else
{
loopImage = loopImage . clone ();
}
2019-02-18 18:19:55 -05:00
drawLandmarks ( loopImage , loopSignature );
}
}
UCvMat2QImageThread qimageThread ( refImage );
UCvMat2QImageThread qimageLoopThread ( loopImage );
2017-11-30 16:52:03 -05:00
qimageThread . start ();
qimageLoopThread . start ();
qimageThread . join ();
qimageLoopThread . join ();
QImage img = qimageThread . getQImage ();
QImage lcImg = qimageLoopThread . getQImage ();
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (convert image to qt)" , time . restart ());
2017-11-30 16:52:03 -05:00
if ( ! img . isNull ())
{
_ui -> imageView_source -> setImage ( img );
}
2025-11-16 18:56:55 -08:00
if ( ! signature . sensorData (). depthOrRightRaw (). empty () && _ui -> imageView_source -> isImageDepthShown ())
2017-11-30 16:52:03 -05:00
{
2025-06-01 14:14:29 -07:00
_ui -> imageView_source -> setImageDepth ( signature . sensorData (). depthOrRightRaw (), signature . sensorData (). depthConfidenceRaw ());
2017-11-30 16:52:03 -05:00
}
if ( ! lcImg . isNull ())
{
_ui -> imageView_loopClosure -> setImage ( lcImg );
}
2025-11-16 18:56:55 -08:00
if ( ! loopSignature . sensorData (). depthOrRightRaw (). empty () && _ui -> imageView_loopClosure -> isImageDepthShown ())
2017-11-30 16:52:03 -05:00
{
2025-06-01 14:14:29 -07:00
_ui -> imageView_loopClosure -> setImageDepth ( loopSignature . sensorData (). depthOrRightRaw (), loopSignature . sensorData (). depthConfidenceRaw ());
2017-11-30 16:52:03 -05:00
}
2025-11-16 18:56:55 -08:00
if ( lcImg . isNull ())
{
QRect sceneRect ;
if ( loopSignature . sensorData (). cameraModels (). size () && loopSignature . sensorData (). cameraModels (). at ( 0 ). imageSize () != cv :: Size ())
{
rect . setWidth ( loopSignature . sensorData (). cameraModels (). at ( 0 ). imageWidth () * loopSignature . sensorData (). cameraModels (). size ());
rect . setHeight ( loopSignature . sensorData (). cameraModels (). at ( 0 ). imageHeight ());
}
else if ( loopSignature . sensorData (). stereoCameraModels (). size () && loopSignature . sensorData (). stereoCameraModels (). at ( 0 ). left (). imageSize () != cv :: Size ())
{
rect . setWidth ( loopSignature . sensorData (). stereoCameraModels (). at ( 0 ). left (). imageWidth () * loopSignature . sensorData (). stereoCameraModels (). size ());
rect . setHeight ( loopSignature . sensorData (). stereoCameraModels (). at ( 0 ). left (). imageHeight ());
}
if ( sceneRect . isValid ())
{
_ui -> imageView_loopClosure -> setSceneRect ( sceneRect );
}
}
2017-11-30 16:52:03 -05:00
if ( _ui -> imageView_loopClosure -> sceneRect (). isNull ())
{
_ui -> imageView_loopClosure -> setSceneRect ( _ui -> imageView_source -> sceneRect ());
}
2013-12-11 00:12:44 +00:00
}
2021-05-16 11:15:05 -04:00
else if ( _ui -> imageView_loopClosure -> sceneRect (). isNull () &&
! _ui -> imageView_source -> sceneRect (). isNull ())
{
_ui -> imageView_loopClosure -> setSceneRect ( _ui -> imageView_source -> sceneRect ());
}
2017-11-30 16:52:03 -05:00
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update detection imageviews)" , time . restart ());
2017-11-30 16:52:03 -05:00
2025-11-16 18:56:55 -08:00
if ( _ui -> imageView_source -> isFeaturesShown () || _ui -> imageView_loopClosure -> isFeaturesShown () ||
( _ui -> imageView_source -> isLinesShown () && _ui -> imageView_loopClosure -> isLinesShown ()))
2020-10-05 17:34:32 -04:00
{
2025-11-16 18:56:55 -08:00
// do it after scaling
std :: multimap < int , cv :: KeyPoint > wordsA ;
std :: multimap < int , cv :: KeyPoint > wordsB ;
if ( signature . getWords (). size () == signature . getWordsKpts (). size () &&
( _ui -> imageView_source -> isFeaturesShown () || ( _ui -> imageView_source -> isLinesShown () && _ui -> imageView_loopClosure -> isLinesShown ())))
2025-09-03 16:55:54 -07:00
{
2025-11-16 18:56:55 -08:00
for ( std :: map < int , int >:: const_iterator iter = signature . getWords (). begin (); iter != signature . getWords (). end (); ++ iter )
{
wordsA . insert ( wordsA . end (), std :: make_pair ( iter -> first , signature . getWordsKpts ()[ iter -> second ]));
}
2025-09-03 16:55:54 -07:00
}
2025-11-16 18:56:55 -08:00
if ( loopSignature . getWords (). size () == loopSignature . getWordsKpts (). size () &&
( _ui -> imageView_loopClosure -> isFeaturesShown () || ( _ui -> imageView_source -> isLinesShown () && _ui -> imageView_loopClosure -> isLinesShown ())))
2025-09-03 16:55:54 -07:00
{
2025-11-16 18:56:55 -08:00
for ( std :: map < int , int >:: const_iterator iter = loopSignature . getWords (). begin (); iter != loopSignature . getWords (). end (); ++ iter )
{
wordsB . insert ( wordsB . end (), std :: make_pair ( iter -> first , loopSignature . getWordsKpts ()[ iter -> second ]));
}
2025-09-03 16:55:54 -07:00
}
2025-11-16 18:56:55 -08:00
this -> drawKeypoints ( wordsA , wordsB );
}
else {
_ui -> imageView_source -> clearFeatures ();
_ui -> imageView_loopClosure -> clearFeatures ();
_ui -> imageView_source -> clearLines ();
_ui -> imageView_loopClosure -> clearLines ();
_lastIds . clear ();
2020-10-05 17:34:32 -04:00
}
2017-11-30 16:52:03 -05:00
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (draw keypoints)" , time . restart ());
2017-11-30 16:52:03 -05:00
// loop closure view
if (( stat . loopClosureId () > 0 || stat . proximityDetectionId () > 0 ) &&
! stat . loopClosureTransform (). isNull () &&
! loopSignature . sensorData (). imageRaw (). empty ())
2013-12-11 00:12:44 +00:00
{
2017-11-30 16:52:03 -05:00
// the last loop closure data
Transform loopClosureTransform = stat . loopClosureTransform ();
signature . setPose ( loopClosureTransform );
_loopClosureViewer -> setData ( loopSignature , signature );
if ( _ui -> dockWidget_loopClosureViewer -> isVisible ())
{
UTimer loopTimer ;
_loopClosureViewer -> updateView ( Transform (), _preferencesDialog -> getAllParameters ());
UINFO ( "Updating loop closure cloud view time=%fs" , loopTimer . elapsed ());
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
{
_ui -> statsToolBox -> updateStat ( "GUI/RGB-D closure view/ms" , _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), int ( loopTimer . elapsed () * 1000.0f ), _preferencesDialog -> isCacheSavedInFigures ());
}
2017-11-30 16:52:03 -05:00
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update loop closure viewer)" , time . restart ());
2015-05-27 19:02:27 -04:00
}
2013-12-11 00:12:44 +00:00
}
2026-05-14 09:20:09 -07:00
else if ( rehearsedSimilarity )
{
_ui -> imageView_source -> setBackgroundColor ( Qt :: darkBlue );
}
else if ( smallMovement )
{
_ui -> imageView_source -> setBackgroundColor ( Qt :: gray );
}
else if ( fastMovement )
{
_ui -> imageView_source -> setBackgroundColor ( Qt :: magenta );
}
2013-12-11 00:12:44 +00:00
2012-06-24 17:19:34 +00:00
// PDF AND LIKELIHOOD
if ( ! stat . posterior (). empty () && _ui -> dockWidget_posterior -> isVisible ())
{
UDEBUG ( "" );
2012-12-11 18:05:05 +00:00
_posteriorCurve -> setData ( QMap < int , float > ( stat . posterior ()), QMap < int , int > ( stat . weights ()));
2012-06-24 17:19:34 +00:00
ULOGGER_DEBUG ( "" );
//Adjust thresholds
2024-11-03 03:51:05 +08:00
Q_EMIT ( loopClosureThrChanged ( 1.0 - _preferencesDialog -> getLoopThr ()));
2012-06-24 17:19:34 +00:00
}
if ( ! stat . likelihood (). empty () && _ui -> dockWidget_likelihood -> isVisible ())
{
2012-12-11 18:05:05 +00:00
_likelihoodCurve -> setData ( QMap < int , float > ( stat . likelihood ()), QMap < int , int > ( stat . weights ()));
2012-06-24 17:19:34 +00:00
}
2012-12-11 18:05:05 +00:00
if ( ! stat . rawLikelihood (). empty () && _ui -> dockWidget_rawlikelihood -> isVisible ())
2012-06-24 17:19:34 +00:00
{
2012-12-11 18:05:05 +00:00
_rawLikelihoodCurve -> setData ( QMap < int , float > ( stat . rawLikelihood ()), QMap < int , int > ( stat . weights ()));
2012-06-24 17:19:34 +00:00
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update likelihood and posterior)" , time . restart ());
2012-06-24 17:19:34 +00:00
// Update statistics tool box
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
2012-06-24 17:19:34 +00:00
{
2021-05-23 17:21:12 -04:00
const std :: map < std :: string , float > & statistics = stat . data ();
std :: string odomStr = "Odometry/" ;
for ( std :: map < std :: string , float >:: const_iterator iter = statistics . begin (); iter != statistics . end (); ++ iter )
2020-02-21 16:16:36 -05:00
{
2021-05-23 17:21:12 -04:00
//ULOGGER_DEBUG("Updating stat \"%s\"", (*iter).first.c_str());
if (( * iter ). first . size () < odomStr . size () || ( * iter ). first . substr ( 0 , odomStr . size ()). compare ( odomStr ) != 0 )
{
_ui -> statsToolBox -> updateStat ( QString (( * iter ). first . c_str ()). replace ( '_' , ' ' ), _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), ( * iter ). second , _preferencesDialog -> isCacheSavedInFigures ());
}
2020-02-21 16:16:36 -05:00
}
2012-06-24 17:19:34 +00:00
}
2012-12-11 18:05:05 +00:00
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update stats toolbox)" , time . restart ());
2013-12-11 00:12:44 +00:00
//======================
// RGB-D Mapping stuff
//======================
2023-06-16 16:30:07 -07:00
if ( ! stat . mapCorrection (). isNull ())
{
_odometryCorrection = stat . mapCorrection ();
}
2013-12-11 00:12:44 +00:00
// update clouds
if ( stat . poses (). size ())
{
2014-10-28 19:57:21 +00:00
// update pose only if odometry is not received
2018-11-08 17:39:16 -05:00
std :: map < int , int > mapIds = _currentMapIds ;
std :: map < int , Transform > groundTruth = _currentGTPosesMap ;
mapIds . insert ( std :: make_pair ( stat . getLastSignatureData (). id (), stat . getLastSignatureData (). mapId ()));
2019-07-03 12:01:34 -04:00
if ( ! stat . getLastSignatureData (). getGroundTruthPose (). isNull () &&
_cachedSignatures . contains ( stat . getLastSignatureData (). id ()))
2015-05-30 20:05:35 -04:00
{
2018-11-08 17:39:16 -05:00
groundTruth . insert ( std :: make_pair ( stat . getLastSignatureData (). id (), stat . getLastSignatureData (). getGroundTruthPose ()));
}
2016-01-07 19:39:21 -05:00
2019-01-16 20:25:14 -05:00
if ( _preferencesDialog -> isPriorIgnored () &&
_ui -> graphicsView_graphView -> getWorldMapRotation () == 0.0f &&
stat . getLastSignatureData (). sensorData (). gps (). stamp () != 0.0 &&
stat . poses (). find ( stat . getLastSignatureData (). id ()) != stat . poses (). end ())
{
float bearing = ( float )(( - ( stat . getLastSignatureData (). sensorData (). gps (). bearing () - 90 )) * M_PI / 180.0 );
float gpsRotationOffset = stat . poses (). at ( stat . getLastSignatureData (). id ()). theta () - bearing ;
_ui -> graphicsView_graphView -> setWorldMapRotation ( gpsRotationOffset );
}
else if ( ! _preferencesDialog -> isPriorIgnored () &&
_ui -> graphicsView_graphView -> getWorldMapRotation () != 0.0f )
{
_ui -> graphicsView_graphView -> setWorldMapRotation ( 0.0f );
}
2016-01-07 19:39:21 -05:00
std :: map < int , Transform > poses = stat . poses ();
2018-12-07 18:29:41 -05:00
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update gt-gps stuff)" , time . restart ());
2016-01-07 19:39:21 -05:00
2022-01-12 16:14:42 -05:00
UDEBUG ( "%d %d %d" , poses . size (), poses . size () ? poses . rbegin () -> first : 0 , stat . refImageId ());
2017-05-23 12:00:33 -04:00
if ( ! _odometryReceived && poses . size () && poses . rbegin () -> first == stat . refImageId ())
2016-05-17 16:32:21 -04:00
{
2025-11-16 18:56:55 -08:00
if ( _cloudViewer -> isVisible ())
2016-05-17 16:32:21 -04:00
{
2025-11-16 18:56:55 -08:00
if ( poses . rbegin () -> first == stat . getLastSignatureData (). id ())
2016-05-17 16:32:21 -04:00
{
2025-11-16 18:56:55 -08:00
if ( stat . getLastSignatureData (). sensorData (). cameraModels (). size () && stat . getLastSignatureData (). sensorData (). cameraModels ()[ 0 ]. isValidForProjection ())
2021-01-22 12:51:47 -05:00
{
2025-11-16 18:56:55 -08:00
_cloudViewer -> updateCameraFrustums ( poses . rbegin () -> second , stat . getLastSignatureData (). sensorData (). cameraModels ());
2021-01-22 12:51:47 -05:00
}
2025-11-16 18:56:55 -08:00
else if ( stat . getLastSignatureData (). sensorData (). stereoCameraModels (). size () && stat . getLastSignatureData (). sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ())
2021-01-22 12:51:47 -05:00
{
2025-11-16 18:56:55 -08:00
_cloudViewer -> updateCameraFrustums ( poses . rbegin () -> second , stat . getLastSignatureData (). sensorData (). stereoCameraModels ());
}
else if ( ! stat . getLastSignatureData (). sensorData (). laserScanRaw (). isEmpty () ||
! stat . getLastSignatureData (). sensorData (). laserScanCompressed (). isEmpty ())
{
Transform scanLocalTransform ;
if ( ! stat . getLastSignatureData (). sensorData (). laserScanRaw (). isEmpty ())
{
scanLocalTransform = stat . getLastSignatureData (). sensorData (). laserScanRaw (). localTransform ();
}
else
{
scanLocalTransform = stat . getLastSignatureData (). sensorData (). laserScanCompressed (). localTransform ();
}
//fake frustum
CameraModel model (
2 ,
2 ,
2 ,
1.5 ,
scanLocalTransform * CameraModel :: opticalRotation (),
0 ,
cv :: Size ( 4 , 3 ));
_cloudViewer -> updateCameraFrustum ( poses . rbegin () -> second , model );
2021-01-22 12:51:47 -05:00
}
}
2025-11-16 18:56:55 -08:00
_cloudViewer -> updateCameraTargetPosition ( poses . rbegin () -> second );
2016-05-17 16:32:21 -04:00
}
2020-03-10 10:30:42 -04:00
2016-05-17 16:32:21 -04:00
if ( _ui -> graphicsView_graphView -> isVisible ())
{
_ui -> graphicsView_graphView -> updateReferentialPosition ( poses . rbegin () -> second );
}
}
2018-12-07 18:29:41 -05:00
if ( _cachedSignatures . contains ( 0 ) && stat . refImageId () > 0 )
2017-04-06 12:27:12 -04:00
{
if ( poses . find ( stat . refImageId ()) != poses . end ())
{
2018-12-07 18:29:41 -05:00
poses . insert ( std :: make_pair ( 0 , poses . at ( stat . refImageId ())));
2017-04-06 12:27:12 -04:00
poses . erase ( stat . refImageId ());
}
if ( groundTruth . find ( stat . refImageId ()) != groundTruth . end ())
{
2018-12-07 18:29:41 -05:00
groundTruth . insert ( std :: make_pair ( 0 , groundTruth . at ( stat . refImageId ())));
2017-04-06 12:27:12 -04:00
groundTruth . erase ( stat . refImageId ());
}
}
std :: map < std :: string , float > updateCloudSats ;
2016-01-07 19:39:21 -05:00
updateMapCloud (
poses ,
2014-10-30 19:38:58 +00:00
stat . constraints (),
2015-07-26 15:50:39 -04:00
mapIds ,
2022-01-20 18:56:38 -05:00
stat . labels (),
2017-04-06 12:27:12 -04:00
groundTruth ,
2021-12-18 18:53:59 -05:00
stat . odomCachePoses (),
stat . odomCacheConstraints (),
2017-04-06 12:27:12 -04:00
false ,
& updateCloudSats );
2014-02-11 23:04:22 +00:00
2013-12-11 00:12:44 +00:00
_odometryReceived = false ;
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update map cloud)" , time . restart ());
2017-04-06 12:27:12 -04:00
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
2017-04-06 12:27:12 -04:00
{
2021-05-23 17:21:12 -04:00
for ( std :: map < std :: string , float >:: iterator iter = updateCloudSats . begin (); iter != updateCloudSats . end (); ++ iter )
{
_ui -> statsToolBox -> updateStat ( iter -> first . c_str (), _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), int ( iter -> second ), _preferencesDialog -> isCacheSavedInFigures ());
}
2017-04-06 12:27:12 -04:00
}
2022-10-04 16:27:33 -07:00
#if PCL_VERSION_COMPARE(>=, 1, 7, 2)
if ( _preferencesDialog -> isFramesShown ())
{
_cloudViewer -> addOrUpdateCoordinate ( "map_frame" , Transform :: getIdentity (), 0.5 , false );
_cloudViewer -> addOrUpdateCoordinate ( "odom_frame" , _odometryCorrection , 0.35 , false );
_cloudViewer -> addOrUpdateLine ( "map_to_odom" , Transform :: getIdentity (), _odometryCorrection , qRgb ( 255 , 128 , 0 ), true , false );
if ( _preferencesDialog -> isLabelsShown ())
{
_cloudViewer -> addOrUpdateText ( "map_frame_label" , "map" , Transform :: getIdentity (), 0.1 , Qt :: white );
_cloudViewer -> addOrUpdateText ( "odom_frame_label" , "odom" , _odometryCorrection , 0.1 , Qt :: white );
}
}
else
{
_cloudViewer -> removeLine ( "map_to_odom" );
_cloudViewer -> removeCoordinate ( "odom_frame" );
_cloudViewer -> removeCoordinate ( "map_frame" );
_cloudViewer -> removeText ( "map_frame_label" );
_cloudViewer -> removeText ( "odom_frame_label" );
}
#endif
2013-12-11 00:12:44 +00:00
}
2015-01-07 17:30:20 -05:00
2015-10-26 12:59:20 -04:00
if ( _ui -> graphicsView_graphView -> isVisible ())
2015-01-07 17:30:20 -05:00
{
2015-10-26 12:59:20 -04:00
// update posterior on the graph view
if ( _preferencesDialog -> isPosteriorGraphView () &&
stat . posterior (). size ())
{
2025-04-16 13:41:17 -07:00
_ui -> graphicsView_graphView -> updateNodeColorByValue ( "Posterior" , stat . posterior ());
2015-10-26 12:59:20 -04:00
}
2018-07-26 14:40:16 -04:00
else if ( _preferencesDialog -> isRGBDMode ())
2018-07-09 11:33:39 -04:00
{
2018-07-26 14:40:16 -04:00
if ( _preferencesDialog -> isWordsCountGraphView () &&
_cachedWordsCount . size ())
{
2025-04-16 13:41:17 -07:00
_ui -> graphicsView_graphView -> updateNodeColorByValue ( "Visual Words" , _cachedWordsCount , ( float ) _preferencesDialog -> getKpMaxFeatures ());
2018-07-26 14:40:16 -04:00
}
else if ( _preferencesDialog -> isLocalizationsCountGraphView () &&
_cachedLocalizationsCount . size ())
{
2025-04-16 13:41:17 -07:00
_ui -> graphicsView_graphView -> updateNodeColorByValue ( "Re-Localization Count" , _cachedLocalizationsCount , 1.0f );
2018-07-26 14:40:16 -04:00
}
2018-07-09 11:33:39 -04:00
}
2022-10-04 16:27:33 -07:00
if ( _preferencesDialog -> isRelocalizationColorOdomCacheGraphView () && ! stat . odomCachePoses (). empty ())
{
std :: map < int , float > colors ;
colors . insert ( std :: make_pair ( stat . odomCachePoses (). rbegin () -> first , 240 ));
for ( std :: multimap < int , Link >:: const_iterator iter = stat . odomCacheConstraints (). begin (); iter != stat . odomCacheConstraints (). end (); ++ iter )
{
if ( iter -> second . type () != Link :: kNeighbor )
{
uInsert ( colors , std :: pair < int , float > ( iter -> second . from () > iter -> second . to () ? iter -> second . from () : iter -> second . to (), 120 )); //green
}
}
for ( std :: map < int , Transform >:: const_iterator iter = stat . odomCachePoses (). begin (); iter != stat . odomCachePoses (). end (); ++ iter )
{
if ( stat . poses (). find ( iter -> first ) == stat . poses (). end ())
{
colors . insert ( std :: make_pair ( iter -> first , 240 )); //red
}
}
2025-04-16 13:41:17 -07:00
_ui -> graphicsView_graphView -> updateNodeColorByValue ( "Re-Localized" , colors , 240 );
2022-10-04 16:27:33 -07:00
}
2015-10-26 12:59:20 -04:00
// update local path on the graph view
_ui -> graphicsView_graphView -> updateLocalPath ( stat . localPath ());
if ( stat . localPath (). size () == 0 )
{
// clear the global path if set (goal reached)
_ui -> graphicsView_graphView -> setGlobalPath ( std :: vector < std :: pair < int , Transform > > ());
}
// update current goal id
if ( stat . currentGoalId () > 0 )
{
_ui -> graphicsView_graphView -> setCurrentGoalID ( stat . currentGoalId (), uValue ( stat . poses (), stat . currentGoalId (), Transform ()));
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update graph view)" , time . restart ());
2015-05-03 18:16:55 -04:00
}
2016-08-21 19:33:01 -04:00
2022-02-12 13:35:23 -05:00
if ( _multiSessionLocWidget -> isVisible ())
{
_multiSessionLocWidget -> updateView ( signature , stat );
}
2018-12-07 18:29:41 -05:00
_cachedSignatures . remove ( 0 ); // remove tmp negative ids
2017-04-06 12:27:12 -04:00
2016-08-21 19:33:01 -04:00
// keep only compressed data in cache
if ( _cachedSignatures . contains ( stat . refImageId ()))
{
Signature & s = * _cachedSignatures . find ( stat . refImageId ());
_cachedMemoryUsage -= s . sensorData (). getMemoryUsed ();
2019-04-09 20:05:24 -04:00
s . sensorData (). clearRawData ();
2016-08-21 19:33:01 -04:00
_cachedMemoryUsage += s . sensorData (). getMemoryUsed ();
}
2022-10-28 12:25:48 -07:00
// Check missing cache
if ( stat . getSignaturesData (). size () <= 1 )
{
if ( _preferencesDialog -> isMissingCacheRepublished () &&
_preferencesDialog -> isImagesKept () &&
atoi ( _preferencesDialog -> getParameter ( Parameters :: kRtabmapMaxRepublished ()). c_str ()) > 0 )
{
std :: vector < int > missingIds ;
2022-10-31 13:21:13 -07:00
bool ignoreNewData = smallMovement || fastMovement || signature . getWeight () < 0 ;
2023-03-04 13:38:27 -08:00
std :: set < int > ids = uKeysSet ( stat . poses ());
if ( ids . empty ())
2022-10-28 12:25:48 -07:00
{
2023-03-04 13:38:27 -08:00
// In appearance-only mode
ids = uKeysSet ( stat . posterior ());
}
for ( std :: set < int >:: const_iterator iter = ids . lower_bound ( 1 ); iter != ids . end (); ++ iter )
{
if ( ! ignoreNewData || stat . refImageId () != * iter )
2022-10-28 12:25:48 -07:00
{
2023-03-04 13:38:27 -08:00
QMap < int , Signature >:: iterator ster = _cachedSignatures . find ( * iter );
2022-10-30 13:59:08 -07:00
if ( ster == _cachedSignatures . end () ||
( ster . value (). getWeight () >= 0 && // ignore intermediate nodes
ster . value (). sensorData (). imageCompressed (). empty () &&
ster . value (). sensorData (). depthOrRightCompressed (). empty () &&
ster . value (). sensorData (). laserScanCompressed (). empty ()))
{
2023-03-04 13:38:27 -08:00
missingIds . push_back ( * iter );
2022-10-30 13:59:08 -07:00
}
2022-10-28 12:25:48 -07:00
}
}
if ( ! missingIds . empty ())
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdRepublishData , UVariant ( missingIds )));
}
}
}
2021-05-23 17:21:12 -04:00
UDEBUG ( "time= %d ms (update cache)" , time . restart ());
2011-06-05 14:45:39 +00:00
}
else if ( ! stat . extended () && stat . loopClosureId () > 0 )
{
_ui -> label_stats_loopClosuresDetected -> setText ( QString :: number ( _ui -> label_stats_loopClosuresDetected -> text (). toInt () + 1 ));
2014-01-08 16:11:00 +00:00
_ui -> label_matchId -> setText ( QString ( "Match ID = %1 [%2]" ). arg ( stat . loopClosureId ()). arg ( loopMapId ));
2011-06-05 14:45:39 +00:00
}
2015-06-15 14:44:44 -04:00
else
{
_ui -> label_matchId -> clear ();
}
2013-12-11 00:12:44 +00:00
float elapsedTime = static_cast < float > ( totalTime . elapsed ());
2014-02-19 19:00:38 +00:00
UINFO ( "Updating GUI time = %fs" , elapsedTime / 1000.0f );
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
{
_ui -> statsToolBox -> updateStat ( "GUI/Refresh stats/ms" , _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), elapsedTime , _preferencesDialog -> isCacheSavedInFigures ());
}
2014-02-11 23:04:22 +00:00
if ( _ui -> actionAuto_screen_capture -> isChecked () && ! _autoScreenCaptureOdomSync )
{
2017-12-11 17:09:04 -05:00
this -> captureScreen ( _autoScreenCaptureRAM , _autoScreenCapturePNG );
2014-02-11 23:04:22 +00:00
}
2015-01-12 17:43:42 -05:00
if ( ! _preferencesDialog -> isImagesKept ())
{
_cachedSignatures . clear ();
2016-06-12 17:53:35 -04:00
_cachedMemoryUsage = 0 ;
2018-07-09 11:33:39 -04:00
_cachedWordsCount . clear ();
2015-01-12 17:43:42 -05:00
}
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
{
_ui -> statsToolBox -> updateStat ( "GUI/Cache Data Size/MB" , _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), _cachedMemoryUsage / ( 1024 * 1024 ), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "GUI/Cache Clouds Size/MB" , _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), _createdCloudsMemoryUsage / ( 1024 * 1024 ), _preferencesDialog -> isCacheSavedInFigures ());
2016-08-21 19:33:01 -04:00
#ifdef RTABMAP_OCTOMAP
2021-05-23 17:21:12 -04:00
_ui -> statsToolBox -> updateStat ( "GUI/Octomap Size/MB" , _preferencesDialog -> isTimeUsedInFigures () ? stat . stamp () - _firstStamp : stat . refImageId (), _octomap -> octree () -> memoryUsage () / ( 1024 * 1024 ), _preferencesDialog -> isCacheSavedInFigures ());
2016-08-21 19:33:01 -04:00
#endif
2021-05-23 17:21:12 -04:00
}
2015-10-16 20:11:24 -04:00
if ( _state != kMonitoring && _state != kDetecting )
{
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2015-10-16 20:11:24 -04:00
}
2015-03-20 15:54:33 -04:00
_processingStatistics = false ;
2017-10-25 09:56:05 +02:00
2018-06-15 12:17:11 -04:00
Q_EMIT ( statsProcessed ());
2011-06-05 14:45:39 +00:00
}
2014-07-29 21:48:37 +00:00
void MainWindow :: updateMapCloud (
const std :: map < int , Transform > & posesIn ,
2014-10-28 01:20:57 +00:00
const std :: multimap < int , Link > & constraints ,
2014-10-30 19:38:58 +00:00
const std :: map < int , int > & mapIdsIn ,
2015-07-26 15:50:39 -04:00
const std :: map < int , std :: string > & labels ,
2016-01-07 19:39:21 -05:00
const std :: map < int , Transform > & groundTruths , // ground truth should contain only valid transforms
2021-12-18 18:53:59 -05:00
const std :: map < int , Transform > & odomCachePoses ,
const std :: multimap < int , Link > & odomCacheConstraints ,
2017-04-06 12:27:12 -04:00
bool verboseProgress ,
std :: map < std :: string , float > * stats )
2013-12-11 00:12:44 +00:00
{
2017-04-06 12:27:12 -04:00
UTimer timer ;
2018-12-07 18:29:41 -05:00
std :: map < int , Transform > nodePoses ( posesIn . lower_bound ( 0 ), posesIn . end ());
UDEBUG ( "nodes=%d landmarks=%d constraints=%d mapIdsIn=%d labelsIn=%d" ,
( int ) nodePoses . size (), ( int )( posesIn . size () - nodePoses . size ()), ( int ) constraints . size (), ( int ) mapIdsIn . size (), ( int ) labels . size ());
2014-02-13 21:32:46 +00:00
if ( posesIn . size ())
2013-12-11 00:12:44 +00:00
{
2014-02-13 21:32:46 +00:00
_currentPosesMap = posesIn ;
2018-12-07 18:29:41 -05:00
_currentPosesMap . erase ( 0 ); // don't keep 0 if it is there
2014-10-28 19:57:21 +00:00
_currentLinksMap = constraints ;
2014-10-30 19:38:58 +00:00
_currentMapIds = mapIdsIn ;
2016-01-07 19:39:21 -05:00
_currentLabels = labels ;
_currentGTPosesMap = groundTruths ;
2018-12-07 18:29:41 -05:00
_currentGTPosesMap . erase ( 0 );
2015-02-18 18:48:48 -05:00
if ( _state != kMonitoring && _state != kDetecting )
{
2018-12-07 18:29:41 -05:00
_ui -> actionPost_processing -> setEnabled ( _cachedSignatures . size () >= 2 && nodePoses . size () >= 2 && _currentLinksMap . size () >= 1 );
2015-10-16 20:11:24 -04:00
_ui -> menuExport_poses -> setEnabled ( ! _currentPosesMap . empty ());
2015-02-18 18:48:48 -05:00
}
2016-01-07 19:39:21 -05:00
_ui -> actionAnchor_clouds_to_ground_truth -> setEnabled ( ! _currentGTPosesMap . empty ());
2013-12-11 00:12:44 +00:00
}
2014-02-13 21:32:46 +00:00
// filter duplicated poses
std :: map < int , Transform > poses ;
2014-10-30 19:38:58 +00:00
std :: map < int , int > mapIds ;
2018-12-07 18:29:41 -05:00
if ( _preferencesDialog -> isCloudFiltering () && nodePoses . size ())
2014-02-13 21:32:46 +00:00
{
2014-07-03 20:16:39 +00:00
float radius = _preferencesDialog -> getCloudFilteringRadius ();
float angle = _preferencesDialog -> getCloudFilteringAngle () * CV_PI / 180.0 ; // convert to rad
2018-12-07 18:29:41 -05:00
bool hasZero = nodePoses . find ( 0 ) != nodePoses . end ();
if ( hasZero )
2017-10-29 13:23:38 -04:00
{
2018-12-07 18:29:41 -05:00
std :: map < int , Transform > posesInTmp = nodePoses ;
posesInTmp . erase ( 0 );
2018-05-08 19:56:13 -04:00
poses = rtabmap :: graph :: radiusPosesFiltering ( posesInTmp , radius , angle );
2017-10-29 13:23:38 -04:00
}
else
{
2018-12-07 18:29:41 -05:00
poses = rtabmap :: graph :: radiusPosesFiltering ( nodePoses , radius , angle );
2017-10-29 13:23:38 -04:00
}
2014-10-30 19:38:58 +00:00
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
std :: map < int , int >:: const_iterator jter = mapIdsIn . find ( iter -> first );
if ( jter != mapIdsIn . end ())
{
mapIds . insert ( * jter );
}
2015-07-23 17:03:48 -04:00
}
2018-12-07 18:29:41 -05:00
//keep 0
if ( hasZero )
2017-10-29 13:23:38 -04:00
{
2018-12-07 18:29:41 -05:00
poses . insert ( * nodePoses . find ( 0 ));
2017-10-29 13:23:38 -04:00
}
2014-10-30 19:38:58 +00:00
2015-07-23 17:03:48 -04:00
if ( verboseProgress )
{
2018-12-07 18:29:41 -05:00
_progressDialog -> appendText ( tr ( "Map update: %1 nodes shown of %2 (cloud filtering is on)" ). arg ( poses . size ()). arg ( nodePoses . size ()));
2015-07-23 17:03:48 -04:00
QApplication :: processEvents ();
2014-10-30 19:38:58 +00:00
}
2025-11-16 18:56:55 -08:00
UDEBUG ( "Filtered poses" );
2014-02-13 21:32:46 +00:00
}
else
{
2018-12-07 18:29:41 -05:00
poses = nodePoses ;
2014-10-30 19:38:58 +00:00
mapIds = mapIdsIn ;
2014-02-13 21:32:46 +00:00
}
2014-10-30 19:38:58 +00:00
2025-11-16 18:56:55 -08:00
if ( _ui -> widget_mapVisibility -> isVisible ())
2014-06-23 20:43:08 +00:00
{
2025-11-16 18:56:55 -08:00
std :: map < int , bool > posesMask ;
for ( std :: map < int , Transform >:: const_iterator iter = nodePoses . begin (); iter != nodePoses . end (); ++ iter )
{
posesMask . insert ( posesMask . end (), std :: make_pair ( iter -> first , poses . find ( iter -> first ) != poses . end ()));
}
_ui -> widget_mapVisibility -> setMap ( nodePoses , posesMask );
UDEBUG ( "Updated map visibility with %ld poses" , nodePoses . size ());
2014-06-23 20:43:08 +00:00
}
2025-12-01 19:42:27 -08:00
else {
_ui -> widget_mapVisibility -> clear ();
}
2014-02-13 21:32:46 +00:00
2017-04-06 12:27:12 -04:00
if ( groundTruths . size () && _ui -> actionAnchor_clouds_to_ground_truth -> isChecked ())
2016-01-07 15:48:18 -05:00
{
2025-11-16 18:56:55 -08:00
int anchored = 0 ;
2016-01-07 15:48:18 -05:00
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
2017-04-06 12:27:12 -04:00
std :: map < int , Transform >:: const_iterator gtIter = groundTruths . find ( iter -> first );
if ( gtIter != groundTruths . end ())
2016-01-07 15:48:18 -05:00
{
iter -> second = gtIter -> second ;
2025-11-16 18:56:55 -08:00
++ anchored ;
2016-01-07 15:48:18 -05:00
}
}
2025-11-16 18:56:55 -08:00
UDEBUG ( "Anchored %d/%ld poses to ground truth" , anchored , poses . size ());
2016-01-07 15:48:18 -05:00
}
else if ( _currentGTPosesMap . size () == 0 )
{
_ui -> actionAnchor_clouds_to_ground_truth -> setChecked ( false );
}
2018-02-01 22:17:46 -05:00
int maxNodes = uStr2Int ( _preferencesDialog -> getParameter ( Parameters :: kGridGlobalMaxNodes ()));
2021-02-21 13:00:16 -05:00
int altitudeDelta = uStr2Int ( _preferencesDialog -> getParameter ( Parameters :: kGridGlobalAltitudeDelta ()));
if (( maxNodes > 0 || altitudeDelta > 0.0 ) && poses . size () > 1 )
2018-02-01 22:17:46 -05:00
{
2021-02-21 13:00:16 -05:00
Transform currentPose = poses . rbegin () -> second ;
if ( poses . find ( 0 ) != poses . end ())
2018-02-01 22:17:46 -05:00
{
2021-02-21 13:00:16 -05:00
currentPose = poses . at ( 0 );
}
std :: map < int , Transform > nearestPoses ;
if ( maxNodes > 0 )
{
2022-01-20 18:56:38 -05:00
std :: map < int , float > nodes = graph :: findNearestNodes ( currentPose , poses , 0 , 0 , maxNodes );
2021-02-21 13:00:16 -05:00
for ( std :: map < int , float >:: iterator iter = nodes . begin (); iter != nodes . end (); ++ iter )
2018-02-01 22:17:46 -05:00
{
2021-02-21 13:00:16 -05:00
if ( altitudeDelta <= 0.0 ||
fabs ( poses . at ( iter -> first ). z () - currentPose . z ()) < altitudeDelta )
{
nearestPoses . insert ( * poses . find ( iter -> first ));
}
2018-02-01 22:17:46 -05:00
}
}
2021-02-21 13:00:16 -05:00
else // altitudeDelta>0.0
{
for ( std :: map < int , Transform >:: const_iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
if ( fabs ( iter -> second . z () - currentPose . z ()) < altitudeDelta )
{
nearestPoses . insert ( * iter );
}
}
}
2018-12-07 18:29:41 -05:00
//add zero...
if ( poses . find ( 0 ) != poses . end ())
2018-02-01 22:17:46 -05:00
{
2018-12-07 18:29:41 -05:00
nearestPoses . insert ( * poses . find ( 0 ));
2018-02-01 22:17:46 -05:00
}
poses = nearestPoses ;
}
2013-12-11 00:12:44 +00:00
// Map updated! regenerate the assembled cloud, last pose is the new one
2016-05-17 16:32:21 -04:00
UDEBUG ( "Update map with %d locations" , poses . size ());
2016-05-05 18:25:26 -04:00
QMap < std :: string , Transform > viewerClouds = _cloudViewer -> getAddedClouds ();
2020-01-22 10:34:21 -05:00
std :: set < std :: string > viewerLines = _cloudViewer -> getAddedLines ();
2014-10-28 01:20:57 +00:00
int i = 1 ;
2013-12-11 00:12:44 +00:00
for ( std :: map < int , Transform >:: const_iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
if ( ! iter -> second . isNull ())
{
std :: string cloudName = uFormat ( "cloud%d" , iter -> first );
2018-12-07 18:29:41 -05:00
if ( iter -> first == 0 )
2017-04-06 12:27:12 -04:00
{
viewerClouds . remove ( cloudName );
_cloudViewer -> removeCloud ( cloudName );
}
2013-12-11 00:12:44 +00:00
// 3d point cloud
2016-07-08 12:47:23 -04:00
bool update3dCloud = _cloudViewer -> isVisible () && _preferencesDialog -> isCloudsShown ( 0 );
2016-08-21 19:33:01 -04:00
if ( update3dCloud )
2013-12-11 00:12:44 +00:00
{
2016-07-08 12:47:23 -04:00
// update cloud
2013-12-11 00:12:44 +00:00
if ( viewerClouds . contains ( cloudName ))
{
// Update only if the pose has changed
Transform tCloud ;
2016-05-05 18:25:26 -04:00
_cloudViewer -> getPose ( cloudName , tCloud );
2013-12-11 00:12:44 +00:00
if ( tCloud . isNull () || iter -> second != tCloud )
{
2016-05-05 18:25:26 -04:00
if ( ! _cloudViewer -> updateCloudPose ( cloudName , iter -> second ))
2013-12-11 00:12:44 +00:00
{
UERROR ( "Updating pose cloud %d failed!" , iter -> first );
}
}
2016-05-26 12:38:18 -04:00
_cloudViewer -> setCloudVisibility ( cloudName , ( _cloudViewer -> isVisible () && _preferencesDialog -> isCloudsShown ( 0 )));
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( cloudName , _preferencesDialog -> getCloudColorScheme ( 0 ));
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudOpacity ( cloudName , _preferencesDialog -> getCloudOpacity ( 0 ));
_cloudViewer -> setCloudPointSize ( cloudName , _preferencesDialog -> getCloudPointSize ( 0 ));
2013-12-11 00:12:44 +00:00
}
2017-02-09 18:32:44 -05:00
else if ( _cachedEmptyClouds . find ( iter -> first ) == _cachedEmptyClouds . end () &&
_cachedClouds . find ( iter -> first ) == _cachedClouds . end () &&
_cachedSignatures . contains ( iter -> first ))
2013-12-11 00:12:44 +00:00
{
2017-02-09 18:32:44 -05:00
std :: pair < pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr , pcl :: IndicesPtr > createdCloud = this -> createAndAddCloudToMap ( iter -> first , iter -> second , uValue ( mapIds , iter -> first , - 1 ));
2016-07-15 17:46:13 -04:00
if ( _cloudViewer -> getAddedClouds (). contains ( cloudName ))
2016-06-02 17:52:22 -04:00
{
2016-07-08 12:47:23 -04:00
_cloudViewer -> setCloudVisibility ( cloudName . c_str (), _cloudViewer -> isVisible () && _preferencesDialog -> isCloudsShown ( 0 ));
}
}
2013-12-11 00:12:44 +00:00
}
else if ( viewerClouds . contains ( cloudName ))
{
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( cloudName . c_str (), false );
2013-12-11 00:12:44 +00:00
}
// 2d point cloud
std :: string scanName = uFormat ( "scan%d" , iter -> first );
2018-12-07 18:29:41 -05:00
if ( iter -> first == 0 )
2017-04-06 12:27:12 -04:00
{
viewerClouds . remove ( scanName );
_cloudViewer -> removeCloud ( scanName );
}
2016-08-21 19:33:01 -04:00
if ( _cloudViewer -> isVisible () && _preferencesDialog -> isScansShown ( 0 ))
2013-12-11 00:12:44 +00:00
{
if ( viewerClouds . contains ( scanName ))
{
// Update only if the pose has changed
Transform tScan ;
2016-05-05 18:25:26 -04:00
_cloudViewer -> getPose ( scanName , tScan );
2013-12-11 00:12:44 +00:00
if ( tScan . isNull () || iter -> second != tScan )
{
2016-05-05 18:25:26 -04:00
if ( ! _cloudViewer -> updateCloudPose ( scanName , iter -> second ))
2013-12-11 00:12:44 +00:00
{
UERROR ( "Updating pose scan %d failed!" , iter -> first );
}
}
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( scanName , _preferencesDialog -> isScansShown ( 0 ));
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( scanName , _preferencesDialog -> getScanColorScheme ( 0 ));
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudOpacity ( scanName , _preferencesDialog -> getScanOpacity ( 0 ));
_cloudViewer -> setCloudPointSize ( scanName , _preferencesDialog -> getScanPointSize ( 0 ));
2013-12-11 00:12:44 +00:00
}
2014-10-26 21:47:29 +00:00
else if ( _cachedSignatures . contains ( iter -> first ))
2013-12-11 00:12:44 +00:00
{
2014-10-26 21:47:29 +00:00
QMap < int , Signature >:: iterator jter = _cachedSignatures . find ( iter -> first );
2018-02-16 19:20:54 -05:00
if ( ! jter -> sensorData (). laserScanCompressed (). isEmpty () || ! jter -> sensorData (). laserScanRaw (). isEmpty ())
2014-10-26 21:47:29 +00:00
{
2014-10-30 19:38:58 +00:00
this -> createAndAddScanToMap ( iter -> first , iter -> second , uValue ( mapIds , iter -> first , - 1 ));
2014-10-26 21:47:29 +00:00
}
2013-12-11 00:12:44 +00:00
}
}
else if ( viewerClouds . contains ( scanName ))
{
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( scanName . c_str (), false );
2013-12-11 00:12:44 +00:00
}
2014-10-28 01:20:57 +00:00
2016-08-21 19:33:01 -04:00
// occupancy grids
bool updateGridMap =
(( _ui -> graphicsView_graphView -> isVisible () && _ui -> graphicsView_graphView -> isGridMapVisible ()) ||
( _cloudViewer -> isVisible () && _preferencesDialog -> getGridMapShown ())) &&
2017-04-07 18:31:11 -04:00
_occupancyGrid -> addedNodes (). find ( iter -> first ) == _occupancyGrid -> addedNodes (). end ();
2016-08-21 19:33:01 -04:00
bool updateOctomap = false ;
2023-12-17 22:44:11 -08:00
bool updateElevationMap = false ;
2016-08-21 19:33:01 -04:00
#ifdef RTABMAP_OCTOMAP
updateOctomap =
_cloudViewer -> isVisible () &&
_preferencesDialog -> isOctomapUpdated () &&
_octomap -> addedNodes (). find ( iter -> first ) == _octomap -> addedNodes (). end ();
#endif
2023-12-17 22:44:11 -08:00
#ifdef RTABMAP_GRIDMAP
updateElevationMap =
_cloudViewer -> isVisible () &&
_preferencesDialog -> getElevationMapShown () > 0 &&
_elevationMap -> addedNodes (). find ( iter -> first ) == _elevationMap -> addedNodes (). end ();
#endif
if ( updateGridMap || updateOctomap || updateElevationMap )
2016-08-21 19:33:01 -04:00
{
QMap < int , Signature >:: iterator jter = _cachedSignatures . find ( iter -> first );
2017-04-07 18:31:11 -04:00
if ( jter != _cachedSignatures . end () && jter -> sensorData (). gridCellSize () > 0.0f )
2016-08-21 19:33:01 -04:00
{
2017-04-07 18:31:11 -04:00
cv :: Mat ground ;
cv :: Mat obstacles ;
2018-02-08 21:40:17 -05:00
cv :: Mat empty ;
2017-04-07 18:31:11 -04:00
2025-11-16 18:56:55 -08:00
UTimer decompressionTime ;
2018-02-08 21:40:17 -05:00
jter -> sensorData (). uncompressDataConst ( 0 , 0 , 0 , 0 , & ground , & obstacles , & empty );
2025-11-16 18:56:55 -08:00
UDEBUG ( "Uncompressed local occupancy grid of node %d (%f s)" , jter -> id (), decompressionTime . ticks ());
2017-04-07 18:31:11 -04:00
2024-04-12 17:12:23 -07:00
double resolution = jter -> sensorData (). gridCellSize ();
if ( _preferencesDialog -> getGridUIResolution () > jter -> sensorData (). gridCellSize ())
{
resolution = _preferencesDialog -> getGridUIResolution ();
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr groundCloud = util3d :: voxelize ( util3d :: laserScanToPointCloud ( LaserScan :: backwardCompatibility ( ground )), resolution );
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr obstaclesCloud = util3d :: voxelize ( util3d :: laserScanToPointCloud ( LaserScan :: backwardCompatibility ( obstacles )), resolution );
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr emptyCloud = util3d :: voxelize ( util3d :: laserScanToPointCloud ( LaserScan :: backwardCompatibility ( empty )), resolution );
if ( ground . channels () == 2 )
ground = util3d :: laserScan2dFromPointCloud ( * groundCloud ). data ();
else
ground = util3d :: laserScanFromPointCloud ( * groundCloud ). data ();
if ( obstacles . channels () == 2 )
obstacles = util3d :: laserScan2dFromPointCloud ( * obstaclesCloud ). data ();
else
obstacles = util3d :: laserScanFromPointCloud ( * obstaclesCloud ). data ();
if ( empty . channels () == 2 )
empty = util3d :: laserScan2dFromPointCloud ( * emptyCloud ). data ();
else
empty = util3d :: laserScanFromPointCloud ( * emptyCloud ). data ();
}
_cachedLocalMaps . add ( iter -> first , ground , obstacles , empty , resolution , jter -> sensorData (). gridViewPoint ());
2016-08-21 19:33:01 -04:00
}
}
2016-03-01 17:33:27 -05:00
// 3d features
std :: string featuresName = uFormat ( "features%d" , iter -> first );
2018-12-07 18:29:41 -05:00
if ( iter -> first == 0 )
2017-04-06 12:27:12 -04:00
{
viewerClouds . remove ( featuresName );
_cloudViewer -> removeCloud ( featuresName );
}
2016-05-05 18:25:26 -04:00
if ( _cloudViewer -> isVisible () && _preferencesDialog -> isFeaturesShown ( 0 ))
2016-03-01 17:33:27 -05:00
{
if ( viewerClouds . contains ( featuresName ))
{
// Update only if the pose has changed
Transform tFeatures ;
2016-05-05 18:25:26 -04:00
_cloudViewer -> getPose ( featuresName , tFeatures );
2016-03-01 17:33:27 -05:00
if ( tFeatures . isNull () || iter -> second != tFeatures )
{
2016-05-05 18:25:26 -04:00
if ( ! _cloudViewer -> updateCloudPose ( featuresName , iter -> second ))
2016-03-01 17:33:27 -05:00
{
UERROR ( "Updating pose features %d failed!" , iter -> first );
}
}
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( featuresName , _preferencesDialog -> isFeaturesShown ( 0 ));
_cloudViewer -> setCloudPointSize ( featuresName , _preferencesDialog -> getFeaturesPointSize ( 0 ));
2016-03-01 17:33:27 -05:00
}
else if ( _cachedSignatures . contains ( iter -> first ))
{
QMap < int , Signature >:: iterator jter = _cachedSignatures . find ( iter -> first );
if ( ! jter -> getWords3 (). empty ())
{
this -> createAndAddFeaturesToMap ( iter -> first , iter -> second , uValue ( mapIds , iter -> first , - 1 ));
}
}
}
else if ( viewerClouds . contains ( featuresName ))
{
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( featuresName . c_str (), false );
2016-03-01 17:33:27 -05:00
}
2020-01-22 10:34:21 -05:00
// Gravity arrows
std :: string gravityName = uFormat ( "gravity%d" , iter -> first );
if ( iter -> first == 0 )
{
viewerLines . erase ( gravityName );
_cloudViewer -> removeLine ( gravityName );
}
if ( _cloudViewer -> isVisible () && _preferencesDialog -> isIMUGravityShown ( 0 ))
{
std :: multimap < int , Link >:: const_iterator linkIter = graph :: findLink ( constraints , iter -> first , iter -> first , false , Link :: kGravity );
if ( linkIter != constraints . end ())
{
Transform gravityT = linkIter -> second . transform ();
Eigen :: Vector3f gravity ( 0 , 0 , - _preferencesDialog -> getIMUGravityLength ( 0 ));
2024-12-14 16:33:29 -08:00
gravity = gravityT . inverse (). toEigen3f () * gravity ;
_cloudViewer -> addOrUpdateLine ( gravityName , iter -> second , iter -> second * Transform ( gravity [ 0 ], gravity [ 1 ], gravity [ 2 ], 0 , 0 , 0 ), Qt :: yellow , false , false );
2020-01-22 10:34:21 -05:00
}
}
else if ( viewerLines . find ( gravityName ) != viewerLines . end ())
{
_cloudViewer -> removeLine ( gravityName . c_str ());
}
2014-10-28 01:20:57 +00:00
if ( verboseProgress )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Updated cloud %1 (%2/%3)" ). arg ( iter -> first ). arg ( i ). arg ( poses . size ()));
_progressDialog -> incrementStep ();
2015-05-22 15:06:05 -04:00
if ( poses . size () < 200 || i % 100 == 0 )
{
QApplication :: processEvents ();
2016-09-19 14:01:06 -04:00
if ( _progressCanceled )
{
break ;
}
2015-05-22 15:06:05 -04:00
}
2014-10-28 01:20:57 +00:00
}
2013-12-11 00:12:44 +00:00
}
2014-10-28 01:20:57 +00:00
++ i ;
2013-12-11 00:12:44 +00:00
}
2014-10-30 19:38:58 +00:00
2013-12-11 00:12:44 +00:00
//remove not used clouds
for ( QMap < std :: string , Transform >:: iterator iter = viewerClouds . begin (); iter != viewerClouds . end (); ++ iter )
{
std :: list < std :: string > splitted = uSplitNumChar ( iter . key ());
2017-04-10 16:59:48 -04:00
int id = 0 ;
2013-12-11 00:12:44 +00:00
if ( splitted . size () == 2 )
{
2017-04-10 16:59:48 -04:00
id = std :: atoi ( splitted . back (). c_str ());
2019-01-09 13:50:12 -05:00
if ( poses . find ( id ) == poses . end ())
2013-12-11 00:12:44 +00:00
{
2019-01-09 13:50:12 -05:00
if ( _cloudViewer -> getCloudVisibility ( iter . key ()))
{
UDEBUG ( "Hide %s" , iter . key (). c_str ());
_cloudViewer -> setCloudVisibility ( iter . key (), false );
}
2013-12-11 00:12:44 +00:00
}
}
}
2020-01-22 10:34:21 -05:00
// remove not used gravity lines
for ( std :: set < std :: string >:: iterator iter = viewerLines . begin (); iter != viewerLines . end (); ++ iter )
{
std :: list < std :: string > splitted = uSplitNumChar ( * iter );
int id = 0 ;
if ( splitted . size () == 2 )
{
id = std :: atoi ( splitted . back (). c_str ());
if ( poses . find ( id ) == poses . end ())
{
UDEBUG ( "Remove %s" , iter -> c_str ());
_cloudViewer -> removeLine ( * iter );
}
}
}
2013-12-11 00:12:44 +00:00
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2017-04-06 12:27:12 -04:00
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/RGB-D cloud/ms" , ( float ) timer . restart () * 1000.0f ));
}
2016-02-15 19:35:24 -05:00
2014-10-30 19:38:58 +00:00
// update 3D graphes (show all poses)
2016-05-05 18:25:26 -04:00
_cloudViewer -> removeAllGraphs ();
_cloudViewer -> removeCloud ( "graph_nodes" );
2017-11-30 16:52:03 -05:00
if ( ! _preferencesDialog -> isFrustumsShown ( 0 ))
2014-10-30 19:38:58 +00:00
{
2017-11-30 16:52:03 -05:00
QMap < std :: string , Transform > addedFrustums = _cloudViewer -> getAddedFrustums ();
for ( QMap < std :: string , Transform >:: iterator iter = addedFrustums . begin (); iter != addedFrustums . end (); ++ iter )
{
std :: list < std :: string > splitted = uSplitNumChar ( iter . key ());
if ( splitted . size () == 2 )
{
if (( splitted . front (). compare ( "f_" ) == 0 || splitted . front (). compare ( "f_gt_" ) == 0 ))
{
_cloudViewer -> removeFrustum ( iter . key ());
}
}
}
2017-02-09 18:32:44 -05:00
}
2018-02-08 21:40:17 -05:00
Transform mapToGt = Transform :: getIdentity ();
if ( _preferencesDialog -> isGroundTruthAligned () && _currentGTPosesMap . size ())
{
mapToGt = alignPosesToGroundTruth ( _currentPosesMap , _currentGTPosesMap ). inverse ();
2025-11-16 18:56:55 -08:00
UDEBUG ( "Aligned poses to ground truth (%ld poses %ld gt poses)" , _currentPosesMap . size (), _currentGTPosesMap . size ());
2018-02-08 21:40:17 -05:00
}
2021-12-18 18:53:59 -05:00
std :: map < int , Transform > posesWithOdomCache ;
2026-04-19 16:08:03 -07:00
std :: set < int > odomCachePosesIds ;
2021-12-18 18:53:59 -05:00
if ( _ui -> graphicsView_graphView -> isVisible () ||
(( _preferencesDialog -> isGraphsShown () || _preferencesDialog -> isFrustumsShown ( 0 )) && _currentPosesMap . size ()))
{
posesWithOdomCache = posesIn ;
for ( std :: map < int , Transform >:: const_iterator iter = odomCachePoses . begin (); iter != odomCachePoses . end (); ++ iter )
{
2026-04-19 16:08:03 -07:00
if ( posesWithOdomCache . insert ( std :: make_pair ( iter -> first , _odometryCorrection * iter -> second )). second )
{
odomCachePosesIds . insert ( iter -> first );
}
2021-12-18 18:53:59 -05:00
}
}
2025-11-16 18:56:55 -08:00
if ( _cloudViewer -> isVisible () &&
( _preferencesDialog -> isGraphsShown () || _preferencesDialog -> isFrustumsShown ( 0 )) &&
_currentPosesMap . size ())
2017-02-09 18:32:44 -05:00
{
UTimer timerGraph ;
2014-10-30 19:38:58 +00:00
// Find all graphs
std :: map < int , pcl :: PointCloud < pcl :: PointXYZ >:: Ptr > graphs ;
2021-12-18 18:53:59 -05:00
for ( std :: map < int , Transform >:: iterator iter = posesWithOdomCache . lower_bound ( 1 ); iter != posesWithOdomCache . end (); ++ iter )
2014-10-30 19:38:58 +00:00
{
int mapId = uValue ( _currentMapIds , iter -> first , - 1 );
2015-05-07 15:32:56 -04:00
2017-02-09 18:32:44 -05:00
if ( _preferencesDialog -> isGraphsShown ())
2014-10-30 19:38:58 +00:00
{
2017-02-09 18:32:44 -05:00
//edges
std :: map < int , pcl :: PointCloud < pcl :: PointXYZ >:: Ptr >:: iterator kter = graphs . find ( mapId );
if ( kter == graphs . end ())
{
kter = graphs . insert ( std :: make_pair ( mapId , pcl :: PointCloud < pcl :: PointXYZ >:: Ptr ( new pcl :: PointCloud < pcl :: PointXYZ > ))). first ;
}
pcl :: PointXYZ pt ( iter -> second . x (), iter -> second . y (), iter -> second . z ());
kter -> second -> push_back ( pt );
}
// get local transforms for frustums on the graph
2017-11-30 16:52:03 -05:00
if ( _preferencesDialog -> isFrustumsShown ( 0 ))
2017-02-09 18:32:44 -05:00
{
2017-02-15 19:07:10 -05:00
std :: string frustumId = uFormat ( "f_%d" , iter -> first );
2017-02-09 18:32:44 -05:00
if ( _cloudViewer -> getAddedFrustums (). contains ( frustumId ))
{
_cloudViewer -> updateFrustumPose ( frustumId , iter -> second );
}
else if ( _cachedSignatures . contains ( iter -> first ))
{
const Signature & s = _cachedSignatures . value ( iter -> first );
// Supporting only one frustum per node
2022-07-20 15:20:14 -04:00
if ( s . sensorData (). cameraModels (). size () == 1 || s . sensorData (). stereoCameraModels (). size () == 1 )
2017-02-09 18:32:44 -05:00
{
2022-07-20 15:20:14 -04:00
const CameraModel & model = s . sensorData (). stereoCameraModels (). size () ? s . sensorData (). stereoCameraModels ()[ 0 ]. left () : s . sensorData (). cameraModels ()[ 0 ];
2020-05-24 11:13:13 -04:00
Transform t = model . localTransform ();
2017-02-09 18:32:44 -05:00
if ( ! t . isNull ())
{
QColor color = ( Qt :: GlobalColor )(( mapId + 3 ) % 12 + 7 );
2020-05-24 11:13:13 -04:00
_cloudViewer -> addOrUpdateFrustum ( frustumId , iter -> second , t , _cloudViewer -> getFrustumScale (), color , model . fovX (), model . fovY ());
2017-02-15 19:07:10 -05:00
if ( _currentGTPosesMap . find ( iter -> first ) != _currentGTPosesMap . end ())
{
std :: string gtFrustumId = uFormat ( "f_gt_%d" , iter -> first );
color = Qt :: gray ;
2026-03-28 14:33:26 -07:00
_cloudViewer -> addOrUpdateFrustum ( gtFrustumId , mapToGt * _currentGTPosesMap . at ( iter -> first ), t , _cloudViewer -> getFrustumScale (), color , model . fovX (), model . fovY ());
2017-02-15 19:07:10 -05:00
}
2017-02-09 18:32:44 -05:00
}
}
}
2014-10-30 19:38:58 +00:00
}
}
2015-12-22 19:32:52 -05:00
//Ground truth graph?
for ( std :: map < int , Transform >:: iterator iter = _currentGTPosesMap . begin (); iter != _currentGTPosesMap . end (); ++ iter )
{
int mapId = - 100 ;
2017-02-09 18:32:44 -05:00
if ( _preferencesDialog -> isGraphsShown ())
2015-12-22 19:32:52 -05:00
{
2017-02-09 18:32:44 -05:00
//edges
std :: map < int , pcl :: PointCloud < pcl :: PointXYZ >:: Ptr >:: iterator kter = graphs . find ( mapId );
if ( kter == graphs . end ())
{
kter = graphs . insert ( std :: make_pair ( mapId , pcl :: PointCloud < pcl :: PointXYZ >:: Ptr ( new pcl :: PointCloud < pcl :: PointXYZ > ))). first ;
}
2018-02-08 21:40:17 -05:00
Transform t = mapToGt * iter -> second ;
pcl :: PointXYZ pt ( t . x (), t . y (), t . z ());
2017-02-09 18:32:44 -05:00
kter -> second -> push_back ( pt );
}
2015-12-22 19:32:52 -05:00
}
2014-10-30 19:38:58 +00:00
// add graphs
for ( std :: map < int , pcl :: PointCloud < pcl :: PointXYZ >:: Ptr >:: iterator iter = graphs . begin (); iter != graphs . end (); ++ iter )
{
QColor color = Qt :: gray ;
if ( iter -> first >= 0 )
{
2015-05-07 15:32:56 -04:00
color = ( Qt :: GlobalColor )(( iter -> first + 3 ) % 12 + 7 );
2014-10-30 19:38:58 +00:00
}
2016-05-05 18:25:26 -04:00
_cloudViewer -> addOrUpdateGraph ( uFormat ( "graph_%d" , iter -> first ), iter -> second , color );
2014-10-30 19:38:58 +00:00
}
2017-02-09 18:32:44 -05:00
2017-11-30 16:52:03 -05:00
if ( _preferencesDialog -> isFrustumsShown ( 0 ))
2017-02-15 19:07:10 -05:00
{
QMap < std :: string , Transform > addedFrustums = _cloudViewer -> getAddedFrustums ();
UDEBUG ( "remove not used frustums" );
for ( QMap < std :: string , Transform >:: iterator iter = addedFrustums . begin (); iter != addedFrustums . end (); ++ iter )
{
std :: list < std :: string > splitted = uSplitNumChar ( iter . key ());
if ( splitted . size () == 2 )
{
int id = std :: atoi ( splitted . back (). c_str ());
if (( splitted . front (). compare ( "f_" ) == 0 || splitted . front (). compare ( "f_gt_" ) == 0 ) &&
2021-12-18 18:53:59 -05:00
posesWithOdomCache . find ( id ) == posesWithOdomCache . end ())
2017-02-15 19:07:10 -05:00
{
_cloudViewer -> removeFrustum ( iter . key ());
}
}
}
}
2025-11-16 18:56:55 -08:00
UDEBUG ( "timerGraph (CloudViewer)=%fs" , timerGraph . ticks ());
2014-10-30 19:38:58 +00:00
}
2016-02-15 19:35:24 -05:00
UDEBUG ( "labels.size()=%d" , ( int ) labels . size ());
2015-07-26 15:50:39 -04:00
// Update labels
2016-05-05 18:25:26 -04:00
_cloudViewer -> removeAllTexts ();
2015-07-26 15:50:39 -04:00
if ( _preferencesDialog -> isLabelsShown () && labels . size ())
{
for ( std :: map < int , std :: string >:: const_iterator iter = labels . begin (); iter != labels . end (); ++ iter )
{
2018-12-07 18:29:41 -05:00
if ( nodePoses . find ( iter -> first ) != nodePoses . end ())
2015-07-26 15:50:39 -04:00
{
int mapId = uValue ( mapIdsIn , iter -> first , - 1 );
QColor color = Qt :: gray ;
if ( mapId >= 0 )
{
color = ( Qt :: GlobalColor )(( mapId + 3 ) % 12 + 7 );
}
2016-05-05 18:25:26 -04:00
_cloudViewer -> addOrUpdateText (
2015-07-26 15:50:39 -04:00
std :: string ( "label_" ) + uNumber2Str ( iter -> first ),
iter -> second ,
_currentPosesMap . at ( iter -> first ),
0.1 ,
color );
}
}
}
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2017-04-06 12:27:12 -04:00
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/Graph Update/ms" , ( float ) timer . restart () * 1000.0f ));
}
2016-02-15 19:35:24 -05:00
2017-03-08 08:33:43 -05:00
#ifdef RTABMAP_OCTOMAP
_cloudViewer -> removeOctomap ();
_cloudViewer -> removeCloud ( "octomap_cloud" );
if ( _preferencesDialog -> isOctomapUpdated ())
{
UDEBUG ( "" );
UTimer time ;
_octomap -> update ( poses );
UINFO ( "Octomap update time = %fs" , time . ticks ());
}
2017-04-06 12:27:12 -04:00
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/Octomap Update/ms" , ( float ) timer . restart () * 1000.0f ));
}
2017-03-08 08:33:43 -05:00
if ( _preferencesDialog -> isOctomapShown ())
{
UDEBUG ( "" );
UTimer time ;
2018-02-08 21:40:17 -05:00
if ( _preferencesDialog -> getOctomapRenderingType () > 0 )
2017-03-08 08:33:43 -05:00
{
2018-02-08 21:40:17 -05:00
_cloudViewer -> addOctomap ( _octomap , _preferencesDialog -> getOctomapTreeDepth (), _preferencesDialog -> getOctomapRenderingType () > 1 );
2017-03-08 08:33:43 -05:00
}
else
{
pcl :: IndicesPtr obstacles ( new std :: vector < int > );
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud = _octomap -> createCloud ( _preferencesDialog -> getOctomapTreeDepth (), obstacles . get ());
if ( obstacles -> size ())
{
_cloudViewer -> addCloud ( "octomap_cloud" , cloud );
2017-04-06 12:27:12 -04:00
_cloudViewer -> setCloudPointSize ( "octomap_cloud" , _preferencesDialog -> getOctomapPointSize ());
2017-03-08 08:33:43 -05:00
}
}
UINFO ( "Octomap show 3d map time = %fs" , time . ticks ());
}
UDEBUG ( "" );
2017-04-06 12:27:12 -04:00
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/Octomap Rendering/ms" , ( float ) timer . restart () * 1000.0f ));
}
2017-03-08 08:33:43 -05:00
#endif
2023-12-17 22:44:11 -08:00
#ifdef RTABMAP_GRIDMAP
_cloudViewer -> removeElevationMap ();
_cloudViewer -> removeCloud ( "elevation_mesh" );
if ( _preferencesDialog -> getElevationMapShown () > 0 )
{
UDEBUG ( "" );
UTimer time ;
_elevationMap -> update ( poses );
UINFO ( "Elevation map update time = %fs" , time . ticks ());
}
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/Elevation Update/ms" , ( float ) timer . restart () * 1000.0f ));
}
if ( _preferencesDialog -> getElevationMapShown () > 0 )
{
UDEBUG ( "" );
UTimer time ;
if ( _preferencesDialog -> getElevationMapShown () == 1 )
{
float xMin , yMin , cellSize ;
cv :: Mat map = _elevationMap -> createHeightMap ( xMin , yMin , cellSize );
if ( ! map . empty ())
{
_cloudViewer -> addElevationMap ( map , cellSize , xMin , yMin , 1 );
}
}
else // RGB elevation
{
pcl :: PolygonMesh :: Ptr mesh = _elevationMap -> createTerrainMesh ();
if ( mesh -> cloud . data . size ())
{
_cloudViewer -> addCloudMesh ( "elevation_mesh" , mesh );
}
}
UINFO ( "Show elevation map time = %fs" , time . ticks ());
}
UDEBUG ( "" );
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/Elevation Rendering/ms" , ( float ) timer . restart () * 1000.0f ));
}
#endif
2019-02-18 18:19:55 -05:00
// Add landmarks to 3D Map view
#if PCL_VERSION_COMPARE(>=, 1, 7, 2)
_cloudViewer -> removeAllCoordinates ( "landmark_" );
#endif
if ( _preferencesDialog -> isLandmarksShown ())
{
for ( std :: map < int , Transform >:: const_iterator iter = posesIn . begin (); iter != posesIn . end () && iter -> first < 0 ; ++ iter )
{
#if PCL_VERSION_COMPARE(>=, 1, 7, 2)
2019-04-26 14:03:25 -04:00
_cloudViewer -> addOrUpdateCoordinate ( uFormat ( "landmark_%d" , - iter -> first ), iter -> second , _preferencesDialog -> landmarkVisSize () > 0.0 ? _preferencesDialog -> landmarkVisSize () : _preferencesDialog -> getMarkerLength () <= 0 ? 0.1 : _preferencesDialog -> getMarkerLength () / 2.0 , false );
2019-02-18 18:19:55 -05:00
#endif
if ( _preferencesDialog -> isLabelsShown ())
{
std :: string num = uNumber2Str ( - iter -> first );
_cloudViewer -> addOrUpdateText (
std :: string ( "landmark_str_" ) + num ,
num ,
iter -> second ,
0.1 ,
Qt :: yellow );
}
}
}
2014-07-29 21:48:37 +00:00
// Update occupancy grid map in 3D map view and graph view
2015-01-07 17:30:20 -05:00
if ( _ui -> graphicsView_graphView -> isVisible ())
2014-07-29 21:48:37 +00:00
{
2021-12-18 18:53:59 -05:00
std :: multimap < int , Link > constraintsWithOdomCache ;
constraintsWithOdomCache = constraints ;
constraintsWithOdomCache . insert ( odomCacheConstraints . begin (), odomCacheConstraints . end ());
2026-04-19 16:08:03 -07:00
_ui -> graphicsView_graphView -> updateGraph ( posesWithOdomCache , constraintsWithOdomCache , mapIdsIn , std :: map < int , int > (), odomCachePosesIds );
2018-02-08 21:40:17 -05:00
if ( _preferencesDialog -> isGroundTruthAligned () && ! mapToGt . isIdentity ())
{
std :: map < int , Transform > gtPoses = _currentGTPosesMap ;
for ( std :: map < int , Transform >:: iterator iter = gtPoses . begin (); iter != gtPoses . end (); ++ iter )
{
iter -> second = mapToGt * iter -> second ;
}
_ui -> graphicsView_graphView -> updateGTGraph ( gtPoses );
}
else
{
_ui -> graphicsView_graphView -> updateGTGraph ( _currentGTPosesMap );
}
2014-07-29 21:48:37 +00:00
}
cv :: Mat map8U ;
2024-03-27 17:18:02 -07:00
if (( _ui -> graphicsView_graphView -> isVisible () && _ui -> graphicsView_graphView -> isGridMapVisible ()) ||
( _cloudViewer -> isVisible () && _preferencesDialog -> getGridMapShown ()))
2014-07-29 21:48:37 +00:00
{
float xMin , yMin ;
2018-02-08 21:40:17 -05:00
float resolution = _occupancyGrid -> getCellSize ();
2016-08-21 19:33:01 -04:00
cv :: Mat map8S ;
#ifdef RTABMAP_OCTOMAP
if ( _preferencesDialog -> isOctomap2dGrid ())
{
2017-04-07 18:31:11 -04:00
map8S = _octomap -> createProjectionMap ( xMin , yMin , resolution , 0 , _preferencesDialog -> getOctomapTreeDepth ());
2016-08-21 19:33:01 -04:00
}
else
#endif
{
2023-12-17 22:44:11 -08:00
if ( _occupancyGrid -> addedNodes (). size () || _cachedLocalMaps . size () > 0 )
2017-04-24 12:17:18 -04:00
{
_occupancyGrid -> update ( poses );
}
2017-04-07 18:31:11 -04:00
if ( stats )
2016-08-21 19:33:01 -04:00
{
2017-04-07 18:31:11 -04:00
stats -> insert ( std :: make_pair ( "GUI/Grid Update/ms" , ( float ) timer . restart () * 1000.0f ));
2016-08-21 19:33:01 -04:00
}
2017-04-07 18:31:11 -04:00
map8S = _occupancyGrid -> getMap ( xMin , yMin );
2016-08-21 19:33:01 -04:00
}
2014-07-29 21:48:37 +00:00
if ( ! map8S . empty ())
{
//convert to gray scaled map
map8U = util3d :: convertMap2Image8U ( map8S );
2024-03-27 17:18:02 -07:00
if ( _cloudViewer -> isVisible () && _preferencesDialog -> getGridMapShown ())
2014-07-29 21:48:37 +00:00
{
float opacity = _preferencesDialog -> getGridMapOpacity ();
2016-05-05 18:25:26 -04:00
_cloudViewer -> addOccupancyGridMap ( map8U , resolution , xMin , yMin , opacity );
2014-07-29 21:48:37 +00:00
}
2024-03-27 17:18:02 -07:00
if ( _ui -> graphicsView_graphView -> isVisible () && _ui -> graphicsView_graphView -> isGridMapVisible ())
2014-07-29 21:48:37 +00:00
{
_ui -> graphicsView_graphView -> updateMap ( map8U , resolution , xMin , yMin );
}
}
}
2015-02-04 17:40:16 -05:00
_ui -> graphicsView_graphView -> update ();
2023-12-17 22:44:11 -08:00
_cachedLocalMaps . clear ( true );
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2017-04-06 12:27:12 -04:00
if ( stats )
{
stats -> insert ( std :: make_pair ( "GUI/Grid Rendering/ms" , ( float ) timer . restart () * 1000.0f ));
}
2016-02-15 19:35:24 -05:00
2014-07-29 21:48:37 +00:00
if ( ! _preferencesDialog -> getGridMapShown ())
{
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> removeOccupancyGridMap ();
2014-07-29 21:48:37 +00:00
}
2013-12-11 00:12:44 +00:00
if ( viewerClouds . contains ( "cloudOdom" ))
{
if ( ! _preferencesDialog -> isCloudsShown ( 1 ))
{
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( "cloudOdom" , false );
2013-12-11 00:12:44 +00:00
}
else
{
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> updateCloudPose ( "cloudOdom" , _odometryCorrection );
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( "cloudOdom" , _preferencesDialog -> getCloudColorScheme ( 1 ));
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudOpacity ( "cloudOdom" , _preferencesDialog -> getCloudOpacity ( 1 ));
_cloudViewer -> setCloudPointSize ( "cloudOdom" , _preferencesDialog -> getCloudPointSize ( 1 ));
2013-12-11 00:12:44 +00:00
}
}
if ( viewerClouds . contains ( "scanOdom" ))
{
if ( ! _preferencesDialog -> isScansShown ( 1 ))
{
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( "scanOdom" , false );
2013-12-11 00:12:44 +00:00
}
else
{
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> updateCloudPose ( "scanOdom" , _odometryCorrection );
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( "scanOdom" , _preferencesDialog -> getScanColorScheme ( 1 ));
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudOpacity ( "scanOdom" , _preferencesDialog -> getScanOpacity ( 1 ));
_cloudViewer -> setCloudPointSize ( "scanOdom" , _preferencesDialog -> getScanPointSize ( 1 ));
2013-12-11 00:12:44 +00:00
}
}
2016-03-06 15:11:09 -05:00
if ( viewerClouds . contains ( "scanMapOdom" ))
{
if ( ! _preferencesDialog -> isScansShown ( 1 ))
{
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( "scanMapOdom" , false );
2016-03-06 15:11:09 -05:00
}
2016-11-27 14:30:11 -05:00
else if ( _cloudViewer -> getBackgroundColor () != Qt :: darkRed ) // not lost
2016-03-06 15:11:09 -05:00
{
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> updateCloudPose ( "scanMapOdom" , _odometryCorrection );
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( "scanMapOdom" , _preferencesDialog -> getScanColorScheme ( 1 ));
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudOpacity ( "scanMapOdom" , _preferencesDialog -> getScanOpacity ( 1 ));
_cloudViewer -> setCloudPointSize ( "scanMapOdom" , _preferencesDialog -> getScanPointSize ( 1 ));
2016-03-06 15:11:09 -05:00
}
}
2016-03-01 17:33:27 -05:00
if ( viewerClouds . contains ( "featuresOdom" ))
{
if ( ! _preferencesDialog -> isFeaturesShown ( 1 ))
{
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudVisibility ( "featuresOdom" , false );
2016-03-01 17:33:27 -05:00
}
2016-11-27 14:30:11 -05:00
else if ( _cloudViewer -> getBackgroundColor () != Qt :: darkRed ) // not lost
2016-03-01 17:33:27 -05:00
{
UDEBUG ( "" );
2016-05-05 18:25:26 -04:00
_cloudViewer -> updateCloudPose ( "featuresOdom" , _odometryCorrection );
_cloudViewer -> setCloudPointSize ( "featuresOdom" , _preferencesDialog -> getFeaturesPointSize ( 1 ));
2016-03-01 17:33:27 -05:00
}
}
2013-12-11 00:12:44 +00:00
2017-03-08 08:33:43 -05:00
// activate actions
if ( _state != kMonitoring && _state != kDetecting )
{
_ui -> actionSave_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
_ui -> actionView_high_res_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
2017-04-07 18:31:11 -04:00
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( ! _occupancyGrid -> addedNodes (). empty ());
2017-03-08 08:33:43 -05:00
#ifdef RTABMAP_OCTOMAP
_ui -> actionExport_octomap -> setEnabled ( _octomap -> octree () -> size ());
#else
_ui -> actionExport_octomap -> setEnabled ( false );
#endif
}
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2021-06-18 12:43:08 -04:00
_cloudViewer -> refreshView ();
2016-02-15 19:35:24 -05:00
UDEBUG ( "" );
2013-12-11 00:12:44 +00:00
}
2016-07-08 12:47:23 -04:00
std :: pair < pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr , pcl :: IndicesPtr > MainWindow :: createAndAddCloudToMap ( int nodeId , const Transform & pose , int mapId )
2014-06-23 20:43:08 +00:00
{
2015-01-07 17:30:20 -05:00
UASSERT ( ! pose . isNull ());
2014-06-23 20:43:08 +00:00
std :: string cloudName = uFormat ( "cloud%d" , nodeId );
2016-07-08 12:47:23 -04:00
std :: pair < pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr , pcl :: IndicesPtr > outputPair ;
2016-05-05 18:25:26 -04:00
if ( _cloudViewer -> getAddedClouds (). contains ( cloudName ))
2014-06-23 20:43:08 +00:00
{
UERROR ( "Cloud %d already added to map." , nodeId );
2016-07-08 12:47:23 -04:00
return outputPair ;
2014-06-23 20:43:08 +00:00
}
2014-10-26 21:47:29 +00:00
QMap < int , Signature >:: iterator iter = _cachedSignatures . find ( nodeId );
if ( iter == _cachedSignatures . end ())
{
UERROR ( "Node %d is not in the cache." , nodeId );
2016-07-08 12:47:23 -04:00
return outputPair ;
2014-10-26 21:47:29 +00:00
}
2016-06-12 17:53:35 -04:00
UASSERT ( _cachedClouds . find ( nodeId ) == _cachedClouds . end ());
2016-02-15 19:35:24 -05:00
2016-02-19 21:05:14 -05:00
if (( ! iter -> sensorData (). imageCompressed (). empty () || ! iter -> sensorData (). imageRaw (). empty ()) &&
( ! iter -> sensorData (). depthOrRightCompressed (). empty () || ! iter -> sensorData (). depthOrRightRaw (). empty ()))
2014-10-30 19:38:58 +00:00
{
2015-04-23 22:01:41 -04:00
cv :: Mat image , depth ;
2015-05-29 14:46:48 -04:00
SensorData data = iter -> sensorData ();
data . uncompressData ( & image , & depth , 0 );
2018-09-24 14:36:54 -04:00
ParametersMap allParameters = _preferencesDialog -> getAllParameters ();
bool rectifyOnlyFeatures = Parameters :: defaultRtabmapRectifyOnlyFeatures ();
bool imagesAlreadyRectified = Parameters :: defaultRtabmapImagesAlreadyRectified ();
Parameters :: parse ( allParameters , Parameters :: kRtabmapRectifyOnlyFeatures (), rectifyOnlyFeatures );
Parameters :: parse ( allParameters , Parameters :: kRtabmapImagesAlreadyRectified (), imagesAlreadyRectified );
if ( rectifyOnlyFeatures && ! imagesAlreadyRectified )
{
if ( data . cameraModels (). size ())
{
UTimer time ;
// Note that only RGB image is rectified, the depth image is assumed to be already registered to rectified RGB camera.
UASSERT ( int (( data . imageRaw (). cols / data . cameraModels (). size ()) * data . cameraModels (). size ()) == data . imageRaw (). cols );
int subImageWidth = data . imageRaw (). cols / data . cameraModels (). size ();
cv :: Mat rectifiedImages = data . imageRaw (). clone ();
bool initRectMaps = _rectCameraModels . empty ();
if ( initRectMaps )
{
_rectCameraModels . resize ( data . cameraModels (). size ());
}
for ( unsigned int i = 0 ; i < data . cameraModels (). size (); ++ i )
{
if ( data . cameraModels ()[ i ]. isValidForRectification ())
{
if ( initRectMaps )
{
_rectCameraModels [ i ] = data . cameraModels ()[ i ];
if ( ! _rectCameraModels [ i ]. isRectificationMapInitialized ())
{
UWARN ( "Initializing rectification maps for camera %d (only done for the first image received)..." , i );
_rectCameraModels [ i ]. initRectificationMap ();
UWARN ( "Initializing rectification maps for camera %d (only done for the first image received)... done!" , i );
}
}
UASSERT ( _rectCameraModels [ i ]. imageWidth () == data . cameraModels ()[ i ]. imageWidth () &&
_rectCameraModels [ i ]. imageHeight () == data . cameraModels ()[ i ]. imageHeight ());
cv :: Mat rectifiedImage = _rectCameraModels [ i ]. rectifyImage ( cv :: Mat ( data . imageRaw (), cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data . imageRaw (). rows )));
rectifiedImage . copyTo ( cv :: Mat ( rectifiedImages , cv :: Rect ( subImageWidth * i , 0 , subImageWidth , data . imageRaw (). rows )));
}
else
{
UWARN ( "Camera %d of data %d is not valid for rectification (%dx%d)." ,
i , data . id (),
data . cameraModels ()[ i ]. imageWidth (),
data . cameraModels ()[ i ]. imageHeight ());
}
}
UINFO ( "Time rectification: %fs" , time . ticks ());
2019-04-09 20:05:24 -04:00
data . setRGBDImage ( rectifiedImages , data . depthOrRightRaw (), data . cameraModels ());
2018-09-24 14:36:54 -04:00
image = rectifiedImages ;
}
}
2014-10-28 01:20:57 +00:00
2016-06-12 13:51:49 -04:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ;
2016-02-15 19:35:24 -05:00
pcl :: IndicesPtr indices ( new std :: vector < int > );
2018-12-07 18:29:41 -05:00
UASSERT_MSG ( nodeId == 0 || nodeId == data . id (), uFormat ( "nodeId=%d data.id()=%d" , nodeId , data . id ()). c_str ());
2016-06-02 17:27:12 -04:00
2016-02-15 19:35:24 -05:00
// Create organized cloud
2016-06-12 13:51:49 -04:00
cloud = util3d :: cloudRGBFromSensorData ( data ,
2015-04-23 22:01:41 -04:00
_preferencesDialog -> getCloudDecimation ( 0 ),
2015-05-29 14:46:48 -04:00
_preferencesDialog -> getCloudMaxDepth ( 0 ),
2016-04-12 15:14:04 -04:00
_preferencesDialog -> getCloudMinDepth ( 0 ),
2016-05-31 19:09:49 -04:00
indices . get (),
2018-09-24 14:36:54 -04:00
allParameters ,
2025-06-01 14:14:29 -07:00
_preferencesDialog -> getCloudRoiRatios ( 0 ),
_preferencesDialog -> getCloudConfidenceThr ( 0 ));
2014-06-23 20:43:08 +00:00
2016-06-12 17:53:35 -04:00
// view point
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 ();
}
2022-07-20 15:20:14 -04:00
else if ( data . stereoCameraModels (). size () && ! data . stereoCameraModels ()[ 0 ]. localTransform (). isNull ())
2016-06-12 17:53:35 -04:00
{
2022-07-20 15:20:14 -04:00
viewPoint [ 0 ] = data . stereoCameraModels ()[ 0 ]. localTransform (). x ();
viewPoint [ 1 ] = data . stereoCameraModels ()[ 0 ]. localTransform (). y ();
viewPoint [ 2 ] = data . stereoCameraModels ()[ 0 ]. localTransform (). z ();
2016-06-12 17:53:35 -04:00
}
2016-06-10 20:12:13 -04:00
// filtering pipeline
2017-02-09 18:32:44 -05:00
if ( indices -> size () && _preferencesDialog -> getVoxel () > 0.0 )
2016-06-10 20:12:13 -04:00
{
2017-02-09 18:32:44 -05:00
cloud = util3d :: voxelize ( cloud , indices , _preferencesDialog -> getVoxel ());
2016-06-10 20:12:13 -04:00
//generate indices for all points (they are all valid)
2016-06-12 13:51:49 -04:00
indices -> resize ( cloud -> size ());
for ( unsigned int i = 0 ; i < cloud -> size (); ++ i )
2016-06-10 20:12:13 -04:00
{
indices -> at ( i ) = i ;
}
}
2017-02-09 18:32:44 -05:00
// Do ceiling/floor filtering
if ( indices -> size () &&
( _preferencesDialog -> getFloorFilteringHeight () != 0.0 ||
_preferencesDialog -> getCeilingFilteringHeight () != 0.0 ))
{
// perform in /map frame
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloud , pose );
indices = rtabmap :: util3d :: passThrough (
cloudTransformed ,
indices ,
"z" ,
_preferencesDialog -> getFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getFloorFilteringHeight (),
_preferencesDialog -> getCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getCeilingFilteringHeight ());
}
2016-06-10 20:12:13 -04:00
// Do radius filtering after voxel filtering ( a lot faster)
if ( indices -> size () &&
2017-02-09 18:32:44 -05:00
_preferencesDialog -> getNoiseRadius () > 0.0 &&
_preferencesDialog -> getNoiseMinNeighbors () > 0 )
2016-06-10 20:12:13 -04:00
{
indices = rtabmap :: util3d :: radiusFiltering (
2016-06-12 13:51:49 -04:00
cloud ,
2016-06-10 20:12:13 -04:00
indices ,
2017-02-09 18:32:44 -05:00
_preferencesDialog -> getNoiseRadius (),
_preferencesDialog -> getNoiseMinNeighbors ());
2016-06-10 20:12:13 -04:00
}
2016-06-12 13:51:49 -04:00
pcl :: PointCloud < pcl :: PointXYZRGBNormal >:: Ptr cloudWithNormals ( new pcl :: PointCloud < pcl :: PointXYZRGBNormal > );
2017-02-09 18:32:44 -05:00
if ( _preferencesDialog -> isSubtractFiltering () &&
2017-04-10 16:59:48 -04:00
_preferencesDialog -> getSubtractFilteringRadius () > 0.0 &&
nodeId > 0 )
2015-06-29 00:19:45 -04:00
{
2016-07-15 17:46:13 -04:00
pcl :: IndicesPtr beforeFiltering = indices ;
if ( cloud -> size () &&
_previousCloud . first > 0 &&
_previousCloud . second . first . first . get () != 0 &&
_previousCloud . second . second . get () != 0 &&
_previousCloud . second . second -> size () &&
_currentPosesMap . find ( _previousCloud . first ) != _currentPosesMap . end ())
2015-06-29 00:19:45 -04:00
{
2016-07-15 17:46:13 -04:00
UTimer time ;
rtabmap :: Transform t = pose . inverse () * _currentPosesMap . at ( _previousCloud . first );
//UWARN("saved new.pcd and old.pcd");
//pcl::io::savePCDFile("new.pcd", *cloud, *indices);
//pcl::io::savePCDFile("old.pcd", *previousCloud, *_previousCloud.second.second);
2016-09-07 12:15:44 -04:00
if ( _preferencesDialog -> isSubtractFiltering ())
{
if ( _preferencesDialog -> getSubtractFilteringAngle () > 0.0f )
2016-06-12 13:51:49 -04:00
{
2016-09-07 12:15:44 -04:00
//normals required
2017-08-25 18:02:40 -04:00
if ( _preferencesDialog -> getNormalKSearch () > 0 || _preferencesDialog -> getNormalRadiusSearch () > 0 )
2016-09-07 12:15:44 -04:00
{
2017-08-25 18:02:40 -04:00
pcl :: PointCloud < pcl :: Normal >:: Ptr normals = util3d :: computeNormals ( cloud , indices , _preferencesDialog -> getNormalKSearch (), _preferencesDialog -> getNormalRadiusSearch (), viewPoint );
2016-09-07 12:15:44 -04:00
pcl :: concatenateFields ( * cloud , * normals , * cloudWithNormals );
}
else
{
UWARN ( "Cloud subtraction with angle filtering is activated but "
"cloud normal K search is 0. Subtraction is done with angle." );
}
}
if ( cloudWithNormals -> size () &&
_previousCloud . second . first . second . get () &&
_previousCloud . second . first . second -> size ())
{
pcl :: PointCloud < pcl :: PointXYZRGBNormal >:: Ptr previousCloud = rtabmap :: util3d :: transformPointCloud ( _previousCloud . second . first . second , t );
indices = rtabmap :: util3d :: subtractFiltering (
cloudWithNormals ,
indices ,
previousCloud ,
_previousCloud . second . second ,
_preferencesDialog -> getSubtractFilteringRadius (),
_preferencesDialog -> getSubtractFilteringAngle (),
_preferencesDialog -> getSubtractFilteringMinPts ());
2016-06-12 13:51:49 -04:00
}
2016-07-15 17:46:13 -04:00
else
2016-06-10 20:12:13 -04:00
{
2016-09-07 12:15:44 -04:00
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr previousCloud = rtabmap :: util3d :: transformPointCloud ( _previousCloud . second . first . first , t );
indices = rtabmap :: util3d :: subtractFiltering (
cloud ,
indices ,
previousCloud ,
_previousCloud . second . second ,
_preferencesDialog -> getSubtractFilteringRadius (),
_preferencesDialog -> getSubtractFilteringMinPts ());
2016-06-10 20:12:13 -04:00
}
2016-06-12 13:51:49 -04:00
2016-07-15 17:46:13 -04:00
2016-09-07 12:15:44 -04:00
UINFO ( "Time subtract filtering %d from %d -> %d (%fs)" ,
( int ) _previousCloud . second . second -> size (),
( int ) beforeFiltering -> size (),
( int ) indices -> size (),
time . ticks ());
}
2016-07-15 17:46:13 -04:00
}
// keep all indices for next subtraction
_previousCloud . first = nodeId ;
_previousCloud . second . first . first = cloud ;
_previousCloud . second . first . second = cloudWithNormals ;
_previousCloud . second . second = beforeFiltering ;
}
if ( indices -> size ())
{
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr output ;
bool added = false ;
if ( _preferencesDialog -> isCloudMeshing () && cloud -> isOrganized ())
{
// Fast organized mesh
// we need to extract indices as pcl::OrganizedFastMesh doesn't take indices
output = util3d :: extractIndices ( cloud , indices , false , true );
std :: vector < pcl :: Vertices > polygons = util3d :: organizedFastMesh (
output ,
_preferencesDialog -> getCloudMeshingAngle (),
_preferencesDialog -> isCloudMeshingQuad (),
_preferencesDialog -> getCloudMeshingTriangleSize (),
viewPoint );
if ( polygons . size ())
{
// remove unused vertices to save memory
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr outputFiltered ( new pcl :: PointCloud < pcl :: PointXYZRGB > );
std :: vector < pcl :: Vertices > outputPolygons ;
2016-09-07 19:59:25 -04:00
std :: vector < int > denseToOrganizedIndices = util3d :: filterNotUsedVerticesFromMesh ( * output , polygons , * outputFiltered , outputPolygons );
2016-09-07 12:15:44 -04:00
if ( _preferencesDialog -> isCloudMeshingTexture () && ! image . empty ())
{
pcl :: TextureMesh :: Ptr textureMesh ( new pcl :: TextureMesh );
pcl :: toPCLPointCloud2 ( * outputFiltered , textureMesh -> cloud );
textureMesh -> tex_polygons . push_back ( outputPolygons );
int w = cloud -> width ;
int h = cloud -> height ;
UASSERT ( w > 1 && h > 1 );
textureMesh -> tex_coordinates . resize ( 1 );
2016-12-02 13:43:44 -05:00
int nPoints = ( int ) outputFiltered -> size ();
2016-09-07 12:15:44 -04:00
textureMesh -> tex_coordinates [ 0 ]. resize ( nPoints );
2016-12-02 13:43:44 -05:00
for ( int i = 0 ; i < nPoints ; ++ i )
2016-09-07 12:15:44 -04:00
{
//uv
2016-12-02 13:43:44 -05:00
UASSERT ( i < ( int ) denseToOrganizedIndices . size ());
2016-09-07 19:59:25 -04:00
int originalVertex = denseToOrganizedIndices [ i ];
2016-09-07 12:15:44 -04:00
textureMesh -> tex_coordinates [ 0 ][ i ] = Eigen :: Vector2f (
float ( originalVertex % w ) / float ( w ), // u
float ( h - originalVertex / w ) / float ( h )); // v
}
pcl :: TexMaterial mesh_material ;
mesh_material . tex_d = 1.0f ;
mesh_material . tex_Ns = 75.0f ;
mesh_material . tex_illum = 1 ;
std :: stringstream tex_name ;
tex_name << "material_" << nodeId ;
tex_name >> mesh_material . tex_name ;
2017-01-30 21:23:56 -05:00
mesh_material . tex_file = "" ;
2016-09-07 12:15:44 -04:00
textureMesh -> tex_materials . push_back ( mesh_material );
2017-01-30 21:23:56 -05:00
if ( ! _cloudViewer -> addCloudTextureMesh ( cloudName , textureMesh , image , pose ))
2016-09-07 12:15:44 -04:00
{
UERROR ( "Adding texture mesh %d to viewer failed!" , nodeId );
}
else
{
added = true ;
}
}
else if ( ! _cloudViewer -> addCloudMesh ( cloudName , outputFiltered , outputPolygons , pose ))
2016-06-10 20:12:13 -04:00
{
2016-07-15 17:46:13 -04:00
UERROR ( "Adding mesh cloud %d to viewer failed!" , nodeId );
2016-06-12 13:51:49 -04:00
}
else
{
2016-07-15 17:46:13 -04:00
added = true ;
2016-07-08 12:47:23 -04:00
}
}
2015-06-29 00:19:45 -04:00
}
2016-07-15 17:46:13 -04:00
else
{
if ( _preferencesDialog -> isCloudMeshing ())
{
UWARN ( "Online meshing is activated but the generated cloud is "
"dense (voxel filtering is used or multiple cameras are used). Disable "
"online meshing in Preferences->3D Rendering to hide this warning." );
}
if ( _preferencesDialog -> getNormalKSearch () > 0 && cloudWithNormals -> size () == 0 )
{
2017-08-25 18:02:40 -04:00
pcl :: PointCloud < pcl :: Normal >:: Ptr normals = util3d :: computeNormals ( cloud , indices , _preferencesDialog -> getNormalKSearch (), _preferencesDialog -> getNormalRadiusSearch (), viewPoint );
2016-07-15 17:46:13 -04:00
pcl :: concatenateFields ( * cloud , * normals , * cloudWithNormals );
}
QColor color = Qt :: gray ;
if ( mapId >= 0 )
{
color = ( Qt :: GlobalColor )( mapId + 3 % 12 + 7 );
}
output = util3d :: extractIndices ( cloud , indices , false , true );
if ( cloudWithNormals -> size ())
{
pcl :: PointCloud < pcl :: PointXYZRGBNormal >:: Ptr outputWithNormals ;
outputWithNormals = util3d :: extractIndices ( cloudWithNormals , indices , false , false );
if ( ! _cloudViewer -> addCloud ( cloudName , outputWithNormals , pose , color ))
{
UERROR ( "Adding cloud %d to viewer failed!" , nodeId );
}
else
{
added = true ;
}
}
else
{
if ( ! _cloudViewer -> addCloud ( cloudName , output , pose , color ))
{
UERROR ( "Adding cloud %d to viewer failed!" , nodeId );
}
else
{
added = true ;
}
}
}
if ( added )
{
outputPair . first = output ;
outputPair . second = indices ;
2017-04-10 16:59:48 -04:00
if ( _preferencesDialog -> isCloudsKept () && nodeId > 0 )
2016-07-15 17:46:13 -04:00
{
_cachedClouds . insert ( std :: make_pair ( nodeId , outputPair ));
2016-12-02 13:43:44 -05:00
_createdCloudsMemoryUsage += ( long )( output -> size () * sizeof ( pcl :: PointXYZRGB ) + indices -> size () * sizeof ( int ));
2016-07-15 17:46:13 -04:00
}
2018-02-17 09:46:08 -05:00
_cloudViewer -> setCloudColorIndex ( cloudName , _preferencesDialog -> getCloudColorScheme ( 0 ));
2017-02-09 18:32:44 -05:00
_cloudViewer -> setCloudOpacity ( cloudName , _preferencesDialog -> getCloudOpacity ( 0 ));
_cloudViewer -> setCloudPointSize ( cloudName , _preferencesDialog -> getCloudPointSize ( 0 ));
}
2017-04-10 16:59:48 -04:00
else if ( nodeId > 0 )
2017-02-09 18:32:44 -05:00
{
_cachedEmptyClouds . insert ( nodeId );
2016-07-15 17:46:13 -04:00
}
2014-06-23 20:43:08 +00:00
}
2017-04-10 16:59:48 -04:00
else if ( nodeId > 0 )
2017-02-09 18:32:44 -05:00
{
_cachedEmptyClouds . insert ( nodeId );
}
2014-06-23 20:43:08 +00:00
}
2016-07-08 12:47:23 -04:00
2016-07-15 17:46:13 -04:00
return outputPair ;
2016-07-08 12:47:23 -04:00
}
2014-10-30 19:38:58 +00:00
void MainWindow :: createAndAddScanToMap ( int nodeId , const Transform & pose , int mapId )
2014-06-23 20:43:08 +00:00
{
std :: string scanName = uFormat ( "scan%d" , nodeId );
2016-05-05 18:25:26 -04:00
if ( _cloudViewer -> getAddedClouds (). contains ( scanName ))
2014-06-23 20:43:08 +00:00
{
UERROR ( "Scan %d already added to map." , nodeId );
return ;
}
2014-10-26 21:47:29 +00:00
QMap < int , Signature >:: iterator iter = _cachedSignatures . find ( nodeId );
if ( iter == _cachedSignatures . end ())
{
UERROR ( "Node %d is not in the cache." , nodeId );
return ;
}
2018-02-16 19:20:54 -05:00
if ( ! iter -> sensorData (). laserScanCompressed (). isEmpty () || ! iter -> sensorData (). laserScanRaw (). isEmpty ())
2014-10-01 21:44:46 +00:00
{
2018-02-16 19:20:54 -05:00
LaserScan scan ;
2016-01-08 19:15:49 -05:00
iter -> sensorData (). uncompressData ( 0 , 0 , & scan );
2014-10-30 19:38:58 +00:00
2018-02-17 09:46:08 -05:00
if ( _preferencesDialog -> getDownsamplingStepScan ( 0 ) > 1 ||
_preferencesDialog -> getScanMaxRange ( 0 ) > 0.0f ||
_preferencesDialog -> getScanMinRange ( 0 ) > 0.0f )
2015-11-22 18:08:32 -05:00
{
2018-02-17 09:46:08 -05:00
scan = util3d :: commonFiltering ( scan ,
_preferencesDialog -> getDownsamplingStepScan ( 0 ),
_preferencesDialog -> getScanMinRange ( 0 ),
_preferencesDialog -> getScanMaxRange ( 0 ));
2015-11-22 18:08:32 -05:00
}
2016-01-08 19:15:49 -05:00
2017-08-25 18:02:40 -04:00
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloud ;
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloudRGB ;
2018-02-17 09:46:08 -05:00
pcl :: PointCloud < pcl :: PointXYZI >:: Ptr cloudI ;
2017-08-25 18:02:40 -04:00
pcl :: PointCloud < pcl :: PointNormal >:: Ptr cloudWithNormals ;
pcl :: PointCloud < pcl :: PointXYZRGBNormal >:: Ptr cloudRGBWithNormals ;
2018-02-17 09:46:08 -05:00
pcl :: PointCloud < pcl :: PointXYZINormal >:: Ptr cloudIWithNormals ;
2018-02-16 19:20:54 -05:00
if ( scan . hasNormals () && scan . hasRGB () && _preferencesDialog -> getCloudVoxelSizeScan ( 0 ) <= 0.0 )
2015-11-22 18:08:32 -05:00
{
2018-02-16 19:20:54 -05:00
cloudRGBWithNormals = util3d :: laserScanToPointCloudRGBNormal ( scan , scan . localTransform ());
2017-08-25 18:02:40 -04:00
}
2018-02-17 09:46:08 -05:00
else if ( scan . hasNormals () && scan . hasIntensity () && _preferencesDialog -> getCloudVoxelSizeScan ( 0 ) <= 0.0 )
{
cloudIWithNormals = util3d :: laserScanToPointCloudINormal ( scan , scan . localTransform ());
}
2018-02-16 19:20:54 -05:00
else if (( scan . hasNormals ()) && _preferencesDialog -> getCloudVoxelSizeScan ( 0 ) <= 0.0 )
2017-08-25 18:02:40 -04:00
{
2018-02-16 19:20:54 -05:00
cloudWithNormals = util3d :: laserScanToPointCloudNormal ( scan , scan . localTransform ());
2017-08-25 18:02:40 -04:00
}
2018-02-16 19:20:54 -05:00
else if ( scan . hasRGB ())
2017-08-25 18:02:40 -04:00
{
2018-02-16 19:20:54 -05:00
cloudRGB = util3d :: laserScanToPointCloudRGB ( scan , scan . localTransform ());
2017-08-25 18:02:40 -04:00
}
2018-02-17 09:46:08 -05:00
else if ( scan . hasIntensity ())
{
cloudI = util3d :: laserScanToPointCloudI ( scan , scan . localTransform ());
}
2017-08-25 18:02:40 -04:00
else
{
2018-02-16 19:20:54 -05:00
cloud = util3d :: laserScanToPointCloud ( scan , scan . localTransform ());
2017-08-25 18:02:40 -04:00
}
if ( _preferencesDialog -> getCloudVoxelSizeScan ( 0 ) > 0.0 )
{
if ( cloud . get ())
2016-01-08 19:15:49 -05:00
{
cloud = util3d :: voxelize ( cloud , _preferencesDialog -> getCloudVoxelSizeScan ( 0 ));
}
2017-08-25 18:02:40 -04:00
if ( cloudRGB . get ())
{
cloudRGB = util3d :: voxelize ( cloudRGB , _preferencesDialog -> getCloudVoxelSizeScan ( 0 ));
}
2018-02-17 09:46:08 -05:00
if ( cloudI . get ())
{
cloudI = util3d :: voxelize ( cloudI , _preferencesDialog -> getCloudVoxelSizeScan ( 0 ));
}
2017-08-25 18:02:40 -04:00
}
2017-04-24 19:12:53 -04:00
2017-08-25 18:02:40 -04:00
// Do ceiling/floor filtering
2018-02-16 19:20:54 -05:00
if (( ! scan . is2d ()) && // don't filter 2D scans
2017-08-25 18:02:40 -04:00
( _preferencesDialog -> getScanFloorFilteringHeight () != 0.0 ||
_preferencesDialog -> getScanCeilingFilteringHeight () != 0.0 ))
{
if ( cloudRGBWithNormals . get ())
2017-04-24 19:12:53 -04:00
{
// perform in /map frame
2017-08-25 18:02:40 -04:00
pcl :: PointCloud < pcl :: PointXYZRGBNormal >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloudRGBWithNormals , pose );
2017-04-24 19:12:53 -04:00
cloudTransformed = rtabmap :: util3d :: passThrough (
cloudTransformed ,
"z" ,
_preferencesDialog -> getScanFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getScanFloorFilteringHeight (),
_preferencesDialog -> getScanCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getScanCeilingFilteringHeight ());
//transform back in sensor frame
2017-08-25 18:02:40 -04:00
cloudRGBWithNormals = util3d :: transformPointCloud ( cloudTransformed , pose . inverse ());
2017-04-24 19:12:53 -04:00
}
2018-02-17 09:46:08 -05:00
if ( cloudIWithNormals . get ())
{
// perform in /map frame
pcl :: PointCloud < pcl :: PointXYZINormal >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloudIWithNormals , pose );
cloudTransformed = rtabmap :: util3d :: passThrough (
cloudTransformed ,
"z" ,
_preferencesDialog -> getScanFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getScanFloorFilteringHeight (),
_preferencesDialog -> getScanCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getScanCeilingFilteringHeight ());
//transform back in sensor frame
cloudIWithNormals = util3d :: transformPointCloud ( cloudTransformed , pose . inverse ());
}
2017-08-25 18:02:40 -04:00
if ( cloudWithNormals . get ())
{
// perform in /map frame
pcl :: PointCloud < pcl :: PointNormal >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloudWithNormals , pose );
cloudTransformed = rtabmap :: util3d :: passThrough (
cloudTransformed ,
"z" ,
_preferencesDialog -> getScanFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getScanFloorFilteringHeight (),
_preferencesDialog -> getScanCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getScanCeilingFilteringHeight ());
2017-04-24 19:12:53 -04:00
2017-08-25 18:02:40 -04:00
//transform back in sensor frame
cloudWithNormals = util3d :: transformPointCloud ( cloudTransformed , pose . inverse ());
2016-01-08 19:15:49 -05:00
}
2017-08-25 18:02:40 -04:00
if ( cloudRGB . get ())
2016-01-08 19:15:49 -05:00
{
2017-08-25 18:02:40 -04:00
// perform in /map frame
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloudRGB , pose );
cloudTransformed = rtabmap :: util3d :: passThrough (
cloudTransformed ,
"z" ,
_preferencesDialog -> getScanFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getScanFloorFilteringHeight (),
_preferencesDialog -> getScanCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getScanCeilingFilteringHeight ());
2017-04-24 19:12:53 -04:00
2017-08-25 18:02:40 -04:00
//transform back in sensor frame
cloudRGB = util3d :: transformPointCloud ( cloudTransformed , pose . inverse ());
}
2018-02-17 09:46:08 -05:00
if ( cloudI . get ())
{
// perform in /map frame
pcl :: PointCloud < pcl :: PointXYZI >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloudI , pose );
cloudTransformed = rtabmap :: util3d :: passThrough (
cloudTransformed ,
"z" ,
_preferencesDialog -> getScanFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getScanFloorFilteringHeight (),
_preferencesDialog -> getScanCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getScanCeilingFilteringHeight ());
//transform back in sensor frame
cloudI = util3d :: transformPointCloud ( cloudTransformed , pose . inverse ());
}
2017-08-25 18:02:40 -04:00
if ( cloud . get ())
2017-04-24 19:12:53 -04:00
{
// perform in /map frame
pcl :: PointCloud < pcl :: PointXYZ >:: Ptr cloudTransformed = util3d :: transformPointCloud ( cloud , pose );
cloudTransformed = rtabmap :: util3d :: passThrough (
cloudTransformed ,
"z" ,
_preferencesDialog -> getScanFloorFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: min () : _preferencesDialog -> getScanFloorFilteringHeight (),
_preferencesDialog -> getScanCeilingFilteringHeight () == 0.0 ? ( float ) std :: numeric_limits < int >:: max () : _preferencesDialog -> getScanCeilingFilteringHeight ());
//transform back in sensor frame
cloud = util3d :: transformPointCloud ( cloudTransformed , pose . inverse ());
}
2017-08-25 18:02:40 -04:00
}
2017-04-24 19:12:53 -04:00
2018-02-17 09:46:08 -05:00
if ( ( cloud . get () || cloudRGB . get () || cloudI . get ()) &&
2017-08-25 18:02:40 -04:00
( _preferencesDialog -> getScanNormalKSearch () > 0 || _preferencesDialog -> getScanNormalRadiusSearch () > 0.0 ))
{
Eigen :: Vector3f scanViewpoint (
2018-02-16 19:20:54 -05:00
scan . localTransform (). x (),
scan . localTransform (). y (),
scan . localTransform (). z ());
2017-04-24 19:12:53 -04:00
2017-08-25 18:02:40 -04:00
pcl :: PointCloud < pcl :: Normal >:: Ptr normals ;
2017-09-18 17:00:15 -04:00
if ( cloud . get () && cloud -> size ())
2016-01-08 19:15:49 -05:00
{
2018-02-16 19:20:54 -05:00
if ( scan . is2d ())
2017-04-24 19:12:53 -04:00
{
2017-08-25 18:02:40 -04:00
normals = util3d :: computeFastOrganizedNormals2D ( cloud , _preferencesDialog -> getScanNormalKSearch (), _preferencesDialog -> getScanNormalRadiusSearch (), scanViewpoint );
2017-04-24 19:12:53 -04:00
}
else
{
2017-08-25 18:02:40 -04:00
normals = util3d :: computeNormals ( cloud , _preferencesDialog -> getScanNormalKSearch (), _preferencesDialog -> getScanNormalRadiusSearch (), scanViewpoint );
2017-04-24 19:12:53 -04:00
}
2017-08-25 18:02:40 -04:00
cloudWithNormals . reset ( new pcl :: PointCloud < pcl :: PointNormal > );
pcl :: concatenateFields ( * cloud , * normals , * cloudWithNormals );
cloud . reset ();
2016-01-08 19:15:49 -05:00
}
2018-02-17 09:46:08 -05:00
else if ( cloudRGB . get () && cloudRGB -> size ())
2016-01-08 19:15:49 -05:00
{
2018-02-17 09:46:08 -05:00
// Assuming 3D
2017-08-25 18:02:40 -04:00
normals = util3d :: computeNormals ( cloudRGB , _preferencesDialog -> getScanNormalKSearch (), _preferencesDialog -> getScanNormalRadiusSearch (), scanViewpoint );
cloudRGBWithNormals . reset ( new pcl :: PointCloud < pcl :: PointXYZRGBNormal > );
pcl :: concatenateFields ( * cloudRGB , * normals , * cloudRGBWithNormals );
cloudRGB . reset ();
}
2018-02-17 09:46:08 -05:00
else if ( cloudI . get ())
{
if ( scan . is2d ())
{
normals = util3d :: computeFastOrganizedNormals2D ( cloudI , _preferencesDialog -> getScanNormalKSearch (), _preferencesDialog -> getScanNormalRadiusSearch (), scanViewpoint );
}
else
{
normals = util3d :: computeNormals ( cloudI , _preferencesDialog -> getScanNormalKSearch (), _preferencesDialog -> getScanNormalRadiusSearch (), scanViewpoint );
}
cloudIWithNormals . reset ( new pcl :: PointCloud < pcl :: PointXYZINormal > );
2019-02-21 17:51:07 -05:00
pcl :: concatenateFields ( * cloudI , * normals , * cloudIWithNormals );
2018-02-17 09:46:08 -05:00
cloudI . reset ();
}
2017-08-25 18:02:40 -04:00
}
QColor color = Qt :: gray ;
if ( mapId >= 0 )
{
color = ( Qt :: GlobalColor )( mapId + 3 % 12 + 7 );
}
bool added = false ;
if ( cloudRGBWithNormals . get ())
{
added = _cloudViewer -> addCloud ( scanName , cloudRGBWithNormals , pose , color );
if ( added && nodeId > 0 )
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScanFromPointCloud ( * cloudRGBWithNormals , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2017-08-25 18:02:40 -04:00
}
}
2018-02-17 09:46:08 -05:00
else if ( cloudIWithNormals . get ())
{
added = _cloudViewer -> addCloud ( scanName , cloudIWithNormals , pose , color );
if ( added && nodeId > 0 )
{
if ( scan . is2d ())
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScan2dFromPointCloud ( * cloudIWithNormals , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2018-02-17 09:46:08 -05:00
}
else
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScanFromPointCloud ( * cloudIWithNormals , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2018-02-17 09:46:08 -05:00
}
}
}
2017-08-25 18:02:40 -04:00
else if ( cloudWithNormals . get ())
{
added = _cloudViewer -> addCloud ( scanName , cloudWithNormals , pose , color );
if ( added && nodeId > 0 )
{
2018-02-16 19:20:54 -05:00
if ( scan . is2d ())
2017-08-27 19:58:49 -04:00
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScan2dFromPointCloud ( * cloudWithNormals , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2017-08-27 19:58:49 -04:00
}
else
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScanFromPointCloud ( * cloudWithNormals , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2017-08-27 19:58:49 -04:00
}
2017-08-25 18:02:40 -04:00
}
}
else if ( cloudRGB . get ())
{
2017-09-18 15:52:39 -04:00
added = _cloudViewer -> addCloud ( scanName , cloudRGB , pose , color );
2017-08-25 18:02:40 -04:00
if ( added && nodeId > 0 )
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScanFromPointCloud ( * cloudRGB , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2017-08-25 18:02:40 -04:00
}
}
2018-02-17 09:46:08 -05:00
else if ( cloudI . get ())
{
added = _cloudViewer -> addCloud ( scanName , cloudI , pose , color );
if ( added && nodeId > 0 )
{
if ( scan . is2d ())
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScan2dFromPointCloud ( * cloudI , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2018-02-17 09:46:08 -05:00
}
else
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScanFromPointCloud ( * cloudI , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2018-02-17 09:46:08 -05:00
}
}
}
2017-08-25 18:02:40 -04:00
else
{
UASSERT ( cloud . get ());
added = _cloudViewer -> addCloud ( scanName , cloud , pose , color );
if ( added && nodeId > 0 )
{
2018-02-16 19:20:54 -05:00
if ( scan . is2d ())
2016-03-14 17:27:53 -04:00
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScan2dFromPointCloud ( * cloud , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2016-03-14 17:27:53 -04:00
}
2017-04-24 19:12:53 -04:00
else
{
2021-02-24 09:43:22 -05:00
scan = LaserScan ( util3d :: laserScanFromPointCloud ( * cloud , scan . localTransform (). inverse ()), scan . maxPoints (), scan . rangeMax (), scan . localTransform ());
2017-04-24 19:12:53 -04:00
}
2015-11-22 18:08:32 -05:00
}
2014-10-30 19:38:58 +00:00
}
2017-08-25 18:02:40 -04:00
if ( ! added )
{
UERROR ( "Adding cloud %d to viewer failed!" , nodeId );
}
else
{
if ( nodeId > 0 )
{
2018-02-16 19:20:54 -05:00
_createdScans . insert ( std :: make_pair ( nodeId , scan )); // keep scan in scan frame
2017-08-25 18:02:40 -04:00
}
2018-02-23 11:13:26 -05:00
_cloudViewer -> setCloudColorIndex ( scanName , _preferencesDialog -> getScanColorScheme ( 0 ) == 0 && scan . is2d () ? 2 : _preferencesDialog -> getScanColorScheme ( 0 ));
2017-08-25 18:02:40 -04:00
_cloudViewer -> setCloudOpacity ( scanName , _preferencesDialog -> getScanOpacity ( 0 ));
_cloudViewer -> setCloudPointSize ( scanName , _preferencesDialog -> getScanPointSize ( 0 ));
}
2014-10-01 21:44:46 +00:00
}
2014-06-23 20:43:08 +00:00
}
2016-03-01 17:33:27 -05:00
void MainWindow :: createAndAddFeaturesToMap ( int nodeId , const Transform & pose , int mapId )
{
UDEBUG ( "" );
UASSERT ( ! pose . isNull ());
std :: string cloudName = uFormat ( "features%d" , nodeId );
2016-05-05 18:25:26 -04:00
if ( _cloudViewer -> getAddedClouds (). contains ( cloudName ))
2016-03-01 17:33:27 -05:00
{
UERROR ( "Features cloud %d already added to map." , nodeId );
return ;
}
QMap < int , Signature >:: iterator iter = _cachedSignatures . find ( nodeId );
if ( iter == _cachedSignatures . end ())
{
UERROR ( "Node %d is not in the cache." , nodeId );
return ;
}
if ( _createdFeatures . find ( nodeId ) != _createdFeatures . end ())
{
UDEBUG ( "Features cloud %d already created." );
return ;
}
if ( iter -> getWords3 (). size ())
{
UINFO ( "Create cloud from 3D words" );
QColor color = Qt :: gray ;
if ( mapId >= 0 )
{
color = ( Qt :: GlobalColor )( mapId + 3 % 12 + 7 );
}
cv :: Mat rgb ;
if ( ! iter -> sensorData (). imageCompressed (). empty () || ! iter -> sensorData (). imageRaw (). empty ())
{
SensorData data = iter -> sensorData ();
data . uncompressData ( & rgb , 0 , 0 );
}
pcl :: PointCloud < pcl :: PointXYZRGB >:: Ptr cloud ( new pcl :: PointCloud < pcl :: PointXYZRGB > );
cloud -> resize ( iter -> getWords3 (). size ());
int oi = 0 ;
UASSERT ( iter -> getWords (). size () == iter -> getWords3 (). size ());
2018-06-01 16:26:09 -04:00
float maxDepth = _preferencesDialog -> getCloudMaxDepth ( 0 );
UDEBUG ( "rgb.channels()=%d" );
2025-09-03 16:55:54 -07:00
if ( ! iter -> getWords3 (). empty () && iter -> getWords3 (). size () == iter -> getWordsKpts (). size ())
2016-03-01 17:33:27 -05:00
{
2020-10-05 17:34:32 -04:00
Transform invLocalTransform = Transform :: getIdentity ();
2022-07-20 15:20:14 -04:00
if ( iter . value (). sensorData (). cameraModels (). size () == 1 &&
iter . value (). sensorData (). cameraModels (). at ( 0 ). isValidForProjection ())
2016-03-01 17:33:27 -05:00
{
2020-10-05 17:34:32 -04:00
invLocalTransform = iter . value (). sensorData (). cameraModels ()[ 0 ]. localTransform (). inverse ();
}
2022-07-20 15:20:14 -04:00
else if ( iter . value (). sensorData (). stereoCameraModels (). size () == 1 &&
iter . value (). sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ())
2020-10-05 17:34:32 -04:00
{
2022-07-20 15:20:14 -04:00
invLocalTransform = iter . value (). sensorData (). stereoCameraModels ()[ 0 ]. left (). localTransform (). inverse ();
2020-10-05 17:34:32 -04:00
}
for ( std :: multimap < int , int >:: const_iterator jter = iter -> getWords (). begin (); jter != iter -> getWords (). end (); ++ jter )
{
const cv :: Point3f & pt = iter -> getWords3 ()[ jter -> second ];
if ( util3d :: isFinite ( pt ) &&
( maxDepth == 0.0f ||
//move back point in camera frame (to get depth along z), ignore for multi-camera
( iter . value (). sensorData (). cameraModels (). size () <= 1 &&
util3d :: transformPoint ( pt , invLocalTransform ). z < maxDepth )))
2016-03-01 17:33:27 -05:00
{
2020-10-05 17:34:32 -04:00
( * cloud )[ oi ]. x = pt . x ;
( * cloud )[ oi ]. y = pt . y ;
( * cloud )[ oi ]. z = pt . z ;
const cv :: KeyPoint & kpt = iter -> getWordsKpts ()[ jter -> second ];
int u = kpt . pt . x + 0.5 ;
int v = kpt . pt . y + 0.5 ;
if ( ! rgb . empty () &&
uIsInBounds ( u , 0 , rgb . cols - 1 ) &&
uIsInBounds ( v , 0 , rgb . rows - 1 ))
2018-06-01 16:26:09 -04:00
{
2020-10-05 17:34:32 -04:00
if ( rgb . channels () == 1 )
{
( * cloud )[ oi ]. r = ( * cloud )[ oi ]. g = ( * cloud )[ oi ]. b = rgb . at < unsigned char > ( v , u );
}
else
{
cv :: Vec3b bgr = rgb . at < cv :: Vec3b > ( v , u );
( * cloud )[ oi ]. b = bgr . val [ 0 ];
( * cloud )[ oi ]. g = bgr . val [ 1 ];
( * cloud )[ oi ]. r = bgr . val [ 2 ];
}
2018-06-01 16:26:09 -04:00
}
else
{
2020-10-05 17:34:32 -04:00
( * cloud )[ oi ]. r = ( * cloud )[ oi ]. g = ( * cloud )[ oi ]. b = 255 ;
2018-06-01 16:26:09 -04:00
}
2020-10-05 17:34:32 -04:00
++ oi ;
2016-03-01 17:33:27 -05:00
}
}
}
2018-06-01 16:26:09 -04:00
cloud -> resize ( oi );
2016-05-05 18:25:26 -04:00
if ( ! _cloudViewer -> addCloud ( cloudName , cloud , pose , color ))
2016-03-01 17:33:27 -05:00
{
UERROR ( "Adding features cloud %d to viewer failed!" , nodeId );
}
2017-04-10 16:59:48 -04:00
else if ( nodeId > 0 )
2016-03-01 17:33:27 -05:00
{
_createdFeatures . insert ( std :: make_pair ( nodeId , cloud ));
}
}
else
{
return ;
}
2016-05-05 18:25:26 -04:00
_cloudViewer -> setCloudPointSize ( cloudName , _preferencesDialog -> getFeaturesPointSize ( 0 ));
2016-03-01 17:33:27 -05:00
UDEBUG ( "" );
}
2016-01-07 19:39:21 -05:00
Transform MainWindow :: alignPosesToGroundTruth (
2018-02-08 21:40:17 -05:00
const std :: map < int , Transform > & poses ,
const std :: map < int , Transform > & groundTruth )
2016-01-07 19:39:21 -05:00
{
Transform t = Transform :: getIdentity ();
2017-01-03 21:05:00 -05:00
if ( groundTruth . size () && poses . size ())
2016-01-07 19:39:21 -05:00
{
2017-11-09 12:03:01 -05:00
float translational_rmse = 0.0f ;
float translational_mean = 0.0f ;
float translational_median = 0.0f ;
float translational_std = 0.0f ;
float translational_min = 0.0f ;
float translational_max = 0.0f ;
float rotational_rmse = 0.0f ;
float rotational_mean = 0.0f ;
float rotational_median = 0.0f ;
float rotational_std = 0.0f ;
float rotational_min = 0.0f ;
float rotational_max = 0.0f ;
2018-02-08 21:40:17 -05:00
t = graph :: calcRMSE (
2017-11-09 12:03:01 -05:00
groundTruth ,
poses ,
translational_rmse ,
translational_mean ,
translational_median ,
translational_std ,
translational_min ,
translational_max ,
rotational_rmse ,
rotational_mean ,
rotational_median ,
rotational_std ,
rotational_min ,
rotational_max );
2017-01-03 21:05:00 -05:00
// ground truth live statistics
2017-11-09 12:03:01 -05:00
UINFO ( "translational_rmse=%f" , translational_rmse );
UINFO ( "translational_mean=%f" , translational_mean );
UINFO ( "translational_median=%f" , translational_median );
UINFO ( "translational_std=%f" , translational_std );
UINFO ( "translational_min=%f" , translational_min );
UINFO ( "translational_max=%f" , translational_max );
2017-01-03 21:05:00 -05:00
2017-11-09 12:03:01 -05:00
UINFO ( "rotational_rmse=%f" , rotational_rmse );
UINFO ( "rotational_mean=%f" , rotational_mean );
UINFO ( "rotational_median=%f" , rotational_median );
UINFO ( "rotational_std=%f" , rotational_std );
UINFO ( "rotational_min=%f" , rotational_min );
UINFO ( "rotational_max=%f" , rotational_max );
2016-01-07 19:39:21 -05:00
}
return t ;
}
2014-01-22 19:49:28 +00:00
void MainWindow :: updateNodeVisibility ( int nodeId , bool visible )
{
2017-04-18 23:08:28 -04:00
UINFO ( "Update visibility %d" , nodeId );
QMap < std :: string , Transform > viewerClouds = _cloudViewer -> getAddedClouds ();
2017-08-28 16:15:22 -04:00
Transform pose ;
if ( _currentGTPosesMap . size () &&
_ui -> actionAnchor_clouds_to_ground_truth -> isChecked () &&
_currentGTPosesMap . find ( nodeId ) != _currentGTPosesMap . end ())
2014-01-22 19:49:28 +00:00
{
2017-08-28 16:15:22 -04:00
pose = _currentGTPosesMap . at ( nodeId );
}
else if ( _currentPosesMap . find ( nodeId ) != _currentPosesMap . end ())
{
pose = _currentPosesMap . at ( nodeId );
2014-01-22 19:49:28 +00:00
}
2017-04-18 23:08:28 -04:00
2017-08-28 16:15:22 -04:00
if ( ! pose . isNull () || ! visible )
2017-04-18 23:08:28 -04:00
{
2017-08-28 16:15:22 -04:00
if ( _preferencesDialog -> isCloudsShown ( 0 ))
2017-04-18 23:08:28 -04:00
{
2017-08-28 16:15:22 -04:00
std :: string cloudName = uFormat ( "cloud%d" , nodeId );
if ( visible && ! viewerClouds . contains ( cloudName ) && _cachedSignatures . contains ( nodeId ))
2017-04-18 23:08:28 -04:00
{
2017-08-28 16:15:22 -04:00
createAndAddCloudToMap ( nodeId , pose , uValue ( _currentMapIds , nodeId , - 1 ));
}
else if ( viewerClouds . contains ( cloudName ))
{
if ( visible )
{
//make sure the transformation was done
_cloudViewer -> updateCloudPose ( cloudName , pose );
}
_cloudViewer -> setCloudVisibility ( cloudName , visible );
2017-04-18 23:08:28 -04:00
}
}
2017-08-28 16:15:22 -04:00
if ( _preferencesDialog -> isScansShown ( 0 ))
{
std :: string scanName = uFormat ( "scan%d" , nodeId );
if ( visible && ! viewerClouds . contains ( scanName ) && _cachedSignatures . contains ( nodeId ))
{
createAndAddScanToMap ( nodeId , pose , uValue ( _currentMapIds , nodeId , - 1 ));
}
else if ( viewerClouds . contains ( scanName ))
{
if ( visible )
{
//make sure the transformation was done
_cloudViewer -> updateCloudPose ( scanName , pose );
}
_cloudViewer -> setCloudVisibility ( scanName , visible );
}
}
2021-06-18 12:43:08 -04:00
_cloudViewer -> refreshView ();
2017-08-28 16:15:22 -04:00
}
2014-01-22 19:49:28 +00:00
}
2016-07-08 12:47:23 -04:00
void MainWindow :: updateGraphView ()
{
if ( _ui -> dockWidget_graphViewer -> isVisible ())
{
UDEBUG ( "Graph visible!" );
if ( _currentPosesMap . size ())
{
this -> updateMapCloud (
std :: map < int , Transform > ( _currentPosesMap ),
std :: multimap < int , Link > ( _currentLinksMap ),
std :: map < int , int > ( _currentMapIds ),
std :: map < int , std :: string > ( _currentLabels ),
std :: map < int , Transform > ( _currentGTPosesMap ));
}
}
}
2011-06-05 14:45:39 +00:00
void MainWindow :: processRtabmapEventInit ( int status , const QString & info )
{
if (( RtabmapEventInit :: Status ) status == RtabmapEventInit :: kInitializing )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
2014-10-28 01:20:57 +00:00
this -> changeState ( MainWindow :: kInitializing );
2011-06-05 14:45:39 +00:00
}
else if (( RtabmapEventInit :: Status ) status == RtabmapEventInit :: kInitialized )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> setValue ( _progressDialog -> maximumSteps ());
2014-10-28 01:20:57 +00:00
this -> changeState ( MainWindow :: kInitialized );
2016-04-10 14:52:14 -04:00
if ( ! _openedDatabasePath . isEmpty ())
{
this -> downloadAllClouds ();
}
2014-10-28 01:20:57 +00:00
}
else if (( RtabmapEventInit :: Status ) status == RtabmapEventInit :: kClosing )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
2014-10-28 01:20:57 +00:00
if ( _state != kApplicationClosing )
{
this -> changeState ( MainWindow :: kClosing );
}
}
else if (( RtabmapEventInit :: Status ) status == RtabmapEventInit :: kClosed )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> setValue ( _progressDialog -> maximumSteps ());
2015-02-18 18:22:59 -05:00
if ( _databaseUpdated )
2014-10-28 01:20:57 +00:00
{
2015-03-01 19:41:03 -05:00
if ( ! _newDatabasePath . isEmpty ())
2014-10-28 01:20:57 +00:00
{
2015-03-01 19:41:03 -05:00
if ( ! _newDatabasePathOutput . isEmpty ())
2015-01-07 17:30:20 -05:00
{
2015-05-21 09:16:13 -04:00
bool removed = true ;
if ( QFile :: exists ( _newDatabasePathOutput ))
2015-03-01 19:41:03 -05:00
{
2015-05-21 09:16:13 -04:00
removed = QFile :: remove ( _newDatabasePathOutput );
}
if ( removed )
{
if ( QFile :: rename ( _newDatabasePath , _newDatabasePathOutput ))
{
std :: string msg = uFormat ( "Database saved to \" %s \" ." , _newDatabasePathOutput . toStdString (). c_str ());
UINFO ( msg . c_str ());
QMessageBox :: information ( this , tr ( "Database saved!" ), QString ( msg . c_str ()));
}
else
{
std :: string msg = uFormat ( "Failed to rename temporary database from \" %s \" to \" %s \" ." ,
_newDatabasePath . toStdString (). c_str (), _newDatabasePathOutput . toStdString (). c_str ());
UERROR ( msg . c_str ());
QMessageBox :: critical ( this , tr ( "Closing failed!" ), QString ( msg . c_str ()));
}
2015-03-01 19:41:03 -05:00
}
else
{
2015-05-21 09:16:13 -04:00
std :: string msg = uFormat ( "Failed to overwrite the database \" %s \" . The temporary database is still correctly saved at \" %s \" ." ,
_newDatabasePathOutput . toStdString (). c_str (), _newDatabasePath . toStdString (). c_str ());
2015-03-01 19:41:03 -05:00
UERROR ( msg . c_str ());
QMessageBox :: critical ( this , tr ( "Closing failed!" ), QString ( msg . c_str ()));
}
}
else if ( QFile :: remove ( _newDatabasePath ))
{
UINFO ( "Deleted temporary database \" %s \" ." , _newDatabasePath . toStdString (). c_str ());
2015-01-07 17:30:20 -05:00
}
2016-09-07 20:15:30 -04:00
else if ( ! uStr2Bool ( _preferencesDialog -> getAllParameters (). at ( Parameters :: kDbSqlite3InMemory ())))
2015-01-07 17:30:20 -05:00
{
2015-03-01 19:41:03 -05:00
UERROR ( "Temporary database \" %s \" could not be deleted." , _newDatabasePath . toStdString (). c_str ());
2015-01-07 17:30:20 -05:00
}
2015-03-01 19:41:03 -05:00
2015-02-18 18:22:59 -05:00
}
else if ( ! _openedDatabasePath . isEmpty ())
{
std :: string msg = uFormat ( "Database \" %s \" updated." , _openedDatabasePath . toStdString (). c_str ());
UINFO ( msg . c_str ());
QMessageBox :: information ( this , tr ( "Database updated!" ), QString ( msg . c_str ()));
2014-10-28 01:20:57 +00:00
}
}
2015-03-01 19:41:03 -05:00
else if ( ! _newDatabasePath . isEmpty ())
{
// just remove temporary database;
if ( QFile :: remove ( _newDatabasePath ))
{
UINFO ( "Deleted temporary database \" %s \" ." , _newDatabasePath . toStdString (). c_str ());
}
2016-09-07 12:15:44 -04:00
else if ( ! uStr2Bool ( _preferencesDialog -> getAllParameters (). at ( Parameters :: kDbSqlite3InMemory ())))
2015-03-01 19:41:03 -05:00
{
UERROR ( "Temporary database \" %s \" could not be deleted." , _newDatabasePath . toStdString (). c_str ());
}
}
2014-10-28 01:20:57 +00:00
_openedDatabasePath . clear ();
2015-02-18 18:22:59 -05:00
_newDatabasePath . clear ();
_newDatabasePathOutput . clear ();
2014-10-28 01:20:57 +00:00
bool applicationClosing = _state == kApplicationClosing ;
this -> changeState ( MainWindow :: kIdle );
if ( applicationClosing )
{
this -> close ();
}
2011-06-05 14:45:39 +00:00
}
else
{
2017-10-20 12:09:34 +02:00
_progressDialog -> incrementStep ();
2011-06-05 14:45:39 +00:00
QString msg ( info );
if (( RtabmapEventInit :: Status ) status == RtabmapEventInit :: kError )
{
2014-10-28 01:20:57 +00:00
_openedDatabasePath . clear ();
2015-02-18 18:22:59 -05:00
_newDatabasePath . clear ();
_newDatabasePathOutput . clear ();
2017-10-20 12:09:34 +02:00
_progressDialog -> setAutoClose ( false );
2011-06-05 14:45:39 +00:00
msg . prepend ( tr ( "[ERROR] " ));
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( msg );
2014-10-28 01:20:57 +00:00
this -> changeState ( MainWindow :: kIdle );
}
else
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( msg );
2011-06-05 14:45:39 +00:00
}
}
}
2013-12-11 00:12:44 +00:00
void MainWindow :: processRtabmapEvent3DMap ( const rtabmap :: RtabmapEvent3DMap & event )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( "Downloading the map... done." );
_progressDialog -> incrementStep ();
2013-12-11 00:12:44 +00:00
if ( event . getCode ())
{
UERROR ( "Map received with code error %d!" , event . getCode ());
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( uFormat ( "[ERROR] Map received with code error %d!" , event . getCode ()). c_str ());
_progressDialog -> setAutoClose ( false );
2013-12-11 00:12:44 +00:00
}
else
{
2015-08-04 13:33:01 -04:00
_processingDownloadedMap = true ;
2013-12-11 00:12:44 +00:00
UINFO ( "Received map!" );
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( " poses = %1" ). arg ( event . getPoses (). size ()));
_progressDialog -> appendText ( tr ( " constraints = %1" ). arg ( event . getConstraints (). size ()));
2013-12-11 00:12:44 +00:00
2017-10-20 12:09:34 +02:00
_progressDialog -> setMaximumSteps ( int ( event . getSignatures (). size () + event . getPoses (). size () + 1 ));
_progressDialog -> appendText ( QString ( "Inserting data in the cache (%1 signatures downloaded)..." ). arg ( event . getSignatures (). size ()));
2014-10-28 01:20:57 +00:00
QApplication :: processEvents ();
2013-12-11 00:12:44 +00:00
2014-10-26 21:47:29 +00:00
int addedSignatures = 0 ;
2015-05-29 14:46:48 -04:00
std :: map < int , int > mapIds ;
2016-01-07 19:39:21 -05:00
std :: map < int , Transform > groundTruth ;
2015-07-26 15:50:39 -04:00
std :: map < int , std :: string > labels ;
2014-10-26 21:47:29 +00:00
for ( std :: map < int , Signature >:: const_iterator iter = event . getSignatures (). begin ();
iter != event . getSignatures (). end ();
2013-12-11 00:12:44 +00:00
++ iter )
{
2015-05-29 14:46:48 -04:00
mapIds . insert ( std :: make_pair ( iter -> first , iter -> second . mapId ()));
2016-01-07 19:39:21 -05:00
if ( ! iter -> second . getGroundTruthPose (). isNull ())
{
groundTruth . insert ( std :: make_pair ( iter -> first , iter -> second . getGroundTruthPose ()));
}
2015-07-26 15:50:39 -04:00
if ( ! iter -> second . getLabel (). empty ())
{
labels . insert ( std :: make_pair ( iter -> first , iter -> second . getLabel ()));
}
2015-07-23 16:14:31 -04:00
if ( ! _cachedSignatures . contains ( iter -> first ) ||
( _cachedSignatures . value ( iter -> first ). sensorData (). imageCompressed (). empty () && ! iter -> second . sensorData (). imageCompressed (). empty ()))
2014-10-26 21:47:29 +00:00
{
2014-10-28 01:20:57 +00:00
_cachedSignatures . insert ( iter -> first , iter -> second );
2016-06-12 17:53:35 -04:00
_cachedMemoryUsage += iter -> second . sensorData (). getMemoryUsed ();
2018-07-09 11:33:39 -04:00
unsigned int count = 0 ;
2020-10-05 17:34:32 -04:00
if ( ! iter -> second . getWords3 (). empty ())
2018-07-09 11:33:39 -04:00
{
2020-10-05 17:34:32 -04:00
for ( std :: multimap < int , int >:: const_iterator jter = iter -> second . getWords (). upper_bound ( - 1 ); jter != iter -> second . getWords (). end (); ++ jter )
2018-07-09 11:33:39 -04:00
{
2020-10-05 17:34:32 -04:00
if ( util3d :: isFinite ( iter -> second . getWords3 ()[ jter -> second ]))
{
++ count ;
}
2018-07-09 11:33:39 -04:00
}
}
_cachedWordsCount . insert ( std :: make_pair ( iter -> first , ( float ) count ));
2014-10-28 01:20:57 +00:00
++ addedSignatures ;
2014-10-26 21:47:29 +00:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> incrementStep ();
2014-10-28 01:20:57 +00:00
QApplication :: processEvents ();
2013-12-11 00:12:44 +00:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Inserted %1 new signatures." ). arg ( addedSignatures ));
_progressDialog -> incrementStep ();
2014-10-28 01:20:57 +00:00
QApplication :: processEvents ();
2013-12-11 00:12:44 +00:00
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( "Inserting data in the cache... done." );
2013-12-11 00:12:44 +00:00
if ( event . getPoses (). size ())
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( "Updating the 3D map cloud..." );
_progressDialog -> incrementStep ();
_progressDialog -> setCancelButtonVisible ( true );
2016-09-19 14:01:06 -04:00
_progressCanceled = false ;
2014-02-11 23:04:22 +00:00
QApplication :: processEvents ();
2016-01-07 19:39:21 -05:00
std :: map < int , Transform > poses = event . getPoses ();
2021-12-18 18:53:59 -05:00
this -> updateMapCloud ( poses , event . getConstraints (), mapIds , labels , groundTruth , std :: map < int , Transform > (), std :: multimap < int , Link > (), true );
2018-07-09 11:33:39 -04:00
if ( _ui -> graphicsView_graphView -> isVisible () &&
_preferencesDialog -> isWordsCountGraphView () &&
_preferencesDialog -> isRGBDMode () &&
_cachedWordsCount . size ())
{
2025-04-16 13:41:17 -07:00
_ui -> graphicsView_graphView -> updateNodeColorByValue ( "Visual Words" , _cachedWordsCount , ( float ) _preferencesDialog -> getKpMaxFeatures ());
2018-07-09 11:33:39 -04:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( "Updating the 3D map cloud... done." );
2013-12-11 00:12:44 +00:00
}
else
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( "No poses received! The map cloud cannot be updated..." );
2013-12-11 00:12:44 +00:00
UINFO ( "Map received is empty! Cannot update the map cloud..." );
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "%1 locations are updated to/inserted in the cache." ). arg ( event . getPoses (). size ()));
2015-01-12 17:43:42 -05:00
if ( ! _preferencesDialog -> isImagesKept ())
{
_cachedSignatures . clear ();
2016-06-12 17:53:35 -04:00
_cachedMemoryUsage = 0 ;
2018-07-09 11:33:39 -04:00
_cachedWordsCount . clear ();
2015-01-12 17:43:42 -05:00
}
2015-10-16 20:11:24 -04:00
if ( _state != kMonitoring && _state != kDetecting )
{
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2015-10-16 20:11:24 -04:00
}
2015-08-04 13:33:01 -04:00
_processingDownloadedMap = false ;
2013-12-11 00:12:44 +00:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> setValue ( _progressDialog -> maximumSteps ());
_progressDialog -> setCancelButtonVisible ( false );
2016-09-19 14:01:06 -04:00
_progressCanceled = false ;
2017-10-25 09:48:00 +02:00
2018-06-15 12:17:11 -04:00
Q_EMIT ( rtabmapEvent3DMapProcessed ());
2013-12-11 00:12:44 +00:00
}
2015-04-17 18:30:12 -04:00
void MainWindow :: processRtabmapGlobalPathEvent ( const rtabmap :: RtabmapGlobalPathEvent & event )
{
2015-05-05 17:38:38 -04:00
if ( ! event . getPoses (). empty ())
2015-04-17 18:30:12 -04:00
{
_ui -> graphicsView_graphView -> setGlobalPath ( event . getPoses ());
2015-05-05 17:38:38 -04:00
}
2021-05-23 17:21:12 -04:00
if ( _preferencesDialog -> isCacheSavedInFigures () || _ui -> statsToolBox -> isVisible ())
{
_ui -> statsToolBox -> updateStat ( "Planning/From/" , float ( event . getPoses (). size () ? event . getPoses (). begin () -> first : 0 ), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Planning/Time/ms" , float ( event . getPlanningTime () * 1000.0 ), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Planning/Goal/" , float ( event . getGoal ()), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Planning/Poses/" , float ( event . getPoses (). size ()), _preferencesDialog -> isCacheSavedInFigures ());
_ui -> statsToolBox -> updateStat ( "Planning/Length/m" , float ( graph :: computePathLength ( event . getPoses ())), _preferencesDialog -> isCacheSavedInFigures ());
}
2015-05-05 17:38:38 -04:00
if ( _preferencesDialog -> notifyWhenNewGlobalPathIsReceived ())
{
// use MessageBox
if ( event . getPoses (). empty ())
{
QMessageBox * warn = new QMessageBox (
QMessageBox :: Warning ,
tr ( "Setting goal failed!" ),
2015-09-21 16:35:36 -04:00
tr ( "Setting goal to location %1%2 failed. "
2015-05-05 17:38:38 -04:00
"Some reasons: \n "
2015-11-29 16:20:04 -05:00
"1) the robot is not yet localized in the map, \n "
"2) the location doesn't exist in the map, \n "
"3) the location is not linked to the global map or \n "
"4) the location is too near of the current location (goal already reached)." )
2015-09-21 16:35:36 -04:00
. arg ( event . getGoal ())
. arg ( ! event . getGoalLabel (). empty () ? QString ( " \" %1 \" " ). arg ( event . getGoalLabel (). c_str ()) : "" ),
2015-05-05 17:38:38 -04:00
QMessageBox :: Ok ,
this );
warn -> setAttribute ( Qt :: WA_DeleteOnClose , true );
warn -> show ();
}
else
{
QMessageBox * info = new QMessageBox (
QMessageBox :: Information ,
tr ( "Goal detected!" ),
2015-09-21 16:35:36 -04:00
tr ( "Global path computed to %1%2 (%3 poses, %4 m)." )
. arg ( event . getGoal ())
. arg ( ! event . getGoalLabel (). empty () ? QString ( " \" %1 \" " ). arg ( event . getGoalLabel (). c_str ()) : "" )
. arg ( event . getPoses (). size ())
. arg ( graph :: computePathLength ( event . getPoses ())),
2015-05-05 17:38:38 -04:00
QMessageBox :: Ok ,
this );
info -> setAttribute ( Qt :: WA_DeleteOnClose , true );
info -> show ();
}
2015-04-17 18:30:12 -04:00
}
2015-10-24 17:03:44 -04:00
else if ( event . getPoses (). empty () && _waypoints . size ())
{
// resend the same goal
2022-10-28 12:25:48 -07:00
QTimer :: singleShot ( 1000 , this , SLOT ( postGoal ()));
2015-10-24 17:03:44 -04:00
}
2015-04-17 18:30:12 -04:00
}
2015-07-26 15:50:39 -04:00
void MainWindow :: processRtabmapLabelErrorEvent ( int id , const QString & label )
{
QMessageBox * warn = new QMessageBox (
QMessageBox :: Warning ,
tr ( "Setting label failed!" ),
tr ( "Setting label %1 to location %2 failed. "
"Some reasons: \n "
"1) the location doesn't exist in the map, \n "
"2) the location has already a label." ). arg ( label ). arg ( id ),
QMessageBox :: Ok ,
this );
warn -> setAttribute ( Qt :: WA_DeleteOnClose , true );
warn -> show ();
}
2015-10-13 12:50:25 -04:00
void MainWindow :: processRtabmapGoalStatusEvent ( int status )
{
_ui -> widget_console -> appendMsg ( tr ( "Goal status received=%1" ). arg ( status ), ULogger :: kInfo );
if ( _waypoints . size ())
{
this -> postGoal ( _waypoints . at ( ++ _waypointsIndex % _waypoints . size ()));
}
}
2011-06-05 14:45:39 +00:00
void MainWindow :: applyPrefSettings ( PreferencesDialog :: PANEL_FLAGS flags )
{
ULOGGER_DEBUG ( "" );
if ( flags & PreferencesDialog :: kPanelSource )
{
// Camera settings...
2012-06-24 17:19:34 +00:00
_ui -> doubleSpinBox_stats_imgRate -> setValue ( _preferencesDialog -> getGeneralInputRate ());
2015-03-01 19:41:03 -05:00
this -> updateSelectSourceMenu ();
2015-06-26 18:21:32 -04:00
_ui -> label_stats_source -> setText ( _preferencesDialog -> getSourceDriverStr ());
2011-06-05 14:45:39 +00:00
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2012-03-03 01:46:30 +00:00
{
2024-04-14 19:06:04 -07:00
if ( dynamic_cast < DBReader *> ( _sensorCapture -> camera ()) != 0 )
2016-06-21 11:22:24 -04:00
{
2024-04-14 19:06:04 -07:00
_sensorCapture -> setFrameRate ( _preferencesDialog -> isSourceDatabaseStampsUsed () ?- 1 : _preferencesDialog -> getGeneralInputRate ());
2016-06-21 11:22:24 -04:00
}
else
{
2024-04-14 19:06:04 -07:00
_sensorCapture -> setFrameRate ( _preferencesDialog -> getGeneralInputRate ());
2016-06-21 11:22:24 -04:00
}
2012-06-24 17:19:34 +00:00
}
2016-06-21 11:22:24 -04:00
2014-10-28 01:20:57 +00:00
} //This will update the statistics toolbox
2011-06-05 14:45:39 +00:00
if ( flags & PreferencesDialog :: kPanelGeneral )
{
2013-12-11 00:12:44 +00:00
UDEBUG ( "General settings changed..." );
setupMainLayout ( _preferencesDialog -> isVerticalLayoutUsed ());
2018-10-25 19:08:00 -04:00
if ( ! _preferencesDialog -> isLocalizationsCountGraphView ())
{
_cachedLocalizationsCount . clear ();
}
2015-01-07 17:30:20 -05:00
if ( ! _preferencesDialog -> isPosteriorGraphView () && _ui -> graphicsView_graphView -> isVisible ())
{
2025-04-16 13:41:17 -07:00
_ui -> graphicsView_graphView -> clearNodeColorByValue ();
2015-01-07 17:30:20 -05:00
}
2013-12-11 00:12:44 +00:00
}
if ( flags & PreferencesDialog :: kPanelCloudRendering )
{
UDEBUG ( "Cloud rendering settings changed..." );
2014-02-25 21:07:32 +00:00
if ( _currentPosesMap . size ())
{
2015-01-07 17:30:20 -05:00
this -> updateMapCloud (
std :: map < int , Transform > ( _currentPosesMap ),
std :: multimap < int , Link > ( _currentLinksMap ),
2015-07-26 15:50:39 -04:00
std :: map < int , int > ( _currentMapIds ),
2016-01-07 19:39:21 -05:00
std :: map < int , std :: string > ( _currentLabels ),
std :: map < int , Transform > ( _currentGTPosesMap ));
2014-02-25 21:07:32 +00:00
}
2013-12-11 00:12:44 +00:00
}
if ( flags & PreferencesDialog :: kPanelLogging )
{
UDEBUG ( "Logging settings changed..." );
2011-06-05 14:45:39 +00:00
ULogger :: setLevel (( ULogger :: Level ) _preferencesDialog -> getGeneralLoggerLevel ());
ULogger :: setEventLevel (( ULogger :: Level ) _preferencesDialog -> getGeneralLoggerEventLevel ());
2013-12-11 00:12:44 +00:00
ULogger :: setType (( ULogger :: Type ) _preferencesDialog -> getGeneralLoggerType (),
2014-10-28 05:00:10 +00:00
( _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + LOG_FILE_NAME ). toStdString (), true );
2011-06-05 14:45:39 +00:00
ULogger :: setPrintTime ( _preferencesDialog -> getGeneralLoggerPrintTime ());
2016-01-05 13:55:21 -05:00
ULogger :: setPrintThreadId ( _preferencesDialog -> getGeneralLoggerPrintThreadId ());
2016-11-14 19:54:31 -05:00
ULogger :: setTreadIdFilter ( _preferencesDialog -> getGeneralLoggerThreads ());
2011-06-05 14:45:39 +00:00
}
}
void MainWindow :: applyPrefSettings ( const rtabmap :: ParametersMap & parameters )
2015-05-03 18:16:55 -04:00
{
applyPrefSettings ( parameters , true ); //post parameters
}
void MainWindow :: applyPrefSettings ( const rtabmap :: ParametersMap & parameters , bool postParamEvent )
2011-06-05 14:45:39 +00:00
{
ULOGGER_DEBUG ( "" );
if ( parameters . size ())
{
2013-12-11 00:12:44 +00:00
for ( rtabmap :: ParametersMap :: const_iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
UDEBUG ( "Parameter changed: Key=%s Value=%s" , iter -> first . c_str (), iter -> second . c_str ());
}
2012-06-24 17:19:34 +00:00
rtabmap :: ParametersMap parametersModified = parameters ;
2016-04-13 11:58:54 -04:00
if ( uContains ( parameters , Parameters :: kRtabmapWorkingDirectory ()))
{
_ui -> statsToolBox -> setWorkingDirectory ( _preferencesDialog -> getWorkingDirectory ());
_ui -> graphicsView_graphView -> setWorkingDirectory ( _preferencesDialog -> getWorkingDirectory ());
2017-08-07 16:31:49 -04:00
_exportBundlerDialog -> setWorkingDirectory ( _preferencesDialog -> getWorkingDirectory ());
2016-04-13 11:58:54 -04:00
}
2014-10-28 01:20:57 +00:00
if ( _state != kIdle && parametersModified . size ())
2011-06-05 14:45:39 +00:00
{
2015-05-03 18:16:55 -04:00
if ( postParamEvent )
{
this -> post ( new ParamEvent ( parametersModified ));
}
2011-06-05 14:45:39 +00:00
}
2014-10-28 01:20:57 +00:00
2015-11-22 18:08:32 -05:00
// update loop closure viewer parameters (Use Map parameters)
2016-05-05 18:25:26 -04:00
_loopClosureViewer -> setDecimation ( _preferencesDialog -> getCloudDecimation ( 0 ));
_loopClosureViewer -> setMaxDepth ( _preferencesDialog -> getCloudMaxDepth ( 0 ));
2015-05-03 18:16:55 -04:00
// update graph view parameters
if ( uContains ( parameters , Parameters :: kRGBDLocalRadius ()))
{
_ui -> graphicsView_graphView -> setLocalRadius ( uStr2Float ( parameters . at ( Parameters :: kRGBDLocalRadius ())));
}
2011-06-05 14:45:39 +00:00
}
//update ui
2013-12-11 00:12:44 +00:00
_ui -> doubleSpinBox_stats_detectionRate -> setValue ( _preferencesDialog -> getDetectionRate ());
2011-06-05 14:45:39 +00:00
_ui -> doubleSpinBox_stats_timeLimit -> setValue ( _preferencesDialog -> getTimeLimit ());
2013-12-11 00:12:44 +00:00
_ui -> actionSLAM_mode -> setChecked ( _preferencesDialog -> isSLAMMode ());
2011-06-05 14:45:39 +00:00
2024-11-03 03:51:05 +08:00
Q_EMIT ( loopClosureThrChanged ( 1.0 - _preferencesDialog -> getLoopThr ()));
2011-06-05 14:45:39 +00:00
}
void MainWindow :: drawKeypoints ( const std :: multimap < int , cv :: KeyPoint > & refWords , const std :: multimap < int , cv :: KeyPoint > & loopWords )
{
UTimer timer ;
timer . start ();
ULOGGER_DEBUG ( "refWords.size() = %d" , refWords . size ());
2025-11-16 18:56:55 -08:00
_ui -> imageView_source -> clearFeatures ();
if ( _ui -> imageView_source -> isFeaturesShown ())
2016-11-27 20:55:53 -05:00
{
2025-11-16 18:56:55 -08:00
for ( std :: multimap < int , cv :: KeyPoint >:: const_iterator iter = refWords . begin (); iter != refWords . end (); ++ iter )
{
int id = iter -> first ;
QColor color ;
if ( id < 0 )
{
// GRAY = NOT QUANTIZED
color = Qt :: gray ;
}
else if ( uContains ( loopWords , id ))
{
// PINK = FOUND IN LOOP SIGNATURE
color = Qt :: magenta ;
}
else if ( _lastIds . contains ( id ))
{
// BLUE = FOUND IN LAST SIGNATURE
color = Qt :: blue ;
}
else if ( id <= _lastId )
{
// RED = ALREADY EXISTS
color = Qt :: red ;
}
else if ( refWords . count ( id ) > 1 )
{
// YELLOW = NEW and multiple times
color = Qt :: yellow ;
}
else
{
// GREEN = NEW
color = Qt :: green ;
}
_ui -> imageView_source -> addFeature ( iter -> first , iter -> second , 0 , color );
}
2016-11-27 20:55:53 -05:00
}
2025-11-16 18:56:55 -08:00
ULOGGER_DEBUG ( "source time (shown=%d) = %f s" , _ui -> imageView_source -> isFeaturesShown () ? 1 : 0 , timer . ticks ());
2011-06-05 14:45:39 +00:00
timer . start ();
ULOGGER_DEBUG ( "loopWords.size() = %d" , loopWords . size ());
2015-03-30 10:47:38 -04:00
QList < QPair < cv :: Point2f , cv :: Point2f > > uniqueCorrespondences ;
2025-11-16 18:56:55 -08:00
_ui -> imageView_loopClosure -> clearFeatures ();
if ( _ui -> imageView_loopClosure -> isFeaturesShown ())
2016-11-27 20:55:53 -05:00
{
2025-11-16 18:56:55 -08:00
for ( std :: multimap < int , cv :: KeyPoint >:: const_iterator iter = loopWords . begin (); iter != loopWords . end (); ++ iter )
2017-02-23 11:49:33 -05:00
{
2025-11-16 18:56:55 -08:00
int id = iter -> first ;
QColor color ;
if ( id < 0 )
2012-04-07 19:08:54 +00:00
{
2025-11-16 18:56:55 -08:00
// GRAY = NOT QUANTIZED
color = Qt :: gray ;
}
else if ( uContains ( refWords , id ))
{
// PINK = FOUND IN LOOP SIGNATURE
color = Qt :: magenta ;
//To draw lines... get only unique correspondences
if ( uValues ( refWords , id ). size () == 1 && uValues ( loopWords , id ). size () == 1 )
{
const cv :: KeyPoint & a = refWords . find ( id ) -> second ;
const cv :: KeyPoint & b = iter -> second ;
uniqueCorrespondences . push_back ( QPair < cv :: Point2f , cv :: Point2f > ( a . pt , b . pt ));
}
}
else if ( id <= _lastId )
{
// RED = ALREADY EXISTS
color = Qt :: red ;
}
else if ( refWords . count ( id ) > 1 )
{
// YELLOW = NEW and multiple times
color = Qt :: yellow ;
}
else
{
// GREEN = NEW
color = Qt :: green ;
}
_ui -> imageView_loopClosure -> addFeature ( iter -> first , iter -> second , 0 , color );
}
}
else if ( _ui -> imageView_source -> isLinesShown () && _ui -> imageView_loopClosure -> isLinesShown ())
{
for ( std :: multimap < int , cv :: KeyPoint >:: const_iterator iter = loopWords . begin (); iter != loopWords . end (); ++ iter )
{
int id = iter -> first ;
if ( id >= 0 && uContains ( refWords , id ))
{
//To draw lines... get only unique correspondences
if ( uValues ( refWords , id ). size () == 1 && uValues ( loopWords , id ). size () == 1 )
{
const cv :: KeyPoint & a = refWords . find ( id ) -> second ;
const cv :: KeyPoint & b = iter -> second ;
uniqueCorrespondences . push_back ( QPair < cv :: Point2f , cv :: Point2f > ( a . pt , b . pt ));
}
2012-04-07 19:08:54 +00:00
}
2011-06-05 14:45:39 +00:00
}
}
2025-11-16 18:56:55 -08:00
ULOGGER_DEBUG ( "loop closure time (shown=%d) = %f s" , _ui -> imageView_loopClosure -> isFeaturesShown () ? 1 : 0 , timer . ticks ());
2015-03-30 10:47:38 -04:00
2025-11-16 18:56:55 -08:00
_lastIds . clear ();
2011-06-05 14:45:39 +00:00
if ( refWords . size () > 0 )
{
if (( * refWords . rbegin ()). first > _lastId )
{
_lastId = ( * refWords . rbegin ()). first ;
}
2023-03-05 16:34:56 -08:00
std :: list < int > kpts = uKeysList ( refWords );
2024-06-02 15:04:36 -07:00
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3)
2023-03-05 16:34:56 -08:00
_lastIds = QSet < int > ( kpts . begin (), kpts . end ());
#else
_lastIds = QSet < int >:: fromList ( QList < int >:: fromStdList ( kpts ));
#endif
2011-06-05 14:45:39 +00:00
}
2012-04-07 19:08:54 +00:00
2025-11-16 18:56:55 -08:00
_ui -> imageView_source -> clearLines ();
_ui -> imageView_loopClosure -> clearLines ();
if ( _ui -> imageView_source -> isLinesShown () && _ui -> imageView_loopClosure -> isLinesShown ())
2012-04-07 19:08:54 +00:00
{
2025-11-16 18:56:55 -08:00
// Draw lines between corresponding features...
float scaleSource = _ui -> imageView_source -> viewScale ();
float scaleLoop = _ui -> imageView_loopClosure -> viewScale ();
UDEBUG ( "scale source=%f loop=%f" , scaleSource , scaleLoop );
// Delta in actual window pixels
float sourceMarginX = ( _ui -> imageView_source -> width () - _ui -> imageView_source -> sceneRect (). width () * scaleSource ) / 2.0f ;
float sourceMarginY = ( _ui -> imageView_source -> height () - _ui -> imageView_source -> sceneRect (). height () * scaleSource ) / 2.0f ;
float loopMarginX = ( _ui -> imageView_loopClosure -> width () - _ui -> imageView_loopClosure -> sceneRect (). width () * scaleLoop ) / 2.0f ;
float loopMarginY = ( _ui -> imageView_loopClosure -> height () - _ui -> imageView_loopClosure -> sceneRect (). height () * scaleLoop ) / 2.0f ;
2015-05-26 14:15:04 -04:00
2025-11-16 18:56:55 -08:00
float deltaX = 0 ;
float deltaY = 0 ;
2016-11-27 20:55:53 -05:00
2025-11-16 18:56:55 -08:00
if ( _preferencesDialog -> isVerticalLayoutUsed ())
{
deltaY = _ui -> label_matchId -> height () + _ui -> imageView_source -> height ();
}
else
{
deltaX = _ui -> imageView_source -> width ();
}
2015-05-26 14:15:04 -04:00
2025-11-16 18:56:55 -08:00
for ( QList < QPair < cv :: Point2f , cv :: Point2f > >:: iterator iter = uniqueCorrespondences . begin ();
iter != uniqueCorrespondences . end ();
++ iter )
{
2012-04-07 19:08:54 +00:00
2025-11-16 18:56:55 -08:00
_ui -> imageView_source -> addLine (
iter -> first . x ,
iter -> first . y ,
( iter -> second . x * scaleLoop + loopMarginX + deltaX - sourceMarginX ) / scaleSource ,
( iter -> second . y * scaleLoop + loopMarginY + deltaY - sourceMarginY ) / scaleSource ,
_ui -> imageView_source -> getDefaultMatchingLineColor ());
_ui -> imageView_loopClosure -> addLine (
( iter -> first . x * scaleSource + sourceMarginX - deltaX - loopMarginX ) / scaleLoop ,
( iter -> first . y * scaleSource + sourceMarginY - deltaY - loopMarginY ) / scaleLoop ,
iter -> second . x ,
iter -> second . y ,
_ui -> imageView_loopClosure -> getDefaultMatchingLineColor ());
}
2012-04-07 19:08:54 +00:00
}
2015-03-30 10:47:38 -04:00
_ui -> imageView_source -> update ();
_ui -> imageView_loopClosure -> update ();
2011-06-05 14:45:39 +00:00
}
2019-02-18 18:19:55 -05:00
void MainWindow :: drawLandmarks ( cv :: Mat & image , const Signature & signature )
{
2026-05-26 17:43:34 -07:00
UDEBUG ( "%ld landmarks" , signature . getLandmarks (). size ());
2019-02-18 18:19:55 -05:00
for ( std :: map < int , Link >:: const_iterator iter = signature . getLandmarks (). begin (); iter != signature . getLandmarks (). end (); ++ iter )
{
2022-09-24 12:29:42 -07:00
// Project in all cameras in which the landmark is visible
for ( size_t i = 0 ; i < signature . sensorData (). cameraModels (). size () || i < signature . sensorData (). stereoCameraModels (). size (); ++ i )
2019-02-18 18:19:55 -05:00
{
2022-09-24 12:29:42 -07:00
CameraModel model ;
if ( i < signature . sensorData (). cameraModels (). size ())
{
model = signature . sensorData (). cameraModels ()[ i ];
}
else if ( i < signature . sensorData (). stereoCameraModels (). size ())
{
model = signature . sensorData (). stereoCameraModels ()[ i ]. left ();
}
if ( model . isValidForProjection ())
{
Transform t = model . localTransform (). inverse () * iter -> second . transform ();
cv :: Vec3d rvec , tvec ;
tvec . val [ 0 ] = t . x ();
tvec . val [ 1 ] = t . y ();
tvec . val [ 2 ] = t . z ();
2019-02-18 18:19:55 -05:00
2022-09-24 12:29:42 -07:00
// In front of the camera?
if ( t . z () > 0 )
{
cv :: Mat R ;
t . rotationMatrix (). convertTo ( R , CV_64F );
cv :: Rodrigues ( R , rvec );
2019-02-18 18:19:55 -05:00
2022-09-24 12:29:42 -07:00
//cv::aruco::drawAxis(image, model.K(), model.D(), rvec, tvec, _preferencesDialog->getMarkerLength()<=0?0.1:_preferencesDialog->getMarkerLength() * 0.5f);
// project axis points
std :: vector < cv :: Point3f > axisPoints ;
float length = _preferencesDialog -> getMarkerLength () <= 0 ? 0.1 : _preferencesDialog -> getMarkerLength () * 0.5f ;
axisPoints . push_back ( cv :: Point3f ( 0 , 0 , 0 ));
axisPoints . push_back ( cv :: Point3f ( length , 0 , 0 ));
axisPoints . push_back ( cv :: Point3f ( 0 , length , 0 ));
axisPoints . push_back ( cv :: Point3f ( 0 , 0 , length ));
std :: vector < cv :: Point2f > imagePoints ;
projectPoints ( axisPoints , rvec , tvec , model . K (), model . D (), imagePoints );
//offset x based on camera index
bool valid = true ;
if ( i != 0 )
{
if ( model . imageWidth () <= 0 )
{
valid = false ;
UWARN ( "Cannot draw correctly landmark %d with provided camera model %d (missing image width)" , - iter -> first , ( int ) i );
}
else
{
for ( int j = 0 ; j < 4 ; ++ j )
{
imagePoints [ j ]. x += i * model . imageWidth ();
}
2026-05-26 17:43:34 -07:00
// Make sure the frame origin is visible
valid = imagePoints [ 0 ]. x >= i * model . imageWidth () && imagePoints [ 0 ]. x < ( i + 1 ) * model . imageWidth () &&
imagePoints [ 0 ]. y >= 0 && imagePoints [ 0 ]. y < image . rows ;
2022-09-24 12:29:42 -07:00
}
}
if ( valid )
{
// draw axis lines
cv :: line ( image , imagePoints [ 0 ], imagePoints [ 1 ], cv :: Scalar ( 0 , 0 , 255 ), 3 );
cv :: line ( image , imagePoints [ 0 ], imagePoints [ 2 ], cv :: Scalar ( 0 , 255 , 0 ), 3 );
cv :: line ( image , imagePoints [ 0 ], imagePoints [ 3 ], cv :: Scalar ( 255 , 0 , 0 ), 3 );
cv :: putText ( image , uNumber2Str ( - iter -> first ), imagePoints [ 0 ], cv :: FONT_HERSHEY_SIMPLEX , 0.75 , cv :: Scalar ( 0 , 255 , 255 ), 2 );
}
}
}
2019-02-18 18:19:55 -05:00
}
}
}
2015-01-23 11:17:42 -05:00
void MainWindow :: showEvent ( QShowEvent * anEvent )
{
2015-04-25 17:30:15 -04:00
//if the config file doesn't exist, make the GUI obsolete
this -> setWindowModified ( ! QFile :: exists ( _preferencesDialog -> getIniFilePath ()));
2015-01-23 11:17:42 -05:00
}
void MainWindow :: moveEvent ( QMoveEvent * anEvent )
{
if ( this -> isVisible ())
{
// HACK, there is a move event when the window is shown the first time.
2015-03-04 17:27:44 -05:00
if ( ! _firstCall )
2015-01-23 11:17:42 -05:00
{
this -> configGUIModified ();
}
2015-03-04 17:27:44 -05:00
_firstCall = false ;
2015-01-23 11:17:42 -05:00
}
}
2011-06-05 14:45:39 +00:00
void MainWindow :: resizeEvent ( QResizeEvent * anEvent )
{
2015-01-23 11:17:42 -05:00
if ( this -> isVisible ())
{
this -> configGUIModified ();
}
2011-06-05 14:45:39 +00:00
}
2017-04-26 18:39:57 -04:00
void MainWindow :: keyPressEvent ( QKeyEvent * event )
{
//catch ctrl-s to save settings
if (( event -> modifiers () & Qt :: ControlModifier ) && event -> key () == Qt :: Key_S )
{
this -> saveConfigGUI ();
}
}
2015-02-27 19:33:32 -05:00
bool MainWindow :: eventFilter ( QObject * obj , QEvent * event )
{
if ( event -> type () == QEvent :: Resize && qobject_cast < QDockWidget *> ( obj ))
{
this -> setWindowModified ( true );
}
2016-04-10 14:52:14 -04:00
else if ( event -> type () == QEvent :: FileOpen )
{
openDatabase ((( QFileOpenEvent * ) event ) -> file ());
}
2015-02-27 19:33:32 -05:00
return QWidget :: eventFilter ( obj , event );
}
2015-03-01 19:41:03 -05:00
void MainWindow :: updateSelectSourceMenu ()
2011-06-05 14:45:39 +00:00
{
2015-06-26 18:21:32 -04:00
_ui -> actionMore_options -> setChecked (
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcDatabase ||
2024-02-26 19:06:39 -08:00
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcUsbDevice ||
2015-06-26 18:21:32 -04:00
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcVideo ||
2016-05-31 19:09:49 -04:00
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoVideo ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRGBDImages
);
2011-06-05 14:45:39 +00:00
2015-06-26 18:21:32 -04:00
_ui -> actionOpenNI_PCL -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI_PCL );
_ui -> actionOpenNI_PCL_ASUS -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI_PCL );
_ui -> actionFreenect -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcFreenect );
_ui -> actionOpenNI_CV -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI_CV );
_ui -> actionOpenNI_CV_ASUS -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI_CV_ASUS );
_ui -> actionOpenNI2 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI2 );
_ui -> actionOpenNI2_kinect -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI2 );
2021-12-29 15:53:40 -05:00
_ui -> actionOpenNI2_orbbec -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI2 );
2015-06-26 18:21:32 -04:00
_ui -> actionOpenNI2_sense -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOpenNI2 );
_ui -> actionFreenect2 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcFreenect2 );
2017-11-06 00:19:12 -05:00
_ui -> actionKinect_for_Windows_SDK_v2 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcK4W2 );
2021-01-08 13:04:24 -05:00
_ui -> actionKinect_for_Azure -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcK4A );
2017-04-19 15:24:42 -04:00
_ui -> actionRealSense_R200 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRealSense );
_ui -> actionRealSense_ZR300 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRealSense );
2019-07-05 11:19:45 -04:00
_ui -> actionRealSense2_SR300 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRealSense2 );
2021-01-07 23:51:06 -05:00
_ui -> actionRealSense2_D400 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRealSense2 );
2020-12-11 09:56:17 -05:00
_ui -> actionRealSense2_L515 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRealSense2 );
2015-06-26 18:21:32 -04:00
_ui -> actionStereoDC1394 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcDC1394 );
_ui -> actionStereoFlyCapture2 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcFlyCapture2 );
2016-05-31 19:09:49 -04:00
_ui -> actionStereoZed -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoZed );
2021-02-28 10:31:18 -05:00
_ui -> actionZed_Open_Capture -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoZedOC );
2018-10-31 20:27:07 +05:30
_ui -> actionStereoTara -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoTara );
2016-05-31 19:09:49 -04:00
_ui -> actionStereoUsb -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoUsb );
2019-05-13 18:42:46 -04:00
_ui -> actionRealSense2_T265 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoRealSense2 );
2020-04-30 01:00:17 -04:00
_ui -> actionMYNT_EYE_S_SDK -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoMyntEye );
2022-10-23 18:15:23 -07:00
_ui -> actionDepthAI_oakd -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoDepthAI );
_ui -> actionDepthAI_oakdlite -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoDepthAI );
2024-03-30 18:08:32 -07:00
_ui -> actionDepthAI_oakdpro -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoDepthAI );
2024-06-09 11:13:29 -07:00
_ui -> actionXvisio_SeerSense -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcSeerSense );
2025-10-07 10:22:16 -07:00
_ui -> actionOrbbecSDK_astra2 -> setChecked ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcOrbbecSDK );
2024-04-14 19:06:04 -07:00
_ui -> actionVelodyne_VLP_16 -> setChecked ( _preferencesDialog -> getLidarSourceDriver () == PreferencesDialog :: kSrcLidarVLP16 );
2013-12-11 00:12:44 +00:00
}
2011-06-05 14:45:39 +00:00
void MainWindow :: changeImgRateSetting ()
{
2018-06-15 12:17:11 -04:00
Q_EMIT imgRateChanged ( _ui -> doubleSpinBox_stats_imgRate -> value ());
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
void MainWindow :: changeDetectionRateSetting ()
{
2018-06-15 12:17:11 -04:00
Q_EMIT detectionRateChanged ( _ui -> doubleSpinBox_stats_detectionRate -> value ());
2013-12-11 00:12:44 +00:00
}
2011-06-05 14:45:39 +00:00
void MainWindow :: changeTimeLimitSetting ()
{
2018-06-15 12:17:11 -04:00
Q_EMIT timeLimitChanged (( float ) _ui -> doubleSpinBox_stats_timeLimit -> value ());
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
void MainWindow :: changeMappingMode ()
{
2018-06-15 12:17:11 -04:00
Q_EMIT mappingModeChanged ( _ui -> actionSLAM_mode -> isChecked ());
2013-12-11 00:12:44 +00:00
}
2017-12-11 17:09:04 -05:00
QString MainWindow :: captureScreen ( bool cacheInRAM , bool png )
2011-06-05 14:45:39 +00:00
{
2017-12-11 17:09:04 -05:00
QString name = ( QDateTime :: currentDateTime (). toString ( "yyMMddhhmmsszzz" ) + ( png ? ".png" : ".jpg" ));
2011-06-05 14:45:39 +00:00
_ui -> statusbar -> clearMessage ();
2023-03-05 16:34:56 -08:00
QPixmap figure = this -> grab ();
2015-09-04 13:46:37 -04:00
QString targetDir = _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + "ScreensCaptured" ;
QString msg ;
if ( cacheInRAM )
{
msg = tr ( "Screen captured \" %1 \" " ). arg ( name );
QByteArray bytes ;
QBuffer buffer ( & bytes );
buffer . open ( QIODevice :: WriteOnly );
2017-12-11 17:09:04 -05:00
figure . save ( & buffer , png ? "PNG" : "JPEG" );
2015-09-04 13:46:37 -04:00
_autoScreenCaptureCachedImages . insert ( name , bytes );
}
else
{
QDir dir ;
if ( ! dir . exists ( targetDir ))
{
dir . mkdir ( targetDir );
}
targetDir += QDir :: separator ();
targetDir += "Main_window" ;
if ( ! dir . exists ( targetDir ))
{
dir . mkdir ( targetDir );
}
targetDir += QDir :: separator ();
figure . save ( targetDir + name );
msg = tr ( "Screen captured \" %1 \" " ). arg ( targetDir + name );
}
2014-02-11 23:04:22 +00:00
_ui -> statusbar -> showMessage ( msg , _preferencesDialog -> getTimeLimit () * 500 );
_ui -> widget_console -> appendMsg ( msg );
2015-08-04 14:16:29 -04:00
return targetDir + name ;
2011-06-05 14:45:39 +00:00
}
2012-06-24 17:19:34 +00:00
void MainWindow :: beep ()
{
QApplication :: beep ();
}
2011-06-05 14:45:39 +00:00
2016-09-19 14:01:06 -04:00
void MainWindow :: cancelProgress ()
{
_progressCanceled = true ;
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Canceled!" ));
2016-09-19 14:01:06 -04:00
}
2015-01-23 11:17:42 -05:00
void MainWindow :: configGUIModified ()
{
this -> setWindowModified ( true );
}
2016-11-16 17:39:17 -05:00
void MainWindow :: updateParameters ( const ParametersMap & parameters )
{
if ( parameters . size ())
{
for ( ParametersMap :: const_iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
QString msg = tr ( "Parameter update \" %1 \" = \" %2 \" " )
. arg ( iter -> first . c_str ())
. arg ( iter -> second . c_str ());
_ui -> widget_console -> appendMsg ( msg );
UWARN ( msg . toStdString (). c_str ());
}
2017-04-28 11:43:27 -04:00
QMessageBox :: StandardButton button = QMessageBox :: question ( this ,
tr ( "Parameters" ),
tr ( "Some parameters have been set on command line, do you "
"want to set all other RTAB-Map's parameters to default?" ),
QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: No );
_preferencesDialog -> updateParameters ( parameters , button == QMessageBox :: Yes );
2016-11-16 17:39:17 -05:00
}
}
2011-06-05 14:45:39 +00:00
//ACTIONS
2015-01-23 11:17:42 -05:00
void MainWindow :: saveConfigGUI ()
{
2015-01-30 15:30:57 -05:00
_savedMaximized = this -> isMaximized ();
2015-01-23 11:17:42 -05:00
_preferencesDialog -> saveMainWindowState ( this );
_preferencesDialog -> saveWindowGeometry ( _preferencesDialog );
_preferencesDialog -> saveWindowGeometry ( _aboutDialog );
2016-05-05 18:25:26 -04:00
_preferencesDialog -> saveWidgetState ( _cloudViewer );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> saveWidgetState ( _ui -> imageView_source );
_preferencesDialog -> saveWidgetState ( _ui -> imageView_loopClosure );
_preferencesDialog -> saveWidgetState ( _ui -> imageView_odometry );
2016-03-14 17:27:53 -04:00
_preferencesDialog -> saveWidgetState ( _exportCloudsDialog );
2017-08-07 16:31:49 -04:00
_preferencesDialog -> saveWidgetState ( _exportBundlerDialog );
2015-01-23 11:17:42 -05:00
_preferencesDialog -> saveWidgetState ( _postProcessingDialog );
2016-09-19 14:01:06 -04:00
_preferencesDialog -> saveWidgetState ( _depthCalibrationDialog );
2015-01-30 15:30:57 -05:00
_preferencesDialog -> saveWidgetState ( _ui -> graphicsView_graphView );
2022-02-12 13:35:23 -05:00
_preferencesDialog -> saveWidgetState ( _multiSessionLocWidget -> getImageView ());
2015-02-13 16:50:32 -05:00
_preferencesDialog -> saveSettings ();
2015-05-03 18:16:55 -04:00
this -> saveFigures ();
2015-01-23 11:17:42 -05:00
this -> setWindowModified ( false );
}
2014-10-28 01:20:57 +00:00
void MainWindow :: newDatabase ()
{
if ( _state != MainWindow :: kIdle )
{
UERROR ( "This method can be called only in IDLE state." );
return ;
}
_openedDatabasePath . clear ();
2015-02-18 18:22:59 -05:00
_newDatabasePath . clear ();
_newDatabasePathOutput . clear ();
_databaseUpdated = false ;
2020-03-10 10:30:42 -04:00
_cloudViewer -> removeLine ( "map_to_odom" );
_cloudViewer -> removeLine ( "odom_to_base_link" );
_cloudViewer -> removeCoordinate ( "odom_frame" );
2020-04-03 20:04:15 -04:00
_cloudViewer -> removeCoordinate ( "map_frame" );
2022-10-04 16:27:33 -07:00
_cloudViewer -> removeText ( "map_frame_label" );
_cloudViewer -> removeText ( "odom_frame_label" );
2014-10-28 01:20:57 +00:00
ULOGGER_DEBUG ( "" );
this -> clearTheCache ();
2015-03-01 19:41:03 -05:00
std :: string databasePath = ( _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + QString ( "rtabmap.tmp.db" )). toStdString ();
2014-10-28 01:20:57 +00:00
if ( QFile :: exists ( databasePath . c_str ()))
{
2015-03-01 19:41:03 -05:00
int r = QMessageBox :: question ( this ,
tr ( "Creating temporary database" ),
tr ( "Cannot create a new database because the temporary database \" %1 \" already exists. "
"There may be another instance of RTAB-Map running with the same Working Directory or "
"the last time RTAB-Map was not closed correctly. "
2017-10-11 19:36:00 -04:00
"Do you want to recover the database (click Ignore to delete it and create a new one)?" ). arg ( databasePath . c_str ()),
QMessageBox :: Yes | QMessageBox :: No | QMessageBox :: Ignore , QMessageBox :: No );
2015-03-01 19:41:03 -05:00
2017-10-11 19:36:00 -04:00
if ( r == QMessageBox :: Ignore )
2014-10-28 01:20:57 +00:00
{
2015-03-01 19:41:03 -05:00
if ( QFile :: remove ( databasePath . c_str ()))
{
UINFO ( "Deleted temporary database \" %s \" ." , databasePath . c_str ());
}
else
{
UERROR ( "Temporary database \" %s \" could not be deleted!" , databasePath . c_str ());
return ;
}
2014-10-28 01:20:57 +00:00
}
2017-10-11 19:36:00 -04:00
else if ( r == QMessageBox :: Yes )
{
std :: string errorMsg ;
rtabmap :: ProgressDialog * progressDialog = new rtabmap :: ProgressDialog ( this );
progressDialog -> setAttribute ( Qt :: WA_DeleteOnClose );
progressDialog -> setMaximumSteps ( 100 );
progressDialog -> show ();
progressDialog -> setCancelButtonVisible ( true );
RecoveryState state ( progressDialog );
_recovering = true ;
if ( databaseRecovery ( databasePath , false , & errorMsg , & state ))
{
_recovering = false ;
progressDialog -> setValue ( progressDialog -> maximumSteps ());
QString newPath = QFileDialog :: getSaveFileName ( this , tr ( "Save recovered database" ), _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + QString ( "recovered.db" ), tr ( "RTAB-Map database files (*.db)" ));
if ( newPath . isEmpty ())
{
return ;
}
if ( QFileInfo ( newPath ). suffix () == "" )
{
newPath += ".db" ;
}
if ( QFile :: exists ( newPath ))
{
QFile :: remove ( newPath );
}
QFile :: rename ( databasePath . c_str (), newPath );
return ;
}
else
{
_recovering = false ;
progressDialog -> setValue ( progressDialog -> maximumSteps ());
2017-10-24 11:34:39 +02:00
QMessageBox :: warning ( this , "Database recovery" , tr ( "Database recovery failed: \" %1 \" ." ). arg ( errorMsg . c_str ()));
2017-10-11 19:36:00 -04:00
return ;
}
}
2014-10-28 01:20:57 +00:00
else
{
return ;
}
}
2015-02-18 18:22:59 -05:00
_newDatabasePath = databasePath . c_str ();
2015-08-01 23:42:36 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdInit , databasePath , _preferencesDialog -> getAllParameters ()));
2015-05-03 18:16:55 -04:00
applyPrefSettings ( _preferencesDialog -> getAllParameters (), false );
2014-10-28 01:20:57 +00:00
}
void MainWindow :: openDatabase ()
{
2020-05-19 15:01:50 -04:00
QString path = QFileDialog :: getOpenFileName ( this , tr ( "Open database..." ), _defaultOpenDatabasePath . isEmpty () ? _preferencesDialog -> getWorkingDirectory () : _defaultOpenDatabasePath , tr ( "RTAB-Map database files (*.db)" ));
2014-10-28 01:20:57 +00:00
if ( ! path . isEmpty ())
{
2016-04-10 14:52:14 -04:00
this -> openDatabase ( path );
}
}
2018-07-27 16:01:17 -04:00
void MainWindow :: openDatabase ( const QString & path , const ParametersMap & overridedParameters )
2016-04-10 14:52:14 -04:00
{
if ( _state != MainWindow :: kIdle )
{
UERROR ( "Database can only be opened in IDLE state." );
return ;
}
std :: string value = path . toStdString ();
if ( UFile :: exists ( value ) &&
UFile :: getExtension ( value ). compare ( "db" ) == 0 )
{
_openedDatabasePath . clear ();
_newDatabasePath . clear ();
_newDatabasePathOutput . clear ();
_databaseUpdated = false ;
2014-10-28 01:20:57 +00:00
this -> clearTheCache ();
_openedDatabasePath = path ;
2020-05-19 15:01:50 -04:00
_defaultOpenDatabasePath = path ;
2016-06-13 17:34:17 -04:00
// look if there are saved parameters
DBDriver * driver = DBDriver :: create ();
if ( driver -> openConnection ( value , false ))
{
ParametersMap parameters = driver -> getLastParameters ();
driver -> closeConnection ( false );
delete driver ;
if ( parameters . size ())
{
2016-11-18 16:59:14 -05:00
//backward compatibility with databases not saving all parameters, use default for not saved ones
for ( ParametersMap :: const_iterator iter = Parameters :: getDefaultParameters (). begin (); iter != Parameters :: getDefaultParameters (). end (); ++ iter )
{
parameters . insert ( * iter );
}
2018-07-27 16:01:17 -04:00
uInsert ( parameters , overridedParameters );
2016-06-13 17:34:17 -04:00
ParametersMap currentParameters = _preferencesDialog -> getAllParameters ();
ParametersMap differentParameters ;
for ( ParametersMap :: iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
ParametersMap :: iterator jter = currentParameters . find ( iter -> first );
if ( jter != currentParameters . end () &&
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 ;
}
}
2019-11-03 14:10:02 -05:00
else if ( Parameters :: getType ( iter -> first ). compare ( "bool" ) == 0 )
{
if ( uStr2Bool ( iter -> second ) == uStr2Bool ( jter -> second ))
{
different = false ;
}
}
2016-08-26 12:34:19 -04:00
if ( different )
{
differentParameters . insert ( * iter );
2022-10-04 16:27:33 -07:00
QString msg = tr ( "Parameter \" %1 \" : %2= \" %3 \" Preferences= \" %4 \" " )
2016-08-26 12:34:19 -04:00
. arg ( iter -> first . c_str ())
2022-10-04 16:27:33 -07:00
. arg ( overridedParameters . find ( iter -> first ) != overridedParameters . end () ? "Arguments" : "Database" )
2016-08-26 12:34:19 -04:00
. arg ( iter -> second . c_str ())
. arg ( jter -> second . c_str ());
_ui -> widget_console -> appendMsg ( msg );
UWARN ( msg . toStdString (). c_str ());
}
2016-06-13 17:34:17 -04:00
}
}
if ( differentParameters . size ())
{
int r = QMessageBox :: question ( this ,
tr ( "Update parameters..." ),
2022-10-04 16:27:33 -07:00
tr ( "The %1 using %2 different parameter(s) than "
2016-06-13 17:34:17 -04:00
"those currently set in Preferences. Do you want "
2022-10-04 16:27:33 -07:00
"to use those parameters?" )
. arg ( overridedParameters . empty () ? tr ( "database is" ) : tr ( "database and input arguments are" ))
. arg ( differentParameters . size ()),
2016-06-13 17:34:17 -04:00
QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: Yes );
if ( r == QMessageBox :: Yes )
{
2016-07-20 17:07:35 -04:00
_preferencesDialog -> updateParameters ( differentParameters );
2016-06-13 17:34:17 -04:00
}
}
}
}
2016-04-10 14:52:14 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdInit , value , 0 , _preferencesDialog -> getAllParameters ()));
applyPrefSettings ( _preferencesDialog -> getAllParameters (), false );
}
else
{
UERROR ( "File \" %s \" not valid." , value . c_str ());
2014-10-28 01:20:57 +00:00
}
}
2015-02-18 18:22:59 -05:00
bool MainWindow :: closeDatabase ()
2014-10-28 01:20:57 +00:00
{
if ( _state != MainWindow :: kInitialized )
{
UERROR ( "This method can be called only in INITIALIZED state." );
2015-02-18 18:22:59 -05:00
return false ;
2014-10-28 01:20:57 +00:00
}
2015-02-18 18:22:59 -05:00
_newDatabasePathOutput . clear ();
if ( ! _newDatabasePath . isEmpty () && _databaseUpdated )
{
QMessageBox :: Button b = QMessageBox :: question ( this ,
tr ( "Save database" ),
tr ( "Save the new database?" ),
QMessageBox :: Save | QMessageBox :: Cancel | QMessageBox :: Discard ,
QMessageBox :: Save );
if ( b == QMessageBox :: Save )
{
// Temp database used, automatically backup with unique name (timestamp)
QString newName = QDateTime :: currentDateTime (). toString ( "yyMMdd-hhmmss" );
2015-05-15 12:39:56 -04:00
QString newPath = _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + newName + ".db" ;
2015-02-18 18:22:59 -05:00
2015-05-15 12:39:56 -04:00
newPath = QFileDialog :: getSaveFileName ( this , tr ( "Save database" ), newPath , tr ( "RTAB-Map database files (*.db)" ));
if ( newPath . isEmpty ())
2015-02-18 18:22:59 -05:00
{
return false ;
}
2015-05-15 12:39:56 -04:00
if ( QFileInfo ( newPath ). suffix () == "" )
{
newPath += ".db" ;
}
_newDatabasePathOutput = newPath ;
2015-02-18 18:22:59 -05:00
}
else if ( b != QMessageBox :: Discard )
{
return false ;
}
}
2016-02-15 19:35:24 -05:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdClose , ! _openedDatabasePath . isEmpty () || ! _newDatabasePathOutput . isEmpty ()));
2015-02-18 18:22:59 -05:00
return true ;
2014-10-28 01:20:57 +00:00
}
void MainWindow :: editDatabase ()
{
if ( _state != MainWindow :: kIdle )
{
UERROR ( "This method can be called only in IDLE state." );
return ;
}
QString path = QFileDialog :: getOpenFileName ( this , tr ( "Edit database..." ), _preferencesDialog -> getWorkingDirectory (), tr ( "RTAB-Map database files (*.db)" ));
if ( ! path . isEmpty ())
{
2017-03-30 15:33:48 -04:00
{
// copy database settings to tmp ini file
QSettings dbSettingsIn ( _preferencesDialog -> getIniFilePath (), QSettings :: IniFormat );
QSettings dbSettingsOut ( _preferencesDialog -> getTmpIniFilePath (), QSettings :: IniFormat );
dbSettingsIn . beginGroup ( "DatabaseViewer" );
dbSettingsOut . beginGroup ( "DatabaseViewer" );
QStringList keys = dbSettingsIn . childKeys ();
for ( QStringList :: iterator iter = keys . begin (); iter != keys . end (); ++ iter )
{
dbSettingsOut . setValue ( * iter , dbSettingsIn . value ( * iter ));
}
dbSettingsIn . endGroup ();
dbSettingsOut . endGroup ();
}
2016-01-12 17:07:38 -05:00
DatabaseViewer * viewer = new DatabaseViewer ( _preferencesDialog -> getTmpIniFilePath (), this );
2015-04-28 11:52:39 -04:00
viewer -> setWindowModality ( Qt :: WindowModal );
viewer -> setAttribute ( Qt :: WA_DeleteOnClose , true );
2015-04-28 13:04:35 -04:00
viewer -> showCloseButton ();
2015-11-30 19:39:05 -05:00
if ( viewer -> isSavedMaximized ())
2014-10-28 01:20:57 +00:00
{
2015-11-30 19:39:05 -05:00
viewer -> showMaximized ();
2014-10-28 01:20:57 +00:00
}
else
{
2015-11-30 19:39:05 -05:00
viewer -> show ();
2014-10-28 01:20:57 +00:00
}
2015-11-30 19:39:05 -05:00
viewer -> openDatabase ( path );
2014-10-28 01:20:57 +00:00
}
}
2011-06-05 14:45:39 +00:00
void MainWindow :: startDetection ()
{
2016-01-07 19:39:21 -05:00
UDEBUG ( "" );
2013-12-11 00:12:44 +00:00
ParametersMap parameters = _preferencesDialog -> getAllParameters ();
2019-12-10 18:48:54 -05:00
uInsert ( parameters , this -> getCustomParameters ());
2013-12-11 00:12:44 +00:00
// verify source with input rates
2015-06-26 18:21:32 -04:00
if ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcVideo ||
2015-07-30 14:17:29 -04:00
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRGBDImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoVideo ||
2015-06-26 18:21:32 -04:00
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcDatabase )
2013-12-11 00:12:44 +00:00
{
float inputRate = _preferencesDialog -> getGeneralInputRate ();
2015-02-04 21:20:17 -05:00
float detectionRate = uStr2Float ( parameters . at ( Parameters :: kRtabmapDetectionRate ()));
int bufferingSize = uStr2Float ( parameters . at ( Parameters :: kRtabmapImageBufferSize ()));
2015-07-19 14:11:09 -04:00
if ((( detectionRate != 0.0f && detectionRate <= inputRate ) || ( detectionRate > 0.0f && inputRate == 0.0f )) &&
2016-09-19 14:01:06 -04:00
( _preferencesDialog -> getSourceDriver () != PreferencesDialog :: kSrcDatabase || ! _preferencesDialog -> isSourceDatabaseStampsUsed ()))
2013-12-11 00:12:44 +00:00
{
int button = QMessageBox :: question ( this ,
tr ( "Incompatible frame rates!" ),
2015-07-19 14:11:09 -04:00
tr ( " \" Source/Input rate \" (%1 Hz) is equal to/higher than \" RTAB-Map/Detection rate \" (%2 Hz). As the "
2013-12-11 00:12:44 +00:00
"source input is a directory of images/video/database, some images may be "
"skipped by the detector. You may want to increase the \" RTAB-Map/Detection rate \" over "
"the \" Source/Input rate \" to guaranty that all images are processed. Would you want to "
"start the detection anyway?" ). arg ( inputRate ). arg ( detectionRate ),
QMessageBox :: Yes | QMessageBox :: No );
if ( button == QMessageBox :: No )
{
return ;
}
}
2016-11-16 17:39:17 -05:00
if ( _preferencesDialog -> getSourceDriver () != PreferencesDialog :: kSrcDatabase || ! _preferencesDialog -> isSourceDatabaseStampsUsed ())
2013-12-11 00:12:44 +00:00
{
2016-11-16 17:39:17 -05:00
if ( bufferingSize != 0 )
2013-12-11 00:12:44 +00:00
{
2016-11-16 17:39:17 -05:00
int button = QMessageBox :: question ( this ,
tr ( "Some images may be skipped!" ),
tr ( " \" RTAB-Map/Images buffer size \" is not infinite (size=%1). As the "
"source input is a directory of images/video/database, some images may be "
"skipped by the detector if the \" Source/Input rate \" (which is %2 Hz) is higher than the "
"rate at which RTAB-Map can process the images. You may want to set the "
" \" RTAB-Map/Images buffer size \" to 0 (infinite) to guaranty that all "
"images are processed. Would you want to start the detection "
"anyway?" ). arg ( bufferingSize ). arg ( inputRate ),
QMessageBox :: Yes | QMessageBox :: No );
if ( button == QMessageBox :: No )
{
return ;
}
}
else if ( inputRate == 0 )
{
int button = QMessageBox :: question ( this ,
tr ( "Large number of images may be buffered!" ),
tr ( " \" RTAB-Map/Images buffer size \" is infinite. As the "
"source input is a directory of images/video/database and "
"that \" Source/Input rate \" is infinite too, a lot of images "
"could be buffered at the same time (e.g., reading all images "
"of a directory at once). This could make the GUI not responsive. "
"You may want to set \" Source/Input rate \" at the rate at "
"which the images have been recorded. "
"Would you want to start the detection "
"anyway?" ). arg ( bufferingSize ). arg ( inputRate ),
QMessageBox :: Yes | QMessageBox :: No );
if ( button == QMessageBox :: No )
{
return ;
}
2013-12-11 00:12:44 +00:00
}
}
}
2012-06-24 17:19:34 +00:00
UDEBUG ( "" );
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kStartingDetection );
2011-06-05 14:45:39 +00:00
2024-04-14 19:06:04 -07:00
if ( _sensorCapture != 0 )
2011-06-05 14:45:39 +00:00
{
QMessageBox :: warning ( this ,
tr ( "RTAB-Map" ),
tr ( "A camera is running, stop it first." ));
2024-04-14 19:06:04 -07:00
UWARN ( "_sensorCapture is not null... it must be stopped first" );
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kInitialized );
2012-06-24 17:19:34 +00:00
return ;
}
2011-06-05 14:45:39 +00:00
2012-06-24 17:19:34 +00:00
// Adjust pre-requirements
2024-04-14 19:06:04 -07:00
if ( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcUndef &&
_preferencesDialog -> getLidarSourceDriver () == PreferencesDialog :: kSrcUndef )
2011-06-05 14:45:39 +00:00
{
2012-06-24 17:19:34 +00:00
QMessageBox :: warning ( this ,
tr ( "RTAB-Map" ),
tr ( "No sources are selected. See Preferences->Source panel." ));
UWARN ( "No sources are selected. See Preferences->Source panel." );
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kInitialized );
2012-06-24 17:19:34 +00:00
return ;
2011-06-05 14:45:39 +00:00
}
2012-06-24 17:19:34 +00:00
2013-12-11 00:12:44 +00:00
2021-05-26 13:40:46 -04:00
double poseTimeOffset = 0.0 ;
float scaleFactor = 0.0f ;
2024-04-14 19:06:04 -07:00
double waitTime = 0.1 ;
2021-05-26 13:40:46 -04:00
Transform extrinsics ;
2024-04-14 19:06:04 -07:00
SensorCapture * odomSensor = 0 ;
Camera * camera = 0 ;
Lidar * lidar = 0 ;
if ( _preferencesDialog -> getLidarSourceDriver () != PreferencesDialog :: kSrcUndef )
2015-06-26 18:21:32 -04:00
{
2024-04-14 19:06:04 -07:00
lidar = _preferencesDialog -> createLidar ();
if ( ! lidar )
{
Q_EMIT stateChanged ( kInitialized );
return ;
}
2016-06-21 11:22:24 -04:00
}
2024-04-14 19:06:04 -07:00
if ( ! lidar || _preferencesDialog -> getSourceDriver () != PreferencesDialog :: kSrcUndef )
2021-05-23 17:21:12 -04:00
{
2024-04-14 19:06:04 -07:00
camera = _preferencesDialog -> createCamera ();
if ( ! camera )
{
delete lidar ;
Q_EMIT stateChanged ( kInitialized );
return ;
}
2021-05-23 17:21:12 -04:00
}
2024-04-14 19:06:04 -07:00
2025-06-28 20:23:34 -07:00
if (( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcDatabase ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRGBDImages ) &&
camera && camera -> odomProvided ())
2024-06-25 12:04:37 -07:00
{
odomSensor = camera ;
}
else if ( _preferencesDialog -> getOdomSourceDriver () != PreferencesDialog :: kSrcUndef )
2021-05-23 17:21:12 -04:00
{
2024-04-14 19:06:04 -07:00
if ( camera == 0 ||
( _preferencesDialog -> getOdomSourceDriver () != _preferencesDialog -> getSourceDriver () &&
! ( _preferencesDialog -> getOdomSourceDriver () == PreferencesDialog :: kSrcStereoRealSense2 &&
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRealSense2 )))
{
UINFO ( "Create Odom Sensor %d (camera = %d)" ,
_preferencesDialog -> getOdomSourceDriver (),
_preferencesDialog -> getSourceDriver ());
odomSensor = _preferencesDialog -> createOdomSensor ( extrinsics , poseTimeOffset , scaleFactor , waitTime );
if ( ! odomSensor )
{
delete camera ;
delete lidar ;
Q_EMIT stateChanged ( kInitialized );
return ;
}
}
else if ( camera -> odomProvided ())
{
UINFO ( "The camera is also the odometry sensor (camera=%d odom=%d)." ,
_preferencesDialog -> getSourceDriver (),
_preferencesDialog -> getOdomSourceDriver ());
odomSensor = camera ;
}
2021-05-23 17:21:12 -04:00
}
2024-04-14 19:06:04 -07:00
_sensorCapture = new SensorCaptureThread ( lidar , camera , odomSensor , extrinsics , poseTimeOffset , scaleFactor , waitTime , parameters );
_sensorCapture -> setOdomAsGroundTruth ( _preferencesDialog -> isOdomSensorAsGt ());
_sensorCapture -> setMirroringEnabled ( _preferencesDialog -> isSourceMirroring ());
_sensorCapture -> setColorOnly ( _preferencesDialog -> isSourceRGBDColorOnly ());
_sensorCapture -> setImageDecimation ( _preferencesDialog -> getSourceImageDecimation ());
_sensorCapture -> setHistogramMethod ( _preferencesDialog -> getSourceHistogramMethod ());
2023-11-19 01:05:52 -08:00
if ( _preferencesDialog -> isSourceFeatureDetection ())
{
2024-04-14 19:06:04 -07:00
_sensorCapture -> enableFeatureDetection ( parameters );
2023-11-19 01:05:52 -08:00
}
2024-04-14 19:06:04 -07:00
_sensorCapture -> setStereoToDepth ( _preferencesDialog -> isSourceStereoDepthGenerated ());
_sensorCapture -> setStereoExposureCompensation ( _preferencesDialog -> isSourceStereoExposureCompensation ());
_sensorCapture -> setScanParameters (
2016-06-21 11:22:24 -04:00
_preferencesDialog -> isSourceScanFromDepth (),
2019-01-15 15:41:07 -05:00
_preferencesDialog -> getSourceScanDownsampleStep (),
_preferencesDialog -> getSourceScanRangeMin (),
_preferencesDialog -> getSourceScanRangeMax (),
2016-06-21 11:22:24 -04:00
_preferencesDialog -> getSourceScanVoxelSize (),
2017-08-25 18:02:40 -04:00
_preferencesDialog -> getSourceScanNormalsK (),
2019-01-15 15:41:07 -05:00
_preferencesDialog -> getSourceScanNormalsRadius (),
2024-04-14 19:06:04 -07:00
( float ) _preferencesDialog -> getSourceScanForceGroundNormalsUp (),
_preferencesDialog -> isSourceScanDeskewing ());
2019-09-13 21:45:13 -04:00
if ( _preferencesDialog -> getIMUFilteringStrategy () > 0 && dynamic_cast < DBReader *> ( camera ) == 0 )
2019-05-13 18:42:46 -04:00
{
2024-04-14 19:06:04 -07:00
_sensorCapture -> enableIMUFiltering ( _preferencesDialog -> getIMUFilteringStrategy () - 1 , parameters , _preferencesDialog -> getIMUFilteringBaseFrameConversion ());
2019-05-13 18:42:46 -04:00
}
2017-01-03 21:05:00 -05:00
if ( _preferencesDialog -> isDepthFilteringAvailable ())
2016-09-19 14:01:06 -04:00
{
2016-11-03 17:04:31 -04:00
if ( _preferencesDialog -> isBilateralFiltering ())
{
2024-04-14 19:06:04 -07:00
_sensorCapture -> enableBilateralFiltering (
2016-11-03 17:04:31 -04:00
_preferencesDialog -> getBilateralSigmaS (),
_preferencesDialog -> getBilateralSigmaR ());
}
2024-04-14 19:06:04 -07:00
_sensorCapture -> setDistortionModel ( _preferencesDialog -> getSourceDistortionModel (). toStdString ());
2016-09-19 14:01:06 -04:00
}
2016-06-21 11:22:24 -04:00
//Create odometry thread if rgbd slam
if ( uStr2Bool ( parameters . at ( Parameters :: kRGBDEnabled ()). c_str ()))
{
// Require calibrated camera
2024-04-14 19:06:04 -07:00
if ( camera && ! camera -> isCalibrated ())
2014-06-15 03:31:35 +00:00
{
2016-06-21 11:22:24 -04:00
UWARN ( "Camera is not calibrated!" );
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kInitialized );
2024-04-14 19:06:04 -07:00
delete _sensorCapture ;
_sensorCapture = 0 ;
2016-06-21 11:22:24 -04:00
int button = QMessageBox :: question ( this ,
tr ( "Camera is not calibrated!" ),
2017-04-24 14:11:38 -04:00
tr ( "RTAB-Map in metric SLAM mode cannot run with an uncalibrated camera. Do you want to calibrate the camera now?" ),
2016-06-21 11:22:24 -04:00
QMessageBox :: Yes | QMessageBox :: No );
if ( button == QMessageBox :: Yes )
{
QTimer :: singleShot ( 0 , _preferencesDialog , SLOT ( calibrate ()));
}
2014-06-15 03:31:35 +00:00
return ;
}
2016-06-21 11:22:24 -04:00
else
2014-06-15 03:31:35 +00:00
{
2016-06-21 11:22:24 -04:00
if ( _odomThread )
2015-04-06 00:28:04 -04:00
{
2016-06-21 11:22:24 -04:00
UERROR ( "OdomThread must be already deleted here?!" );
delete _odomThread ;
_odomThread = 0 ;
2015-04-06 00:28:04 -04:00
}
2016-06-21 11:22:24 -04:00
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
UERROR ( "ImuThread must be already deleted here?!" );
delete _imuThread ;
_imuThread = 0 ;
}
2025-09-28 16:31:42 -07:00
if (( ! _sensorCapture -> odomProvided () || _preferencesDialog -> isOdomAsGuessEnabled ()) && ! _preferencesDialog -> isOdomDisabled ())
2015-04-06 00:28:04 -04:00
{
2017-01-03 21:05:00 -05:00
ParametersMap odomParameters = parameters ;
if ( _preferencesDialog -> getOdomRegistrationApproach () < 3 )
{
uInsert ( odomParameters , ParametersPair ( Parameters :: kRegStrategy (), uNumber2Str ( _preferencesDialog -> getOdomRegistrationApproach ())));
}
2018-02-01 22:17:46 -05:00
odomParameters . erase ( Parameters :: kRtabmapPublishRAMUsage ()); // as odometry is in the same process than rtabmap, don't get RAM usage in odometry.
int odomStrategy = Parameters :: defaultOdomStrategy ();
Parameters :: parse ( odomParameters , Parameters :: kOdomStrategy (), odomStrategy );
2020-05-31 11:22:12 -04:00
double gravitySigma = _preferencesDialog -> getOdomF2MGravitySigma ();
2020-07-01 14:04:19 -04:00
UDEBUG ( "Odom gravitySigma=%f" , gravitySigma );
2020-05-31 11:22:12 -04:00
if ( gravitySigma >= 0.0 )
{
uInsert ( odomParameters , ParametersPair ( Parameters :: kOptimizerGravitySigma (), uNumber2Str ( gravitySigma )));
}
2018-10-04 17:09:03 -04:00
if ( odomStrategy != 1 )
2018-02-01 22:17:46 -05:00
{
// Only Frame To Frame supports all VisCorType
2018-10-04 17:09:03 -04:00
odomParameters . erase ( Parameters :: kVisCorType ());
2018-02-01 22:17:46 -05:00
}
2018-03-07 19:43:30 -05:00
_imuThread = 0 ;
if (( _preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcStereoImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcRGBDImages ||
_preferencesDialog -> getSourceDriver () == PreferencesDialog :: kSrcImages ) &&
! _preferencesDialog -> getIMUPath (). isEmpty ())
{
2023-04-16 17:32:32 -07:00
_imuThread = new IMUThread ( _preferencesDialog -> getIMURate (), _preferencesDialog -> getIMULocalTransform ());
if ( _preferencesDialog -> getIMUFilteringStrategy () > 0 )
{
_imuThread -> enableIMUFiltering ( _preferencesDialog -> getIMUFilteringStrategy () - 1 , parameters , _preferencesDialog -> getIMUFilteringBaseFrameConversion ());
}
if ( ! _imuThread -> init ( _preferencesDialog -> getIMUPath (). toStdString ()))
2018-03-07 19:43:30 -05:00
{
QMessageBox :: warning ( this , tr ( "Source IMU Path" ),
2023-04-16 17:32:32 -07:00
tr ( "Initialization of IMU data has failed! Path=%1." ). arg ( _preferencesDialog -> getIMUPath ()), QMessageBox :: Ok );
2024-04-14 19:06:04 -07:00
delete _sensorCapture ;
_sensorCapture = 0 ;
2023-04-16 17:32:32 -07:00
delete _imuThread ;
_imuThread = 0 ;
return ;
2018-03-07 19:43:30 -05:00
}
}
2017-01-03 21:05:00 -05:00
Odometry * odom = Odometry :: create ( odomParameters );
2015-10-02 10:40:18 -04:00
_odomThread = new OdometryThread ( odom , _preferencesDialog -> getOdomBufferSize ());
2015-04-06 00:28:04 -04:00
UEventsManager :: addHandler ( _odomThread );
2024-04-14 19:06:04 -07:00
UEventsManager :: createPipe ( _sensorCapture , _odomThread , "SensorEvent" );
UEventsManager :: createPipe ( _sensorCapture , this , "SensorEvent" );
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
UEventsManager :: createPipe ( _imuThread , _odomThread , "IMUEvent" );
}
2015-04-06 00:28:04 -04:00
_odomThread -> start ();
}
2013-12-11 00:12:44 +00:00
}
}
2016-06-21 11:22:24 -04:00
2025-09-28 16:31:42 -07:00
if ( _dataRecorder && _sensorCapture )
2012-06-24 17:19:34 +00:00
{
2024-04-14 19:06:04 -07:00
UEventsManager :: createPipe ( _sensorCapture , _dataRecorder , "SensorEvent" );
2015-03-29 09:23:00 -04:00
}
2013-12-11 00:12:44 +00:00
_lastOdomPose . setNull ();
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdCleanDataBuffer )); // clean sensors buffer
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdTriggerNewMap )); // Trigger a new map
2011-06-05 14:45:39 +00:00
2013-12-11 00:12:44 +00:00
if ( _odomThread )
{
_ui -> actionReset_Odometry -> setEnabled ( true );
}
2012-06-24 17:19:34 +00:00
if ( ! _preferencesDialog -> isStatisticsPublished ())
{
QMessageBox :: information ( this ,
tr ( "Information" ),
tr ( "Note that publishing statistics is disabled, "
"progress will not be shown in the GUI." ));
}
2011-06-05 14:45:39 +00:00
2024-04-12 17:12:23 -07:00
// Override map resolution for UI
if ( _preferencesDialog -> getGridUIResolution () > 0 )
{
uInsert ( parameters , ParametersPair ( Parameters :: kGridCellSize (), uNumber2Str ( _preferencesDialog -> getGridUIResolution ())));
}
2023-12-17 22:44:11 -08:00
_cachedLocalMaps . clear ();
delete _occupancyGrid ;
_occupancyGrid = new OccupancyGrid ( & _cachedLocalMaps , parameters );
2017-04-07 18:31:11 -04:00
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
delete _octomap ;
2023-12-17 22:44:11 -08:00
_octomap = new OctoMap ( & _cachedLocalMaps , parameters );
#endif
#ifdef RTABMAP_GRIDMAP
delete _elevationMap ;
_elevationMap = new GridMap ( & _cachedLocalMaps , parameters );
2016-07-15 17:46:13 -04:00
#endif
2016-11-27 14:30:11 -05:00
// clear odometry visual stuff
_cloudViewer -> removeCloud ( "cloudOdom" );
_cloudViewer -> removeCloud ( "scanOdom" );
_cloudViewer -> removeCloud ( "scanMapOdom" );
_cloudViewer -> removeCloud ( "featuresOdom" );
_cloudViewer -> setBackgroundColor ( _cloudViewer -> getDefaultBackgroundColor ());
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kDetecting );
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
// Could not be in the main thread here! (see handleEvents())
2011-06-05 14:45:39 +00:00
void MainWindow :: pauseDetection ()
{
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2012-03-03 01:46:30 +00:00
{
2012-06-24 17:19:34 +00:00
if ( _state == kPaused && ( QApplication :: keyboardModifiers () & Qt :: ShiftModifier ))
2012-03-03 01:46:30 +00:00
{
// On Ctrl-click, start the camera and pause it automatically
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kPaused );
2012-06-24 17:19:34 +00:00
if ( _preferencesDialog -> getGeneralInputRate ())
{
2017-11-30 16:52:03 -05:00
QTimer :: singleShot ( 500.0 / _preferencesDialog -> getGeneralInputRate (), this , SLOT ( pauseDetection ()));
2012-06-24 17:19:34 +00:00
}
else
2012-03-03 01:46:30 +00:00
{
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kPaused );
2012-03-03 01:46:30 +00:00
}
}
2012-06-24 17:19:34 +00:00
else
{
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kPaused );
2012-06-24 17:19:34 +00:00
}
2012-03-03 01:46:30 +00:00
}
2013-12-11 00:12:44 +00:00
else if ( _state == kMonitoring )
{
UINFO ( "Sending pause event!" );
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kMonitoringPaused );
2013-12-11 00:12:44 +00:00
}
else if ( _state == kMonitoringPaused )
{
UINFO ( "Sending unpause event!" );
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kMonitoring );
2013-12-11 00:12:44 +00:00
}
2011-06-05 14:45:39 +00:00
}
void MainWindow :: stopDetection ()
{
2024-04-14 19:06:04 -07:00
if ( ! _sensorCapture && ! _odomThread )
2011-06-05 14:45:39 +00:00
{
return ;
}
2012-06-24 17:19:34 +00:00
if ( _state == kDetecting &&
2024-04-14 19:06:04 -07:00
( _sensorCapture && _sensorCapture -> isRunning ()) )
2011-06-05 14:45:39 +00:00
{
QMessageBox :: StandardButton button = QMessageBox :: question ( this , tr ( "Stopping process..." ), tr ( "Are you sure you want to stop the process?" ), QMessageBox :: Yes | QMessageBox :: No , QMessageBox :: No );
if ( button != QMessageBox :: Yes )
{
return ;
}
}
ULOGGER_DEBUG ( "" );
2012-06-24 17:19:34 +00:00
// kill the processes
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
_imuThread -> join ( true );
}
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2012-06-24 17:19:34 +00:00
{
2024-04-14 19:06:04 -07:00
_sensorCapture -> join ( true );
2012-06-24 17:19:34 +00:00
}
2012-12-11 18:05:05 +00:00
2013-12-11 00:12:44 +00:00
if ( _odomThread )
{
_ui -> actionReset_Odometry -> setEnabled ( false );
_odomThread -> kill ();
}
2012-06-24 17:19:34 +00:00
// delete the processes
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
delete _imuThread ;
_imuThread = 0 ;
}
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2011-06-05 14:45:39 +00:00
{
2024-04-14 19:06:04 -07:00
delete _sensorCapture ;
_sensorCapture = 0 ;
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
if ( _odomThread )
{
delete _odomThread ;
_odomThread = 0 ;
}
2015-03-29 09:23:00 -04:00
if ( _dataRecorder )
{
delete _dataRecorder ;
_dataRecorder = 0 ;
}
2018-06-15 12:17:11 -04:00
Q_EMIT stateChanged ( kInitialized );
2011-06-05 14:45:39 +00:00
}
2015-06-11 16:57:16 -04:00
void MainWindow :: notifyNoMoreImages ()
{
QMessageBox :: information ( this ,
tr ( "No more images..." ),
tr ( "The camera has reached the end of the stream." ));
}
2012-12-11 19:41:44 +00:00
void MainWindow :: printLoopClosureIds ()
{
_ui -> dockWidget_console -> show ();
QString msgRef ;
QString msgLoop ;
for ( int i = 0 ; i < _refIds . size (); ++ i )
{
msgRef . append ( QString :: number ( _refIds [ i ]));
msgLoop . append ( QString :: number ( _loopClosureIds [ i ]));
if ( i < _refIds . size () - 1 )
{
msgRef . append ( " " );
msgLoop . append ( " " );
}
}
_ui -> widget_console -> appendMsg ( QString ( "IDs = [%1];" ). arg ( msgRef ));
_ui -> widget_console -> appendMsg ( QString ( "LoopIDs = [%1];" ). arg ( msgLoop ));
}
2015-08-01 23:42:36 -04:00
void MainWindow :: generateGraphDOT ()
2012-12-11 18:05:05 +00:00
{
if ( _graphSavingFileName . isEmpty ())
{
2014-10-28 05:00:10 +00:00
_graphSavingFileName = _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + "Graph.dot" ;
2012-12-11 18:05:05 +00:00
}
2015-08-01 23:42:36 -04:00
bool ok ;
int id = QInputDialog :: getInt ( this , tr ( "Around which location?" ), tr ( "Location ID (0=full map)" ), 0 , 0 , 999999 , 0 , & ok );
2012-12-11 18:05:05 +00:00
if ( ok )
{
2015-08-01 23:42:36 -04:00
int margin = 0 ;
if ( id > 0 )
{
margin = QInputDialog :: getInt ( this , tr ( "Depth around the location?" ), tr ( "Margin" ), 4 , 1 , 100 , 1 , & ok );
}
2012-12-11 18:05:05 +00:00
if ( ok )
{
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save File" ), _graphSavingFileName , tr ( "Graphiz file (*.dot)" ));
if ( ! path . isEmpty ())
{
_graphSavingFileName = path ;
2015-08-01 23:42:36 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdGenerateDOTGraph , false , path . toStdString (), id , margin ));
2012-12-11 20:21:22 +00:00
_ui -> dockWidget_console -> show ();
2015-10-16 14:54:47 -04:00
_ui -> widget_console -> appendMsg ( QString ( "Graph saved... Tip: \n neato -Tpdf \" %1 \" -o out.pdf" ). arg ( _graphSavingFileName ));
2012-12-11 18:05:05 +00:00
}
}
}
}
2015-08-25 19:36:48 -04:00
void MainWindow :: exportPosesRaw ()
2014-01-22 19:49:28 +00:00
{
2015-08-01 23:42:36 -04:00
exportPoses ( 0 );
}
2018-11-08 12:26:37 -05:00
void MainWindow :: exportPosesRGBDSLAMMotionCapture ()
2015-08-01 23:42:36 -04:00
{
exportPoses ( 1 );
}
2018-11-08 12:26:37 -05:00
void MainWindow :: exportPosesRGBDSLAM ()
{
exportPoses ( 10 );
}
2021-04-13 15:49:04 -04:00
void MainWindow :: exportPosesRGBDSLAMID ()
{
exportPoses ( 11 );
}
2015-08-25 19:36:48 -04:00
void MainWindow :: exportPosesKITTI ()
2015-08-01 23:42:36 -04:00
{
exportPoses ( 2 );
2014-01-22 19:49:28 +00:00
}
2015-08-25 19:36:48 -04:00
void MainWindow :: exportPosesTORO ()
{
exportPoses ( 3 );
}
2015-09-02 17:51:43 -04:00
void MainWindow :: exportPosesG2O ()
{
exportPoses ( 4 );
}
2014-01-22 19:49:28 +00:00
2015-08-01 23:42:36 -04:00
void MainWindow :: exportPoses ( int format )
2015-06-11 16:57:16 -04:00
{
2015-10-16 20:11:24 -04:00
if ( _currentPosesMap . size ())
2015-06-11 16:57:16 -04:00
{
2017-05-10 16:47:29 -04:00
std :: map < int , Transform > localTransforms ;
2017-05-10 15:53:02 -04:00
QStringList items ;
2018-11-08 12:26:37 -05:00
items . push_back ( "Robot (base frame)" );
2017-05-10 15:53:02 -04:00
items . push_back ( "Camera" );
items . push_back ( "Scan" );
2017-05-10 16:47:29 -04:00
bool ok ;
QString item = QInputDialog :: getItem ( this , tr ( "Export Poses" ), tr ( "Frame: " ), items , _exportPosesFrame , false , & ok );
if ( ! ok || item . isEmpty ())
2017-05-10 15:53:02 -04:00
{
return ;
}
2018-11-08 12:26:37 -05:00
if ( item . compare ( "Robot (base frame)" ) != 0 )
2017-05-10 15:53:02 -04:00
{
bool cameraFrame = item . compare ( "Camera" ) == 0 ;
_exportPosesFrame = cameraFrame ? 1 : 2 ;
2018-12-07 18:29:41 -05:00
for ( std :: map < int , Transform >:: iterator iter = _currentPosesMap . lower_bound ( 1 ); iter != _currentPosesMap . end (); ++ iter )
2017-05-10 15:53:02 -04:00
{
if ( _cachedSignatures . contains ( iter -> first ))
{
Transform localTransform ;
if ( cameraFrame )
{
2022-07-20 15:20:14 -04:00
if ( _cachedSignatures [ iter -> first ]. sensorData (). cameraModels (). size () == 1 &&
! _cachedSignatures [ iter -> first ]. sensorData (). cameraModels (). at ( 0 ). localTransform (). isNull ())
2017-05-10 15:53:02 -04:00
{
localTransform = _cachedSignatures [ iter -> first ]. sensorData (). cameraModels (). at ( 0 ). localTransform ();
}
2022-07-20 15:20:14 -04:00
else if ( _cachedSignatures [ iter -> first ]. sensorData (). stereoCameraModels (). size () == 1 &&
! _cachedSignatures [ iter -> first ]. sensorData (). stereoCameraModels ()[ 0 ]. localTransform (). isNull ())
2017-05-10 15:53:02 -04:00
{
2022-07-20 15:20:14 -04:00
localTransform = _cachedSignatures [ iter -> first ]. sensorData (). stereoCameraModels ()[ 0 ]. localTransform ();
2017-05-10 15:53:02 -04:00
}
2022-07-20 15:20:14 -04:00
else if ( _cachedSignatures [ iter -> first ]. sensorData (). cameraModels (). size () > 1 ||
_cachedSignatures [ iter -> first ]. sensorData (). stereoCameraModels (). size () > 1 )
2017-05-10 15:53:02 -04:00
{
UWARN ( "Multi-camera is not supported (node %d)" , iter -> first );
}
else
{
UWARN ( "Missing calibration for node %d" , iter -> first );
}
}
else
{
2018-02-16 19:20:54 -05:00
if ( ! _cachedSignatures [ iter -> first ]. sensorData (). laserScanRaw (). localTransform (). isNull ())
2017-05-10 15:53:02 -04:00
{
2018-02-16 19:20:54 -05:00
localTransform = _cachedSignatures [ iter -> first ]. sensorData (). laserScanRaw (). localTransform ();
}
else if ( ! _cachedSignatures [ iter -> first ]. sensorData (). laserScanCompressed (). localTransform (). isNull ())
{
localTransform = _cachedSignatures [ iter -> first ]. sensorData (). laserScanCompressed (). localTransform ();
2017-05-10 15:53:02 -04:00
}
else
{
UWARN ( "Missing scan info for node %d" , iter -> first );
}
}
if ( ! localTransform . isNull ())
{
2017-05-10 16:47:29 -04:00
localTransforms . insert ( std :: make_pair ( iter -> first , localTransform ));
2017-05-10 15:53:02 -04:00
}
}
else
{
UWARN ( "Did not find node %d in cache" , iter -> first );
}
}
2017-05-10 16:47:29 -04:00
if ( localTransforms . empty ())
2017-05-10 15:53:02 -04:00
{
QMessageBox :: warning ( this ,
tr ( "Export Poses" ),
tr ( "Could not find any \" %1 \" frame, exporting in Robot frame instead." ). arg ( item ));
}
}
else
{
_exportPosesFrame = 0 ;
2017-05-10 16:47:29 -04:00
}
std :: map < int , Transform > poses ;
std :: multimap < int , Link > links ;
if ( localTransforms . empty ())
{
2024-01-20 12:04:26 -08:00
poses = std :: map < int , Transform > ( _currentPosesMap . begin (), _currentPosesMap . end ());
links = std :: multimap < int , Link > ( _currentLinksMap . begin (), _currentLinksMap . end ());
2017-05-10 16:47:29 -04:00
}
else
{
//adjust poses and links
for ( std :: map < int , Transform >:: iterator iter = localTransforms . begin (); iter != localTransforms . end (); ++ iter )
{
poses . insert ( std :: make_pair ( iter -> first , _currentPosesMap . at ( iter -> first ) * iter -> second ));
}
2018-12-07 18:29:41 -05:00
for ( std :: multimap < int , Link >:: iterator iter = _currentLinksMap . lower_bound ( 1 ); iter != _currentLinksMap . end (); ++ iter )
2017-05-10 16:47:29 -04:00
{
if ( uContains ( poses , iter -> second . from ()) && uContains ( poses , iter -> second . to ()))
{
std :: multimap < int , Link >:: iterator inserted = links . insert ( * iter );
int from = iter -> second . from ();
int to = iter -> second . to ();
inserted -> second . setTransform ( localTransforms . at ( from ). inverse () * iter -> second . transform () * localTransforms . at ( to ));
}
}
2017-05-10 15:53:02 -04:00
}
2024-01-20 12:04:26 -08:00
if ( format != 4 && format != 11 && ! poses . empty () && poses . begin () -> first < 0 ) // not g2o, landmark not supported
2022-01-15 21:49:40 -05:00
{
2024-01-20 12:04:26 -08:00
UWARN ( "Only g2o format (4) and RGBD format with ID format can export landmarks, they are ignored with format %d" , format );
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end () && iter -> first < 0 ;)
2022-01-15 21:49:40 -05:00
{
poses . erase ( iter ++ );
}
2024-01-20 12:04:26 -08:00
for ( std :: multimap < int , Link >:: iterator iter = links . begin (); iter != links . end ();)
{
if ( iter -> second . from () < 0 || iter -> second . to () < 0 )
{
links . erase ( iter ++ );
}
else
{
++ iter ;
}
}
2022-01-15 21:49:40 -05:00
}
2015-10-16 20:11:24 -04:00
std :: map < int , double > stamps ;
2021-04-13 15:49:04 -04:00
if ( format == 1 || format == 10 || format == 11 )
2015-06-11 16:57:16 -04:00
{
2017-05-10 15:53:02 -04:00
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
2015-10-16 20:11:24 -04:00
{
2024-01-20 12:04:26 -08:00
if ( iter -> first < 0 && format == 11 ) // in case of landmarks
{
stamps . insert ( std :: make_pair ( iter -> first , 0 ));
}
else if ( _cachedSignatures . contains ( iter -> first ))
2015-10-16 20:11:24 -04:00
{
stamps . insert ( std :: make_pair ( iter -> first , _cachedSignatures . value ( iter -> first ). getStamp ()));
}
}
2017-05-10 15:53:02 -04:00
if ( stamps . size () != poses . size ())
2015-10-16 20:11:24 -04:00
{
QMessageBox :: warning ( this , tr ( "Export poses..." ), tr ( "RGB-D SLAM format: Poses (%1) and stamps (%2) have not the same size! Try again after updating the cache." )
2017-05-10 15:53:02 -04:00
. arg ( poses . size ()). arg ( stamps . size ()));
2015-10-16 20:11:24 -04:00
return ;
}
2015-06-11 16:57:16 -04:00
}
2015-08-01 23:42:36 -04:00
if ( _exportPosesFileName [ format ]. isEmpty ())
{
2015-09-02 17:51:43 -04:00
_exportPosesFileName [ format ] = _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + ( format == 3 ? "toro.graph" : format == 4 ? "poses.g2o" : "poses.txt" );
2015-08-01 23:42:36 -04:00
}
QString path = QFileDialog :: getSaveFileName (
this ,
tr ( "Save File" ),
_exportPosesFileName [ format ],
2015-09-02 17:51:43 -04:00
format == 3 ? tr ( "TORO file (*.graph)" ) : format == 4 ? tr ( "g2o file (*.g2o)" ) : tr ( "Text file (*.txt)" ));
2015-08-01 23:42:36 -04:00
2015-06-11 16:57:16 -04:00
if ( ! path . isEmpty ())
{
2018-03-12 20:26:34 -04:00
if ( QFileInfo ( path ). suffix () == "" )
{
if ( format == 3 )
{
path += ".graph" ;
}
else if ( format == 4 )
{
path += ".g2o" ;
}
else
{
path += ".txt" ;
}
}
2015-08-01 23:42:36 -04:00
_exportPosesFileName [ format ] = path ;
2018-10-15 13:41:44 -04:00
bool saved = graph :: exportPoses ( path . toStdString (), format , poses , links , stamps , _preferencesDialog -> getAllParameters ());
2015-06-11 16:57:16 -04:00
2015-10-16 20:11:24 -04:00
if ( saved )
{
QMessageBox :: information ( this ,
tr ( "Export poses..." ),
tr ( "%1 saved to \" %2 \" ." )
. arg ( format == 3 ? "TORO graph" : format == 4 ? "g2o graph" : "Poses" )
. arg ( _exportPosesFileName [ format ]));
}
else
{
QMessageBox :: information ( this ,
tr ( "Export poses..." ),
tr ( "Failed to save %1 to \" %2 \" !" )
. arg ( format == 3 ? "TORO graph" : format == 4 ? "g2o graph" : "poses" )
. arg ( _exportPosesFileName [ format ]));
}
2015-06-11 16:57:16 -04:00
}
}
}
2021-11-09 10:16:12 -05:00
void MainWindow :: showPostProcessingDialog ()
{
if ( _postProcessingDialog -> exec () != QDialog :: Accepted )
{
return ;
}
postProcessing (
_postProcessingDialog -> isRefineNeighborLinks (),
_postProcessingDialog -> isRefineLoopClosureLinks (),
_postProcessingDialog -> isDetectMoreLoopClosures (),
_postProcessingDialog -> clusterRadius (),
_postProcessingDialog -> clusterAngle (),
_postProcessingDialog -> iterations (),
_postProcessingDialog -> interSession (),
_postProcessingDialog -> intraSession (),
2026-04-04 19:48:03 -07:00
_postProcessingDialog -> minGraphDistance (),
2021-11-09 10:16:12 -05:00
_postProcessingDialog -> isSBA (),
_postProcessingDialog -> sbaIterations (),
_postProcessingDialog -> sbaVariance (),
_postProcessingDialog -> sbaType (),
_postProcessingDialog -> sbaRematchFeatures ());
}
void MainWindow :: postProcessing (
bool refineNeighborLinks ,
bool refineLoopClosureLinks ,
bool detectMoreLoopClosures ,
double clusterRadius ,
double clusterAngle ,
int iterations ,
bool interSession ,
bool intraSession ,
2026-04-04 19:48:03 -07:00
int minGraphDistance ,
2021-11-09 10:16:12 -05:00
bool sba ,
int sbaIterations ,
double sbaVariance ,
Optimizer :: Type sbaType ,
double sbaRematchFeatures ,
bool abortIfDataMissing )
2014-10-30 14:43:43 +00:00
{
2015-01-12 17:43:42 -05:00
if ( _cachedSignatures . size () == 0 )
{
2015-10-16 20:43:00 -04:00
QMessageBox :: warning ( this ,
tr ( "Post-processing..." ),
tr ( "Signatures must be cached in the GUI for post-processing. "
"Check the option in Preferences->General Settings (GUI), then "
"refresh the cache." ));
2015-01-12 17:43:42 -05:00
return ;
}
2014-10-30 14:43:43 +00:00
2015-08-12 20:59:21 -04:00
if ( ! detectMoreLoopClosures && ! refineNeighborLinks && ! refineLoopClosureLinks && ! sba )
2014-10-30 14:43:43 +00:00
{
UWARN ( "No post-processing selection..." );
return ;
}
2018-12-07 18:29:41 -05:00
if ( _currentPosesMap . lower_bound ( 1 ) == _currentPosesMap . end ())
{
UWARN ( "No nodes to process..." );
return ;
}
2014-10-30 14:43:43 +00:00
// First, verify that we have all data required in the GUI
2019-03-31 15:51:50 -04:00
std :: map < int , Transform > odomPoses ;
2014-10-30 14:43:43 +00:00
bool allDataAvailable = true ;
2018-12-07 18:29:41 -05:00
for ( std :: map < int , Transform >:: iterator iter = _currentPosesMap . lower_bound ( 1 );
2014-10-30 14:43:43 +00:00
iter != _currentPosesMap . end () && allDataAvailable ;
++ iter )
{
QMap < int , Signature >:: iterator jter = _cachedSignatures . find ( iter -> first );
2018-12-07 18:29:41 -05:00
if ( jter == _cachedSignatures . end ())
2014-10-30 14:43:43 +00:00
{
UWARN ( "Node %d missing." , iter -> first );
allDataAvailable = false ;
}
2019-03-31 15:51:50 -04:00
else if ( ! jter . value (). getPose (). isNull ())
{
odomPoses . insert ( std :: make_pair ( iter -> first , jter . value (). getPose ()));
}
2014-10-30 14:43:43 +00:00
}
if ( ! allDataAvailable )
{
2021-11-09 10:16:12 -05:00
QString msg = tr ( "Some data missing in the cache to respect the constraints chosen. "
"Try \" Edit->Download all clouds \" to update the cache and try again." );
UWARN ( msg . toStdString (). c_str ());
if ( abortIfDataMissing )
{
QMessageBox :: warning ( this , tr ( "Not all data available in the GUI..." ), msg );
return ;
}
2014-10-30 14:43:43 +00:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> clear ();
_progressDialog -> show ();
_progressDialog -> appendText ( "Post-processing beginning!" );
_progressDialog -> setCancelButtonVisible ( true );
2016-09-19 14:01:06 -04:00
_progressCanceled = false ;
2014-10-30 14:43:43 +00:00
int totalSteps = 0 ;
if ( refineNeighborLinks )
{
2018-12-07 18:29:41 -05:00
totalSteps += ( int ) _currentPosesMap . size ();
2014-10-30 14:43:43 +00:00
}
if ( refineLoopClosureLinks )
{
2018-12-07 18:29:41 -05:00
totalSteps += ( int ) _currentLinksMap . size () - ( int ) _currentPosesMap . size ();
2014-10-30 14:43:43 +00:00
}
2015-08-12 20:59:21 -04:00
if ( sba )
{
totalSteps += 1 ;
}
2017-10-20 12:09:34 +02:00
_progressDialog -> setMaximumSteps ( totalSteps );
_progressDialog -> show ();
2014-10-30 14:43:43 +00:00
ParametersMap parameters = _preferencesDialog -> getAllParameters ();
2015-11-27 11:09:39 -05:00
Optimizer * optimizer = Optimizer :: create ( parameters );
2015-03-12 17:00:56 -04:00
bool optimizeFromGraphEnd = Parameters :: defaultRGBDOptimizeFromGraphEnd ();
2017-03-30 15:33:48 -04:00
float optimizeMaxError = Parameters :: defaultRGBDOptimizeMaxError ();
int optimizeIterations = Parameters :: defaultOptimizerIterations ();
2017-07-24 12:18:19 -04:00
bool reextractFeatures = Parameters :: defaultRGBDLoopClosureReextractFeatures ();
2015-03-12 17:00:56 -04:00
Parameters :: parse ( parameters , Parameters :: kRGBDOptimizeFromGraphEnd (), optimizeFromGraphEnd );
2017-03-30 15:33:48 -04:00
Parameters :: parse ( parameters , Parameters :: kRGBDOptimizeMaxError (), optimizeMaxError );
Parameters :: parse ( parameters , Parameters :: kOptimizerIterations (), optimizeIterations );
2017-07-24 12:18:19 -04:00
Parameters :: parse ( parameters , Parameters :: kRGBDLoopClosureReextractFeatures (), reextractFeatures );
2014-10-30 14:43:43 +00:00
2015-10-16 20:43:00 -04:00
bool warn = false ;
2014-10-30 14:43:43 +00:00
int loopClosuresAdded = 0 ;
2016-03-28 18:19:17 -04:00
std :: multimap < int , int > checkedLoopClosures ;
2014-10-30 14:43:43 +00:00
if ( detectMoreLoopClosures )
{
2015-05-29 14:46:48 -04:00
UDEBUG ( "" );
2014-10-30 14:43:43 +00:00
2018-10-01 20:22:20 -04:00
bool loopCovLimited = Parameters :: defaultRGBDLoopCovLimited ();
Parameters :: parse ( parameters , Parameters :: kRGBDLoopCovLimited (), loopCovLimited );
std :: vector < double > odomMaxInf ;
if ( loopCovLimited )
{
odomMaxInf = graph :: getMaxOdomInf ( _currentLinksMap );
}
2026-04-04 19:48:03 -07:00
std :: multimap < int , Link > neigborLinks = graph :: filterLinks ( _currentLinksMap , Link :: kNeighbor , true );
2018-10-01 20:22:20 -04:00
2025-05-17 16:34:31 -07:00
std :: shared_ptr < Registration > registration ( Registration :: create ( parameters ));
2021-11-09 10:16:12 -05:00
UASSERT ( iterations > 0 );
for ( int n = 0 ; n < iterations && ! _progressCanceled ; ++ n )
2014-10-30 14:43:43 +00:00
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Looking for more loop closures, clustering poses... (iteration=%1/%2, radius=%3 m angle=%4 degrees)" )
2021-11-09 10:16:12 -05:00
. arg ( n + 1 ). arg ( iterations ). arg ( clusterRadius ). arg ( clusterAngle ));
2014-10-30 14:43:43 +00:00
2015-11-27 11:09:39 -05:00
std :: multimap < int , int > clusters = graph :: radiusPosesClustering (
2019-02-28 20:33:16 -05:00
std :: map < int , Transform > ( _currentPosesMap . upper_bound ( 0 ), _currentPosesMap . end ()),
2014-10-30 14:43:43 +00:00
clusterRadius ,
clusterAngle * CV_PI / 180.0 );
2017-10-20 12:09:34 +02:00
_progressDialog -> setMaximumSteps ( _progressDialog -> maximumSteps () + ( int ) clusters . size ());
_progressDialog -> appendText ( tr ( "Looking for more loop closures, clustering poses... found %1 clusters." ). arg ( clusters . size ()));
2016-09-19 17:54:51 -04:00
QApplication :: processEvents ();
2014-10-30 14:43:43 +00:00
2026-04-04 19:48:03 -07:00
if ( minGraphDistance > 1 )
{
int clustersBefore = clusters . size ();
for ( std :: multimap < int , int >:: iterator iter = clusters . begin (); iter != clusters . end ();)
{
if ( abs ( iter -> first - iter -> second ) < minGraphDistance )
{
iter = clusters . erase ( iter );
}
else
{
// compute path to know how far we are in terms of graph length
std :: list < int > path = graph :: computePath ( neigborLinks , iter -> first , iter -> second );
if ( ! path . empty () && ( int ) path . size () <= minGraphDistance )
{
iter = clusters . erase ( iter );
}
else
{
++ iter ;
}
}
}
_progressDialog -> appendText ( tr ( "Filtered %1/%2 clusters for too close nodes (below minimum graph distance=%3)." )
. arg ( clustersBefore - clusters . size ()). arg ( clustersBefore ). arg ( minGraphDistance ));
QApplication :: processEvents ();
}
2014-10-30 14:43:43 +00:00
int i = 0 ;
std :: set < int > addedLinks ;
2016-09-19 14:01:06 -04:00
for ( std :: multimap < int , int >:: iterator iter = clusters . begin (); iter != clusters . end () && ! _progressCanceled ; ++ iter , ++ i )
2014-10-30 14:43:43 +00:00
{
int from = iter -> first ;
int to = iter -> second ;
if ( iter -> first < iter -> second )
{
from = iter -> second ;
to = iter -> first ;
}
2018-11-05 18:40:08 -05:00
int mapIdFrom = uValue ( _currentMapIds , from , 0 );
int mapIdTo = uValue ( _currentMapIds , to , 0 );
if (( interSession && mapIdFrom != mapIdTo ) ||
( intraSession && mapIdFrom == mapIdTo ))
2014-10-30 14:43:43 +00:00
{
2018-11-05 18:40:08 -05:00
bool alreadyChecked = false ;
for ( std :: multimap < int , int >:: iterator jter = checkedLoopClosures . lower_bound ( from );
! alreadyChecked && jter != checkedLoopClosures . end () && jter -> first == from ;
++ jter )
2014-10-30 14:43:43 +00:00
{
2018-11-05 18:40:08 -05:00
if ( to == jter -> second )
{
alreadyChecked = true ;
}
2014-10-30 14:43:43 +00:00
}
2018-11-05 18:40:08 -05:00
if ( ! alreadyChecked )
2016-03-28 18:19:17 -04:00
{
2018-11-05 18:40:08 -05:00
// only add new links and one per cluster per iteration
if ( addedLinks . find ( from ) == addedLinks . end () &&
addedLinks . find ( to ) == addedLinks . end () &&
rtabmap :: graph :: findLink ( _currentLinksMap , from , to ) == _currentLinksMap . end ())
{
2021-03-13 18:42:13 -05:00
// Reverify if in the bounds with the current optimized graph
Transform delta = _currentPosesMap . at ( from ). inverse () * _currentPosesMap . at ( to );
if ( delta . getNorm () < clusterRadius )
{
checkedLoopClosures . insert ( std :: make_pair ( from , to ));
2015-11-22 18:08:32 -05:00
2021-03-13 18:42:13 -05:00
if ( ! _cachedSignatures . contains ( from ))
2017-07-24 12:18:19 -04:00
{
2021-03-13 18:42:13 -05:00
UERROR ( "Didn't find signature %d" , from );
}
else if ( ! _cachedSignatures . contains ( to ))
{
UERROR ( "Didn't find signature %d" , to );
}
else
{
Signature signatureFrom = _cachedSignatures [ from ];
Signature signatureTo = _cachedSignatures [ to ];
2017-07-24 12:18:19 -04:00
2021-03-13 18:42:13 -05:00
if ( signatureFrom . getWeight () >= 0 &&
signatureTo . getWeight () >= 0 ) // ignore intermediate nodes
2017-07-24 12:18:19 -04:00
{
2021-03-13 18:42:13 -05:00
Transform transform ;
RegistrationInfo info ;
if ( parameters . find ( Parameters :: kRegStrategy ()) != parameters . end () &&
parameters . at ( Parameters :: kRegStrategy ()). compare ( "1" ) == 0 )
{
uInsert ( parameters , ParametersPair ( Parameters :: kRegStrategy (), "2" ));
}
2018-11-05 18:40:08 -05:00
2021-03-13 18:42:13 -05:00
if ( reextractFeatures )
2018-10-01 20:22:20 -04:00
{
2021-03-13 18:42:13 -05:00
signatureFrom . sensorData (). uncompressData ();
signatureTo . sensorData (). uncompressData ();
if ( signatureFrom . sensorData (). imageRaw (). empty () &&
signatureTo . sensorData (). imageRaw (). empty ())
2018-11-05 18:40:08 -05:00
{
2021-03-13 18:42:13 -05:00
UWARN ( " \" %s \" is false and signatures (%d and %d) don't have raw "
"images. Update the cache." ,
Parameters :: kRGBDLoopClosureReextractFeatures (). c_str ());
2018-11-05 18:40:08 -05:00
}
else
{
2021-03-13 18:42:13 -05:00
signatureFrom . removeAllWords ();
signatureFrom . sensorData (). setFeatures ( std :: vector < cv :: KeyPoint > (), std :: vector < cv :: Point3f > (), cv :: Mat ());
signatureTo . removeAllWords ();
signatureTo . sensorData (). setFeatures ( std :: vector < cv :: KeyPoint > (), std :: vector < cv :: Point3f > (), cv :: Mat ());
2018-11-05 18:40:08 -05:00
}
}
2021-03-13 18:42:13 -05:00
else if ( ! reextractFeatures && signatureFrom . getWords (). empty () && signatureTo . getWords (). empty ())
2018-11-05 18:40:08 -05:00
{
2021-03-13 18:42:13 -05:00
UWARN ( " \" %s \" is false and signatures (%d and %d) don't have words, "
"registration will not be possible. Set \" %s \" to true." ,
Parameters :: kRGBDLoopClosureReextractFeatures (). c_str (),
signatureFrom . id (),
signatureTo . id (),
Parameters :: kRGBDLoopClosureReextractFeatures (). c_str ());
}
transform = registration -> computeTransformation ( signatureFrom , signatureTo , Transform (), & info );
if ( ! transform . isNull ())
{
//optimize the graph to see if the new constraint is globally valid
bool updateConstraint = true ;
cv :: Mat information = info . covariance . inv ();
if ( odomMaxInf . size () == 6 && information . cols == 6 && information . rows == 6 )
{
for ( int i = 0 ; i < 6 ; ++ i )
{
if ( information . at < double > ( i , i ) > odomMaxInf [ i ])
{
information . at < double > ( i , i ) = odomMaxInf [ i ];
}
}
}
if ( optimizeMaxError > 0.0f && optimizeIterations > 0 )
{
int fromId = from ;
int mapId = _currentMapIds . at ( from );
// use first node of the map containing from
for ( std :: map < int , int >:: iterator iter = _currentMapIds . begin (); iter != _currentMapIds . end (); ++ iter )
{
if ( iter -> second == mapId && _currentPosesMap . find ( iter -> first ) != _currentPosesMap . end ())
{
fromId = iter -> first ;
break ;
}
}
std :: multimap < int , Link > linksIn = _currentLinksMap ;
linksIn . insert ( std :: make_pair ( from , Link ( from , to , Link :: kUserClosure , transform , information )));
std :: map < int , Transform > poses ;
std :: multimap < int , Link > links ;
UASSERT ( _currentPosesMap . find ( fromId ) != _currentPosesMap . end ());
UASSERT_MSG ( _currentPosesMap . find ( from ) != _currentPosesMap . end (), uFormat ( "id=%d poses=%d links=%d" , from , ( int ) poses . size (), ( int ) links . size ()). c_str ());
UASSERT_MSG ( _currentPosesMap . find ( to ) != _currentPosesMap . end (), uFormat ( "id=%d poses=%d links=%d" , to , ( int ) poses . size (), ( int ) links . size ()). c_str ());
optimizer -> getConnectedGraph ( fromId , _currentPosesMap , linksIn , poses , links );
UASSERT ( poses . find ( fromId ) != poses . end ());
UASSERT_MSG ( poses . find ( from ) != poses . end (), uFormat ( "id=%d poses=%d links=%d" , from , ( int ) poses . size (), ( int ) links . size ()). c_str ());
UASSERT_MSG ( poses . find ( to ) != poses . end (), uFormat ( "id=%d poses=%d links=%d" , to , ( int ) poses . size (), ( int ) links . size ()). c_str ());
UASSERT ( graph :: findLink ( links , from , to ) != links . end ());
poses = optimizer -> optimize ( fromId , poses , links );
std :: string msg ;
if ( poses . size ())
{
2026-05-04 16:02:55 -07:00
graph :: MaxGraphErrors maxGraphErrors = graph :: computeMaxGraphErrors (
2021-03-13 18:42:13 -05:00
poses ,
2026-05-04 16:02:55 -07:00
links );
if ( maxGraphErrors . linearLink . isValid ())
2021-03-13 18:42:13 -05:00
{
2026-05-04 16:02:55 -07:00
UINFO ( "Max optimization linear error = %f m (link %d->%d)" , maxGraphErrors . linear , maxGraphErrors . linearLink . from (), maxGraphErrors . linearLink . to ());
if ( maxGraphErrors . linearRatio > optimizeMaxError )
2021-03-13 18:42:13 -05:00
{
msg = uFormat ( "Rejecting edge %d->%d because "
"graph error is too large after optimization (%f m for edge %d->%d with ratio %f > std=%f m). "
" \" %s \" is %f." ,
from ,
to ,
2026-05-04 16:02:55 -07:00
maxGraphErrors . linear ,
maxGraphErrors . linearLink . from (),
maxGraphErrors . linearLink . to (),
maxGraphErrors . linearRatio ,
sqrt ( maxGraphErrors . linearLink . transVariance ()),
2021-03-13 18:42:13 -05:00
Parameters :: kRGBDOptimizeMaxError (). c_str (),
optimizeMaxError );
}
}
2026-05-04 16:02:55 -07:00
else if ( maxGraphErrors . angularLink . isValid ())
2021-03-13 18:42:13 -05:00
{
2026-05-04 16:02:55 -07:00
UINFO ( "Max optimization angular error = %f deg (link %d->%d)" , maxGraphErrors . angular * 180.0f / M_PI , maxGraphErrors . angularLink . from (), maxGraphErrors . angularLink . to ());
if ( maxGraphErrors . angularRatio > optimizeMaxError )
2021-03-13 18:42:13 -05:00
{
msg = uFormat ( "Rejecting edge %d->%d because "
"graph error is too large after optimization (%f deg for edge %d->%d with ratio %f > std=%f deg). "
" \" %s \" is %f m." ,
from ,
to ,
2026-05-04 16:02:55 -07:00
maxGraphErrors . angular * 180.0f / M_PI ,
maxGraphErrors . angularLink . from (),
maxGraphErrors . angularLink . to (),
maxGraphErrors . angularRatio ,
sqrt ( maxGraphErrors . angularLink . rotVariance ()),
2021-03-13 18:42:13 -05:00
Parameters :: kRGBDOptimizeMaxError (). c_str (),
optimizeMaxError );
}
}
}
else
{
msg = uFormat ( "Rejecting edge %d->%d because graph optimization has failed!" ,
from ,
to );
}
if ( ! msg . empty ())
{
UWARN ( "%s" , msg . c_str ());
_progressDialog -> appendText ( tr ( "%1" ). arg ( msg . c_str ()));
QApplication :: processEvents ();
updateConstraint = false ;
}
else
{
_currentPosesMap = poses ;
}
}
2018-11-05 18:40:08 -05:00
2021-03-13 18:42:13 -05:00
if ( updateConstraint )
{
UINFO ( "Added new loop closure between %d and %d." , from , to );
addedLinks . insert ( from );
addedLinks . insert ( to );
_currentLinksMap . insert ( std :: make_pair ( from , Link ( from , to , Link :: kUserClosure , transform , information )));
++ loopClosuresAdded ;
_progressDialog -> appendText ( tr ( "Detected loop closure %1->%2! (%3/%4)" ). arg ( from ). arg ( to ). arg ( i + 1 ). arg ( clusters . size ()));
}
2018-11-05 18:40:08 -05:00
}
2017-03-30 15:33:48 -04:00
}
2016-02-18 15:23:25 -05:00
}
2016-02-18 12:52:15 -05:00
}
2014-10-30 14:43:43 +00:00
}
}
}
2017-07-24 12:18:19 -04:00
QApplication :: processEvents ();
2017-10-20 12:09:34 +02:00
_progressDialog -> incrementStep ();
2014-10-30 14:43:43 +00:00
}
2021-11-09 10:16:12 -05:00
_progressDialog -> appendText ( tr ( "Iteration %1/%2: Detected %3 loop closures!" ). arg ( n + 1 ). arg ( iterations ). arg ( addedLinks . size () / 2 ));
2014-10-30 14:43:43 +00:00
if ( addedLinks . size () == 0 )
{
break ;
}
2021-11-09 10:16:12 -05:00
if ( n + 1 < iterations )
2014-10-30 14:43:43 +00:00
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Optimizing graph with new links (%1 nodes, %2 constraints)..." )
2018-12-07 18:29:41 -05:00
. arg ( _currentPosesMap . size ()). arg ( _currentLinksMap . size ()));
2016-09-19 17:54:51 -04:00
QApplication :: processEvents ();
2018-12-07 18:29:41 -05:00
UASSERT ( _currentPosesMap . lower_bound ( 1 ) != _currentPosesMap . end ());
int fromId = optimizeFromGraphEnd ? _currentPosesMap . rbegin () -> first : _currentPosesMap . lower_bound ( 1 ) -> first ;
2015-03-12 17:00:56 -04:00
std :: map < int , rtabmap :: Transform > posesOut ;
std :: multimap < int , rtabmap :: Link > linksOut ;
2014-10-30 14:43:43 +00:00
std :: map < int , rtabmap :: Transform > optimizedPoses ;
2019-03-31 15:51:50 -04:00
std :: multimap < int , rtabmap :: Link > linksIn = _currentLinksMap ;
2015-03-12 17:00:56 -04:00
optimizer -> getConnectedGraph (
fromId ,
2018-12-07 18:29:41 -05:00
_currentPosesMap ,
2019-03-31 15:51:50 -04:00
linksIn ,
2015-03-12 17:00:56 -04:00
posesOut ,
linksOut );
optimizedPoses = optimizer -> optimize ( fromId , posesOut , linksOut );
2014-10-30 14:43:43 +00:00
_currentPosesMap = optimizedPoses ;
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Optimizing graph with new links... done!" ));
2014-10-30 14:43:43 +00:00
}
}
UINFO ( "Added %d loop closures." , loopClosuresAdded );
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Total new loop closures detected=%1" ). arg ( loopClosuresAdded ));
2014-10-30 14:43:43 +00:00
}
2016-09-19 14:01:06 -04:00
if ( ! _progressCanceled && ( refineNeighborLinks || refineLoopClosureLinks ))
2014-10-30 14:43:43 +00:00
{
2015-05-29 14:46:48 -04:00
UDEBUG ( "" );
2014-10-30 14:43:43 +00:00
if ( refineLoopClosureLinks )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> setMaximumSteps ( _progressDialog -> maximumSteps () + loopClosuresAdded );
2014-10-30 14:43:43 +00:00
}
2015-11-22 18:08:32 -05:00
// TODO: support ICP from laser scans?
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Refining links..." ));
2016-09-19 17:54:51 -04:00
QApplication :: processEvents ();
2014-10-30 14:43:43 +00:00
2015-11-22 18:08:32 -05:00
RegistrationIcp regIcp ( parameters );
2014-10-30 14:43:43 +00:00
int i = 0 ;
2018-12-07 18:29:41 -05:00
for ( std :: multimap < int , Link >:: iterator iter = _currentLinksMap . lower_bound ( 1 ); iter != _currentLinksMap . end () && ! _progressCanceled ; ++ iter , ++ i )
2014-10-30 14:43:43 +00:00
{
int type = iter -> second . type ();
2019-03-31 15:51:50 -04:00
int from = iter -> second . from ();
int to = iter -> second . to ();
2014-10-30 14:43:43 +00:00
if (( refineNeighborLinks && type == Link :: kNeighbor ) ||
2019-03-31 15:51:50 -04:00
( refineLoopClosureLinks && type != Link :: kNeighbor && type != Link :: kLandmark && from != to ))
2014-10-30 14:43:43 +00:00
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Refining link %1->%2 (%3/%4)" ). arg ( from ). arg ( to ). arg ( i + 1 ). arg ( _currentLinksMap . size ()));
_progressDialog -> incrementStep ();
2014-10-30 14:43:43 +00:00
QApplication :: processEvents ();
if ( ! _cachedSignatures . contains ( from ))
{
UERROR ( "Didn't find signature %d" , from );
}
else if ( ! _cachedSignatures . contains ( to ))
{
UERROR ( "Didn't find signature %d" , to );
}
else
{
Signature & signatureFrom = _cachedSignatures [ from ];
Signature & signatureTo = _cachedSignatures [ to ];
2018-02-16 19:20:54 -05:00
LaserScan tmp ;
2016-09-01 18:36:55 -04:00
signatureFrom . sensorData (). uncompressData ( 0 , 0 , & tmp );
signatureTo . sensorData (). uncompressData ( 0 , 0 , & tmp );
2018-02-16 19:20:54 -05:00
if ( ! signatureFrom . sensorData (). laserScanRaw (). isEmpty () &&
! signatureTo . sensorData (). laserScanRaw (). isEmpty ())
2014-10-30 14:43:43 +00:00
{
2016-01-06 17:26:55 -05:00
RegistrationInfo info ;
Transform transform = regIcp . computeTransformation ( signatureFrom . sensorData (), signatureTo . sensorData (), iter -> second . transform (), & info );
2014-10-30 14:43:43 +00:00
2015-11-22 18:08:32 -05:00
if ( ! transform . isNull ())
2015-05-29 14:46:48 -04:00
{
2017-05-04 16:13:45 -04:00
Link newLink ( from , to , iter -> second . type (), transform , info . covariance . inv ());
2015-05-29 14:46:48 -04:00
iter -> second = newLink ;
}
else
{
2016-01-07 13:59:21 -05:00
QString str = tr ( "Cannot refine link %1->%2 (%3" ). arg ( from ). arg ( to ). arg ( info . rejectedMsg . c_str ());
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( str , Qt :: darkYellow );
2015-05-29 14:46:48 -04:00
UWARN ( "%s" , str . toStdString (). c_str ());
2015-10-16 20:43:00 -04:00
warn = true ;
2015-05-29 14:46:48 -04:00
}
2014-10-30 14:43:43 +00:00
}
else
{
2015-10-16 20:43:00 -04:00
QString str ;
if ( signatureFrom . getWeight () < 0 || signatureTo . getWeight () < 0 )
{
str = tr ( "Cannot refine link %1->%2 (Intermediate node detected!)" ). arg ( from ). arg ( to );
}
else
{
2016-09-01 18:36:55 -04:00
str = tr ( "Cannot refine link %1->%2 (scans empty!)" ). arg ( from ). arg ( to );
2015-10-16 20:43:00 -04:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( str , Qt :: darkYellow );
2015-01-14 11:02:17 -05:00
UWARN ( "%s" , str . toStdString (). c_str ());
2015-10-16 20:43:00 -04:00
warn = true ;
2014-10-30 14:43:43 +00:00
}
}
}
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Refining links...done!" ));
2014-10-30 14:43:43 +00:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Optimizing graph with updated links (%1 nodes, %2 constraints)..." )
2018-12-07 18:29:41 -05:00
. arg ( _currentPosesMap . size ()). arg ( _currentLinksMap . size ()));
2015-03-12 17:00:56 -04:00
2018-12-07 18:29:41 -05:00
UASSERT ( _currentPosesMap . lower_bound ( 1 ) != _currentPosesMap . end ());
int fromId = optimizeFromGraphEnd ? _currentPosesMap . rbegin () -> first : _currentPosesMap . lower_bound ( 1 ) -> first ;
2015-03-12 17:00:56 -04:00
std :: map < int , rtabmap :: Transform > posesOut ;
std :: multimap < int , rtabmap :: Link > linksOut ;
2014-10-30 14:43:43 +00:00
std :: map < int , rtabmap :: Transform > optimizedPoses ;
2019-03-31 15:51:50 -04:00
std :: multimap < int , rtabmap :: Link > linksIn = _currentLinksMap ;
2015-03-12 17:00:56 -04:00
optimizer -> getConnectedGraph (
fromId ,
2018-12-07 18:29:41 -05:00
_currentPosesMap ,
2019-03-31 15:51:50 -04:00
linksIn ,
2015-03-12 17:00:56 -04:00
posesOut ,
linksOut );
optimizedPoses = optimizer -> optimize ( fromId , posesOut , linksOut );
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Optimizing graph with updated links... done!" ));
_progressDialog -> incrementStep ();
2014-10-30 14:43:43 +00:00
2016-09-19 14:01:06 -04:00
if ( ! _progressCanceled && sba )
2015-08-12 20:59:21 -04:00
{
2016-03-28 18:19:17 -04:00
UASSERT ( Optimizer :: isAvailable ( sbaType ));
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "SBA (%1 nodes, %2 constraints, %3 iterations)..." )
2015-08-13 11:31:19 -04:00
. arg ( optimizedPoses . size ()). arg ( linksOut . size ()). arg ( sbaIterations ));
2015-08-12 20:59:21 -04:00
QApplication :: processEvents ();
2015-08-13 11:31:19 -04:00
uSleep ( 100 );
2015-08-12 20:59:21 -04:00
QApplication :: processEvents ();
ParametersMap parametersSBA = _preferencesDialog -> getAllParameters ();
2015-11-27 11:09:39 -05:00
uInsert ( parametersSBA , std :: make_pair ( Parameters :: kOptimizerIterations (), uNumber2Str ( sbaIterations )));
2026-05-31 00:03:18 -07:00
uInsert ( parametersSBA , std :: make_pair ( Parameters :: kOptimizerPixelVariance (), uNumber2Str ( sbaVariance )));
2018-10-25 16:31:13 -04:00
Optimizer * sbaOptimizer = Optimizer :: create ( sbaType , parametersSBA );
2025-05-17 16:34:31 -07:00
std :: map < int , Transform > newPoses = sbaOptimizer -> optimizeBA (
optimizedPoses . begin () -> first ,
optimizedPoses ,
linksOut ,
_cachedSignatures . toStdMap (),
sbaRematchFeatures ,
parametersSBA );
2018-10-25 16:31:13 -04:00
delete sbaOptimizer ;
2015-08-12 20:59:21 -04:00
if ( newPoses . size ())
{
optimizedPoses = newPoses ;
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "SBA... done!" ));
2015-08-12 20:59:21 -04:00
}
else
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "SBA... failed!" ));
_progressDialog -> setAutoClose ( false );
2015-08-12 20:59:21 -04:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> incrementStep ();
2015-08-12 20:59:21 -04:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Updating map..." ));
2015-07-26 15:50:39 -04:00
this -> updateMapCloud (
2016-01-07 19:39:21 -05:00
optimizedPoses ,
2015-07-26 15:50:39 -04:00
std :: multimap < int , Link > ( _currentLinksMap ),
std :: map < int , int > ( _currentMapIds ),
2016-01-07 19:39:21 -05:00
std :: map < int , std :: string > ( _currentLabels ),
std :: map < int , Transform > ( _currentGTPosesMap ),
2021-12-18 18:53:59 -05:00
std :: map < int , Transform > (),
std :: multimap < int , Link > (),
2015-07-26 15:50:39 -04:00
false );
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Updating map... done!" ));
2014-10-30 14:43:43 +00:00
2015-10-16 20:43:00 -04:00
if ( warn )
{
2017-10-20 12:09:34 +02:00
_progressDialog -> setAutoClose ( false );
2015-10-16 20:43:00 -04:00
}
2017-10-20 12:09:34 +02:00
_progressDialog -> setValue ( _progressDialog -> maximumSteps ());
_progressDialog -> appendText ( "Post-processing finished!" );
_progressDialog -> setCancelButtonVisible ( false );
2016-09-19 14:01:06 -04:00
_progressCanceled = false ;
2015-03-12 17:00:56 -04:00
delete optimizer ;
2014-10-30 14:43:43 +00:00
}
2016-09-19 14:01:06 -04:00
void MainWindow :: depthCalibration ()
{
if ( _currentPosesMap . size () && _cachedSignatures . size ())
{
_depthCalibrationDialog -> calibrate (
_currentPosesMap ,
_cachedSignatures ,
_preferencesDialog -> getWorkingDirectory (),
_preferencesDialog -> getAllParameters ());
}
else
{
QMessageBox :: warning ( this , tr ( "Depth Calibration" ), tr ( "No data in cache. Try to refresh the cache." ));
}
}
2011-06-05 14:45:39 +00:00
void MainWindow :: deleteMemory ()
{
QMessageBox :: StandardButton button ;
2013-12-11 00:12:44 +00:00
if ( _state == kMonitoring || _state == kMonitoringPaused )
2011-06-05 14:45:39 +00:00
{
button = QMessageBox :: question ( this ,
tr ( "Deleting memory..." ),
2014-10-28 01:20:57 +00:00
tr ( "The remote database and log files will be deleted. Are you sure you want to continue? (This cannot be reverted)" ),
2011-06-05 14:45:39 +00:00
QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: No );
}
else
{
button = QMessageBox :: question ( this ,
tr ( "Deleting memory..." ),
2014-10-28 01:20:57 +00:00
tr ( "The current database and log files will be deleted. Are you sure you want to continue? (This cannot be reverted)" ),
2011-06-05 14:45:39 +00:00
QMessageBox :: Yes | QMessageBox :: No ,
QMessageBox :: No );
}
if ( button != QMessageBox :: Yes )
{
return ;
}
2014-10-28 01:20:57 +00:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdResetMemory ));
2014-12-03 22:27:31 -05:00
if ( _state != kDetecting )
{
2015-02-18 18:22:59 -05:00
_databaseUpdated = false ;
2014-12-03 22:27:31 -05:00
}
2011-06-05 14:45:39 +00:00
this -> clearTheCache ();
}
2011-06-30 17:48:14 +00:00
QString MainWindow :: getWorkingDirectory () const
{
return _preferencesDialog -> getWorkingDirectory ();
}
2011-06-05 14:45:39 +00:00
void MainWindow :: openWorkingDirectory ()
{
QString filePath = _preferencesDialog -> getWorkingDirectory ();
#if defined(Q_WS_MAC)
QStringList args ;
args << "-e" ;
args << "tell application \" Finder \" " ;
args << "-e" ;
args << "activate" ;
args << "-e" ;
args << "select POSIX file \" " + filePath + " \" " ;
args << "-e" ;
args << "end tell" ;
QProcess :: startDetached ( "osascript" , args );
#elif defined(Q_WS_WIN)
QStringList args ;
args << "/select," << QDir :: toNativeSeparators ( filePath );
QProcess :: startDetached ( "explorer" , args );
#else
UERROR ( "Only works on Mac and Windows" );
#endif
}
void MainWindow :: updateEditMenu ()
{
// Update Memory delete database size
2015-02-18 18:22:59 -05:00
if ( _state != kMonitoring && _state != kMonitoringPaused && ( ! _openedDatabasePath . isEmpty () || ! _newDatabasePath . isEmpty ()))
2011-06-05 14:45:39 +00:00
{
2015-02-18 18:22:59 -05:00
if ( ! _openedDatabasePath . isEmpty ())
{
_ui -> actionDelete_memory -> setText ( tr ( "Delete memory (%1 MB)" ). arg ( UFile :: length ( _openedDatabasePath . toStdString ()) / 1000000 ));
}
else
{
_ui -> actionDelete_memory -> setText ( tr ( "Delete memory (%1 MB)" ). arg ( UFile :: length ( _newDatabasePath . toStdString ()) / 1000000 ));
}
2011-06-05 14:45:39 +00:00
}
}
2013-12-11 00:12:44 +00:00
void MainWindow :: selectOpenni ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcOpenNI_PCL );
2013-12-11 00:12:44 +00:00
}
2014-06-03 04:42:08 +00:00
void MainWindow :: selectFreenect ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcFreenect );
2014-06-13 03:47:39 +00:00
}
void MainWindow :: selectOpenniCv ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcOpenNI_CV );
2014-06-13 03:47:39 +00:00
}
void MainWindow :: selectOpenniCvAsus ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcOpenNI_CV_ASUS );
2014-06-03 04:42:08 +00:00
}
2014-06-13 23:44:38 +00:00
void MainWindow :: selectOpenni2 ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcOpenNI2 );
2014-06-13 23:44:38 +00:00
}
2015-04-06 00:28:04 -04:00
void MainWindow :: selectFreenect2 ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcFreenect2 );
2015-04-06 00:28:04 -04:00
}
2017-11-06 00:19:12 -05:00
void MainWindow :: selectK4W2 ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcK4W2 );
}
2020-05-19 11:03:36 -08:00
void MainWindow :: selectK4A ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcK4A );
}
2025-10-07 10:22:16 -07:00
void MainWindow :: selectOrbbecSDK ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcOrbbecSDK );
}
2016-07-26 18:39:34 -04:00
void MainWindow :: selectRealSense ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcRealSense );
}
2018-06-15 13:29:08 -04:00
void MainWindow :: selectRealSense2 ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcRealSense2 );
}
2021-01-08 01:10:23 -05:00
void MainWindow :: selectRealSense2L515 ()
{
2021-01-08 13:04:24 -05:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcRealSense2 , 1 );
2021-01-08 01:10:23 -05:00
}
2018-06-15 13:29:08 -04:00
2019-05-09 19:20:26 -04:00
void MainWindow :: selectRealSense2Stereo ()
{
2019-05-13 18:42:46 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoRealSense2 );
2019-05-09 19:20:26 -04:00
}
2015-04-06 00:28:04 -04:00
void MainWindow :: selectStereoDC1394 ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcDC1394 );
2015-04-06 00:28:04 -04:00
}
2015-04-07 12:00:10 -04:00
void MainWindow :: selectStereoFlyCapture2 ()
{
2015-06-26 18:21:32 -04:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcFlyCapture2 );
2015-04-07 12:00:10 -04:00
}
2016-05-31 19:09:49 -04:00
void MainWindow :: selectStereoZed ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoZed );
}
2021-02-28 10:31:18 -05:00
void MainWindow :: selectStereoZedOC ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoZedOC );
}
2015-04-07 12:00:10 -04:00
2018-10-31 20:27:07 +05:30
void MainWindow :: selectStereoTara ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoTara );
}
2016-05-31 19:09:49 -04:00
void MainWindow :: selectStereoUsb ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoUsb );
}
2013-12-11 00:12:44 +00:00
2020-04-30 00:57:47 -04:00
void MainWindow :: selectMyntEyeS ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoMyntEye );
}
2022-10-23 18:15:23 -07:00
void MainWindow :: selectDepthAIOAKD ()
2021-03-07 12:27:21 -05:00
{
2022-10-23 18:15:23 -07:00
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoDepthAI , 1 ); // variant 1=IMU
}
void MainWindow :: selectDepthAIOAKDLite ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoDepthAI , 0 ); // variant 0=no IMU
2021-03-07 12:27:21 -05:00
}
2024-03-30 18:08:32 -07:00
void MainWindow :: selectDepthAIOAKDPro ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcStereoDepthAI , 2 ); // variant 2=IMU+color
}
2024-06-09 11:13:29 -07:00
void MainWindow :: selectXvisioSeerSense ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcSeerSense );
}
2024-04-14 19:06:04 -07:00
void MainWindow :: selectVLP16 ()
{
_preferencesDialog -> selectSourceDriver ( PreferencesDialog :: kSrcLidarVLP16 );
}
2011-06-05 14:45:39 +00:00
void MainWindow :: dumpTheMemory ()
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdDumpMemory ));
}
void MainWindow :: dumpThePrediction ()
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdDumpPrediction ));
}
2015-04-17 18:30:12 -04:00
void MainWindow :: sendGoal ()
{
UINFO ( "Sending a goal..." );
bool ok = false ;
2015-10-13 12:50:25 -04:00
QString text = QInputDialog :: getText ( this , tr ( "Send a goal" ), tr ( "Goal location ID or label: " ), QLineEdit :: Normal , "" , & ok );
if ( ok && ! text . isEmpty ())
2015-04-17 18:30:12 -04:00
{
2015-10-13 12:50:25 -04:00
_waypoints . clear ();
_waypointsIndex = 0 ;
this -> postGoal ( text );
}
}
void MainWindow :: sendWaypoints ()
{
UINFO ( "Sending waypoints..." );
bool ok = false ;
QString text = QInputDialog :: getText ( this , tr ( "Send waypoints" ), tr ( "Waypoint IDs or labels (separated by spaces): " ), QLineEdit :: Normal , "" , & ok );
if ( ok && ! text . isEmpty ())
{
QStringList wp = text . split ( ' ' );
if ( wp . size () < 2 )
{
QMessageBox :: warning ( this , tr ( "Send waypoints" ), tr ( "At least two waypoints should be set. For only one goal, use send goal action." ));
}
else
{
_waypoints = wp ;
_waypointsIndex = 0 ;
this -> postGoal ( _waypoints . at ( _waypointsIndex ));
}
}
}
2022-10-28 12:25:48 -07:00
void MainWindow :: postGoal ()
{
if ( ! _waypoints . isEmpty ())
{
postGoal ( _waypoints . at ( _waypointsIndex % _waypoints . size ()));
}
}
2015-10-13 12:50:25 -04:00
void MainWindow :: postGoal ( const QString & goal )
{
if ( ! goal . isEmpty ())
{
bool ok = false ;
int id = goal . toInt ( & ok );
2015-04-17 18:30:12 -04:00
_ui -> graphicsView_graphView -> setGlobalPath ( std :: vector < std :: pair < int , Transform > > ()); // clear
2015-10-13 12:50:25 -04:00
UINFO ( "Posting event with goal %s" , goal . toStdString (). c_str ());
if ( ok )
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdGoal , id ));
}
else
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdGoal , goal . toStdString ()));
}
2015-04-17 18:30:12 -04:00
}
}
2015-06-21 20:16:56 -04:00
void MainWindow :: cancelGoal ()
{
UINFO ( "Cancelling goal..." );
2015-10-13 12:50:25 -04:00
_waypoints . clear ();
_waypointsIndex = 0 ;
2015-06-21 20:16:56 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdCancelGoal ));
}
2015-07-26 15:50:39 -04:00
void MainWindow :: label ()
{
UINFO ( "Labelling current location..." );
bool ok = false ;
QString label = QInputDialog :: getText ( this , tr ( "Label current location" ), tr ( "Label: " ), QLineEdit :: Normal , "" , & ok );
if ( ok && ! label . isEmpty ())
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdLabel , label . toStdString (), 0 ));
}
}
2022-01-20 18:56:38 -05:00
void MainWindow :: removeLabel ()
{
UINFO ( "Removing label..." );
bool ok = false ;
QString label = QInputDialog :: getText ( this , tr ( "Remove label" ), tr ( "Label: " ), QLineEdit :: Normal , "" , & ok );
if ( ok && ! label . isEmpty ())
{
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdRemoveLabel , label . toStdString (), 0 ));
}
}
2015-10-16 17:43:31 -04:00
void MainWindow :: updateCacheFromDatabase ()
{
QString dir = getWorkingDirectory ();
QString path = QFileDialog :: getOpenFileName ( this , tr ( "Select file" ), dir , tr ( "RTAB-Map database files (*.db)" ));
if ( ! path . isEmpty ())
{
updateCacheFromDatabase ( path );
}
}
void MainWindow :: updateCacheFromDatabase ( const QString & path )
{
if ( ! path . isEmpty ())
{
DBDriver * driver = DBDriver :: create ();
if ( driver -> openConnection ( path . toStdString ()))
{
UINFO ( "Update cache..." );
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
_progressDialog -> appendText ( tr ( "Downloading the map from \" %1 \" (without poses and links)..." )
2015-10-16 17:43:31 -04:00
. arg ( path ));
std :: set < int > ids ;
driver -> getAllNodeIds ( ids , true );
std :: list < Signature *> signaturesList ;
driver -> loadSignatures ( std :: list < int > ( ids . begin (), ids . end ()), signaturesList );
std :: map < int , Signature > signatures ;
driver -> loadNodeData ( signaturesList );
for ( std :: list < Signature *>:: iterator iter = signaturesList . begin (); iter != signaturesList . end (); ++ iter )
{
signatures . insert ( std :: make_pair (( * iter ) -> id (), * ( * iter )));
delete * iter ;
}
2023-06-16 16:30:07 -07:00
if ( _currentPosesMap . empty () && _currentLinksMap . empty ())
{
_currentPosesMap = driver -> loadOptimizedPoses ();
driver -> getAllLinks ( _currentLinksMap , true , true );
}
2015-10-16 17:43:31 -04:00
RtabmapEvent3DMap event ( signatures , _currentPosesMap , _currentLinksMap );
processRtabmapEvent3DMap ( event );
}
else
{
QMessageBox :: warning ( this , tr ( "Update cache" ), tr ( "Failed to open database \" %1 \" " ). arg ( path ));
}
delete driver ;
}
}
2013-12-11 00:12:44 +00:00
void MainWindow :: downloadAllClouds ()
{
2014-01-22 19:49:28 +00:00
QStringList items ;
2014-02-17 22:58:46 +00:00
items . append ( "Local map optimized" );
2014-07-28 14:43:25 +00:00
items . append ( "Local map not optimized" );
2014-02-17 22:58:46 +00:00
items . append ( "Global map optimized" );
2014-07-28 14:43:25 +00:00
items . append ( "Global map not optimized" );
2014-01-22 19:49:28 +00:00
bool ok ;
2021-09-11 11:35:43 -04:00
QString item = QInputDialog :: getItem ( this , tr ( "Download map" ), tr ( "Options:" ), items , 0 , false , & ok );
2014-01-22 19:49:28 +00:00
if ( ok )
{
2014-02-17 22:58:46 +00:00
bool optimized = false , global = false ;
if ( item . compare ( "Local map optimized" ) == 0 )
2014-01-22 19:49:28 +00:00
{
optimized = true ;
}
2014-02-17 22:58:46 +00:00
else if ( item . compare ( "Local map not optimized" ) == 0 )
2014-01-22 19:49:28 +00:00
{
}
2014-02-17 22:58:46 +00:00
else if ( item . compare ( "Global map optimized" ) == 0 )
2014-01-22 19:49:28 +00:00
{
2014-02-17 22:58:46 +00:00
global = true ;
2014-01-22 19:49:28 +00:00
optimized = true ;
}
2014-02-17 22:58:46 +00:00
else if ( item . compare ( "Global map not optimized" ) == 0 )
2014-01-22 19:49:28 +00:00
{
2014-02-17 22:58:46 +00:00
global = true ;
2014-01-22 19:49:28 +00:00
}
else
{
UFATAL ( "Item \" %s \" not found?!?" , item . toStdString (). c_str ());
}
UINFO ( "Download clouds..." );
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
_progressDialog -> appendText ( tr ( "Downloading the map (global=%1 ,optimized=%2)..." )
2014-02-17 22:58:46 +00:00
. arg ( global ? "true" : "false" ). arg ( optimized ? "true" : "false" ));
2015-08-01 23:42:36 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdPublish3DMap , global , optimized , false ));
2014-01-22 19:49:28 +00:00
}
2013-12-11 00:12:44 +00:00
}
2014-02-11 23:04:22 +00:00
void MainWindow :: downloadPoseGraph ()
{
QStringList items ;
2014-02-17 22:58:46 +00:00
items . append ( "Local map optimized" );
2014-07-28 14:43:25 +00:00
items . append ( "Local map not optimized" );
2014-02-17 22:58:46 +00:00
items . append ( "Global map optimized" );
2014-07-28 14:43:25 +00:00
items . append ( "Global map not optimized" );
2014-02-11 23:04:22 +00:00
bool ok ;
2021-09-11 11:35:43 -04:00
QString item = QInputDialog :: getItem ( this , tr ( "Download graph" ), tr ( "Options:" ), items , 0 , false , & ok );
2014-02-11 23:04:22 +00:00
if ( ok )
{
2014-02-17 22:58:46 +00:00
bool optimized = false , global = false ;
if ( item . compare ( "Local map optimized" ) == 0 )
2014-02-11 23:04:22 +00:00
{
optimized = true ;
}
2014-02-17 22:58:46 +00:00
else if ( item . compare ( "Local map not optimized" ) == 0 )
2014-02-11 23:04:22 +00:00
{
}
2014-02-17 22:58:46 +00:00
else if ( item . compare ( "Global map optimized" ) == 0 )
2014-02-11 23:04:22 +00:00
{
2014-02-17 22:58:46 +00:00
global = true ;
2014-02-11 23:04:22 +00:00
optimized = true ;
}
2014-02-17 22:58:46 +00:00
else if ( item . compare ( "Global map not optimized" ) == 0 )
2014-02-11 23:04:22 +00:00
{
2014-02-17 22:58:46 +00:00
global = true ;
2014-02-11 23:04:22 +00:00
}
else
{
UFATAL ( "Item \" %s \" not found?!?" , item . toStdString (). c_str ());
}
UINFO ( "Download the graph..." );
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
_progressDialog -> appendText ( tr ( "Downloading the graph (global=%1 ,optimized=%2)..." )
2014-02-17 22:58:46 +00:00
. arg ( global ? "true" : "false" ). arg ( optimized ? "true" : "false" ));
2015-08-01 23:42:36 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdPublish3DMap , global , optimized , true ));
2014-02-11 23:04:22 +00:00
}
}
2016-01-07 15:48:18 -05:00
void MainWindow :: anchorCloudsToGroundTruth ()
{
this -> updateMapCloud (
std :: map < int , Transform > ( _currentPosesMap ),
std :: multimap < int , Link > ( _currentLinksMap ),
std :: map < int , int > ( _currentMapIds ),
2016-01-07 19:39:21 -05:00
std :: map < int , std :: string > ( _currentLabels ),
std :: map < int , Transform > ( _currentGTPosesMap ));
2016-01-07 15:48:18 -05:00
}
2011-06-05 14:45:39 +00:00
void MainWindow :: clearTheCache ()
{
2014-10-26 21:47:29 +00:00
_cachedSignatures . clear ();
2016-06-12 17:53:35 -04:00
_cachedMemoryUsage = 0 ;
2018-07-09 11:33:39 -04:00
_cachedWordsCount . clear ();
2016-06-12 17:53:35 -04:00
_cachedClouds . clear ();
_createdCloudsMemoryUsage = 0 ;
2017-02-09 18:32:44 -05:00
_cachedEmptyClouds . clear ();
2016-02-15 19:35:24 -05:00
_previousCloud . first = 0 ;
2016-06-12 13:51:49 -04:00
_previousCloud . second . first . first . reset ();
_previousCloud . second . first . second . reset ();
2016-02-15 19:35:24 -05:00
_previousCloud . second . second . reset ();
2014-07-29 21:48:37 +00:00
_createdScans . clear ();
2016-03-01 17:33:27 -05:00
_createdFeatures . clear ();
2016-05-05 18:25:26 -04:00
_cloudViewer -> clear ();
_cloudViewer -> setBackgroundColor ( _cloudViewer -> getDefaultBackgroundColor ());
_cloudViewer -> clearTrajectory ();
2014-10-30 14:43:43 +00:00
_ui -> widget_mapVisibility -> clear ();
2013-12-11 00:12:44 +00:00
_currentPosesMap . clear ();
2015-12-22 19:32:52 -05:00
_currentGTPosesMap . clear ();
2014-10-30 19:38:58 +00:00
_currentLinksMap . clear ();
_currentMapIds . clear ();
2016-01-07 19:39:21 -05:00
_currentLabels . clear ();
2013-12-11 00:12:44 +00:00
_odometryCorrection = Transform :: getIdentity ();
_lastOdomPose . setNull ();
2016-12-01 21:46:09 -05:00
_ui -> statsToolBox -> clear ();
2013-12-11 00:12:44 +00:00
//disable save cloud action
2014-07-29 14:11:08 +00:00
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( false );
2014-10-30 14:43:43 +00:00
_ui -> actionPost_processing -> setEnabled ( false );
2013-12-11 00:12:44 +00:00
_ui -> actionSave_point_cloud -> setEnabled ( false );
2015-08-10 11:39:40 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( false );
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( false );
2016-07-15 17:46:13 -04:00
_ui -> actionExport_octomap -> setEnabled ( false );
2013-12-11 00:12:44 +00:00
_ui -> actionView_high_res_point_cloud -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_likelihoodCurve -> clear ();
2012-12-11 18:05:05 +00:00
_rawLikelihoodCurve -> clear ();
2011-06-05 14:45:39 +00:00
_posteriorCurve -> clear ();
_lastId = 0 ;
_lastIds . clear ();
2016-07-17 19:58:32 -04:00
_firstStamp = 0.0f ;
2011-06-05 14:45:39 +00:00
_ui -> label_stats_loopClosuresDetected -> setText ( "0" );
_ui -> label_stats_loopClosuresReactivatedDetected -> setText ( "0" );
_ui -> label_stats_loopClosuresRejected -> setText ( "0" );
2012-12-11 19:41:44 +00:00
_refIds . clear ();
_loopClosureIds . clear ();
2018-07-26 14:40:16 -04:00
_cachedLocalizationsCount . clear ();
2014-10-01 17:52:27 +00:00
_ui -> label_refId -> clear ();
_ui -> label_matchId -> clear ();
2014-07-29 21:48:37 +00:00
_ui -> graphicsView_graphView -> clearAll ();
2014-10-01 17:52:27 +00:00
_ui -> imageView_source -> clear ();
_ui -> imageView_loopClosure -> clear ();
2014-12-02 17:01:24 -05:00
_ui -> imageView_odometry -> clear ();
2017-10-20 12:09:34 +02:00
_ui -> imageView_source -> setBackgroundColor ( _ui -> imageView_source -> getDefaultBackgroundColor ());
_ui -> imageView_loopClosure -> setBackgroundColor ( _ui -> imageView_loopClosure -> getDefaultBackgroundColor ());
_ui -> imageView_odometry -> setBackgroundColor ( _ui -> imageView_odometry -> getDefaultBackgroundColor ());
2022-02-12 13:35:23 -05:00
_multiSessionLocWidget -> clear ();
2023-12-17 22:44:11 -08:00
_cachedLocalMaps . clear ();
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
// re-create one if the resolution has changed
delete _octomap ;
2023-12-17 22:44:11 -08:00
_octomap = new OctoMap ( & _cachedLocalMaps , _preferencesDialog -> getAllParameters ());
2016-07-15 17:46:13 -04:00
#endif
2016-08-21 19:33:01 -04:00
_occupancyGrid -> clear ();
2018-09-24 14:36:54 -04:00
_rectCameraModels . clear ();
2021-04-02 19:06:03 -04:00
_rectCameraModelsOdom . clear ();
2011-06-05 14:45:39 +00:00
}
2017-08-22 16:47:48 -04:00
void MainWindow :: openHelp ()
{
if ( _state == kMonitoringPaused || _state == kMonitoring )
{
QDesktopServices :: openUrl ( QUrl ( "http://wiki.ros.org/rtabmap_ros" ));
}
else
{
QDesktopServices :: openUrl ( QUrl ( "https://github.com/introlab/rtabmap/wiki" ));
}
}
2011-06-05 14:45:39 +00:00
void MainWindow :: updateElapsedTime ()
{
if ( _state == kDetecting || _state == kMonitoring )
{
QString format = "hh:mm:ss" ;
_ui -> label_elapsedTime -> setText (( QTime (). fromString ( _ui -> label_elapsedTime -> text (), format ). addMSecs ( _elapsedTime -> restart ())). toString ( format ));
}
}
void MainWindow :: saveFigures ()
{
QList < int > curvesPerFigure ;
QStringList curveNames ;
2024-04-12 17:12:23 -07:00
QStringList curveThresholds ;
_ui -> statsToolBox -> getFiguresSetup ( curvesPerFigure , curveNames , curveThresholds );
2011-06-05 14:45:39 +00:00
QStringList curvesPerFigureStr ;
for ( int i = 0 ; i < curvesPerFigure . size (); ++ i )
{
curvesPerFigureStr . append ( QString :: number ( curvesPerFigure [ i ]));
}
for ( int i = 0 ; i < curveNames . size (); ++ i )
{
curveNames [ i ]. replace ( ' ' , '_' );
}
_preferencesDialog -> saveCustomConfig ( "Figures" , "counts" , curvesPerFigureStr . join ( " " ));
_preferencesDialog -> saveCustomConfig ( "Figures" , "curves" , curveNames . join ( " " ));
2024-04-12 17:12:23 -07:00
_preferencesDialog -> saveCustomConfig ( "Figures" , "thresholds" , curveThresholds . join ( " " ));
2011-06-05 14:45:39 +00:00
}
void MainWindow :: loadFigures ()
{
QString curvesPerFigure = _preferencesDialog -> loadCustomConfig ( "Figures" , "counts" );
QString curveNames = _preferencesDialog -> loadCustomConfig ( "Figures" , "curves" );
2024-04-12 17:12:23 -07:00
QString curveThresholds = _preferencesDialog -> loadCustomConfig ( "Figures" , "thresholds" );
2011-06-05 14:45:39 +00:00
2015-05-03 18:16:55 -04:00
if ( ! curvesPerFigure . isEmpty ())
2011-06-05 14:45:39 +00:00
{
2015-05-03 18:16:55 -04:00
QStringList curvesPerFigureList = curvesPerFigure . split ( " " );
QStringList curvesNamesList = curveNames . split ( " " );
2024-04-12 17:12:23 -07:00
QStringList curveThresholdsList = curveThresholds . split ( " " );
if ( curveThresholdsList [ 0 ]. isEmpty ()) {
curveThresholdsList . clear ();
}
UASSERT ( curveThresholdsList . isEmpty () || curveThresholdsList . size () == curvesNamesList . size ());
2015-05-03 18:16:55 -04:00
int j = 0 ;
for ( int i = 0 ; i < curvesPerFigureList . size (); ++ i )
2011-06-05 14:45:39 +00:00
{
2015-05-03 18:16:55 -04:00
bool ok = false ;
int count = curvesPerFigureList [ i ]. toInt ( & ok );
if ( ! ok )
2011-06-05 14:45:39 +00:00
{
2015-05-03 18:16:55 -04:00
QMessageBox :: warning ( this , "Loading failed" , "Corrupted figures setup..." );
break ;
}
else
{
_ui -> statsToolBox -> addCurve ( curvesNamesList [ j ++ ]. replace ( '_' , ' ' ));
for ( int k = 1 ; k < count && j < curveNames . size (); ++ k )
{
2024-04-12 17:12:23 -07:00
if ( curveThresholdsList . size ())
{
bool ok = false ;
double thresholdValue = curveThresholdsList [ j ]. toDouble ( & ok );
if ( ok )
{
_ui -> statsToolBox -> addThreshold ( curvesNamesList [ j ++ ]. replace ( '_' , ' ' ), thresholdValue );
}
else
{
_ui -> statsToolBox -> addCurve ( curvesNamesList [ j ++ ]. replace ( '_' , ' ' ), false );
}
}
else
{
_ui -> statsToolBox -> addCurve ( curvesNamesList [ j ++ ]. replace ( '_' , ' ' ), false );
}
2015-05-03 18:16:55 -04:00
}
2011-06-05 14:45:39 +00:00
}
}
}
}
2013-12-11 00:12:44 +00:00
void MainWindow :: openPreferences ()
{
_preferencesDialog -> setMonitoringState ( _state == kMonitoring || _state == kMonitoringPaused );
_preferencesDialog -> exec ();
}
2015-06-26 18:21:32 -04:00
void MainWindow :: openPreferencesSource ()
{
_preferencesDialog -> setCurrentPanelToSource ();
openPreferences ();
this -> updateSelectSourceMenu ();
}
2015-06-21 20:47:41 -04:00
void MainWindow :: setDefaultViews ()
{
_ui -> dockWidget_posterior -> setVisible ( false );
_ui -> dockWidget_likelihood -> setVisible ( false );
_ui -> dockWidget_rawlikelihood -> setVisible ( false );
_ui -> dockWidget_statsV2 -> setVisible ( false );
_ui -> dockWidget_console -> setVisible ( false );
_ui -> dockWidget_loopClosureViewer -> setVisible ( false );
_ui -> dockWidget_mapVisibility -> setVisible ( false );
_ui -> dockWidget_graphViewer -> setVisible ( false );
_ui -> dockWidget_odometry -> setVisible ( true );
_ui -> dockWidget_cloudViewer -> setVisible ( true );
_ui -> dockWidget_imageView -> setVisible ( true );
2022-02-12 13:35:23 -05:00
_ui -> dockWidget_multiSessionLoc -> setVisible ( false );
2015-06-21 20:47:41 -04:00
_ui -> toolBar -> setVisible ( _state != kMonitoring && _state != kMonitoringPaused );
_ui -> toolBar_2 -> setVisible ( true );
2015-09-04 13:46:37 -04:00
_ui -> statusbar -> setVisible ( false );
this -> setAspectRatio720p ();
2016-05-05 18:25:26 -04:00
_cloudViewer -> resetCamera ();
2017-01-16 01:32:53 -05:00
_cloudViewer -> setCameraLockZ ( true );
_cloudViewer -> setCameraTargetFollow ( true );
2015-06-21 20:47:41 -04:00
}
2011-07-23 03:55:05 +00:00
void MainWindow :: selectScreenCaptureFormat ( bool checked )
{
if ( checked )
{
QStringList items ;
2014-02-11 23:04:22 +00:00
items << QString ( "Synchronize with map update" ) << QString ( "Synchronize with odometry update" );
2011-07-23 03:55:05 +00:00
bool ok ;
2014-02-11 23:04:22 +00:00
QString item = QInputDialog :: getItem ( this , tr ( "Select synchronization behavior" ), tr ( "Sync:" ), items , 0 , false , & ok );
2011-07-23 03:55:05 +00:00
if ( ok && ! item . isEmpty ())
{
2014-02-11 23:04:22 +00:00
if ( item . compare ( "Synchronize with map update" ) == 0 )
{
_autoScreenCaptureOdomSync = false ;
}
else
{
_autoScreenCaptureOdomSync = true ;
}
2015-09-04 13:46:37 -04:00
if ( _state != kMonitoring && _state != kMonitoringPaused )
{
int r = QMessageBox :: question ( this , tr ( "Hard drive or RAM?" ), tr ( "Save in RAM? Images will be saved on disk when clicking auto screen capture again." ), QMessageBox :: Yes | QMessageBox :: No , QMessageBox :: No );
if ( r == QMessageBox :: No || r == QMessageBox :: Yes )
{
_autoScreenCaptureRAM = r == QMessageBox :: Yes ;
}
else
{
_ui -> actionAuto_screen_capture -> setChecked ( false );
}
2017-12-11 17:09:04 -05:00
r = QMessageBox :: question ( this , tr ( "Save in JPEG?" ), tr ( "Save in JPEG format? Otherwise they are saved in PNG." ), QMessageBox :: Yes | QMessageBox :: No , QMessageBox :: Yes );
if ( r == QMessageBox :: No || r == QMessageBox :: Yes )
{
_autoScreenCapturePNG = r == QMessageBox :: No ;
}
else
{
_ui -> actionAuto_screen_capture -> setChecked ( false );
}
2015-09-04 13:46:37 -04:00
}
2011-07-23 03:55:05 +00:00
}
2015-01-12 15:39:33 -05:00
else
{
_ui -> actionAuto_screen_capture -> setChecked ( false );
}
2011-07-23 03:55:05 +00:00
}
2015-09-04 13:46:37 -04:00
else if ( _autoScreenCaptureCachedImages . size ())
{
QString targetDir = _preferencesDialog -> getWorkingDirectory () + QDir :: separator () + "ScreensCaptured" ;
QDir dir ;
if ( ! dir . exists ( targetDir ))
{
dir . mkdir ( targetDir );
}
targetDir += QDir :: separator ();
targetDir += "Main_window" ;
if ( ! dir . exists ( targetDir ))
{
dir . mkdir ( targetDir );
}
targetDir += QDir :: separator ();
2017-12-11 17:09:04 -05:00
_progressDialog -> setCancelButtonVisible ( true );
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
_progressDialog -> setMaximumSteps ( _autoScreenCaptureCachedImages . size ());
2015-09-04 13:46:37 -04:00
int i = 0 ;
2017-12-11 17:09:04 -05:00
for ( QMap < QString , QByteArray >:: iterator iter = _autoScreenCaptureCachedImages . begin (); iter != _autoScreenCaptureCachedImages . end () && ! _progressDialog -> isCanceled (); ++ iter )
2015-09-04 13:46:37 -04:00
{
QPixmap figure ;
2017-12-11 17:09:04 -05:00
figure . loadFromData ( iter . value (), _autoScreenCapturePNG ? "PNG" : "JPEG" );
figure . save ( targetDir + iter . key (), _autoScreenCapturePNG ? "PNG" : "JPEG" );
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "Saved image \" %1 \" (%2/%3)." ). arg ( targetDir + iter . key ()). arg ( ++ i ). arg ( _autoScreenCaptureCachedImages . size ()));
_progressDialog -> incrementStep ();
2017-12-11 17:09:04 -05:00
QApplication :: processEvents ();
2015-09-04 13:46:37 -04:00
}
_autoScreenCaptureCachedImages . clear ();
2017-10-20 12:09:34 +02:00
_progressDialog -> setValue ( _progressDialog -> maximumSteps ());
2017-12-11 17:09:04 -05:00
_progressDialog -> setCancelButtonVisible ( false );
2015-09-04 13:46:37 -04:00
}
2011-07-23 03:55:05 +00:00
}
2014-02-11 23:04:22 +00:00
void MainWindow :: takeScreenshot ()
{
2015-08-04 14:16:29 -04:00
QDesktopServices :: openUrl ( QUrl :: fromLocalFile ( this -> captureScreen ()));
2014-02-11 23:04:22 +00:00
}
2012-06-24 17:19:34 +00:00
void MainWindow :: setAspectRatio ( int w , int h )
{
QRect rect = this -> geometry ();
if ( h < 100 && w < 100 )
{
// it is a ratio
if ( float ( rect . width ()) / float ( rect . height ()) > float ( w ) / float ( h ))
{
rect . setWidth ( w * ( rect . height () / h ));
rect . setHeight (( rect . height () / h ) * h );
}
else
{
rect . setHeight ( h * ( rect . width () / w ));
rect . setWidth (( rect . width () / w ) * w );
}
}
else
{
// it is absolute size
rect . setWidth ( w );
rect . setHeight ( h );
}
this -> setGeometry ( rect );
}
void MainWindow :: setAspectRatio16_9 ()
{
this -> setAspectRatio ( 16 , 9 );
}
void MainWindow :: setAspectRatio16_10 ()
{
this -> setAspectRatio ( 16 , 10 );
}
void MainWindow :: setAspectRatio4_3 ()
{
this -> setAspectRatio ( 4 , 3 );
}
void MainWindow :: setAspectRatio240p ()
{
this -> setAspectRatio (( 240 * 16 ) / 9 , 240 );
}
void MainWindow :: setAspectRatio360p ()
{
this -> setAspectRatio (( 360 * 16 ) / 9 , 360 );
}
void MainWindow :: setAspectRatio480p ()
{
this -> setAspectRatio (( 480 * 16 ) / 9 , 480 );
}
void MainWindow :: setAspectRatio720p ()
{
this -> setAspectRatio (( 720 * 16 ) / 9 , 720 );
}
void MainWindow :: setAspectRatio1080p ()
{
this -> setAspectRatio (( 1080 * 16 ) / 9 , 1080 );
}
2015-09-04 13:46:37 -04:00
void MainWindow :: setAspectRatioCustom ()
{
bool ok ;
int width = QInputDialog :: getInt ( this , tr ( "Aspect ratio" ), tr ( "Width (pixels):" ), this -> geometry (). width (), 100 , 10000 , 100 , & ok );
if ( ok )
{
int height = QInputDialog :: getInt ( this , tr ( "Aspect ratio" ), tr ( "Height (pixels):" ), this -> geometry (). height (), 100 , 10000 , 100 , & ok );
if ( ok )
{
this -> setAspectRatio ( width , height );
}
}
}
2014-07-29 00:52:35 +00:00
void MainWindow :: exportGridMap ()
2013-12-11 00:12:44 +00:00
{
2016-08-21 19:33:01 -04:00
float gridCellSize = 0.05f ;
2014-07-29 00:52:35 +00:00
bool ok ;
2016-08-21 19:33:01 -04:00
gridCellSize = ( float ) QInputDialog :: getDouble ( this , tr ( "Grid cell size" ), tr ( "Size (m):" ), ( double ) gridCellSize , 0.01 , 1 , 2 , & ok );
2014-07-29 00:52:35 +00:00
if ( ! ok )
{
return ;
}
// create the map
float xMin = 0.0f , yMin = 0.0f ;
2016-08-21 19:33:01 -04:00
cv :: Mat pixels ;
#ifdef RTABMAP_OCTOMAP
if ( _preferencesDialog -> isOctomap2dGrid ())
{
pixels = _octomap -> createProjectionMap ( xMin , yMin , gridCellSize , 0 );
}
else
#endif
{
2017-04-07 18:31:11 -04:00
pixels = _occupancyGrid -> getMap ( xMin , yMin );
2016-08-21 19:33:01 -04:00
}
2014-07-29 00:52:35 +00:00
if ( ! pixels . empty ())
{
cv :: Mat map8U ( pixels . rows , pixels . cols , CV_8U );
//convert to gray scaled map
for ( int i = 0 ; i < pixels . rows ; ++ i )
{
for ( int j = 0 ; j < pixels . cols ; ++ j )
{
char v = pixels . at < char > ( i , j );
unsigned char gray ;
if ( v == 0 )
{
gray = 178 ;
2014-07-14 01:25:28 +00:00
}
2014-07-29 00:52:35 +00:00
else if ( v == 100 )
{
gray = 0 ;
}
else // -1
{
gray = 89 ;
}
map8U . at < unsigned char > ( i , j ) = gray ;
2014-06-08 05:16:14 +00:00
}
2013-12-11 00:12:44 +00:00
}
2014-07-29 21:48:37 +00:00
2014-07-29 00:52:35 +00:00
QImage image = uCvMat2QImage ( map8U , false );
2014-07-29 21:48:37 +00:00
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save to ..." ), "grid.png" , tr ( "Image (*.png *.bmp)" ));
2014-07-29 00:52:35 +00:00
if ( ! path . isEmpty ())
{
2014-07-29 21:48:37 +00:00
if ( QFileInfo ( path ). suffix () != "png" && QFileInfo ( path ). suffix () != "bmp" )
{
//use png by default
path += ".png" ;
}
QImage img = image . mirrored ( false , true ). transformed ( QTransform (). rotate ( - 90 ));
QPixmap :: fromImage ( img ). save ( path );
QDesktopServices :: openUrl ( QUrl :: fromLocalFile ( path ));
2014-07-29 00:52:35 +00:00
}
}
}
2016-03-14 17:27:53 -04:00
void MainWindow :: exportClouds ()
{
if ( _exportCloudsDialog -> isVisible ())
{
return ;
}
2025-12-01 19:42:27 -08:00
std :: map < int , Transform > poses = ! _ui -> widget_mapVisibility -> isEmpty () ? _ui -> widget_mapVisibility -> getVisiblePoses () : _currentPosesMap ;
2017-02-09 18:32:44 -05:00
// Use ground truth poses if current clouds are using them
if ( _currentGTPosesMap . size () && _ui -> actionAnchor_clouds_to_ground_truth -> isChecked ())
{
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
std :: map < int , Transform >:: iterator gtIter = _currentGTPosesMap . find ( iter -> first );
if ( gtIter != _currentGTPosesMap . end ())
{
iter -> second = gtIter -> second ;
}
else
{
UWARN ( "Not found ground truth pose for node %d" , iter -> first );
}
}
}
2016-03-14 17:27:53 -04:00
_exportCloudsDialog -> exportClouds (
2017-02-09 18:32:44 -05:00
poses ,
2016-09-07 12:15:44 -04:00
_currentLinksMap ,
2016-02-15 19:35:24 -05:00
_currentMapIds ,
_cachedSignatures ,
2016-06-12 17:53:35 -04:00
_cachedClouds ,
2017-05-11 13:20:51 -04:00
_createdScans ,
2016-05-31 19:09:49 -04:00
_preferencesDialog -> getWorkingDirectory (),
_preferencesDialog -> getAllParameters ());
2013-12-11 00:12:44 +00:00
}
2014-07-29 00:52:35 +00:00
void MainWindow :: viewClouds ()
2013-12-11 00:12:44 +00:00
{
2016-03-14 17:27:53 -04:00
if ( _exportCloudsDialog -> isVisible ())
2014-10-01 17:52:27 +00:00
{
2016-02-15 19:35:24 -05:00
return ;
2014-10-01 17:52:27 +00:00
}
2014-06-23 20:43:08 +00:00
2025-12-01 19:42:27 -08:00
std :: map < int , Transform > poses = ! _ui -> widget_mapVisibility -> isEmpty () ? _ui -> widget_mapVisibility -> getVisiblePoses () : _currentPosesMap ;
2017-02-09 18:32:44 -05:00
// Use ground truth poses if current clouds are using them
if ( _currentGTPosesMap . size () && _ui -> actionAnchor_clouds_to_ground_truth -> isChecked ())
{
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
{
std :: map < int , Transform >:: iterator gtIter = _currentGTPosesMap . find ( iter -> first );
if ( gtIter != _currentGTPosesMap . end ())
{
iter -> second = gtIter -> second ;
}
else
{
UWARN ( "Not found ground truth pose for node %d" , iter -> first );
}
}
}
2016-03-14 17:27:53 -04:00
_exportCloudsDialog -> viewClouds (
2017-02-09 18:32:44 -05:00
poses ,
2016-09-07 12:15:44 -04:00
_currentLinksMap ,
2016-02-15 19:35:24 -05:00
_currentMapIds ,
_cachedSignatures ,
2016-06-12 17:53:35 -04:00
_cachedClouds ,
2017-05-11 13:20:51 -04:00
_createdScans ,
2016-05-31 19:09:49 -04:00
_preferencesDialog -> getWorkingDirectory (),
_preferencesDialog -> getAllParameters ());
2013-12-11 00:12:44 +00:00
}
2016-07-15 17:46:13 -04:00
void MainWindow :: exportOctomap ()
{
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
if ( _octomap -> octree () -> size ())
2016-07-15 17:46:13 -04:00
{
QString path = QFileDialog :: getSaveFileName (
this ,
tr ( "Save File" ),
this -> getWorkingDirectory () + "/" + "octomap.bt" ,
tr ( "Octomap file (*.bt)" ));
if ( ! path . isEmpty ())
{
if ( _octomap -> writeBinary ( path . toStdString ()))
{
QMessageBox :: information ( this ,
tr ( "Export octomap..." ),
tr ( "Octomap successfully saved to \" %1 \" ." )
. arg ( path ));
}
else
{
QMessageBox :: information ( this ,
tr ( "Export octomap..." ),
tr ( "Failed to save octomap to \" %1 \" !" )
. arg ( path ));
}
}
}
else
{
UERROR ( "Empty octomap." );
}
#else
UERROR ( "Cannot export octomap, RTAB-Map is not built with it." );
#endif
}
2015-10-16 20:11:24 -04:00
void MainWindow :: exportImages ()
{
if ( _cachedSignatures . empty ())
{
QMessageBox :: warning ( this , tr ( "Export images..." ), tr ( "Cannot export images, the cache is empty!" ));
return ;
}
2025-12-01 19:42:27 -08:00
std :: map < int , Transform > poses = ! _ui -> widget_mapVisibility -> isEmpty () ? _ui -> widget_mapVisibility -> getVisiblePoses () : _currentPosesMap ;
2015-10-16 20:11:24 -04:00
if ( poses . empty ())
{
QMessageBox :: warning ( this , tr ( "Export images..." ), tr ( "There is no map!" ));
return ;
}
2016-07-17 17:44:12 -04:00
QStringList formats ;
2021-02-02 09:36:12 -05:00
formats . push_back ( "id.jpg" );
formats . push_back ( "id.png" );
formats . push_back ( "timestamp.jpg" );
formats . push_back ( "timestamp.png" );
2016-07-17 17:44:12 -04:00
bool ok ;
2021-02-02 09:36:12 -05:00
QString format = QInputDialog :: getItem ( this , tr ( "Which RGB format?" ), tr ( "Format:" ), formats , 0 , false , & ok );
2016-07-17 17:44:12 -04:00
if ( ! ok )
{
return ;
}
2021-02-02 09:36:12 -05:00
QString ext = format . split ( '.' ). back ();
bool useStamp = format . split ( '.' ). front (). compare ( "timestamp" ) == 0 ;
2016-07-17 17:44:12 -04:00
2021-02-02 09:36:12 -05:00
QMap < int , double > stamps ;
2015-10-16 20:11:24 -04:00
QString path = QFileDialog :: getExistingDirectory ( this , tr ( "Select directory where to save images..." ), this -> getWorkingDirectory ());
2016-10-25 14:54:02 -04:00
if ( ! path . isEmpty ())
2015-10-16 20:11:24 -04:00
{
SensorData data ;
if ( _cachedSignatures . contains ( poses . rbegin () -> first ))
{
data = _cachedSignatures . value ( poses . rbegin () -> first ). sensorData ();
data . uncompressData ();
}
2017-10-20 12:09:34 +02:00
_progressDialog -> resetProgress ();
_progressDialog -> show ();
_progressDialog -> setMaximumSteps ( _cachedSignatures . size ());
2015-10-16 20:11:24 -04:00
2016-10-25 14:54:02 -04:00
unsigned int saved = 0 ;
2021-02-02 09:36:12 -05:00
bool calibrationSaved = false ;
2016-10-25 14:54:02 -04:00
for ( std :: map < int , Transform >:: iterator iter = poses . begin (); iter != poses . end (); ++ iter )
2015-10-16 20:11:24 -04:00
{
2021-02-02 09:36:12 -05:00
QString id = QString :: number ( iter -> first );
2016-10-25 14:54:02 -04:00
SensorData data ;
if ( _cachedSignatures . contains ( iter -> first ))
{
data = _cachedSignatures . value ( iter -> first ). sensorData ();
data . uncompressData ();
2021-02-02 09:36:12 -05:00
if ( ! calibrationSaved )
{
if ( ! data . imageRaw (). empty () && ! data . rightRaw (). empty ())
{
QDir dir ;
dir . mkdir ( QString ( "%1/left" ). arg ( path ));
dir . mkdir ( QString ( "%1/right" ). arg ( path ));
2022-07-20 15:20:14 -04:00
if ( data . stereoCameraModels (). size () > 1 )
{
UERROR ( "Only one stereo camera calibration can be saved at this time (%d detected)" , ( int ) data . stereoCameraModels (). size ());
}
else if ( data . stereoCameraModels (). size () == 1 && data . stereoCameraModels (). front (). isValidForProjection ())
2021-02-02 09:36:12 -05:00
{
std :: string cameraName = "calibration" ;
StereoCameraModel model (
cameraName ,
data . imageRaw (). size (),
2022-07-20 15:20:14 -04:00
data . stereoCameraModels ()[ 0 ]. left (). K (),
data . stereoCameraModels ()[ 0 ]. left (). D (),
data . stereoCameraModels ()[ 0 ]. left (). R (),
data . stereoCameraModels ()[ 0 ]. left (). P (),
2021-02-02 09:36:12 -05:00
data . rightRaw (). size (),
2022-07-20 15:20:14 -04:00
data . stereoCameraModels ()[ 0 ]. right (). K (),
data . stereoCameraModels ()[ 0 ]. right (). D (),
data . stereoCameraModels ()[ 0 ]. right (). R (),
data . stereoCameraModels ()[ 0 ]. right (). P (),
data . stereoCameraModels ()[ 0 ]. R (),
data . stereoCameraModels ()[ 0 ]. T (),
data . stereoCameraModels ()[ 0 ]. E (),
data . stereoCameraModels ()[ 0 ]. F (),
data . stereoCameraModels ()[ 0 ]. left (). localTransform ());
2021-02-02 09:36:12 -05:00
if ( model . save ( path . toStdString ()))
{
calibrationSaved = true ;
UINFO ( "Saved stereo calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
else
{
UERROR ( "Failed saving calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
}
}
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 ));
2025-06-01 14:14:29 -07:00
if ( ! data . depthConfidenceRaw (). empty ())
{
QDir dir ;
dir . mkdir ( QString ( "%1/confidence" ). arg ( path ));
}
2021-02-02 09:36:12 -05:00
}
if ( data . cameraModels (). size () > 1 )
{
UERROR ( "Only one camera calibration can be saved at this time (%d detected)" , ( int ) data . cameraModels (). size ());
}
else if ( data . cameraModels (). size () == 1 && data . cameraModels (). front (). isValidForProjection ())
{
std :: string cameraName = "calibration" ;
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 ()))
{
calibrationSaved = true ;
UINFO ( "Saved calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
else
{
UERROR ( "Failed saving calibration \" %s \" " , ( path . toStdString () + "/" + cameraName ). c_str ());
}
}
}
}
if ( ! data . imageRaw (). empty () && useStamp )
{
double stamp = _cachedSignatures . value ( iter -> first ). getStamp ();
if ( stamp == 0.0 )
{
UWARN ( "Node %d has null timestamp! Using id instead!" , iter -> first );
}
else
{
id = QString :: number ( stamp , 'f' );
}
}
2016-10-25 14:54:02 -04:00
}
QString info ;
bool warn = false ;
if ( ! data . imageRaw (). empty () && ! data . rightRaw (). empty ())
{
2021-02-02 09:36:12 -05:00
if ( ! cv :: imwrite ( QString ( "%1/left/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . imageRaw ()))
UWARN ( "Failed saving \" %s \" " , QString ( "%1/left/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (). c_str ());
if ( ! cv :: imwrite ( QString ( "%1/right/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . rightRaw ()))
UWARN ( "Failed saving \" %s \" " , QString ( "%1/right/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (). c_str ());
2016-10-25 14:54:02 -04:00
info = tr ( "Saved left/%1.%2 and right/%1.%2." ). arg ( id ). arg ( ext );
}
else if ( ! data . imageRaw (). empty () && ! data . depthRaw (). empty ())
{
2021-02-02 09:36:12 -05:00
if ( ! cv :: imwrite ( QString ( "%1/rgb/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . imageRaw ()))
UWARN ( "Failed saving \" %s \" " , QString ( "%1/rgb/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (). c_str ());
if ( ! cv :: imwrite ( QString ( "%1/depth/%2.png" ). arg ( path ). arg ( id ). toStdString (), data . depthRaw (). type () == CV_32FC1 ? util2d :: cvtDepthFromFloat ( data . depthRaw ()) : data . depthRaw ()))
UWARN ( "Failed saving \" %s \" " , QString ( "%1/depth/%2.png" ). arg ( path ). arg ( id ). toStdString (). c_str ());
2025-06-01 14:14:29 -07:00
if ( data . depthConfidenceRaw (). empty ()) {
info = tr ( "Saved rgb/%1.%2 and depth/%1.png." ). arg ( id ). arg ( ext );
}
else {
if ( ! cv :: imwrite ( QString ( "%1/confidence/%2.png" ). arg ( path ). arg ( id ). toStdString (), data . depthConfidenceRaw ()))
UWARN ( "Failed saving \" %s \" " , QString ( "%1/confidence/%2.png" ). arg ( path ). arg ( id ). toStdString (). c_str ());
info = tr ( "Saved rgb/%1.%2, depth/%1.png and confidence/%1.png." ). arg ( id ). arg ( ext );
}
2016-10-25 14:54:02 -04:00
}
else if ( ! data . imageRaw (). empty ())
{
2021-02-02 09:36:12 -05:00
if ( ! cv :: imwrite ( QString ( "%1/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (), data . imageRaw ()))
UWARN ( "Failed saving \" %s \" " , QString ( "%1/%2.%3" ). arg ( path ). arg ( id ). arg ( ext ). toStdString (). c_str ());
else
info = tr ( "Saved %1.%2." ). arg ( id ). arg ( ext );
2016-10-25 14:54:02 -04:00
}
else
{
info = tr ( "No images saved for node %1!" ). arg ( id );
warn = true ;
}
saved += warn ? 0 : 1 ;
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( info , ! warn ? Qt :: black : Qt :: darkYellow );
_progressDialog -> incrementStep ();
2016-10-25 14:54:02 -04:00
QApplication :: processEvents ();
2015-10-16 20:11:24 -04:00
}
2016-10-25 14:54:02 -04:00
if ( saved != poses . size ())
2015-10-16 20:11:24 -04:00
{
2017-10-20 12:09:34 +02:00
_progressDialog -> setAutoClose ( false );
_progressDialog -> appendText ( tr ( "%1 images of %2 saved to \" %3 \" ." ). arg ( saved ). arg ( poses . size ()). arg ( path ));
2015-10-16 20:11:24 -04:00
}
else
{
2017-10-20 12:09:34 +02:00
_progressDialog -> appendText ( tr ( "%1 images saved to \" %2 \" ." ). arg ( saved ). arg ( path ));
2015-10-16 20:11:24 -04:00
}
2021-02-02 09:36:12 -05:00
if ( ! calibrationSaved )
{
QMessageBox :: warning ( this ,
tr ( "Export images..." ),
tr ( "Data in the cache don't seem to have valid calibration. Calibration file will not be saved. Try refreshing the cache (with clouds)." ));
}
2017-10-20 12:09:34 +02:00
_progressDialog -> setValue ( _progressDialog -> maximumSteps ());
2015-10-16 20:11:24 -04:00
}
}
2015-08-10 11:39:40 -04:00
void MainWindow :: exportBundlerFormat ()
{
2017-08-07 16:31:49 -04:00
if ( _exportBundlerDialog -> isVisible ())
{
return ;
}
2025-12-01 19:42:27 -08:00
std :: map < int , Transform > posesIn = ! _ui -> widget_mapVisibility -> isEmpty () ? _ui -> widget_mapVisibility -> getVisiblePoses () : _currentPosesMap ;
2015-08-10 11:39:40 -04:00
2017-02-09 18:32:44 -05:00
// Use ground truth poses if current clouds are using them
if ( _currentGTPosesMap . size () && _ui -> actionAnchor_clouds_to_ground_truth -> isChecked ())
{
for ( std :: map < int , Transform >:: iterator iter = posesIn . begin (); iter != posesIn . end (); ++ iter )
{
std :: map < int , Transform >:: iterator gtIter = _currentGTPosesMap . find ( iter -> first );
if ( gtIter != _currentGTPosesMap . end ())
{
iter -> second = gtIter -> second ;
}
else
{
UWARN ( "Not found ground truth pose for node %d" , iter -> first );
}
}
}
2015-08-10 11:39:40 -04:00
std :: map < int , Transform > poses ;
for ( std :: map < int , Transform >:: iterator iter = posesIn . begin (); iter != posesIn . end (); ++ iter )
{
if ( _cachedSignatures . contains ( iter -> first ))
{
if ( _cachedSignatures [ iter -> first ]. sensorData (). imageRaw (). empty () &&
_cachedSignatures [ iter -> first ]. sensorData (). imageCompressed (). empty ())
{
UWARN ( "Missing image in cache for node %d" , iter -> first );
}
2022-07-20 15:20:14 -04:00
else if (( _cachedSignatures [ iter -> first ]. sensorData (). cameraModels (). size () == 1 &&
_cachedSignatures [ iter -> first ]. sensorData (). cameraModels (). at ( 0 ). isValidForProjection ()) ||
( _cachedSignatures [ iter -> first ]. sensorData (). stereoCameraModels (). size () == 1 &&
_cachedSignatures [ iter -> first ]. sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ()))
2015-08-10 11:39:40 -04:00
{
poses . insert ( * iter );
}
else
{
UWARN ( "Missing calibration for node %d" , iter -> first );
}
}
else
{
UWARN ( "Did not find node %d in cache" , iter -> first );
}
}
if ( poses . size ())
{
2018-10-24 20:01:57 -04:00
_exportBundlerDialog -> exportBundler (
poses ,
_currentLinksMap ,
2018-10-25 16:31:13 -04:00
_cachedSignatures ,
_preferencesDialog -> getAllParameters ());
2025-04-13 15:58:21 -07:00
if ( ! poses . empty ())
{
UINFO ( "Updating map..." );
this -> updateMapCloud (
poses ,
std :: multimap < int , Link > ( _currentLinksMap ),
std :: map < int , int > ( _currentMapIds ),
std :: map < int , std :: string > ( _currentLabels ),
std :: map < int , Transform > ( _currentGTPosesMap ),
std :: map < int , Transform > (),
std :: multimap < int , Link > (),
false );
UINFO ( "Updating map... done!" );
}
2015-08-10 11:39:40 -04:00
}
else
{
2015-10-16 20:11:24 -04:00
QMessageBox :: warning ( this , tr ( "Exporting cameras..." ), tr ( "No poses exported because of missing images. Try refreshing the cache (with clouds)." ));
2015-08-10 11:39:40 -04:00
}
}
2013-12-11 00:12:44 +00:00
void MainWindow :: resetOdometry ()
{
UINFO ( "reset odometry" );
this -> post ( new OdometryResetEvent ());
}
void MainWindow :: triggerNewMap ()
{
UINFO ( "trigger a new map" );
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdTriggerNewMap ));
}
2014-06-23 00:26:14 +00:00
void MainWindow :: dataRecorder ()
{
2015-03-29 09:23:00 -04:00
if ( _dataRecorder == 0 )
2014-06-23 00:26:14 +00:00
{
2015-04-23 22:01:41 -04:00
QString path = QFileDialog :: getSaveFileName ( this , tr ( "Save to..." ), _preferencesDialog -> getWorkingDirectory () + "/output.db" , "RTAB-Map database (*.db)" );
2014-06-23 00:26:14 +00:00
if ( ! path . isEmpty ())
{
int r = QMessageBox :: question ( this , tr ( "Hard drive or RAM?" ), tr ( "Save in RAM?" ), QMessageBox :: Yes | QMessageBox :: No , QMessageBox :: Yes );
if ( r == QMessageBox :: No || r == QMessageBox :: Yes )
{
bool recordInRAM = r == QMessageBox :: Yes ;
2015-03-29 09:23:00 -04:00
_dataRecorder = new DataRecorder ( this );
_dataRecorder -> setWindowFlags ( Qt :: Dialog );
_dataRecorder -> setAttribute ( Qt :: WA_DeleteOnClose , true );
_dataRecorder -> setWindowTitle ( tr ( "Data recorder (%1)" ). arg ( path ));
2014-06-23 00:26:14 +00:00
2015-03-29 09:23:00 -04:00
if ( _dataRecorder -> init ( path , recordInRAM ))
2014-06-23 00:26:14 +00:00
{
2015-03-29 09:23:00 -04:00
this -> connect ( _dataRecorder , SIGNAL ( destroyed ( QObject * )), this , SLOT ( dataRecorderDestroyed ()));
_dataRecorder -> show ();
_dataRecorder -> registerToEventsManager ();
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2015-03-29 09:23:00 -04:00
{
2024-04-14 19:06:04 -07:00
UEventsManager :: createPipe ( _sensorCapture , _dataRecorder , "SensorEvent" );
2015-03-29 09:23:00 -04:00
}
_ui -> actionData_recorder -> setEnabled ( false );
2014-06-23 00:26:14 +00:00
}
else
{
QMessageBox :: warning ( this , tr ( "" ), tr ( "Cannot initialize the data recorder!" ));
UERROR ( "Cannot initialize the data recorder!" );
2015-03-29 09:23:00 -04:00
delete _dataRecorder ;
_dataRecorder = 0 ;
2014-06-23 00:26:14 +00:00
}
}
}
}
else
{
2015-03-29 09:23:00 -04:00
UERROR ( "Only one recorder at the same time." );
2014-06-23 00:26:14 +00:00
}
}
2015-03-29 09:23:00 -04:00
void MainWindow :: dataRecorderDestroyed ()
{
_ui -> actionData_recorder -> setEnabled ( true );
_dataRecorder = 0 ;
}
2011-07-23 03:55:05 +00:00
//END ACTIONS
2011-06-05 14:45:39 +00:00
// STATES
2013-12-11 00:12:44 +00:00
// in monitoring state, only some actions are enabled
void MainWindow :: setMonitoringState ( bool pauseChecked )
{
this -> changeState ( pauseChecked ? kMonitoringPaused : kMonitoring );
}
2011-06-05 14:45:39 +00:00
// Must be called by the GUI thread, use signal StateChanged()
void MainWindow :: changeState ( MainWindow :: State newState )
{
2015-03-31 15:45:38 -04:00
bool monitoring = newState == kMonitoring || newState == kMonitoringPaused ;
2016-03-17 10:47:50 -04:00
_ui -> label_source -> setVisible ( ! monitoring );
_ui -> label_stats_source -> setVisible ( ! monitoring );
2015-03-31 15:45:38 -04:00
_ui -> actionNew_database -> setVisible ( ! monitoring );
_ui -> actionOpen_database -> setVisible ( ! monitoring );
_ui -> actionClose_database -> setVisible ( ! monitoring );
_ui -> actionEdit_database -> setVisible ( ! monitoring );
_ui -> actionStart -> setVisible ( ! monitoring );
_ui -> actionStop -> setVisible ( ! monitoring );
_ui -> actionDump_the_memory -> setVisible ( ! monitoring );
_ui -> actionDump_the_prediction_matrix -> setVisible ( ! monitoring );
_ui -> actionGenerate_map -> setVisible ( ! monitoring );
2015-10-16 17:43:31 -04:00
_ui -> actionUpdate_cache_from_database -> setVisible ( monitoring );
2015-03-31 15:45:38 -04:00
_ui -> actionData_recorder -> setVisible ( ! monitoring );
_ui -> menuSelect_source -> menuAction () -> setVisible ( ! monitoring );
_ui -> doubleSpinBox_stats_imgRate -> setVisible ( ! monitoring );
_ui -> doubleSpinBox_stats_imgRate_label -> setVisible ( ! monitoring );
2015-07-19 14:11:09 -04:00
bool wasMonitoring = _state == kMonitoring || _state == kMonitoringPaused ;
if ( wasMonitoring != monitoring )
{
_ui -> toolBar -> setVisible ( ! monitoring );
_ui -> toolBar -> toggleViewAction () -> setVisible ( ! monitoring );
}
2015-03-31 15:45:38 -04:00
QList < QAction *> actions = _ui -> menuTools -> actions ();
for ( int i = 0 ; i < actions . size (); ++ i )
{
if ( actions . at ( i ) -> isSeparator ())
{
actions . at ( i ) -> setVisible ( ! monitoring );
}
}
actions = _ui -> menuFile -> actions ();
2017-05-22 11:48:01 -04:00
if ( actions . size () == 16 )
2015-03-31 15:45:38 -04:00
{
if ( actions . at ( 2 ) -> isSeparator ())
{
actions . at ( 2 ) -> setVisible ( ! monitoring );
}
else
{
UWARN ( "Menu File separators have not the same order." );
}
2017-05-22 11:48:01 -04:00
if ( actions . at ( 12 ) -> isSeparator ())
2015-03-31 15:45:38 -04:00
{
2017-05-22 11:48:01 -04:00
actions . at ( 12 ) -> setVisible ( ! monitoring );
2015-03-31 15:45:38 -04:00
}
else
{
UWARN ( "Menu File separators have not the same order." );
}
}
else
{
2015-08-10 11:39:40 -04:00
UWARN ( "Menu File actions size has changed (%d)" , actions . size ());
2015-03-31 15:45:38 -04:00
}
actions = _ui -> menuProcess -> actions ();
if ( actions . size () >= 2 )
{
if ( actions . at ( 1 ) -> isSeparator ())
{
actions . at ( 1 ) -> setVisible ( ! monitoring );
}
else
{
UWARN ( "Menu File separators have not the same order." );
}
}
else
{
UWARN ( "Menu File separators have not the same order." );
}
2016-01-07 15:48:18 -05:00
_ui -> actionAnchor_clouds_to_ground_truth -> setEnabled ( ! _currentGTPosesMap . empty ());
2011-06-05 14:45:39 +00:00
switch ( newState )
{
2014-10-28 01:20:57 +00:00
case kIdle : // RTAB-Map is not initialized yet
_ui -> actionNew_database -> setEnabled ( true );
_ui -> actionOpen_database -> setEnabled ( true );
_ui -> actionClose_database -> setEnabled ( false );
_ui -> actionEdit_database -> setEnabled ( true );
_ui -> actionStart -> setEnabled ( false );
_ui -> actionPause -> setEnabled ( false );
_ui -> actionPause -> setChecked ( false );
_ui -> actionPause -> setToolTip ( tr ( "Pause" ));
_ui -> actionStop -> setEnabled ( false );
_ui -> actionPause_on_match -> setEnabled ( true );
_ui -> actionPause_on_local_loop_detection -> setEnabled ( true );
_ui -> actionPause_when_a_loop_hypothesis_is_rejected -> setEnabled ( true );
_ui -> actionDump_the_memory -> setEnabled ( false );
_ui -> actionDump_the_prediction_matrix -> setEnabled ( false );
_ui -> actionDelete_memory -> setEnabled ( false );
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( _cachedSignatures . size () >= 2 && _currentPosesMap . size () >= 2 && _currentLinksMap . size () >= 1 );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2014-10-28 01:20:57 +00:00
_ui -> actionGenerate_map -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> menuExport_poses -> setEnabled ( ! _currentPosesMap . empty ());
2016-06-12 17:53:35 -04:00
_ui -> actionSave_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
_ui -> actionView_high_res_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
2017-04-07 18:31:11 -04:00
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( ! _occupancyGrid -> addedNodes (). empty ());
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
_ui -> actionExport_octomap -> setEnabled ( _octomap -> octree () -> size ());
2016-07-15 17:46:13 -04:00
#else
_ui -> actionExport_octomap -> setEnabled ( false );
#endif
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_all_clouds -> setEnabled ( false );
_ui -> actionDownload_graph -> setEnabled ( false );
2015-12-02 11:48:07 -05:00
_ui -> menuSelect_source -> setEnabled ( true );
2015-09-21 18:12:55 -04:00
_ui -> actionLabel_current_location -> setEnabled ( false );
_ui -> actionSend_goal -> setEnabled ( false );
_ui -> actionCancel_goal -> setEnabled ( false );
2015-12-02 11:48:07 -05:00
_ui -> toolBar -> findChild < QAction *> ( "toolbar_source" ) -> setEnabled ( true );
2014-10-28 01:20:57 +00:00
_ui -> actionTrigger_a_new_map -> setEnabled ( false );
_ui -> doubleSpinBox_stats_imgRate -> setEnabled ( true );
_ui -> statusbar -> clearMessage ();
_state = newState ;
_oneSecondTimer -> stop ();
break ;
case kApplicationClosing :
case kClosing :
_ui -> actionStart -> setEnabled ( false );
_ui -> actionPause -> setEnabled ( false );
_ui -> actionStop -> setEnabled ( false );
_state = newState ;
break ;
case kInitializing :
_ui -> actionNew_database -> setEnabled ( false );
_ui -> actionOpen_database -> setEnabled ( false );
2014-10-28 04:16:36 +00:00
_ui -> actionClose_database -> setEnabled ( false );
2014-10-28 01:20:57 +00:00
_ui -> actionEdit_database -> setEnabled ( false );
_state = newState ;
break ;
case kInitialized :
_ui -> actionNew_database -> setEnabled ( false );
_ui -> actionOpen_database -> setEnabled ( false );
_ui -> actionClose_database -> setEnabled ( true );
_ui -> actionEdit_database -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionStart -> setEnabled ( true );
_ui -> actionPause -> setEnabled ( false );
_ui -> actionPause -> setChecked ( false );
_ui -> actionPause -> setToolTip ( tr ( "Pause" ));
_ui -> actionStop -> setEnabled ( false );
_ui -> actionPause_on_match -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
_ui -> actionPause_on_local_loop_detection -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> actionPause_when_a_loop_hypothesis_is_rejected -> setEnabled ( true );
_ui -> actionDump_the_memory -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
_ui -> actionDump_the_prediction_matrix -> setEnabled ( true );
2017-01-03 21:05:00 -05:00
_ui -> actionDelete_memory -> setEnabled ( _openedDatabasePath . isEmpty ());
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( _cachedSignatures . size () >= 2 && _currentPosesMap . size () >= 2 && _currentLinksMap . size () >= 1 );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2011-06-05 14:45:39 +00:00
_ui -> actionGenerate_map -> setEnabled ( true );
2015-10-16 20:11:24 -04:00
_ui -> menuExport_poses -> setEnabled ( ! _currentPosesMap . empty ());
2016-06-12 17:53:35 -04:00
_ui -> actionSave_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
_ui -> actionView_high_res_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
2017-04-07 18:31:11 -04:00
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( ! _occupancyGrid -> addedNodes (). empty ());
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
_ui -> actionExport_octomap -> setEnabled ( _octomap -> octree () -> size ());
2016-07-15 17:46:13 -04:00
#else
_ui -> actionExport_octomap -> setEnabled ( false );
#endif
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2013-12-11 00:12:44 +00:00
_ui -> actionDownload_all_clouds -> setEnabled ( true );
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_graph -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> menuSelect_source -> setEnabled ( true );
2015-09-21 18:12:55 -04:00
_ui -> actionLabel_current_location -> setEnabled ( true );
_ui -> actionSend_goal -> setEnabled ( true );
_ui -> actionCancel_goal -> setEnabled ( true );
2015-03-31 15:45:38 -04:00
_ui -> toolBar -> findChild < QAction *> ( "toolbar_source" ) -> setEnabled ( true );
2014-10-28 01:20:57 +00:00
_ui -> actionTrigger_a_new_map -> setEnabled ( true );
2012-03-03 01:46:30 +00:00
_ui -> doubleSpinBox_stats_imgRate -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> statusbar -> clearMessage ();
_state = newState ;
_oneSecondTimer -> stop ();
break ;
case kStartingDetection :
_ui -> actionStart -> setEnabled ( false );
2014-10-28 01:20:57 +00:00
_state = newState ;
2011-06-05 14:45:39 +00:00
break ;
case kDetecting :
2014-10-28 04:16:36 +00:00
_ui -> actionNew_database -> setEnabled ( false );
_ui -> actionOpen_database -> setEnabled ( false );
_ui -> actionClose_database -> setEnabled ( false );
_ui -> actionEdit_database -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionStart -> setEnabled ( false );
_ui -> actionPause -> setEnabled ( true );
_ui -> actionPause -> setChecked ( false );
_ui -> actionPause -> setToolTip ( tr ( "Pause" ));
_ui -> actionStop -> setEnabled ( true );
_ui -> actionPause_on_match -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
_ui -> actionPause_on_local_loop_detection -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> actionPause_when_a_loop_hypothesis_is_rejected -> setEnabled ( true );
_ui -> actionDump_the_memory -> setEnabled ( false );
2013-12-11 00:12:44 +00:00
_ui -> actionDump_the_prediction_matrix -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionDelete_memory -> setEnabled ( false );
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionGenerate_map -> setEnabled ( false );
2015-08-01 23:42:36 -04:00
_ui -> menuExport_poses -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionSave_point_cloud -> setEnabled ( false );
_ui -> actionView_high_res_point_cloud -> setEnabled ( false );
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( false );
2016-07-15 17:46:13 -04:00
_ui -> actionExport_octomap -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( false );
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( false );
2013-12-11 00:12:44 +00:00
_ui -> actionDownload_all_clouds -> setEnabled ( false );
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_graph -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> menuSelect_source -> setEnabled ( false );
2015-09-21 18:12:55 -04:00
_ui -> actionLabel_current_location -> setEnabled ( true );
_ui -> actionSend_goal -> setEnabled ( true );
_ui -> actionCancel_goal -> setEnabled ( true );
2015-03-31 15:45:38 -04:00
_ui -> toolBar -> findChild < QAction *> ( "toolbar_source" ) -> setEnabled ( false );
2014-10-28 01:20:57 +00:00
_ui -> actionTrigger_a_new_map -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> doubleSpinBox_stats_imgRate -> setEnabled ( true );
_ui -> statusbar -> showMessage ( tr ( "Detecting..." ));
_state = newState ;
_ui -> label_elapsedTime -> setText ( "00:00:00" );
_elapsedTime -> start ();
_oneSecondTimer -> start ();
2012-12-11 18:05:05 +00:00
2015-02-18 18:22:59 -05:00
_databaseUpdated = true ; // if a new database is used, it won't be empty anymore...
2014-12-03 22:27:31 -05:00
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2012-06-24 17:19:34 +00:00
{
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
_imuThread -> start ();
2025-06-29 17:00:12 -07:00
// give imu thread a head start
uSleep ( 10 );
2018-03-07 19:43:30 -05:00
}
2025-06-29 17:00:12 -07:00
_sensorCapture -> start ();
2017-09-11 14:44:39 -04:00
ULogger :: setTreadIdFilter ( _preferencesDialog -> getGeneralLoggerThreads ());
2012-06-24 17:19:34 +00:00
}
2011-06-05 14:45:39 +00:00
break ;
case kPaused :
if ( _state == kPaused )
{
_ui -> actionPause -> setToolTip ( tr ( "Pause" ));
_ui -> actionPause -> setChecked ( false );
_ui -> statusbar -> showMessage ( tr ( "Detecting..." ));
_ui -> actionDump_the_memory -> setEnabled ( false );
2013-12-11 00:12:44 +00:00
_ui -> actionDump_the_prediction_matrix -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionDelete_memory -> setEnabled ( false );
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionGenerate_map -> setEnabled ( false );
2015-08-01 23:42:36 -04:00
_ui -> menuExport_poses -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionSave_point_cloud -> setEnabled ( false );
_ui -> actionView_high_res_point_cloud -> setEnabled ( false );
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( false );
2016-07-15 17:46:13 -04:00
_ui -> actionExport_octomap -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( false );
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( false );
2013-12-11 00:12:44 +00:00
_ui -> actionDownload_all_clouds -> setEnabled ( false );
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_graph -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_state = kDetecting ;
_elapsedTime -> start ();
_oneSecondTimer -> start ();
2012-12-11 18:05:05 +00:00
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2012-03-03 01:46:30 +00:00
{
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
_imuThread -> start ();
2025-06-29 17:00:12 -07:00
// give imu thread a head start
uSleep ( 10 );
2018-03-07 19:43:30 -05:00
}
2025-06-29 17:00:12 -07:00
_sensorCapture -> start ();
2017-09-11 14:44:39 -04:00
ULogger :: setTreadIdFilter ( _preferencesDialog -> getGeneralLoggerThreads ());
2012-06-24 17:19:34 +00:00
}
2011-06-05 14:45:39 +00:00
}
else if ( _state == kDetecting )
{
2012-06-24 17:19:34 +00:00
_ui -> actionPause -> setToolTip ( tr ( "Continue (shift-click for step-by-step)" ));
2011-06-05 14:45:39 +00:00
_ui -> actionPause -> setChecked ( true );
_ui -> statusbar -> showMessage ( tr ( "Paused..." ));
_ui -> actionDump_the_memory -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
_ui -> actionDump_the_prediction_matrix -> setEnabled ( true );
2014-12-14 16:42:10 -05:00
_ui -> actionDelete_memory -> setEnabled ( false );
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( _cachedSignatures . size () >= 2 && _currentPosesMap . size () >= 2 && _currentLinksMap . size () >= 1 );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2011-06-05 14:45:39 +00:00
_ui -> actionGenerate_map -> setEnabled ( true );
2015-10-16 20:11:24 -04:00
_ui -> menuExport_poses -> setEnabled ( ! _currentPosesMap . empty ());
2016-06-12 17:53:35 -04:00
_ui -> actionSave_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
_ui -> actionView_high_res_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
2017-04-07 18:31:11 -04:00
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( ! _occupancyGrid -> addedNodes (). empty ());
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
_ui -> actionExport_octomap -> setEnabled ( _octomap -> octree () -> size ());
2016-07-15 17:46:13 -04:00
#else
_ui -> actionExport_octomap -> setEnabled ( false );
#endif
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2013-12-11 00:12:44 +00:00
_ui -> actionDownload_all_clouds -> setEnabled ( true );
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_graph -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_state = kPaused ;
_oneSecondTimer -> stop ();
2012-12-11 18:05:05 +00:00
2012-06-24 17:19:34 +00:00
// kill sensors
2024-04-14 19:06:04 -07:00
if ( _sensorCapture )
2012-03-03 01:46:30 +00:00
{
2018-03-07 19:43:30 -05:00
if ( _imuThread )
{
_imuThread -> join ( true );
}
2024-04-14 19:06:04 -07:00
_sensorCapture -> join ( true );
2012-06-24 17:19:34 +00:00
}
2011-06-05 14:45:39 +00:00
}
break ;
case kMonitoring :
2013-12-11 00:12:44 +00:00
_ui -> actionPause -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> actionPause -> setChecked ( false );
_ui -> actionPause -> setToolTip ( tr ( "Pause" ));
2013-12-11 00:12:44 +00:00
_ui -> actionPause_on_match -> setEnabled ( true );
_ui -> actionPause_on_local_loop_detection -> setEnabled ( true );
_ui -> actionPause_when_a_loop_hypothesis_is_rejected -> setEnabled ( true );
_ui -> actionReset_Odometry -> setEnabled ( true );
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( false );
_ui -> menuExport_poses -> setEnabled ( false );
_ui -> actionSave_point_cloud -> setEnabled ( false );
_ui -> actionView_high_res_point_cloud -> setEnabled ( false );
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( false );
2016-07-15 17:46:13 -04:00
_ui -> actionExport_octomap -> setEnabled ( false );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( false );
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( false );
2011-06-05 14:45:39 +00:00
_ui -> actionDelete_memory -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
_ui -> actionDownload_all_clouds -> setEnabled ( true );
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_graph -> setEnabled ( true );
_ui -> actionTrigger_a_new_map -> setEnabled ( true );
2015-09-21 18:12:55 -04:00
_ui -> actionLabel_current_location -> setEnabled ( true );
_ui -> actionSend_goal -> setEnabled ( true );
_ui -> actionCancel_goal -> setEnabled ( true );
2011-06-05 14:45:39 +00:00
_ui -> statusbar -> showMessage ( tr ( "Monitoring..." ));
_state = newState ;
_elapsedTime -> start ();
_oneSecondTimer -> start ();
2015-08-03 17:55:38 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdResume ));
2013-12-11 00:12:44 +00:00
break ;
case kMonitoringPaused :
_ui -> actionPause -> setToolTip ( tr ( "Continue" ));
_ui -> actionPause -> setChecked ( true );
_ui -> actionPause -> setEnabled ( true );
_ui -> actionPause_on_match -> setEnabled ( true );
_ui -> actionPause_on_local_loop_detection -> setEnabled ( true );
_ui -> actionPause_when_a_loop_hypothesis_is_rejected -> setEnabled ( true );
_ui -> actionReset_Odometry -> setEnabled ( true );
2015-02-18 18:48:48 -05:00
_ui -> actionPost_processing -> setEnabled ( _cachedSignatures . size () >= 2 && _currentPosesMap . size () >= 2 && _currentLinksMap . size () >= 1 );
2015-10-16 20:11:24 -04:00
_ui -> actionExport_images_RGB_jpg_Depth_png -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
_ui -> menuExport_poses -> setEnabled ( ! _currentPosesMap . empty ());
2016-06-12 17:53:35 -04:00
_ui -> actionSave_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
_ui -> actionView_high_res_point_cloud -> setEnabled ( ! _cachedSignatures . empty () || ! _cachedClouds . empty ());
2017-04-07 18:31:11 -04:00
_ui -> actionExport_2D_Grid_map_bmp_png -> setEnabled ( ! _occupancyGrid -> addedNodes (). empty ());
2016-07-15 17:46:13 -04:00
#ifdef RTABMAP_OCTOMAP
2016-07-17 14:58:28 -04:00
_ui -> actionExport_octomap -> setEnabled ( _octomap -> octree () -> size ());
2016-07-15 17:46:13 -04:00
#else
_ui -> actionExport_octomap -> setEnabled ( false );
#endif
2015-10-16 20:11:24 -04:00
_ui -> actionExport_cameras_in_Bundle_format_out -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2016-09-19 14:01:06 -04:00
_ui -> actionDepth_Calibration -> setEnabled ( ! _cachedSignatures . empty () && ! _currentPosesMap . empty ());
2013-12-11 00:12:44 +00:00
_ui -> actionDelete_memory -> setEnabled ( true );
_ui -> actionDownload_all_clouds -> setEnabled ( true );
2014-10-28 01:20:57 +00:00
_ui -> actionDownload_graph -> setEnabled ( true );
_ui -> actionTrigger_a_new_map -> setEnabled ( true );
2015-09-21 18:12:55 -04:00
_ui -> actionLabel_current_location -> setEnabled ( true );
_ui -> actionSend_goal -> setEnabled ( true );
_ui -> actionCancel_goal -> setEnabled ( true );
2013-12-11 00:12:44 +00:00
_ui -> statusbar -> showMessage ( tr ( "Monitoring paused..." ));
_state = newState ;
_oneSecondTimer -> stop ();
2015-08-03 17:55:38 -04:00
this -> post ( new RtabmapEventCmd ( RtabmapEventCmd :: kCmdPause ));
2011-06-05 14:45:39 +00:00
break ;
default :
break ;
}
}
}