Merged pcl_integration branch to trunk

git-svn-id: http://rtabmap.googlecode.com/svn/trunk/rtabmap@1014 f169173b-cf89-36c8-b27e-44dbe73f0c83
This commit is contained in:
matlabbe
2013-12-11 00:12:44 +00:00
parent 97c70d394e
commit 8b8511e154
124 changed files with 21692 additions and 4458 deletions

View File

@@ -0,0 +1,138 @@
/*
* CloudViewer.h
*
* Created on: 2013-10-13
* Author: Mathieu
*/
#ifndef CLOUDVIEWER_H_
#define CLOUDVIEWER_H_
#include "rtabmap/gui/RtabmapGuiExp.h" // DLL export/import defines
#include <QVTKWidget.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/PolygonMesh.h>
#include "rtabmap/core/Transform.h"
#include <QtCore/QMap>
#include <pcl/PCLPointCloud2.h>
namespace pcl {
namespace visualization {
class PCLVisualizer;
}
}
class QMenu;
namespace rtabmap {
class RTABMAPGUI_EXP CloudViewer : public QVTKWidget
{
Q_OBJECT
public:
CloudViewer(QWidget * parent = 0);
virtual ~CloudViewer();
bool updateCloudPose(
const std::string & id,
const Transform & pose); //including mesh
bool updateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const Transform & pose = Transform::getIdentity());
bool updateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
const Transform & pose = Transform::getIdentity());
bool addOrUpdateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const Transform & pose = Transform::getIdentity());
bool addOrUpdateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
const Transform & pose = Transform::getIdentity());
bool addCloud(
const std::string & id,
const pcl::PCLPointCloud2Ptr & binaryCloud,
const Transform & pose,
bool rgb);
bool addCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const Transform & pose = Transform::getIdentity());
bool addCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
const Transform & pose = Transform::getIdentity());
bool addCloudMesh(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const std::vector<pcl::Vertices> & polygons,
const Transform & pose = Transform::getIdentity());
bool addCloudMesh(
const std::string & id,
const pcl::PolygonMesh::Ptr & mesh,
const Transform & pose = Transform::getIdentity());
void updateCameraPosition(
const Transform & pose);
void setTrajectoryShown(bool shown);
void setTrajectorySize(int value);
void removeAllClouds(); //including meshes
bool removeCloud(const std::string & id); //including mesh
bool getPose(const std::string & id, Transform & pose); //including meshes
const QMap<std::string, Transform> & getAddedClouds() {return _addedClouds;} //including meshes
public slots:
void render();
void setBackgroundColor(const QColor & color);
void setCloudVisibility(const std::string & id, bool isVisible);
void setCloudOpacity(const std::string & id, double opacity = 1.0);
void setCloudPointSize(const std::string & id, int size);
protected:
virtual void contextMenuEvent(QContextMenuEvent * event);
virtual void handleAction(QAction * event);
QMenu * menu() {return _menu;}
private:
void createMenu();
private:
pcl::visualization::PCLVisualizer * _visualizer;
QAction * _aLockCamera;
QAction * _aFollowCamera;
QAction * _aResetCamera;
QAction * _aLockViewZ;
QAction * _aShowTrajectory;
QAction * _aSetTrajectorySize;
QAction * _aClearTrajectory;
QAction * _aShowGrid;
QMenu * _menu;
pcl::PointCloud<pcl::PointXYZ>::Ptr _trajectory;
unsigned int _maxTrajectorySize;
QMap<std::string, Transform> _addedClouds; // include meshes
Transform _lastPose;
std::list<std::string> _gridLines;
};
} /* namespace rtabmap */
#endif /* CLOUDVIEWER_H_ */

View File

@@ -0,0 +1,48 @@
/*
* DataRecorder.h
*
* Created on: 2013-10-30
* Author: Mathieu
*/
#ifndef DATARECORDER_H_
#define DATARECORDER_H_
#include "rtabmap/gui/RtabmapGuiExp.h" // DLL export/import defines
#include <rtabmap/utilite/UEventsHandler.h>
#include <QtGui/QWidget>
#include <rtabmap/core/Image.h>
#include <rtabmap/utilite/UTimer.h>
namespace rtabmap {
class Memory;
class ImageView;
class RTABMAPGUI_EXP DataRecorder : public QWidget, public UEventsHandler
{
Q_OBJECT
public:
DataRecorder(QWidget * parent = 0);
bool init(const QString & path);
void close();
virtual ~DataRecorder();
public slots:
void addData(const rtabmap::Image & image);
void showImage(const rtabmap::Image & image);
protected:
void handleEvent(UEvent * event);
private:
Memory * memory_;
ImageView* imageView_;
UTimer timer_;
int dataQueue_;
};
} /* namespace rtabmap */
#endif /* DATARECORDER_H_ */

View File

@@ -39,6 +39,7 @@ class QLabel;
namespace rtabmap
{
class Memory;
class ImageView;
}
class RTABMAP_EXP DatabaseViewer : public QMainWindow
@@ -54,6 +55,7 @@ private slots:
void openDatabase();
void generateGraph();
void generateLocalGraph();
void generate3DMap();
void sliderAValueChanged(int);
void sliderBValueChanged(int);
void sliderAMoved(int);
@@ -61,19 +63,18 @@ private slots:
private:
void updateIds();
QImage ipl2QImage(const IplImage *newImage);
void drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, QGraphicsScene * scene);
void update(int value,
QLabel * labelIndex,
QLabel * labelActions,
QLabel * labelParents,
QLabel * labelChildren,
QGraphicsView * view,
rtabmap::ImageView * view,
QLabel * labelId);
private:
Ui_DatabaseViewer * ui_;
QMap<int, QByteArray> imagesMap_;
QMap<int, QByteArray> depthImagesMap_;
QList<int> ids_;
rtabmap::Memory * memory_;
QString pathDatabase_;

View File

@@ -20,17 +20,21 @@
#ifndef IMAGEVIEW_H_
#define IMAGEVIEW_H_
#include "rtabmap/core/RtabmapExp.h" // DLL export/import defines
#include "rtabmap/gui/RtabmapGuiExp.h" // DLL export/import defines
#include <QtGui/QGraphicsView>
#include <QtCore/QRectF>
#include <opencv2/features2d/features2d.hpp>
#include <map>
class QAction;
class QMenu;
namespace rtabmap {
class RTABMAP_EXP ImageView : public QGraphicsView {
class KeypointItem;
class RTABMAPGUI_EXP ImageView : public QGraphicsView {
Q_OBJECT
@@ -41,12 +45,21 @@ public:
void resetZoom();
bool isImageShown();
bool isImageDepthShown();
bool isFeaturesShown();
bool isLinesShown();
void setFeaturesShown(bool shown);
void setImageShown(bool shown);
void setImageDepthShown(bool shown);
void setLinesShown(bool shown);
void setFeatures(const std::multimap<int, cv::KeyPoint> & refWords);
void setImage(const QImage & image);
void setImageDepth(const QImage & image);
void clear();
protected:
virtual void contextMenuEvent(QContextMenuEvent * e);
virtual void wheelEvent(QWheelEvent * e);
@@ -55,7 +68,7 @@ private slots:
void updateZoom();
private:
void updateItemsShown();
void updateOpacity();
private:
int _zoom;
@@ -64,9 +77,14 @@ private:
QMenu * _menu;
QAction * _showImage;
QAction * _showImageDepth;
QAction * _showFeatures;
QAction * _showLines;
QAction * _saveImage;
QList<rtabmap::KeypointItem *> _features;
QGraphicsPixmapItem * _image;
QGraphicsPixmapItem * _imageDepth;
};
}

View File

@@ -0,0 +1,60 @@
/*
* LoopClosureViewer.h
*
* Created on: 2013-10-21
* Author: Mathieu
*/
#ifndef LOOPCLOSUREVIEWER_H_
#define LOOPCLOSUREVIEWER_H_
#include "rtabmap/gui/RtabmapGuiExp.h" // DLL export/import defines
#include <rtabmap/core/Parameters.h>
#include <rtabmap/core/Transform.h>
#include <opencv2/opencv.hpp>
#include <QtGui/QWidget>
class Ui_loopClosureViewer;
namespace rtabmap {
class Signature;
class RTABMAPGUI_EXP LoopClosureViewer : public QWidget {
Q_OBJECT
public:
LoopClosureViewer(QWidget * parent);
virtual ~LoopClosureViewer();
// take ownership
void setData(Signature * sA, Signature * sB); // sB contains loop transform as pose() from sA
const Signature * sA() const {return sA_;}
const Signature * sB() const {return sB_;}
public slots:
void setDecimation(int decimation) {decimation_ = decimation;}
void setMaxDepth(int maxDepth) {maxDepth_ = maxDepth;}
void setSamples(int samples) {samples_ = samples;}
void updateView(const Transform & AtoB = Transform());
protected:
virtual void showEvent(QShowEvent * event);
private:
Ui_loopClosureViewer * ui_;
Signature * sA_;
Signature * sB_;
Transform transform_;
int decimation_;
float maxDepth_;
int samples_;
};
} /* namespace rtabmap */
#endif /* LOOPCLOSUREVIEWER_H_ */

View File

@@ -26,11 +26,19 @@
#include <QtGui/QMainWindow>
#include <QtCore/QSet>
#include "rtabmap/core/RtabmapEvent.h"
#include "rtabmap/core/Image.h"
#include "rtabmap/gui/PreferencesDialog.h"
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/PolygonMesh.h>
namespace rtabmap {
class CameraThread;
class DBReader;
class CameraOpenni;
class OdometryThread;
class CloudViewer;
}
class QGraphicsScene;
@@ -57,7 +65,8 @@ public:
kStartingDetection,
kDetecting,
kPaused,
kMonitoring
kMonitoring,
kMonitoringPaused
};
enum SrcType {
@@ -76,9 +85,10 @@ public:
virtual ~MainWindow();
QString getWorkingDirectory() const;
void setMonitoringState(bool pauseChecked = false); // in monitoring state, only some actions are enabled
public slots:
void changeState(MainWindow::State state);
void processStats(const rtabmap::Statistics & stat);
protected:
virtual void closeEvent(QCloseEvent* event);
@@ -86,6 +96,7 @@ protected:
virtual void resizeEvent(QResizeEvent* anEvent);
private slots:
void changeState(MainWindow::State state);
void beep();
void startDetection();
void pauseDetection();
@@ -100,21 +111,27 @@ private slots:
void selectVideo();
void selectStream();
void selectDatabase();
void selectOpenni();
void resetTheMemory();
void dumpTheMemory();
void dumpThePrediction();
void downloadAllClouds();
void clearTheCache();
void saveFigures();
void loadFigures();
void openPreferences();
void selectScreenCaptureFormat(bool checked);
void updateElapsedTime();
void processStats(const rtabmap::Statistics & stat);
void processOdometry(const rtabmap::Image & data);
void applyAllPrefSettings();
void applyPrefSettings(PreferencesDialog::PANEL_FLAGS flags);
void applyPrefSettings(const rtabmap::ParametersMap & parameters);
void processRtabmapEventInit(int status, const QString & info);
void processRtabmapEvent3DMap(const rtabmap::RtabmapEvent3DMap & event);
void changeImgRateSetting();
void changeDetectionRateSetting();
void changeTimeLimitSetting();
void changeMappingMode();
void captureScreen();
void setAspectRatio(int w, int h);
void setAspectRatio16_9();
@@ -125,23 +142,53 @@ private slots:
void setAspectRatio480p();
void setAspectRatio720p();
void setAspectRatio1080p();
void savePointClouds();
void saveMeshes();
void viewPointClouds();
void viewMeshes();
void resetOdometry();
void triggerNewMap();
signals:
void statsReceived(const rtabmap::Statistics &);
void odometryReceived(const rtabmap::Image &);
void thresholdsChanged(int, int);
void stateChanged(MainWindow::State);
void rtabmapEventInitReceived(int status, const QString & info);
void rtabmapEvent3DMapReceived(const rtabmap::RtabmapEvent3DMap & event);
void imgRateChanged(double);
void detectionRateChanged(double);
void timeLimitChanged(float);
void mappingModeChanged(bool);
void noMoreImagesReceived();
void loopClosureThrChanged(float);
void twistReceived(float x, float y, float z, float roll, float pitch, float yaw, int row, int col);
private:
void update3DMapVisibility(bool cloudsShown, bool scansShown);
void updateMapCloud(const std::map<int, Transform> & poses, const Transform & pose);
void drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, const std::multimap<int, cv::KeyPoint> & loopWords);
void setupMainLayout(bool vertical);
void updateSelectSourceImageMenu(int type);
void updateSelectSourceDatabase(bool used);
void updateSelectSourceOpenni(bool used);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr createAssembledCloud();
pcl::PointCloud<pcl::PointXYZRGB>::Ptr createCloud(
int id,
const cv::Mat & rgb,
const cv::Mat & depth,
float depthConstant,
const Transform & localTransform,
const Transform & pose,
float voxelSize,
int decimation,
float maxDepth);
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > createPointClouds();
std::map<int, pcl::PolygonMesh::Ptr> createMeshes();
void savePointClouds(const std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr> & clouds);
void saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes);
private:
Ui_mainWindow * _ui;
@@ -149,6 +196,8 @@ private:
State _state;
rtabmap::CameraThread * _camera;
rtabmap::DBReader * _dbReader;
rtabmap::CameraOpenni * _cameraOpenni;
rtabmap::OdometryThread * _odomThread;
SrcType _srcType;
QString _srcPath;
@@ -160,8 +209,17 @@ private:
QSet<int> _lastIds;
int _lastId;
bool _processingStatistics;
bool _odometryReceived;
QMap<int, QByteArray> _imagesMap;
QMap<int, std::vector<unsigned char> > _imagesMap;
QMap<int, std::vector<unsigned char> > _depthsMap;
QMap<int, std::vector<unsigned char> > _depths2DMap;
QMap<int, float> _depthConstantsMap;
QMap<int, Transform> _localTransformsMap;
std::map<int, Transform> _currentPosesMap;
Transform _odometryCorrection;
Transform _lastOdomPose;
bool _lastOdometryProcessed;
QTimer * _oneSecondTimer;
QTime * _elapsedTime;
@@ -172,7 +230,6 @@ private:
PdfPlotCurve * _rawLikelihoodCurve;
DetailedProgressDialog * _initProgressDialog;
QActionGroup * _selectSourceImageGrp;
QString _graphSavingFileName;
QString _autoScreenCaptureFormat;

View File

@@ -0,0 +1,52 @@
/*
* OdometryViewer.h
*
* Created on: 2013-10-15
* Author: Mathieu
*/
#ifndef ODOMETRYVIEWER_H_
#define ODOMETRYVIEWER_H_
#include "rtabmap/gui/RtabmapGuiExp.h" // DLL export/import defines
#include "rtabmap/core/Image.h"
#include "rtabmap/gui/CloudViewer.h"
#include "rtabmap/utilite/UEventsHandler.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UMutex.h"
namespace rtabmap {
class RTABMAPGUI_EXP OdometryViewer : public CloudViewer, public UEventsHandler
{
Q_OBJECT
public:
OdometryViewer(int maxClouds = 10, int decimation = 2, float voxelSize = 0.0f, QWidget * parent = 0);
virtual ~OdometryViewer() {}
protected:
void handleAction(QAction * a);
virtual void handleEvent(UEvent * event);
private slots:
void processData();
private:
UMutex dataMutex_;
std::list<rtabmap::Image> buffer_;
UTimer timer_;
int maxClouds_;
float voxelSize_;
int decimation_;
int id_;
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > clouds_;
QAction * _aSetVoxelSize;
QAction * _aSetDecimation;
QAction * _aSetCloudHistorySize;
QAction * _aPause;
};
} /* namespace rtabmap */
#endif /* ODOMETRYVIEWER_H_ */

View File

@@ -20,12 +20,14 @@
#ifndef PREFERENCESDIALOG_H_
#define PREFERENCESDIALOG_H_
#include "rtabmap/core/RtabmapExp.h" // DLL export/import defines
#include "rtabmap/gui/RtabmapGuiExp.h" // DLL export/import defines
#include <QtGui/QDialog>
#include <QtCore/QModelIndex>
#include <QtCore/QVector>
#include <set>
#include "rtabmap/core/Transform.h"
#include "rtabmap/core/Parameters.h"
class Ui_preferencesDialog;
@@ -40,22 +42,30 @@ class QLineEdit;
class QSlider;
class QProgressDialog;
class UPlotCurve;
class QStackedWidget;
class QCheckBox;
class QSpinBox;
class QDoubleSpinBox;
namespace rtabmap {
class RTABMAP_EXP PreferencesDialog : public QDialog
class CameraOpenni;
class OdometryThread;
class Signature;
class LoopClosureViewer;
class RTABMAPGUI_EXP PreferencesDialog : public QDialog
{
Q_OBJECT
public:
enum PanelFlag {
kPanelDummy = 0,
kPanelGeneralStrategy = 1,
kPanelGeneral = 2,
kPanelFourier = 4,
kPanelSurf = 8,
kPanelSource = 16,
kPanelAll = 31
kPanelGeneral = 1,
kPanelCloudRendering = 2,
kPanelLogging = 4,
kPanelSource = 8,
kPanelAll = 15
};
// TODO, tried to change the name of PANEL_FLAGS to PanelFlags... but signals/slots errors appeared...
Q_DECLARE_FLAGS(PANEL_FLAGS, PanelFlag);
@@ -67,11 +77,17 @@ public:
kSrcVideo
};
enum OdomTest {
kOdomBIN,
kOdomBOW,
kOdomICP
};
public:
PreferencesDialog(QWidget * parent = 0);
virtual ~PreferencesDialog();
virtual QString getIniFilePath();
virtual QString getIniFilePath() const;
void init();
void saveWindowGeometry(const QString & windowName, const QWidget * window);
@@ -96,12 +112,28 @@ public:
bool imageHighestHypShown() const;
bool beepOnPause() const;
int getKeypointsOpacity() const;
QString getWorkingDirectory();
bool isCloudMeshing(int index) const; // 0=map
bool isCloudsShown(int index) const; // 0=map, 1=odom, 2=save
double getCloudVoxelSize(int index) const; // 0=map, 1=odom, 2=save
int getCloudDecimation(int index) const; // 0=map, 1=odom, 2=save
double getCloudMaxDepth(int index) const; // 0=map, 1=odom, 2=save
double getCloudOpacity(int index) const; // 0=map, 1=odom, 2=save
int getCloudPointSize(int index) const; // 0=map, 1=odom, 2=save
bool isScansShown(int index) const; // 0=map, 1=odom, 2=save
double getScanOpacity(int index) const; // 0=map, 1=odom, 2=save
int getScanPointSize(int index) const; // 0=map, 1=odom, 2=save
QString getWorkingDirectory() const;
// source panel
double getGeneralInputRate() const;
bool isSourceImageUsed() const;
bool isSourceDatabaseUsed() const;
bool isSourceOpenniUsed() const;
bool isSourceOpenniOdometryBIN() const;
bool isSourceOpenniOdometryBOW() const;
bool getGeneralAutoRestart() const;
bool getGeneralCameraKeypoints() const;
int getSourceImageType() const;
@@ -117,13 +149,18 @@ public:
QString getSourceVideoPath() const; //Video group
int getSourceUsbDeviceId() const; //UsbDevice group
QString getSourceDatabasePath() const; //Database group
bool getSourceDatabaseOdometryIgnored() const; //Database group
int getSourceDatabaseStartPos() const; //Database group
QString getSourceOpenniDevice() const; //Openni group
Transform getSourceOpenniLocalTransform() const; //Openni group
int getIgnoredDCComponents() const;
//
bool isImagesKept() const;
float getTimeLimit() const;
float getDetectionRate() const;
bool isSLAMMode() const;
//specific
bool isStatisticsPublished() const;
@@ -132,7 +169,7 @@ public:
double getExpThr() const;
//
void disableGeneralCameraKeypoints();
void setMonitoringState(bool monitoringState) {_monitoringState = monitoringState;}
signals:
void settingsChanged(PreferencesDialog::PANEL_FLAGS);
@@ -140,11 +177,14 @@ signals:
public slots:
void setInputRate(double value);
void setDetectionRate(double value);
void setHardThr(int value);
void setAutoRestart(bool value);
void setTimeLimit(float value);
void setSLAMMode(bool enabled);
void selectSourceImage(Src src = kSrcUndef);
void selectSourceDatabase(bool user = false);
void selectSourceOpenni(bool user = false);
private slots:
void closeDialog ( QAbstractButton * button );
@@ -153,22 +193,29 @@ private slots:
void loadConfigFrom();
void saveConfigTo();
void makeObsoleteGeneralPanel();
void makeObsoleteCloudRenderingPanel();
void makeObsoleteLoggingPanel();
void makeObsoleteSourcePanel();
void clicked(const QModelIndex &index);
void addParameter(int value);
void addParameter(bool value);
void addParameter(double value);
void addParameter(const QString & value);
void updatePredictionPlot();
void updateKpROI();
void changeDatabasePath();
void changeWorkingDirectory();
void changeDictionaryPath();
void readSettingsEnd();
void setupTreeView();
void updateBasicParameter();
void openDatabaseViewer();
void cleanOdometryTest();
void testSourceOdometry();
protected:
virtual void showEvent ( QShowEvent * event );
virtual void closeEvent(QCloseEvent *event);
void setParameter(const std::string & key, const std::string & value);
@@ -189,12 +236,17 @@ private:
void setupSignals();
void setupKpRoiPanel();
bool parseModel(QList<QGroupBox*> & boxes, QStandardItem * parentItem, int currentLevel, int & absoluteIndex);
void resetSettings(QGroupBox * groupBox);
void addParameter(const QObject * object, int value);
void addParameter(const QObject * object, bool value);
void addParameter(const QObject * object, double value);
void addParameter(const QObject * object, const QString & value);
void addParameters(const QObjectList & children);
void addParameters(const QStackedWidget * stackedWidget);
void addParameters(const QGroupBox * box);
QList<QGroupBox*> getGroupBoxes();
void readSettingsBegin();
void testOdometry(OdomTest test);
protected:
rtabmap::ParametersMap _parameters;
@@ -204,8 +256,24 @@ private:
Ui_preferencesDialog * _ui;
QStandardItemModel * _indexModel;
bool _initialized;
bool _monitoringState;
QProgressDialog * _progressDialog;
//Odometry test
CameraOpenni * _odomCamera;
OdometryThread * _odomThread;
QVector<QCheckBox*> _3dRenderingShowClouds;
QVector<QDoubleSpinBox*> _3dRenderingVoxelSize;
QVector<QSpinBox*> _3dRenderingDecimation;
QVector<QDoubleSpinBox*> _3dRenderingMaxDepth;
QVector<QDoubleSpinBox*> _3dRenderingOpacity;
QVector<QSpinBox*> _3dRenderingPtSize;
QVector<QCheckBox*> _3dRenderingShowScans;
QVector<QDoubleSpinBox*> _3dRenderingOpacityScan;
QVector<QSpinBox*> _3dRenderingPtSizeScan;
QVector<QCheckBox*> _3dRenderingMeshing;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(PreferencesDialog::PANEL_FLAGS)

View File

@@ -0,0 +1,177 @@
/*
* utilite is a cross-platform library with
* useful utilities for fast and small developing.
* Copyright (C) 2010 Mathieu Labbe
*
* utilite is free library: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* utilite is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UCV2QT_H_
#define UCV2QT_H_
#include <QtGui/QImage>
#include <opencv2/core/core.hpp>
#include <rtabmap/utilite/UMath.h>
#include <rtabmap/utilite/UThread.h>
/**
* Convert a cv::Mat image to a QImage. Support
* depth (float32, uint16) image and RGB/BGR 8bits images.
* @param image the cv::Mat image (can be 1 channel [CV_8U, CV_16U or CV_32F] or 3 channels [CV_U8])
* @param isBgr if 3 channels, it is BGR or RGB order.
* @return the QImage
*/
inline QImage uCvMat2QImage(const cv::Mat & image, bool isBgr = true)
{
QImage qtemp;
if(!image.empty() && image.depth() == CV_8U)
{
if(image.channels()==3)
{
const unsigned char * data = image.data;
if(image.channels() == 3)
{
qtemp = QImage(image.cols, image.rows, QImage::Format_RGB32);
for(int y = 0; y < image.rows; ++y, data += image.cols*image.elemSize())
{
for(int x = 0; x < image.cols; ++x)
{
QRgb * p = ((QRgb*)qtemp.scanLine (y)) + x;
if(isBgr)
{
*p = qRgb(data[x * image.channels()+2], data[x * image.channels()+1], data[x * image.channels()]);
}
else
{
*p = qRgb(data[x * image.channels()], data[x * image.channels()+1], data[x * image.channels()+2]);
}
}
}
}
}
else if(image.channels() == 1)
{
// mono grayscale
qtemp = QImage(image.data, image.cols, image.rows, image.cols, QImage::Format_Indexed8).copy();
}
else
{
printf("Wrong image format, must have 1 or 3 channels\n");
}
}
else if(image.depth() == CV_32F && image.channels()==1)
{
// Assume depth image (float in meters)
const float * data = (const float *)image.data;
float min=0, max=0;
uMinMax(data, image.rows*image.cols, min, max);
qtemp = QImage(image.cols, image.rows, QImage::Format_Indexed8);
for(int y = 0; y < image.rows; ++y, data += image.cols)
{
for(int x = 0; x < image.cols; ++x)
{
uchar * p = qtemp.scanLine (y) + x;
if(data[x] < min || data[x] > max || uIsNan(data[x]))
{
*p = 0;
}
else
{
*p = uchar(255.0f - ((data[x]-min)*255.0f)/(max-min));
if(*p == 255)
{
*p = 0;
}
}
}
}
QVector<QRgb> my_table;
for(int i = 0; i < 256; i++) my_table.push_back(qRgb(i,i,i));
qtemp.setColorTable(my_table);
}
else if(image.depth() == CV_16U && image.channels()==1)
{
// Assume depth image (unsigned short in mm)
const unsigned short * data = (const unsigned short *)image.data;
unsigned short min=data[0], max=data[0];
for(unsigned int i=1; i<image.total(); ++i)
{
if(!uIsNan(data[i]) && data[i] > 0)
{
if((uIsNan(min) && data[i] > 0) ||
(data[i] > 0 && data[i]<min))
{
min = data[i];
}
if((uIsNan(max) && data[i] > 0) ||
(data[i] > 0 && data[i]>max))
{
max = data[i];
}
}
}
qtemp = QImage(image.cols, image.rows, QImage::Format_Indexed8);
for(int y = 0; y < image.rows; ++y, data += image.cols)
{
for(int x = 0; x < image.cols; ++x)
{
uchar * p = qtemp.scanLine (y) + x;
if(data[x] < min || data[x] > max || uIsNan(data[x]) || max == min)
{
*p = 0;
}
else
{
*p = uchar(255.0f - (float(data[x]-min)/float(max-min))*255.0f);
if(*p == 255)
{
*p = 0;
}
}
}
}
QVector<QRgb> my_table;
for(int i = 0; i < 256; i++) my_table.push_back(qRgb(i,i,i));
qtemp.setColorTable(my_table);
}
else if(!image.empty() && image.depth() != CV_8U)
{
printf("Wrong image format, must be 8_bits/3channels or (depth) 32bitsFloat/1channel, 16bits/1channel\n");
}
return qtemp;
}
class UCvMat2QImageThread : public UThread
{
public:
UCvMat2QImageThread(const cv::Mat & image, bool isBgr = true) :
image_(image),
isBgr_(isBgr) {}
QImage & getQImage() {return qtImage_;}
protected:
virtual void mainLoop()
{
qtImage_ = uCvMat2QImage(image_, isBgr_);
this->kill();
}
private:
cv::Mat image_;
bool isBgr_;
QImage qtImage_;
};
#endif /* UCV2QT_H_ */

View File

@@ -21,6 +21,7 @@
#include "rtabmap/core/Rtabmap.h"
#include "ui_aboutDialog.h"
#include <opencv2/core/version.hpp>
#include <pcl/pcl_config.h>
namespace rtabmap {
@@ -29,8 +30,13 @@ AboutDialog::AboutDialog(QWidget * parent) :
{
_ui = new Ui_aboutDialog();
_ui->setupUi(this);
_ui->label_version->setText(Rtabmap::getVersion().c_str());
QString version = Rtabmap::getVersion().c_str();
#if DEMO_BUILD
version.append(" [DEMO]");
#endif
_ui->label_version->setText(version);
_ui->label_opencv_version->setText(CV_VERSION);
_ui->label_pcl_version->setText(PCL_VERSION_PRETTY);
}
AboutDialog::~AboutDialog()

View File

@@ -12,6 +12,10 @@ SET(headers_ui
./StatsToolBox.h
./DetailedProgressDialog.h
./utilite/UPlot.h
../include/${PROJECT_PREFIX}/gui/CloudViewer.h
../include/${PROJECT_PREFIX}/gui/OdometryViewer.h
../include/${PROJECT_PREFIX}/gui/LoopClosureViewer.h
../include/${PROJECT_PREFIX}/gui/DataRecorder.h
)
SET(uis
@@ -20,6 +24,7 @@ SET(uis
./ui/aboutDialog.ui
./ui/consoleWidget.ui
./ui/DatabaseViewer.ui
./ui/loopClosureViewer.ui
)
SET(qrc
@@ -43,7 +48,6 @@ SET(SRC_FILES
./MainWindow.cpp
./PreferencesDialog.cpp
./KeypointItem.cpp
./qtipl.cpp
./ImageView.cpp
./PdfPlot.cpp
./StatsToolBox.cpp
@@ -52,6 +56,10 @@ SET(SRC_FILES
./ConsoleWidget.cpp
./DatabaseViewer.cpp
./utilite/UPlot.cpp
./CloudViewer.cpp
./OdometryViewer.cpp
./LoopClosureViewer.cpp
./DataRecorder.cpp
${moc_srcs}
${moc_uis}
${srcs_qrc}
@@ -64,33 +72,42 @@ SET(INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
${OpenCV_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR} # for qt ui generated in binary dir
${PCL_INCLUDE_DIRS}
)
INCLUDE(${QT_USE_FILE})
INCLUDE(${VTK_USE_FILE})
SET(LIBRARIES
${QT_LIBRARIES}
${OpenCV_LIBS}
${PCL_LIBRARIES}
QVTK
vtkHybrid
)
#include files
INCLUDE_DIRECTORIES(${INCLUDE_DIRS})
add_definitions(${PCL_DEFINITIONS})
# create a library from the source files
ADD_LIBRARY(rtabmap_guilib ${SRC_FILES})
ADD_LIBRARY(rtabmap_gui ${SRC_FILES})
# Linking with Qt libraries
TARGET_LINK_LIBRARIES(rtabmap_guilib rtabmap_corelib rtabmap_utilite ${LIBRARIES})
TARGET_LINK_LIBRARIES(rtabmap_gui rtabmap_core rtabmap_utilite ${LIBRARIES})
SET_TARGET_PROPERTIES(
rtabmap_guilib
rtabmap_gui
PROPERTIES
OUTPUT_NAME ${PROJECT_PREFIX}_gui
INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib
)
INSTALL(TARGETS rtabmap_guilib
RUNTIME DESTINATION bin COMPONENT runtime
LIBRARY DESTINATION lib COMPONENT devel
ARCHIVE DESTINATION lib COMPONENT devel)
INSTALL(TARGETS rtabmap_gui
EXPORT RTABMapTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT runtime
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT devel
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT devel)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/ DESTINATION include/ COMPONENT devel FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/ DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT devel FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE)

558
guilib/src/CloudViewer.cpp Normal file
View File

@@ -0,0 +1,558 @@
/*
* CloudViewer.cpp
*
* Created on: 2013-10-13
* Author: Mathieu
*/
#include "rtabmap/gui/CloudViewer.h"
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UConversion.h>
#include <rtabmap/core/util3d.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <QtGui/QMenu>
#include <QtGui/QAction>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QInputDialog>
#include <QtGui/QWheelEvent>
#include <vtkRenderWindow.h>
namespace rtabmap {
CloudViewer::CloudViewer(QWidget *parent) :
QVTKWidget(parent),
#if defined(WIN32) || defined(__APPLE__)
_visualizer(new pcl::visualization::PCLVisualizer("PCLVisualizer")),
#else
_visualizer(new pcl::visualization::PCLVisualizer("PCLVisualizer", false)),
#endif
_aLockCamera(0),
_aFollowCamera(0),
_aResetCamera(0),
_aLockViewZ(0),
_aShowTrajectory(0),
_aSetTrajectorySize(0),
_aClearTrajectory(0),
_aShowGrid(0),
_menu(0),
_trajectory(new pcl::PointCloud<pcl::PointXYZ>),
_maxTrajectorySize(100),
_lastPose(Transform::getIdentity())
{
this->setMinimumSize(200, 200);
this->SetRenderWindow(_visualizer->getRenderWindow());
#if !defined(WIN32) && !defined(__APPLE__)
_visualizer->setupInteractor(this->GetInteractor(), this->GetRenderWindow());
#endif
_visualizer->setCameraPosition(
-1, 0, 0,
0, 0, 0,
0, 0, 1);
//setup menu/actions
createMenu();
}
CloudViewer::~CloudViewer()
{
UDEBUG("");
//_visualizer->close();
delete _visualizer;
}
void CloudViewer::createMenu()
{
_aLockCamera = new QAction("Lock target", this);
_aLockCamera->setCheckable(true);
_aLockCamera->setChecked(false);
_aFollowCamera = new QAction("Follow", this);
_aFollowCamera->setCheckable(true);
_aFollowCamera->setChecked(true);
QAction * freeCamera = new QAction("Free", this);
freeCamera->setCheckable(true);
freeCamera->setChecked(false);
_aLockViewZ = new QAction("Lock view Z", this);
_aLockViewZ->setCheckable(true);
_aLockViewZ->setChecked(true);
_aResetCamera = new QAction("Reset position", this);
_aShowTrajectory= new QAction("Show trajectory", this);
_aShowTrajectory->setCheckable(true);
_aShowTrajectory->setChecked(true);
_aSetTrajectorySize = new QAction("Set trajectory size...", this);
_aClearTrajectory = new QAction("Clear trajectory", this);
_aShowGrid = new QAction("Show grid", this);
_aShowGrid->setCheckable(true);
QMenu * cameraMenu = new QMenu("Camera", this);
cameraMenu->addAction(_aLockCamera);
cameraMenu->addAction(_aFollowCamera);
cameraMenu->addAction(freeCamera);
cameraMenu->addSeparator();
cameraMenu->addAction(_aLockViewZ);
cameraMenu->addAction(_aResetCamera);
QActionGroup * group = new QActionGroup(this);
group->addAction(_aLockCamera);
group->addAction(_aFollowCamera);
group->addAction(freeCamera);
QMenu * trajectoryMenu = new QMenu("Trajectory", this);
trajectoryMenu->addAction(_aShowTrajectory);
trajectoryMenu->addAction(_aSetTrajectorySize);
trajectoryMenu->addAction(_aClearTrajectory);
//menus
_menu = new QMenu(this);
_menu->addMenu(cameraMenu);
_menu->addMenu(trajectoryMenu);
_menu->addAction(_aShowGrid);
}
bool CloudViewer::updateCloudPose(
const std::string & id,
const Transform & pose)
{
if(_addedClouds.contains(id))
{
UDEBUG("Updating pose %s to %s", id.c_str(), pose.prettyPrint().c_str());
if(_addedClouds.find(id).value() == pose ||
_visualizer->updatePointCloudPose(id, util3d::transformToEigen3f(pose)))
{
_addedClouds.find(id).value() = pose;
return true;
}
}
return false;
}
bool CloudViewer::updateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const Transform & pose)
{
if(_addedClouds.contains(id))
{
UDEBUG("Updating %s with %d points", id.c_str(), (int)cloud->size());
int index = _visualizer->getColorHandlerIndex(id);
this->removeCloud(id);
if(this->addCloud(id, cloud, pose))
{
_visualizer->updateColorHandlerIndex(id, index);
return true;
}
}
return false;
}
bool CloudViewer::updateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
const Transform & pose)
{
if(_addedClouds.contains(id))
{
UDEBUG("Updating %s with %d points", id.c_str(), (int)cloud->size());
int index = _visualizer->getColorHandlerIndex(id);
this->removeCloud(id);
if(this->addCloud(id, cloud, pose))
{
_visualizer->updateColorHandlerIndex(id, index);
return true;
}
}
return false;
}
bool CloudViewer::addOrUpdateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const Transform & pose)
{
if(!updateCloud(id, cloud, pose))
{
return addCloud(id, cloud, pose);
}
return true;
}
bool CloudViewer::addOrUpdateCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
const Transform & pose)
{
if(!updateCloud(id, cloud, pose))
{
return addCloud(id, cloud, pose);
}
return true;
}
bool CloudViewer::addCloud(
const std::string & id,
const pcl::PCLPointCloud2Ptr & binaryCloud,
const Transform & pose,
bool rgb)
{
if(!_addedClouds.contains(id))
{
Eigen::Vector4f origin(pose.x(), pose.y(), pose.z(), 0.0f);
Eigen::Quaternionf orientation = Eigen::Quaternionf(util3d::transformToEigen3f(pose).rotation());
// add random color channel
pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>::Ptr colorHandler;
colorHandler.reset (new pcl::visualization::PointCloudColorHandlerRandom<pcl::PCLPointCloud2> (binaryCloud));
if(_visualizer->addPointCloud (binaryCloud, colorHandler, origin, orientation, id))
{
// white
colorHandler.reset (new pcl::visualization::PointCloudColorHandlerCustom<pcl::PCLPointCloud2> (binaryCloud, 255, 255, 255));
_visualizer->addPointCloud (binaryCloud, colorHandler, origin, orientation, id);
// x,y,z
colorHandler.reset (new pcl::visualization::PointCloudColorHandlerGenericField<pcl::PCLPointCloud2> (binaryCloud, "x"));
_visualizer->addPointCloud (binaryCloud, colorHandler, origin, orientation, id);
colorHandler.reset (new pcl::visualization::PointCloudColorHandlerGenericField<pcl::PCLPointCloud2> (binaryCloud, "y"));
_visualizer->addPointCloud (binaryCloud, colorHandler, origin, orientation, id);
colorHandler.reset (new pcl::visualization::PointCloudColorHandlerGenericField<pcl::PCLPointCloud2> (binaryCloud, "z"));
_visualizer->addPointCloud (binaryCloud, colorHandler, origin, orientation, id);
if(rgb)
{
//rgb
colorHandler.reset(new pcl::visualization::PointCloudColorHandlerRGBField<pcl::PCLPointCloud2>(binaryCloud));
_visualizer->addPointCloud (binaryCloud, colorHandler, origin, orientation, id);
_visualizer->updateColorHandlerIndex(id, 5);
}
_addedClouds.insert(id, pose);
return true;
}
}
return false;
}
bool CloudViewer::addCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const Transform & pose)
{
if(!_addedClouds.contains(id))
{
UDEBUG("Adding %s with %d points", id.c_str(), (int)cloud->size());
pcl::PCLPointCloud2Ptr binaryCloud(new pcl::PCLPointCloud2);
pcl::toPCLPointCloud2(*cloud, *binaryCloud);
return addCloud(id, binaryCloud, pose, true);
}
return false;
}
bool CloudViewer::addCloud(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
const Transform & pose)
{
if(!_addedClouds.contains(id))
{
UDEBUG("Adding %s with %d points", id.c_str(), (int)cloud->size());
pcl::PCLPointCloud2Ptr binaryCloud(new pcl::PCLPointCloud2);
pcl::toPCLPointCloud2(*cloud, *binaryCloud);
return addCloud(id, binaryCloud, pose, false);
}
return false;
}
bool CloudViewer::addCloudMesh(
const std::string & id,
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const std::vector<pcl::Vertices> & polygons,
const Transform & pose)
{
if(!_addedClouds.contains(id))
{
UDEBUG("Adding %s with %d points and %d polygons", id.c_str(), (int)cloud->size(), (int)polygons.size());
if(_visualizer->addPolygonMesh<pcl::PointXYZRGB>(cloud, polygons, id))
{
_visualizer->updatePointCloudPose(id, util3d::transformToEigen3f(pose));
_addedClouds.insert(id, pose);
return true;
}
}
return false;
}
bool CloudViewer::addCloudMesh(
const std::string & id,
const pcl::PolygonMesh::Ptr & mesh,
const Transform & pose)
{
if(!_addedClouds.contains(id))
{
UDEBUG("Adding %s with %d polygons", id.c_str(), (int)mesh->polygons.size());
if(_visualizer->addPolygonMesh(*mesh, id))
{
_visualizer->updatePointCloudPose(id, util3d::transformToEigen3f(pose));
_addedClouds.insert(id, pose);
return true;
}
}
return false;
}
void CloudViewer::setTrajectoryShown(bool shown)
{
_aShowTrajectory->setChecked(shown);
}
void CloudViewer::setTrajectorySize(int value)
{
_maxTrajectorySize = value;
}
void CloudViewer::removeAllClouds()
{
_addedClouds.clear();
_visualizer->removeAllPointClouds();
}
bool CloudViewer::removeCloud(const std::string & id)
{
_addedClouds.remove(id);
return _visualizer->removePointCloud(id);
}
bool CloudViewer::getPose(const std::string & id, Transform & pose)
{
if(_addedClouds.contains(id))
{
pose = _addedClouds.value(id);
return true;
}
return false;
}
void CloudViewer::updateCameraPosition(const Transform & pose)
{
if(!pose.isNull())
{
Eigen::Affine3f m = util3d::transformToEigen3f(pose);
Eigen::Vector3f pos = m.translation();
Eigen::Vector3f lastPos(0,0,0);
if(_trajectory->size())
{
lastPos[0]=_trajectory->back().x;
lastPos[1]=_trajectory->back().y;
lastPos[2]=_trajectory->back().z;
}
_trajectory->push_back(pcl::PointXYZ(pos[0], pos[1], pos[2]));
if(_maxTrajectorySize>0)
{
while(_trajectory->size() > _maxTrajectorySize)
{
_trajectory->erase(_trajectory->begin());
}
}
if(_aShowTrajectory->isChecked())
{
_visualizer->removeShape("trajectory");
pcl::PolygonMesh mesh;
pcl::Vertices vertices;
vertices.vertices.resize(_trajectory->size());
for(unsigned int i=0; i<vertices.vertices.size(); ++i)
{
vertices.vertices[i] = i;
}
pcl::toPCLPointCloud2(*_trajectory, mesh.cloud);
mesh.polygons.push_back(vertices);
_visualizer->addPolylineFromPolygonMesh(mesh, "trajectory");
}
if(pose != _lastPose)
{
std::vector<pcl::visualization::Camera> cameras;
_visualizer->getCameras(cameras);
if(_aLockCamera->isChecked())
{
//update camera position
Eigen::Vector3f diff = pos - Eigen::Vector3f(_lastPose.x(), _lastPose.y(), _lastPose.z());
cameras.front().pos[0] += diff[0];
cameras.front().pos[1] += diff[1];
cameras.front().pos[2] += diff[2];
cameras.front().focal[0] += diff[0];
cameras.front().focal[1] += diff[1];
cameras.front().focal[2] += diff[2];
}
else if(_aFollowCamera->isChecked())
{
Eigen::Vector3f vPosToFocal = Eigen::Vector3f(cameras.front().focal[0] - cameras.front().pos[0],
cameras.front().focal[1] - cameras.front().pos[1],
cameras.front().focal[2] - cameras.front().pos[2]).normalized();
Eigen::Vector3f zAxis(cameras.front().view[0], cameras.front().view[1], cameras.front().view[2]);
Eigen::Vector3f yAxis = zAxis.cross(vPosToFocal);
Eigen::Vector3f xAxis = yAxis.cross(zAxis);
Transform PR(xAxis[0], xAxis[1], xAxis[2],0,
yAxis[0], yAxis[1], yAxis[2],0,
zAxis[0], zAxis[1], zAxis[2],0);
Transform P(PR[0], PR[1], PR[2], cameras.front().pos[0],
PR[4], PR[5], PR[6], cameras.front().pos[1],
PR[8], PR[9], PR[10], cameras.front().pos[2]);
Transform F(PR[0], PR[1], PR[2], cameras.front().focal[0],
PR[4], PR[5], PR[6], cameras.front().focal[1],
PR[8], PR[9], PR[10], cameras.front().focal[2]);
Transform N = pose;
Transform O = _lastPose;
Transform O2N = O.inverse()*N;
Transform F2O = F.inverse()*O;
Transform T = F2O * O2N * F2O.inverse();
Transform Fp = F * T;
Transform P2F = P.inverse()*F;
Transform Pp = P * P2F * T * P2F.inverse();
cameras.front().pos[0] = Pp.x();
cameras.front().pos[1] = Pp.y();
cameras.front().pos[2] = Pp.z();
cameras.front().focal[0] = Fp.x();
cameras.front().focal[1] = Fp.y();
cameras.front().focal[2] = Fp.z();
//FIXME: the view up is not set properly...
cameras.front().view[0] = Fp[8];
cameras.front().view[1] = Fp[9];
cameras.front().view[2] = Fp[10];
}
if(_aLockViewZ->isChecked())
{
cameras.front().view[0] = 0;
cameras.front().view[1] = 0;
cameras.front().view[2] = 1;
}
_visualizer->setCameraPosition(
cameras.front().pos[0], cameras.front().pos[1], cameras.front().pos[2],
cameras.front().focal[0], cameras.front().focal[1], cameras.front().focal[2],
cameras.front().view[0], cameras.front().view[1], cameras.front().view[2]);
}
_visualizer->removeCoordinateSystem();
_visualizer->addCoordinateSystem(0.2, m);
}
_lastPose = pose;
}
void CloudViewer::render()
{
this->GetRenderWindow()->Render();
}
void CloudViewer::setBackgroundColor(const QColor & color)
{
_visualizer->setBackgroundColor(color.redF(), color.greenF(), color.blueF());
}
void CloudViewer::setCloudVisibility(const std::string & id, bool isVisible)
{
pcl::visualization::CloudActorMapPtr cloudActorMap = _visualizer->getCloudActorMap();
pcl::visualization::CloudActorMap::iterator iter = cloudActorMap->find(id);
if(iter != cloudActorMap->end())
{
iter->second.actor->SetVisibility(isVisible?1:0);
}
else
{
UERROR("Cannot find actor named \"%s\".", id.c_str());
}
}
void CloudViewer::setCloudOpacity(const std::string & id, double opacity)
{
_visualizer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, opacity, id);
}
void CloudViewer::setCloudPointSize(const std::string & id, int size)
{
_visualizer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, (double)size, id);
}
void CloudViewer::contextMenuEvent(QContextMenuEvent * event)
{
QAction * a = _menu->exec(event->globalPos());
if(a)
{
handleAction(a);
}
}
void CloudViewer::handleAction(QAction * a)
{
if(a == _aSetTrajectorySize)
{
bool ok;
int value = QInputDialog::getInt(this, tr("Set trajectory size"), tr("Size (0=infinite)"), _maxTrajectorySize, 0, 10000, 10, &ok);
if(ok)
{
_maxTrajectorySize = value;
}
}
else if(a == _aClearTrajectory)
{
_trajectory->clear();
_visualizer->removeShape("trajectory");
this->render();
}
else if(a == _aResetCamera)
{
_visualizer->setCameraPosition(
-1, 0, 0,
0, 0, 0,
0, 0, 1);
this->render();
}
else if(a == _aShowGrid)
{
if(_aShowGrid->isChecked())
{
float cellSize = 1.0f;
int cellCount = 50;
double r=0.5;
double g=0.5;
double b=0.5;
int id = 0;
float min = -float(cellCount/2) * cellSize;
float max = float(cellCount/2) * cellSize;
std::string name;
for(float i=min; i<=max; i += cellSize)
{
//over x
name = uFormat("line%d", ++id);
_visualizer->addLine(pcl::PointXYZ(i, min, 0.0f), pcl::PointXYZ(i, max, 0.0f), r, g, b, name);
_gridLines.push_back(name);
//over y
name = uFormat("line%d", ++id);
_visualizer->addLine(pcl::PointXYZ(min, i, 0.0f), pcl::PointXYZ(max, i, 0.0f), r, g, b, name);
_gridLines.push_back(name);
}
}
else
{
for(std::list<std::string>::iterator iter = _gridLines.begin(); iter!=_gridLines.end(); ++iter)
{
_visualizer->removeShape(*iter);
}
_gridLines.clear();
}
this->render();
}
}
} /* namespace rtabmap */

130
guilib/src/DataRecorder.cpp Normal file
View File

@@ -0,0 +1,130 @@
/*
* CloudRecorder.cpp
*
* Created on: 2013-10-30
* Author: Mathieu
*/
#include "rtabmap/gui/DataRecorder.h"
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UTimer.h>
#include <rtabmap/core/Memory.h>
#include <rtabmap/core/CameraEvent.h>
#include <rtabmap/core/util3d.h>
#include <rtabmap/gui/ImageView.h>
#include <rtabmap/gui/UCv2Qt.h>
#include <QtCore/QMetaType>
#include <QtGui/QHBoxLayout>
namespace rtabmap {
DataRecorder::DataRecorder(QWidget * parent) :
QWidget(parent),
memory_(0),
imageView_(new ImageView(this)),
dataQueue_(0)
{
qRegisterMetaType<rtabmap::Image>("rtabmap::Image");
QHBoxLayout * layout = new QHBoxLayout(this);
layout->addWidget(imageView_);
this->setLayout(layout);
}
bool DataRecorder::init(const QString & path)
{
if(!memory_)
{
ParametersMap customParameters;
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
customParameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), "-1")); // desactivate keypoints extraction
customParameters.insert(ParametersPair(Parameters::kMemImageKept(), "true")); // to keep images
memory_ = new Memory();
if(!memory_->init(path.toStdString(), true, customParameters, false))
{
delete memory_;
memory_ = 0;
UERROR("Error initializing the memory.");
return false;
}
return true;
}
else
{
UERROR("Already initialized, close it first.");
return false;
}
}
void DataRecorder::close()
{
if(memory_)
{
delete memory_;
memory_ = 0;
}
}
DataRecorder::~DataRecorder()
{
this->close();
}
void DataRecorder::addData(const rtabmap::Image & image)
{
if(memory_)
{
//save to database
UTimer time;
memory_->update(image);
memory_->cleanup();
if(image.id() % 30)
{
memory_->emptyTrash();
}
UDEBUG("Time to process a message = %f s", time.ticks());
}
else
{
UWARN("CloudRecorder not initialized!");
}
--dataQueue_;
}
void DataRecorder::showImage(const rtabmap::Image & image)
{
if(this->isVisible() && !image.empty())
{
imageView_->setImage(uCvMat2QImage(image.image()));
imageView_->setImageDepth(uCvMat2QImage(image.depth()));
imageView_->fitInView(imageView_->sceneRect(), Qt::KeepAspectRatio);
}
}
void DataRecorder::handleEvent(UEvent * event)
{
if(event->getClassName().compare("CameraEvent") == 0)
{
CameraEvent * camEvent = (CameraEvent*)event;
if(camEvent->getCode() == CameraEvent::kCodeImageDepth ||
camEvent->getCode() == CameraEvent::kCodeImage)
{
if(!camEvent->image().empty())
{
UINFO("Receiving rate = %f Hz", 1.0f/timer_.ticks());
QMetaObject::invokeMethod(this, "addData", Q_ARG(rtabmap::Image, camEvent->image()));
++dataQueue_;
if(dataQueue_ < 2 && this->isVisible())
{
QMetaObject::invokeMethod(this, "showImage", Q_ARG(rtabmap::Image, camEvent->image()));
}
}
}
}
}
} /* namespace rtabmap */

View File

@@ -32,6 +32,13 @@
#include "rtabmap/core/Memory.h"
#include "rtabmap/core/DBDriver.h"
#include "rtabmap/gui/KeypointItem.h"
#include "rtabmap/gui/UCv2Qt.h"
#include "rtabmap/core/util3d.h"
#include "rtabmap/core/Signature.h"
#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/common/transforms.h>
DatabaseViewer::DatabaseViewer(QWidget * parent) :
QMainWindow(parent),
@@ -54,9 +61,7 @@ DatabaseViewer::DatabaseViewer(QWidget * parent) :
connect(ui_->actionOpen_database, SIGNAL(triggered()), this, SLOT(openDatabase()));
connect(ui_->actionGenerate_graph_dot, SIGNAL(triggered()), this, SLOT(generateGraph()));
connect(ui_->actionGenerate_local_graph_dot, SIGNAL(triggered()), this, SLOT(generateLocalGraph()));
ui_->graphicsView_A->setScene(new QGraphicsScene(this));
ui_->graphicsView_B->setScene(new QGraphicsScene(this));
connect(ui_->actionGenerate_3D_map_pcd, SIGNAL(triggered()), this, SLOT(generate3DMap()));
ui_->horizontalSlider_A->setTracking(false);
ui_->horizontalSlider_B->setTracking(false);
@@ -99,6 +104,7 @@ bool DatabaseViewer::openDatabase(const QString & path)
delete memory_;
memory_ = 0;
imagesMap_.clear();
depthImagesMap_.clear();
ids_.clear();
}
@@ -135,9 +141,8 @@ void DatabaseViewer::updateIds()
std::set<int> ids = memory_->getAllSignatureIds();
ids_ = QList<int>::fromStdList(std::list<int>(ids.begin(), ids.end()));
ids_.prepend(0);
UDEBUG("Loaded %d ids", ids_.size());
UINFO("Loaded %d ids", ids_.size());
if(ids_.size())
{
@@ -145,12 +150,12 @@ void DatabaseViewer::updateIds()
ui_->horizontalSlider_B->setMinimum(0);
ui_->horizontalSlider_A->setMaximum(ids_.size()-1);
ui_->horizontalSlider_B->setMaximum(ids_.size()-1);
ui_->horizontalSlider_A->setSliderPosition(0);
ui_->horizontalSlider_B->setSliderPosition(0);
ui_->horizontalSlider_A->setEnabled(true);
ui_->horizontalSlider_B->setEnabled(true);
ui_->label_idA->setText("0");
ui_->label_idB->setText("0");
ui_->horizontalSlider_A->setSliderPosition(0);
ui_->horizontalSlider_B->setSliderPosition(0);
sliderAValueChanged(0);
sliderBValueChanged(0);
}
else
{
@@ -217,31 +222,93 @@ void DatabaseViewer::generateLocalGraph()
}
}
void DatabaseViewer::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, QGraphicsScene * scene)
void DatabaseViewer::generate3DMap()
{
if(!scene)
if(!ids_.size() || !memory_)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("The database is empty..."));
return;
}
rtabmap::KeypointItem * item = 0;
int alpha = 70;
for(std::multimap<int, cv::KeyPoint>::const_iterator i = refWords.begin(); i != refWords.end(); ++i )
bool ok = false;
int id = QInputDialog::getInt(this, tr("Around which location?"), tr("Location ID"), ids_.first(), ids_.first(), ids_.last(), 1, &ok);
if(ok)
{
const cv::KeyPoint & r = (*i).second;
int id = (*i).first;
QString info = QString( "WordRef = %1\n"
"Laplacian = %2\n"
"Dir = %3\n"
"Hessian = %4\n"
"X = %5\n"
"Y = %6\n"
"Size = %7").arg(id).arg(1).arg(r.angle).arg(r.response).arg(r.pt.x).arg(r.pt.y).arg(r.size);
float radius = r.size*1.2/9.*2;
int margin = QInputDialog::getInt(this, tr("Depth around the location?"), tr("Margin"), 4, 1, 100, 1, &ok);
if(ok)
{
float voxelSize = QInputDialog::getDouble(this, tr("Voxel size?"), tr("Voxel Size"), 0.01, 0, 0.1, 3, &ok);
if(ok)
{
QString path = QFileDialog::getSaveFileName(this, tr("Save File"), pathDatabase_+"/Map" + QString::number(id) + ".pcd", tr("PCL file (*.pcd)"));
if(!path.isEmpty())
{
std::map<int, int> ids = memory_->getNeighborsId(id, margin, -1, false);
if(ids.size() > 0)
{
std::map<int, rtabmap::Transform> poses, optimizedPoses;
std::multimap<int, std::pair<int, rtabmap::Transform> > edgeConstraints;
memory_->getMetricConstraints(uKeys(ids), memory_->getSignature(id)->mapId(), poses, edgeConstraints, true);
item = new rtabmap::KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 255, 0, alpha));
UINFO("Poses=%d, constraints=%d", poses.size(), edgeConstraints.size());
scene->addItem(item);
item->setZValue(1);
rtabmap::util3d::saveTOROGraph("toro1.graph", poses, edgeConstraints);
rtabmap::Transform mapCorrection;
rtabmap::util3d::optimizeTOROGraph(poses, edgeConstraints, 100, optimizedPoses, mapCorrection);
rtabmap::util3d::saveTOROGraph("toro2.graph", optimizedPoses, edgeConstraints);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
for(std::map<int, int>::iterator iter = ids.begin(); iter!=ids.end(); ++iter)
{
rtabmap::Transform pose = uValue(optimizedPoses, iter->first, rtabmap::Transform());
if(!pose.isNull())
{
std::vector<unsigned char> image, depth, depth2d;
float depthConstant;
rtabmap::Transform localTransform;
memory_->getImageDepth(iter->first, image, depth, depth2d, depthConstant, localTransform);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
cv::Mat imageMat = rtabmap::util3d::uncompressImage(image);
cv::Mat depthMat = rtabmap::util3d::uncompressImage(depth);
cloud = rtabmap::util3d::cloudFromDepthRGB(
imageMat,
depthMat,
depthMat.cols/2, depthMat.rows/2,
1.0f/depthConstant, 1.0f/depthConstant);
if(voxelSize > 0.0f)
{
cloud = rtabmap::util3d::voxelize(cloud, voxelSize);
}
cloud = rtabmap::util3d::transformPointCloud(cloud, pose);
*assembledCloud += *cloud;
}
}
if(voxelSize > 0.0f)
{
pcl::VoxelGrid<pcl::PointXYZRGB> filter;
filter.setLeafSize(voxelSize, voxelSize, voxelSize);
filter.setInputCloud(assembledCloud);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZRGB>);
filter.filter(*tmp);
assembledCloud = tmp;
}
pcl::io::savePCDFile(path.toStdString(), *assembledCloud, true);
QMessageBox::information(this, "Generated Map", tr("Map saved to %1!\n(%2 nodes, %3 points)").arg(path).arg(ids.size()).arg(assembledCloud->size()));
}
else
{
QMessageBox::critical(this, tr("Error"), tr("No neighbors found for signature %1.").arg(id));
}
}
}
}
}
}
@@ -272,7 +339,7 @@ void DatabaseViewer::update(int value,
QLabel * labelActions,
QLabel * labelParents,
QLabel * labelChildren,
QGraphicsView * view,
rtabmap::ImageView * view,
QLabel * labelId)
{
UTimer timer;
@@ -282,23 +349,33 @@ void DatabaseViewer::update(int value,
labelChildren->clear();
if(value >= 0 && value < ids_.size())
{
view->scene()->clear();
view->clear();
int id = ids_.at(value);
labelId->setText(QString::number(id));
if(id>0)
{
//image
QImage img;
QImage imgDepth;
QMap<int, QByteArray>::iterator iter = imagesMap_.find(id);
QMap<int, QByteArray>::iterator iterDepth = depthImagesMap_.find(id);
if(iter == imagesMap_.end())
{
if(memory_)
{
cv::Mat image = memory_->getImage(id);
std::vector<unsigned char> image, depth, depth2d;
float depthConstant;
rtabmap::Transform localTransform;
memory_->getImageDepth(id, image, depth, depth2d, depthConstant, localTransform);
cv::Mat imageMat = rtabmap::util3d::uncompressImage(image);
cv::Mat depthMat = rtabmap::util3d::uncompressImage(depth);
UINFO("loaded image(%d/%d) depth(%d/%d) depthConstant(%f)",
imageMat.cols, imageMat.rows,
depthMat.cols, depthMat.rows,
depthConstant);
if(!image.empty())
{
IplImage iplImg = image;
img = ipl2QImage(&iplImg);
img = uCvMat2QImage(imageMat);
if(!img.isNull())
{
QByteArray ba;
@@ -308,11 +385,27 @@ void DatabaseViewer::update(int value,
imagesMap_.insert(id, ba);
}
}
if(!depth.empty())
{
imgDepth = uCvMat2QImage(depthMat);
if(!imgDepth.isNull())
{
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
QPixmap::fromImage(imgDepth).save(&buffer, "PGM"); // writes image into ba in PGM format
depthImagesMap_.insert(id, ba);
}
}
}
}
else
{
img.loadFromData(iter.value(), "BMP");
if(iterDepth != depthImagesMap_.end())
{
imgDepth.loadFromData(iterDepth.value(), "PGM");
}
}
if(memory_)
@@ -320,38 +413,47 @@ void DatabaseViewer::update(int value,
std::multimap<int, cv::KeyPoint> words = memory_->getWords(id);
if(words.size())
{
drawKeypoints(words, view->scene());
view->setFeatures(words);
}
}
if(!img.isNull())
{
view->scene()->addPixmap(QPixmap::fromImage(img));
view->setImage(img);
}
else
{
ULOGGER_DEBUG("Image is empty");
}
if(!imgDepth.isNull())
{
view->setImageDepth(imgDepth);
}
else
{
ULOGGER_DEBUG("Image depth is empty");
}
view->fitInView(view->sceneRect(), Qt::KeepAspectRatio);
// loops
std::set<int> parents;
std::set<int> children;
std::map<int, rtabmap::Transform> parents;
std::map<int, rtabmap::Transform> children;
memory_->getLoopClosureIds(id, parents, children, true);
if(parents.size())
{
QString str;
for(std::set<int>::iterator iter=parents.begin(); iter!=parents.end(); ++iter)
for(std::map<int, rtabmap::Transform>::iterator iter=parents.begin(); iter!=parents.end(); ++iter)
{
str.append(QString("%1 ").arg(*iter));
str.append(QString("%1 ").arg(iter->first));
}
labelParents->setText(str);
}
if(children.size())
{
QString str;
for(std::set<int>::iterator iter=children.begin(); iter!=children.end(); ++iter)
for(std::map<int, rtabmap::Transform>::iterator iter=children.begin(); iter!=children.end(); ++iter)
{
str.append(QString("%1 ").arg(*iter));
str.append(QString("%1 ").arg(iter->first));
}
labelChildren->setText(str);
}
@@ -392,29 +494,3 @@ void DatabaseViewer::sliderBMoved(int value)
ULOGGER_ERROR("Slider index out of range ?");
}
}
QImage DatabaseViewer::ipl2QImage(const IplImage *newImage)
{
QImage qtemp;
if (newImage && newImage->depth == IPL_DEPTH_8U && cvGetSize(newImage).width>0)
{
int x;
int y;
char* data = newImage->imageData;
qtemp= QImage(newImage->width, newImage->height,QImage::Format_RGB32 );
for( y = 0; y < newImage->height; y++, data +=newImage->widthStep )
{
for( x = 0; x < newImage->width; x++)
{
uint *p = (uint*)qtemp.scanLine (y) + x;
*p = qRgb(data[x * newImage->nChannels+2], data[x * newImage->nChannels+1],data[x * newImage->nChannels]);
}
}
}
else
{
ULOGGER_ERROR("Wrong IplImage format");
}
return qtemp;
}

View File

@@ -20,12 +20,13 @@
#include "DetailedProgressDialog.h"
#include <QtGui/QLayout>
#include <QtGui/QProgressBar>
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextEdit>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QCloseEvent>
#include <QtGui/QCheckBox>
#include <QtCore/QTimer>
#include <QtCore/QTime>
#include "rtabmap/utilite/ULogger.h"
namespace rtabmap {
@@ -39,9 +40,9 @@ DetailedProgressDialog::DetailedProgressDialog(QWidget *parent, Qt::WindowFlags
_text->setWordWrap(true);
_progressBar = new QProgressBar(this);
_progressBar->setMaximum(1);
_detailedText = new QPlainTextEdit(this);
_detailedText = new QTextEdit(this);
_detailedText->setReadOnly(true);
_detailedText->setLineWrapMode(QPlainTextEdit::NoWrap);
_detailedText->setLineWrapMode(QTextEdit::NoWrap);
_closeButton = new QPushButton(this);
_closeButton->setText("Close");
_closeWhenDoneCheckBox = new QCheckBox(this);
@@ -76,7 +77,9 @@ void DetailedProgressDialog::setAutoClose(bool on, int delayedClosingTimeSec)
void DetailedProgressDialog::appendText(const QString & text)
{
_text->setText(text);
_detailedText->appendPlainText(text);
QString html = tr("<html><font color=\"#999999\">%1 </font>%2</html>").arg(QTime::currentTime().toString("HH:mm:ss")).arg(text);
_detailedText->append(html);
_detailedText->ensureCursorVisible();
}
void DetailedProgressDialog::setValue(int value)
{
@@ -122,6 +125,12 @@ void DetailedProgressDialog::clear()
_closeButton->setEnabled(false);
}
void DetailedProgressDialog::resetProgress()
{
_progressBar->reset();
_closeButton->setEnabled(false);
}
void DetailedProgressDialog::closeEvent(QCloseEvent *event)
{
if(_progressBar->value() == _progressBar->maximum())

View File

@@ -23,7 +23,7 @@
#include <QtGui/QDialog>
class QLabel;
class QPlainTextEdit;
class QTextEdit;
class QProgressBar;
class QPushButton;
class QCheckBox;
@@ -51,10 +51,11 @@ public slots:
void appendText(const QString & text);
void incrementStep();
void clear();
void resetProgress();
private:
QLabel * _text;
QPlainTextEdit * _detailedText;
QTextEdit * _detailedText;
QProgressBar * _progressBar;
QPushButton * _closeButton;
QCheckBox * _closeWhenDoneCheckBox;

View File

@@ -6,6 +6,7 @@
<file>images/Play1Normal.png</file>
<file>images/Pause.ico</file>
<file>images/PauseOnLoop.ico</file>
<file>images/PauseOnLocalLoop.ico</file>
<file>images/PauseLoopRejected.ico</file>
<file>qss/default.qss</file>
<file>images/Plot16.png</file>

View File

@@ -25,6 +25,7 @@
#include <QtGui/QFileDialog>
#include <QtCore/QDir>
#include <QtGui/QAction>
#include <QtGui/QGraphicsEffect>
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/gui/KeypointItem.h"
@@ -34,7 +35,9 @@ ImageView::ImageView(QWidget * parent) :
QGraphicsView(parent),
_zoom(250),
_minZoom(250),
_savedFileName((QDir::homePath()+ "/") + "picture" + ".png")
_savedFileName((QDir::homePath()+ "/") + "picture" + ".png"),
_image(0),
_imageDepth(0)
{
this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
this->setScene(new QGraphicsScene(this));
@@ -44,6 +47,9 @@ ImageView::ImageView(QWidget * parent) :
_showImage = _menu->addAction(tr("Show image"));
_showImage->setCheckable(true);
_showImage->setChecked(true);
_showImageDepth = _menu->addAction(tr("Show image depth"));
_showImageDepth->setCheckable(true);
_showImageDepth->setChecked(false);
_showFeatures = _menu->addAction(tr("Show features"));
_showFeatures->setCheckable(true);
_showFeatures->setChecked(true);
@@ -54,7 +60,7 @@ ImageView::ImageView(QWidget * parent) :
}
ImageView::~ImageView() {
clear();
}
void ImageView::resetZoom()
@@ -68,6 +74,11 @@ bool ImageView::isImageShown()
return _showImage->isChecked();
}
bool ImageView::isImageDepthShown()
{
return _showImageDepth->isChecked();
}
bool ImageView::isFeaturesShown()
{
return _showFeatures->isChecked();
@@ -76,7 +87,30 @@ bool ImageView::isFeaturesShown()
void ImageView::setFeaturesShown(bool shown)
{
_showFeatures->setChecked(shown);
this->updateItemsShown();
for(int i=0; i<_features.size(); ++i)
{
_features[i]->setVisible(_showFeatures->isChecked());
}
}
void ImageView::setImageShown(bool shown)
{
_showImage->setChecked(shown);
if(_image)
{
_image->setVisible(_showImage->isChecked());
this->updateOpacity();
}
}
void ImageView::setImageDepthShown(bool shown)
{
_showImageDepth->setChecked(shown);
if(_imageDepth)
{
_imageDepth->setVisible(_showImageDepth->isChecked());
this->updateOpacity();
}
}
bool ImageView::isLinesShown()
@@ -87,7 +121,14 @@ bool ImageView::isLinesShown()
void ImageView::setLinesShown(bool shown)
{
_showLines->setChecked(shown);
this->updateItemsShown();
QList<QGraphicsItem*> items = this->scene()->items();
for(int i=0; i<items.size(); ++i)
{
if( qgraphicsitem_cast<QGraphicsLineItem*>(items.at(i)))
{
items.at(i)->setVisible(_showLines->isChecked());
}
}
}
void ImageView::contextMenuEvent(QContextMenuEvent * e)
@@ -110,28 +151,46 @@ void ImageView::contextMenuEvent(QContextMenuEvent * e)
img.save(text);
}
}
else if(action == _showFeatures || action == _showImage || action == _showLines)
else if(action == _showFeatures)
{
this->updateItemsShown();
this->setFeaturesShown(_showFeatures->isChecked());
}
else if(action == _showImage)
{
this->setImageShown(_showImage->isChecked());
}
else if(action == _showImageDepth)
{
this->setImageDepthShown(_showImageDepth->isChecked());
}
else if(action == _showLines)
{
this->setLinesShown(_showLines->isChecked());
}
if(action == _showImage || action ==_showImageDepth)
{
this->updateOpacity();
}
}
void ImageView::updateItemsShown()
void ImageView::updateOpacity()
{
QList<QGraphicsItem*> items = this->scene()->items();
for(int i=0; i<items.size(); ++i)
if(_image && _imageDepth)
{
if(qgraphicsitem_cast<KeypointItem*>(items.at(i)))
if(_image->isVisible() && _imageDepth->isVisible())
{
items.at(i)->setVisible(_showFeatures->isChecked());
QGraphicsOpacityEffect * effect = new QGraphicsOpacityEffect();
QGraphicsOpacityEffect * effect2 = new QGraphicsOpacityEffect();
effect->setOpacity(0.5);
effect2->setOpacity(0.5);
_image->setGraphicsEffect(effect);
_imageDepth->setGraphicsEffect(effect2);
}
else if( qgraphicsitem_cast<QGraphicsLineItem*>(items.at(i)))
else
{
items.at(i)->setVisible(_showLines->isChecked());
}
else if(qgraphicsitem_cast<QGraphicsPixmapItem*>(items.at(i)))
{
items.at(i)->setVisible(_showImage->isChecked());
_image->setGraphicsEffect(0);
_imageDepth->setGraphicsEffect(0);
}
}
}
@@ -175,4 +234,94 @@ void ImageView::wheelEvent(QWheelEvent * e)
this->setMatrix(matrix);
}
void ImageView::setFeatures(const std::multimap<int, cv::KeyPoint> & refWords)
{
for(int i=0; i<_features.size(); ++i)
{
scene()->removeItem(_features[i]);
delete _features[i];
}
_features.clear();
rtabmap::KeypointItem * item = 0;
int alpha = 70;
for(std::multimap<int, cv::KeyPoint>::const_iterator i = refWords.begin(); i != refWords.end(); ++i )
{
const cv::KeyPoint & r = (*i).second;
int id = (*i).first;
QString info = QString( "WordRef = %1\n"
"Laplacian = %2\n"
"Dir = %3\n"
"Hessian = %4\n"
"X = %5\n"
"Y = %6\n"
"Size = %7").arg(id).arg(1).arg(r.angle).arg(r.response).arg(r.pt.x).arg(r.pt.y).arg(r.size);
float radius = r.size*1.2/9.*2;
item = new rtabmap::KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 255, 0, alpha));
scene()->addItem(item);
_features.append(item);
item->setVisible(_showFeatures->isChecked());
item->setZValue(1);
}
}
void ImageView::setImage(const QImage & image)
{
if(_image)
{
_image->setPixmap(QPixmap::fromImage(image));
}
else
{
_image = scene()->addPixmap(QPixmap::fromImage(image));
_image->setVisible(_showImage->isChecked());
_showImage->setEnabled(true);
this->updateOpacity();
}
}
void ImageView::setImageDepth(const QImage & imageDepth)
{
if(_imageDepth)
{
_imageDepth->setPixmap(QPixmap::fromImage(imageDepth));
}
else
{
_imageDepth = scene()->addPixmap(QPixmap::fromImage(imageDepth));
_imageDepth->setVisible(_showImageDepth->isChecked());
_showImageDepth->setEnabled(true);
this->updateOpacity();
}
}
void ImageView::clear()
{
for(int i=0; i<_features.size(); ++i)
{
scene()->removeItem(_features[i]);
delete _features[i];
}
_features.clear();
if(_image)
{
scene()->removeItem(_image);
delete _image;
_image = 0;
_showImage->setEnabled(false);
}
if(_imageDepth)
{
scene()->removeItem(_imageDepth);
delete _imageDepth;
_imageDepth = 0;
_showImageDepth->setEnabled(false);
}
scene()->clear();
}
}

View File

@@ -0,0 +1,212 @@
/*
* LoopClosureViewer.cpp
*
* Created on: 2013-10-21
* Author: Mathieu
*/
#include "rtabmap/gui/LoopClosureViewer.h"
#include "ui_loopClosureViewer.h"
#include "rtabmap/core/Memory.h"
#include "rtabmap/core/util3d.h"
#include "rtabmap/core/Signature.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UStl.h"
#include <QtCore/QTimer>
namespace rtabmap {
LoopClosureViewer::LoopClosureViewer(QWidget * parent) :
QWidget(parent),
sA_(0),
sB_(0),
decimation_(1),
maxDepth_(0),
samples_(0)
{
ui_ = new Ui_loopClosureViewer();
ui_->setupUi(this);
connect(ui_->checkBox_rawCloud, SIGNAL(clicked()), this, SLOT(updateView()));
}
LoopClosureViewer::~LoopClosureViewer() {
delete ui_;
if(sA_)
{
delete sA_;
}
if(sB_)
{
delete sB_;
}
}
void LoopClosureViewer::setData(Signature * sA, Signature * sB)
{
if(sA_)
{
delete sA_;
}
if(sB_)
{
delete sB_;
}
sA_ = sA;
sB_ = sB;
if(sA_ && sB_)
{
ui_->label_idA->setText(QString("[%1-%2]").arg(sA->id()).arg(sB->id()));
}
}
void LoopClosureViewer::updateView(const Transform & transform)
{
if(sA_ && sB_)
{
int decimation = 1;
float maxDepth = 0;
int samples = 0;
if(!ui_->checkBox_rawCloud->isChecked())
{
decimation = decimation_;
maxDepth = maxDepth_;
samples = samples_;
}
UDEBUG("decimation = %d", decimation);
UDEBUG("maxDepth = %d", maxDepth);
UDEBUG("samples = %d", samples);
Transform t;
if(!transform.isNull())
{
transform_ = transform;
t = transform;
}
else if(!transform_.isNull())
{
t = transform_;
}
else
{
t = sB_->getPose();
}
UDEBUG("t= %s", t.prettyPrint().c_str());
ui_->label_transform->setText(QString("(%1)").arg(t.prettyPrint().c_str()));
if(!t.isNull())
{
util3d::CompressionThread ctiA(sA_->getImage(), true);
util3d::CompressionThread ctdA(sA_->getDepth(), true);
util3d::CompressionThread ctiB(sB_->getImage(), true);
util3d::CompressionThread ctdB(sB_->getDepth(), true);
util3d::CompressionThread ct2dA(sA_->getDepth2D(), false);
util3d::CompressionThread ct2dB(sB_->getDepth2D(), false);
ctiA.start();
ctdA.start();
ctiB.start();
ctdB.start();
ct2dA.start();
ct2dB.start();
ctiA.join();
ctdA.join();
ctiB.join();
ctdB.join();
ct2dA.join();
ct2dB.join();
cv::Mat imageA = ctiA.getUncompressedData();
cv::Mat depthA = ctdA.getUncompressedData();
cv::Mat imageB = ctiB.getUncompressedData();
cv::Mat depthB = ctdB.getUncompressedData();
cv::Mat depth2dA = ct2dA.getUncompressedData();
cv::Mat depth2dB = ct2dB.getUncompressedData();
//cloud 3d
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudA;
cloudA = util3d::cloudFromDepthRGB(
imageA,
depthA,
depthA.cols/2,
depthA.rows/2,
1.0f/sA_->getDepthConstant(),
1.0f/sA_->getDepthConstant(),
decimation);
cloudA = util3d::removeNaNFromPointCloud(cloudA);
if(maxDepth>0.0)
{
cloudA = util3d::passThrough(cloudA, "z", 0, maxDepth);
}
if(samples>0 && (int)cloudA->size() > samples)
{
cloudA = util3d::sampling(cloudA, samples);
}
cloudA = util3d::transformPointCloud(cloudA, sA_->getLocalTransform());
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudB;
cloudB = util3d::cloudFromDepthRGB(
imageB,
depthB,
depthB.cols/2,
depthB.rows/2,
1.0f/sB_->getDepthConstant(),
1.0f/sB_->getDepthConstant(),
decimation);
cloudB = util3d::removeNaNFromPointCloud(cloudB);
if(maxDepth>0.0)
{
cloudB = util3d::passThrough(cloudB, "z", 0, maxDepth);
}
if(samples>0 && (int)cloudB->size() > samples)
{
cloudB = util3d::sampling(cloudB, samples);
}
cloudB = util3d::transformPointCloud(cloudB, t*sB_->getLocalTransform());
//cloud 2d
pcl::PointCloud<pcl::PointXYZ>::Ptr scanA, scanB;
scanA = util3d::depth2DToPointCloud(depth2dA);
scanB = util3d::depth2DToPointCloud(depth2dB);
scanB = util3d::transformPointCloud(scanB, t);
ui_->label_idA->setText(QString("[%1 (%2) -> %3 (%4)]").arg(sB_->id()).arg(cloudB->size()).arg(sA_->id()).arg(cloudA->size()));
if(cloudA->size())
{
ui_->cloudViewerTransform->addOrUpdateCloud("cloud0", cloudA);
}
if(cloudB->size())
{
ui_->cloudViewerTransform->addOrUpdateCloud("cloud1", cloudB);
}
if(scanA->size())
{
ui_->cloudViewerTransform->addOrUpdateCloud("scan0", scanA);
}
if(scanB->size())
{
ui_->cloudViewerTransform->addOrUpdateCloud("scan1", scanB);
}
}
else
{
ui_->cloudViewerTransform->removeAllClouds();
}
ui_->cloudViewerTransform->render();
}
}
void LoopClosureViewer::showEvent(QShowEvent * event)
{
QWidget::showEvent( event );
QTimer::singleShot(500, this, SLOT(updateView())); // make sure the QVTKWidget is shown!
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
/*
* OdometryViewer.cpp
*
* Created on: 2013-10-15
* Author: Mathieu
*/
#include "rtabmap/gui/OdometryViewer.h"
#include "rtabmap/core/util3d.h"
#include "rtabmap/core/OdometryEvent.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UConversion.h"
#include <pcl/common/transforms.h>
#include <pcl/io/pcd_io.h>
#include <QtGui/QInputDialog>
#include <QtGui/QAction>
#include <QtGui/QMenu>
#include <QtGui/QKeyEvent>
namespace rtabmap {
OdometryViewer::OdometryViewer(int maxClouds, int decimation, float voxelSize, QWidget * parent) :
CloudViewer(parent),
maxClouds_(maxClouds),
voxelSize_(voxelSize),
decimation_(decimation),
id_(0),
_aSetVoxelSize(0),
_aSetDecimation(0),
_aSetCloudHistorySize(0),
_aPause(0)
{
//add actions to CloudViewer menu
_aSetVoxelSize = new QAction("Set voxel size...", this);
_aSetDecimation = new QAction("Set depth image decimation...", this);
_aSetCloudHistorySize = new QAction("Set cloud history size...", this);
_aPause = new QAction("Pause", this);
_aPause->setCheckable(true);
menu()->addAction(_aSetVoxelSize);
menu()->addAction(_aSetDecimation);
menu()->addAction(_aSetCloudHistorySize);
menu()->addAction(_aPause);
}
void OdometryViewer::processData()
{
rtabmap::Image data;
dataMutex_.lock();
if(buffer_.size())
{
data = buffer_.back();
buffer_.clear();
}
dataMutex_.unlock();
if(!data.empty() && this->isVisible())
{
UINFO("New pose = %s", data.pose().prettyPrint().c_str());
// visualization: buffering the clouds
// Create the new cloud
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
cloud = util3d::cloudFromDepthRGB(
data.image(),
data.depth(),
float(data.depth().cols/2),
float(data.depth().rows/2),
1.0f/data.depthConstant(),
1.0f/data.depthConstant(),
decimation_);
if(voxelSize_ > 0.0f)
{
cloud = util3d::voxelize(cloud, voxelSize_);
}
cloud = util3d::transformPointCloud(cloud, data.localTransform());
data.id()?id_=data.id():++id_;
clouds_.insert(std::make_pair(id_, cloud));
if((int)clouds_.size() > maxClouds_)
{
this->removeCloud(uFormat("cloud%d", clouds_.begin()->first));
clouds_.erase(clouds_.begin());
}
if(clouds_.size())
{
this->addCloud(uFormat("cloud%d", clouds_.rbegin()->first), clouds_.rbegin()->second, data.pose());
}
this->updateCameraPosition(data.pose());
this->setBackgroundColor(Qt::black);
this->render();
}
}
void OdometryViewer::handleEvent(UEvent * event)
{
if(!_aPause->isChecked())
{
if(event->getClassName().compare("OdometryEvent") == 0)
{
rtabmap::OdometryEvent * odomEvent = (rtabmap::OdometryEvent*)event;
if(odomEvent->isValid())
{
bool empty = false;
dataMutex_.lock();
if(buffer_.empty())
{
buffer_.push_back(odomEvent->data());
empty= true;
}
else
{
buffer_.back() = odomEvent->data();
}
dataMutex_.unlock();
if(empty)
{
QMetaObject::invokeMethod(this, "processData");
}
}
else
{
//UWARN("odom=%fs, Cannot compute odometry!!!", timer_.restart());
QMetaObject::invokeMethod(this, "setBackgroundColor", Q_ARG(QColor, Qt::darkRed));
QMetaObject::invokeMethod(this, "render");
}
}
}
}
void OdometryViewer::handleAction(QAction * a)
{
CloudViewer::handleAction(a);
if(a == _aSetVoxelSize)
{
bool ok;
double value = QInputDialog::getDouble(this, tr("Set voxel size"), tr("Size (0=disabled)"), voxelSize_, 0.0, 0.1, 2, &ok);
if(ok)
{
voxelSize_ = value;
}
}
else if(a == _aSetCloudHistorySize)
{
bool ok;
int value = QInputDialog::getInt(this, tr("Set cloud history size"), tr("Size (0=infinite)"), maxClouds_, 0, 100, 1, &ok);
if(ok)
{
maxClouds_ = value;
}
}
else if(a == _aSetDecimation)
{
bool ok;
int value = QInputDialog::getInt(this, tr("Set depth image decimation"), tr("Decimation (0=infinite)"), decimation_, 1, 8, 1, &ok);
if(ok)
{
decimation_ = value;
}
}
}
} /* namespace rtabmap */

View File

@@ -19,6 +19,8 @@
#include "PdfPlot.h"
#include <rtabmap/utilite/ULogger.h>
#include "rtabmap/gui/UCv2Qt.h"
#include "rtabmap/core/util3d.h"
namespace rtabmap {
@@ -59,15 +61,13 @@ void PdfPlotItem::showDescription(bool shown)
if(!_img && _imagesRef)
{
QImage img;
QMap<int, QByteArray>::const_iterator iter = _imagesRef->find(int(this->data().x()));
QMap<int, std::vector<unsigned char> >::const_iterator iter = _imagesRef->find(int(this->data().x()));
if(iter != _imagesRef->constEnd())
{
if(img.loadFromData(iter.value(), "JPEG"))
{
QPixmap scaled = QPixmap::fromImage(img).scaledToWidth(128);
_img = new QGraphicsPixmapItem(scaled, this);
_img->setVisible(false);
}
img = uCvMat2QImage(util3d::uncompressImage(iter.value()));
QPixmap scaled = QPixmap::fromImage(img).scaledToWidth(128);
_img = new QGraphicsPixmapItem(scaled, this);
_img->setVisible(false);
}
}
@@ -103,7 +103,7 @@ void PdfPlotItem::showDescription(bool shown)
PdfPlotCurve::PdfPlotCurve(const QString & name, const QMap<int, QByteArray> * imagesMapRef = 0, QObject * parent) :
PdfPlotCurve::PdfPlotCurve(const QString & name, const QMap<int, std::vector<unsigned char> > * imagesMapRef = 0, QObject * parent) :
UPlotCurve(name, parent),
_imagesMapRef(imagesMapRef)
{

View File

@@ -21,6 +21,7 @@
#define PDFPLOT_H_
#include <utilite/UPlot.h>
#include "opencv2/opencv.hpp"
namespace rtabmap {
@@ -31,7 +32,7 @@ public:
virtual ~PdfPlotItem();
void setLikelihood(int id, float value, int childCount);
void setImagesRef(const QMap<int, QByteArray> * imagesRef) {_imagesRef = imagesRef;}
void setImagesRef(const QMap<int, std::vector<unsigned char> > * imagesRef) {_imagesRef = imagesRef;}
float value() const {return this->data().y();}
int id() const {return this->data().x();}
@@ -42,7 +43,7 @@ protected:
private:
QGraphicsPixmapItem * _img;
int _childCount;
const QMap<int, QByteArray> * _imagesRef;
const QMap<int, std::vector<unsigned char> > * _imagesRef;
QGraphicsTextItem * _text;
};
@@ -52,14 +53,14 @@ class PdfPlotCurve : public UPlotCurve
Q_OBJECT
public:
PdfPlotCurve(const QString & name, const QMap<int, QByteArray> * imagesMapRef, QObject * parent = 0);
PdfPlotCurve(const QString & name, const QMap<int, std::vector<unsigned char> > * imagesMapRef, QObject * parent = 0);
virtual ~PdfPlotCurve();
virtual void clear();
void setData(const QMap<int, float> & dataMap, const QMap<int, int> & weightsMap);
private:
const QMap<int, QByteArray> * _imagesMapRef;
const QMap<int, std::vector<unsigned char> > * _imagesMapRef;
};
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke
*
* This file is part of RTAB-Map.
*
* RTAB-Map is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RTAB-Map is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RTAB-Map. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rtabmap/gui/qtipl.h"
#include "rtabmap/utilite/ULogger.h"
#include <opencv2/core/core_c.h>
namespace rtabmap {
// TODO : support only from gray 8bits ?
QImage Ipl2QImage(const IplImage *newImage, int alpha)
{
QImage qtemp;
if (newImage && newImage->depth == IPL_DEPTH_8U && cvGetSize(newImage).width>0)
{
int x;
int y;
char* data = newImage->imageData;
qtemp= QImage(newImage->width, newImage->height,QImage::Format_ARGB32 );
for( y = 0; y < newImage->height; y++, data +=newImage->widthStep )
{
for( x = 0; x < newImage->width; x++)
{
uint *p = (uint*)qtemp.scanLine (y) + x;
*p = qRgba(data[x * newImage->nChannels+2], data[x * newImage->nChannels+1],data[x * newImage->nChannels], alpha);
}
}
}
else
{
ULOGGER_ERROR("Wrong IplImage format");
}
return qtemp;
}
}

View File

@@ -29,7 +29,7 @@
<number>0</number>
</property>
<item>
<widget class="QGraphicsView" name="graphicsView_A"/>
<widget class="rtabmap::ImageView" name="graphicsView_A"/>
</item>
<item>
<widget class="QScrollArea" name="scrollArea_2">
@@ -153,7 +153,7 @@
<number>0</number>
</property>
<item>
<widget class="QGraphicsView" name="graphicsView_B"/>
<widget class="rtabmap::ImageView" name="graphicsView_B"/>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
@@ -288,7 +288,7 @@
<x>0</x>
<y>0</y>
<width>736</width>
<height>22</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -309,7 +309,7 @@
<addaction name="actionClean_database"/>
<addaction name="actionClean_local_graph"/>
<addaction name="separator"/>
<addaction name="actionUpdate_base_ids"/>
<addaction name="actionGenerate_3D_map_pcd"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
@@ -354,7 +354,19 @@
<string>Update base ids</string>
</property>
</action>
<action name="actionGenerate_3D_map_pcd">
<property name="text">
<string>Generate 3D map (.pcd) ...</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>rtabmap::ImageView</class>
<extends>QGraphicsView</extends>
<header>rtabmap/gui/ImageView.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -82,10 +82,10 @@ p, li { white-space: pre-wrap; }
</item>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<item row="7" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Author :</string>
<string>Version :</string>
</property>
</widget>
</item>
@@ -96,10 +96,10 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_6">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Version :</string>
<string>Author :</string>
</property>
</widget>
</item>
@@ -174,6 +174,23 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>PCL version :</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_pcl_version">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>loopClosureViewer</class>
<widget class="QWidget" name="loopClosureViewer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>515</width>
<height>390</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_idA">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_idB">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_transform">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="checkBox_rawCloud">
<property name="text">
<string>Raw cloud</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="rtabmap::CloudViewer" name="cloudViewerTransform" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>rtabmap::CloudViewer</class>
<extends>QWidget</extends>
<header>rtabmap/gui/CloudViewer.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1056</width>
<height>642</height>
<width>809</width>
<height>661</height>
</rect>
</property>
<property name="windowTitle">
@@ -101,8 +101,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1056</width>
<height>22</height>
<width>809</width>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -115,35 +115,25 @@
<property name="title">
<string>Edit</string>
</property>
<widget class="QMenu" name="menuAspect_ratio">
<property name="title">
<string>Aspect ratio</string>
</property>
<addaction name="action16_9"/>
<addaction name="action16_10"/>
<addaction name="action4_3"/>
<addaction name="separator"/>
<addaction name="action1080p"/>
<addaction name="action720p"/>
<addaction name="action480p"/>
<addaction name="action360p"/>
<addaction name="action240p"/>
</widget>
<addaction name="actionApply_settings_to_the_detector"/>
<addaction name="actionClear_cache"/>
<addaction name="actionDownload_all_clouds"/>
<addaction name="separator"/>
<addaction name="actionOpen_working_directory"/>
<addaction name="actionPrint_loop_closure_IDs_to_console"/>
<addaction name="separator"/>
<addaction name="actionReset_the_memory"/>
<addaction name="actionDelete_memory"/>
<addaction name="actionClear_cache"/>
<addaction name="separator"/>
<addaction name="actionDump_the_memory"/>
<addaction name="actionDump_the_prediction_matrix"/>
<addaction name="actionPrint_loop_closure_IDs_to_console"/>
<addaction name="actionGenerate_map"/>
<addaction name="actionGenerate_local_map"/>
<addaction name="actionReset_the_memory"/>
<addaction name="separator"/>
<addaction name="actionAuto_screen_capture"/>
<addaction name="menuAspect_ratio"/>
<addaction name="actionView_high_res_point_cloud"/>
<addaction name="actionSave_point_cloud"/>
<addaction name="actionView_point_cloud_as_mesh"/>
<addaction name="actionSave_mesh_ply_vtk_stl"/>
</widget>
<widget class="QMenu" name="menu6">
<property name="title">
@@ -171,6 +161,7 @@
</widget>
<addaction name="menuImage"/>
<addaction name="actionDatabase"/>
<addaction name="actionOpenni_RGBD"/>
</widget>
<addaction name="menuSelect_source"/>
<addaction name="separator"/>
@@ -180,6 +171,13 @@
<addaction name="separator"/>
<addaction name="actionPause_on_match"/>
<addaction name="actionPause_when_a_loop_hypothesis_is_rejected"/>
<addaction name="actionPause_on_local_loop_detection"/>
<addaction name="separator"/>
<addaction name="actionSLAM_mode"/>
<addaction name="actionLocalization_mode"/>
<addaction name="separator"/>
<addaction name="actionReset_Odometry"/>
<addaction name="actionTrigger_a_new_map"/>
</widget>
<widget class="QMenu" name="menuWindow">
<property name="title">
@@ -198,8 +196,24 @@
<addaction name="actionSave_state"/>
<addaction name="actionLoad_state"/>
</widget>
<widget class="QMenu" name="menuAspect_ratio_2">
<property name="title">
<string>Aspect ratio</string>
</property>
<addaction name="action16_9"/>
<addaction name="action16_10"/>
<addaction name="action4_3"/>
<addaction name="separator"/>
<addaction name="action1080p"/>
<addaction name="action720p"/>
<addaction name="action480p"/>
<addaction name="action360p"/>
<addaction name="action240p"/>
</widget>
<addaction name="menuShow_view"/>
<addaction name="menuFigures"/>
<addaction name="actionAuto_screen_capture"/>
<addaction name="menuAspect_ratio_2"/>
<addaction name="separator"/>
<addaction name="actionPreferences"/>
</widget>
@@ -250,6 +264,11 @@
<addaction name="separator"/>
<addaction name="actionPause_on_match"/>
<addaction name="actionPause_when_a_loop_hypothesis_is_rejected"/>
<addaction name="actionPause_on_local_loop_detection"/>
<addaction name="separator"/>
<addaction name="actionSLAM_mode"/>
<addaction name="actionLocalization_mode"/>
<addaction name="separator"/>
</widget>
<widget class="QDockWidget" name="dockWidget_statsV2">
<property name="floating">
@@ -277,6 +296,28 @@
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_stats_timeLimit">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> ms</string>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
<property name="singleStep">
<double>50.000000000000000</double>
</property>
<property name="value">
<double>450.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
@@ -316,7 +357,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_45">
<property name="text">
<string>Elapsed time (hh:mm:ss)</string>
@@ -326,7 +367,7 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QLabel" name="label_elapsedTime">
<property name="text">
<string>Unknown</string>
@@ -336,14 +377,14 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Current image id</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QLabel" name="label_stats_imageNumber">
<property name="text">
<string>Unknown</string>
@@ -353,14 +394,14 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Loop closures detected</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QLabel" name="label_stats_loopClosuresDetected">
<property name="text">
<string>0</string>
@@ -370,7 +411,7 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_38">
<property name="text">
<string>Loop closures detected
@@ -378,7 +419,7 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QLabel" name="label_stats_loopClosuresReactivatedDetected">
<property name="text">
<string>0</string>
@@ -388,14 +429,14 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Loop closures rejected</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QLabel" name="label_stats_loopClosuresRejected">
<property name="text">
<string>0</string>
@@ -406,14 +447,14 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="doubleSpinBox_stats_imgRate_label">
<property name="text">
<string>Image rate</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<item row="3" column="0">
<widget class="QLabel" name="label_timeLimit">
<property name="text">
<string>Time limit processing</string>
</property>
@@ -423,24 +464,31 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_stats_timeLimit">
<widget class="QDoubleSpinBox" name="doubleSpinBox_stats_detectionRate">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> ms</string>
<string> Hz</string>
</property>
<property name="decimals">
<number>0</number>
<number>1</number>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>50.000000000000000</double>
<double>0.100000000000000</double>
</property>
<property name="value">
<double>450.000000000000000</double>
<double>2.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="doubleSpinBox_stats_imgRate_label_2">
<property name="text">
<string>RTAB-Map update rate</string>
</property>
</widget>
</item>
@@ -480,13 +528,7 @@
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
@@ -537,6 +579,48 @@
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_cloudViewer">
<property name="windowTitle">
<string>3D Map</string>
</property>
<attribute name="dockWidgetArea">
<number>4</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_5">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="rtabmap::CloudViewer" name="widget_cloudViewer" native="true"/>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_loopClosureViewer">
<property name="windowTitle">
<string>3D Loop closure</string>
</property>
<attribute name="dockWidgetArea">
<number>8</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_7">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="rtabmap::LoopClosureViewer" name="widget_loopClosureViewer" native="true"/>
</item>
</layout>
</widget>
</widget>
<action name="actionExit">
<property name="text">
<string>Exit</string>
@@ -593,7 +677,7 @@
<normaloff>:/images/PauseOnLoop.ico</normaloff>:/images/PauseOnLoop.ico</iconset>
</property>
<property name="text">
<string>Pause on match</string>
<string>Pause on loop closure detection</string>
</property>
</action>
<action name="actionStop">
@@ -612,7 +696,7 @@
</action>
<action name="actionApply_settings_to_the_detector">
<property name="text">
<string>Apply settings to the detector</string>
<string>Apply settings to detector</string>
</property>
</action>
<action name="actionDump_the_memory">
@@ -629,7 +713,7 @@
<normaloff>:/images/PauseLoopRejected.ico</normaloff>:/images/PauseLoopRejected.ico</iconset>
</property>
<property name="text">
<string>Pause when a loop hypothesis is rejected</string>
<string>Pause on loop closure rejection</string>
</property>
</action>
<action name="actionClear_cache">
@@ -662,7 +746,7 @@
</action>
<action name="actionGenerate_map">
<property name="text">
<string>Generate map...</string>
<string>Generate graph map (*.dot)...</string>
</property>
</action>
<action name="actionDelete_memory">
@@ -749,7 +833,7 @@
</action>
<action name="actionGenerate_local_map">
<property name="text">
<string>Generate local map...</string>
<string>Generate graph local map (*.dot)...</string>
</property>
</action>
<action name="actionPrint_loop_closure_IDs_to_console">
@@ -757,6 +841,80 @@
<string>Print loop closure IDs to console</string>
</property>
</action>
<action name="actionOpenni_RGBD">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Openni (RGBD)</string>
</property>
</action>
<action name="actionSave_point_cloud">
<property name="text">
<string>Save high-res point clouds (*.pcd *.ply *.vtk)...</string>
</property>
</action>
<action name="actionDownload_all_clouds">
<property name="text">
<string>Download all clouds (update cache)</string>
</property>
</action>
<action name="actionPause_on_local_loop_detection">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../GuiLib.qrc">
<normaloff>:/images/PauseOnLocalLoop.ico</normaloff>:/images/PauseOnLocalLoop.ico</iconset>
</property>
<property name="text">
<string>Pause on local loop closure detection</string>
</property>
</action>
<action name="actionSLAM_mode">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Mapping</string>
</property>
<property name="toolTip">
<string>Simultaneous Localization And Mapping (SLAM)</string>
</property>
</action>
<action name="actionLocalization_mode">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Localization</string>
</property>
</action>
<action name="actionReset_Odometry">
<property name="text">
<string>Reset Odometry</string>
</property>
</action>
<action name="actionView_high_res_point_cloud">
<property name="text">
<string>View high-res point clouds</string>
</property>
</action>
<action name="actionView_point_cloud_as_mesh">
<property name="text">
<string>View meshes</string>
</property>
</action>
<action name="actionSave_mesh_ply_vtk_stl">
<property name="text">
<string>Save meshes (*.ply *.vtk)...</string>
</property>
</action>
<action name="actionTrigger_a_new_map">
<property name="text">
<string>Trigger a new map</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@@ -782,6 +940,18 @@
<header>ConsoleWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>rtabmap::CloudViewer</class>
<extends>QWidget</extends>
<header>../include/rtabmap/gui/CloudViewer.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>rtabmap::LoopClosureViewer</class>
<extends>QWidget</extends>
<header>../include/rtabmap/gui/LoopClosureViewer.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../GuiLib.qrc"/>

File diff suppressed because it is too large Load Diff