Compare commits

..
30 Commits
Author SHA1 Message Date
Mathieu Labbé 530fd5d2c1 increased version to 0.8.3 2015-02-08 18:00:18 -05:00
Mathieu Labbé 571172754a Windows: fixed crash on startup with CloudViewer/PCLVisualization/QVTKWidget initialization 2015-02-08 17:55:40 -05:00
Mathieu Labbe cff869ad03 accepting plan of only 1 node 2015-02-06 16:44:38 -05:00
Mathieu Labbe 695b7005d0 minor fix 2015-02-06 16:01:49 -05:00
Mathieu Labbe e3074d304c Added rtabmap::computePath() using a pose goal instead of node Id. Graph methods moved in rtabmap::graph namespace 2015-02-06 13:52:02 -05:00
Mathieu Labbe 3d1eccbce1 changed all std::atof() to uStr2Float() to fix locale comma float conversion problem 2015-02-04 21:20:17 -05:00
Mathieu Labbe f8b625c7c7 Added DBDriver::setTimestampUpdateEnabled()
Don't update timestamps if only links are modified
2015-02-04 17:40:16 -05:00
Mathieu Labbe 09625b766e added some debug infos 2015-02-03 17:20:17 -05:00
Mathieu Labbe b2e5b7fbfc check if same node before adding virtual link 2015-02-03 14:11:57 -05:00
Mathieu Labbe ee885321aa Rtabmap: Added getPathNextPoses() and getPathNextNodes(). Local path is sent over statistics.
GUI: local path is shown in GraphViewer. Now handling stereo odometry data.
2015-02-03 11:09:10 -05:00
Mathieu Labbe ee0051a585 fixed Time threshold not set on Console loop closure detection app 2015-01-30 18:32:35 -05:00
Mathieu Labbe 9d20403930 Simplified rtabmap-imagesJoiner tools 2015-01-30 18:07:28 -05:00
Mathieu Labbe 1fe2a9ed95 refactored GraphViewer 2015-01-30 17:27:34 -05:00
Mathieu Labbe 2872387d4c fixed GraphView blue referential color 2015-01-30 17:19:51 -05:00
Mathieu Labbe d97ae60e6c CloudViewer: using QWidget::update() call instead of render directly the window 2015-01-30 17:02:02 -05:00
Mathieu Labbe a2dde36093 Integration of the path planner into rtabmap's memory management to actually retrieve nodes on the planned path on the graph.
Added Rtabmap::clearPath(), Rtabmap::computePath(), Rtabmap::getPathGoalId() and Rtabmap::updateGoalIndex() methods.
Added virtual links when path retrieval is activated (virtual links are not saved to database and removed when the path goal is reached).
New parameters: RGBD/GoalReachedRadius and RGBD/MaxAnticipatedNodes
Refactored how loop closure hypotheses are selected (fixed ratio between consecutive hypotheses, not the last loop closure).
Updated getNodesInRadius() to use all filtered scans.
GUI: window can be saved/loaded maximized
GUI: saving Graph View parameters to config.ini
2015-01-30 15:30:57 -05:00
Mathieu Labbe 45a5da9f16 Fixed ./rtabmap-rgbd_mapping fatal error : "[FATAL] (2015-01-30 11:29:02.636) util3d.cpp:632::cloudFromDepthRGB() Condition (!imageDepth.empty() && (imageDepth.type() == CV_16UC1 || imageDepth.type() == CV_32FC1)) not met!" 2015-01-30 11:41:46 -05:00
Mathieu Labbe 2cc34cbe7e fixed a not retrieved signatures bug 2015-01-27 16:57:07 -05:00
Mathieu Labbe 47045002a9 Added initial node to graph computed from A* 2015-01-26 17:20:43 -05:00
Mathieu Labbe f9fb6216d8 GUI: Optimized odometry display
GUI: moved alpha parameter into ImageView object
2015-01-23 15:16:05 -05:00
Mathieu Labbe 7a6bf630ca Moved some util3d methods to Transform.h, Graph.h and Compression.h
Added computePath() method implementating A star on graph
GUI: SetWindowModified() and user should explicitly save the GUI config to keep them
Calibration: added mirror checkbox, added device id argument
2015-01-23 11:17:42 -05:00
Mathieu Labbe fdf7f69783 Added rtabmap-camera tool. Added Export button in Calibration dialog to
save calibration to yaml file (same format as ROS).
2015-01-19 16:14:49 -05:00
Mathieu Labbe e7ae2c5e31 Added "Restaure defaults" buttons to ExportClouds and PostProcessing dialogs 2015-01-14 15:09:16 -05:00
Mathieu Labbe 199d9a948a Minor text change... 2015-01-14 12:11:49 -05:00
Mathieu Labbe 645d2c4973 Saving ExportCloudsDialog and PostProcessing parameters to config file. 2015-01-14 11:02:17 -05:00
Mathieu Labbe fb0b23ae06 saving CloudViewer settings (Camera,Trajectory,Grid) in the config file 2015-01-13 18:11:34 -05:00
Mathieu Labbe 6bd2a0926b Split RawDataKept parameter into Raw and Binary parameters to save RAM when keeping raw data in the core is not required 2015-01-12 18:23:25 -05:00
Mathieu Labbe cdb1e29d45 Fixed not working "Insert data in GUI cache" when disabled 2015-01-12 17:43:42 -05:00
Mathieu Labbe cd3bd2948e fixed checked/unchecked actions of menu "Select Source..." 2015-01-12 16:53:18 -05:00
Mathieu Labbe 74d6f9c83d Fixed not unchecked Auto-Screenshot when cancelled 2015-01-12 15:39:33 -05:00
81 changed files with 4619 additions and 2142 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
####################### #######################
SET(RTABMAP_MAJOR_VERSION 0) SET(RTABMAP_MAJOR_VERSION 0)
SET(RTABMAP_MINOR_VERSION 8) SET(RTABMAP_MINOR_VERSION 8)
SET(RTABMAP_PATCH_VERSION 2) SET(RTABMAP_PATCH_VERSION 3)
SET(RTABMAP_VERSION SET(RTABMAP_VERSION
${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION}) ${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION})
+14 -30
View File
@@ -43,16 +43,23 @@ int main(int argc, char* argv[])
ULogger::setType(ULogger::kTypeConsole); ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kInfo); ULogger::setLevel(ULogger::kInfo);
ULOGGER_INFO("Program started...");
/* Create tasks */ /* Create tasks */
QApplication * app = new QApplication(argc, argv); QApplication * app = new QApplication(argc, argv);
MainWindow * mainWindow = new MainWindow(); MainWindow * mainWindow = new MainWindow();
UINFO("Program started...");
UEventsManager::addHandler(mainWindow); UEventsManager::addHandler(mainWindow);
/* Start thread's task */ /* Start thread's task */
mainWindow->showNormal(); if(mainWindow->isSavedMaximized())
{
mainWindow->showMaximized();
}
else
{
mainWindow->show();
}
RtabmapThread * rtabmap = new RtabmapThread(new Rtabmap()); RtabmapThread * rtabmap = new RtabmapThread(new Rtabmap());
rtabmap->start(); // start it not initialized... will be initialized by event from the gui rtabmap->start(); // start it not initialized... will be initialized by event from the gui
@@ -67,37 +74,14 @@ int main(int argc, char* argv[])
UEventsManager::removeHandler(mainWindow); UEventsManager::removeHandler(mainWindow);
UEventsManager::removeHandler(rtabmap); UEventsManager::removeHandler(rtabmap);
ULOGGER_INFO("Killing threads..."); UINFO("Killing threads...");
rtabmap->join(true); rtabmap->join(true);
ULogger::setLevel(ULogger::kInfo); UINFO("Closing RTAB-Map...");
delete rtabmap;
ULOGGER_INFO("Closing RTAB-Map core...");
//Since we can't put the Rtabmap object in the MainWindow class,
//we pop up a message box indicating that the rtabmap object
// is being deleted (saving data to the database)
QMessageBox * msg = new QMessageBox(QMessageBox::Information,
QObject::tr("RTAB-Map is closing..."),
QObject::tr("The detector is saving the working memory to database (located in RTAB-Map's working directory)..."),
QMessageBox::NoButton,
mainWindow);
msg->setEnabled(false);
msg->setIconPixmap(QPixmap(":/images/RTAB-Map.ico"));
msg->setWindowIcon(QIcon(":/images/RTAB-Map.ico"));
msg->show();
UObjDeletionThread<RtabmapThread> delThread(rtabmap);
ObjDeletionHandler handler(delThread.id(), app, SLOT(quit()));
UEventsManager::addHandler(&handler);
delThread.startDeletion(1); // make sure that app-exec() is called before the deletion of the object
app->exec();
ULOGGER_INFO("Closing RTAB-Map gui...");
delete mainWindow; delete mainWindow;
delete app; delete app;
UINFO("All done!");
ULOGGER_INFO("All done!");
return 0; return 0;
} }
+6
View File
@@ -61,6 +61,10 @@ public:
void setImageRate(float imageRate) {_imageRate = imageRate;} void setImageRate(float imageRate) {_imageRate = imageRate;}
void setImageSize(unsigned int width, unsigned int height); void setImageSize(unsigned int width, unsigned int height);
void setCalibration(const std::string & fileName);
void setCalibration(const cv::Mat & cameraMatrix, const cv::Mat & distorsionCoefficients);
void resetCalibration();
protected: protected:
/** /**
* Constructor * Constructor
@@ -78,6 +82,8 @@ private:
unsigned int _imageWidth; unsigned int _imageWidth;
unsigned int _imageHeight; unsigned int _imageHeight;
UTimer * _frameRateTimer; UTimer * _frameRateTimer;
cv::Mat _k; // camera_matrix
cv::Mat _d; // distorsion_coefficients
}; };
@@ -0,0 +1,87 @@
/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef COMPRESSION_H_
#define COMPRESSION_H_
#include "rtabmap/core/RtabmapExp.h" // DLL export/import defines
#include <rtabmap/utilite/UThread.h>
#include <opencv2/opencv.hpp>
namespace rtabmap {
/**
* Compress image or data
*
* Example compression:
* cv::Mat image;// an image
* CompressionThread ct(image);
* ct.start();
* ct.join();
* std::vector<unsigned char> bytes = ct.getCompressedData();
*
* Example uncompression
* std::vector<unsigned char> bytes;// a compressed image
* CompressionThread ct(bytes);
* ct.start();
* ct.join();
* cv::Mat image = ct.getUncompressedData();
*/
class RTABMAP_EXP CompressionThread : public UThread
{
public:
// format : ".png" ".jpg" "" (empty is general)
CompressionThread(const cv::Mat & mat, const std::string & format = "");
CompressionThread(const cv::Mat & bytes, bool isImage);
const cv::Mat & getCompressedData() const {return compressedData_;}
cv::Mat & getUncompressedData() {return uncompressedData_;}
protected:
virtual void mainLoop();
private:
cv::Mat compressedData_;
cv::Mat uncompressedData_;
std::string format_;
bool image_;
bool compressMode_;
};
std::vector<unsigned char> RTABMAP_EXP compressImage(const cv::Mat & image, const std::string & format = ".png");
cv::Mat RTABMAP_EXP compressImage2(const cv::Mat & image, const std::string & format = ".png");
cv::Mat RTABMAP_EXP uncompressImage(const cv::Mat & bytes);
cv::Mat RTABMAP_EXP uncompressImage(const std::vector<unsigned char> & bytes);
std::vector<unsigned char> RTABMAP_EXP compressData(const cv::Mat & data);
cv::Mat RTABMAP_EXP compressData2(const cv::Mat & data);
cv::Mat RTABMAP_EXP uncompressData(const cv::Mat & bytes);
cv::Mat RTABMAP_EXP uncompressData(const std::vector<unsigned char> & bytes);
cv::Mat RTABMAP_EXP uncompressData(const unsigned char * bytes, unsigned long size);
} /* namespace rtabmap */
#endif /* COMPRESSION_H_ */
+4 -2
View File
@@ -72,6 +72,7 @@ public:
void asyncSave(VisualWord * vw); //ownership transferred void asyncSave(VisualWord * vw); //ownership transferred
void emptyTrashes(bool async = false); void emptyTrashes(bool async = false);
double getEmptyTrashesTime() const {return _emptyTrashesTime;} double getEmptyTrashesTime() const {return _emptyTrashesTime;}
void setTimestampUpdateEnabled(bool enabled) {_timestampUpdate = enabled;} // used on Update Signature and Word queries
public: public:
void addStatisticsAfterRun(int stMemSize, int lastSignAdded, int processMemUsed, int databaseMemUsed, int dictionarySize) const; void addStatisticsAfterRun(int stMemSize, int lastSignAdded, int processMemUsed, int databaseMemUsed, int dictionarySize) const;
@@ -121,8 +122,8 @@ private:
virtual void saveQuery(const std::list<Signature *> & signatures) const = 0; virtual void saveQuery(const std::list<Signature *> & signatures) const = 0;
virtual void saveQuery(const std::list<VisualWord *> & words) const = 0; virtual void saveQuery(const std::list<VisualWord *> & words) const = 0;
virtual void updateQuery(const std::list<Signature *> & signatures) const = 0; virtual void updateQuery(const std::list<Signature *> & signatures, bool updateTimestamp) const = 0;
virtual void updateQuery(const std::list<VisualWord *> & words) const = 0; virtual void updateQuery(const std::list<VisualWord *> & words, bool updateTimestamp) const = 0;
// Load objects // Load objects
@@ -157,6 +158,7 @@ private:
USemaphore _addSem; USemaphore _addSem;
double _emptyTrashesTime; double _emptyTrashesTime;
std::string _url; std::string _url;
bool _timestampUpdate;
}; };
} }
+125
View File
@@ -0,0 +1,125 @@
/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GRAPH_H_
#define GRAPH_H_
#include "rtabmap/core/RtabmapExp.h" // DLL export/import defines
#include <map>
#include <list>
#include <rtabmap/core/Link.h>
namespace rtabmap {
namespace graph {
std::multimap<int, Link>::iterator RTABMAP_EXP findLink(
std::multimap<int, Link> & links,
int from,
int to);
// <int, depth> depth=0 means infinite depth
std::map<int, int> RTABMAP_EXP generateDepthGraph(
const std::multimap<int, Link> & links,
int fromId,
int depth = 0);
void RTABMAP_EXP optimizeTOROGraph(
const std::map<int, int> & depthGraph,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & links,
std::map<int, Transform> & optimizedPoses,
int toroIterations = 100,
bool toroInitialGuess = true,
bool ignoreCovariance = false,
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
void RTABMAP_EXP optimizeTOROGraph(
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints,
std::map<int, Transform> & optimizedPoses,
int toroIterations = 100,
bool toroInitialGuess = true,
bool ignoreCovariance = false,
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
bool RTABMAP_EXP saveTOROGraph(
const std::string & fileName,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints);
bool RTABMAP_EXP loadTOROGraph(const std::string & fileName,
std::map<int, Transform> & poses,
std::multimap<int, std::pair<int, Transform> > & edgeConstraints);
std::map<int, Transform> RTABMAP_EXP radiusPosesFiltering(
const std::map<int, Transform> & poses,
float radius,
float angle,
bool keepLatest = true);
/**
* Get all neighbor nodes in a fixed radius around each pose.
* @param poses The poses
* @param radius Radius (m) of the search for near neighbors
* @param angle Maximum angle (rad, [0,PI]) of accepted neighbor nodes in the radius
* @return A map between each pose id and its neighbors found in the radius
*/
std::multimap<int, int> RTABMAP_EXP radiusPosesClustering(
const std::map<int, Transform> & poses,
float radius,
float angle);
/**
* Perform A* path planning in the graph.
* @param poses The graph's poses
* @param links The graph's links (from node id -> to node id)
* @param from initial node
* @param to final node
* @return the path ids from id "from" to id "to" including initial and final nodes.
*/
std::vector<int> RTABMAP_EXP computePath(
const std::map<int, rtabmap::Transform> & poses,
const std::multimap<int, int> & links,
int from,
int to);
int RTABMAP_EXP findNearestNode(
const std::map<int, rtabmap::Transform> & nodes,
const rtabmap::Transform & targetPose);
std::map<int, float> RTABMAP_EXP getNodesInRadius(
int nodeId,
const std::map<int, Transform> & nodes,
int maxNearestNeighbors,
float radius);
} /* namespace graph */
} /* namespace rtabmap */
#endif /* GRAPH_H_ */
+1 -1
View File
@@ -35,7 +35,7 @@ namespace rtabmap {
class Link class Link
{ {
public: public:
enum Type {kNeighbor, kGlobalClosure, kLocalSpaceClosure, kLocalTimeClosure, kUserClosure, kUndef}; enum Type {kNeighbor, kGlobalClosure, kLocalSpaceClosure, kLocalTimeClosure, kUserClosure, kVirtualClosure, kUndef};
Link() : Link() :
from_(0), from_(0),
to_(0), to_(0),
+8 -5
View File
@@ -80,8 +80,9 @@ public:
std::list<int> cleanup(const std::list<int> & ignoredIds = std::list<int>()); std::list<int> cleanup(const std::list<int> & ignoredIds = std::list<int>());
void emptyTrash(); void emptyTrash();
void joinTrashThread(); void joinTrashThread();
bool addLoopClosureLink(int oldId, int newId, const Transform & transform, Link::Type type, float variance); bool addLink(int to, int from, const Transform & transform, Link::Type type, float variance);
void updateNeighborLink(int fromId, int toId, const Transform & transform, float variance); void updateLink(int fromId, int toId, const Transform & transform, float variance);
void removeAllVirtualLinks();
std::map<int, int> getNeighborsId(int signatureId, std::map<int, int> getNeighborsId(int signatureId,
int margin, int margin,
int maxCheckedInDatabase = -1, int maxCheckedInDatabase = -1,
@@ -89,7 +90,7 @@ public:
bool ignoreLoopIds = false, bool ignoreLoopIds = false,
double * dbAccessTime = 0) const; double * dbAccessTime = 0) const;
void deleteLocation(int locationId, std::list<int> * deletedWords = 0); void deleteLocation(int locationId, std::list<int> * deletedWords = 0);
void rejectLoopClosure(int oldId, int newId); void removeLink(int idA, int idB);
//getters //getters
const std::set<int> & getWorkingMem() const {return _workingMem;} const std::set<int> & getWorkingMem() const {return _workingMem;}
@@ -103,6 +104,7 @@ public:
std::map<int, Link> getLoopClosureLinks(int signatureId, std::map<int, Link> getLoopClosureLinks(int signatureId,
bool lookInDatabase = false) const; bool lookInDatabase = false) const;
bool isRawDataKept() const {return _rawDataKept;} bool isRawDataKept() const {return _rawDataKept;}
bool isBinDataKept() const {return _binDataKept;}
float getSimilarityThreshold() const {return _similarityThreshold;} float getSimilarityThreshold() const {return _similarityThreshold;}
std::map<int, int> getWeights() const; std::map<int, int> getWeights() const;
int getLastSignatureId() const; int getLastSignatureId() const;
@@ -183,7 +185,6 @@ private:
void copyData(const Signature * from, Signature * to); void copyData(const Signature * from, Signature * to);
Signature * createSignature( Signature * createSignature(
const SensorData & data, const SensorData & data,
bool keepRawData=false,
Statistics * stats = 0); Statistics * stats = 0);
//keypoint stuff //keypoint stuff
@@ -199,6 +200,7 @@ private:
// parameters // parameters
float _similarityThreshold; float _similarityThreshold;
bool _rawDataKept; bool _rawDataKept;
bool _binDataKept;
bool _keepRehearsedNodesInDb; bool _keepRehearsedNodesInDb;
bool _incrementalMemory; bool _incrementalMemory;
int _maxStMemSize; int _maxStMemSize;
@@ -212,7 +214,8 @@ private:
Signature * _lastSignature; Signature * _lastSignature;
int _lastGlobalLoopClosureParentId; int _lastGlobalLoopClosureParentId;
int _lastGlobalLoopClosureChildId; int _lastGlobalLoopClosureChildId;
bool _memoryChanged; // False by default, become true when Memory::update() is called. bool _memoryChanged; // False by default, become true only when Memory::update() is called.
bool _linksChanged; // False by default, become true when links are modified.
int _signaturesAdded; int _signaturesAdded;
bool _postInitClosingEvents; bool _postInitClosingEvents;
+5 -2
View File
@@ -175,7 +175,7 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(Rtabmap, StatisticLogsBufferedInRAM, bool, true, "Statistic logs buffered in RAM instead of written to hard drive after each iteration."); RTABMAP_PARAM(Rtabmap, StatisticLogsBufferedInRAM, bool, true, "Statistic logs buffered in RAM instead of written to hard drive after each iteration.");
RTABMAP_PARAM(Rtabmap, StatisticLogged, bool, false, "Logging enabled."); RTABMAP_PARAM(Rtabmap, StatisticLogged, bool, false, "Logging enabled.");
RTABMAP_PARAM(Rtabmap, StatisticLoggedHeaders, bool, true, "Add column header description to log files."); RTABMAP_PARAM(Rtabmap, StatisticLoggedHeaders, bool, true, "Add column header description to log files.");
RTABMAP_PARAM(Rtabmap, StartNewMapOnLoopClosure, bool, false, "Start a new map only if there is a global loop closure with a previous map.") RTABMAP_PARAM(Rtabmap, StartNewMapOnLoopClosure, bool, false, "Start a new map only if there is a global loop closure with a previous map.");
// Hypotheses selection // Hypotheses selection
RTABMAP_PARAM(Rtabmap, LoopThr, float, 0.11, "Loop closing threshold."); RTABMAP_PARAM(Rtabmap, LoopThr, float, 0.11, "Loop closing threshold.");
@@ -183,7 +183,8 @@ class RTABMAP_EXP Parameters
// Memory // Memory
RTABMAP_PARAM(Mem, RehearsalSimilarity, float, 0.6, "Rehearsal similarity."); RTABMAP_PARAM(Mem, RehearsalSimilarity, float, 0.6, "Rehearsal similarity.");
RTABMAP_PARAM(Mem, ImageKept, bool, true, "Keep images in db."); RTABMAP_PARAM(Mem, ImageKept, bool, false, "Keep raw images in RAM.");
RTABMAP_PARAM(Mem, BinDataKept, bool, true, "Keep binary data in db.");
RTABMAP_PARAM(Mem, RehearsedNodesKept, bool, true, "Keep rehearsed ndoes in db."); RTABMAP_PARAM(Mem, RehearsedNodesKept, bool, true, "Keep rehearsed ndoes in db.");
RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size."); RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size.");
RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, othwersize it is Localization mode."); RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, othwersize it is Localization mode.");
@@ -285,6 +286,8 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(RGBD, ToroIterations, int, 100, "TORO graph optimization iterations"); RTABMAP_PARAM(RGBD, ToroIterations, int, 100, "TORO graph optimization iterations");
RTABMAP_PARAM(RGBD, ToroIgnoreVariance, bool, false, "Ignore constraints' variance. If checked, identity information matrix is used for each constraint in TORO. Otherwise, an information matrix is generated from the variance saved in the links."); RTABMAP_PARAM(RGBD, ToroIgnoreVariance, bool, false, "Ignore constraints' variance. If checked, identity information matrix is used for each constraint in TORO. Otherwise, an information matrix is generated from the variance saved in the links.");
RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, false, "Optimize graph from the newest node. If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest mode of the current graph, but it can be useful to preserve the map referential from the oldest node). Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation)."); RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, false, "Optimize graph from the newest node. If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest mode of the current graph, but it can be useful to preserve the map referential from the oldest node). Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation).");
RTABMAP_PARAM(RGBD, GoalReachedRadius, float, 1.0, "Goal reached radius (m).");
RTABMAP_PARAM(RGBD, MaxAnticipatedNodes, unsigned int, 10, "Maximum anticipated nodes on the computed path that can be retrieved (the number of nodes actually retrieved at each iteration is limited by \"Rtabmap/MaxRetrieved\").");
// Local loop closure detection // Local loop closure detection
RTABMAP_PARAM(RGBD, LocalLoopDetectionTime, bool, false, "Detection over all locations in STM."); RTABMAP_PARAM(RGBD, LocalLoopDetectionTime, bool, false, "Detection over all locations in STM.");
+28 -7
View File
@@ -71,10 +71,11 @@ public:
void close(); void close();
const std::string & getWorkingDir() const {return _wDir;} const std::string & getWorkingDir() const {return _wDir;}
int getLoopClosureId() const; int getLoopClosureId() const {return _loopClosureHypothesis.first;}
int getRetrievedId() const; float getLoopClosureValue() const {return _loopClosureHypothesis.second;}
int getHighestHypothesisId() const {return _highestHypothesis.first;}
float getHighestHypothesisValue() const {return _highestHypothesis.second;}
int getLastLocationId() const; int getLastLocationId() const;
float getLcHypValue() const {return _lcHypothesisValue;}
std::list<int> getWM() const; // working memory std::list<int> getWM() const; // working memory
std::set<int> getSTM() const; // short-term memory std::set<int> getSTM() const; // short-term memory
int getWMSize() const; // working memory size int getWMSize() const; // working memory size
@@ -90,6 +91,7 @@ public:
Transform getPose(int locationId) const; Transform getPose(int locationId) const;
Transform getMapCorrection() const {return _mapCorrection;} Transform getMapCorrection() const {return _mapCorrection;}
const Memory * getMemory() const {return _memory;} const Memory * getMemory() const {return _memory;}
float getGoalReachedRadius() const {return _goalReachedRadius;}
float getTimeThreshold() const {return _maxTimeAllowed;} // in ms float getTimeThreshold() const {return _maxTimeAllowed;} // in ms
void setTimeThreshold(float maxTimeAllowed); // in ms void setTimeThreshold(float maxTimeAllowed); // in ms
@@ -115,8 +117,17 @@ public:
std::map<int, int> & mapIds, std::map<int, int> & mapIds,
bool optimized, bool optimized,
bool global); bool global);
void clearPath();
std::list<std::pair<int, Transform> > computePath(int targetNode, bool global);
std::list<std::pair<int, Transform> > computePath(const Transform & targetPose, bool global);
const std::vector<int> & getPath() const {return _path;}
std::list<std::pair<int, Transform> > getPathNextPoses() const;
std::vector<int> getPathNextNodes() const;
int getPathCurrentGoalId() const;
const Transform & getPathTransformToGoal() const {return _pathTransformToGoal;}
std::map<int, Transform> getOptimizedWMPosesInRadius(int fromId, int maxNearestNeighbors, float radius, int maxDiffID, int & nearestId) const; std::map<int, float> getNodesInRadius(int fromId, int maxNearestNeighbors, float radius) const;
std::map<int, Transform> getWMPosesInRadius(int fromId, int maxNearestNeighbors, float radius, int maxDiffID, int & nearestId) const;
void adjustLikelihood(std::map<int, float> & likelihood) const; void adjustLikelihood(std::map<int, float> & likelihood) const;
std::pair<int, float> selectHypothesis(const std::map<int, float> & posterior, std::pair<int, float> selectHypothesis(const std::map<int, float> & posterior,
const std::map<int, float> & likelihood) const; const std::map<int, float> & likelihood) const;
@@ -126,6 +137,8 @@ private:
bool lookInDatabase, bool lookInDatabase,
std::map<int, Transform> & optimizedPoses, std::map<int, Transform> & optimizedPoses,
std::multimap<int, Link> * constraints = 0) const; std::multimap<int, Link> * constraints = 0) const;
void updateGoalIndex();
bool computePath(int targetNode, const std::map<int, Transform> & nodes, const std::multimap<int, rtabmap::Link> & constraints);
void setupLogFiles(bool overwrite = false); void setupLogFiles(bool overwrite = false);
void flushStatisticLogs(); void flushStatisticLogs();
@@ -166,10 +179,11 @@ private:
int _reextractFeatureType; int _reextractFeatureType;
int _reextractMaxWords; int _reextractMaxWords;
bool _startNewMapOnLoopClosure; bool _startNewMapOnLoopClosure;
float _goalReachedRadius; // meters
unsigned int _maxAnticipatedNodes;
int _lcHypothesisId; std::pair<int, float> _loopClosureHypothesis;
float _lcHypothesisValue; std::pair<int, float> _highestHypothesis;
int _retrievedId;
double _lastProcessTime; double _lastProcessTime;
// Abstract classes containing all loop closure // Abstract classes containing all loop closure
@@ -193,6 +207,13 @@ private:
std::multimap<int, Link> _constraints; std::multimap<int, Link> _constraints;
Transform _mapCorrection; Transform _mapCorrection;
Transform _mapTransform; // for localization mode Transform _mapTransform; // for localization mode
// Planning stuff
std::vector<int> _path;
unsigned int _pathCurrentIndex;
unsigned int _pathGoalIndex;
Transform _pathTransformToGoal;
}; };
#endif /* RTABMAP_H_ */ #endif /* RTABMAP_H_ */
+1
View File
@@ -89,6 +89,7 @@ public:
void removeLinks(); void removeLinks();
void removeLink(int idTo); void removeLink(int idTo);
void removeVirtualLinks();
void setSaved(bool saved) {_saved = saved;} void setSaved(bool saved) {_saved = saved;}
void setModified(bool modified) {_modified = modified; _linksModified = modified;} void setModified(bool modified) {_modified = modified; _linksModified = modified;}
@@ -140,6 +140,7 @@ public:
void setPosterior(const std::map<int, float> & posterior) {_posterior = posterior;} void setPosterior(const std::map<int, float> & posterior) {_posterior = posterior;}
void setLikelihood(const std::map<int, float> & likelihood) {_likelihood = likelihood;} void setLikelihood(const std::map<int, float> & likelihood) {_likelihood = likelihood;}
void setRawLikelihood(const std::map<int, float> & rawLikelihood) {_rawLikelihood = rawLikelihood;} void setRawLikelihood(const std::map<int, float> & rawLikelihood) {_rawLikelihood = rawLikelihood;}
void setLocalPath(const std::vector<int> & localPath) {_localPath=localPath;}
// getters // getters
bool extended() const {return _extended;} bool extended() const {return _extended;}
@@ -158,6 +159,7 @@ public:
const std::map<int, float> & posterior() const {return _posterior;} const std::map<int, float> & posterior() const {return _posterior;}
const std::map<int, float> & likelihood() const {return _likelihood;} const std::map<int, float> & likelihood() const {return _likelihood;}
const std::map<int, float> & rawLikelihood() const {return _rawLikelihood;} const std::map<int, float> & rawLikelihood() const {return _rawLikelihood;}
const std::vector<int> & localPath() const {return _localPath;}
const std::map<std::string, float> & data() const {return _data;} const std::map<std::string, float> & data() const {return _data;}
@@ -184,6 +186,8 @@ private:
std::map<int, float> _likelihood; std::map<int, float> _likelihood;
std::map<int, float> _rawLikelihood; std::map<int, float> _rawLikelihood;
std::vector<int> _localPath;
// Format for statistics (Plottable statistics must go in that map) : // Format for statistics (Plottable statistics must go in that map) :
// {"Group/Name/Unit", value} // {"Group/Name/Unit", value}
// Example : {"Timing/Total time/ms", 500.0f} // Example : {"Timing/Total time/ms", 500.0f}
+15 -4
View File
@@ -31,6 +31,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/core/RtabmapExp.h> #include <rtabmap/core/RtabmapExp.h>
#include <vector> #include <vector>
#include <string> #include <string>
#include <Eigen/Core>
#include <Eigen/Geometry>
namespace rtabmap { namespace rtabmap {
@@ -89,6 +91,8 @@ public:
void getTranslation(float & x, float & y, float & z) const; void getTranslation(float & x, float & y, float & z) const;
float getNorm() const; float getNorm() const;
float getNormSquared() const; float getNormSquared() const;
float getDistance(const Transform & t) const;
float getDistanceSquared(const Transform & t) const;
std::string prettyPrint() const; std::string prettyPrint() const;
Transform operator*(const Transform & t) const; Transform operator*(const Transform & t) const;
@@ -96,10 +100,17 @@ public:
bool operator==(const Transform & t) const; bool operator==(const Transform & t) const;
bool operator!=(const Transform & t) const; bool operator!=(const Transform & t) const;
static Transform getIdentity() Eigen::Matrix4f toEigen4f() const;
{ Eigen::Matrix4d toEigen4d() const;
return Transform(1,0,0,0, 0,1,0,0, 0,0,1,0); Eigen::Affine3f toEigen3f() const;
} Eigen::Affine3d toEigen3d() const;
public:
static Transform getIdentity();
static Transform fromEigen4f(const Eigen::Matrix4f & matrix);
static Transform fromEigen4d(const Eigen::Matrix4d & matrix);
static Transform fromEigen3f(const Eigen::Affine3f & matrix);
static Transform fromEigen3d(const Eigen::Affine3d & matrix);
private: private:
std::vector<float> data_; std::vector<float> data_;
+2 -2
View File
@@ -127,7 +127,7 @@ typename pcl::PointCloud<PointT>::Ptr transformPointCloud(
typedef typename pcl::PointCloud<PointT> PointCloud; typedef typename pcl::PointCloud<PointT> PointCloud;
typedef typename PointCloud::Ptr PointCloudPtr; typedef typename PointCloud::Ptr PointCloudPtr;
PointCloudPtr output(new PointCloud); PointCloudPtr output(new PointCloud);
pcl::transformPointCloud<PointT>(*cloud, *output, transformToEigen4f(transform)); pcl::transformPointCloud<PointT>(*cloud, *output, transform.toEigen4f());
return output; return output;
} }
@@ -136,7 +136,7 @@ PointT transformPoint(
const PointT & pt, const PointT & pt,
const Transform & transform) const Transform & transform)
{ {
return pcl::transformPoint(pt, transformToEigen3f(transform)); return pcl::transformPoint(pt, transform.toEigen3f());
} }
template<typename PointT> template<typename PointT>
-153
View File
@@ -49,41 +49,6 @@ namespace rtabmap
namespace util3d namespace util3d
{ {
/**
* Compress image or data
*
* Example compression:
* cv::Mat image;// an image
* CompressionThread ct(image);
* ct.start();
* ct.join();
* std::vector<unsigned char> bytes = ct.getCompressedData();
*
* Example uncompression
* std::vector<unsigned char> bytes;// a compressed image
* CompressionThread ct(bytes);
* ct.start();
* ct.join();
* cv::Mat image = ct.getUncompressedData();
*/
class RTABMAP_EXP CompressionThread : public UThread
{
public:
// format : ".png" ".jpg" "" (empty is general)
CompressionThread(const cv::Mat & mat, const std::string & format = "");
CompressionThread(const cv::Mat & bytes, bool isImage);
const cv::Mat & getCompressedData() const {return compressedData_;}
cv::Mat & getUncompressedData() {return uncompressedData_;}
protected:
virtual void mainLoop();
private:
cv::Mat compressedData_;
cv::Mat uncompressedData_;
std::string format_;
bool image_;
bool compressMode_;
};
cv::Mat RTABMAP_EXP rgbFromCloud(const pcl::PointCloud<pcl::PointXYZRGBA> & cloud, bool bgrOrder = true); cv::Mat RTABMAP_EXP rgbFromCloud(const pcl::PointCloud<pcl::PointXYZRGBA> & cloud, bool bgrOrder = true);
cv::Mat RTABMAP_EXP depthFromCloud( cv::Mat RTABMAP_EXP depthFromCloud(
const pcl::PointCloud<pcl::PointXYZRGBA> & cloud, const pcl::PointCloud<pcl::PointXYZRGBA> & cloud,
@@ -235,19 +200,6 @@ cv::Mat RTABMAP_EXP depthFromDisparity(const cv::Mat & disparity,
cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud); cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud);
pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP laserScanToPointCloud(const cv::Mat & laserScan); pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP laserScanToPointCloud(const cv::Mat & laserScan);
std::vector<unsigned char> RTABMAP_EXP compressImage(const cv::Mat & image, const std::string & format = ".png");
cv::Mat RTABMAP_EXP compressImage2(const cv::Mat & image, const std::string & format = ".png");
cv::Mat RTABMAP_EXP uncompressImage(const cv::Mat & bytes);
cv::Mat RTABMAP_EXP uncompressImage(const std::vector<unsigned char> & bytes);
std::vector<unsigned char> RTABMAP_EXP compressData(const cv::Mat & data);
cv::Mat RTABMAP_EXP compressData2(const cv::Mat & data);
cv::Mat RTABMAP_EXP uncompressData(const cv::Mat & bytes);
cv::Mat RTABMAP_EXP uncompressData(const std::vector<unsigned char> & bytes);
cv::Mat RTABMAP_EXP uncompressData(const unsigned char * bytes, unsigned long size);
// remove depth by z axis // remove depth by z axis
void RTABMAP_EXP extractXYZCorrespondences(const std::multimap<int, pcl::PointXYZ> & words1, void RTABMAP_EXP extractXYZCorrespondences(const std::multimap<int, pcl::PointXYZ> & words1,
const std::multimap<int, pcl::PointXYZ> & words2, const std::multimap<int, pcl::PointXYZ> & words2,
@@ -374,61 +326,6 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP getICPReadyCloud(
int samples, int samples,
const Transform & transform = Transform::getIdentity()); const Transform & transform = Transform::getIdentity());
inline Eigen::Matrix4f transformToEigen4f(const Transform & transform)
{
Eigen::Matrix4f m;
m << transform[0], transform[1], transform[2], transform[3],
transform[4], transform[5], transform[6], transform[7],
transform[8], transform[9], transform[10], transform[11],
0,0,0,1;
return m;
}
inline Eigen::Matrix4d transformToEigen4d(const Transform & transform)
{
Eigen::Matrix4d m;
m << transform[0], transform[1], transform[2], transform[3],
transform[4], transform[5], transform[6], transform[7],
transform[8], transform[9], transform[10], transform[11],
0,0,0,1;
return m;
}
inline Eigen::Affine3f transformToEigen3f(const Transform & transform)
{
return Eigen::Affine3f(transformToEigen4f(transform));
}
inline Eigen::Affine3d transformToEigen3d(const Transform & transform)
{
return Eigen::Affine3d(transformToEigen4d(transform));
}
inline Transform transformFromEigen4f(const Eigen::Matrix4f & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
inline Transform transformFromEigen4d(const Eigen::Matrix4d & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
inline Transform transformFromEigen3f(const Eigen::Affine3f & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
inline Transform transformFromEigen3d(const Eigen::Affine3d & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP concatenateClouds(const std::list<pcl::PointCloud<pcl::PointXYZ>::Ptr> & clouds); pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP concatenateClouds(const std::list<pcl::PointCloud<pcl::PointXYZ>::Ptr> & clouds);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr RTABMAP_EXP concatenateClouds(const std::list<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> & clouds); pcl::PointCloud<pcl::PointXYZRGB>::Ptr RTABMAP_EXP concatenateClouds(const std::list<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> & clouds);
pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP get3DFASTKpts( pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP get3DFASTKpts(
@@ -449,56 +346,6 @@ pcl::PolygonMesh::Ptr RTABMAP_EXP createMesh(
float gp3MaximumAngle = 2*M_PI/3, float gp3MaximumAngle = 2*M_PI/3,
bool gp3NormalConsistency = false); bool gp3NormalConsistency = false);
std::multimap<int, Link>::iterator RTABMAP_EXP findLink(
std::multimap<int, Link> & links,
int from,
int to);
// <int, depth> depth=0 means infinite depth
std::map<int, int> RTABMAP_EXP generateDepthGraph(
const std::multimap<int, Link> & links,
int fromId,
int depth = 0);
void RTABMAP_EXP optimizeTOROGraph(
const std::map<int, int> & depthGraph,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & links,
std::map<int, Transform> & optimizedPoses,
int toroIterations = 100,
bool toroInitialGuess = true,
bool ignoreCovariance = false,
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
void RTABMAP_EXP optimizeTOROGraph(
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints,
std::map<int, Transform> & optimizedPoses,
int toroIterations = 100,
bool toroInitialGuess = true,
bool ignoreCovariance = false,
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
bool RTABMAP_EXP saveTOROGraph(
const std::string & fileName,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints);
bool RTABMAP_EXP loadTOROGraph(const std::string & fileName,
std::map<int, Transform> & poses,
std::multimap<int, std::pair<int, Transform> > & edgeConstraints);
std::map<int, Transform> RTABMAP_EXP radiusPosesFiltering(
const std::map<int, Transform> & poses,
float radius,
float angle,
bool keepLatest = true);
std::multimap<int, int> RTABMAP_EXP radiusPosesClustering(
const std::map<int, Transform> & poses,
float radius,
float angle);
bool RTABMAP_EXP occupancy2DFromCloud3D( bool RTABMAP_EXP occupancy2DFromCloud3D(
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
cv::Mat & ground, cv::Mat & ground,
+1 -1
View File
@@ -75,7 +75,7 @@ void BayesFilter::setPredictionLC(const std::string & prediction)
bool valid = true; bool valid = true;
for(std::list<std::string>::iterator iter = strValues.begin(); iter!=strValues.end(); ++iter) for(std::list<std::string>::iterator iter = strValues.begin(); iter!=strValues.end(); ++iter)
{ {
tmpValues[i] = std::atof((*iter).c_str()); tmpValues[i] = uStr2Float((*iter).c_str());
//UINFO("%d=%e", i, tmpValues[i]); //UINFO("%d=%e", i, tmpValues[i]);
if(tmpValues[i] < 0.0 || tmpValues[i]>1.0) if(tmpValues[i] < 0.0 || tmpValues[i]>1.0)
{ {
+2
View File
@@ -27,6 +27,8 @@ SET(SRC_FILES
util3d.cpp util3d.cpp
Odometry.cpp Odometry.cpp
SensorData.cpp SensorData.cpp
Graph.cpp
Compression.cpp
toro3d/posegraph3.cpp toro3d/posegraph3.cpp
toro3d/treeoptimizer3_iteration.cpp toro3d/treeoptimizer3_iteration.cpp
+78
View File
@@ -74,6 +74,79 @@ void Camera::getImageSize(unsigned int & width, unsigned int & height)
height = _imageHeight; height = _imageHeight;
} }
void Camera::setCalibration(const std::string & fileName)
{
if(UFile::getExtension(fileName).compare("yaml") == 0)
{
cv::FileStorage fs;
fs.open(fileName, cv::FileStorage::READ);
if (!fs.isOpened())
{
UERROR("Failed to open file \"%s\"", fileName.c_str());
return;
}
cv::Mat k,d;
cv::FileNode n = fs["camera_matrix"];
int rows = n["rows"];
int cols = n["cols"];
std::vector<double> data;
n["data"] >> data;
if(rows > 0 && cols > 0 && (int)data.size() == rows*cols)
{
k = cv::Mat(rows, cols, CV_64FC1, data.data()).clone();
}
cv::FileNode nd = fs["distortion_coefficients"];
rows = nd["rows"];
cols = nd["cols"];
data.clear();
nd["data"] >> data;
if(rows > 0 && cols > 0 && (int)data.size() == rows*cols)
{
d = cv::Mat(rows, cols, CV_64FC1, data.data()).clone();
}
if(k.empty())
{
UERROR("Failed to load \"camera_matrix\" matrix.");
}
if(d.empty())
{
UERROR("Failed to load \"distortion_coefficients\" matrix.");
}
if(!k.empty() && !d.empty())
{
this->setCalibration(k, d);
}
}
else
{
UERROR("Calibration file must be in \"*.yaml\" format");
}
}
void Camera::setCalibration(const cv::Mat & cameraMatrix, const cv::Mat & distorsionCoefficients)
{
UASSERT(cameraMatrix.type() == CV_64FC1 &&
cameraMatrix.rows == 3 &&
cameraMatrix.cols == 3);
UASSERT(distorsionCoefficients.type() == CV_64FC1 &&
distorsionCoefficients.rows ==1 &&
(distorsionCoefficients.cols == 4 || distorsionCoefficients.cols == 5 || distorsionCoefficients.cols == 8));
_k = cameraMatrix;
_d = distorsionCoefficients;
}
void Camera::resetCalibration()
{
_k = cv::Mat();
_d = cv::Mat();
}
cv::Mat Camera::takeImage() cv::Mat Camera::takeImage()
{ {
cv::Mat img; cv::Mat img;
@@ -99,6 +172,11 @@ cv::Mat Camera::takeImage()
UTimer timer; UTimer timer;
img = this->captureImage(); img = this->captureImage();
if(!img.empty() && !_k.empty() && !_d.empty())
{
cv::Mat temp = img.clone();
cv::undistort(temp, img, _k, _d);
}
UDEBUG("Time capturing image = %fs", timer.ticks()); UDEBUG("Time capturing image = %fs", timer.ticks());
return img; return img;
} }
+247
View File
@@ -0,0 +1,247 @@
/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "rtabmap/core/Compression.h"
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UConversion.h>
#include <opencv2/opencv.hpp>
#include <zlib.h>
namespace rtabmap {
// format : ".png" ".jpg" "" (empty is general)
CompressionThread::CompressionThread(const cv::Mat & mat, const std::string & format) :
uncompressedData_(mat),
format_(format),
image_(!format.empty()),
compressMode_(true)
{
UASSERT(format.empty() || format.compare(".png") == 0 || format.compare(".jpg") == 0);
}
// assume image
CompressionThread::CompressionThread(const cv::Mat & bytes, bool isImage) :
compressedData_(bytes),
image_(isImage),
compressMode_(false)
{}
void CompressionThread::mainLoop()
{
if(compressMode_)
{
if(!uncompressedData_.empty())
{
if(image_)
{
compressedData_ = compressImage2(uncompressedData_, format_);
}
else
{
compressedData_ = compressData2(uncompressedData_);
}
}
}
else // uncompress
{
if(!compressedData_.empty())
{
if(image_)
{
uncompressedData_ = uncompressImage(compressedData_);
}
else
{
uncompressedData_ = uncompressData(compressedData_);
}
}
}
this->kill();
}
// ".png" or ".jpg"
std::vector<unsigned char> compressImage(const cv::Mat & image, const std::string & format)
{
std::vector<unsigned char> bytes;
if(!image.empty())
{
cv::imencode(format, image, bytes);
}
return bytes;
}
// ".png" or ".jpg"
cv::Mat compressImage2(const cv::Mat & image, const std::string & format)
{
std::vector<unsigned char> bytes = compressImage(image, format);
if(bytes.size())
{
return cv::Mat(1, bytes.size(), CV_8UC1, bytes.data()).clone();
}
return cv::Mat();
}
cv::Mat uncompressImage(const cv::Mat & bytes)
{
cv::Mat image;
if(!bytes.empty())
{
#if CV_MAJOR_VERSION>2 || (CV_MAJOR_VERSION >=2 && CV_MINOR_VERSION >=4)
image = cv::imdecode(bytes, cv::IMREAD_UNCHANGED);
#else
image = cv::imdecode(bytes, -1);
#endif
}
return image;
}
cv::Mat uncompressImage(const std::vector<unsigned char> & bytes)
{
cv::Mat image;
if(bytes.size())
{
#if CV_MAJOR_VERSION>2 || (CV_MAJOR_VERSION >=2 && CV_MINOR_VERSION >=4)
image = cv::imdecode(bytes, cv::IMREAD_UNCHANGED);
#else
image = cv::imdecode(bytes, -1);
#endif
}
return image;
}
std::vector<unsigned char> compressData(const cv::Mat & data)
{
std::vector<unsigned char> bytes;
if(!data.empty())
{
uLong sourceLen = uLong(data.total())*uLong(data.elemSize());
uLong destLen = compressBound(sourceLen);
bytes.resize(destLen);
int errCode = compress(
(Bytef *)bytes.data(),
&destLen,
(const Bytef *)data.data,
sourceLen);
bytes.resize(destLen+3*sizeof(int));
*((int*)&bytes[destLen]) = data.rows;
*((int*)&bytes[destLen+sizeof(int)]) = data.cols;
*((int*)&bytes[destLen+2*sizeof(int)]) = data.type();
if(errCode == Z_MEM_ERROR)
{
UERROR("Z_MEM_ERROR : Insufficient memory.");
}
else if(errCode == Z_BUF_ERROR)
{
UERROR("Z_BUF_ERROR : The buffer dest was not large enough to hold the uncompressed data.");
}
}
return bytes;
}
cv::Mat compressData2(const cv::Mat & data)
{
cv::Mat bytes;
if(!data.empty())
{
uLong sourceLen = uLong(data.total())*uLong(data.elemSize());
uLong destLen = compressBound(sourceLen);
bytes = cv::Mat(1, destLen+3*sizeof(int), CV_8UC1);
int errCode = compress(
(Bytef *)bytes.data,
&destLen,
(const Bytef *)data.data,
sourceLen);
bytes = cv::Mat(bytes, cv::Rect(0,0, destLen+3*sizeof(int), 1));
*((int*)&bytes.data[destLen]) = data.rows;
*((int*)&bytes.data[destLen+sizeof(int)]) = data.cols;
*((int*)&bytes.data[destLen+2*sizeof(int)]) = data.type();
if(errCode == Z_MEM_ERROR)
{
UERROR("Z_MEM_ERROR : Insufficient memory.");
}
else if(errCode == Z_BUF_ERROR)
{
UERROR("Z_BUF_ERROR : The buffer dest was not large enough to hold the uncompressed data.");
}
}
return bytes;
}
cv::Mat uncompressData(const cv::Mat & bytes)
{
UASSERT(bytes.empty() || bytes.type() == CV_8UC1);
return uncompressData(bytes.data, bytes.cols*bytes.rows);
}
cv::Mat uncompressData(const std::vector<unsigned char> & bytes)
{
return uncompressData(bytes.data(), bytes.size());
}
cv::Mat uncompressData(const unsigned char * bytes, unsigned long size)
{
cv::Mat data;
if(bytes && size>=3*sizeof(int))
{
//last 3 int elements are matrix size and type
int height = *((int*)&bytes[size-3*sizeof(int)]);
int width = *((int*)&bytes[size-2*sizeof(int)]);
int type = *((int*)&bytes[size-1*sizeof(int)]);
// If the size is higher, it may be a wrong data format.
UASSERT_MSG(height>=0 && height<10000 &&
width>=0 && width<10000,
uFormat("size=%d, height=%d width=%d type=%d", size, height, width, type).c_str());
data = cv::Mat(height, width, type);
uLongf totalUncompressed = uLongf(data.total())*uLongf(data.elemSize());
int errCode = uncompress(
(Bytef*)data.data,
&totalUncompressed,
(const Bytef*)bytes,
uLong(size));
if(errCode == Z_MEM_ERROR)
{
UERROR("Z_MEM_ERROR : Insufficient memory.");
}
else if(errCode == Z_BUF_ERROR)
{
UERROR("Z_BUF_ERROR : The buffer dest was not large enough to hold the uncompressed data.");
}
else if(errCode == Z_DATA_ERROR)
{
UERROR("Z_DATA_ERROR : The compressed data (referenced by source) was corrupted.");
}
}
return data;
}
} /* namespace rtabmap */
+4 -3
View File
@@ -38,7 +38,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace rtabmap { namespace rtabmap {
DBDriver::DBDriver(const ParametersMap & parameters) : DBDriver::DBDriver(const ParametersMap & parameters) :
_emptyTrashesTime(0) _emptyTrashesTime(0),
_timestampUpdate(true)
{ {
this->parseParameters(parameters); this->parseParameters(parameters);
} }
@@ -242,7 +243,7 @@ void DBDriver::saveOrUpdate(const std::vector<Signature *> & signatures) const
if(toUpdate.size()) if(toUpdate.size())
{ {
this->updateQuery(toUpdate); this->updateQuery(toUpdate, _timestampUpdate);
} }
if(toSave.size()) if(toSave.size())
{ {
@@ -272,7 +273,7 @@ void DBDriver::saveOrUpdate(const std::vector<VisualWord *> & words) const
if(toUpdate.size()) if(toUpdate.size())
{ {
this->updateQuery(toUpdate); this->updateQuery(toUpdate, _timestampUpdate);
} }
if(toSave.size()) if(toSave.size())
{ {
+23 -6
View File
@@ -340,7 +340,7 @@ bool DBDriverSqlite3::connectDatabaseQuery(const std::string & url, bool overwri
} }
if(rc != SQLITE_OK) if(rc != SQLITE_OK)
{ {
UFATAL("DB error : %s", sqlite3_errmsg(_ppDb)); UFATAL("DB error : %s (path=\"%s\")", sqlite3_errmsg(_ppDb), url.c_str());
_ppDb = 0; _ppDb = 0;
return false; return false;
} }
@@ -1548,7 +1548,7 @@ void DBDriverSqlite3::loadLinksQuery(std::list<Signature *> & signatures) const
} }
void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes) const void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes, bool updateTimestamp) const
{ {
UDEBUG("nodes = %d", nodes.size()); UDEBUG("nodes = %d", nodes.size());
if(_ppDb && nodes.size()) if(_ppDb && nodes.size())
@@ -1559,7 +1559,15 @@ void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes) const
sqlite3_stmt * ppStmt = 0; sqlite3_stmt * ppStmt = 0;
Signature * s = 0; Signature * s = 0;
std::string query = "UPDATE Node SET weight=?, time_enter = DATETIME('NOW') WHERE id=?;"; std::string query;
if(updateTimestamp)
{
query = "UPDATE Node SET weight=?, time_enter = DATETIME('NOW') WHERE id=?;";
}
else
{
query = "UPDATE Node SET weight=? WHERE id=?;";
}
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0); rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str()); UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error: %s", sqlite3_errmsg(_ppDb)).c_str());
@@ -1655,10 +1663,11 @@ void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes) const
} }
} }
void DBDriverSqlite3::updateQuery(const std::list<VisualWord *> & words) const void DBDriverSqlite3::updateQuery(const std::list<VisualWord *> & words, bool updateTimestamp) const
{ {
if(_ppDb && words.size()) if(_ppDb && words.size() && updateTimestamp)
{ {
// Only timestamp update is done here, so don't enter this if at all if false
UTimer timer; UTimer timer;
timer.start(); timer.start();
int rc = SQLITE_OK; int rc = SQLITE_OK;
@@ -2027,13 +2036,21 @@ std::string DBDriverSqlite3::queryStepLink() const
return "INSERT INTO Link(from_id, to_id, type, transform) VALUES(?,?,?,?);"; return "INSERT INTO Link(from_id, to_id, type, transform) VALUES(?,?,?,?);";
} }
} }
void DBDriverSqlite3::stepLink(sqlite3_stmt * ppStmt, int fromId, int toId, int type, float variance, const Transform & transform) const void DBDriverSqlite3::stepLink(sqlite3_stmt * ppStmt, int fromId, int toId, Link::Type type, float variance, const Transform & transform) const
{ {
if(!ppStmt) if(!ppStmt)
{ {
UFATAL(""); UFATAL("");
} }
UDEBUG("Save link from %d to %d, type=%d", fromId, toId, type); UDEBUG("Save link from %d to %d, type=%d", fromId, toId, type);
// Don't save virtual links
if(type==Link::kVirtualClosure)
{
UDEBUG("Virtual link ignored....");
return;
}
int rc = SQLITE_OK; int rc = SQLITE_OK;
int index = 1; int index = 1;
rc = sqlite3_bind_int(ppStmt, index++, fromId); rc = sqlite3_bind_int(ppStmt, index++, fromId);
+3 -3
View File
@@ -60,8 +60,8 @@ private:
virtual void saveQuery(const std::list<Signature *> & signatures) const; virtual void saveQuery(const std::list<Signature *> & signatures) const;
virtual void saveQuery(const std::list<VisualWord *> & words) const; virtual void saveQuery(const std::list<VisualWord *> & words) const;
virtual void updateQuery(const std::list<Signature *> & signatures) const; virtual void updateQuery(const std::list<Signature *> & signatures, bool updateTimestamp) const;
virtual void updateQuery(const std::list<VisualWord *> & words) const; virtual void updateQuery(const std::list<VisualWord *> & words, bool updateTimestamp) const;
// Load objects // Load objects
virtual void loadQuery(VWDictionary * dictionary) const; virtual void loadQuery(VWDictionary * dictionary) const;
@@ -109,7 +109,7 @@ private:
float cx, float cx,
float cy, float cy,
const Transform & localTransform) const; const Transform & localTransform) const;
void stepLink(sqlite3_stmt * ppStmt, int fromId, int toId, int type, float variance, const Transform & transform) const; void stepLink(sqlite3_stmt * ppStmt, int fromId, int toId, Link::Type type, float variance, const Transform & transform) const;
void stepWordsChanged(sqlite3_stmt * ppStmt, int signatureId, int oldWordId, int newWordId) const; void stepWordsChanged(sqlite3_stmt * ppStmt, int signatureId, int oldWordId, int newWordId) const;
void stepKeypoint(sqlite3_stmt * ppStmt, int signatureId, int wordId, const cv::KeyPoint & kp, const pcl::PointXYZ & pt) const; void stepKeypoint(sqlite3_stmt * ppStmt, int signatureId, int wordId, const cv::KeyPoint & kp, const pcl::PointXYZ & pt) const;
+4 -3
View File
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/CameraEvent.h" #include "rtabmap/core/CameraEvent.h"
#include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/OdometryEvent.h"
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/Compression.h"
namespace rtabmap { namespace rtabmap {
@@ -212,9 +213,9 @@ SensorData DBReader::getNextData()
UWARN("No image loaded from the database for id=%d!", *_currentId); UWARN("No image loaded from the database for id=%d!", *_currentId);
} }
util3d::CompressionThread ctImage(imageBytes, true); rtabmap::CompressionThread ctImage(imageBytes, true);
util3d::CompressionThread ctDepth(depthBytes, true); rtabmap::CompressionThread ctDepth(depthBytes, true);
util3d::CompressionThread ctLaserScan(laserScanBytes, false); rtabmap::CompressionThread ctLaserScan(laserScanBytes, false);
ctImage.start(); ctImage.start();
ctDepth.start(); ctDepth.start();
ctLaserScan.start(); ctLaserScan.start();
+1 -1
View File
@@ -251,7 +251,7 @@ cv::Rect Feature2D::computeRoi(const cv::Mat & image, const std::string & roiRat
unsigned int i=0; unsigned int i=0;
for(std::list<std::string>::iterator iter = strValues.begin(); iter!=strValues.end(); ++iter) for(std::list<std::string>::iterator iter = strValues.begin(); iter!=strValues.end(); ++iter)
{ {
values[i] = std::atof((*iter).c_str()); values[i] = uStr2Float(*iter);
++i; ++i;
} }
+839
View File
@@ -0,0 +1,839 @@
/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "rtabmap/core/Graph.h"
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/UMath.h>
#include <rtabmap/utilite/UConversion.h>
#include <pcl/search/kdtree.h>
#include <pcl/common/eigen.h>
#include <pcl/common/common.h>
#include <set>
#include <queue>
#include "toro3d/treeoptimizer3.hh"
namespace rtabmap {
namespace graph {
std::multimap<int, Link>::iterator findLink(
std::multimap<int, Link> & links,
int from,
int to)
{
std::multimap<int, Link>::iterator iter = links.find(from);
while(iter != links.end() && iter->first == from)
{
if(iter->second.to() == to)
{
return iter;
}
++iter;
}
// let's try to -> from
iter = links.find(to);
while(iter != links.end() && iter->first == to)
{
if(iter->second.to() == from)
{
return iter;
}
++iter;
}
return links.end();
}
// <int, depth> margin=0 means infinite margin
std::map<int, int> generateDepthGraph(
const std::multimap<int, Link> & links,
int fromId,
int depth)
{
UASSERT(depth >= 0);
//UDEBUG("signatureId=%d, neighborsMargin=%d", signatureId, margin);
std::map<int, int> ids;
if(fromId<=0)
{
return ids;
}
std::list<int> curentDepthList;
std::set<int> nextDepth;
nextDepth.insert(fromId);
int d = 0;
while((depth == 0 || d < depth) && nextDepth.size())
{
curentDepthList = std::list<int>(nextDepth.begin(), nextDepth.end());
nextDepth.clear();
for(std::list<int>::iterator jter = curentDepthList.begin(); jter!=curentDepthList.end(); ++jter)
{
if(ids.find(*jter) == ids.end())
{
std::set<int> marginIds;
ids.insert(std::pair<int, int>(*jter, d));
for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
{
if(iter->second.from() == *jter)
{
marginIds.insert(iter->second.to());
}
else if(iter->second.to() == *jter)
{
marginIds.insert(iter->second.from());
}
}
// Margin links
for(std::set<int>::const_iterator iter=marginIds.begin(); iter!=marginIds.end(); ++iter)
{
if( !uContains(ids, *iter) && nextDepth.find(*iter) == nextDepth.end())
{
nextDepth.insert(*iter);
}
}
}
}
++d;
}
return ids;
}
void optimizeTOROGraph(
const std::map<int, int> & depthGraph,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & links,
std::map<int, Transform> & optimizedPoses,
int toroIterations,
bool toroInitialGuess,
bool ignoreCovariance,
std::list<std::map<int, Transform> > * intermediateGraphes)
{
optimizedPoses.clear();
if(depthGraph.size() && poses.size()>=2 && links.size()>=1)
{
// Modify IDs using the margin from the current signature (TORO root will be the last signature)
int m = 0;
int toroId = 1;
std::map<int, int> rtabmapToToro; // <RTAB-Map ID, TORO ID>
std::map<int, int> toroToRtabmap; // <TORO ID, RTAB-Map ID>
std::map<int, int> idsTmp = depthGraph;
while(idsTmp.size())
{
for(std::map<int, int>::iterator iter = idsTmp.begin(); iter!=idsTmp.end();)
{
if(m == iter->second)
{
rtabmapToToro.insert(std::make_pair(iter->first, toroId));
toroToRtabmap.insert(std::make_pair(toroId, iter->first));
++toroId;
idsTmp.erase(iter++);
}
else
{
++iter;
}
}
++m;
}
std::map<int, rtabmap::Transform> posesToro;
std::multimap<int, rtabmap::Link> edgeConstraintsToro;
for(std::map<int, rtabmap::Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
if(uContains(depthGraph, iter->first))
{
UASSERT_MSG(!iter->second.isNull(), uFormat("Poses should not be null! Id=%d", iter->first).c_str());
posesToro.insert(std::make_pair(rtabmapToToro.at(iter->first), iter->second));
}
}
for(std::multimap<int, rtabmap::Link>::const_iterator iter = links.begin();
iter!=links.end();
++iter)
{
if(uContains(depthGraph, iter->second.from()) && uContains(depthGraph, iter->second.to()))
{
UASSERT(!iter->second.transform().isNull());
edgeConstraintsToro.insert(std::make_pair(rtabmapToToro.at(iter->first), Link(rtabmapToToro.at(iter->first), rtabmapToToro.at(iter->second.to()), iter->second.type(), iter->second.transform(), iter->second.variance())));
}
}
std::map<int, rtabmap::Transform> optimizedPosesToro;
if(posesToro.size() && edgeConstraintsToro.size())
{
std::list<std::map<int, rtabmap::Transform> > graphesToro;
// Optimize!
optimizeTOROGraph(
posesToro,
edgeConstraintsToro,
optimizedPosesToro,
toroIterations,
toroInitialGuess,
ignoreCovariance,
&graphesToro);
for(std::map<int, rtabmap::Transform>::iterator iter=optimizedPosesToro.begin(); iter!=optimizedPosesToro.end(); ++iter)
{
optimizedPoses.insert(std::make_pair(toroToRtabmap.at(iter->first), iter->second));
}
if(intermediateGraphes)
{
for(std::list<std::map<int, rtabmap::Transform> >::iterator iter = graphesToro.begin(); iter!=graphesToro.end(); ++iter)
{
std::map<int, rtabmap::Transform> tmp;
for(std::map<int, rtabmap::Transform>::iterator jter=iter->begin(); jter!=iter->end(); ++jter)
{
tmp.insert(std::make_pair(toroToRtabmap.at(jter->first), jter->second));
}
intermediateGraphes->push_back(tmp);
}
}
}
else
{
UERROR("No TORO poses and constraints!?");
}
}
else if(links.size() == 0 && poses.size() == 1)
{
optimizedPoses = poses;
}
else
{
UERROR("Wrong inputs! depthGraph=%d poses=%d links=%d",
(int)depthGraph.size(), (int)poses.size(), (int)links.size());
}
}
//On success, optimizedPoses is cleared and new poses are inserted in
void optimizeTOROGraph(
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints,
std::map<int, Transform> & optimizedPoses,
int toroIterations,
bool toroInitialGuess,
bool ignoreCovariance,
std::list<std::map<int, Transform> > * intermediateGraphes) // contains poses after tree init to last one before the end
{
UASSERT(toroIterations>0);
optimizedPoses.clear();
if(edgeConstraints.size()>=1 && poses.size()>=2)
{
// Apply TORO optimization
AISNavigation::TreeOptimizer3 pg;
pg.verboseLevel = 0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
float x,y,z, roll,pitch,yaw;
UASSERT(!iter->second.isNull());
pcl::getTranslationAndEulerAngles(iter->second.toEigen3f(), x,y,z, roll,pitch,yaw);
AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v = pg.addVertex(iter->first, p);
if (v)
{
v->transformation=AISNavigation::TreePoseGraph3::Transformation(p);
}
else
{
UERROR("cannot insert vertex %d!?", iter->first);
}
}
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
{
int id1 = iter->first;
int id2 = iter->second.to();
float x,y,z, roll,pitch,yaw;
UASSERT(!iter->second.transform().isNull());
pcl::getTranslationAndEulerAngles(iter->second.transform().toEigen3f(), x,y,z, roll,pitch,yaw);
AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
AISNavigation::TreePoseGraph3::InformationMatrix inf = DMatrix<double>::I(6);
if(!ignoreCovariance && iter->second.variance()>0)
{
inf[0][0] = 1.0f/iter->second.variance(); // x
inf[1][1] = 1.0f/iter->second.variance(); // y
inf[2][2] = 1.0f/iter->second.variance(); // z
inf[3][3] = 1.0f/iter->second.variance(); // roll
inf[4][4] = 1.0f/iter->second.variance(); // pitch
inf[5][5] = 1.0f/iter->second.variance(); // yaw
}
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v1=pg.vertex(id1);
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v2=pg.vertex(id2);
AISNavigation::TreePoseGraph3::Transformation t(p);
if (!pg.addEdge(v1, v2, t, inf))
{
UERROR("Map: Edge already exits between nodes %d and %d, skipping", id1, id2);
return;
}
}
pg.buildMST(pg.vertices.begin()->first); // pg.buildSimpleTree();
UDEBUG("Initial guess...");
if(toroInitialGuess)
{
pg.initializeOnTree(); // optional
}
pg.initializeTreeParameters();
UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
"TORO is not able to find the root of the graph!)");
pg.initializeOptimization();
UDEBUG("TORO iterate begin (iterations=%d)", toroIterations);
for (int i=0; i<toroIterations; i++)
{
if(intermediateGraphes && (toroInitialGuess || i>0))
{
std::map<int, Transform> tmpPoses;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v=pg.vertex(iter->first);
v->pose=v->transformation.toPoseType();
Transform newPose = Transform::fromEigen3f(pcl::getTransformation(v->pose.x(), v->pose.y(), v->pose.z(), v->pose.roll(), v->pose.pitch(), v->pose.yaw()));
tmpPoses.insert(std::pair<int, Transform>(iter->first, newPose));
}
intermediateGraphes->push_back(tmpPoses);
}
pg.iterate();
}
UDEBUG("TORO iterate end");
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v=pg.vertex(iter->first);
v->pose=v->transformation.toPoseType();
Transform newPose = Transform::fromEigen3f(pcl::getTransformation(v->pose.x(), v->pose.y(), v->pose.z(), v->pose.roll(), v->pose.pitch(), v->pose.yaw()));
optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
}
//Eigen::Matrix4f newPose = transformToEigen4f(optimizedPoses.at(poses.rbegin()->first));
//Eigen::Matrix4f oldPose = transformToEigen4f(poses.rbegin()->second);
//Eigen::Matrix4f poseCorrection = oldPose.inverse() * newPose; // transform from odom to correct odom
//Eigen::Matrix4f result = oldPose*poseCorrection*oldPose.inverse();
//mapCorrection = transformFromEigen4f(result);
}
else if(edgeConstraints.size() == 0 && poses.size() == 1)
{
optimizedPoses = poses;
}
else
{
UWARN("This method should be called at least with 1 pose!");
}
}
bool saveTOROGraph(
const std::string & fileName,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints)
{
FILE * file = 0;
#ifdef _MSC_VER
fopen_s(&file, fileName.c_str(), "w");
#else
file = fopen(fileName.c_str(), "w");
#endif
if(file)
{
// VERTEX3 id x y z phi theta psi
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
float x,y,z, yaw,pitch,roll;
pcl::getTranslationAndEulerAngles(iter->second.toEigen3f(), x,y,z, roll, pitch, yaw);
fprintf(file, "VERTEX3 %d %f %f %f %f %f %f\n",
iter->first,
x,
y,
z,
roll,
pitch,
yaw);
}
//EDGE3 observed_vertex_id observing_vertex_id x y z roll pitch yaw inf_11 inf_12 .. inf_16 inf_22 .. inf_66
for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
{
float x,y,z, yaw,pitch,roll;
pcl::getTranslationAndEulerAngles(iter->second.transform().toEigen3f(), x,y,z, roll, pitch, yaw);
fprintf(file, "EDGE3 %d %d %f %f %f %f %f %f %f 0 0 0 0 0 %f 0 0 0 0 %f 0 0 0 %f 0 0 %f 0 %f\n",
iter->first,
iter->second.to(),
x,
y,
z,
roll,
pitch,
yaw,
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance());
}
UINFO("Graph saved to %s", fileName.c_str());
fclose(file);
}
else
{
UERROR("Cannot save to file %s", fileName.c_str());
return false;
}
return true;
}
bool loadTOROGraph(const std::string & fileName,
std::map<int, Transform> & poses,
std::multimap<int, std::pair<int, Transform> > & edgeConstraints)
{
FILE * file = 0;
#ifdef _MSC_VER
fopen_s(&file, fileName.c_str(), "r");
#else
file = fopen(fileName.c_str(), "r");
#endif
if(file)
{
char line[200];
while ( fgets (line , 200 , file) != NULL )
{
std::vector<std::string> strList = uListToVector(uSplit(line, ' '));
if(strList.size() == 8)
{
//VERTEX3
int id = atoi(strList[1].c_str());
float x = uStr2Float(strList[2]);
float y = uStr2Float(strList[3]);
float z = uStr2Float(strList[4]);
float roll = uStr2Float(strList[5]);
float pitch = uStr2Float(strList[6]);
float yaw = uStr2Float(strList[7]);
Transform pose = Transform::fromEigen3f(pcl::getTransformation(x, y, z, roll, pitch, yaw));
std::map<int, Transform>::iterator iter = poses.find(id);
if(iter != poses.end())
{
iter->second = pose;
}
else
{
UFATAL("");
}
}
else if(strList.size() == 30)
{
//EDGE3
int idFrom = atoi(strList[1].c_str());
int idTo = atoi(strList[2].c_str());
float x = uStr2Float(strList[3]);
float y = uStr2Float(strList[4]);
float z = uStr2Float(strList[5]);
float roll = uStr2Float(strList[6]);
float pitch = uStr2Float(strList[7]);
float yaw = uStr2Float(strList[8]);
Transform transform = Transform::fromEigen3f(pcl::getTransformation(x, y, z, roll, pitch, yaw));
if(poses.find(idFrom) != poses.end() && poses.find(idTo) != poses.end())
{
std::pair<int, Transform> edge(idTo, transform);
edgeConstraints.insert(std::pair<int, std::pair<int, Transform> >(idFrom, edge));
}
else
{
UFATAL("");
}
}
else
{
UFATAL("Error parsing map file %s", fileName.c_str());
}
}
UINFO("Graph loaded from %s", fileName.c_str());
fclose(file);
}
else
{
UERROR("Cannot open file %s", fileName.c_str());
return false;
}
return true;
}
std::map<int, Transform> radiusPosesFiltering(const std::map<int, Transform> & poses, float radius, float angle, bool keepLatest)
{
if(poses.size() > 1 && radius > 0.0f && angle>0.0f)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(poses.size());
int i=0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
(*cloud)[i++] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
}
// radius filtering
std::vector<int> names = uKeys(poses);
std::vector<Transform> transforms = uValues(poses);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
tree->setInputCloud(cloud);
std::set<int> indicesChecked;
std::set<int> indicesKept;
for(unsigned int i=0; i<cloud->size(); ++i)
{
// ignore scans
if(indicesChecked.find(i) == indicesChecked.end())
{
std::vector<int> kIndices;
std::vector<float> kDistances;
tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
std::set<int> cloudIndices;
const Transform & currentT = transforms.at(i);
Eigen::Vector3f vA = currentT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
for(unsigned int j=0; j<kIndices.size(); ++j)
{
if(indicesChecked.find(kIndices[j]) == indicesChecked.end())
{
const Transform & checkT = transforms.at(kIndices[j]);
// same orientation?
Eigen::Vector3f vB = checkT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
if(a <= angle)
{
cloudIndices.insert(kIndices[j]);
}
}
}
if(keepLatest)
{
bool lastAdded = false;
for(std::set<int>::reverse_iterator iter = cloudIndices.rbegin(); iter!=cloudIndices.rend(); ++iter)
{
if(!lastAdded)
{
indicesKept.insert(*iter);
lastAdded = true;
}
indicesChecked.insert(*iter);
}
}
else
{
bool firstAdded = false;
for(std::set<int>::iterator iter = cloudIndices.begin(); iter!=cloudIndices.end(); ++iter)
{
if(!firstAdded)
{
indicesKept.insert(*iter);
firstAdded = true;
}
indicesChecked.insert(*iter);
}
}
}
}
//pcl::IndicesPtr indicesOut(new std::vector<int>);
//indicesOut->insert(indicesOut->end(), indicesKept.begin(), indicesKept.end());
UINFO("Cloud filtered In = %d, Out = %d", cloud->size(), indicesKept.size());
//pcl::io::savePCDFile("duplicateIn.pcd", *cloud);
//pcl::io::savePCDFile("duplicateOut.pcd", *cloud, *indicesOut);
std::map<int, Transform> keptPoses;
for(std::set<int>::iterator iter = indicesKept.begin(); iter!=indicesKept.end(); ++iter)
{
keptPoses.insert(std::make_pair(names.at(*iter), transforms.at(*iter)));
}
return keptPoses;
}
else
{
return poses;
}
}
std::multimap<int, int> radiusPosesClustering(const std::map<int, Transform> & poses, float radius, float angle)
{
std::multimap<int, int> clusters;
if(poses.size() > 1 && radius > 0.0f && angle>0.0f)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(poses.size());
int i=0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
(*cloud)[i++] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
}
// radius clustering (nearest neighbors)
std::vector<int> ids = uKeys(poses);
std::vector<Transform> transforms = uValues(poses);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
tree->setInputCloud(cloud);
for(unsigned int i=0; i<cloud->size(); ++i)
{
std::vector<int> kIndices;
std::vector<float> kDistances;
tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
std::set<int> cloudIndices;
const Transform & currentT = transforms.at(i);
Eigen::Vector3f vA = currentT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
for(unsigned int j=0; j<kIndices.size(); ++j)
{
if((int)i != kIndices[j])
{
const Transform & checkT = transforms.at(kIndices[j]);
// same orientation?
Eigen::Vector3f vB = checkT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
if(a <= angle)
{
clusters.insert(std::make_pair(ids[i], ids[kIndices[j]]));
}
}
}
}
}
return clusters;
}
class Node
{
public:
Node(int id, int fromId, const rtabmap::Transform & pose) :
id_(id),
costSoFar_(0.0f),
distToEnd_(0.0f),
fromId_(fromId),
closed_(false),
pose_(pose)
{}
int id() const {return id_;}
int fromId() const {return fromId_;}
bool isClosed() const {return closed_;}
bool isOpened() const {return !closed_;}
float costSoFar() const {return costSoFar_;} // Dijkstra cost
float distToEnd() const {return distToEnd_;} // Breath-first cost
float totalCost() const {return costSoFar_ + distToEnd_;} // A* cost
rtabmap::Transform pose() const {return pose_;}
float distFrom(const rtabmap::Transform & pose) const
{
return pose_.getDistance(pose);
}
void setClosed(bool closed) {closed_ = closed;}
void setFromId(int fromId) {fromId_ = fromId;}
void setCostSoFar(float costSoFar) {costSoFar_ = costSoFar;}
void setDistToEnd(float distToEnd) {distToEnd_ = distToEnd;}
private:
int id_;
float costSoFar_;
float distToEnd_;
int fromId_;
bool closed_;
rtabmap::Transform pose_;
};
typedef std::pair<int, float> Pair; // first is id, second is cost
struct Order
{
bool operator()(Pair const& a, Pair const& b) const
{
return a.second > b.second;
}
};
std::vector<int> computePath(
const std::map<int, rtabmap::Transform> & poses,
const std::multimap<int, int> & links,
int from,
int to)
{
std::list<int> path;
//A*
int startNode = from;
int endNode = to;
rtabmap::Transform endPose = poses.at(endNode);
std::map<int, Node> nodes;
nodes.insert(std::make_pair(startNode, Node(startNode, 0, poses.at(startNode))));
std::priority_queue<Pair, std::vector<Pair>, Order> pq;
pq.push(Pair(startNode, 0));
while(pq.size())
{
Node & currentNode = nodes.find(pq.top().first)->second;
pq.pop();
currentNode.setClosed(true);
if(currentNode.id() == endNode)
{
while(currentNode.id()!=startNode)
{
path.push_front(currentNode.id());
currentNode = nodes.find(currentNode.fromId())->second;
}
path.push_front(startNode);
break;
}
// lookup neighbors
for(std::multimap<int, int>::const_iterator iter = links.find(currentNode.id());
iter!=links.end() && iter->first == currentNode.id();
++iter)
{
std::map<int, Node>::iterator nodeIter = nodes.find(iter->second);
if(nodeIter == nodes.end())
{
std::map<int, rtabmap::Transform>::const_iterator poseIter = poses.find(iter->second);
UASSERT(poseIter != poses.end());
Node n(iter->second, currentNode.id(), poseIter->second);
n.setCostSoFar(currentNode.costSoFar() + currentNode.distFrom(poseIter->second));
n.setDistToEnd(n.distFrom(endPose));
nodes.insert(std::make_pair(iter->second, n));
pq.push(Pair(n.id(), n.totalCost()));
}
else if(nodeIter->second.isOpened())
{
float newCostSoFar = currentNode.costSoFar() + currentNode.distFrom(nodeIter->second.pose());
if(nodeIter->second.costSoFar() > newCostSoFar)
{
UWARN("newCostSoFar > previous cost (%f vs %f)", newCostSoFar, nodeIter->second.costSoFar());
}
}
}
}
return uListToVector(path);
}
int findNearestNode(
const std::map<int, rtabmap::Transform> & nodes,
const rtabmap::Transform & targetPose)
{
int id = 0;
if(nodes.size() && !targetPose.isNull())
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(nodes.size());
std::vector<int> ids(nodes.size());
int oi = 0;
for(std::map<int, Transform>::const_iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
{
(*cloud)[oi] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
ids[oi++] = iter->first;
}
std::map<int, float> foundNodes;
if(cloud->size())
{
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdTree(new pcl::search::KdTree<pcl::PointXYZ>);
kdTree->setInputCloud(cloud);
std::vector<int> ind;
std::vector<float> dist;
pcl::PointXYZ pt(targetPose.x(), targetPose.y(), targetPose.z());
kdTree->nearestKSearch(pt, 1, ind, dist);
if(ind.size() && dist.size() && ind[0] >= 0)
{
UDEBUG("Nearest node = %d: %f", ids[ind[0]], dist[0]);
id = ids[ind[0]];
}
}
}
return id;
}
// return <id, distance>, including query
std::map<int, float> getNodesInRadius(
int nodeId,
const std::map<int, Transform> & nodes,
int maxNearestNeighbors,
float radius)
{
UASSERT(uContains(nodes, nodeId));
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(nodes.size());
std::vector<int> ids(nodes.size());
int oi = 0;
for(std::map<int, Transform>::const_iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
{
(*cloud)[oi] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
ids[oi++] = iter->first;
}
Transform fromT = nodes.at(nodeId);
std::map<int, float> foundNodes;
if(cloud->size())
{
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdTree(new pcl::search::KdTree<pcl::PointXYZ>);
kdTree->setInputCloud(cloud);
std::vector<int> ind;
std::vector<float> dist;
pcl::PointXYZ pt(fromT.x(), fromT.y(), fromT.z());
kdTree->radiusSearch(pt, radius, ind, dist, maxNearestNeighbors);
for(unsigned int i=0; i<ind.size(); ++i)
{
if(ind[i] >=0)
{
UDEBUG("Inlier %d: %f", ids[ind[i]], dist[i]);
foundNodes.insert(std::make_pair(ids[ind[i]], dist[i]));
}
}
}
UDEBUG("found nodes=%d", (int)foundNodes.size());
return foundNodes;
}
} /* namespace graph */
} /* namespace rtabmap */
+122 -71
View File
@@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "DBDriverSqlite3.h" #include "DBDriverSqlite3.h"
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/Statistics.h" #include "rtabmap/core/Statistics.h"
#include "rtabmap/core/Compression.h"
#include <pcl/io/pcd_io.h> #include <pcl/io/pcd_io.h>
#include <pcl/common/common.h> #include <pcl/common/common.h>
@@ -57,6 +58,7 @@ Memory::Memory(const ParametersMap & parameters) :
_dbDriver(0), _dbDriver(0),
_similarityThreshold(Parameters::defaultMemRehearsalSimilarity()), _similarityThreshold(Parameters::defaultMemRehearsalSimilarity()),
_rawDataKept(Parameters::defaultMemImageKept()), _rawDataKept(Parameters::defaultMemImageKept()),
_binDataKept(Parameters::defaultMemBinDataKept()),
_keepRehearsedNodesInDb(Parameters::defaultMemRehearsedNodesKept()), _keepRehearsedNodesInDb(Parameters::defaultMemRehearsedNodesKept()),
_incrementalMemory(Parameters::defaultMemIncrementalMemory()), _incrementalMemory(Parameters::defaultMemIncrementalMemory()),
_maxStMemSize(Parameters::defaultMemSTMSize()), _maxStMemSize(Parameters::defaultMemSTMSize()),
@@ -70,6 +72,7 @@ Memory::Memory(const ParametersMap & parameters) :
_lastGlobalLoopClosureParentId(0), _lastGlobalLoopClosureParentId(0),
_lastGlobalLoopClosureChildId(0), _lastGlobalLoopClosureChildId(0),
_memoryChanged(false), _memoryChanged(false),
_linksChanged(false),
_signaturesAdded(0), _signaturesAdded(0),
_postInitClosingEvents(false), _postInitClosingEvents(false),
@@ -129,7 +132,7 @@ bool Memory::init(const std::string & dbUrl, bool dbOverwritten, const Parameter
if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Clearing memory...")); if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Clearing memory..."));
DBDriver * tmpDriver = 0; DBDriver * tmpDriver = 0;
if(!_memoryChanged) if(!_memoryChanged && !_linksChanged)
{ {
if(_dbDriver) if(_dbDriver)
{ {
@@ -137,6 +140,10 @@ bool Memory::init(const std::string & dbUrl, bool dbOverwritten, const Parameter
_dbDriver = 0; // HACK for the clear() below to think that there is no db _dbDriver = 0; // HACK for the clear() below to think that there is no db
} }
} }
else if(!_memoryChanged && _linksChanged)
{
_dbDriver->setTimestampUpdateEnabled(false); // update links only
}
this->clear(); this->clear();
if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Clearing memory, done!")); if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Clearing memory, done!"));
@@ -160,6 +167,7 @@ bool Memory::init(const std::string & dbUrl, bool dbOverwritten, const Parameter
bool success = true; bool success = true;
if(_dbDriver) if(_dbDriver)
{ {
_dbDriver->setTimestampUpdateEnabled(true); // make sure that timestamp update is enabled (may be disabled above)
success = false; success = false;
if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Connecting to database ") + dbUrl + "...")); if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Connecting to database ") + dbUrl + "..."));
if(_dbDriver->openConnection(dbUrl, dbOverwritten)) if(_dbDriver->openConnection(dbUrl, dbOverwritten))
@@ -310,7 +318,7 @@ Memory::~Memory()
{ {
if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(RtabmapEventInit::kClosing)); if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(RtabmapEventInit::kClosing));
UDEBUG(""); UDEBUG("");
if(!_memoryChanged) if(!_memoryChanged && !_linksChanged)
{ {
UDEBUG(""); UDEBUG("");
if(_dbDriver) if(_dbDriver)
@@ -329,6 +337,11 @@ Memory::~Memory()
{ {
UDEBUG(""); UDEBUG("");
if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Saving memory...")); if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Saving memory..."));
if(!_memoryChanged && _linksChanged && _dbDriver)
{
// don't update the time stamps!
_dbDriver->setTimestampUpdateEnabled(false);
}
this->clear(); this->clear();
if(_dbDriver) if(_dbDriver)
{ {
@@ -363,6 +376,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
ParametersMap::const_iterator iter; ParametersMap::const_iterator iter;
Parameters::parse(parameters, Parameters::kMemImageKept(), _rawDataKept); Parameters::parse(parameters, Parameters::kMemImageKept(), _rawDataKept);
Parameters::parse(parameters, Parameters::kMemBinDataKept(), _binDataKept);
Parameters::parse(parameters, Parameters::kMemRehearsedNodesKept(), _keepRehearsedNodesInDb); Parameters::parse(parameters, Parameters::kMemRehearsedNodesKept(), _keepRehearsedNodesInDb);
Parameters::parse(parameters, Parameters::kMemRehearsalIdUpdatedToNewOne(), _idUpdatedToNewOneRehearsal); Parameters::parse(parameters, Parameters::kMemRehearsalIdUpdatedToNewOne(), _idUpdatedToNewOneRehearsal);
Parameters::parse(parameters, Parameters::kMemGenerateIds(), _generateIds); Parameters::parse(parameters, Parameters::kMemGenerateIds(), _generateIds);
@@ -514,7 +528,7 @@ bool Memory::update(const SensorData & data, Statistics * stats)
//============================================================ //============================================================
// Create a signature with the image received. // Create a signature with the image received.
//============================================================ //============================================================
Signature * signature = this->createSignature(data, this->isRawDataKept(), stats); Signature * signature = this->createSignature(data, stats);
if (signature == 0) if (signature == 0)
{ {
UERROR("Failed to create a signature..."); UERROR("Failed to create a signature...");
@@ -597,7 +611,7 @@ void Memory::setRoi(const std::string & roi)
unsigned int i=0; unsigned int i=0;
for(std::list<std::string>::iterator iter = strValues.begin(); iter!=strValues.end(); ++iter) for(std::list<std::string>::iterator iter = strValues.begin(); iter!=strValues.end(); ++iter)
{ {
tmpValues[i] = std::atof((*iter).c_str()); tmpValues[i] = uStr2Float(*iter);
++i; ++i;
} }
@@ -809,18 +823,6 @@ std::map<int, int> Memory::getNeighborsId(int signatureId,
{ {
curentMarginList = std::list<int>(nextMargin.begin(), nextMargin.end()); curentMarginList = std::list<int>(nextMargin.begin(), nextMargin.end());
nextMargin.clear(); nextMargin.clear();
// first pass: count number of node in current margin in database
for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end();++jter)
{
if(!uContains(ids, *jter))
{
const Signature * s = this->getSignature(*jter);
if(!s)
{
++nbLoadedFromDb;
}
}
}
for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end(); ++jter) for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end(); ++jter)
{ {
@@ -839,6 +841,7 @@ std::map<int, int> Memory::getNeighborsId(int signatureId,
} }
else if(maxCheckedInDatabase == -1 || (maxCheckedInDatabase > 0 && _dbDriver && nbLoadedFromDb < maxCheckedInDatabase)) else if(maxCheckedInDatabase == -1 || (maxCheckedInDatabase > 0 && _dbDriver && nbLoadedFromDb < maxCheckedInDatabase))
{ {
++nbLoadedFromDb;
ids.insert(std::pair<int, int>(*jter, m)); ids.insert(std::pair<int, int>(*jter, m));
UTimer timer; UTimer timer;
@@ -869,12 +872,6 @@ std::map<int, int> Memory::getNeighborsId(int signatureId,
{ {
if(currentMargin.insert(iter->first).second) if(currentMargin.insert(iter->first).second)
{ {
const Signature * s = this->getSignature(iter->first);
if(!s)
{
// update db count because it's on current margin
++nbLoadedFromDb;
}
curentMarginList.push_back(iter->first); curentMarginList.push_back(iter->first);
} }
} }
@@ -960,11 +957,14 @@ void Memory::clear()
_workingMem.size(), _stMem.size(), _signatures.size()).c_str()); _workingMem.size(), _stMem.size(), _signatures.size()).c_str());
UDEBUG("Adding statistics after run..."); UDEBUG("Adding statistics after run...");
_dbDriver->addStatisticsAfterRun(memSize, if(_memoryChanged)
_lastSignature?_lastSignature->id():0, {
UProcessInfo::getMemoryUsage(), _dbDriver->addStatisticsAfterRun(memSize,
_dbDriver->getMemoryUsed(), _lastSignature?_lastSignature->id():0,
(int)_vwd->getVisualWords().size()); UProcessInfo::getMemoryUsage(),
_dbDriver->getMemoryUsed(),
(int)_vwd->getVisualWords().size());
}
} }
UDEBUG(""); UDEBUG("");
@@ -1008,6 +1008,7 @@ void Memory::clear()
_idCount = kIdStart; _idCount = kIdStart;
_idMapCount = kIdStart; _idMapCount = kIdStart;
_memoryChanged = false; _memoryChanged = false;
_linksChanged = false;
if(_dbDriver) if(_dbDriver)
{ {
@@ -1541,43 +1542,63 @@ void Memory::deleteLocation(int locationId, std::list<int> * deletedWords)
} }
} }
void Memory::rejectLoopClosure(int oldId, int newId) void Memory::removeLink(int oldId, int newId)
{ {
Signature * oldS = this->_getSignature(oldId); //this method assumes receiving oldId < newId, if not switch them
Signature * newS = this->_getSignature(newId); Signature * oldS = this->_getSignature(oldId<newId?oldId:newId);
Signature * newS = this->_getSignature(oldId<newId?newId:oldId);
if(oldS && newS) if(oldS && newS)
{ {
UDEBUG("removing loop closure from location %d", newS->id()); UINFO("removing link between location %d and %d", oldS->id(), newS->id());
oldS->removeLink(newS->id());
oldS->setWeight(oldS->getWeight()+1);
newS->removeLink(oldS->id()); if(oldS->hasLink(newS->id()) && newS->hasLink(oldS->id()))
newS->setWeight(newS->getWeight()>0?newS->getWeight()-1:0);
bool noChildrenAnymore = true;
for(std::map<int, Link>::const_iterator iter=newS->getLinks().begin(); iter!=newS->getLinks().end(); ++iter)
{ {
if(iter->second.type() > Link::kNeighbor && iter->first < newS->id()) Link::Type type = oldS->getLinks().at(newS->id()).type();
if(type == Link::kGlobalClosure && newS->getWeight() > 0)
{ {
noChildrenAnymore = false; // adjust the weight
break; oldS->setWeight(oldS->getWeight()+1);
newS->setWeight(newS->getWeight()>0?newS->getWeight()-1:0);
}
oldS->removeLink(newS->id());
newS->removeLink(oldS->id());
if(type!=Link::kVirtualClosure)
{
_linksChanged = true;
}
bool noChildrenAnymore = true;
for(std::map<int, Link>::const_iterator iter=newS->getLinks().begin(); iter!=newS->getLinks().end(); ++iter)
{
if(iter->second.type() > Link::kNeighbor && iter->first < newS->id())
{
noChildrenAnymore = false;
break;
}
}
if(noChildrenAnymore && newS->id() == _lastGlobalLoopClosureParentId)
{
_lastGlobalLoopClosureParentId = 0;
_lastGlobalLoopClosureChildId = 0;
} }
} }
if(noChildrenAnymore && newId == _lastGlobalLoopClosureParentId) else
{ {
_lastGlobalLoopClosureParentId = 0; UERROR("Signatures %d and %d don't have bidirectional link!", oldS->id(), newS->id());
_lastGlobalLoopClosureChildId = 0;
} }
} }
else else
{ {
if(!newS) if(!newS)
{ {
UERROR("Signature %d is not in working memory... cannot remove loop closure links.", newS->id()); UERROR("Signature %d is not in working memory... cannot remove link.", newS->id());
} }
if(!oldS) if(!oldS)
{ {
UERROR("Signature %d is not in working memory... cannot remove loop closure links.", oldS->id()); UERROR("Signature %d is not in working memory... cannot remove link.", oldS->id());
} }
} }
} }
@@ -1656,7 +1677,7 @@ Transform Memory::computeVisualTransform(
UDEBUG("Forcing 2D..."); UDEBUG("Forcing 2D...");
float x,y,z,r,p,yaw; float x,y,z,r,p,yaw;
transform.getTranslationAndEulerAngles(x,y,z, r,p,yaw); transform.getTranslationAndEulerAngles(x,y,z, r,p,yaw);
transform = util3d::transformFromEigen3f(pcl::getTransformation(x,y,0, 0, 0, yaw)); transform = Transform::fromEigen3f(pcl::getTransformation(x,y,0, 0, 0, yaw));
} }
} }
else if(inliersCount < _bowMinInliers) else if(inliersCount < _bowMinInliers)
@@ -1921,7 +1942,7 @@ Transform Memory::computeIcpTransform(
// We are 2D here, make sure the guess has only YAW rotation // We are 2D here, make sure the guess has only YAW rotation
float x,y,z,r,p,yaw; float x,y,z,r,p,yaw;
guess.getTranslationAndEulerAngles(x,y,z, r,p,yaw); guess.getTranslationAndEulerAngles(x,y,z, r,p,yaw);
guess = util3d::transformFromEigen3f(pcl::getTransformation(x,y,0, 0, 0, yaw)); guess = Transform::fromEigen3f(pcl::getTransformation(x,y,0, 0, 0, yaw));
if(r!=0 || p!=0) if(r!=0 || p!=0)
{ {
UINFO("2D ICP: Dropping z (%f), roll (%f) and pitch (%f) rotation!", z, r, p); UINFO("2D ICP: Dropping z (%f), roll (%f) and pitch (%f) rotation!", z, r, p);
@@ -2038,7 +2059,7 @@ Transform Memory::computeScanMatchingTransform(
const Signature * s = this->getSignature(iter->first); const Signature * s = this->getSignature(iter->first);
if(!s->getLaserScanCompressed().empty()) if(!s->getLaserScanCompressed().empty())
{ {
*assembledOldClouds += *util3d::cvMat2Cloud(util3d::uncompressData(s->getLaserScanCompressed()), iter->second); *assembledOldClouds += *util3d::cvMat2Cloud(rtabmap::uncompressData(s->getLaserScanCompressed()), iter->second);
} }
else else
{ {
@@ -2057,7 +2078,7 @@ Transform Memory::computeScanMatchingTransform(
const Signature * newS = getSignature(newId); const Signature * newS = getSignature(newId);
pcl::PointCloud<pcl::PointXYZ>::Ptr newCloud; pcl::PointCloud<pcl::PointXYZ>::Ptr newCloud;
UASSERT(uContains(poses, newId)); UASSERT(uContains(poses, newId));
newCloud = util3d::cvMat2Cloud(util3d::uncompressData(newS->getLaserScanCompressed()), poses.at(newId)); newCloud = util3d::cvMat2Cloud(rtabmap::uncompressData(newS->getLaserScanCompressed()), poses.at(newId));
//voxelize //voxelize
if(newCloud->size() && _icp2VoxelSize > 0.0f) if(newCloud->size() && _icp2VoxelSize > 0.0f)
@@ -2132,7 +2153,7 @@ Transform Memory::computeScanMatchingTransform(
} }
// Transform from new to old // Transform from new to old
bool Memory::addLoopClosureLink(int oldId, int newId, const Transform & transform, Link::Type type, float variance) bool Memory::addLink(int oldId, int newId, const Transform & transform, Link::Type type, float variance)
{ {
UASSERT(type > Link::kNeighbor && type != Link::kUndef); UASSERT(type > Link::kNeighbor && type != Link::kUndef);
@@ -2148,20 +2169,32 @@ bool Memory::addLoopClosureLink(int oldId, int newId, const Transform & transfor
return true; return true;
} }
_memoryChanged = true; UDEBUG("Add link between %d and %d", oldS->id(), newS->id());
UDEBUG("Add loop closure link between %d and %d", oldS->id(), newS->id());
oldS->addLink(Link(oldS->id(), newS->id(), type, transform.inverse(), variance)); oldS->addLink(Link(oldS->id(), newS->id(), type, transform.inverse(), variance));
newS->addLink(Link(newS->id(), oldS->id(), type, transform, variance)); newS->addLink(Link(newS->id(), oldS->id(), type, transform, variance));
if(type!=Link::kVirtualClosure)
{
_linksChanged = true;
}
if(_incrementalMemory && type == Link::kGlobalClosure) if(_incrementalMemory && type == Link::kGlobalClosure)
{ {
_lastGlobalLoopClosureParentId = newS->id(); _lastGlobalLoopClosureParentId = newS->id()>oldS->id()?newS->id():oldS->id();
_lastGlobalLoopClosureChildId = oldS->id(); _lastGlobalLoopClosureChildId = newS->id()>oldS->id()?oldS->id():newS->id();
// udpate weights only if the memory is incremental // udpate weights only if the memory is incremental
newS->setWeight(newS->getWeight() + oldS->getWeight()); if(newS->id() > oldS->id())
oldS->setWeight(0); {
newS->setWeight(newS->getWeight() + oldS->getWeight());
oldS->setWeight(0);
}
else
{
oldS->setWeight(oldS->getWeight() + newS->getWeight());
newS->setWeight(0);
}
} }
return true; return true;
} }
@@ -2179,22 +2212,37 @@ bool Memory::addLoopClosureLink(int oldId, int newId, const Transform & transfor
return false; return false;
} }
void Memory::updateNeighborLink(int fromId, int toId, const Transform & transform, float variance) void Memory::updateLink(int fromId, int toId, const Transform & transform, float variance)
{ {
Signature * fromS = this->_getSignature(fromId); Signature * fromS = this->_getSignature(fromId);
Signature * toS = this->_getSignature(toId); Signature * toS = this->_getSignature(toId);
if(fromS->hasLink(toId) && toS->hasLink(fromId)) if(fromS->hasLink(toId) && toS->hasLink(fromId))
{ {
Link::Type type = fromS->getLinks().at(toId).type();
fromS->removeLink(toId); fromS->removeLink(toId);
toS->removeLink(fromId); toS->removeLink(fromId);
fromS->addLink(Link(fromId, toId, Link::kNeighbor, transform, variance)); fromS->addLink(Link(fromId, toId, type, transform, variance));
toS->addLink(Link(toId, fromId, Link::kNeighbor, transform.inverse(), variance)); toS->addLink(Link(toId, fromId, type, transform.inverse(), variance));
if(type!=Link::kVirtualClosure)
{
_linksChanged = true;
}
} }
else else
{ {
UERROR("fromId=%d and toId=%d are not neighbors!", fromId, toId); UERROR("fromId=%d and toId=%d are not linked!", fromId, toId);
}
}
void Memory::removeAllVirtualLinks()
{
UDEBUG("");
for(std::map<int, Signature*>::iterator iter=_signatures.begin(); iter!=_signatures.end(); ++iter)
{
iter->second->removeVirtualLinks();
} }
} }
@@ -2484,7 +2532,7 @@ cv::Mat Memory::getImageCompressed(int signatureId) const
{ {
image = s->getImageCompressed(); image = s->getImageCompressed();
} }
if(image.empty() && this->isRawDataKept() && _dbDriver) if(image.empty() && this->isBinDataKept() && _dbDriver)
{ {
_dbDriver->getNodeData(signatureId, image); _dbDriver->getNodeData(signatureId, image);
} }
@@ -2877,7 +2925,7 @@ private:
VWDictionary * _vwp; VWDictionary * _vwp;
}; };
Signature * Memory::createSignature(const SensorData & data, bool keepRawData, Statistics * stats) Signature * Memory::createSignature(const SensorData & data, Statistics * stats)
{ {
UASSERT(data.image().empty() || data.image().type() == CV_8UC1 || data.image().type() == CV_8UC3); UASSERT(data.image().empty() || data.image().type() == CV_8UC1 || data.image().type() == CV_8UC3);
UASSERT(data.depth().empty() || data.depth().type() == CV_16UC1 || data.depth().type() == CV_32FC1); UASSERT(data.depth().empty() || data.depth().type() == CV_16UC1 || data.depth().type() == CV_32FC1);
@@ -3283,7 +3331,7 @@ Signature * Memory::createSignature(const SensorData & data, bool keepRawData, S
} }
Signature * s; Signature * s;
if(keepRawData) if(this->isBinDataKept())
{ {
std::vector<unsigned char> imageBytes; std::vector<unsigned char> imageBytes;
std::vector<unsigned char> depthBytes; std::vector<unsigned char> depthBytes;
@@ -3300,9 +3348,9 @@ Signature * Memory::createSignature(const SensorData & data, bool keepRawData, S
{ {
depthOrRightImage = data.rightImage(); depthOrRightImage = data.rightImage();
} }
util3d::CompressionThread ctImage(data.image(), std::string(".jpg")); rtabmap::CompressionThread ctImage(data.image(), std::string(".jpg"));
util3d::CompressionThread ctDepth(depthOrRightImage, std::string(".png")); rtabmap::CompressionThread ctDepth(depthOrRightImage, std::string(".png"));
util3d::CompressionThread ctDepth2d(data.laserScan()); rtabmap::CompressionThread ctDepth2d(data.laserScan());
ctImage.start(); ctImage.start();
ctDepth.start(); ctDepth.start();
ctDepth2d.start(); ctDepth2d.start();
@@ -3323,9 +3371,6 @@ Signature * Memory::createSignature(const SensorData & data, bool keepRawData, S
data.cx(), data.cx(),
data.cy(), data.cy(),
data.localTransform()); data.localTransform());
s->setImageRaw(data.image());
s->setDepthRaw(depthOrRightImage);
s->setLaserScanRaw(data.laserScan());
} }
else else
{ {
@@ -3334,7 +3379,13 @@ Signature * Memory::createSignature(const SensorData & data, bool keepRawData, S
words, words,
words3D, words3D,
data.pose(), data.pose(),
util3d::compressData2(data.laserScan())); rtabmap::compressData2(data.laserScan()));
}
if(this->isRawDataKept())
{
s->setImageRaw(data.image());
s->setDepthRaw(data.depth());
s->setLaserScanRaw(data.laserScan());
} }
+1 -1
View File
@@ -158,7 +158,7 @@ OdometryBOW::OdometryBOW(const ParametersMap & parameters) :
customParameters.insert(ParametersPair(Parameters::kKpMaxDepth(), uNumber2Str(this->getMaxDepth()))); customParameters.insert(ParametersPair(Parameters::kKpMaxDepth(), uNumber2Str(this->getMaxDepth())));
customParameters.insert(ParametersPair(Parameters::kKpRoiRatios(), this->getRoiRatios())); customParameters.insert(ParametersPair(Parameters::kKpRoiRatios(), this->getRoiRatios()));
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
customParameters.insert(ParametersPair(Parameters::kMemImageKept(), "false")); customParameters.insert(ParametersPair(Parameters::kMemBinDataKept(), "false"));
customParameters.insert(ParametersPair(Parameters::kMemSTMSize(), "0")); customParameters.insert(ParametersPair(Parameters::kMemSTMSize(), "0"));
int nn = Parameters::defaultOdomBowNNType(); int nn = Parameters::defaultOdomBowNNType();
float nndr = Parameters::defaultOdomBowNNDR(); float nndr = Parameters::defaultOdomBowNNDR();
+3 -2
View File
@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/utilite/UConversion.h> #include <rtabmap/utilite/UConversion.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <sstream>
namespace rtabmap namespace rtabmap
{ {
@@ -112,7 +113,7 @@ void Parameters::parse(const ParametersMap & parameters, const std::string & key
ParametersMap::const_iterator iter = parameters.find(key); ParametersMap::const_iterator iter = parameters.find(key);
if(iter != parameters.end()) if(iter != parameters.end())
{ {
value = atof(iter->second.c_str()); value = uStr2Float(iter->second);
} }
} }
void Parameters::parse(const ParametersMap & parameters, const std::string & key, double & value) void Parameters::parse(const ParametersMap & parameters, const std::string & key, double & value)
@@ -120,7 +121,7 @@ void Parameters::parse(const ParametersMap & parameters, const std::string & key
ParametersMap::const_iterator iter = parameters.find(key); ParametersMap::const_iterator iter = parameters.find(key);
if(iter != parameters.end()) if(iter != parameters.end())
{ {
value = atof(iter->second.c_str()); value = uStr2Double(iter->second);
} }
} }
void Parameters::parse(const ParametersMap & parameters, const std::string & key, std::string & value) void Parameters::parse(const ParametersMap & parameters, const std::string & key, std::string & value)
+481 -145
View File
File diff suppressed because it is too large Load Diff
+19 -4
View File
@@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/Signature.h" #include "rtabmap/core/Signature.h"
#include "rtabmap/core/EpipolarGeometry.h" #include "rtabmap/core/EpipolarGeometry.h"
#include "rtabmap/core/Memory.h" #include "rtabmap/core/Memory.h"
#include "rtabmap/core/util3d.h" #include "rtabmap/core/Compression.h"
#include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui.hpp>
#include <rtabmap/utilite/UtiLite.h> #include <rtabmap/utilite/UtiLite.h>
@@ -149,6 +149,21 @@ void Signature::removeLink(int idTo)
} }
} }
void Signature::removeVirtualLinks()
{
for(std::map<int, Link>::iterator iter=_links.begin(); iter!=_links.end();)
{
if(iter->second.type() == Link::kVirtualClosure)
{
_links.erase(iter++);
}
else
{
++iter;
}
}
}
float Signature::compareTo(const Signature & s) const float Signature::compareTo(const Signature & s) const
{ {
float similarity = 0.0f; float similarity = 0.0f;
@@ -283,9 +298,9 @@ void Signature::uncompressDataConst(cv::Mat * imageRaw, cv::Mat * depthRaw, cv::
(depthRaw && depthRaw->empty()) || (depthRaw && depthRaw->empty()) ||
(laserScanRaw && laserScanRaw->empty())) (laserScanRaw && laserScanRaw->empty()))
{ {
util3d::CompressionThread ctImage(_imageCompressed, true); rtabmap::CompressionThread ctImage(_imageCompressed, true);
util3d::CompressionThread ctDepth(_depthCompressed, true); rtabmap::CompressionThread ctDepth(_depthCompressed, true);
util3d::CompressionThread ctLaserScan(_laserScanCompressed, false); rtabmap::CompressionThread ctLaserScan(_laserScanCompressed, false);
if(imageRaw && imageRaw->empty()) if(imageRaw && imageRaw->empty())
{ {
ctImage.start(); ctImage.start();
+75 -9
View File
@@ -74,7 +74,7 @@ Transform::Transform(float r11, float r12, float r13, float o14,
Transform::Transform(float x, float y, float z, float roll, float pitch, float yaw) Transform::Transform(float x, float y, float z, float roll, float pitch, float yaw)
{ {
Eigen::Affine3f t = pcl::getTransformation (x, y, z, roll, pitch, yaw); Eigen::Affine3f t = pcl::getTransformation (x, y, z, roll, pitch, yaw);
*this = util3d::transformFromEigen3f(t); *this = fromEigen3f(t);
} }
bool Transform::isNull() const bool Transform::isNull() const
@@ -133,8 +133,7 @@ void Transform::setIdentity()
Transform Transform::inverse() const Transform Transform::inverse() const
{ {
Eigen::Matrix4f m = util3d::transformToEigen4f(*this); return fromEigen4f(toEigen4f().inverse());
return util3d::transformFromEigen4f(m.inverse());
} }
Transform Transform::rotation() const Transform Transform::rotation() const
@@ -153,7 +152,7 @@ Transform Transform::translation() const
void Transform::getTranslationAndEulerAngles(float & x, float & y, float & z, float & roll, float & pitch, float & yaw) const void Transform::getTranslationAndEulerAngles(float & x, float & y, float & z, float & roll, float & pitch, float & yaw) const
{ {
pcl::getTranslationAndEulerAngles(util3d::transformToEigen3f(*this), x, y, z, roll, pitch, yaw); pcl::getTranslationAndEulerAngles(toEigen3f(), x, y, z, roll, pitch, yaw);
} }
void Transform::getTranslation(float & x, float & y, float & z) const void Transform::getTranslation(float & x, float & y, float & z) const
@@ -165,12 +164,22 @@ void Transform::getTranslation(float & x, float & y, float & z) const
float Transform::getNorm() const float Transform::getNorm() const
{ {
return std::sqrt(this->getNormSquared()); return uNorm(this->x(), this->y(), this->z());
} }
float Transform::getNormSquared() const float Transform::getNormSquared() const
{ {
return this->x()*this->x() + this->y()*this->y() + this->z()*this->z(); return uNormSquared(this->x(), this->y(), this->z());
}
float Transform::getDistance(const Transform & t) const
{
return uNorm(this->x()-t.x(), this->y()-t.y(), this->z()-t.z());
}
float Transform::getDistanceSquared(const Transform & t) const
{
return uNormSquared(this->x()-t.x(), this->y()-t.y(), this->z()-t.z());
} }
std::string Transform::prettyPrint() const std::string Transform::prettyPrint() const
@@ -182,9 +191,7 @@ std::string Transform::prettyPrint() const
Transform Transform::operator*(const Transform & t) const Transform Transform::operator*(const Transform & t) const
{ {
Eigen::Matrix4f m1 = util3d::transformToEigen4f(*this); return fromEigen4f(toEigen4f()*t.toEigen4f());
Eigen::Matrix4f m2 = util3d::transformToEigen4f(t);
return util3d::transformFromEigen4f(m1*m2);
} }
Transform & Transform::operator*=(const Transform & t) Transform & Transform::operator*=(const Transform & t)
@@ -216,5 +223,64 @@ std::ostream& operator<<(std::ostream& os, const Transform& s)
return os; return os;
} }
Eigen::Matrix4f Transform::toEigen4f() const
{
Eigen::Matrix4f m;
m << data_[0], data_[1], data_[2], data_[3],
data_[4], data_[5], data_[6], data_[7],
data_[8], data_[9], data_[10], data_[11],
0,0,0,1;
return m;
}
Eigen::Matrix4d Transform::toEigen4d() const
{
Eigen::Matrix4d m;
m << data_[0], data_[1], data_[2], data_[3],
data_[4], data_[5], data_[6], data_[7],
data_[8], data_[9], data_[10], data_[11],
0,0,0,1;
return m;
}
Eigen::Affine3f Transform::toEigen3f() const
{
return Eigen::Affine3f(toEigen4f());
}
Eigen::Affine3d Transform::toEigen3d() const
{
return Eigen::Affine3d(toEigen4d());
}
Transform Transform::getIdentity()
{
return Transform(1,0,0,0, 0,1,0,0, 0,0,1,0);
}
Transform Transform::fromEigen4f(const Eigen::Matrix4f & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
Transform Transform::fromEigen4d(const Eigen::Matrix4d & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
Transform Transform::fromEigen3f(const Eigen::Affine3f & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
Transform Transform::fromEigen3d(const Eigen::Affine3d & matrix)
{
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
}
} }
+2 -2
View File
@@ -71,7 +71,7 @@ void VWDictionary::parseParameters(const ParametersMap & parameters)
Parameters::parse(parameters, Parameters::kKpNndrRatio(), _nndrRatio); Parameters::parse(parameters, Parameters::kKpNndrRatio(), _nndrRatio);
Parameters::parse(parameters, Parameters::kKpNewWordsComparedTogether(), _newWordsComparedTogether); Parameters::parse(parameters, Parameters::kKpNewWordsComparedTogether(), _newWordsComparedTogether);
UASSERT(_nndrRatio > 0.0f); UASSERT_MSG(_nndrRatio > 0.0f, uFormat("String=%s value=%f", uContains(parameters, Parameters::kKpNndrRatio())?parameters.at(Parameters::kKpNndrRatio()).c_str():"", _nndrRatio).c_str());
std::string dictionaryPath = _dictionaryPath; std::string dictionaryPath = _dictionaryPath;
bool incrementalDictionary = _incrementalDictionary; bool incrementalDictionary = _incrementalDictionary;
@@ -167,7 +167,7 @@ void VWDictionary::setFixedDictionary(const std::string & dictionaryPath)
//get descriptor //get descriptor
for(;i<dimension && iter != strList.end(); ++i, ++iter) for(;i<dimension && iter != strList.end(); ++i, ++iter)
{ {
descriptor.at<float>(i) = std::atof(iter->c_str()); descriptor.at<float>(i) = uStr2Float(*iter);
} }
if(i != dimension) if(i != dimension)
{ {
+26 -848
View File
@@ -47,13 +47,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cmath> #include <cmath>
#include <stdio.h> #include <stdio.h>
#include <zlib.h>
#include "rtabmap/utilite/UConversion.h" #include "rtabmap/utilite/UConversion.h"
#include "rtabmap/utilite/UTimer.h" #include "rtabmap/utilite/UTimer.h"
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/Signature.h" #include "rtabmap/core/Signature.h"
#include "toro3d/treeoptimizer3.hh"
#include <pcl/filters/random_sample.h> #include <pcl/filters/random_sample.h>
@@ -63,54 +60,6 @@ namespace rtabmap
namespace util3d namespace util3d
{ {
// format : ".png" ".jpg" "" (empty is general)
CompressionThread::CompressionThread(const cv::Mat & mat, const std::string & format) :
uncompressedData_(mat),
format_(format),
image_(!format.empty()),
compressMode_(true)
{
UASSERT(format.empty() || format.compare(".png") == 0 || format.compare(".jpg") == 0);
}
// assume image
CompressionThread::CompressionThread(const cv::Mat & bytes, bool isImage) :
compressedData_(bytes),
image_(isImage),
compressMode_(false)
{}
void CompressionThread::mainLoop()
{
if(compressMode_)
{
if(!uncompressedData_.empty())
{
if(image_)
{
compressedData_ = compressImage2(uncompressedData_, format_);
}
else
{
compressedData_ = compressData2(uncompressedData_);
}
}
}
else // uncompress
{
if(!compressedData_.empty())
{
if(image_)
{
uncompressedData_ = uncompressImage(compressedData_);
}
else
{
uncompressedData_ = uncompressData(compressedData_);
}
}
}
this->kill();
}
cv::Mat bgrFromCloud(const pcl::PointCloud<pcl::PointXYZRGBA> & cloud, bool bgrOrder) cv::Mat bgrFromCloud(const pcl::PointCloud<pcl::PointXYZRGBA> & cloud, bool bgrOrder)
{ {
cv::Mat frameBGR = cv::Mat(cloud.height,cloud.width,CV_8UC3); cv::Mat frameBGR = cv::Mat(cloud.height,cloud.width,CV_8UC3);
@@ -345,7 +294,7 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr generateKeypoints3DDepth(
if(!transform.isNull() && !transform.isIdentity()) if(!transform.isNull() && !transform.isIdentity())
{ {
pt = pcl::transformPoint(pt, util3d::transformToEigen3f(transform)); pt = pcl::transformPoint(pt, transform.toEigen3f());
} }
keypoints3d->at(i) = pt; keypoints3d->at(i) = pt;
} }
@@ -377,7 +326,7 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr generateKeypoints3DDisparity(
if(pcl::isFinite(pt) && !transform.isNull() && !transform.isIdentity()) if(pcl::isFinite(pt) && !transform.isNull() && !transform.isIdentity())
{ {
pt = pcl::transformPoint(pt, util3d::transformToEigen3f(transform)); pt = pcl::transformPoint(pt, transform.toEigen3f());
} }
keypoints3d->at(i) = pt; keypoints3d->at(i) = pt;
} }
@@ -444,7 +393,7 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr generateKeypoints3DStereo(
pt = tmpPt; pt = tmpPt;
if(!transform.isNull() && !transform.isIdentity()) if(!transform.isNull() && !transform.isIdentity())
{ {
pt = pcl::transformPoint(pt, util3d::transformToEigen3f(transform)); pt = pcl::transformPoint(pt, transform.toEigen3f());
} }
} }
} }
@@ -1095,168 +1044,6 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr laserScanToPointCloud(const cv::Mat & laserS
return output; return output;
} }
// ".png" or ".jpg"
std::vector<unsigned char> compressImage(const cv::Mat & image, const std::string & format)
{
std::vector<unsigned char> bytes;
if(!image.empty())
{
cv::imencode(format, image, bytes);
}
return bytes;
}
// ".png" or ".jpg"
cv::Mat compressImage2(const cv::Mat & image, const std::string & format)
{
std::vector<unsigned char> bytes = compressImage(image, format);
if(bytes.size())
{
return cv::Mat(1, bytes.size(), CV_8UC1, bytes.data()).clone();
}
return cv::Mat();
}
cv::Mat uncompressImage(const cv::Mat & bytes)
{
cv::Mat image;
if(!bytes.empty())
{
#if CV_MAJOR_VERSION>2 || (CV_MAJOR_VERSION >=2 && CV_MINOR_VERSION >=4)
image = cv::imdecode(bytes, cv::IMREAD_UNCHANGED);
#else
image = cv::imdecode(bytes, -1);
#endif
}
return image;
}
cv::Mat uncompressImage(const std::vector<unsigned char> & bytes)
{
cv::Mat image;
if(bytes.size())
{
#if CV_MAJOR_VERSION>2 || (CV_MAJOR_VERSION >=2 && CV_MINOR_VERSION >=4)
image = cv::imdecode(bytes, cv::IMREAD_UNCHANGED);
#else
image = cv::imdecode(bytes, -1);
#endif
}
return image;
}
std::vector<unsigned char> compressData(const cv::Mat & data)
{
std::vector<unsigned char> bytes;
if(!data.empty())
{
uLong sourceLen = uLong(data.total())*uLong(data.elemSize());
uLong destLen = compressBound(sourceLen);
bytes.resize(destLen);
int errCode = compress(
(Bytef *)bytes.data(),
&destLen,
(const Bytef *)data.data,
sourceLen);
bytes.resize(destLen+3*sizeof(int));
*((int*)&bytes[destLen]) = data.rows;
*((int*)&bytes[destLen+sizeof(int)]) = data.cols;
*((int*)&bytes[destLen+2*sizeof(int)]) = data.type();
if(errCode == Z_MEM_ERROR)
{
UERROR("Z_MEM_ERROR : Insufficient memory.");
}
else if(errCode == Z_BUF_ERROR)
{
UERROR("Z_BUF_ERROR : The buffer dest was not large enough to hold the uncompressed data.");
}
}
return bytes;
}
cv::Mat compressData2(const cv::Mat & data)
{
cv::Mat bytes;
if(!data.empty())
{
uLong sourceLen = uLong(data.total())*uLong(data.elemSize());
uLong destLen = compressBound(sourceLen);
bytes = cv::Mat(1, destLen+3*sizeof(int), CV_8UC1);
int errCode = compress(
(Bytef *)bytes.data,
&destLen,
(const Bytef *)data.data,
sourceLen);
bytes = cv::Mat(bytes, cv::Rect(0,0, destLen+3*sizeof(int), 1));
*((int*)&bytes.data[destLen]) = data.rows;
*((int*)&bytes.data[destLen+sizeof(int)]) = data.cols;
*((int*)&bytes.data[destLen+2*sizeof(int)]) = data.type();
if(errCode == Z_MEM_ERROR)
{
UERROR("Z_MEM_ERROR : Insufficient memory.");
}
else if(errCode == Z_BUF_ERROR)
{
UERROR("Z_BUF_ERROR : The buffer dest was not large enough to hold the uncompressed data.");
}
}
return bytes;
}
cv::Mat uncompressData(const cv::Mat & bytes)
{
UASSERT(bytes.empty() || bytes.type() == CV_8UC1);
return uncompressData(bytes.data, bytes.cols*bytes.rows);
}
cv::Mat uncompressData(const std::vector<unsigned char> & bytes)
{
return uncompressData(bytes.data(), bytes.size());
}
cv::Mat uncompressData(const unsigned char * bytes, unsigned long size)
{
cv::Mat data;
if(bytes && size>=3*sizeof(int))
{
//last 3 int elements are matrix size and type
int height = *((int*)&bytes[size-3*sizeof(int)]);
int width = *((int*)&bytes[size-2*sizeof(int)]);
int type = *((int*)&bytes[size-1*sizeof(int)]);
// If the size is higher, it may be a wrong data format.
UASSERT_MSG(height>=0 && height<10000 &&
width>=0 && width<10000,
uFormat("size=%d, height=%d width=%d type=%d", size, height, width, type).c_str());
data = cv::Mat(height, width, type);
uLongf totalUncompressed = uLongf(data.total())*uLongf(data.elemSize());
int errCode = uncompress(
(Bytef*)data.data,
&totalUncompressed,
(const Bytef*)bytes,
uLong(size));
if(errCode == Z_MEM_ERROR)
{
UERROR("Z_MEM_ERROR : Insufficient memory.");
}
else if(errCode == Z_BUF_ERROR)
{
UERROR("Z_BUF_ERROR : The buffer dest was not large enough to hold the uncompressed data.");
}
else if(errCode == Z_DATA_ERROR)
{
UERROR("Z_DATA_ERROR : The compressed data (referenced by source) was corrupted.");
}
}
return data;
}
void extractXYZCorrespondences(const std::multimap<int, pcl::PointXYZ> & words1, void extractXYZCorrespondences(const std::multimap<int, pcl::PointXYZ> & words1,
const std::multimap<int, pcl::PointXYZ> & words2, const std::multimap<int, pcl::PointXYZ> & words2,
pcl::PointCloud<pcl::PointXYZ> & cloud1, pcl::PointCloud<pcl::PointXYZ> & cloud1,
@@ -1643,7 +1430,7 @@ Transform transformFromXYZCorrespondences(
bestTransformation.row (2) = model_coefficients.segment<4>(8); bestTransformation.row (2) = model_coefficients.segment<4>(8);
bestTransformation.row (3) = model_coefficients.segment<4>(12); bestTransformation.row (3) = model_coefficients.segment<4>(12);
transform = util3d::transformFromEigen4f(bestTransformation); transform = Transform::fromEigen4f(bestTransformation);
UDEBUG("RANSAC inliers=%d/%d tf=%s", (int)inliers.size(), (int)cloud1->size(), transform.prettyPrint().c_str()); UDEBUG("RANSAC inliers=%d/%d tf=%s", (int)inliers.size(), (int)cloud1->size(), transform.prettyPrint().c_str());
return transform.inverse(); // inverse to get actual pose transform (not correspondences transform) return transform.inverse(); // inverse to get actual pose transform (not correspondences transform)
@@ -1747,7 +1534,7 @@ Transform icp(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud_source,
*hasConvergedOut = hasConverged; *hasConvergedOut = hasConverged;
} }
return transformFromEigen4f(icp.getFinalTransformation()); return Transform::fromEigen4f(icp.getFinalTransformation());
} }
// return transform from source to target (All points/normals must be finite!!!) // return transform from source to target (All points/normals must be finite!!!)
@@ -1837,7 +1624,7 @@ Transform icpPointToPlane(
*hasConvergedOut = hasConverged; *hasConvergedOut = hasConverged;
} }
return transformFromEigen4f(icp.getFinalTransformation()); return Transform::fromEigen4f(icp.getFinalTransformation());
} }
// return transform from source to target (All points must be finite!!!) // return transform from source to target (All points must be finite!!!)
@@ -1926,7 +1713,7 @@ Transform icp2D(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud_source,
*hasConvergedOut = hasConverged; *hasConvergedOut = hasConverged;
} }
return transformFromEigen4f(icp.getFinalTransformation()); return Transform::fromEigen4f(icp.getFinalTransformation());
} }
pcl::PointCloud<pcl::PointNormal>::Ptr computeNormals( pcl::PointCloud<pcl::PointNormal>::Ptr computeNormals(
@@ -2054,7 +1841,7 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr cvMat2Cloud(
UASSERT(matrix.type() == CV_32FC2 || matrix.type() == CV_32FC3); UASSERT(matrix.type() == CV_32FC2 || matrix.type() == CV_32FC3);
UASSERT(matrix.rows == 1); UASSERT(matrix.rows == 1);
Eigen::Affine3f t = transformToEigen3f(tranform); Eigen::Affine3f t = tranform.toEigen3f();
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(matrix.cols); cloud->resize(matrix.cols);
if(matrix.channels() == 2) if(matrix.channels() == 2)
@@ -2219,608 +2006,6 @@ pcl::PolygonMesh::Ptr createMesh(
return mesh; return mesh;
} }
std::multimap<int, Link>::iterator findLink(
std::multimap<int, Link> & links,
int from,
int to)
{
std::multimap<int, Link>::iterator iter = links.find(from);
while(iter != links.end() && iter->first == from)
{
if(iter->second.to() == to)
{
return iter;
}
++iter;
}
// let's try to -> from
iter = links.find(to);
while(iter != links.end() && iter->first == to)
{
if(iter->second.to() == from)
{
return iter;
}
++iter;
}
return links.end();
}
// <int, depth> margin=0 means infinite margin
std::map<int, int> generateDepthGraph(
const std::multimap<int, Link> & links,
int fromId,
int depth)
{
UASSERT(depth >= 0);
//UDEBUG("signatureId=%d, neighborsMargin=%d", signatureId, margin);
std::map<int, int> ids;
if(fromId<=0)
{
return ids;
}
std::list<int> curentDepthList;
std::set<int> nextDepth;
nextDepth.insert(fromId);
int d = 0;
while((depth == 0 || d < depth) && nextDepth.size())
{
curentDepthList = std::list<int>(nextDepth.begin(), nextDepth.end());
nextDepth.clear();
for(std::list<int>::iterator jter = curentDepthList.begin(); jter!=curentDepthList.end(); ++jter)
{
if(ids.find(*jter) == ids.end())
{
std::set<int> marginIds;
ids.insert(std::pair<int, int>(*jter, d));
for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
{
if(iter->second.from() == *jter)
{
marginIds.insert(iter->second.to());
}
else if(iter->second.to() == *jter)
{
marginIds.insert(iter->second.from());
}
}
// Margin links
for(std::set<int>::const_iterator iter=marginIds.begin(); iter!=marginIds.end(); ++iter)
{
if( !uContains(ids, *iter) && nextDepth.find(*iter) == nextDepth.end())
{
nextDepth.insert(*iter);
}
}
}
}
++d;
}
return ids;
}
void optimizeTOROGraph(
const std::map<int, int> & depthGraph,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & links,
std::map<int, Transform> & optimizedPoses,
int toroIterations,
bool toroInitialGuess,
bool ignoreCovariance,
std::list<std::map<int, Transform> > * intermediateGraphes)
{
optimizedPoses.clear();
if(depthGraph.size() && poses.size()>=2 && links.size()>=1)
{
// Modify IDs using the margin from the current signature (TORO root will be the last signature)
int m = 0;
int toroId = 1;
std::map<int, int> rtabmapToToro; // <RTAB-Map ID, TORO ID>
std::map<int, int> toroToRtabmap; // <TORO ID, RTAB-Map ID>
std::map<int, int> idsTmp = depthGraph;
while(idsTmp.size())
{
for(std::map<int, int>::iterator iter = idsTmp.begin(); iter!=idsTmp.end();)
{
if(m == iter->second)
{
rtabmapToToro.insert(std::make_pair(iter->first, toroId));
toroToRtabmap.insert(std::make_pair(toroId, iter->first));
++toroId;
idsTmp.erase(iter++);
}
else
{
++iter;
}
}
++m;
}
std::map<int, rtabmap::Transform> posesToro;
std::multimap<int, rtabmap::Link> edgeConstraintsToro;
for(std::map<int, rtabmap::Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
if(uContains(depthGraph, iter->first))
{
UASSERT(!iter->second.isNull());
posesToro.insert(std::make_pair(rtabmapToToro.at(iter->first), iter->second));
}
}
for(std::multimap<int, rtabmap::Link>::const_iterator iter = links.begin();
iter!=links.end();
++iter)
{
if(uContains(depthGraph, iter->second.from()) && uContains(depthGraph, iter->second.to()))
{
UASSERT(!iter->second.transform().isNull());
edgeConstraintsToro.insert(std::make_pair(rtabmapToToro.at(iter->first), Link(rtabmapToToro.at(iter->first), rtabmapToToro.at(iter->second.to()), iter->second.type(), iter->second.transform(), iter->second.variance())));
}
}
std::map<int, rtabmap::Transform> optimizedPosesToro;
if(posesToro.size() && edgeConstraintsToro.size())
{
std::list<std::map<int, rtabmap::Transform> > graphesToro;
// Optimize!
rtabmap::util3d::optimizeTOROGraph(
posesToro,
edgeConstraintsToro,
optimizedPosesToro,
toroIterations,
toroInitialGuess,
ignoreCovariance,
&graphesToro);
for(std::map<int, rtabmap::Transform>::iterator iter=optimizedPosesToro.begin(); iter!=optimizedPosesToro.end(); ++iter)
{
optimizedPoses.insert(std::make_pair(toroToRtabmap.at(iter->first), iter->second));
}
if(intermediateGraphes)
{
for(std::list<std::map<int, rtabmap::Transform> >::iterator iter = graphesToro.begin(); iter!=graphesToro.end(); ++iter)
{
std::map<int, rtabmap::Transform> tmp;
for(std::map<int, rtabmap::Transform>::iterator jter=iter->begin(); jter!=iter->end(); ++jter)
{
tmp.insert(std::make_pair(toroToRtabmap.at(jter->first), jter->second));
}
intermediateGraphes->push_back(tmp);
}
}
}
else
{
UERROR("No TORO poses and constraints!?");
}
}
else if(links.size() == 0 && poses.size() == 1)
{
optimizedPoses = poses;
}
else
{
UERROR("Wrong inputs! depthGraph=%d poses=%d links=%d",
(int)depthGraph.size(), (int)poses.size(), (int)links.size());
}
}
//On success, optimizedPoses is cleared and new poses are inserted in
void optimizeTOROGraph(
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints,
std::map<int, Transform> & optimizedPoses,
int toroIterations,
bool toroInitialGuess,
bool ignoreCovariance,
std::list<std::map<int, Transform> > * intermediateGraphes) // contains poses after tree init to last one before the end
{
UASSERT(toroIterations>0);
optimizedPoses.clear();
if(edgeConstraints.size()>=1 && poses.size()>=2)
{
// Apply TORO optimization
AISNavigation::TreeOptimizer3 pg;
pg.verboseLevel = 0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
float x,y,z, roll,pitch,yaw;
UASSERT(!iter->second.isNull());
pcl::getTranslationAndEulerAngles(transformToEigen3f(iter->second), x,y,z, roll,pitch,yaw);
AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v = pg.addVertex(iter->first, p);
if (v)
{
v->transformation=AISNavigation::TreePoseGraph3::Transformation(p);
}
else
{
UERROR("cannot insert vertex %d!?", iter->first);
}
}
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
{
int id1 = iter->first;
int id2 = iter->second.to();
float x,y,z, roll,pitch,yaw;
UASSERT(!iter->second.transform().isNull());
pcl::getTranslationAndEulerAngles(transformToEigen3f(iter->second.transform()), x,y,z, roll,pitch,yaw);
AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
AISNavigation::TreePoseGraph3::InformationMatrix inf = DMatrix<double>::I(6);
if(!ignoreCovariance && iter->second.variance()>0)
{
inf[0][0] = 1.0f/iter->second.variance(); // x
inf[1][1] = 1.0f/iter->second.variance(); // y
inf[2][2] = 1.0f/iter->second.variance(); // z
inf[3][3] = 1.0f/iter->second.variance(); // roll
inf[4][4] = 1.0f/iter->second.variance(); // pitch
inf[5][5] = 1.0f/iter->second.variance(); // yaw
}
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v1=pg.vertex(id1);
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v2=pg.vertex(id2);
AISNavigation::TreePoseGraph3::Transformation t(p);
if (!pg.addEdge(v1, v2, t, inf))
{
UERROR("Map: Edge already exits between nodes %d and %d, skipping", id1, id2);
return;
}
}
pg.buildMST(pg.vertices.begin()->first); // pg.buildSimpleTree();
UDEBUG("Initial guess...");
if(toroInitialGuess)
{
pg.initializeOnTree(); // optional
}
pg.initializeTreeParameters();
UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
"TORO is not able to find the root of the graph!)");
pg.initializeOptimization();
UDEBUG("TORO iterate begin (iterations=%d)", toroIterations);
for (int i=0; i<toroIterations; i++)
{
if(intermediateGraphes && (toroInitialGuess || i>0))
{
std::map<int, Transform> tmpPoses;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v=pg.vertex(iter->first);
v->pose=v->transformation.toPoseType();
Transform newPose = transformFromEigen3f(pcl::getTransformation(v->pose.x(), v->pose.y(), v->pose.z(), v->pose.roll(), v->pose.pitch(), v->pose.yaw()));
tmpPoses.insert(std::pair<int, Transform>(iter->first, newPose));
}
intermediateGraphes->push_back(tmpPoses);
}
pg.iterate();
}
UDEBUG("TORO iterate end");
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v=pg.vertex(iter->first);
v->pose=v->transformation.toPoseType();
Transform newPose = transformFromEigen3f(pcl::getTransformation(v->pose.x(), v->pose.y(), v->pose.z(), v->pose.roll(), v->pose.pitch(), v->pose.yaw()));
optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
}
//Eigen::Matrix4f newPose = transformToEigen4f(optimizedPoses.at(poses.rbegin()->first));
//Eigen::Matrix4f oldPose = transformToEigen4f(poses.rbegin()->second);
//Eigen::Matrix4f poseCorrection = oldPose.inverse() * newPose; // transform from odom to correct odom
//Eigen::Matrix4f result = oldPose*poseCorrection*oldPose.inverse();
//mapCorrection = transformFromEigen4f(result);
}
else if(edgeConstraints.size() == 0 && poses.size() == 1)
{
optimizedPoses = poses;
}
else
{
UWARN("This method should be called at least with 1 pose!");
}
}
bool saveTOROGraph(
const std::string & fileName,
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & edgeConstraints)
{
FILE * file = 0;
#ifdef _MSC_VER
fopen_s(&file, fileName.c_str(), "w");
#else
file = fopen(fileName.c_str(), "w");
#endif
if(file)
{
// VERTEX3 id x y z phi theta psi
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
float x,y,z, yaw,pitch,roll;
pcl::getTranslationAndEulerAngles(transformToEigen3f(iter->second), x,y,z, roll, pitch, yaw);
fprintf(file, "VERTEX3 %d %f %f %f %f %f %f\n",
iter->first,
x,
y,
z,
roll,
pitch,
yaw);
}
//EDGE3 observed_vertex_id observing_vertex_id x y z roll pitch yaw inf_11 inf_12 .. inf_16 inf_22 .. inf_66
for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
{
float x,y,z, yaw,pitch,roll;
pcl::getTranslationAndEulerAngles(transformToEigen3f(iter->second.transform()), x,y,z, roll, pitch, yaw);
fprintf(file, "EDGE3 %d %d %f %f %f %f %f %f %f 0 0 0 0 0 %f 0 0 0 0 %f 0 0 0 %f 0 0 %f 0 %f\n",
iter->first,
iter->second.to(),
x,
y,
z,
roll,
pitch,
yaw,
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance(),
1.0f/iter->second.variance());
}
UINFO("Graph saved to %s", fileName.c_str());
fclose(file);
}
else
{
UERROR("Cannot save to file %s", fileName.c_str());
return false;
}
return true;
}
bool loadTOROGraph(const std::string & fileName,
std::map<int, Transform> & poses,
std::multimap<int, std::pair<int, Transform> > & edgeConstraints)
{
FILE * file = 0;
#ifdef _MSC_VER
fopen_s(&file, fileName.c_str(), "r");
#else
file = fopen(fileName.c_str(), "r");
#endif
if(file)
{
char line[200];
while ( fgets (line , 200 , file) != NULL )
{
std::vector<std::string> strList = uListToVector(uSplit(line, ' '));
if(strList.size() == 8)
{
//VERTEX3
int id = atoi(strList[1].c_str());
float x = atof(strList[2].c_str());
float y = atof(strList[3].c_str());
float z = atof(strList[4].c_str());
float roll = atof(strList[5].c_str());
float pitch = atof(strList[6].c_str());
float yaw = atof(strList[7].c_str());
Transform pose = transformFromEigen3f(pcl::getTransformation(x, y, z, roll, pitch, yaw));
std::map<int, Transform>::iterator iter = poses.find(id);
if(iter != poses.end())
{
iter->second = pose;
}
else
{
UFATAL("");
}
}
else if(strList.size() == 30)
{
//EDGE3
int idFrom = atoi(strList[1].c_str());
int idTo = atoi(strList[2].c_str());
float x = atof(strList[3].c_str());
float y = atof(strList[4].c_str());
float z = atof(strList[5].c_str());
float roll = atof(strList[6].c_str());
float pitch = atof(strList[7].c_str());
float yaw = atof(strList[8].c_str());
Transform transform = transformFromEigen3f(pcl::getTransformation(x, y, z, roll, pitch, yaw));
if(poses.find(idFrom) != poses.end() && poses.find(idTo) != poses.end())
{
std::pair<int, Transform> edge(idTo, transform);
edgeConstraints.insert(std::pair<int, std::pair<int, Transform> >(idFrom, edge));
}
else
{
UFATAL("");
}
}
else
{
UFATAL("Error parsing map file %s", fileName.c_str());
}
}
UINFO("Graph loaded from %s", fileName.c_str());
fclose(file);
}
else
{
UERROR("Cannot open file %s", fileName.c_str());
return false;
}
return true;
}
std::map<int, Transform> radiusPosesFiltering(const std::map<int, Transform> & poses, float radius, float angle, bool keepLatest)
{
if(poses.size() > 1 && radius > 0.0f && angle>0.0f)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(poses.size());
int i=0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
(*cloud)[i++] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
}
// radius filtering
std::vector<int> names = uKeys(poses);
std::vector<Transform> transforms = uValues(poses);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
tree->setInputCloud(cloud);
std::set<int> indicesChecked;
std::set<int> indicesKept;
for(unsigned int i=0; i<cloud->size(); ++i)
{
// ignore scans
if(indicesChecked.find(i) == indicesChecked.end())
{
std::vector<int> kIndices;
std::vector<float> kDistances;
tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
std::set<int> cloudIndices;
const Transform & currentT = transforms.at(i);
Eigen::Vector3f vA = util3d::transformToEigen3f(currentT).rotation()*Eigen::Vector3f(1,0,0);
for(unsigned int j=0; j<kIndices.size(); ++j)
{
if(indicesChecked.find(kIndices[j]) == indicesChecked.end())
{
const Transform & checkT = transforms.at(kIndices[j]);
// same orientation?
Eigen::Vector3f vB = util3d::transformToEigen3f(checkT).rotation()*Eigen::Vector3f(1,0,0);
double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
if(a <= angle)
{
cloudIndices.insert(kIndices[j]);
}
}
}
if(keepLatest)
{
bool lastAdded = false;
for(std::set<int>::reverse_iterator iter = cloudIndices.rbegin(); iter!=cloudIndices.rend(); ++iter)
{
if(!lastAdded)
{
indicesKept.insert(*iter);
lastAdded = true;
}
indicesChecked.insert(*iter);
}
}
else
{
bool firstAdded = false;
for(std::set<int>::iterator iter = cloudIndices.begin(); iter!=cloudIndices.end(); ++iter)
{
if(!firstAdded)
{
indicesKept.insert(*iter);
firstAdded = true;
}
indicesChecked.insert(*iter);
}
}
}
}
//pcl::IndicesPtr indicesOut(new std::vector<int>);
//indicesOut->insert(indicesOut->end(), indicesKept.begin(), indicesKept.end());
UINFO("Cloud filtered In = %d, Out = %d", cloud->size(), indicesKept.size());
//pcl::io::savePCDFile("duplicateIn.pcd", *cloud);
//pcl::io::savePCDFile("duplicateOut.pcd", *cloud, *indicesOut);
std::map<int, Transform> keptPoses;
for(std::set<int>::iterator iter = indicesKept.begin(); iter!=indicesKept.end(); ++iter)
{
keptPoses.insert(std::make_pair(names.at(*iter), transforms.at(*iter)));
}
return keptPoses;
}
else
{
return poses;
}
}
std::multimap<int, int> radiusPosesClustering(const std::map<int, Transform> & poses, float radius, float angle)
{
std::multimap<int, int> clusters;
if(poses.size() > 1 && radius > 0.0f && angle>0.0f)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(poses.size());
int i=0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
(*cloud)[i++] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
}
// radius clustering (nearest neighbors)
std::vector<int> ids = uKeys(poses);
std::vector<Transform> transforms = uValues(poses);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
tree->setInputCloud(cloud);
for(unsigned int i=0; i<cloud->size(); ++i)
{
std::vector<int> kIndices;
std::vector<float> kDistances;
tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
std::set<int> cloudIndices;
const Transform & currentT = transforms.at(i);
Eigen::Vector3f vA = util3d::transformToEigen3f(currentT).rotation()*Eigen::Vector3f(1,0,0);
for(unsigned int j=0; j<kIndices.size(); ++j)
{
if((int)i != kIndices[j])
{
const Transform & checkT = transforms.at(kIndices[j]);
// same orientation?
Eigen::Vector3f vB = util3d::transformToEigen3f(checkT).rotation()*Eigen::Vector3f(1,0,0);
double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
if(a <= angle)
{
clusters.insert(std::make_pair(ids[i], ids[kIndices[j]]));
}
}
}
}
}
return clusters;
}
bool occupancy2DFromCloud3D( bool occupancy2DFromCloud3D(
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
cv::Mat & ground, cv::Mat & ground,
@@ -3123,14 +2308,15 @@ cv::Mat create2DMap(const std::map<int, Transform> & poses,
pcl::PointXYZ min, max; pcl::PointXYZ min, max;
pcl::getMinMax3D(minMax, min, max); pcl::getMinMax3D(minMax, min, max);
xMin = min.x-cellSize; // Added X2 to make sure that all points are inside the map (when rounded to integer)
yMin = min.y-cellSize; xMin = min.x-cellSize*2.0f;
float xMax = max.x+cellSize; yMin = min.y-cellSize*2.0f;
float yMax = max.y+cellSize; float xMax = max.x+cellSize*2.0f;
float yMax = max.y+cellSize*2.0f;
UDEBUG("map min=(%f, %f) max=(%f,%f)", xMin, yMin, xMax, yMax); UDEBUG("map min=(%f, %f) max=(%f,%f)", xMin, yMin, xMax, yMax);
//UTimer timer; UTimer timer;
map = cv::Mat::ones((yMax - yMin) / cellSize + 0.5f, (xMax - xMin) / cellSize + 0.5f, CV_8S)*-1; map = cv::Mat::ones((yMax - yMin) / cellSize + 0.5f, (xMax - xMin) / cellSize + 0.5f, CV_8S)*-1;
std::vector<float> maxSquaredLength(localScans.size(), 0.0f); std::vector<float> maxSquaredLength(localScans.size(), 0.0f);
@@ -3158,7 +2344,7 @@ cv::Mat create2DMap(const std::map<int, Transform> & poses,
} }
++j; ++j;
} }
//UWARN("timer=%fs", timer.ticks()); UDEBUG("Ray trace known space=%fs", timer.ticks());
// now fill unknown spaces // now fill unknown spaces
if(unknownSpaceFilled) if(unknownSpaceFilled)
@@ -3222,8 +2408,8 @@ cv::Mat create2DMap(const std::map<int, Transform> & poses,
} }
} }
++j; ++j;
//UWARN("timer=%fs", timer.ticks());
} }
UDEBUG("Fill empty space=%fs", timer.ticks());
} }
} }
return map; return map;
@@ -3261,26 +2447,18 @@ void rayTrace(const cv::Point2i & start, const cv::Point2i & end, cv::Mat & grid
//ROS_WARN("lowerbound=%f upperbound=%f", lowerbound, upperbound); //ROS_WARN("lowerbound=%f upperbound=%f", lowerbound, upperbound);
UASSERT_MSG(lowerbound >= 0 && lowerbound < grid.rows, uFormat("lowerbound=%f grid.rows=%d x=%d slope=%f b=%f x=%f", lowerbound, grid.rows, x, slope, b, x).c_str()); UASSERT_MSG(lowerbound >= 0 && lowerbound < grid.rows, uFormat("lowerbound=%f grid.rows=%d x=%d slope=%f b=%f x=%f", lowerbound, grid.rows, x, slope, b, x).c_str());
UASSERT_MSG(upperbound >= 0 && upperbound < grid.rows, uFormat("upperbound=%f grid.rows=%d x+1=%d slope=%f b=%f x=%f", upperbound, grid.rows, x+1, slope, b, x).c_str()); UASSERT_MSG(upperbound >= 0 && upperbound < grid.rows, uFormat("upperbound=%f grid.rows=%d x+1=%d slope=%f b=%f x=%f", upperbound, grid.rows, x+1, slope, b, x).c_str());
// verify if there is no obstacle
bool stopped = false;
if(stopOnObstacle)
{
for(int y = lowerbound; y<=(int)upperbound; ++y)
{
if(grid.at<char>(y, x) == 100)
{
stopped = true;
break;
}
}
}
if(stopped)
{
break;
}
for(int y = lowerbound; y<=(int)upperbound; ++y) for(int y = lowerbound; y<=(int)upperbound; ++y)
{ {
grid.at<char>(y, x) = 0; // free space char & v = grid.at<char>(y, x);
if(v == 100 && stopOnObstacle)
{
return;
}
else
{
v = 0; // free space
}
} }
} }
} }
+3 -3
View File
@@ -139,7 +139,7 @@ int main(int argc, char * argv[])
(int)rtabmap.getSTM().size(), // short-term memory (int)rtabmap.getSTM().size(), // short-term memory
(int)rtabmap.getWM().size(), // working memory (int)rtabmap.getWM().size(), // working memory
rtabmap.getLoopClosureId(), rtabmap.getLoopClosureId(),
rtabmap.getLcHypValue(), rtabmap.getLoopClosureValue(),
nextIndex, nextIndex,
rtabmap.getLoopClosureId()); rtabmap.getLoopClosureId());
} }
@@ -150,8 +150,8 @@ int main(int argc, char * argv[])
rtabmap.getLastProcessTime(), rtabmap.getLastProcessTime(),
(int)rtabmap.getSTM().size(), // short-term memory (int)rtabmap.getSTM().size(), // short-term memory
(int)rtabmap.getWM().size(), // working memory (int)rtabmap.getWM().size(), // working memory
rtabmap.getRetrievedId(), // highest loop closure hypothesis rtabmap.getHighestHypothesisId(), // highest loop closure hypothesis
rtabmap.getLcHypValue()); rtabmap.getLoopClosureValue());
} }
++nextIndex; ++nextIndex;
+11 -9
View File
@@ -126,10 +126,10 @@ private slots:
if(!data.pose().isNull()) if(!data.pose().isNull())
{ {
// update camera position // update camera position
cloudViewer_->updateCameraPosition(data.pose()); cloudViewer_->updateCameraTargetPosition(data.pose());
} }
} }
cloudViewer_->render(); cloudViewer_->update();
_lastOdometryProcessed = true; _lastOdometryProcessed = true;
} }
@@ -165,14 +165,16 @@ private slots:
else if(iter->first == stats.refImageId() && else if(iter->first == stats.refImageId() &&
stats.getSignature().id() == iter->first) stats.getSignature().id() == iter->first)
{ {
Signature s = stats.getSignature();
s.uncompressData(); // make sure data is uncompressed
// Add the new cloud // Add the new cloud
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = util3d::cloudFromDepthRGB( pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = util3d::cloudFromDepthRGB(
stats.getSignature().getImageRaw(), s.getImageRaw(),
stats.getSignature().getDepthRaw(), s.getDepthRaw(),
stats.getSignature().getDepthCx(), s.getDepthCx(),
stats.getSignature().getDepthCy(), s.getDepthCy(),
stats.getSignature().getDepthFx(), s.getDepthFx(),
stats.getSignature().getDepthFy(), s.getDepthFy(),
8); // decimation 8); // decimation
if(cloud->size()) if(cloud->size())
@@ -191,7 +193,7 @@ private slots:
} }
} }
cloudViewer_->render(); cloudViewer_->update();
_processingStatistics = false; _processingStatistics = false;
} }
@@ -64,6 +64,7 @@ private slots:
void processImage(const cv::Mat & image); void processImage(const cv::Mat & image);
void restart(); void restart();
void calibrate(); void calibrate();
void save();
protected: protected:
virtual void closeEvent(QCloseEvent* event); virtual void closeEvent(QCloseEvent* event);
+31 -3
View File
@@ -127,7 +127,7 @@ public:
float opacity); float opacity);
void removeOccupancyGridMap(); void removeOccupancyGridMap();
void updateCameraPosition( void updateCameraTargetPosition(
const Transform & pose); const Transform & pose);
void addOrUpdateGraph( void addOrUpdateGraph(
@@ -137,8 +137,10 @@ public:
void removeGraph(const std::string & id); void removeGraph(const std::string & id);
void removeAllGraphs(); void removeAllGraphs();
bool isTrajectoryShown() const;
unsigned int getTrajectorySize() const;
void setTrajectoryShown(bool shown); void setTrajectoryShown(bool shown);
void setTrajectorySize(int value); void setTrajectorySize(unsigned int value);
void clearTrajectory(); void clearTrajectory();
void removeAllClouds(); //including meshes void removeAllClouds(); //including meshes
@@ -149,25 +151,47 @@ public:
const QMap<std::string, Transform> & getAddedClouds() const {return _addedClouds;} //including meshes const QMap<std::string, Transform> & getAddedClouds() const {return _addedClouds;} //including meshes
const QColor & getBackgroundColor() const; const QColor & getBackgroundColor() const;
Transform getTargetPose() const;
void getCameraPosition(
float & x, float & y, float & z,
float & focalX, float & focalY, float & focalZ,
float & upX, float & upY, float & upZ) const;
bool isCameraTargetLocked() const;
bool isCameraTargetFollow() const;
bool isCameraFree() const;
bool isCameraLockZ() const;
bool isGridShown() const;
unsigned int getGridCellCount() const;
float getGridCellSize() const;
void setCameraPosition(
float x, float y, float z,
float focalX, float focalY, float focalZ,
float upX, float upY, float upZ);
void setCameraTargetLocked(bool enabled = true); void setCameraTargetLocked(bool enabled = true);
void setCameraTargetFollow(bool enabled = true); void setCameraTargetFollow(bool enabled = true);
void setCameraFree(); void setCameraFree();
void setCameraLockZ(bool enabled = true); void setCameraLockZ(bool enabled = true);
void setGridShown(bool shown); void setGridShown(bool shown);
void setGridCellCount(unsigned int count);
void setGridCellSize(float size);
void setWorkingDirectory(const QString & path) {_workingDirectory = path;} void setWorkingDirectory(const QString & path) {_workingDirectory = path;}
public slots: public slots:
void render();
void setBackgroundColor(const QColor & color); void setBackgroundColor(const QColor & color);
void setCloudVisibility(const std::string & id, bool isVisible); void setCloudVisibility(const std::string & id, bool isVisible);
void setCloudOpacity(const std::string & id, double opacity = 1.0); void setCloudOpacity(const std::string & id, double opacity = 1.0);
void setCloudPointSize(const std::string & id, int size); void setCloudPointSize(const std::string & id, int size);
virtual void clear() {removeAllClouds(); clearTrajectory();} virtual void clear() {removeAllClouds(); clearTrajectory();}
signals:
void configChanged();
protected: protected:
virtual void keyReleaseEvent(QKeyEvent * event); virtual void keyReleaseEvent(QKeyEvent * event);
virtual void keyPressEvent(QKeyEvent * event); virtual void keyPressEvent(QKeyEvent * event);
virtual void mousePressEvent(QMouseEvent * event);
virtual void mouseMoveEvent(QMouseEvent * event);
virtual void contextMenuEvent(QContextMenuEvent * event); virtual void contextMenuEvent(QContextMenuEvent * event);
virtual void handleAction(QAction * event); virtual void handleAction(QAction * event);
QMenu * menu() {return _menu;} QMenu * menu() {return _menu;}
@@ -188,11 +212,15 @@ private:
QAction * _aSetTrajectorySize; QAction * _aSetTrajectorySize;
QAction * _aClearTrajectory; QAction * _aClearTrajectory;
QAction * _aShowGrid; QAction * _aShowGrid;
QAction * _aSetGridCellCount;
QAction * _aSetGridCellSize;
QAction * _aSetBackgroundColor; QAction * _aSetBackgroundColor;
QMenu * _menu; QMenu * _menu;
std::map<std::string, pcl::PointCloud<pcl::PointXYZ>::Ptr > _graphes; std::map<std::string, pcl::PointCloud<pcl::PointXYZ>::Ptr > _graphes;
pcl::PointCloud<pcl::PointXYZ>::Ptr _trajectory; pcl::PointCloud<pcl::PointXYZ>::Ptr _trajectory;
unsigned int _maxTrajectorySize; unsigned int _maxTrajectorySize;
unsigned int _gridCellCount;
float _gridCellSize;
QMap<std::string, Transform> _addedClouds; // include cloud, scan, meshes QMap<std::string, Transform> _addedClouds; // include cloud, scan, meshes
Transform _lastPose; Transform _lastPose;
std::list<std::string> _gridLines; std::list<std::string> _gridLines;
+11 -4
View File
@@ -53,10 +53,11 @@ public:
void resetZoom(); void resetZoom();
bool isImageShown(); bool isImageShown() const;
bool isImageDepthShown(); bool isImageDepthShown() const;
bool isFeaturesShown(); bool isFeaturesShown() const;
bool isLinesShown(); bool isLinesShown() const;
int getAlpha() const {return _alpha;}
void setFeaturesShown(bool shown); void setFeaturesShown(bool shown);
void setImageShown(bool shown); void setImageShown(bool shown);
@@ -69,12 +70,16 @@ public:
void setImageDepth(const QImage & image); void setImageDepth(const QImage & image);
void setFeatureColor(int id, const QColor & color); void setFeatureColor(int id, const QColor & color);
void setFeaturesColor(const QColor & color); void setFeaturesColor(const QColor & color);
void setAlpha(int alpha);
const QMultiMap<int, rtabmap::KeypointItem *> & getFeatures() const {return _features;} const QMultiMap<int, rtabmap::KeypointItem *> & getFeatures() const {return _features;}
void clearLines(); void clearLines();
void clear(); void clear();
signals:
void configChanged();
protected: protected:
virtual void contextMenuEvent(QContextMenuEvent * e); virtual void contextMenuEvent(QContextMenuEvent * e);
virtual void wheelEvent(QWheelEvent * e); virtual void wheelEvent(QWheelEvent * e);
@@ -89,6 +94,7 @@ private:
int _zoom; int _zoom;
int _minZoom; int _minZoom;
QString _savedFileName; QString _savedFileName;
int _alpha;
QMenu * _menu; QMenu * _menu;
QAction * _showImage; QAction * _showImage;
@@ -96,6 +102,7 @@ private:
QAction * _showFeatures; QAction * _showFeatures;
QAction * _showLines; QAction * _showLines;
QAction * _saveImage; QAction * _saveImage;
QAction * _setAlpha;
QMultiMap<int, rtabmap::KeypointItem *> _features; QMultiMap<int, rtabmap::KeypointItem *> _features;
QGraphicsPixmapItem * _image; QGraphicsPixmapItem * _image;
+9 -1
View File
@@ -102,6 +102,7 @@ public:
QString getWorkingDirectory() const; QString getWorkingDirectory() const;
void setMonitoringState(bool pauseChecked = false); // in monitoring state, only some actions are enabled void setMonitoringState(bool pauseChecked = false); // in monitoring state, only some actions are enabled
bool isSavedMaximized() const {return _savedMaximized;}
public slots: public slots:
void processStats(const rtabmap::Statistics & stat); void processStats(const rtabmap::Statistics & stat);
@@ -109,11 +110,15 @@ public slots:
protected: protected:
virtual void closeEvent(QCloseEvent* event); virtual void closeEvent(QCloseEvent* event);
virtual void handleEvent(UEvent* anEvent); virtual void handleEvent(UEvent* anEvent);
virtual void showEvent(QShowEvent* anEvent);
virtual void moveEvent(QMoveEvent* anEvent);
virtual void resizeEvent(QResizeEvent* anEvent); virtual void resizeEvent(QResizeEvent* anEvent);
private slots: private slots:
void changeState(MainWindow::State state); void changeState(MainWindow::State state);
void beep(); void beep();
void configGUIModified();
void saveConfigGUI();
void newDatabase(); void newDatabase();
void openDatabase(); void openDatabase();
void closeDatabase(); void closeDatabase();
@@ -200,7 +205,7 @@ private:
void createAndAddScanToMap(int nodeId, const Transform & pose, int mapId); void createAndAddScanToMap(int nodeId, const Transform & pose, int mapId);
void drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, const std::multimap<int, cv::KeyPoint> & loopWords); void drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords, const std::multimap<int, cv::KeyPoint> & loopWords);
void setupMainLayout(bool vertical); void setupMainLayout(bool vertical);
void updateSelectSourceImageMenu(int type); void updateSelectSourceImageMenu(bool used, PreferencesDialog::Src src);
void updateSelectSourceDatabase(bool used); void updateSelectSourceDatabase(bool used);
void updateSelectSourceRGBDMenu(bool used, PreferencesDialog::Src src); void updateSelectSourceRGBDMenu(bool used, PreferencesDialog::Src src);
@@ -260,6 +265,9 @@ private:
bool _odometryReceived; bool _odometryReceived;
QString _openedDatabasePath; QString _openedDatabasePath;
bool _emptyNewDatabase; bool _emptyNewDatabase;
bool _odomImageShow;
bool _odomImageDepthShow;
bool _savedMaximized;
QMap<int, Signature> _cachedSignatures; QMap<int, Signature> _cachedSignatures;
std::map<int, Transform> _currentPosesMap; // <nodeId, pose> std::map<int, Transform> _currentPosesMap; // <nodeId, pose>
+10 -9
View File
@@ -97,10 +97,13 @@ public:
virtual QString getIniFilePath() const; virtual QString getIniFilePath() const;
void init(); void init();
void saveWindowGeometry(const QString & windowName, const QWidget * window); void saveWindowGeometry(const QWidget * window);
void loadWindowGeometry(const QString & windowName, QWidget * window); void loadWindowGeometry(QWidget * window);
void saveMainWindowState(const QMainWindow * mainWindow); void saveMainWindowState(const QMainWindow * mainWindow);
void loadMainWindowState(QMainWindow * mainWindow); void loadMainWindowState(QMainWindow * mainWindow, bool & maximized);
void saveWidgetState(const QWidget * widget);
void loadWidgetState(QWidget * widget);
void saveCustomConfig(const QString & section, const QString & key, const QString & value); void saveCustomConfig(const QString & section, const QString & key, const QString & value);
QString loadCustomConfig(const QString & section, const QString & key); QString loadCustomConfig(const QString & section, const QString & key);
@@ -114,11 +117,9 @@ public:
int getGeneralLoggerType() const; int getGeneralLoggerType() const;
bool getGeneralLoggerPrintTime() const; bool getGeneralLoggerPrintTime() const;
bool isVerticalLayoutUsed() const; bool isVerticalLayoutUsed() const;
bool isImageFlipped() const;
bool imageRejectedShown() const; bool imageRejectedShown() const;
bool imageHighestHypShown() const; bool imageHighestHypShown() const;
bool beepOnPause() const; bool beepOnPause() const;
int getKeypointsOpacity() const;
int getOdomQualityWarnThr() const; int getOdomQualityWarnThr() const;
bool isPosteriorGraphView() const; bool isPosteriorGraphView() const;
@@ -158,7 +159,7 @@ public:
bool isSourceImageUsed() const; bool isSourceImageUsed() const;
bool isSourceDatabaseUsed() const; bool isSourceDatabaseUsed() const;
bool isSourceOpenniUsed() const; bool isSourceOpenniUsed() const;
int getSourceImageType() const; PreferencesDialog::Src getSourceImageType() const;
QString getSourceImageTypeStr() const; QString getSourceImageTypeStr() const;
int getSourceWidth() const; int getSourceWidth() const;
int getSourceHeight() const; int getSourceHeight() const;
@@ -211,9 +212,9 @@ public slots:
void setHardThr(int value); void setHardThr(int value);
void setTimeLimit(float value); void setTimeLimit(float value);
void setSLAMMode(bool enabled); void setSLAMMode(bool enabled);
void selectSourceImage(Src src = kSrcUndef); void selectSourceImage(Src src = kSrcUndef, bool checked = true);
void selectSourceDatabase(bool user = false); void selectSourceDatabase(bool user = false, bool checked = true);
void selectSourceRGBD(Src src = kSrcUndef); void selectSourceRGBD(Src src = kSrcUndef, bool checked = true);
private slots: private slots:
void closeDialog ( QAbstractButton * button ); void closeDialog ( QAbstractButton * button );
+1
View File
@@ -22,6 +22,7 @@ SET(headers_ui
./PostProcessingDialog.h ./PostProcessingDialog.h
./ExportCloudsDialog.h ./ExportCloudsDialog.h
./MapVisibilityWidget.h ./MapVisibilityWidget.h
./GraphViewer.h
) )
SET(uis SET(uis
+34 -1
View File
@@ -33,6 +33,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <opencv2/calib3d/calib3d.hpp> #include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui.hpp>
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <rtabmap/core/CameraEvent.h> #include <rtabmap/core/CameraEvent.h>
#include <rtabmap/gui/UCv2Qt.h> #include <rtabmap/gui/UCv2Qt.h>
@@ -57,6 +60,7 @@ CalibrationDialog::CalibrationDialog(QWidget * parent) :
connect(ui_->pushButton_calibrate, SIGNAL(clicked()), this, SLOT(calibrate())); connect(ui_->pushButton_calibrate, SIGNAL(clicked()), this, SLOT(calibrate()));
connect(ui_->pushButton_restart, SIGNAL(clicked()), this, SLOT(restart())); connect(ui_->pushButton_restart, SIGNAL(clicked()), this, SLOT(restart()));
connect(ui_->pushButton_save, SIGNAL(clicked()), this, SLOT(save()));
connect(ui_->spinBox_boardWidth, SIGNAL(valueChanged(int)), this, SLOT(setBoardWidth(int))); connect(ui_->spinBox_boardWidth, SIGNAL(valueChanged(int)), this, SLOT(setBoardWidth(int)));
connect(ui_->spinBox_boardHeight, SIGNAL(valueChanged(int)), this, SLOT(setBoardHeight(int))); connect(ui_->spinBox_boardHeight, SIGNAL(valueChanged(int)), this, SLOT(setBoardHeight(int)));
@@ -241,7 +245,7 @@ void CalibrationDialog::processImage(const cv::Mat & image)
} }
//show frame //show frame
ui_->image_view->setImage(uCvMat2QImage(image)); ui_->image_view->setImage(uCvMat2QImage(image).mirrored(ui_->checkBox_mirror->isChecked(), false));
} }
void CalibrationDialog::restart() void CalibrationDialog::restart()
@@ -254,6 +258,7 @@ void CalibrationDialog::restart()
ui_->pushButton_calibrate->setEnabled(false); ui_->pushButton_calibrate->setEnabled(false);
ui_->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui_->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
ui_->checkBox_rectified->setEnabled(false); ui_->checkBox_rectified->setEnabled(false);
//ui_->pushButton_save->setEnabled(false);
ui_->progressBar_count->reset(); ui_->progressBar_count->reset();
ui_->progressBar_count->setMaximum(COUNT_MIN); ui_->progressBar_count->setMaximum(COUNT_MIN);
@@ -343,6 +348,34 @@ void CalibrationDialog::calibrate()
ui_->checkBox_rectified->setChecked(true); ui_->checkBox_rectified->setChecked(true);
ui_->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); ui_->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
ui_->pushButton_save->setEnabled(true);
}
}
void CalibrationDialog::save()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Export"), "calibration.yaml", "*.yaml");
if(!fileName.isEmpty())
{
cv::FileStorage fs(fileName.toStdString(), cv::FileStorage::WRITE);
// export in ROS calibration format
fs << "camera_matrix" << "{";
fs << "rows" << cameraMatrix_.rows;
fs << "cols" << cameraMatrix_.cols;
fs << "data" << std::vector<double>((double*)cameraMatrix_.data, ((double*)cameraMatrix_.data)+(cameraMatrix_.rows*cameraMatrix_.cols));
fs << "}";
fs << "distortion_coefficients" << "{";
fs << "rows" << distCoeffs_.rows;
fs << "cols" << distCoeffs_.cols;
fs << "data" << std::vector<double>((double*)distCoeffs_.data, ((double*)distCoeffs_.data)+(distCoeffs_.rows*distCoeffs_.cols));
fs << "}";
fs.release();
QMessageBox::information(this, tr("Export"), tr("Calibration file saved to \"%1\".").arg(fileName));
UINFO("Saved \"%s\"!", fileName.toStdString().c_str());
} }
} }
+225 -52
View File
@@ -51,17 +51,13 @@ void CloudViewer::mouseEventOccurred (const pcl::visualization::MouseEvent &even
if (event.getButton () == pcl::visualization::MouseEvent::LeftButton || if (event.getButton () == pcl::visualization::MouseEvent::LeftButton ||
event.getButton () == pcl::visualization::MouseEvent::MiddleButton) event.getButton () == pcl::visualization::MouseEvent::MiddleButton)
{ {
this->render(); // this will apply frustum this->update(); // this will apply frustum
} }
} }
CloudViewer::CloudViewer(QWidget *parent) : CloudViewer::CloudViewer(QWidget *parent) :
QVTKWidget(parent), QVTKWidget(parent),
#if defined(_WIN32) || defined(__APPLE__)
_visualizer(new pcl::visualization::PCLVisualizer("PCLVisualizer")),
#else
_visualizer(new pcl::visualization::PCLVisualizer("PCLVisualizer", false)), _visualizer(new pcl::visualization::PCLVisualizer("PCLVisualizer", false)),
#endif
_aLockCamera(0), _aLockCamera(0),
_aFollowCamera(0), _aFollowCamera(0),
_aResetCamera(0), _aResetCamera(0),
@@ -70,10 +66,14 @@ CloudViewer::CloudViewer(QWidget *parent) :
_aSetTrajectorySize(0), _aSetTrajectorySize(0),
_aClearTrajectory(0), _aClearTrajectory(0),
_aShowGrid(0), _aShowGrid(0),
_aSetGridCellCount(0),
_aSetGridCellSize(0),
_aSetBackgroundColor(0), _aSetBackgroundColor(0),
_menu(0), _menu(0),
_trajectory(new pcl::PointCloud<pcl::PointXYZ>), _trajectory(new pcl::PointCloud<pcl::PointXYZ>),
_maxTrajectorySize(100), _maxTrajectorySize(100),
_gridCellCount(50),
_gridCellSize(1),
_workingDirectory("."), _workingDirectory("."),
_backgroundColor(Qt::black) _backgroundColor(Qt::black)
{ {
@@ -81,9 +81,7 @@ CloudViewer::CloudViewer(QWidget *parent) :
this->SetRenderWindow(_visualizer->getRenderWindow()); this->SetRenderWindow(_visualizer->getRenderWindow());
#if !defined(_WIN32) && !defined(__APPLE__)
_visualizer->setupInteractor(this->GetInteractor(), this->GetRenderWindow()); _visualizer->setupInteractor(this->GetInteractor(), this->GetRenderWindow());
#endif
_visualizer->registerMouseCallback (&CloudViewer::mouseEventOccurred, *this, (void*)_visualizer); _visualizer->registerMouseCallback (&CloudViewer::mouseEventOccurred, *this, (void*)_visualizer);
_visualizer->setCameraPosition( _visualizer->setCameraPosition(
@@ -93,6 +91,8 @@ CloudViewer::CloudViewer(QWidget *parent) :
//setup menu/actions //setup menu/actions
createMenu(); createMenu();
setMouseTracking(false);
} }
CloudViewer::~CloudViewer() CloudViewer::~CloudViewer()
@@ -125,6 +125,8 @@ void CloudViewer::createMenu()
_aClearTrajectory = new QAction("Clear trajectory", this); _aClearTrajectory = new QAction("Clear trajectory", this);
_aShowGrid = new QAction("Show grid", this); _aShowGrid = new QAction("Show grid", this);
_aShowGrid->setCheckable(true); _aShowGrid->setCheckable(true);
_aSetGridCellCount = new QAction("Set cell count...", this);
_aSetGridCellSize = new QAction("Set cell size...", this);
_aSetBackgroundColor = new QAction("Set background color...", this); _aSetBackgroundColor = new QAction("Set background color...", this);
QMenu * cameraMenu = new QMenu("Camera", this); QMenu * cameraMenu = new QMenu("Camera", this);
@@ -144,11 +146,16 @@ void CloudViewer::createMenu()
trajectoryMenu->addAction(_aSetTrajectorySize); trajectoryMenu->addAction(_aSetTrajectorySize);
trajectoryMenu->addAction(_aClearTrajectory); trajectoryMenu->addAction(_aClearTrajectory);
QMenu * gridMenu = new QMenu("Grid", this);
gridMenu->addAction(_aShowGrid);
gridMenu->addAction(_aSetGridCellCount);
gridMenu->addAction(_aSetGridCellSize);
//menus //menus
_menu = new QMenu(this); _menu = new QMenu(this);
_menu->addMenu(cameraMenu); _menu->addMenu(cameraMenu);
_menu->addMenu(trajectoryMenu); _menu->addMenu(trajectoryMenu);
_menu->addAction(_aShowGrid); _menu->addMenu(gridMenu);
_menu->addAction(_aSetBackgroundColor); _menu->addAction(_aSetBackgroundColor);
} }
@@ -160,7 +167,7 @@ bool CloudViewer::updateCloudPose(
{ {
UDEBUG("Updating pose %s to %s", id.c_str(), pose.prettyPrint().c_str()); UDEBUG("Updating pose %s to %s", id.c_str(), pose.prettyPrint().c_str());
if(_addedClouds.find(id).value() == pose || if(_addedClouds.find(id).value() == pose ||
_visualizer->updatePointCloudPose(id, util3d::transformToEigen3f(pose))) _visualizer->updatePointCloudPose(id, pose.toEigen3f()))
{ {
_addedClouds.find(id).value() = pose; _addedClouds.find(id).value() = pose;
return true; return true;
@@ -243,7 +250,7 @@ bool CloudViewer::addCloud(
if(!_addedClouds.contains(id)) if(!_addedClouds.contains(id))
{ {
Eigen::Vector4f origin(pose.x(), pose.y(), pose.z(), 0.0f); Eigen::Vector4f origin(pose.x(), pose.y(), pose.z(), 0.0f);
Eigen::Quaternionf orientation = Eigen::Quaternionf(util3d::transformToEigen3f(pose).rotation()); Eigen::Quaternionf orientation = Eigen::Quaternionf(pose.toEigen3f().rotation());
// add random color channel // add random color channel
pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>::Ptr colorHandler; pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>::Ptr colorHandler;
@@ -323,7 +330,7 @@ bool CloudViewer::addCloudMesh(
UDEBUG("Adding %s with %d points and %d polygons", id.c_str(), (int)cloud->size(), (int)polygons.size()); 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)) if(_visualizer->addPolygonMesh<pcl::PointXYZRGB>(cloud, polygons, id))
{ {
_visualizer->updatePointCloudPose(id, util3d::transformToEigen3f(pose)); _visualizer->updatePointCloudPose(id, pose.toEigen3f());
_addedClouds.insert(id, pose); _addedClouds.insert(id, pose);
return true; return true;
} }
@@ -341,7 +348,7 @@ bool CloudViewer::addCloudMesh(
UDEBUG("Adding %s with %d polygons", id.c_str(), (int)mesh->polygons.size()); UDEBUG("Adding %s with %d polygons", id.c_str(), (int)mesh->polygons.size());
if(_visualizer->addPolygonMesh(*mesh, id)) if(_visualizer->addPolygonMesh(*mesh, id))
{ {
_visualizer->updatePointCloudPose(id, util3d::transformToEigen3f(pose)); _visualizer->updatePointCloudPose(id, pose.toEigen3f());
_addedClouds.insert(id, pose); _addedClouds.insert(id, pose);
return true; return true;
} }
@@ -480,12 +487,22 @@ void CloudViewer::removeAllGraphs()
_graphes.clear(); _graphes.clear();
} }
bool CloudViewer::isTrajectoryShown() const
{
return _aShowTrajectory->isChecked();
}
unsigned int CloudViewer::getTrajectorySize() const
{
return _maxTrajectorySize;
}
void CloudViewer::setTrajectoryShown(bool shown) void CloudViewer::setTrajectoryShown(bool shown)
{ {
_aShowTrajectory->setChecked(shown); _aShowTrajectory->setChecked(shown);
} }
void CloudViewer::setTrajectorySize(int value) void CloudViewer::setTrajectorySize(unsigned int value)
{ {
_maxTrajectorySize = value; _maxTrajectorySize = value;
} }
@@ -494,8 +511,7 @@ void CloudViewer::clearTrajectory()
{ {
_trajectory->clear(); _trajectory->clear();
_visualizer->removeShape("trajectory"); _visualizer->removeShape("trajectory");
_lastPose.setNull(); this->update();
this->render();
} }
void CloudViewer::removeAllClouds() void CloudViewer::removeAllClouds()
@@ -521,11 +537,46 @@ bool CloudViewer::getPose(const std::string & id, Transform & pose)
return false; return false;
} }
void CloudViewer::updateCameraPosition(const Transform & pose) Transform CloudViewer::getTargetPose() const
{
if(_lastPose.isNull())
{
return Transform::getIdentity();
}
return _lastPose;
}
void CloudViewer::getCameraPosition(
float & x, float & y, float & z,
float & focalX, float & focalY, float & focalZ,
float & upX, float & upY, float & upZ) const
{
std::vector<pcl::visualization::Camera> cameras;
_visualizer->getCameras(cameras);
x = cameras.front().pos[0];
y = cameras.front().pos[1];
z = cameras.front().pos[2];
focalX = cameras.front().focal[0];
focalY = cameras.front().focal[1];
focalZ = cameras.front().focal[2];
upX = cameras.front().view[0];
upY = cameras.front().view[1];
upZ = cameras.front().view[2];
}
void CloudViewer::setCameraPosition(
float x, float y, float z,
float focalX, float focalY, float focalZ,
float upX, float upY, float upZ)
{
_visualizer->setCameraPosition(x,y,z, focalX,focalY,focalX, upX,upY,upZ);
}
void CloudViewer::updateCameraTargetPosition(const Transform & pose)
{ {
if(!pose.isNull()) if(!pose.isNull())
{ {
Eigen::Affine3f m = util3d::transformToEigen3f(pose); Eigen::Affine3f m = pose.toEigen3f();
Eigen::Vector3f pos = m.translation(); Eigen::Vector3f pos = m.translation();
Eigen::Vector3f lastPos(0,0,0); Eigen::Vector3f lastPos(0,0,0);
@@ -614,9 +665,9 @@ void CloudViewer::updateCameraPosition(const Transform & pose)
cameras.front().focal[1] = Fp.y(); cameras.front().focal[1] = Fp.y();
cameras.front().focal[2] = Fp.z(); cameras.front().focal[2] = Fp.z();
//FIXME: the view up is not set properly... //FIXME: the view up is not set properly...
cameras.front().view[0] = Fp[8]; cameras.front().view[0] = _aLockViewZ->isChecked()?0:Fp[8];
cameras.front().view[1] = Fp[9]; cameras.front().view[1] = _aLockViewZ->isChecked()?0:Fp[9];
cameras.front().view[2] = Fp[10]; cameras.front().view[2] = _aLockViewZ->isChecked()?1:Fp[10];
} }
#if PCL_VERSION_COMPARE(>=, 1, 7, 2) #if PCL_VERSION_COMPARE(>=, 1, 7, 2)
@@ -636,28 +687,6 @@ void CloudViewer::updateCameraPosition(const Transform & pose)
_lastPose = pose; _lastPose = pose;
} }
void CloudViewer::render()
{
// camera view up z locked?
if(_aLockViewZ->isChecked())
{
std::vector<pcl::visualization::Camera> cameras;
_visualizer->getCameras(cameras);
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]);
}
this->GetRenderWindow()->Render();
}
const QColor & CloudViewer::getBackgroundColor() const const QColor & CloudViewer::getBackgroundColor() const
{ {
return _backgroundColor; return _backgroundColor;
@@ -752,12 +781,73 @@ void CloudViewer::setGridShown(bool shown)
} }
} }
bool CloudViewer::isCameraTargetLocked() const
{
return _aLockCamera->isChecked();
}
bool CloudViewer::isCameraTargetFollow() const
{
return _aFollowCamera->isChecked();
}
bool CloudViewer::isCameraFree() const
{
return !_aFollowCamera->isChecked() && !_aLockCamera->isChecked();
}
bool CloudViewer::isCameraLockZ() const
{
return _aLockViewZ->isChecked();
}
bool CloudViewer::isGridShown() const
{
return _aShowGrid->isChecked();
}
unsigned int CloudViewer::getGridCellCount() const
{
return _gridCellCount;
}
float CloudViewer::getGridCellSize() const
{
return _gridCellSize;
}
void CloudViewer::setGridCellCount(unsigned int count)
{
if(count > 0)
{
_gridCellCount = count;
if(_aShowGrid->isChecked())
{
this->removeGrid();
this->addGrid();
}
}
else
{
UERROR("Cannot set grid cell count < 1, count=%d", count);
}
}
void CloudViewer::setGridCellSize(float size)
{
if(size > 0)
{
_gridCellSize = size;
if(_aShowGrid->isChecked())
{
this->removeGrid();
this->addGrid();
}
}
else
{
UERROR("Cannot set grid cell size <= 0, value=%f", size);
}
}
void CloudViewer::addGrid() void CloudViewer::addGrid()
{ {
if(_gridLines.empty()) if(_gridLines.empty())
{ {
float cellSize = 1.0f; float cellSize = _gridCellSize;
int cellCount = 50; int cellCount = _gridCellCount;
double r=0.5; double r=0.5;
double g=0.5; double g=0.5;
double b=0.5; double b=0.5;
@@ -927,7 +1017,9 @@ void CloudViewer::keyPressEvent(QKeyEvent * event)
cameras.front().focal[0], cameras.front().focal[1], cameras.front().focal[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]); cameras.front().view[0], cameras.front().view[1], cameras.front().view[2]);
render(); update();
emit configChanged();
} }
else else
{ {
@@ -935,12 +1027,47 @@ void CloudViewer::keyPressEvent(QKeyEvent * event)
} }
} }
void CloudViewer::mousePressEvent(QMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
event->accept();
}
else
{
QVTKWidget::mousePressEvent(event);
}
}
void CloudViewer::mouseMoveEvent(QMouseEvent * event)
{
QVTKWidget::mouseMoveEvent(event);
// camera view up z locked?
if(_aLockViewZ->isChecked())
{
std::vector<pcl::visualization::Camera> cameras;
_visualizer->getCameras(cameras);
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]);
}
emit configChanged();
}
void CloudViewer::contextMenuEvent(QContextMenuEvent * event) void CloudViewer::contextMenuEvent(QContextMenuEvent * event)
{ {
QAction * a = _menu->exec(event->globalPos()); QAction * a = _menu->exec(event->globalPos());
if(a) if(a)
{ {
handleAction(a); handleAction(a);
emit configChanged();
} }
} }
@@ -961,11 +1088,32 @@ void CloudViewer::handleAction(QAction * a)
} }
else if(a == _aResetCamera) else if(a == _aResetCamera)
{ {
_visualizer->setCameraPosition( if((_aFollowCamera->isChecked() || _aLockCamera->isChecked()) && !_lastPose.isNull())
-1, 0, 0, {
0, 0, 0, // reset relative to last current pose
0, 0, 1); if(_aLockViewZ->isChecked())
this->render(); {
_visualizer->setCameraPosition(
_lastPose.x()-1, _lastPose.y(), _lastPose.z(),
_lastPose.x(), _lastPose.y(), _lastPose.z(),
0, 0, 1);
}
else
{
_visualizer->setCameraPosition(
_lastPose.x()-1, _lastPose.y(), _lastPose.z(),
_lastPose.x(), _lastPose.y(), _lastPose.z(),
_lastPose.r31(), _lastPose.r32(), _lastPose.r33());
}
}
else
{
_visualizer->setCameraPosition(
-1, 0, 0,
0, 0, 0,
0, 0, 1);
}
this->update();
} }
else if(a == _aShowGrid) else if(a == _aShowGrid)
{ {
@@ -978,14 +1126,39 @@ void CloudViewer::handleAction(QAction * a)
this->removeGrid(); this->removeGrid();
} }
this->render(); this->update();
}
else if(a == _aSetGridCellCount)
{
bool ok;
int value = QInputDialog::getInt(this, tr("Set grid cell count"), tr("Count"), _gridCellCount, 1, 10000, 10, &ok);
if(ok)
{
this->setGridCellCount(value);
}
}
else if(a == _aSetGridCellSize)
{
bool ok;
double value = QInputDialog::getDouble(this, tr("Set grid cell size"), tr("Size (m)"), _gridCellSize, 0.01, 10, 2, &ok);
if(ok)
{
this->setGridCellSize(value);
}
} }
else if(a == _aSetBackgroundColor) else if(a == _aSetBackgroundColor)
{ {
QColor color = Qt::black; QColor color = this->getBackgroundColor();
color = QColorDialog::getColor(color, this); color = QColorDialog::getColor(color, this);
this->setBackgroundColor(color); this->setBackgroundColor(color);
} }
else if(a == _aLockViewZ)
{
if(_aLockViewZ->isChecked())
{
this->update();
}
}
} }
} /* namespace rtabmap */ } /* namespace rtabmap */
+1 -1
View File
@@ -60,7 +60,7 @@ bool DataRecorder::init(const QString & path, bool recordInRAM)
ParametersMap customParameters; ParametersMap customParameters;
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
customParameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), "-1")); // desactivate keypoints extraction customParameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), "-1")); // desactivate keypoints extraction
customParameters.insert(ParametersPair(Parameters::kMemImageKept(), "true")); // to keep images customParameters.insert(ParametersPair(Parameters::kMemBinDataKept(), "true")); // to keep images
if(!recordInRAM) if(!recordInRAM)
{ {
customParameters.insert(ParametersPair(Parameters::kDbSqlite3InMemory(), "false")); // to keep images customParameters.insert(ParametersPair(Parameters::kDbSqlite3InMemory(), "false")); // to keep images
+46 -41
View File
@@ -49,6 +49,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/Signature.h" #include "rtabmap/core/Signature.h"
#include "rtabmap/core/Features2d.h" #include "rtabmap/core/Features2d.h"
#include "rtabmap/core/Compression.h"
#include "rtabmap/core/Graph.h"
#include "rtabmap/gui/DataRecorder.h" #include "rtabmap/gui/DataRecorder.h"
#include "rtabmap/core/SensorData.h" #include "rtabmap/core/SensorData.h"
#include "ExportDialog.h" #include "ExportDialog.h"
@@ -239,14 +241,14 @@ void DatabaseViewer::closeEvent(QCloseEvent* event)
// Added links // Added links
for(std::multimap<int, rtabmap::Link>::iterator iter=linksAdded_.begin(); iter!=linksAdded_.end(); ++iter) for(std::multimap<int, rtabmap::Link>::iterator iter=linksAdded_.begin(); iter!=linksAdded_.end(); ++iter)
{ {
std::multimap<int, rtabmap::Link>::iterator refinedIter = util3d::findLink(linksRefined_, iter->second.from(), iter->second.to()); std::multimap<int, rtabmap::Link>::iterator refinedIter = rtabmap::graph::findLink(linksRefined_, iter->second.from(), iter->second.to());
if(refinedIter != linksRefined_.end()) if(refinedIter != linksRefined_.end())
{ {
memory_->addLoopClosureLink(refinedIter->second.to(), refinedIter->second.from(), refinedIter->second.transform(), refinedIter->second.type(), refinedIter->second.variance()); memory_->addLink(refinedIter->second.to(), refinedIter->second.from(), refinedIter->second.transform(), refinedIter->second.type(), refinedIter->second.variance());
} }
else else
{ {
memory_->addLoopClosureLink(iter->second.to(), iter->second.from(), iter->second.transform(), iter->second.type(), iter->second.variance()); memory_->addLink(iter->second.to(), iter->second.from(), iter->second.transform(), iter->second.type(), iter->second.variance());
} }
} }
@@ -255,15 +257,14 @@ void DatabaseViewer::closeEvent(QCloseEvent* event)
{ {
if(!containsLink(linksAdded_, iter->second.from(), iter->second.to())) if(!containsLink(linksAdded_, iter->second.from(), iter->second.to()))
{ {
memory_->rejectLoopClosure(iter->second.to(), iter->second.from()); memory_->updateLink(iter->second.from(), iter->second.to(), iter->second.transform(), iter->second.variance());
memory_->addLoopClosureLink(iter->second.to(), iter->second.from(), iter->second.transform(), iter->second.type(), iter->second.variance());
} }
} }
// Rejected links // Rejected links
for(std::multimap<int, rtabmap::Link>::iterator iter=linksRemoved_.begin(); iter!=linksRemoved_.end(); ++iter) for(std::multimap<int, rtabmap::Link>::iterator iter=linksRemoved_.begin(); iter!=linksRemoved_.end(); ++iter)
{ {
memory_->rejectLoopClosure(iter->second.to(), iter->second.from()); memory_->removeLink(iter->second.to(), iter->second.from());
} }
} }
@@ -373,7 +374,7 @@ void DatabaseViewer::extractImages()
cv::Mat compressedRgb = memory_->getImageCompressed(id); cv::Mat compressedRgb = memory_->getImageCompressed(id);
if(!compressedRgb.empty()) if(!compressedRgb.empty())
{ {
cv::Mat imageMat = rtabmap::util3d::uncompressImage(compressedRgb); cv::Mat imageMat = rtabmap::uncompressImage(compressedRgb);
cv::imwrite(QString("%1/%2.png").arg(path).arg(id).toStdString(), imageMat); cv::imwrite(QString("%1/%2.png").arg(path).arg(id).toStdString(), imageMat);
UINFO(QString("Saved %1/%2.png").arg(path).arg(id).toStdString().c_str()); UINFO(QString("Saved %1/%2.png").arg(path).arg(id).toStdString().c_str());
} }
@@ -414,6 +415,10 @@ void DatabaseViewer::updateIds()
if((!iter->second.isNull() && nullPoses) || if((!iter->second.isNull() && nullPoses) ||
(iter->second.isNull() && !nullPoses)) (iter->second.isNull() && !nullPoses))
{ {
if(iter->second.isNull())
{
UWARN("Pose %d is null!", iter->first);
}
UWARN("Mixed valid and null poses! Ignoring graph..."); UWARN("Mixed valid and null poses! Ignoring graph...");
poses_.clear(); poses_.clear();
links_.clear(); links_.clear();
@@ -569,7 +574,7 @@ void DatabaseViewer::generateTOROGraph()
QString path = QFileDialog::getSaveFileName(this, tr("Save File"), pathDatabase_+"/constraints" + QString::number(id) + ".graph", tr("TORO file (*.graph)")); QString path = QFileDialog::getSaveFileName(this, tr("Save File"), pathDatabase_+"/constraints" + QString::number(id) + ".graph", tr("TORO file (*.graph)"));
if(!path.isEmpty()) if(!path.isEmpty())
{ {
rtabmap::util3d::saveTOROGraph(path.toStdString(), uValueAt(graphes_, id), links); rtabmap::graph::saveTOROGraph(path.toStdString(), uValueAt(graphes_, id), links);
} }
} }
} }
@@ -801,7 +806,7 @@ void DatabaseViewer::detectMoreLoopClosures()
for(int n=0; n<iterations; ++n) for(int n=0; n<iterations; ++n)
{ {
UINFO("iteration %d/%d", n+1, iterations); UINFO("iteration %d/%d", n+1, iterations);
std::multimap<int, int> clusters = util3d::radiusPosesClustering( std::multimap<int, int> clusters = rtabmap::graph::radiusPosesClustering(
optimizedPoses, optimizedPoses,
ui_->doubleSpinBox_detectMore_radius->value(), ui_->doubleSpinBox_detectMore_radius->value(),
ui_->doubleSpinBox_detectMore_angle->value()*CV_PI/180.0); ui_->doubleSpinBox_detectMore_angle->value()*CV_PI/180.0);
@@ -1129,7 +1134,7 @@ void DatabaseViewer::update(int value,
ui_->horizontalSlider_loops->setValue(0); ui_->horizontalSlider_loops->setValue(0);
ui_->horizontalSlider_neighbors->setValue(0); ui_->horizontalSlider_neighbors->setValue(0);
ui_->constraintsViewer->removeAllClouds(); ui_->constraintsViewer->removeAllClouds();
ui_->constraintsViewer->render(); ui_->constraintsViewer->update();
ui_->horizontalSlider_loops->blockSignals(false); ui_->horizontalSlider_loops->blockSignals(false);
ui_->horizontalSlider_neighbors->blockSignals(false); ui_->horizontalSlider_neighbors->blockSignals(false);
} }
@@ -1216,7 +1221,7 @@ void DatabaseViewer::updateStereo(const Signature * data)
if(pcl::isFinite(tmpPt)) if(pcl::isFinite(tmpPt))
{ {
pt = pcl::transformPoint(tmpPt, util3d::transformToEigen3f(data->getLocalTransform())); pt = pcl::transformPoint(tmpPt, data->getLocalTransform().toEigen3f());
if(fabs(pt.x) > 2 || fabs(pt.y) > 2 || fabs(pt.z) > 2) if(fabs(pt.x) > 2 || fabs(pt.y) > 2 || fabs(pt.z) > 2)
{ {
status[i] = 100; //blue status[i] = 100; //blue
@@ -1240,9 +1245,9 @@ void DatabaseViewer::updateStereo(const Signature * data)
UINFO("correspondences = %d/%d (%f) (time kpt=%fs flow=%fs)", UINFO("correspondences = %d/%d (%f) (time kpt=%fs flow=%fs)",
(int)cloud->size(), (int)leftCorners.size(), float(cloud->size())/float(leftCorners.size()), timeKpt, timeFlow); (int)cloud->size(), (int)leftCorners.size(), float(cloud->size())/float(leftCorners.size()), timeKpt, timeFlow);
ui_->stereoViewer->updateCameraPosition(Transform::getIdentity()); ui_->stereoViewer->updateCameraTargetPosition(Transform::getIdentity());
ui_->stereoViewer->addOrUpdateCloud("stereo", cloud); ui_->stereoViewer->addOrUpdateCloud("stereo", cloud);
ui_->stereoViewer->render(); ui_->stereoViewer->update();
std::vector<cv::KeyPoint> rightKpts; std::vector<cv::KeyPoint> rightKpts;
cv::KeyPoint::convert(rightCorners, rightKpts); cv::KeyPoint::convert(rightCorners, rightKpts);
@@ -1413,7 +1418,7 @@ void DatabaseViewer::updateConstraintView(const rtabmap::Link & linkIn,
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudTo, const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudTo,
bool updateImageSliders) bool updateImageSliders)
{ {
std::multimap<int, Link>::iterator iter = util3d::findLink(linksRefined_, linkIn.from(), linkIn.to()); std::multimap<int, Link>::iterator iter = rtabmap::graph::findLink(linksRefined_, linkIn.from(), linkIn.to());
rtabmap::Link link = linkIn; rtabmap::Link link = linkIn;
if(iter != linksRefined_.end()) if(iter != linksRefined_.end())
{ {
@@ -1655,10 +1660,10 @@ void DatabaseViewer::updateConstraintView(const rtabmap::Link & linkIn,
} }
//update cordinate //update cordinate
ui_->constraintsViewer->updateCameraPosition(t); ui_->constraintsViewer->updateCameraTargetPosition(t);
ui_->constraintsViewer->clearTrajectory(); ui_->constraintsViewer->clearTrajectory();
ui_->constraintsViewer->render(); ui_->constraintsViewer->update();
} }
// update buttons // update buttons
@@ -1696,7 +1701,7 @@ void DatabaseViewer::updateConstraintButtons()
//check for modified link //check for modified link
bool modified = false; bool modified = false;
std::multimap<int, Link>::iterator iter = util3d::findLink(linksRefined_, currentLink.from(), currentLink.to()); std::multimap<int, Link>::iterator iter = rtabmap::graph::findLink(linksRefined_, currentLink.from(), currentLink.to());
if(iter != linksRefined_.end()) if(iter != linksRefined_.end())
{ {
currentLink = iter->second; currentLink = iter->second;
@@ -1726,7 +1731,7 @@ void DatabaseViewer::sliderIterationsValueChanged(int value)
if(!data.getLaserScanCompressed().empty()) if(!data.getLaserScanCompressed().empty())
{ {
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud; pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
cv::Mat laserScan = rtabmap::util3d::uncompressData(data.getLaserScanCompressed()); cv::Mat laserScan = rtabmap::uncompressData(data.getLaserScanCompressed());
cloud = rtabmap::util3d::laserScanToPointCloud(laserScan); cloud = rtabmap::util3d::laserScanToPointCloud(laserScan);
scans_.insert(std::make_pair(ids_.at(i), cloud)); scans_.insert(std::make_pair(ids_.at(i), cloud));
} }
@@ -1786,8 +1791,8 @@ void DatabaseViewer::updateGraphView()
graphes_.push_back(poses_); graphes_.push_back(poses_);
ui_->actionGenerate_TORO_graph_graph->setEnabled(true); ui_->actionGenerate_TORO_graph_graph->setEnabled(true);
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_); std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
std::map<int, int> depthGraph = util3d::generateDepthGraph(links, ui_->spinBox_optimizationsFrom->value(), 0); std::map<int, int> depthGraph = rtabmap::graph::generateDepthGraph(links, ui_->spinBox_optimizationsFrom->value(), 0);
util3d::optimizeTOROGraph( rtabmap::graph::optimizeTOROGraph(
depthGraph, depthGraph,
poses_, poses_,
links, finalPoses, links, finalPoses,
@@ -1813,21 +1818,21 @@ void DatabaseViewer::updateGraphView()
Link DatabaseViewer::findActiveLink(int from, int to) Link DatabaseViewer::findActiveLink(int from, int to)
{ {
Link link; Link link;
std::multimap<int, Link>::iterator findIter = util3d::findLink(linksRefined_, from ,to); std::multimap<int, Link>::iterator findIter = rtabmap::graph::findLink(linksRefined_, from ,to);
if(findIter != linksRefined_.end()) if(findIter != linksRefined_.end())
{ {
link = findIter->second; link = findIter->second;
} }
else else
{ {
findIter = util3d::findLink(linksAdded_, from ,to); findIter = rtabmap::graph::findLink(linksAdded_, from ,to);
if(findIter != linksAdded_.end()) if(findIter != linksAdded_.end())
{ {
link = findIter->second; link = findIter->second;
} }
else if(!containsLink(linksRemoved_, from ,to)) else if(!containsLink(linksRemoved_, from ,to))
{ {
findIter = util3d::findLink(links_, from ,to); findIter = rtabmap::graph::findLink(links_, from ,to);
if(findIter != links_.end()) if(findIter != links_.end())
{ {
link = findIter->second; link = findIter->second;
@@ -1839,7 +1844,7 @@ Link DatabaseViewer::findActiveLink(int from, int to)
bool DatabaseViewer::containsLink(std::multimap<int, Link> & links, int from, int to) bool DatabaseViewer::containsLink(std::multimap<int, Link> & links, int from, int to)
{ {
return util3d::findLink(links, from, to) != links.end(); return rtabmap::graph::findLink(links, from, to) != links.end();
} }
void DatabaseViewer::refineConstraint() void DatabaseViewer::refineConstraint()
@@ -1897,8 +1902,8 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
if(ui_->checkBox_icp_2d->isChecked()) if(ui_->checkBox_icp_2d->isChecked())
{ {
//2D //2D
cv::Mat oldLaserScan = util3d::uncompressData(dataFrom.getLaserScanCompressed()); cv::Mat oldLaserScan = rtabmap::uncompressData(dataFrom.getLaserScanCompressed());
cv::Mat newLaserScan = util3d::uncompressData(dataTo.getLaserScanCompressed()); cv::Mat newLaserScan = rtabmap::uncompressData(dataTo.getLaserScanCompressed());
if(!oldLaserScan.empty() && !newLaserScan.empty()) if(!oldLaserScan.empty() && !newLaserScan.empty())
{ {
@@ -1928,13 +1933,13 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
else else
{ {
//3D //3D
cv::Mat depthA = rtabmap::util3d::uncompressImage(dataFrom.getDepthCompressed()); cv::Mat depthA = rtabmap::uncompressImage(dataFrom.getDepthCompressed());
cv::Mat depthB = rtabmap::util3d::uncompressImage(dataTo.getDepthCompressed()); cv::Mat depthB = rtabmap::uncompressImage(dataTo.getDepthCompressed());
if(depthA.type() == CV_8UC1) if(depthA.type() == CV_8UC1)
{ {
cv::Mat leftMono; cv::Mat leftMono;
cv::Mat left = rtabmap::util3d::uncompressImage(dataFrom.getImageCompressed()); cv::Mat left = rtabmap::uncompressImage(dataFrom.getImageCompressed());
if(left.channels() > 1) if(left.channels() > 1)
{ {
cv::cvtColor(left, leftMono, CV_BGR2GRAY); cv::cvtColor(left, leftMono, CV_BGR2GRAY);
@@ -1967,7 +1972,7 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
if(depthB.type() == CV_8UC1) if(depthB.type() == CV_8UC1)
{ {
cv::Mat leftMono; cv::Mat leftMono;
cv::Mat left = rtabmap::util3d::uncompressImage(dataTo.getImageCompressed()); cv::Mat left = rtabmap::uncompressImage(dataTo.getImageCompressed());
if(left.channels() > 1) if(left.channels() > 1)
{ {
cv::cvtColor(left, leftMono, CV_BGR2GRAY); cv::cvtColor(left, leftMono, CV_BGR2GRAY);
@@ -2266,7 +2271,7 @@ bool DatabaseViewer::addConstraint(int from, int to, bool silent, bool updateGra
// We are 2D here, make sure the guess has only YAW rotation // We are 2D here, make sure the guess has only YAW rotation
float x,y,z,r,p,yaw; float x,y,z,r,p,yaw;
t.getTranslationAndEulerAngles(x,y,z, r,p,yaw); t.getTranslationAndEulerAngles(x,y,z, r,p,yaw);
t = util3d::transformFromEigen3f(pcl::getTransformation(x,y,0, 0, 0, yaw)); t = Transform::fromEigen3f(pcl::getTransformation(x,y,0, 0, 0, yaw));
} }
// transform is valid, make a link // transform is valid, make a link
@@ -2277,7 +2282,7 @@ bool DatabaseViewer::addConstraint(int from, int to, bool silent, bool updateGra
else if(containsLink(linksRemoved_, from, to)) else if(containsLink(linksRemoved_, from, to))
{ {
//simply remove from linksRemoved //simply remove from linksRemoved
linksRemoved_.erase(util3d::findLink(linksRemoved_, from, to)); linksRemoved_.erase(rtabmap::graph::findLink(linksRemoved_, from, to));
updateSlider = true; updateSlider = true;
} }
@@ -2310,19 +2315,19 @@ void DatabaseViewer::resetConstraint()
} }
std::multimap<int, Link>::iterator iter = util3d::findLink(linksRefined_, from, to); std::multimap<int, Link>::iterator iter = rtabmap::graph::findLink(linksRefined_, from, to);
if(iter != linksRefined_.end()) if(iter != linksRefined_.end())
{ {
linksRefined_.erase(iter); linksRefined_.erase(iter);
this->updateGraphView(); this->updateGraphView();
} }
iter = util3d::findLink(links_, from, to); iter = rtabmap::graph::findLink(links_, from, to);
if(iter != links_.end()) if(iter != links_.end())
{ {
this->updateConstraintView(iter->second); this->updateConstraintView(iter->second);
} }
iter = util3d::findLink(linksAdded_, from, to); iter = rtabmap::graph::findLink(linksAdded_, from, to);
if(iter != linksAdded_.end()) if(iter != linksAdded_.end())
{ {
this->updateConstraintView(iter->second); this->updateConstraintView(iter->second);
@@ -2350,7 +2355,7 @@ void DatabaseViewer::rejectConstraint()
// find the original one // find the original one
std::multimap<int, Link>::iterator iter; std::multimap<int, Link>::iterator iter;
iter = util3d::findLink(links_, from, to); iter = rtabmap::graph::findLink(links_, from, to);
if(iter != links_.end()) if(iter != links_.end())
{ {
if(iter->second.type() == Link::kNeighbor) if(iter->second.type() == Link::kNeighbor)
@@ -2363,13 +2368,13 @@ void DatabaseViewer::rejectConstraint()
} }
// remove from refined and added // remove from refined and added
iter = util3d::findLink(linksRefined_, from, to); iter = rtabmap::graph::findLink(linksRefined_, from, to);
if(iter != linksRefined_.end()) if(iter != linksRefined_.end())
{ {
linksRefined_.erase(iter); linksRefined_.erase(iter);
removed = true; removed = true;
} }
iter = util3d::findLink(linksAdded_, from, to); iter = rtabmap::graph::findLink(linksAdded_, from, to);
if(iter != linksAdded_.end()) if(iter != linksAdded_.end())
{ {
linksAdded_.erase(iter); linksAdded_.erase(iter);
@@ -2392,7 +2397,7 @@ std::multimap<int, rtabmap::Link> DatabaseViewer::updateLinksWithModifications(
{ {
std::multimap<int, rtabmap::Link>::iterator findIter; std::multimap<int, rtabmap::Link>::iterator findIter;
findIter = util3d::findLink(linksRemoved_, iter->second.from(), iter->second.to()); findIter = rtabmap::graph::findLink(linksRemoved_, iter->second.from(), iter->second.to());
if(findIter != linksRemoved_.end()) if(findIter != linksRemoved_.end())
{ {
if(!(iter->second.from() == findIter->second.from() && if(!(iter->second.from() == findIter->second.from() &&
@@ -2410,7 +2415,7 @@ std::multimap<int, rtabmap::Link> DatabaseViewer::updateLinksWithModifications(
} }
} }
findIter = util3d::findLink(linksRefined_, iter->second.from(), iter->second.to()); findIter = rtabmap::graph::findLink(linksRefined_, iter->second.from(), iter->second.to());
if(findIter!=linksRefined_.end()) if(findIter!=linksRefined_.end())
{ {
if(iter->second.from() == findIter->second.from() && if(iter->second.from() == findIter->second.from() &&
@@ -2488,7 +2493,7 @@ void DatabaseViewer::updateLoopClosuresSlider(int from, int to)
{ {
ui_->horizontalSlider_loops->setEnabled(false); ui_->horizontalSlider_loops->setEnabled(false);
ui_->constraintsViewer->removeAllClouds(); ui_->constraintsViewer->removeAllClouds();
ui_->constraintsViewer->render(); ui_->constraintsViewer->update();
updateConstraintButtons(); updateConstraintButtons();
} }
} }
+2 -2
View File
@@ -82,10 +82,10 @@ void DetailedProgressDialog::setAutoClose(bool on, int delayedClosingTimeSec)
_closeWhenDoneCheckBox->setChecked(on); _closeWhenDoneCheckBox->setChecked(on);
} }
void DetailedProgressDialog::appendText(const QString & text) void DetailedProgressDialog::appendText(const QString & text, const QColor & color)
{ {
_text->setText(text); _text->setText(text);
QString html = tr("<html><font color=\"#999999\">%1 </font>%2</html>").arg(QTime::currentTime().toString("HH:mm:ss")).arg(text); QString html = tr("<html><font color=\"#999999\">%1 </font><font color=\"%2\">%3</font></html>").arg(QTime::currentTime().toString("HH:mm:ss")).arg(color.name()).arg(text);
_detailedText->append(html); _detailedText->append(html);
_detailedText->ensureCursorVisible(); _detailedText->ensureCursorVisible();
} }
+1 -1
View File
@@ -56,7 +56,7 @@ protected:
virtual void closeEvent(QCloseEvent * event); virtual void closeEvent(QCloseEvent * event);
public slots: public slots:
void appendText(const QString & text); void appendText(const QString & text ,const QColor & color = Qt::black);
void incrementStep(); void incrementStep();
void clear(); void clear();
void resetProgress(); void resetProgress();
+99 -3
View File
@@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ExportCloudsDialog.h" #include "ExportCloudsDialog.h"
#include "ui_exportCloudsDialog.h" #include "ui_exportCloudsDialog.h"
#include <QtGui/QFileDialog> #include <QtGui/QPushButton>
namespace rtabmap { namespace rtabmap {
@@ -37,6 +37,21 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
{ {
_ui = new Ui_ExportCloudsDialog(); _ui = new Ui_ExportCloudsDialog();
_ui->setupUi(this); _ui->setupUi(this);
connect(_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(restoreDefaults()));
connect(_ui->groupBox_assemble, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_voxelSize_assembled, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->groupBox_regenerate, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->spinBox_decimation, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_voxelSize, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_maxDepth, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_binary, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->groupBox_mls, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_mlsRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->groupBox_gp3, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->spinBox_normalKSearch, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_gp3Radius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
} }
ExportCloudsDialog::~ExportCloudsDialog() ExportCloudsDialog::~ExportCloudsDialog()
@@ -44,18 +59,49 @@ ExportCloudsDialog::~ExportCloudsDialog()
delete _ui; delete _ui;
} }
void ExportCloudsDialog::restoreDefaults()
{
setAssemble(true);
setAssembleVoxel(0.005);
if(_ui->groupBox_regenerate->isEnabled())
{
setGenerate(true);
}
setGenerateDecimation(1);
setGenerateVoxel(0.005);
setGenerateMaxDepth(4);
setBinaryFile(true);
setMLS(false);
setMLSRadius(0.04);
setMesh(false);
setMeshNormalKSearch(20);
setMeshGp3Radius(0.04);
}
void ExportCloudsDialog::setSaveButton() void ExportCloudsDialog::setSaveButton()
{ {
_ui->buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Save); _ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(false);
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(true);
_ui->checkBox_binary->setVisible(true); _ui->checkBox_binary->setVisible(true);
} }
void ExportCloudsDialog::setOkButton() void ExportCloudsDialog::setOkButton()
{ {
_ui->buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); _ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(true);
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(false);
_ui->checkBox_binary->setVisible(false); _ui->checkBox_binary->setVisible(false);
} }
void ExportCloudsDialog::enableRegeneration(bool enabled)
{
if(!enabled)
{
_ui->groupBox_regenerate->setChecked(false);
}
_ui->groupBox_regenerate->setEnabled(enabled);
}
//getters
bool ExportCloudsDialog::getAssemble() const bool ExportCloudsDialog::getAssemble() const
{ {
return _ui->groupBox_assemble->isChecked(); return _ui->groupBox_assemble->isChecked();
@@ -116,5 +162,55 @@ double ExportCloudsDialog::getMeshGp3Radius() const
return _ui->doubleSpinBox_gp3Radius->value(); return _ui->doubleSpinBox_gp3Radius->value();
} }
//setters
void ExportCloudsDialog::setAssemble(bool on)
{
_ui->groupBox_assemble->setChecked(on);
}
void ExportCloudsDialog::setAssembleVoxel(double voxel)
{
_ui->doubleSpinBox_voxelSize_assembled->setValue(voxel);
}
void ExportCloudsDialog::setGenerate(bool on)
{
_ui->groupBox_regenerate->setChecked(on);
}
void ExportCloudsDialog::setGenerateDecimation(int decimation)
{
_ui->spinBox_decimation->setValue(decimation);
}
void ExportCloudsDialog::setGenerateVoxel(double voxel)
{
_ui->doubleSpinBox_voxelSize->setValue(voxel);
}
void ExportCloudsDialog::setGenerateMaxDepth(double maxDepth)
{
_ui->doubleSpinBox_maxDepth->setValue(maxDepth);
}
void ExportCloudsDialog::setBinaryFile(bool on)
{
_ui->checkBox_binary->setChecked(on);
}
void ExportCloudsDialog::setMLS(bool on)
{
_ui->groupBox_mls->setChecked(on);
}
void ExportCloudsDialog::setMLSRadius(double radius)
{
_ui->doubleSpinBox_mlsRadius->setValue(radius);
}
void ExportCloudsDialog::setMesh(bool on)
{
_ui->groupBox_gp3->setChecked(on);
}
void ExportCloudsDialog::setMeshNormalKSearch(int k)
{
_ui->spinBox_normalKSearch->setValue(k);
}
void ExportCloudsDialog::setMeshGp3Radius(double radius)
{
_ui->doubleSpinBox_gp3Radius->setValue(radius);
}
} }
+23
View File
@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtGui/QDialog> #include <QtGui/QDialog>
class Ui_ExportCloudsDialog; class Ui_ExportCloudsDialog;
class QAbstractButton;
namespace rtabmap { namespace rtabmap {
@@ -45,7 +46,9 @@ public:
void setSaveButton(); void setSaveButton();
void setOkButton(); void setOkButton();
void enableRegeneration(bool enabled);
//getters
bool getAssemble() const; bool getAssemble() const;
double getAssembleVoxel() const; double getAssembleVoxel() const;
bool getGenerate() const; bool getGenerate() const;
@@ -59,6 +62,26 @@ public:
int getMeshNormalKSearch() const; int getMeshNormalKSearch() const;
double getMeshGp3Radius() const; double getMeshGp3Radius() const;
//setters
void setAssemble(bool on);
void setAssembleVoxel(double voxel);
void setGenerate(bool on);
void setGenerateDecimation(int decimation);
void setGenerateVoxel(double voxel);
void setGenerateMaxDepth(double maxDepth);
void setBinaryFile(bool on);
void setMLS(bool on);
void setMLSRadius(double radius);
void setMesh(bool on);
void setMeshNormalKSearch(int k);
void setMeshGp3Radius(double radius);
signals:
void configChanged();
public slots:
void restoreDefaults();
private: private:
Ui_ExportCloudsDialog * _ui; Ui_ExportCloudsDialog * _ui;
}; };
+6
View File
@@ -40,6 +40,12 @@ ExportDialog::ExportDialog(QWidget * parent) :
connect(_ui->toolButton_path, SIGNAL(clicked()), this, SLOT(getPath())); connect(_ui->toolButton_path, SIGNAL(clicked()), this, SLOT(getPath()));
connect(_ui->spinBox_ignored, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_rgb, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_depth, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_depth2d, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_odom, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
_ui->lineEdit_path->setText(QDir::homePath()+QDir::separator()+"output.db"); _ui->lineEdit_path->setText(QDir::homePath()+QDir::separator()+"output.db");
} }
+3
View File
@@ -50,6 +50,9 @@ public:
bool isDepth2dExported() const; bool isDepth2dExported() const;
bool isOdomExported() const; bool isOdomExported() const;
signals:
void configChanged();
private slots: private slots:
void getPath(); void getPath();
+325 -115
View File
@@ -101,13 +101,13 @@ class LinkItem: public QGraphicsLineItem
{ {
public: public:
// in meter // in meter
LinkItem(int from, int to, const Transform & poseA, const Transform & poseB, bool loopClosure) : LinkItem(int from, int to, const Transform & poseA, const Transform & poseB, Link::Type type) :
QGraphicsLineItem(-poseA.y(), -poseA.x(), -poseB.y(), -poseB.x()), QGraphicsLineItem(-poseA.y(), -poseA.x(), -poseB.y(), -poseB.x()),
_from(from), _from(from),
_to(to), _to(to),
_poseA(poseA), _poseA(poseA),
_poseB(poseB), _poseB(poseB),
_loopClosure(loopClosure) _type(type)
{ {
this->setAcceptHoverEvents(true); this->setAcceptHoverEvents(true);
} }
@@ -126,7 +126,7 @@ public:
_poseB = poseB; _poseB = poseB;
} }
bool isLoopClosure() const {return _loopClosure;} Link::Type linkType() const {return _type;}
int from() const {return _from;} int from() const {return _from;}
int to() const {return _to;} int to() const {return _to;}
@@ -146,7 +146,7 @@ private:
int _to; int _to;
Transform _poseA; Transform _poseA;
Transform _poseB; Transform _poseB;
bool _loopClosure; Link::Type _type;
}; };
GraphViewer::GraphViewer(QWidget * parent) : GraphViewer::GraphViewer(QWidget * parent) :
@@ -154,6 +154,10 @@ GraphViewer::GraphViewer(QWidget * parent) :
_nodeColor(Qt::blue), _nodeColor(Qt::blue),
_neighborColor(Qt::blue), _neighborColor(Qt::blue),
_loopClosureColor(Qt::red), _loopClosureColor(Qt::red),
_loopClosureLocalColor(Qt::yellow),
_loopClosureUserColor(Qt::red),
_loopClosureVirtualColor(Qt::magenta),
_pathColor(Qt::cyan),
_root(0), _root(0),
_nodeRadius(0.01), _nodeRadius(0.01),
_linkWidth(0), _linkWidth(0),
@@ -192,6 +196,8 @@ GraphViewer::GraphViewer(QWidget * parent) :
_gridMap = this->scene()->addPixmap(QPixmap()); _gridMap = this->scene()->addPixmap(QPixmap());
_gridMap->setZValue(0); _gridMap->setZValue(0);
_gridMap->setParentItem(_root); _gridMap->setParentItem(_root);
this->restoreDefaults();
} }
GraphViewer::~GraphViewer() GraphViewer::~GraphViewer()
@@ -201,6 +207,7 @@ GraphViewer::~GraphViewer()
void GraphViewer::updateGraph(const std::map<int, Transform> & poses, void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
const std::multimap<int, Link> & constraints) const std::multimap<int, Link> & constraints)
{ {
bool wasEmpty = _nodeItems.size() == 0 && _linkItems.size() == 0;
UDEBUG("poses=%d constraints=%d", (int)poses.size(), (int)constraints.size()); UDEBUG("poses=%d constraints=%d", (int)poses.size(), (int)constraints.size());
//Hide nodes and links //Hide nodes and links
for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter) for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
@@ -208,11 +215,7 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
iter.value()->hide(); iter.value()->hide();
iter.value()->setColor(_nodeColor); // reset color iter.value()->setColor(_nodeColor); // reset color
} }
for(QMultiMap<int, LinkItem*>::iterator iter = _loopLinkItems.begin(); iter!=_loopLinkItems.end(); ++iter) for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end(); ++iter)
{
iter.value()->hide();
}
for(QMultiMap<int, LinkItem*>::iterator iter = _neighborLinkItems.begin(); iter!=_neighborLinkItems.end(); ++iter)
{ {
iter.value()->hide(); iter.value()->hide();
} }
@@ -243,41 +246,50 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
for(std::multimap<int, Link>::const_iterator iter=constraints.begin(); iter!=constraints.end(); ++iter) for(std::multimap<int, Link>::const_iterator iter=constraints.begin(); iter!=constraints.end(); ++iter)
{ {
std::map<int, Transform>::const_iterator jterA = poses.find(iter->first); // make the first id the smallest one
std::map<int, Transform>::const_iterator jterB = poses.find(iter->second.to()); int idFrom = iter->first<iter->second.to()?iter->first:iter->second.to();
int idTo = iter->first<iter->second.to()?iter->second.to():iter->first;
std::map<int, Transform>::const_iterator jterA = poses.find(idFrom);
std::map<int, Transform>::const_iterator jterB = poses.find(idTo);
if(jterA != poses.end() && jterB != poses.end() && if(jterA != poses.end() && jterB != poses.end() &&
_nodeItems.contains(iter->first) && _nodeItems.contains(iter->second.to())) _nodeItems.contains(iter->first) && _nodeItems.contains(idTo))
{ {
const Transform & poseA = jterA->second; const Transform & poseA = jterA->second;
const Transform & poseB = jterB->second; const Transform & poseB = jterB->second;
bool loopClosure = iter->second.type() != Link::kNeighbor;
bool added = false; bool added = false;
if(loopClosure && _loopLinkItems.contains(iter->first)) if(_linkItems.contains(idFrom))
{ {
QMultiMap<int, LinkItem*>::iterator itemIter = _loopLinkItems.find(iter->first); QMultiMap<int, LinkItem*>::iterator itemIter = _linkItems.find(iter->first);
while(itemIter.key() == iter->first && itemIter != _loopLinkItems.end()) while(itemIter.key() == idFrom && itemIter != _linkItems.end())
{ {
if(itemIter.value()->to() == iter->second.to()) if(itemIter.value()->to() == idTo)
{
itemIter.value()->setPoses(poseA, poseB);
itemIter.value()->show();
added = true;
break;
}
++itemIter;
}
}
else if(!loopClosure && _neighborLinkItems.contains(iter->first))
{
QMultiMap<int, LinkItem*>::iterator itemIter = _neighborLinkItems.find(iter->first);
while(itemIter.key() == iter->first && itemIter != _neighborLinkItems.end())
{
if(itemIter.value()->to() == iter->second.to())
{ {
itemIter.value()->setPoses(poseA, poseB); itemIter.value()->setPoses(poseA, poseB);
itemIter.value()->show(); itemIter.value()->show();
added = true; added = true;
// reset color
if(iter->second.type() == Link::kNeighbor)
{
itemIter.value()->setColor(_neighborColor);
}
else if(iter->second.type() == Link::kVirtualClosure)
{
itemIter.value()->setColor(_loopClosureVirtualColor);
}
else if(iter->second.type() == Link::kUserClosure)
{
itemIter.value()->setColor(_loopClosureUserColor);
}
else if(iter->second.type() == Link::kLocalSpaceClosure || iter->second.type() == Link::kLocalTimeClosure)
{
itemIter.value()->setColor(_loopClosureLocalColor);
}
else
{
itemIter.value()->setColor(_loopClosureColor);
}
break; break;
} }
++itemIter; ++itemIter;
@@ -286,22 +298,34 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
if(!added) if(!added)
{ {
//create a link item //create a link item
LinkItem * item = new LinkItem(iter->first, iter->second.to(), poseA, poseB, loopClosure); LinkItem * item = new LinkItem(idFrom, idTo, poseA, poseB, iter->second.type());
QPen p = item->pen(); QPen p = item->pen();
p.setWidthF(_linkWidth); p.setWidthF(_linkWidth);
item->setPen(p); item->setPen(p);
item->setColor(loopClosure?_loopClosureColor:_neighborColor); if(iter->second.type() == Link::kNeighbor)
this->scene()->addItem(item);
item->setZValue(1);
item->setParentItem(_root);
if(loopClosure)
{ {
_loopLinkItems.insert(iter->first, item); item->setColor(_neighborColor);
}
else if(iter->second.type() == Link::kVirtualClosure)
{
item->setColor(_loopClosureVirtualColor);
}
else if(iter->second.type() == Link::kUserClosure)
{
item->setColor(_loopClosureUserColor);
}
else if(iter->second.type() == Link::kLocalSpaceClosure || iter->second.type() == Link::kLocalTimeClosure)
{
item->setColor(_loopClosureLocalColor);
} }
else else
{ {
_neighborLinkItems.insert(iter->first, item); item->setColor(_loopClosureColor);
} }
this->scene()->addItem(item);
item->setZValue(1);
item->setParentItem(_root);
_linkItems.insert(idFrom, item);
} }
} }
} }
@@ -319,24 +343,12 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
++iter; ++iter;
} }
} }
for(QMultiMap<int, LinkItem*>::iterator iter = _loopLinkItems.begin(); iter!=_loopLinkItems.end();) for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end();)
{ {
if(!iter.value()->isVisible()) if(!iter.value()->isVisible())
{ {
delete iter.value(); delete iter.value();
iter = _loopLinkItems.erase(iter); iter = _linkItems.erase(iter);
}
else
{
++iter;
}
}
for(QMultiMap<int, LinkItem*>::iterator iter = _neighborLinkItems.begin(); iter!=_neighborLinkItems.end();)
{
if(!iter.value()->isVisible())
{
delete iter.value();
iter = _neighborLinkItems.erase(iter);
} }
else else
{ {
@@ -356,7 +368,15 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
} }
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
this->fitInView(this->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
if(poses.size() == 0 || wasEmpty)
{
this->fitInView(this->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
}
else
{
this->centerOn(_lastReferential);
}
} }
void GraphViewer::updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin) void GraphViewer::updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin)
@@ -407,14 +427,37 @@ void GraphViewer::updatePosterior(const std::map<int, float> & posterior)
} }
} }
void GraphViewer::updateLocalPath(const std::vector<int> & localPath)
{
if(localPath.size() > 1)
{
for(unsigned int i=0; i<localPath.size()-1; ++i)
{
if(_linkItems.contains(localPath[i]))
{
int idFrom = localPath[i]<localPath[i+1]?localPath[i]:localPath[i+1];
int idTo = localPath[i]<localPath[i+1]?localPath[i+1]:localPath[i];
QMultiMap<int, LinkItem*>::iterator itemIter = _linkItems.find(idFrom);
while(itemIter.key() == idFrom && itemIter != _linkItems.end())
{
if(itemIter.value()->to() == idTo)
{
itemIter.value()->setColor(_pathColor);
break;
}
++itemIter;
}
}
}
}
}
void GraphViewer::clearGraph() void GraphViewer::clearGraph()
{ {
qDeleteAll(_nodeItems); qDeleteAll(_nodeItems);
_nodeItems.clear(); _nodeItems.clear();
qDeleteAll(_neighborLinkItems); qDeleteAll(_linkItems);
_neighborLinkItems.clear(); _linkItems.clear();
qDeleteAll(_loopLinkItems);
_loopLinkItems.clear();
_lastReferential->resetTransform(); _lastReferential->resetTransform();
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
} }
@@ -438,6 +481,129 @@ void GraphViewer::clearAll()
clearGraph(); clearGraph();
} }
bool GraphViewer::isGridMapVisible() const
{
return _gridMap->isVisible();
}
void GraphViewer::setWorkingDirectory(const QString & path)
{
_workingDirectory = path;
}
void GraphViewer::setNodeRadius(float radius)
{
_nodeRadius = radius;
for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
{
iter.value()->setRect(-_nodeRadius, -_nodeRadius, _nodeRadius*2.0f, _nodeRadius*2.0f);
}
}
void GraphViewer::setLinkWidth(float width)
{
_linkWidth = width;
QList<QGraphicsItem*> items = this->scene()->items();
for(int i=0; i<items.size(); ++i)
{
QGraphicsLineItem * line = qgraphicsitem_cast<QGraphicsLineItem *>(items[i]);
if(line)
{
QPen pen = line->pen();
pen.setWidthF(_linkWidth);
line->setPen(pen);
}
}
}
void GraphViewer::setNodeColor(const QColor & color)
{
_nodeColor = color;
for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
{
iter.value()->setColor(_nodeColor);
}
}
void GraphViewer::setNeighborColor(const QColor & color)
{
_neighborColor = color;
for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
{
if(iter.value()->linkType() == Link::kNeighbor)
{
iter.value()->setColor(_neighborColor);
}
}
}
void GraphViewer::setGlobalLoopClosureColor(const QColor & color)
{
_loopClosureColor = color;
for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
{
if(iter.value()->linkType() != Link::kNeighbor &&
iter.value()->linkType() != Link::kLocalSpaceClosure &&
iter.value()->linkType() != Link::kLocalTimeClosure &&
iter.value()->linkType() != Link::kUserClosure &&
iter.value()->linkType() != Link::kVirtualClosure)
{
iter.value()->setColor(_loopClosureColor);
}
}
}
void GraphViewer::setLocalLoopClosureColor(const QColor & color)
{
_loopClosureLocalColor = color;
for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
{
if(iter.value()->linkType() == Link::kLocalSpaceClosure ||
iter.value()->linkType() == Link::kLocalTimeClosure)
{
iter.value()->setColor(_loopClosureLocalColor);
}
}
}
void GraphViewer::setUserLoopClosureColor(const QColor & color)
{
_loopClosureUserColor = color;
for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
{
if(iter.value()->linkType() == Link::kUserClosure)
{
iter.value()->setColor(_loopClosureUserColor);
}
}
}
void GraphViewer::setVirtualLoopClosureColor(const QColor & color)
{
_loopClosureVirtualColor = color;
for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
{
if(iter.value()->linkType() == Link::kVirtualClosure)
{
iter.value()->setColor(_loopClosureVirtualColor);
}
}
}
void GraphViewer::setLocalPathColor(const QColor & color)
{
_pathColor = color;
}
void GraphViewer::setGridMapVisible(bool visible)
{
_gridMap->setVisible(visible);
}
void GraphViewer::restoreDefaults()
{
setNodeRadius(0.01f);
setLinkWidth(0.0f);
setNodeColor(Qt::blue);
setNeighborColor(Qt::blue);
setGlobalLoopClosureColor(Qt::red);
setLocalLoopClosureColor(Qt::yellow);
setUserLoopClosureColor(Qt::red);
setVirtualLoopClosureColor(Qt::magenta);
setGridMapVisible(true);
}
void GraphViewer::wheelEvent ( QWheelEvent * event ) void GraphViewer::wheelEvent ( QWheelEvent * event )
{ {
if(event->delta() > 0) if(event->delta() > 0)
@@ -450,15 +616,44 @@ void GraphViewer::wheelEvent ( QWheelEvent * event )
} }
} }
QIcon createIcon(const QColor & color)
{
QPixmap pixmap(50, 50);
pixmap.fill(color);
return QIcon(pixmap);
}
void GraphViewer::contextMenuEvent(QContextMenuEvent * event) void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
{ {
QMenu menu; QMenu menu;
QAction * aScreenShotPNG = menu.addAction(tr("Take a screenshot (PNG)")); QAction * aScreenShotPNG = menu.addAction(tr("Take a screenshot (PNG)"));
QAction * aScreenShotSVG = menu.addAction(tr("Take a screenshot (SVG)")); QAction * aScreenShotSVG = menu.addAction(tr("Take a screenshot (SVG)"));
menu.addSeparator(); menu.addSeparator();
QAction * aChangeNodeColor = menu.addAction(tr("Set node color..."));
QAction * aChangeNeighborColor = menu.addAction(tr("Set neighbor link color...")); QAction * aChangeNodeColor = menu.addAction(createIcon(_nodeColor), tr("Set node color..."));
QAction * aChangeLoopColor = menu.addAction(tr("Set loop closure link color...")); aChangeNodeColor->setIconVisibleInMenu(true);
// Links
QMenu * menuLink = menu.addMenu(tr("Set link color..."));
QAction * aChangeNeighborColor = menuLink->addAction(tr("Neighbor"));
QAction * aChangeGlobalLoopColor = menuLink->addAction(tr("Global loop closure"));
QAction * aChangeLocalLoopColor = menuLink->addAction(tr("Local loop closure"));
QAction * aChangeUserLoopColor = menuLink->addAction(tr("User loop closure"));
QAction * aChangeVirtualLoopColor = menuLink->addAction(tr("Virtual loop closure"));
QAction * aChangeLocalPathColor = menuLink->addAction(tr("Local path"));
aChangeNeighborColor->setIcon(createIcon(_neighborColor));
aChangeGlobalLoopColor->setIcon(createIcon(_loopClosureColor));
aChangeLocalLoopColor->setIcon(createIcon(_loopClosureLocalColor));
aChangeUserLoopColor->setIcon(createIcon(_loopClosureUserColor));
aChangeVirtualLoopColor->setIcon(createIcon(_loopClosureVirtualColor));
aChangeLocalPathColor->setIcon(createIcon(_pathColor));
aChangeNeighborColor->setIconVisibleInMenu(true);
aChangeGlobalLoopColor->setIconVisibleInMenu(true);
aChangeLocalLoopColor->setIconVisibleInMenu(true);
aChangeUserLoopColor->setIconVisibleInMenu(true);
aChangeVirtualLoopColor->setIconVisibleInMenu(true);
aChangeLocalPathColor->setIconVisibleInMenu(true);
menu.addSeparator(); menu.addSeparator();
QAction * aSetNodeSize = menu.addAction(tr("Set node radius...")); QAction * aSetNodeSize = menu.addAction(tr("Set node radius..."));
QAction * aSetLinkSize = menu.addAction(tr("Set link width...")); QAction * aSetLinkSize = menu.addAction(tr("Set link width..."));
@@ -472,6 +667,8 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
{ {
aShowHideGridMap = menu.addAction(tr("Show grid map")); aShowHideGridMap = menu.addAction(tr("Show grid map"));
} }
menu.addSeparator();
QAction * aRestoreDefaults = menu.addAction(tr("Restore defaults"));
QAction * r = menu.exec(event->globalPos()); QAction * r = menu.exec(event->globalPos());
if(r == aScreenShotPNG || r == aScreenShotSVG) if(r == aScreenShotPNG || r == aScreenShotSVG)
@@ -535,56 +732,81 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
QDesktopServices::openUrl(QUrl::fromLocalFile(targetDir + name)); QDesktopServices::openUrl(QUrl::fromLocalFile(targetDir + name));
} }
return; // without emitting configChanged
} }
else if(r == aChangeNodeColor || else if(r == aChangeNodeColor ||
r == aChangeNeighborColor || r == aChangeNeighborColor ||
r == aChangeLoopColor) r == aChangeGlobalLoopColor ||
r == aChangeLocalLoopColor ||
r == aChangeUserLoopColor ||
r == aChangeVirtualLoopColor ||
r == aChangeLocalPathColor)
{ {
QColor color; QColor color;
if(r == aChangeNodeColor) if(r == aChangeNodeColor)
{ {
color = _nodeColor; color = _nodeColor;
} }
else if(r == aChangeNeighborColor) else if(r == aChangeGlobalLoopColor)
{
color = _neighborColor;
}
else if(r == aChangeLoopColor)
{ {
color = _loopClosureColor; color = _loopClosureColor;
} }
else if(r == aChangeLocalLoopColor)
{
color = _loopClosureLocalColor;
}
else if(r == aChangeUserLoopColor)
{
color = _loopClosureUserColor;
}
else if(r == aChangeVirtualLoopColor)
{
color = _loopClosureVirtualColor;
}
else if(r == aChangeLocalPathColor)
{
color = _pathColor;
}
else //if(r == aChangeNeighborColor)
{
color = _neighborColor;
}
color = QColorDialog::getColor(color, this); color = QColorDialog::getColor(color, this);
if(color.isValid())
{
if(r == aChangeNodeColor) if(r == aChangeNodeColor)
{
_nodeColor = color;
}
else if(r == aChangeNeighborColor)
{
_neighborColor = color;
}
else if(r == aChangeLoopColor)
{
_loopClosureColor = color;
}
QList<QGraphicsItem*> items = this->scene()->items();
for(int i=0; i<items.size(); ++i)
{
LinkItem * link = qgraphicsitem_cast<LinkItem *>(items[i]);
NodeItem * node = qgraphicsitem_cast<NodeItem *>(items[i]);
if(r == aChangeNodeColor && node)
{ {
node->setColor(color); this->setNodeColor(color);
} }
else if(r == aChangeNeighborColor && link && !link->isLoopClosure()) else if(r == aChangeGlobalLoopColor)
{ {
link->setColor(color); this->setGlobalLoopClosureColor(color);
} }
else if(r == aChangeLoopColor && link && link->isLoopClosure()) else if(r == aChangeLocalLoopColor)
{ {
link->setColor(color); this->setLocalLoopClosureColor(color);
} }
else if(r == aChangeUserLoopColor)
{
this->setUserLoopClosureColor(color);
}
else if(r == aChangeVirtualLoopColor)
{
this->setVirtualLoopClosureColor(color);
}
else if(r == aChangeLocalPathColor)
{
this->setLocalPathColor(color);
}
else //if(r == aChangeNeighborColor)
{
this->setNeighborColor(color);
}
}
else
{
return; // without emitting configChanged
} }
} }
else if(r == aSetNodeSize) else if(r == aSetNodeSize)
@@ -593,16 +815,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
double value = QInputDialog::getDouble(this, tr("Node radius"), tr("Radius (m)"), _nodeRadius, 0.01, 100, 2, &ok); double value = QInputDialog::getDouble(this, tr("Node radius"), tr("Radius (m)"), _nodeRadius, 0.01, 100, 2, &ok);
if(ok) if(ok)
{ {
_nodeRadius = value; setNodeRadius(value);
QList<QGraphicsItem*> items = this->scene()->items();
for(int i=0; i<items.size(); ++i)
{
NodeItem * node = qgraphicsitem_cast<NodeItem *>(items[i]);
if(node)
{
node->setRect(-_nodeRadius, -_nodeRadius, _nodeRadius*2.0f, _nodeRadius*2.0f);
}
}
} }
} }
else if(r == aSetLinkSize) else if(r == aSetLinkSize)
@@ -611,23 +824,20 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
double value = QInputDialog::getDouble(this, tr("Link width"), tr("Width (m)"), _linkWidth, 0, 100, 2, &ok); double value = QInputDialog::getDouble(this, tr("Link width"), tr("Width (m)"), _linkWidth, 0, 100, 2, &ok);
if(ok) if(ok)
{ {
_linkWidth = value; setLinkWidth(value);
QList<QGraphicsItem*> items = this->scene()->items();
for(int i=0; i<items.size(); ++i)
{
QGraphicsLineItem * line = qgraphicsitem_cast<QGraphicsLineItem *>(items[i]);
if(line)
{
QPen pen = line->pen();
pen.setWidthF(_linkWidth);
line->setPen(pen);
}
}
} }
} }
else if(r == aShowHideGridMap) else if(r == aShowHideGridMap)
{ {
_gridMap->setVisible(!_gridMap->isVisible()); this->setGridMapVisible(!this->isGridMapVisible());
}
else if(r == aRestoreDefaults)
{
this->restoreDefaults();
}
if(r)
{
emit configChanged();
} }
} }
+40 -3
View File
@@ -44,6 +44,9 @@ class NodeItem;
class LinkItem; class LinkItem;
class GraphViewer : public QGraphicsView { class GraphViewer : public QGraphicsView {
Q_OBJECT;
public: public:
GraphViewer(QWidget * parent = 0); GraphViewer(QWidget * parent = 0);
virtual ~GraphViewer(); virtual ~GraphViewer();
@@ -52,12 +55,43 @@ public:
const std::multimap<int, Link> & constraints); const std::multimap<int, Link> & constraints);
void updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin); void updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin);
void updatePosterior(const std::map<int, float> & posterior); void updatePosterior(const std::map<int, float> & posterior);
void updateLocalPath(const std::vector<int> & localPath);
void clearGraph(); void clearGraph();
void clearMap(); void clearMap();
void clearPosterior(); void clearPosterior();
void clearAll(); void clearAll();
void setWorkingDirectory(const QString & path) {_workingDirectory = path;} //getters
const QString & getWorkingDirectory() const {return _workingDirectory;}
float getNodeRadius() const {return _nodeRadius;}
float getLinkWidth() const {return _linkWidth;}
const QColor & getNodeColor() const {return _nodeColor;}
const QColor & getNeighborColor() const {return _neighborColor;}
const QColor & getGlobalLoopClosureColor() const {return _loopClosureColor;}
const QColor & getLocalLoopClosureColor() const {return _loopClosureLocalColor;}
const QColor & getUserLoopClosureColor() const {return _loopClosureUserColor;}
const QColor & getVirtualLoopClosureColor() const {return _loopClosureVirtualColor;}
const QColor & getLocalPathColor() const {return _pathColor;}
bool isGridMapVisible() const;
// setters
void setWorkingDirectory(const QString & path);
void setNodeRadius(float radius);
void setLinkWidth(float width);
void setNodeColor(const QColor & color);
void setNeighborColor(const QColor & color);
void setGlobalLoopClosureColor(const QColor & color);
void setLocalLoopClosureColor(const QColor & color);
void setUserLoopClosureColor(const QColor & color);
void setVirtualLoopClosureColor(const QColor & color);
void setLocalPathColor(const QColor & color);
void setGridMapVisible(bool visible);
signals:
void configChanged();
public slots:
void restoreDefaults();
protected: protected:
virtual void wheelEvent ( QWheelEvent * event ); virtual void wheelEvent ( QWheelEvent * event );
@@ -68,10 +102,13 @@ private:
QColor _nodeColor; QColor _nodeColor;
QColor _neighborColor; QColor _neighborColor;
QColor _loopClosureColor; QColor _loopClosureColor;
QColor _loopClosureLocalColor;
QColor _loopClosureUserColor;
QColor _loopClosureVirtualColor;
QColor _pathColor;
QGraphicsItem * _root; QGraphicsItem * _root;
QMap<int, NodeItem*> _nodeItems; QMap<int, NodeItem*> _nodeItems;
QMultiMap<int, LinkItem*> _neighborLinkItems; QMultiMap<int, LinkItem*> _linkItems;
QMultiMap<int, LinkItem*> _loopLinkItems;
float _nodeRadius; float _nodeRadius;
float _linkWidth; float _linkWidth;
QGraphicsPixmapItem * _gridMap; QGraphicsPixmapItem * _gridMap;
+35 -4
View File
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtGui/QAction> #include <QtGui/QAction>
#include <QtGui/QGraphicsEffect> #include <QtGui/QGraphicsEffect>
#include <QtGui/QInputDialog>
#include "rtabmap/utilite/ULogger.h" #include "rtabmap/utilite/ULogger.h"
#include "rtabmap/gui/KeypointItem.h" #include "rtabmap/gui/KeypointItem.h"
@@ -44,6 +45,7 @@ ImageView::ImageView(QWidget * parent) :
_zoom(250), _zoom(250),
_minZoom(250), _minZoom(250),
_savedFileName((QDir::homePath()+ "/") + "picture" + ".png"), _savedFileName((QDir::homePath()+ "/") + "picture" + ".png"),
_alpha(100),
_image(0), _image(0),
_imageDepth(0) _imageDepth(0)
{ {
@@ -64,6 +66,7 @@ ImageView::ImageView(QWidget * parent) :
_showLines = _menu->addAction(tr("Show lines")); _showLines = _menu->addAction(tr("Show lines"));
_showLines->setCheckable(true); _showLines->setCheckable(true);
_showLines->setChecked(true); _showLines->setChecked(true);
_setAlpha = _menu->addAction(tr("Set alpha..."));
_saveImage = _menu->addAction(tr("Save picture...")); _saveImage = _menu->addAction(tr("Save picture..."));
} }
@@ -77,17 +80,17 @@ void ImageView::resetZoom()
this->setDragMode(QGraphicsView::NoDrag); this->setDragMode(QGraphicsView::NoDrag);
} }
bool ImageView::isImageShown() bool ImageView::isImageShown() const
{ {
return _showImage->isChecked(); return _showImage->isChecked();
} }
bool ImageView::isImageDepthShown() bool ImageView::isImageDepthShown() const
{ {
return _showImageDepth->isChecked(); return _showImageDepth->isChecked();
} }
bool ImageView::isFeaturesShown() bool ImageView::isFeaturesShown() const
{ {
return _showFeatures->isChecked(); return _showFeatures->isChecked();
} }
@@ -121,7 +124,7 @@ void ImageView::setImageDepthShown(bool shown)
} }
} }
bool ImageView::isLinesShown() bool ImageView::isLinesShown() const
{ {
return _showLines->isChecked(); return _showLines->isChecked();
} }
@@ -162,23 +165,38 @@ void ImageView::contextMenuEvent(QContextMenuEvent * e)
else if(action == _showFeatures) else if(action == _showFeatures)
{ {
this->setFeaturesShown(_showFeatures->isChecked()); this->setFeaturesShown(_showFeatures->isChecked());
emit configChanged();
} }
else if(action == _showImage) else if(action == _showImage)
{ {
this->setImageShown(_showImage->isChecked()); this->setImageShown(_showImage->isChecked());
emit configChanged();
} }
else if(action == _showImageDepth) else if(action == _showImageDepth)
{ {
this->setImageDepthShown(_showImageDepth->isChecked()); this->setImageDepthShown(_showImageDepth->isChecked());
emit configChanged();
} }
else if(action == _showLines) else if(action == _showLines)
{ {
this->setLinesShown(_showLines->isChecked()); this->setLinesShown(_showLines->isChecked());
emit configChanged();
}
else if(action == _setAlpha)
{
bool ok = false;
int value = QInputDialog::getInt(this, tr("Set features and lines alpha"), tr("alpha (0-255)"), _alpha, 0, 255, 10, &ok);
if(ok)
{
this->setAlpha(value);
emit configChanged();
}
} }
if(action == _showImage || action ==_showImageDepth) if(action == _showImage || action ==_showImageDepth)
{ {
this->updateOpacity(); this->updateOpacity();
emit configChanged();
} }
} }
@@ -359,6 +377,19 @@ void ImageView::setFeaturesColor(const QColor & color)
} }
} }
void ImageView::setAlpha(int alpha)
{
UASSERT(alpha >=0 && alpha <= 255);
_alpha = alpha;
for(QMultiMap<int, KeypointItem*>::iterator iter=_features.begin(); iter!=_features.end(); ++iter)
{
QColor c = iter.value()->pen().color();
c.setAlpha(_alpha);
iter.value()->setPen(QPen(c));
iter.value()->setBrush(QBrush(c));
}
}
void ImageView::clearLines() void ImageView::clearLines()
{ {
if(this->scene()) if(this->scene())
+1 -1
View File
@@ -197,7 +197,7 @@ void LoopClosureViewer::updateView(const Transform & transform)
UERROR("loop transform is null !?!?"); UERROR("loop transform is null !?!?");
ui_->cloudViewerTransform->removeAllClouds(); ui_->cloudViewerTransform->removeAllClouds();
} }
ui_->cloudViewerTransform->render(); ui_->cloudViewerTransform->update();
} }
} }
+312 -170
View File
@@ -82,6 +82,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/Odometry.h" #include "rtabmap/core/Odometry.h"
#include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/OdometryEvent.h"
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/Graph.h"
#include <pcl/visualization/cloud_viewer.h> #include <pcl/visualization/cloud_viewer.h>
#include <pcl/common/transforms.h> #include <pcl/common/transforms.h>
#include <pcl/common/common.h> #include <pcl/common/common.h>
@@ -119,6 +120,8 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_odometryReceived(false), _odometryReceived(false),
_openedDatabasePath(""), _openedDatabasePath(""),
_emptyNewDatabase(true), _emptyNewDatabase(true),
_odomImageShow(true),
_odomImageDepthShow(false),
_odometryCorrection(Transform::getIdentity()), _odometryCorrection(Transform::getIdentity()),
_lastOdometryProcessed(true), _lastOdometryProcessed(true),
_oneSecondTimer(0), _oneSecondTimer(0),
@@ -128,7 +131,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_rawLikelihoodCurve(0), _rawLikelihoodCurve(0),
_autoScreenCaptureOdomSync(false) _autoScreenCaptureOdomSync(false)
{ {
ULOGGER_DEBUG(""); UDEBUG("");
initGuiResource(); initGuiResource();
@@ -140,15 +143,19 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
// Create dialogs // Create dialogs
_aboutDialog = new AboutDialog(this); _aboutDialog = new AboutDialog(this);
_aboutDialog->setObjectName("AboutDialog");
_exportDialog = new ExportCloudsDialog(this); _exportDialog = new ExportCloudsDialog(this);
_exportDialog->setObjectName("ExportCloudsDialog");
_postProcessingDialog = new PostProcessingDialog(this); _postProcessingDialog = new PostProcessingDialog(this);
_postProcessingDialog->setObjectName("PostProcessingDialog");
_ui = new Ui_mainWindow(); _ui = new Ui_mainWindow();
_ui->setupUi(this); _ui->setupUi(this);
QString title("RTAB-Map: Real-Time Appearance-Based Mapping"); QString title("RTAB-Map[*]");
this->setWindowTitle(title); this->setWindowTitle(title);
this->setWindowIconText(tr("RTAB-Map")); this->setWindowIconText(tr("RTAB-Map"));
this->setObjectName("MainWindow");
//Setup dock widgets position if it is the first time the application is started. //Setup dock widgets position if it is the first time the application is started.
//if(!QFile::exists(PreferencesDialog::getIniFilePath())) //if(!QFile::exists(PreferencesDialog::getIniFilePath()))
@@ -177,10 +184,15 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
{ {
_preferencesDialog = new PreferencesDialog(this); _preferencesDialog = new PreferencesDialog(this);
} }
_preferencesDialog->setObjectName("PreferencesDialog");
_preferencesDialog->init(); _preferencesDialog->init();
// Restore window geometry // Restore window geometry
_preferencesDialog->loadMainWindowState(this); _preferencesDialog->loadMainWindowState(this, _savedMaximized);
_preferencesDialog->loadWindowGeometry(_preferencesDialog);
_preferencesDialog->loadWindowGeometry(_exportDialog);
_preferencesDialog->loadWindowGeometry(_postProcessingDialog);
_preferencesDialog->loadWindowGeometry(_aboutDialog);
setupMainLayout(_preferencesDialog->isVerticalLayoutUsed()); setupMainLayout(_preferencesDialog->isVerticalLayoutUsed());
// Timer // Timer
@@ -196,6 +208,11 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_ui->imageView_source->setBackgroundBrush(QBrush(Qt::black)); _ui->imageView_source->setBackgroundBrush(QBrush(Qt::black));
_ui->imageView_loopClosure->setBackgroundBrush(QBrush(Qt::black)); _ui->imageView_loopClosure->setBackgroundBrush(QBrush(Qt::black));
_ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::black)); _ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::black));
_ui->imageView_odometry->setAlpha(200);
_preferencesDialog->loadWidgetState(_ui->imageView_source);
_preferencesDialog->loadWidgetState(_ui->imageView_loopClosure);
_preferencesDialog->loadWidgetState(_ui->imageView_odometry);
_preferencesDialog->loadWidgetState(_ui->graphicsView_graphView);
_posteriorCurve = new PdfPlotCurve("Posterior", &_cachedSignatures, this); _posteriorCurve = new PdfPlotCurve("Posterior", &_cachedSignatures, this);
_ui->posteriorPlot->addCurve(_posteriorCurve, false); _ui->posteriorPlot->addCurve(_posteriorCurve, false);
@@ -250,6 +267,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
connect(a, SIGNAL(triggered(bool)), _initProgressDialog, SLOT(show())); connect(a, SIGNAL(triggered(bool)), _initProgressDialog, SLOT(show()));
// connect actions with custom slots // connect actions with custom slots
connect(_ui->actionSave_GUI_config, SIGNAL(triggered()), this, SLOT(saveConfigGUI()));
connect(_ui->actionNew_database, SIGNAL(triggered()), this, SLOT(newDatabase())); connect(_ui->actionNew_database, SIGNAL(triggered()), this, SLOT(newDatabase()));
connect(_ui->actionOpen_database, SIGNAL(triggered()), this, SLOT(openDatabase())); connect(_ui->actionOpen_database, SIGNAL(triggered()), this, SLOT(openDatabase()));
connect(_ui->actionClose_database, SIGNAL(triggered()), this, SLOT(closeDatabase())); connect(_ui->actionClose_database, SIGNAL(triggered()), this, SLOT(closeDatabase()));
@@ -290,6 +308,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
connect(_ui->actionPost_processing, SIGNAL(triggered()), this, SLOT(postProcessing())); connect(_ui->actionPost_processing, SIGNAL(triggered()), this, SLOT(postProcessing()));
_ui->actionPause->setShortcut(Qt::Key_Space); _ui->actionPause->setShortcut(Qt::Key_Space);
_ui->actionSave_GUI_config->setShortcut(QKeySequence::Save);
_ui->actionSave_point_cloud->setEnabled(false); _ui->actionSave_point_cloud->setEnabled(false);
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false); _ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false); _ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
@@ -305,7 +324,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
#endif #endif
//Settings menu //Settings menu
this->updateSelectSourceImageMenu(_preferencesDialog->getSourceImageType()); this->updateSelectSourceImageMenu(_preferencesDialog->isSourceImageUsed(), _preferencesDialog->getSourceImageType());
connect(_ui->actionImageFiles, SIGNAL(triggered()), this, SLOT(selectImages())); connect(_ui->actionImageFiles, SIGNAL(triggered()), this, SLOT(selectImages()));
connect(_ui->actionVideo, SIGNAL(triggered()), this, SLOT(selectVideo())); connect(_ui->actionVideo, SIGNAL(triggered()), this, SLOT(selectVideo()));
connect(_ui->actionUsbCamera, SIGNAL(triggered()), this, SLOT(selectStream())); connect(_ui->actionUsbCamera, SIGNAL(triggered()), this, SLOT(selectStream()));
@@ -344,6 +363,22 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
connect(_preferencesDialog, SIGNAL(settingsChanged(PreferencesDialog::PANEL_FLAGS)), this, SLOT(applyPrefSettings(PreferencesDialog::PANEL_FLAGS))); connect(_preferencesDialog, SIGNAL(settingsChanged(PreferencesDialog::PANEL_FLAGS)), this, SLOT(applyPrefSettings(PreferencesDialog::PANEL_FLAGS)));
qRegisterMetaType<rtabmap::ParametersMap>("rtabmap::ParametersMap"); qRegisterMetaType<rtabmap::ParametersMap>("rtabmap::ParametersMap");
connect(_preferencesDialog, SIGNAL(settingsChanged(rtabmap::ParametersMap)), this, SLOT(applyPrefSettings(rtabmap::ParametersMap))); connect(_preferencesDialog, SIGNAL(settingsChanged(rtabmap::ParametersMap)), this, SLOT(applyPrefSettings(rtabmap::ParametersMap)));
// config GUI modified
connect(_ui->imageView_source, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_ui->imageView_loopClosure, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_ui->imageView_odometry, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_ui->graphicsView_graphView, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_ui->widget_cloudViewer, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_exportDialog, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_postProcessingDialog, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_ui->toolBar->toggleViewAction(), SIGNAL(toggled(bool)), this, SLOT(configGUIModified()));
connect(_ui->toolBar, SIGNAL(orientationChanged(Qt::Orientation)), this, SLOT(configGUIModified()));
QList<QDockWidget*> dockWidgets = this->findChildren<QDockWidget*>();
for(int i=0; i<dockWidgets.size(); ++i)
{
connect(dockWidgets[i], SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(configGUIModified()));
connect(dockWidgets[i]->toggleViewAction(), SIGNAL(toggled(bool)), this, SLOT(configGUIModified()));
}
// more connects... // more connects...
connect(_ui->doubleSpinBox_stats_imgRate, SIGNAL(editingFinished()), this, SLOT(changeImgRateSetting())); connect(_ui->doubleSpinBox_stats_imgRate, SIGNAL(editingFinished()), this, SLOT(changeImgRateSetting()));
@@ -370,6 +405,11 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_ui->statsToolBox->setWorkingDirectory(_preferencesDialog->getWorkingDirectory()); _ui->statsToolBox->setWorkingDirectory(_preferencesDialog->getWorkingDirectory());
_ui->graphicsView_graphView->setWorkingDirectory(_preferencesDialog->getWorkingDirectory()); _ui->graphicsView_graphView->setWorkingDirectory(_preferencesDialog->getWorkingDirectory());
_ui->widget_cloudViewer->setWorkingDirectory(_preferencesDialog->getWorkingDirectory()); _ui->widget_cloudViewer->setWorkingDirectory(_preferencesDialog->getWorkingDirectory());
_preferencesDialog->loadWidgetState(_ui->widget_cloudViewer);
//dialog states
_preferencesDialog->loadWidgetState(_exportDialog);
_preferencesDialog->loadWidgetState(_postProcessingDialog);
if(_ui->statsToolBox->findChildren<StatItem*>().size() == 0) if(_ui->statsToolBox->findChildren<StatItem*>().size() == 0)
{ {
@@ -383,7 +423,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
// update loop closure viewer parameters // update loop closure viewer parameters
ParametersMap parameters = _preferencesDialog->getAllParameters(); ParametersMap parameters = _preferencesDialog->getAllParameters();
_ui->widget_loopClosureViewer->setDecimation(atoi(parameters.at(Parameters::kLccIcp3Decimation()).c_str())); _ui->widget_loopClosureViewer->setDecimation(atoi(parameters.at(Parameters::kLccIcp3Decimation()).c_str()));
_ui->widget_loopClosureViewer->setMaxDepth(atof(parameters.at(Parameters::kLccIcp3MaxDepth()).c_str())); _ui->widget_loopClosureViewer->setMaxDepth(uStr2Float(parameters.at(Parameters::kLccIcp3MaxDepth())));
_ui->widget_loopClosureViewer->setSamples(atoi(parameters.at(Parameters::kLccIcp3Samples()).c_str())); _ui->widget_loopClosureViewer->setSamples(atoi(parameters.at(Parameters::kLccIcp3Samples()).c_str()));
//update ui //update ui
@@ -394,6 +434,8 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
splash.close(); splash.close();
this->setFocus(); this->setFocus();
UDEBUG("");
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@@ -444,10 +486,31 @@ void MainWindow::closeEvent(QCloseEvent* event)
if(processStopped) if(processStopped)
{ {
_ui->statsToolBox->closeFigures();
//write settings before quit? //write settings before quit?
_preferencesDialog->saveMainWindowState(this); bool save = false;
if(this->isWindowModified())
{
QMessageBox::Button b=QMessageBox::question(this,
tr("RTAB-Map"),
tr("There are unsaved GUI changes. Save them?"),
QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard);
if(b == QMessageBox::Save)
{
save = true;
}
else if(b != QMessageBox::Discard)
{
event->ignore();
return;
}
}
if(save)
{
saveConfigGUI();
}
_ui->statsToolBox->closeFigures();
_ui->dockWidget_imageView->close(); _ui->dockWidget_imageView->close();
_ui->dockWidget_likelihood->close(); _ui->dockWidget_likelihood->close();
@@ -577,10 +640,12 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
UTimer time; UTimer time;
Transform pose = data.pose(); Transform pose = data.pose();
bool lost = false; bool lost = false;
bool lostStateChanged = false;
_ui->imageView_odometry->resetTransform(); _ui->imageView_odometry->resetTransform();
if(pose.isNull()) if(pose.isNull())
{ {
UDEBUG("odom lost"); // use last pose UDEBUG("odom lost"); // use last pose
lostStateChanged = _ui->widget_cloudViewer->getBackgroundColor() != Qt::darkRed;
_ui->widget_cloudViewer->setBackgroundColor(Qt::darkRed); _ui->widget_cloudViewer->setBackgroundColor(Qt::darkRed);
_ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::darkRed)); _ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::darkRed));
@@ -592,12 +657,14 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
info.inliers < _preferencesDialog->getOdomQualityWarnThr()) info.inliers < _preferencesDialog->getOdomQualityWarnThr())
{ {
UDEBUG("odom warn, quality(inliers)=%d thr=%d", info.inliers, _preferencesDialog->getOdomQualityWarnThr()); UDEBUG("odom warn, quality(inliers)=%d thr=%d", info.inliers, _preferencesDialog->getOdomQualityWarnThr());
lostStateChanged = _ui->widget_cloudViewer->getBackgroundColor() == Qt::darkRed;
_ui->widget_cloudViewer->setBackgroundColor(Qt::darkYellow); _ui->widget_cloudViewer->setBackgroundColor(Qt::darkYellow);
_ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::darkYellow)); _ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::darkYellow));
} }
else else
{ {
UDEBUG("odom ok"); UDEBUG("odom ok");
lostStateChanged = _ui->widget_cloudViewer->getBackgroundColor() == Qt::darkRed;
_ui->widget_cloudViewer->setBackgroundColor(Qt::black); _ui->widget_cloudViewer->setBackgroundColor(Qt::black);
_ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::black)); _ui->imageView_odometry->setBackgroundBrush(QBrush(Qt::black));
} }
@@ -646,19 +713,19 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
_odometryReceived = true; _odometryReceived = true;
// 3d cloud // 3d cloud
if(data.depth().cols == data.image().cols && if(data.depthOrRightImage().cols == data.image().cols &&
data.depth().rows == data.image().rows && data.depthOrRightImage().rows == data.image().rows &&
!data.depth().empty() && !data.depthOrRightImage().empty() &&
data.fx() > 0.0f && data.fx() > 0.0f &&
data.fy() > 0.0f && data.fyOrBaseline() > 0.0f &&
_preferencesDialog->isCloudsShown(1)) _preferencesDialog->isCloudsShown(1))
{ {
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud; pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
cloud = createCloud(0, cloud = createCloud(0,
data.image(), data.image(),
data.depth(), data.depthOrRightImage(),
data.fx(), data.fx(),
data.fy(), data.fyOrBaseline(),
data.cx(), data.cx(),
data.cy(), data.cy(),
data.localTransform(), data.localTransform(),
@@ -695,63 +762,95 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
if(!data.pose().isNull()) if(!data.pose().isNull())
{ {
// update camera position // update camera position
_ui->widget_cloudViewer->updateCameraPosition(_odometryCorrection*data.pose()); _ui->widget_cloudViewer->updateCameraTargetPosition(_odometryCorrection*data.pose());
} }
} }
_ui->widget_cloudViewer->render(); _ui->widget_cloudViewer->update();
} }
if(_ui->dockWidget_odometry->isVisible() && if(_ui->dockWidget_odometry->isVisible() &&
!data.image().empty()) !data.image().empty())
{ {
int alpha = _preferencesDialog->getKeypointsOpacity()*255/100; if(_ui->imageView_odometry->isFeaturesShown())
if(info.type == 0)
{ {
_ui->imageView_odometry->setFeatures(info.words, QColor(255,255,0, alpha)); int alpha = _ui->imageView_odometry->getAlpha();
} if(info.type == 0)
else if(info.type == 1) {
{ _ui->imageView_odometry->setFeatures(info.words, QColor(255,255,0, alpha));
_ui->imageView_odometry->setFeatures(info.refCorners, QColor(255,0,0, alpha)); }
else if(info.type == 1)
{
_ui->imageView_odometry->setFeatures(info.refCorners, QColor(255,0,0, alpha));
}
} }
_ui->imageView_odometry->clearLines(); _ui->imageView_odometry->clearLines();
if(lost) if(lost)
{ {
if(lostStateChanged)
{
// save state
_odomImageShow = _ui->imageView_odometry->isImageShown();
_odomImageDepthShow = _ui->imageView_odometry->isImageDepthShown();
}
_ui->imageView_odometry->setImageDepth(uCvMat2QImage(data.image())); _ui->imageView_odometry->setImageDepth(uCvMat2QImage(data.image()));
_ui->imageView_odometry->setImageShown(true); _ui->imageView_odometry->setImageShown(true);
_ui->imageView_odometry->setImageDepthShown(true); _ui->imageView_odometry->setImageDepthShown(true);
} }
else else
{ {
if(lostStateChanged)
{
// restore state
_ui->imageView_odometry->setImageShown(_odomImageShow);
_ui->imageView_odometry->setImageDepthShown(_odomImageDepthShow);
}
_ui->imageView_odometry->setImage(uCvMat2QImage(data.image())); _ui->imageView_odometry->setImage(uCvMat2QImage(data.image()));
_ui->imageView_odometry->setImageShown(true); if(_ui->imageView_odometry->isImageDepthShown())
_ui->imageView_odometry->setImageDepthShown(false); {
_ui->imageView_odometry->setImageDepth(uCvMat2QImage(data.depthOrRightImage()));
}
if(info.type == 0) if(info.type == 0)
{ {
for(unsigned int i=0; i<info.wordMatches.size(); ++i) if(_ui->imageView_odometry->isFeaturesShown())
{ {
_ui->imageView_odometry->setFeatureColor(info.wordMatches[i], QColor(255,0,0, alpha)); // outliers int alpha = _ui->imageView_odometry->getAlpha();
} for(unsigned int i=0; i<info.wordMatches.size(); ++i)
for(unsigned int i=0; i<info.wordInliers.size(); ++i) {
{ _ui->imageView_odometry->setFeatureColor(info.wordMatches[i], QColor(255,0,0, alpha)); // outliers
_ui->imageView_odometry->setFeatureColor(info.wordInliers[i], QColor(0,255,0, alpha)); // inliers }
for(unsigned int i=0; i<info.wordInliers.size(); ++i)
{
_ui->imageView_odometry->setFeatureColor(info.wordInliers[i], QColor(0,255,0, alpha)); // inliers
}
} }
} }
else if(info.type == 1) else if(info.type == 1)
{ {
//draw lines if(_ui->imageView_odometry->isFeaturesShown() || _ui->imageView_odometry->isLinesShown())
UASSERT(info.refCorners.size() == info.newCorners.size());
for(unsigned int i=0; i<info.cornerInliers.size(); ++i)
{ {
_ui->imageView_odometry->setFeatureColor(info.cornerInliers[i], QColor(0,255,0, alpha)); // inliers //draw lines
QGraphicsLineItem * item = _ui->imageView_odometry->scene()->addLine( int alpha = _ui->imageView_odometry->getAlpha();
info.refCorners[info.cornerInliers[i]].pt.x, UASSERT(info.refCorners.size() == info.newCorners.size());
info.refCorners[info.cornerInliers[i]].pt.y, for(unsigned int i=0; i<info.cornerInliers.size(); ++i)
info.newCorners[info.cornerInliers[i]].pt.x, {
info.newCorners[info.cornerInliers[i]].pt.y, if(_ui->imageView_odometry->isFeaturesShown())
QPen(QColor(0, 0, 255, alpha))); {
item->setVisible(_ui->imageView_odometry->isLinesShown()); _ui->imageView_odometry->setFeatureColor(info.cornerInliers[i], QColor(0,255,0, alpha)); // inliers
item->setZValue(1); }
if(_ui->imageView_odometry->isLinesShown())
{
QGraphicsLineItem * item = _ui->imageView_odometry->scene()->addLine(
info.refCorners[info.cornerInliers[i]].pt.x,
info.refCorners[info.cornerInliers[i]].pt.y,
info.newCorners[info.cornerInliers[i]].pt.x,
info.newCorners[info.cornerInliers[i]].pt.y,
QPen(QColor(0, 0, 255, alpha)));
item->setVisible(_ui->imageView_odometry->isLinesShown());
item->setZValue(1);
}
}
} }
} }
} }
@@ -759,10 +858,6 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
_ui->imageView_odometry->resetZoom(); _ui->imageView_odometry->resetZoom();
_ui->imageView_odometry->setSceneRect(_ui->imageView_odometry->scene()->itemsBoundingRect()); _ui->imageView_odometry->setSceneRect(_ui->imageView_odometry->scene()->itemsBoundingRect());
_ui->imageView_odometry->fitInView(_ui->imageView_odometry->sceneRect(), Qt::KeepAspectRatio); _ui->imageView_odometry->fitInView(_ui->imageView_odometry->sceneRect(), Qt::KeepAspectRatio);
if(_preferencesDialog->isImageFlipped())
{
_ui->imageView_odometry->scale(-1.0, 1.0);
}
} }
_lastOdometryProcessed = true; _lastOdometryProcessed = true;
@@ -951,12 +1046,6 @@ void MainWindow::processStats(const rtabmap::Statistics & stat)
// do it after scaling // do it after scaling
this->drawKeypoints(signature.getWords(), loopSignature.getWords()); this->drawKeypoints(signature.getWords(), loopSignature.getWords());
if(_preferencesDialog->isImageFlipped())
{
_ui->imageView_source->scale(-1.0, 1.0);
_ui->imageView_loopClosure->scale(-1.0, 1.0);
}
UDEBUG("time= %d ms", time.restart()); UDEBUG("time= %d ms", time.restart());
_ui->statsToolBox->updateStat("Keypoint/Keypoints count in the last signature/", stat.refImageId(), signature.getWords().size()); _ui->statsToolBox->updateStat("Keypoint/Keypoints count in the last signature/", stat.refImageId(), signature.getWords().size());
@@ -1044,6 +1133,8 @@ void MainWindow::processStats(const rtabmap::Statistics & stat)
{ {
_ui->graphicsView_graphView->updatePosterior(stat.posterior()); _ui->graphicsView_graphView->updatePosterior(stat.posterior());
} }
// update local path on the graph view
_ui->graphicsView_graphView->updateLocalPath(stat.localPath());
UDEBUG(""); UDEBUG("");
} }
@@ -1060,6 +1151,11 @@ void MainWindow::processStats(const rtabmap::Statistics & stat)
this->captureScreen(); this->captureScreen();
} }
_processingStatistics = false; _processingStatistics = false;
if(!_preferencesDialog->isImagesKept())
{
_cachedSignatures.clear();
}
} }
void MainWindow::updateMapCloud( void MainWindow::updateMapCloud(
@@ -1101,7 +1197,7 @@ void MainWindow::updateMapCloud(
} }
} }
_ui->actionPost_processing->setEnabled(_currentPosesMap.size() >= 2 && _currentLinksMap.size() >= 1); _ui->actionPost_processing->setEnabled(_cachedSignatures.size() >= 2 && _currentPosesMap.size() >= 2 && _currentLinksMap.size() >= 1);
} }
// filter duplicated poses // filter duplicated poses
@@ -1111,7 +1207,7 @@ void MainWindow::updateMapCloud(
{ {
float radius = _preferencesDialog->getCloudFilteringRadius(); float radius = _preferencesDialog->getCloudFilteringRadius();
float angle = _preferencesDialog->getCloudFilteringAngle()*CV_PI/180.0; // convert to rad float angle = _preferencesDialog->getCloudFilteringAngle()*CV_PI/180.0; // convert to rad
poses = util3d::radiusPosesFiltering(posesIn, radius, angle); poses = rtabmap::graph::radiusPosesFiltering(posesIn, radius, angle);
// make sure the last is here // make sure the last is here
poses.insert(*posesIn.rbegin()); poses.insert(*posesIn.rbegin());
for(std::map<int, Transform>::iterator iter= poses.begin(); iter!=poses.end(); ++iter) for(std::map<int, Transform>::iterator iter= poses.begin(); iter!=poses.end(); ++iter)
@@ -1152,7 +1248,8 @@ void MainWindow::updateMapCloud(
std::string cloudName = uFormat("cloud%d", iter->first); std::string cloudName = uFormat("cloud%d", iter->first);
// 3d point cloud // 3d point cloud
if(_preferencesDialog->isCloudsShown(0)) if((_ui->widget_cloudViewer->isVisible() && _preferencesDialog->isCloudsShown(0)) ||
(_ui->graphicsView_graphView->isVisible() && _ui->graphicsView_graphView->isGridMapVisible() && _preferencesDialog->isGridMapFrom3DCloud()))
{ {
if(viewerClouds.contains(cloudName)) if(viewerClouds.contains(cloudName))
{ {
@@ -1187,7 +1284,8 @@ void MainWindow::updateMapCloud(
// 2d point cloud // 2d point cloud
std::string scanName = uFormat("scan%d", iter->first); std::string scanName = uFormat("scan%d", iter->first);
if(_preferencesDialog->isScansShown(0) || _ui->graphicsView_graphView->isVisible() || _preferencesDialog->getGridMapShown()) if((_ui->widget_cloudViewer->isVisible() && (_preferencesDialog->isScansShown(0) || _preferencesDialog->getGridMapShown())) ||
(_ui->graphicsView_graphView->isVisible() && _ui->graphicsView_graphView->isGridMapVisible()))
{ {
if(viewerClouds.contains(scanName)) if(viewerClouds.contains(scanName))
{ {
@@ -1277,7 +1375,7 @@ void MainWindow::updateMapCloud(
QColor color = Qt::gray; QColor color = Qt::gray;
if(iter->first >= 0) if(iter->first >= 0)
{ {
color = (Qt::GlobalColor)(iter->first % 12 + 7 ); color = (Qt::GlobalColor)((iter->first+2) % 12 + 7 );
} }
_ui->widget_cloudViewer->addOrUpdateGraph(uFormat("graph_%d", iter->first), iter->second, color); _ui->widget_cloudViewer->addOrUpdateGraph(uFormat("graph_%d", iter->first), iter->second, color);
} }
@@ -1320,6 +1418,8 @@ void MainWindow::updateMapCloud(
} }
} }
} }
_ui->graphicsView_graphView->update();
if(!_preferencesDialog->getGridMapShown()) if(!_preferencesDialog->getGridMapShown())
{ {
_ui->widget_cloudViewer->removeOccupancyGridMap(); _ui->widget_cloudViewer->removeOccupancyGridMap();
@@ -1354,10 +1454,10 @@ void MainWindow::updateMapCloud(
if(!currentPose.isNull()) if(!currentPose.isNull())
{ {
_ui->widget_cloudViewer->updateCameraPosition(currentPose); _ui->widget_cloudViewer->updateCameraTargetPosition(currentPose);
} }
_ui->widget_cloudViewer->render(); _ui->widget_cloudViewer->update();
} }
void MainWindow::createAndAddCloudToMap(int nodeId, const Transform & pose, int mapId) void MainWindow::createAndAddCloudToMap(int nodeId, const Transform & pose, int mapId)
@@ -1518,10 +1618,10 @@ void MainWindow::updateNodeVisibility(int nodeId, bool visible)
if(_currentPosesMap.find(nodeId) != _currentPosesMap.end()) if(_currentPosesMap.find(nodeId) != _currentPosesMap.end())
{ {
QMap<std::string, Transform> viewerClouds = _ui->widget_cloudViewer->getAddedClouds(); QMap<std::string, Transform> viewerClouds = _ui->widget_cloudViewer->getAddedClouds();
if(_preferencesDialog->isCloudsShown(0) && _cachedSignatures.contains(nodeId)) if(_preferencesDialog->isCloudsShown(0))
{ {
std::string cloudName = uFormat("cloud%d", nodeId); std::string cloudName = uFormat("cloud%d", nodeId);
if(visible && !viewerClouds.contains(cloudName)) if(visible && !viewerClouds.contains(cloudName) && _cachedSignatures.contains(nodeId))
{ {
createAndAddCloudToMap(nodeId, _currentPosesMap.find(nodeId)->second, uValue(_currentMapIds, nodeId, -1)); createAndAddCloudToMap(nodeId, _currentPosesMap.find(nodeId)->second, uValue(_currentMapIds, nodeId, -1));
} }
@@ -1536,10 +1636,10 @@ void MainWindow::updateNodeVisibility(int nodeId, bool visible)
} }
} }
if(_preferencesDialog->isScansShown(0) && _cachedSignatures.contains(nodeId)) if(_preferencesDialog->isScansShown(0))
{ {
std::string scanName = uFormat("scan%d", nodeId); std::string scanName = uFormat("scan%d", nodeId);
if(visible && !viewerClouds.contains(scanName)) if(visible && !viewerClouds.contains(scanName) && _cachedSignatures.contains(nodeId))
{ {
createAndAddScanToMap(nodeId, _currentPosesMap.find(nodeId)->second, uValue(_currentMapIds, nodeId, -1)); createAndAddScanToMap(nodeId, _currentPosesMap.find(nodeId)->second, uValue(_currentMapIds, nodeId, -1));
} }
@@ -1554,7 +1654,7 @@ void MainWindow::updateNodeVisibility(int nodeId, bool visible)
} }
} }
} }
_ui->widget_cloudViewer->render(); _ui->widget_cloudViewer->update();
} }
void MainWindow::processRtabmapEventInit(int status, const QString & info) void MainWindow::processRtabmapEventInit(int status, const QString & info)
@@ -1590,7 +1690,7 @@ void MainWindow::processRtabmapEventInit(int status, const QString & info)
// Temp database used, automatically backup with unique name (timestamp) // Temp database used, automatically backup with unique name (timestamp)
QString newName = QDateTime::currentDateTime().toString("yyMMdd-hhmmss"); QString newName = QDateTime::currentDateTime().toString("yyMMdd-hhmmss");
bool ok = false; bool ok = false;
newName = QInputDialog::getText(this, tr("Saving database..."), tr("Database name:"), QLineEdit::Normal, newName, &ok); newName = QInputDialog::getText(this, tr("Save database?"), tr("Database name:"), QLineEdit::Normal, newName, &ok);
while(ok) while(ok)
{ {
QString newPath = _preferencesDialog->getWorkingDirectory()+QDir::separator()+newName+".db"; QString newPath = _preferencesDialog->getWorkingDirectory()+QDir::separator()+newName+".db";
@@ -1714,6 +1814,11 @@ void MainWindow::processRtabmapEvent3DMap(const rtabmap::RtabmapEvent3DMap & eve
} }
_initProgressDialog->appendText(tr("%1 locations are updated to/inserted in the cache.").arg(event.getPoses().size())); _initProgressDialog->appendText(tr("%1 locations are updated to/inserted in the cache.").arg(event.getPoses().size()));
if(!_preferencesDialog->isImagesKept())
{
_cachedSignatures.clear();
}
} }
_initProgressDialog->setValue(_initProgressDialog->maximumSteps()); _initProgressDialog->setValue(_initProgressDialog->maximumSteps());
} }
@@ -1725,7 +1830,7 @@ void MainWindow::applyPrefSettings(PreferencesDialog::PANEL_FLAGS flags)
{ {
// Camera settings... // Camera settings...
_ui->doubleSpinBox_stats_imgRate->setValue(_preferencesDialog->getGeneralInputRate()); _ui->doubleSpinBox_stats_imgRate->setValue(_preferencesDialog->getGeneralInputRate());
this->updateSelectSourceImageMenu(_preferencesDialog->getSourceImageType()); this->updateSelectSourceImageMenu(_preferencesDialog->isSourceImageUsed(), _preferencesDialog->getSourceImageType());
this->updateSelectSourceDatabase(_preferencesDialog->isSourceDatabaseUsed()); this->updateSelectSourceDatabase(_preferencesDialog->isSourceDatabaseUsed());
this->updateSelectSourceRGBDMenu(_preferencesDialog->isSourceOpenniUsed(), _preferencesDialog->getSourceRGBD()); this->updateSelectSourceRGBDMenu(_preferencesDialog->isSourceOpenniUsed(), _preferencesDialog->getSourceRGBD());
QString src; QString src;
@@ -1837,7 +1942,7 @@ void MainWindow::applyPrefSettings(const rtabmap::ParametersMap & parameters)
} }
if(uContains(parameters, Parameters::kLccIcp3MaxDepth())) if(uContains(parameters, Parameters::kLccIcp3MaxDepth()))
{ {
_ui->widget_loopClosureViewer->setMaxDepth(atof(parameters.at(Parameters::kLccIcp3MaxDepth()).c_str())); _ui->widget_loopClosureViewer->setMaxDepth(uStr2Float(parameters.at(Parameters::kLccIcp3MaxDepth())));
} }
if(uContains(parameters, Parameters::kLccIcp3Samples())) if(uContains(parameters, Parameters::kLccIcp3Samples()))
{ {
@@ -1861,7 +1966,7 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
timer.start(); timer.start();
KeypointItem * item = 0; KeypointItem * item = 0;
int alpha = _preferencesDialog->getKeypointsOpacity()*255/100; int alphaA = _ui->imageView_source->getAlpha();
ULOGGER_DEBUG("refWords.size() = %d", refWords.size()); ULOGGER_DEBUG("refWords.size() = %d", refWords.size());
QMap<int, KeypointItem*> addedKeypoints; QMap<int, KeypointItem*> addedKeypoints;
for(std::multimap<int, cv::KeyPoint>::const_iterator i = refWords.begin(); i != refWords.end(); ++i ) for(std::multimap<int, cv::KeyPoint>::const_iterator i = refWords.begin(); i != refWords.end(); ++i )
@@ -1880,27 +1985,27 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
if(uContains(loopWords, id)) if(uContains(loopWords, id))
{ {
// PINK = FOUND IN LOOP SIGNATURE // PINK = FOUND IN LOOP SIGNATURE
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 255, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 255, alphaA));
} }
else if(_lastIds.contains(id)) else if(_lastIds.contains(id))
{ {
// BLUE = FOUND IN LAST SIGNATURE // BLUE = FOUND IN LAST SIGNATURE
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(0, 0, 255, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(0, 0, 255, alphaA));
} }
else if(id<=_lastId) else if(id<=_lastId)
{ {
// RED = ALREADY EXISTS // RED = ALREADY EXISTS
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 0, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 0, alphaA));
} }
else if(refWords.count(id) > 1) else if(refWords.count(id) > 1)
{ {
// YELLOW = NEW and multiple times // YELLOW = NEW and multiple times
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 255, 0, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 255, 0, alphaA));
} }
else else
{ {
// GREEN = NEW // GREEN = NEW
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(0, 255, 0, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(0, 255, 0, alphaA));
} }
item->setVisible(this->_ui->imageView_source->isFeaturesShown()); item->setVisible(this->_ui->imageView_source->isFeaturesShown());
this->_ui->imageView_source->scene()->addItem(item); this->_ui->imageView_source->scene()->addItem(item);
@@ -1911,6 +2016,7 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
timer.start(); timer.start();
item = 0; item = 0;
int alphaB = _ui->imageView_loopClosure->getAlpha();
ULOGGER_DEBUG("loopWords.size() = %d", loopWords.size()); ULOGGER_DEBUG("loopWords.size() = %d", loopWords.size());
QList<QPair<KeypointItem*, KeypointItem*> > uniqueCorrespondences; QList<QPair<KeypointItem*, KeypointItem*> > uniqueCorrespondences;
for(std::multimap<int, cv::KeyPoint>::const_iterator i = loopWords.begin(); i != loopWords.end(); ++i ) for(std::multimap<int, cv::KeyPoint>::const_iterator i = loopWords.begin(); i != loopWords.end(); ++i )
@@ -1929,7 +2035,7 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
if(uContains(refWords, id)) if(uContains(refWords, id))
{ {
// PINK = FOUND IN LOOP SIGNATURE // PINK = FOUND IN LOOP SIGNATURE
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 255, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 255, alphaB));
//To draw lines... get only unique correspondences //To draw lines... get only unique correspondences
if(uValues(refWords, id).size() == 1 && uValues(loopWords, id).size() == 1) if(uValues(refWords, id).size() == 1 && uValues(loopWords, id).size() == 1)
{ {
@@ -1939,17 +2045,17 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
else if(id<=_lastId) else if(id<=_lastId)
{ {
// RED = ALREADY EXISTS // RED = ALREADY EXISTS
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 0, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 0, 0, alphaB));
} }
else if(refWords.count(id) > 1) else if(refWords.count(id) > 1)
{ {
// YELLOW = NEW and multiple times // YELLOW = NEW and multiple times
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 255, 0, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(255, 255, 0, alphaB));
} }
else else
{ {
// GREEN = NEW // GREEN = NEW
item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(0, 255, 0, alpha)); item = new KeypointItem(r.pt.x-radius, r.pt.y-radius, radius*2, info, QColor(0, 255, 0, alphaB));
} }
item->setVisible(this->_ui->imageView_loopClosure->isFeaturesShown()); item->setVisible(this->_ui->imageView_loopClosure->isFeaturesShown());
this->_ui->imageView_loopClosure->scene()->addItem(item); this->_ui->imageView_loopClosure->scene()->addItem(item);
@@ -1987,7 +2093,7 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
iter->first->rect().y()+iter->first->rect().height()/2, iter->first->rect().y()+iter->first->rect().height()/2,
iter->second->rect().x()+iter->second->rect().width()/2+deltaX, iter->second->rect().x()+iter->second->rect().width()/2+deltaX,
iter->second->rect().y()+iter->second->rect().height()/2+deltaY, iter->second->rect().y()+iter->second->rect().height()/2+deltaY,
QPen(QColor(0, 255, 255, alpha))); QPen(QColor(0, 255, 255, alphaA)));
item->setVisible(_ui->imageView_source->isLinesShown()); item->setVisible(_ui->imageView_source->isLinesShown());
item->setZValue(1); item->setZValue(1);
@@ -1996,12 +2102,31 @@ void MainWindow::drawKeypoints(const std::multimap<int, cv::KeyPoint> & refWords
iter->first->rect().y()+iter->first->rect().height()/2-deltaY, iter->first->rect().y()+iter->first->rect().height()/2-deltaY,
iter->second->rect().x()+iter->second->rect().width()/2, iter->second->rect().x()+iter->second->rect().width()/2,
iter->second->rect().y()+iter->second->rect().height()/2, iter->second->rect().y()+iter->second->rect().height()/2,
QPen(QColor(0, 255, 255, alpha))); QPen(QColor(0, 255, 255, alphaB)));
item->setVisible(_ui->imageView_loopClosure->isLinesShown()); item->setVisible(_ui->imageView_loopClosure->isLinesShown());
item->setZValue(1); item->setZValue(1);
} }
} }
void MainWindow::showEvent(QShowEvent* anEvent)
{
this->setWindowModified(false);
}
void MainWindow::moveEvent(QMoveEvent* anEvent)
{
if(this->isVisible())
{
// HACK, there is a move event when the window is shown the first time.
static bool firstCall = true;
if(!firstCall)
{
this->configGUIModified();
}
firstCall = false;
}
}
void MainWindow::resizeEvent(QResizeEvent* anEvent) void MainWindow::resizeEvent(QResizeEvent* anEvent)
{ {
_ui->imageView_source->fitInView(_ui->imageView_source->sceneRect(), Qt::KeepAspectRatio); _ui->imageView_source->fitInView(_ui->imageView_source->sceneRect(), Qt::KeepAspectRatio);
@@ -2010,34 +2135,17 @@ void MainWindow::resizeEvent(QResizeEvent* anEvent)
_ui->imageView_source->resetZoom(); _ui->imageView_source->resetZoom();
_ui->imageView_loopClosure->resetZoom(); _ui->imageView_loopClosure->resetZoom();
_ui->imageView_odometry->resetZoom(); _ui->imageView_odometry->resetZoom();
if(this->isVisible())
{
this->configGUIModified();
}
} }
void MainWindow::updateSelectSourceImageMenu(int type) void MainWindow::updateSelectSourceImageMenu(bool used, PreferencesDialog::Src src)
{ {
if(_preferencesDialog->isSourceImageUsed()) _ui->actionUsbCamera->setChecked(used && src == PreferencesDialog::kSrcUsbDevice);
{ _ui->actionImageFiles->setChecked(used && src == PreferencesDialog::kSrcImages);
switch(type) _ui->actionVideo->setChecked(used && src == PreferencesDialog::kSrcVideo);
{
case 0:
_ui->actionUsbCamera->setChecked(true);
break;
case 1:
_ui->actionImageFiles->setChecked(true);
break;
case 2:
_ui->actionVideo->setChecked(true);
break;
default:
UERROR("Unknown source image type");
break;
}
}
else
{
// they are exclusive actions, so check/uncheck one should disable all.
_ui->actionUsbCamera->setChecked(true);
_ui->actionUsbCamera->setChecked(false);
}
} }
void MainWindow::updateSelectSourceDatabase(bool used) void MainWindow::updateSelectSourceDatabase(bool used)
@@ -2104,7 +2212,28 @@ void MainWindow::beep()
QApplication::beep(); QApplication::beep();
} }
void MainWindow::configGUIModified()
{
this->setWindowModified(true);
}
//ACTIONS //ACTIONS
void MainWindow::saveConfigGUI()
{
_savedMaximized = this->isMaximized();
_preferencesDialog->saveMainWindowState(this);
_preferencesDialog->saveWindowGeometry(_preferencesDialog);
_preferencesDialog->saveWindowGeometry(_aboutDialog);
_preferencesDialog->saveWidgetState(_ui->widget_cloudViewer);
_preferencesDialog->saveWidgetState(_ui->imageView_source);
_preferencesDialog->saveWidgetState(_ui->imageView_loopClosure);
_preferencesDialog->saveWidgetState(_ui->imageView_odometry);
_preferencesDialog->saveWidgetState(_exportDialog);
_preferencesDialog->saveWidgetState(_postProcessingDialog);
_preferencesDialog->saveWidgetState(_ui->graphicsView_graphView);
this->setWindowModified(false);
}
void MainWindow::newDatabase() void MainWindow::newDatabase()
{ {
if(_state != MainWindow::kIdle) if(_state != MainWindow::kIdle)
@@ -2191,8 +2320,8 @@ void MainWindow::startDetection()
_preferencesDialog->isSourceDatabaseUsed()) _preferencesDialog->isSourceDatabaseUsed())
{ {
float inputRate = _preferencesDialog->getGeneralInputRate(); float inputRate = _preferencesDialog->getGeneralInputRate();
float detectionRate = std::atof(parameters.at(Parameters::kRtabmapDetectionRate()).c_str()); float detectionRate = uStr2Float(parameters.at(Parameters::kRtabmapDetectionRate()));
int bufferingSize = std::atof(parameters.at(Parameters::kRtabmapImageBufferSize()).c_str()); int bufferingSize = uStr2Float(parameters.at(Parameters::kRtabmapImageBufferSize()));
if((detectionRate!=0.0f && detectionRate < inputRate) || (detectionRate > 0.0f && inputRate == 0.0f)) if((detectionRate!=0.0f && detectionRate < inputRate) || (detectionRate > 0.0f && inputRate == 0.0f))
{ {
int button = QMessageBox::question(this, int button = QMessageBox::question(this,
@@ -2732,6 +2861,11 @@ void MainWindow::generateTOROMap()
void MainWindow::postProcessing() void MainWindow::postProcessing()
{ {
if(_cachedSignatures.size() == 0)
{
UERROR("Signatures must be cached in the GUI to post processing.");
return;
}
if(_postProcessingDialog->exec() != QDialog::Accepted) if(_postProcessingDialog->exec() != QDialog::Accepted)
{ {
return; return;
@@ -2845,7 +2979,7 @@ void MainWindow::postProcessing()
ParametersMap customParameters; ParametersMap customParameters;
// override some parameters // override some parameters
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
customParameters.insert(ParametersPair(Parameters::kMemImageKept(), "false")); customParameters.insert(ParametersPair(Parameters::kMemBinDataKept(), "false"));
customParameters.insert(ParametersPair(Parameters::kMemSTMSize(), "0")); customParameters.insert(ParametersPair(Parameters::kMemSTMSize(), "0"));
customParameters.insert(ParametersPair(Parameters::kKpNewWordsComparedTogether(), "false")); customParameters.insert(ParametersPair(Parameters::kKpNewWordsComparedTogether(), "false"));
customParameters.insert(ParametersPair(Parameters::kKpNNStrategy(), parameters.at(Parameters::kLccReextractNNType()))); customParameters.insert(ParametersPair(Parameters::kKpNNStrategy(), parameters.at(Parameters::kLccReextractNNType())));
@@ -2862,7 +2996,7 @@ void MainWindow::postProcessing()
_initProgressDialog->appendText(tr("Looking for more loop closures, clustering poses... (iteration=%1/%2, radius=%3 m angle=%4 degrees)") _initProgressDialog->appendText(tr("Looking for more loop closures, clustering poses... (iteration=%1/%2, radius=%3 m angle=%4 degrees)")
.arg(n+1).arg(detectLoopClosureIterations).arg(clusterRadius).arg(clusterAngle)); .arg(n+1).arg(detectLoopClosureIterations).arg(clusterRadius).arg(clusterAngle));
std::multimap<int, int> clusters = util3d::radiusPosesClustering( std::multimap<int, int> clusters = rtabmap::graph::radiusPosesClustering(
_currentPosesMap, _currentPosesMap,
clusterRadius, clusterRadius,
clusterAngle*CV_PI/180.0); clusterAngle*CV_PI/180.0);
@@ -2884,7 +3018,7 @@ void MainWindow::postProcessing()
// only add new links and one per cluster per iteration // only add new links and one per cluster per iteration
if(addedLinks.find(from) == addedLinks.end() && addedLinks.find(to) == addedLinks.end() && if(addedLinks.find(from) == addedLinks.end() && addedLinks.find(to) == addedLinks.end() &&
util3d::findLink(_currentLinksMap, from, to) == _currentLinksMap.end()) rtabmap::graph::findLink(_currentLinksMap, from, to) == _currentLinksMap.end())
{ {
if(!_cachedSignatures.contains(from)) if(!_cachedSignatures.contains(from))
{ {
@@ -2967,8 +3101,8 @@ void MainWindow::postProcessing()
_initProgressDialog->appendText(tr("Optimizing graph with new links (%1 nodes, %2 constraints)...") _initProgressDialog->appendText(tr("Optimizing graph with new links (%1 nodes, %2 constraints)...")
.arg(odomPoses.size()).arg(_currentLinksMap.size())); .arg(odomPoses.size()).arg(_currentLinksMap.size()));
std::map<int, rtabmap::Transform> optimizedPoses; std::map<int, rtabmap::Transform> optimizedPoses;
std::map<int, int> depthGraph = util3d::generateDepthGraph(_currentLinksMap, toroOptimizeFromGraphEnd?odomPoses.rbegin()->first:odomPoses.begin()->first); std::map<int, int> depthGraph = rtabmap::graph::generateDepthGraph(_currentLinksMap, toroOptimizeFromGraphEnd?odomPoses.rbegin()->first:odomPoses.begin()->first);
util3d::optimizeTOROGraph(depthGraph, odomPoses, _currentLinksMap, optimizedPoses, toroIterations, true, ignoreVariance); rtabmap::graph::optimizeTOROGraph(depthGraph, odomPoses, _currentLinksMap, optimizedPoses, toroIterations, true, ignoreVariance);
_currentPosesMap = optimizedPoses; _currentPosesMap = optimizedPoses;
_initProgressDialog->appendText(tr("Optimizing graph with new links... done!")); _initProgressDialog->appendText(tr("Optimizing graph with new links... done!"));
} }
@@ -3109,7 +3243,9 @@ void MainWindow::postProcessing()
} }
else else
{ {
UWARN("Cannot refine link %d->%d (converged=%s variance=%f)", from, to, hasConverged?"true":"false", variance); QString str = tr("Cannot refine link %1->%2 (converged=%3 variance=%4 correspondencesRatio=%5 (ref=%6))").arg(from).arg(to).arg(hasConverged?"true":"false").arg(variance).arg(correspondencesRatio).arg(correspondenceRatio);
_initProgressDialog->appendText(str, Qt::darkYellow);
UWARN("%s", str.toStdString().c_str());
} }
} }
} }
@@ -3120,8 +3256,8 @@ void MainWindow::postProcessing()
_initProgressDialog->appendText(tr("Optimizing graph with updated links (%1 nodes, %2 constraints)...") _initProgressDialog->appendText(tr("Optimizing graph with updated links (%1 nodes, %2 constraints)...")
.arg(odomPoses.size()).arg(_currentLinksMap.size())); .arg(odomPoses.size()).arg(_currentLinksMap.size()));
std::map<int, rtabmap::Transform> optimizedPoses; std::map<int, rtabmap::Transform> optimizedPoses;
std::map<int, int> depthGraph = util3d::generateDepthGraph(_currentLinksMap, toroOptimizeFromGraphEnd?odomPoses.rbegin()->first:odomPoses.begin()->first); std::map<int, int> depthGraph = rtabmap::graph::generateDepthGraph(_currentLinksMap, toroOptimizeFromGraphEnd?odomPoses.rbegin()->first:odomPoses.begin()->first);
util3d::optimizeTOROGraph(depthGraph, odomPoses, _currentLinksMap, optimizedPoses, toroIterations, true, ignoreVariance); rtabmap::graph::optimizeTOROGraph(depthGraph, odomPoses, _currentLinksMap, optimizedPoses, toroIterations, true, ignoreVariance);
_initProgressDialog->appendText(tr("Optimizing graph with updated links... done!")); _initProgressDialog->appendText(tr("Optimizing graph with updated links... done!"));
_initProgressDialog->incrementStep(); _initProgressDialog->incrementStep();
@@ -3205,47 +3341,47 @@ void MainWindow::updateEditMenu()
void MainWindow::selectImages() void MainWindow::selectImages()
{ {
_preferencesDialog->selectSourceImage(PreferencesDialog::kSrcImages); _preferencesDialog->selectSourceImage(PreferencesDialog::kSrcImages, _ui->actionImageFiles->isChecked());
} }
void MainWindow::selectVideo() void MainWindow::selectVideo()
{ {
_preferencesDialog->selectSourceImage(PreferencesDialog::kSrcVideo); _preferencesDialog->selectSourceImage(PreferencesDialog::kSrcVideo, _ui->actionVideo->isChecked());
} }
void MainWindow::selectStream() void MainWindow::selectStream()
{ {
_preferencesDialog->selectSourceImage(PreferencesDialog::kSrcUsbDevice); _preferencesDialog->selectSourceImage(PreferencesDialog::kSrcUsbDevice, _ui->actionUsbCamera->isChecked());
} }
void MainWindow::selectDatabase() void MainWindow::selectDatabase()
{ {
_preferencesDialog->selectSourceDatabase(true); _preferencesDialog->selectSourceDatabase(true, _ui->actionDatabase->isChecked());
} }
void MainWindow::selectOpenni() void MainWindow::selectOpenni()
{ {
_preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI_PCL); _preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI_PCL, _ui->actionOpenNI_PCL->isChecked());
} }
void MainWindow::selectFreenect() void MainWindow::selectFreenect()
{ {
_preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcFreenect); _preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcFreenect, _ui->actionFreenect->isChecked());
} }
void MainWindow::selectOpenniCv() void MainWindow::selectOpenniCv()
{ {
_preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI_CV); _preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI_CV, _ui->actionOpenNI_CV->isChecked());
} }
void MainWindow::selectOpenniCvAsus() void MainWindow::selectOpenniCvAsus()
{ {
_preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI_CV_ASUS); _preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI_CV_ASUS, _ui->actionOpenNI_CV_ASUS->isChecked());
} }
void MainWindow::selectOpenni2() void MainWindow::selectOpenni2()
{ {
_preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI2); _preferencesDialog->selectSourceRGBD(PreferencesDialog::kSrcOpenNI2, _ui->actionOpenNI2->isChecked());
} }
@@ -3507,6 +3643,10 @@ void MainWindow::selectScreenCaptureFormat(bool checked)
_autoScreenCaptureOdomSync = true; _autoScreenCaptureOdomSync = true;
} }
} }
else
{
_ui->actionAuto_screen_capture->setChecked(false);
}
} }
} }
@@ -3918,6 +4058,7 @@ bool MainWindow::getExportedClouds(
{ {
_exportDialog->setOkButton(); _exportDialog->setOkButton();
} }
_exportDialog->enableRegeneration(_preferencesDialog->isImagesKept());
if(_exportDialog->exec() == QDialog::Accepted) if(_exportDialog->exec() == QDialog::Accepted)
{ {
std::map<int, Transform> poses = _ui->widget_mapVisibility->getVisiblePoses(); std::map<int, Transform> poses = _ui->widget_mapVisibility->getVisiblePoses();
@@ -4470,14 +4611,15 @@ pcl::PointCloud<pcl::PointXYZRGB>::Ptr MainWindow::getAssembledCloud(
bool inserted = false; bool inserted = false;
if(!iter->second.isNull()) if(!iter->second.isNull())
{ {
if(_cachedSignatures.contains(iter->first)) pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
if(regenerateClouds)
{ {
const Signature & s = _cachedSignatures.find(iter->first).value(); if(_cachedSignatures.contains(iter->first))
cv::Mat image, depth;
s.uncompressDataConst(&image, &depth, 0);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
if(regenerateClouds)
{ {
const Signature & s = _cachedSignatures.find(iter->first).value();
cv::Mat image, depth;
s.uncompressDataConst(&image, &depth, 0);
cloud = createCloud(iter->first, cloud = createCloud(iter->first,
image, image,
depth, depth,
@@ -4491,22 +4633,22 @@ pcl::PointCloud<pcl::PointXYZRGB>::Ptr MainWindow::getAssembledCloud(
regenerateDecimation, regenerateDecimation,
regenerateMaxDepth); regenerateMaxDepth);
} }
else if(uContains(_createdClouds, iter->first)) else
{ {
cloud = util3d::transformPointCloud<pcl::PointXYZRGB>(_createdClouds.at(iter->first), iter->second); UWARN("Cloud %d not found in cache!", iter->first);
}
if(cloud->size())
{
*assembledCloud += *cloud;
inserted = true;
++count;
} }
} }
else else if(uContains(_createdClouds, iter->first))
{ {
UERROR("Cloud %d not found?!?", iter->first); cloud = util3d::transformPointCloud<pcl::PointXYZRGB>(_createdClouds.at(iter->first), iter->second);
}
if(cloud->size())
{
*assembledCloud += *cloud;
inserted = true;
++count;
} }
} }
else else
@@ -4556,41 +4698,41 @@ std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > MainWindow::getClouds(
bool inserted = false; bool inserted = false;
if(!iter->second.isNull()) if(!iter->second.isNull())
{ {
if(_cachedSignatures.contains(iter->first)) pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
if(regenerateClouds)
{ {
const Signature & s = _cachedSignatures.find(iter->first).value(); if(_cachedSignatures.contains(iter->first))
cv::Mat image, depth;
s.uncompressDataConst(&image, &depth, 0);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
if(regenerateClouds)
{ {
cloud = createCloud(iter->first, const Signature & s = _cachedSignatures.find(iter->first).value();
image, cv::Mat image, depth;
depth, s.uncompressDataConst(&image, &depth, 0);
s.getDepthFx(), cloud = createCloud(iter->first,
s.getDepthFy(), image,
s.getDepthCx(), depth,
s.getDepthCy(), s.getDepthFx(),
s.getLocalTransform(), s.getDepthFy(),
Transform::getIdentity(), s.getDepthCx(),
regenerateVoxelSize, s.getDepthCy(),
regenerateDecimation, s.getLocalTransform(),
regenerateMaxDepth); Transform::getIdentity(),
regenerateVoxelSize,
regenerateDecimation,
regenerateMaxDepth);
} }
else if(uContains(_createdClouds, iter->first)) else
{ {
cloud = _createdClouds.at(iter->first); UERROR("Cloud %d not found in cache!", iter->first);
}
if(cloud->size())
{
clouds.insert(std::make_pair(iter->first, cloud));
inserted = true;
} }
} }
else else if(uContains(_createdClouds, iter->first))
{ {
UERROR("Cloud %d not found?!?", iter->first); cloud = _createdClouds.at(iter->first);
}
if(cloud->size())
{
clouds.insert(std::make_pair(iter->first, cloud));
inserted = true;
} }
} }
else else
+2 -2
View File
@@ -147,7 +147,7 @@ void OdometryViewer::processData()
this->addCloud(uFormat("cloud%d", clouds_.rbegin()->first), clouds_.rbegin()->second, data.pose()); this->addCloud(uFormat("cloud%d", clouds_.rbegin()->first), clouds_.rbegin()->second, data.pose());
} }
this->updateCameraPosition(data.pose()); this->updateCameraTargetPosition(data.pose());
if(qualityWarningThr_ && quality>=0 && quality < qualityWarningThr_) if(qualityWarningThr_ && quality>=0 && quality < qualityWarningThr_)
{ {
@@ -164,7 +164,7 @@ void OdometryViewer::processData()
this->setBackgroundColor(Qt::darkRed); this->setBackgroundColor(Qt::darkRed);
} }
this->render(); this->update();
} }
} }
+50
View File
@@ -41,6 +41,15 @@ PostProcessingDialog::PostProcessingDialog(QWidget * parent) :
connect(_ui->detectMoreLoopClosures, SIGNAL(clicked(bool)), this, SLOT(updateButtonBox())); connect(_ui->detectMoreLoopClosures, SIGNAL(clicked(bool)), this, SLOT(updateButtonBox()));
connect(_ui->refineNeighborLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox())); connect(_ui->refineNeighborLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox()));
connect(_ui->refineLoopClosureLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox())); connect(_ui->refineLoopClosureLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox()));
connect(_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(restoreDefaults()));
connect(_ui->detectMoreLoopClosures, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->clusterRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->clusterAngle, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->iterations, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->reextractFeatures, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->refineNeighborLinks, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->refineLoopClosureLinks, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
} }
PostProcessingDialog::~PostProcessingDialog() PostProcessingDialog::~PostProcessingDialog()
@@ -48,6 +57,17 @@ PostProcessingDialog::~PostProcessingDialog()
delete _ui; delete _ui;
} }
void PostProcessingDialog::restoreDefaults()
{
setDetectMoreLoopClosures(true);
setClusterRadius(0.3);
setClusterAngle(30);
setIterations(1);
setReextractFeatures(false);
setRefineNeighborLinks(false);
setRefineLoopClosureLinks(false);
}
void PostProcessingDialog::updateButtonBox() void PostProcessingDialog::updateButtonBox()
{ {
_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled( _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
@@ -89,4 +109,34 @@ bool PostProcessingDialog::isRefineLoopClosureLinks() const
return _ui->refineLoopClosureLinks->isChecked(); return _ui->refineLoopClosureLinks->isChecked();
} }
//setters
void PostProcessingDialog::setDetectMoreLoopClosures(bool on)
{
_ui->detectMoreLoopClosures->setChecked(on);
}
void PostProcessingDialog::setClusterRadius(double radius)
{
_ui->clusterRadius->setValue(radius);
}
void PostProcessingDialog::setClusterAngle(double angle)
{
_ui->clusterAngle->setValue(angle);
}
void PostProcessingDialog::setIterations(int iterations)
{
_ui->iterations->setValue(iterations);
}
void PostProcessingDialog::setReextractFeatures(bool on)
{
_ui->reextractFeatures->setChecked(on);
}
void PostProcessingDialog::setRefineNeighborLinks(bool on)
{
_ui->refineNeighborLinks->setChecked(on);
}
void PostProcessingDialog::setRefineLoopClosureLinks(bool on)
{
_ui->refineLoopClosureLinks->setChecked(on);
}
} }
+18
View File
@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtGui/QDialog> #include <QtGui/QDialog>
class Ui_PostProcessingDialog; class Ui_PostProcessingDialog;
class QAbstractButton;
namespace rtabmap { namespace rtabmap {
@@ -43,6 +44,7 @@ public:
virtual ~PostProcessingDialog(); virtual ~PostProcessingDialog();
//getters
bool isDetectMoreLoopClosures() const; bool isDetectMoreLoopClosures() const;
double clusterRadius() const; double clusterRadius() const;
double clusterAngle() const; double clusterAngle() const;
@@ -51,9 +53,25 @@ public:
bool isRefineNeighborLinks() const; bool isRefineNeighborLinks() const;
bool isRefineLoopClosureLinks() const; bool isRefineLoopClosureLinks() const;
//setters
void setDetectMoreLoopClosures(bool on);
void setClusterRadius(double radius);
void setClusterAngle(double angle);
void setIterations(int iterations);
void setReextractFeatures(bool on);
void setRefineNeighborLinks(bool on);
void setRefineLoopClosureLinks(bool on);
signals:
void configChanged();
public slots:
void restoreDefaults();
private slots: private slots:
void updateButtonBox(); void updateButtonBox();
private: private:
Ui_PostProcessingDialog * _ui; Ui_PostProcessingDialog * _ui;
}; };
+418 -162
View File
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtGui/QVector3D>
#include <QtGui/QFileDialog> #include <QtGui/QFileDialog>
#include <QtGui/QMessageBox> #include <QtGui/QMessageBox>
#include <QtGui/QStandardItemModel> #include <QtGui/QStandardItemModel>
@@ -56,6 +57,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/gui/LoopClosureViewer.h" #include "rtabmap/gui/LoopClosureViewer.h"
#include "rtabmap/gui/DataRecorder.h" #include "rtabmap/gui/DataRecorder.h"
#include "rtabmap/gui/CloudViewer.h"
#include "rtabmap/gui/ImageView.h"
#include "GraphViewer.h"
#include "ExportCloudsDialog.h"
#include "PostProcessingDialog.h"
#include <rtabmap/utilite/ULogger.h> #include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UConversion.h> #include <rtabmap/utilite/UConversion.h>
@@ -147,11 +153,9 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
// General panel // General panel
connect(_ui->general_checkBox_imagesKept, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->general_checkBox_imagesKept, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_verticalLayoutUsed, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->checkBox_verticalLayoutUsed, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_imageFlipped, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_imageRejectedShown, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->checkBox_imageRejectedShown, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_imageHighestHypShown, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->checkBox_imageHighestHypShown, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_beep, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->checkBox_beep, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->horizontalSlider_keypointsOpacity, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->spinBox_odomQualityWarnThr, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->spinBox_odomQualityWarnThr, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_posteriorGraphView, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel())); connect(_ui->checkBox_posteriorGraphView, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
@@ -327,6 +331,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
// Memory // Memory
_ui->general_checkBox_keepRawData->setObjectName(Parameters::kMemImageKept().c_str()); _ui->general_checkBox_keepRawData->setObjectName(Parameters::kMemImageKept().c_str());
_ui->general_checkBox_keepBinaryData->setObjectName(Parameters::kMemBinDataKept().c_str());
_ui->general_checkBox_keepRehearsedNodes->setObjectName(Parameters::kMemRehearsedNodesKept().c_str()); _ui->general_checkBox_keepRehearsedNodes->setObjectName(Parameters::kMemRehearsedNodesKept().c_str());
_ui->general_spinBox_maxStMemSize->setObjectName(Parameters::kMemSTMSize().c_str()); _ui->general_spinBox_maxStMemSize->setObjectName(Parameters::kMemSTMSize().c_str());
_ui->doubleSpinBox_similarityThreshold->setObjectName(Parameters::kMemRehearsalSimilarity().c_str()); _ui->doubleSpinBox_similarityThreshold->setObjectName(Parameters::kMemRehearsalSimilarity().c_str());
@@ -444,6 +449,9 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->globalDetection_toroIgnoreVariance->setObjectName(Parameters::kRGBDToroIgnoreVariance().c_str()); _ui->globalDetection_toroIgnoreVariance->setObjectName(Parameters::kRGBDToroIgnoreVariance().c_str());
_ui->globalDetection_optimizeFromGraphEnd->setObjectName(Parameters::kRGBDOptimizeFromGraphEnd().c_str()); _ui->globalDetection_optimizeFromGraphEnd->setObjectName(Parameters::kRGBDOptimizeFromGraphEnd().c_str());
_ui->graphPlan_goalReachedRadius->setObjectName(Parameters::kRGBDGoalReachedRadius().c_str());
_ui->graphPlan_maxAnticipatedNodes->setObjectName(Parameters::kRGBDMaxAnticipatedNodes().c_str());
_ui->groupBox_localDetection_time->setObjectName(Parameters::kRGBDLocalLoopDetectionTime().c_str()); _ui->groupBox_localDetection_time->setObjectName(Parameters::kRGBDLocalLoopDetectionTime().c_str());
_ui->groupBox_localDetection_space->setObjectName(Parameters::kRGBDLocalLoopDetectionSpace().c_str()); _ui->groupBox_localDetection_space->setObjectName(Parameters::kRGBDLocalLoopDetectionSpace().c_str());
_ui->localDetection_radius->setObjectName(Parameters::kRGBDLocalLoopDetectionRadius().c_str()); _ui->localDetection_radius->setObjectName(Parameters::kRGBDLocalLoopDetectionRadius().c_str());
@@ -537,7 +545,6 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
} }
PreferencesDialog::~PreferencesDialog() { PreferencesDialog::~PreferencesDialog() {
this->saveWindowGeometry("PreferencesDialog", this);
delete _ui; delete _ui;
} }
@@ -553,8 +560,6 @@ void PreferencesDialog::init()
this->readSettings(); this->readSettings();
this->writeSettings();// This will create the ini file if not exist this->writeSettings();// This will create the ini file if not exist
this->loadWindowGeometry("PreferencesDialog", this);
_initialized = true; _initialized = true;
} }
@@ -814,10 +819,8 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->general_checkBox_imagesKept->setChecked(true); _ui->general_checkBox_imagesKept->setChecked(true);
_ui->checkBox_beep->setChecked(false); _ui->checkBox_beep->setChecked(false);
_ui->checkBox_verticalLayoutUsed->setChecked(true); _ui->checkBox_verticalLayoutUsed->setChecked(true);
_ui->checkBox_imageFlipped->setChecked(false);
_ui->checkBox_imageRejectedShown->setChecked(true); _ui->checkBox_imageRejectedShown->setChecked(true);
_ui->checkBox_imageHighestHypShown->setChecked(false); _ui->checkBox_imageHighestHypShown->setChecked(false);
_ui->horizontalSlider_keypointsOpacity->setSliderPosition(20);
_ui->spinBox_odomQualityWarnThr->setValue(50); _ui->spinBox_odomQualityWarnThr->setValue(50);
_ui->checkBox_posteriorGraphView->setChecked(true); _ui->checkBox_posteriorGraphView->setChecked(true);
} }
@@ -853,7 +856,7 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->doubleSpinBox_map_resolution->setValue(0.05); _ui->doubleSpinBox_map_resolution->setValue(0.05);
_ui->checkBox_map_fillEmptySpace->setChecked(true); _ui->checkBox_map_fillEmptySpace->setChecked(true);
_ui->checkBox_map_occupancyFrom3DCloud->setChecked(false); _ui->checkBox_map_occupancyFrom3DCloud->setChecked(false);
_ui->spinbox_map_fillEmptyRadius->setValue(0); _ui->spinbox_map_fillEmptyRadius->setValue(1);
_ui->doubleSpinBox_map_opacity->setValue(0.75); _ui->doubleSpinBox_map_opacity->setValue(0.75);
} }
else if(groupBox->objectName() == _ui->groupBox_logging1->objectName()) else if(groupBox->objectName() == _ui->groupBox_logging1->objectName())
@@ -1058,11 +1061,9 @@ void PreferencesDialog::readGuiSettings(const QString & filePath)
_ui->comboBox_loggerType->setCurrentIndex(settings.value("loggerType", _ui->comboBox_loggerType->currentIndex()).toInt()); _ui->comboBox_loggerType->setCurrentIndex(settings.value("loggerType", _ui->comboBox_loggerType->currentIndex()).toInt());
_ui->checkBox_logger_printTime->setChecked(settings.value("loggerPrintTime", _ui->checkBox_logger_printTime->isChecked()).toBool()); _ui->checkBox_logger_printTime->setChecked(settings.value("loggerPrintTime", _ui->checkBox_logger_printTime->isChecked()).toBool());
_ui->checkBox_verticalLayoutUsed->setChecked(settings.value("verticalLayoutUsed", _ui->checkBox_verticalLayoutUsed->isChecked()).toBool()); _ui->checkBox_verticalLayoutUsed->setChecked(settings.value("verticalLayoutUsed", _ui->checkBox_verticalLayoutUsed->isChecked()).toBool());
_ui->checkBox_imageFlipped->setChecked(settings.value("imageFlipped", _ui->checkBox_imageFlipped->isChecked()).toBool());
_ui->checkBox_imageRejectedShown->setChecked(settings.value("imageRejectedShown", _ui->checkBox_imageRejectedShown->isChecked()).toBool()); _ui->checkBox_imageRejectedShown->setChecked(settings.value("imageRejectedShown", _ui->checkBox_imageRejectedShown->isChecked()).toBool());
_ui->checkBox_imageHighestHypShown->setChecked(settings.value("imageHighestHypShown", _ui->checkBox_imageHighestHypShown->isChecked()).toBool()); _ui->checkBox_imageHighestHypShown->setChecked(settings.value("imageHighestHypShown", _ui->checkBox_imageHighestHypShown->isChecked()).toBool());
_ui->checkBox_beep->setChecked(settings.value("beep", _ui->checkBox_beep->isChecked()).toBool()); _ui->checkBox_beep->setChecked(settings.value("beep", _ui->checkBox_beep->isChecked()).toBool());
_ui->horizontalSlider_keypointsOpacity->setValue(settings.value("keypointsOpacity", _ui->horizontalSlider_keypointsOpacity->value()).toInt());
_ui->spinBox_odomQualityWarnThr->setValue(settings.value("odomQualityThr", _ui->spinBox_odomQualityWarnThr->value()).toInt()); _ui->spinBox_odomQualityWarnThr->setValue(settings.value("odomQualityThr", _ui->spinBox_odomQualityWarnThr->value()).toInt());
_ui->checkBox_posteriorGraphView->setChecked(settings.value("posteriorGraphView", _ui->checkBox_posteriorGraphView->isChecked()).toBool()); _ui->checkBox_posteriorGraphView->setChecked(settings.value("posteriorGraphView", _ui->checkBox_posteriorGraphView->isChecked()).toBool());
@@ -1309,11 +1310,9 @@ void PreferencesDialog::writeGuiSettings(const QString & filePath)
settings.setValue("loggerType", _ui->comboBox_loggerType->currentIndex()); settings.setValue("loggerType", _ui->comboBox_loggerType->currentIndex());
settings.setValue("loggerPrintTime", _ui->checkBox_logger_printTime->isChecked()); settings.setValue("loggerPrintTime", _ui->checkBox_logger_printTime->isChecked());
settings.setValue("verticalLayoutUsed", _ui->checkBox_verticalLayoutUsed->isChecked()); settings.setValue("verticalLayoutUsed", _ui->checkBox_verticalLayoutUsed->isChecked());
settings.setValue("imageFlipped", _ui->checkBox_imageFlipped->isChecked());
settings.setValue("imageRejectedShown", _ui->checkBox_imageRejectedShown->isChecked()); settings.setValue("imageRejectedShown", _ui->checkBox_imageRejectedShown->isChecked());
settings.setValue("imageHighestHypShown", _ui->checkBox_imageHighestHypShown->isChecked()); settings.setValue("imageHighestHypShown", _ui->checkBox_imageHighestHypShown->isChecked());
settings.setValue("beep", _ui->checkBox_beep->isChecked()); settings.setValue("beep", _ui->checkBox_beep->isChecked());
settings.setValue("keypointsOpacity", _ui->horizontalSlider_keypointsOpacity->value());
settings.setValue("odomQualityThr", _ui->spinBox_odomQualityWarnThr->value()); settings.setValue("odomQualityThr", _ui->spinBox_odomQualityWarnThr->value());
settings.setValue("posteriorGraphView", _ui->checkBox_posteriorGraphView->isChecked()); settings.setValue("posteriorGraphView", _ui->checkBox_posteriorGraphView->isChecked());
@@ -1632,60 +1631,282 @@ void PreferencesDialog::readSettingsEnd()
_progressDialog->setValue(2); // this will make closing... _progressDialog->setValue(2); // this will make closing...
} }
void PreferencesDialog::saveWindowGeometry(const QString & windowName, const QWidget * window) void PreferencesDialog::saveWindowGeometry(const QWidget * window)
{ {
QSettings settings(getIniFilePath(), QSettings::IniFormat); if(!window->objectName().isNull() && !window->isMaximized())
settings.beginGroup("Gui"); {
settings.beginGroup(windowName); QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.setValue("geometry", window->saveGeometry()); settings.beginGroup("Gui");
settings.endGroup(); // "windowName" settings.beginGroup(window->objectName());
settings.endGroup(); // rtabmap settings.setValue("geometry", window->saveGeometry());
settings.endGroup(); // "windowName"
settings.endGroup(); // rtabmap
}
} }
void PreferencesDialog::loadWindowGeometry(const QString & windowName, QWidget * window) void PreferencesDialog::loadWindowGeometry(QWidget * window)
{ {
QByteArray bytes; if(!window->objectName().isNull())
QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup(windowName);
bytes = settings.value("geometry", QByteArray()).toByteArray();
if(!bytes.isEmpty())
{ {
window->restoreGeometry(bytes); QByteArray bytes;
QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup(window->objectName());
bytes = settings.value("geometry", QByteArray()).toByteArray();
if(!bytes.isEmpty())
{
window->restoreGeometry(bytes);
}
settings.endGroup(); // "windowName"
settings.endGroup(); // rtabmap
} }
settings.endGroup(); // "windowName"
settings.endGroup(); // rtabmap
} }
void PreferencesDialog::saveMainWindowState(const QMainWindow * mainWindow) void PreferencesDialog::saveMainWindowState(const QMainWindow * mainWindow)
{ {
QSettings settings(getIniFilePath(), QSettings::IniFormat); if(!mainWindow->objectName().isNull())
settings.beginGroup("Gui");
settings.beginGroup("MainWindow");
settings.setValue("state", mainWindow->saveState());
settings.endGroup(); // "MainWindow"
settings.endGroup(); // rtabmap
saveWindowGeometry("MainWindow", mainWindow);
}
void PreferencesDialog::loadMainWindowState(QMainWindow * mainWindow)
{
QByteArray bytes;
QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup("MainWindow");
bytes = settings.value("state", QByteArray()).toByteArray();
if(!bytes.isEmpty())
{ {
mainWindow->restoreState(bytes); saveWindowGeometry(mainWindow);
}
settings.endGroup(); // "MainWindow"
settings.endGroup(); // rtabmap
loadWindowGeometry("MainWindow", mainWindow); QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup(mainWindow->objectName());
settings.setValue("state", mainWindow->saveState());
settings.setValue("maximized", mainWindow->isMaximized());
settings.endGroup(); // "MainWindow"
settings.endGroup(); // rtabmap
}
} }
void PreferencesDialog::loadMainWindowState(QMainWindow * mainWindow, bool & maximized)
{
if(!mainWindow->objectName().isNull())
{
loadWindowGeometry(mainWindow);
QByteArray bytes;
QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup(mainWindow->objectName());
bytes = settings.value("state", QByteArray()).toByteArray();
if(!bytes.isEmpty())
{
mainWindow->restoreState(bytes);
}
maximized = settings.value("maximized", false).toBool();
settings.endGroup(); // "MainWindow"
settings.endGroup(); // rtabmap
}
}
void PreferencesDialog::saveWidgetState(const QWidget * widget)
{
if(!widget->objectName().isNull())
{
QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup(widget->objectName());
const CloudViewer * cloudViewer = qobject_cast<const CloudViewer*>(widget);
const ImageView * imageView = qobject_cast<const ImageView*>(widget);
const ExportCloudsDialog * exportCloudsDialog = qobject_cast<const ExportCloudsDialog*>(widget);
const PostProcessingDialog * postProcessingDialog = qobject_cast<const PostProcessingDialog *>(widget);
const GraphViewer * graphViewer = qobject_cast<const GraphViewer *>(widget);
if(cloudViewer)
{
float poseX, poseY, poseZ, focalX, focalY, focalZ, upX, upY, upZ;
cloudViewer->getCameraPosition(poseX, poseY, poseZ, focalX, focalY, focalZ, upX, upY, upZ);
QVector3D pose(poseX, poseY, poseZ);
QVector3D focal(focalX, focalY, focalZ);
if(!cloudViewer->isCameraFree())
{
// make camera position relative to target
Transform T = cloudViewer->getTargetPose();
if(cloudViewer->isCameraTargetLocked())
{
T = Transform(T.x(), T.y(), T.z(), 0,0,0);
}
Transform F(focalX, focalY, focalZ, 0,0,0);
Transform P(poseX, poseY, poseZ, 0,0,0);
Transform newFocal = T.inverse() * F;
Transform newPose = newFocal * F.inverse() * P;
pose = QVector3D(newPose.x(), newPose.y(), newPose.z());
focal = QVector3D(newFocal.x(), newFocal.y(), newFocal.z());
}
settings.setValue("camera_pose", pose);
settings.setValue("camera_focal", focal);
settings.setValue("camera_up", QVector3D(upX, upY, upZ));
settings.setValue("grid", cloudViewer->isGridShown());
settings.setValue("grid_cell_count", cloudViewer->getGridCellCount());
settings.setValue("grid_cell_size", cloudViewer->getGridCellSize());
settings.setValue("trajectory_shown", cloudViewer->isTrajectoryShown());
settings.setValue("trajectory_size", cloudViewer->getTrajectorySize());
settings.setValue("camera_target_locked", cloudViewer->isCameraTargetLocked());
settings.setValue("camera_target_follow", cloudViewer->isCameraTargetFollow());
settings.setValue("camera_free", cloudViewer->isCameraFree());
settings.setValue("camera_lockZ", cloudViewer->isCameraLockZ());
settings.setValue("bg_color", cloudViewer->getBackgroundColor());
}
else if(imageView)
{
settings.setValue("image_shown", imageView->isImageShown());
settings.setValue("depth_shown", imageView->isImageDepthShown());
settings.setValue("features_shown", imageView->isFeaturesShown());
settings.setValue("lines_shown", imageView->isLinesShown());
settings.setValue("alpha", imageView->getAlpha());
}
else if(exportCloudsDialog)
{
settings.setValue("assemble", exportCloudsDialog->getAssemble());
settings.setValue("assemble_voxel", exportCloudsDialog->getAssembleVoxel());
settings.setValue("regenerate", exportCloudsDialog->getGenerate());
settings.setValue("regenerate_decimation", exportCloudsDialog->getGenerateDecimation());
settings.setValue("regenerate_voxel", exportCloudsDialog->getGenerateVoxel());
settings.setValue("regenerate_max_depth", exportCloudsDialog->getGenerateMaxDepth());
settings.setValue("binary", exportCloudsDialog->getBinaryFile());
settings.setValue("mls", exportCloudsDialog->getMLS());
settings.setValue("mls_radius", exportCloudsDialog->getMLSRadius());
settings.setValue("mesh", exportCloudsDialog->getMesh());
settings.setValue("mesh_k", exportCloudsDialog->getMeshNormalKSearch());
settings.setValue("mesh_radius", exportCloudsDialog->getMeshGp3Radius());
}
else if(postProcessingDialog)
{
settings.setValue("detect_more_lc", postProcessingDialog->isDetectMoreLoopClosures());
settings.setValue("cluster_radius", postProcessingDialog->clusterRadius());
settings.setValue("cluster_angle", postProcessingDialog->clusterAngle());
settings.setValue("iterations", postProcessingDialog->iterations());
settings.setValue("reextract_features", postProcessingDialog->isReextractFeatures());
settings.setValue("refine_neigbors", postProcessingDialog->isRefineNeighborLinks());
settings.setValue("refine_lc", postProcessingDialog->isRefineLoopClosureLinks());
}
else if(graphViewer)
{
settings.setValue("node_radius", graphViewer->getNodeRadius());
settings.setValue("link_width", graphViewer->getLinkWidth());
settings.setValue("node_color", graphViewer->getNodeColor());
settings.setValue("neighbor_color", graphViewer->getNeighborColor());
settings.setValue("global_color", graphViewer->getGlobalLoopClosureColor());
settings.setValue("local_color", graphViewer->getLocalLoopClosureColor());
settings.setValue("user_color", graphViewer->getUserLoopClosureColor());
settings.setValue("virtual_color", graphViewer->getVirtualLoopClosureColor());
settings.setValue("local_path_color", graphViewer->getLocalPathColor());
settings.setValue("grid_visible", graphViewer->isGridMapVisible());
}
else
{
UERROR("Widget \"%s\" cannot be exported in config file.", widget->objectName().toStdString().c_str());
}
settings.endGroup(); // "name"
settings.endGroup(); // Gui
}
}
void PreferencesDialog::loadWidgetState(QWidget * widget)
{
if(!widget->objectName().isNull())
{
QByteArray bytes;
QSettings settings(getIniFilePath(), QSettings::IniFormat);
settings.beginGroup("Gui");
settings.beginGroup(widget->objectName());
CloudViewer * cloudViewer = qobject_cast<CloudViewer*>(widget);
ImageView * imageView = qobject_cast<ImageView*>(widget);
ExportCloudsDialog * exportCloudsDialog = qobject_cast<ExportCloudsDialog*>(widget);
PostProcessingDialog * postProcessingDialog = qobject_cast<PostProcessingDialog *>(widget);
GraphViewer * graphViewer = qobject_cast<GraphViewer *>(widget);
if(cloudViewer)
{
float poseX, poseY, poseZ, focalX, focalY, focalZ, upX, upY, upZ;
cloudViewer->getCameraPosition(poseX, poseY, poseZ, focalX, focalY, focalZ, upX, upY, upZ);
QVector3D pose(poseX, poseY, poseZ), focal(focalX, focalY, focalZ), up(upX, upY, upZ);
pose = settings.value("camera_pose", pose).value<QVector3D>();
focal = settings.value("camera_focal", focal).value<QVector3D>();
up = settings.value("camera_up", up).value<QVector3D>();
cloudViewer->setCameraPosition(pose.x(),pose.y(),pose.z(), focal.x(),focal.y(),focal.z(), up.x(),up.y(),up.z());
cloudViewer->setGridShown(settings.value("grid", cloudViewer->isGridShown()).toBool());
cloudViewer->setGridCellCount(settings.value("grid_cell_count", cloudViewer->getGridCellCount()).toUInt());
cloudViewer->setGridCellSize(settings.value("grid_cell_size", cloudViewer->getGridCellSize()).toFloat());
cloudViewer->setTrajectoryShown(settings.value("trajectory_shown", cloudViewer->isTrajectoryShown()).toBool());
cloudViewer->setTrajectorySize(settings.value("trajectory_size", cloudViewer->getTrajectorySize()).toUInt());
cloudViewer->setCameraTargetLocked(settings.value("camera_target_locked", cloudViewer->isCameraTargetLocked()).toBool());
cloudViewer->setCameraTargetFollow(settings.value("camera_target_follow", cloudViewer->isCameraTargetFollow()).toBool());
if(settings.value("camera_free", cloudViewer->isCameraFree()).toBool())
{
cloudViewer->setCameraFree();
}
cloudViewer->setCameraLockZ(settings.value("camera_lockZ", cloudViewer->isCameraLockZ()).toBool());
cloudViewer->setBackgroundColor(settings.value("bg_color", cloudViewer->getBackgroundColor()).value<QColor>());
}
else if(imageView)
{
imageView->setImageShown(settings.value("image_shown", imageView->isImageShown()).toBool());
imageView->setImageDepthShown(settings.value("depth_shown", imageView->isImageDepthShown()).toBool());
imageView->setFeaturesShown(settings.value("features_shown", imageView->isFeaturesShown()).toBool());
imageView->setLinesShown(settings.value("lines_shown", imageView->isLinesShown()).toBool());
imageView->setAlpha(settings.value("alpha", imageView->getAlpha()).toInt());
}
else if(exportCloudsDialog)
{
exportCloudsDialog->setAssemble(settings.value("assemble", exportCloudsDialog->getAssemble()).toBool());
exportCloudsDialog->setAssembleVoxel(settings.value("assemble_voxel", exportCloudsDialog->getAssembleVoxel()).toDouble());
exportCloudsDialog->setGenerate(settings.value("regenerate", exportCloudsDialog->getGenerate()).toBool());
exportCloudsDialog->setGenerateDecimation(settings.value("regenerate_decimation", exportCloudsDialog->getGenerateDecimation()).toInt());
exportCloudsDialog->setGenerateVoxel(settings.value("regenerate_voxel", exportCloudsDialog->getGenerateVoxel()).toDouble());
exportCloudsDialog->setGenerateMaxDepth(settings.value("regenerate_max_depth", exportCloudsDialog->getGenerateMaxDepth()).toDouble());
exportCloudsDialog->setBinaryFile(settings.value("binary", exportCloudsDialog->getBinaryFile()).toBool());
exportCloudsDialog->setMLS(settings.value("mls", exportCloudsDialog->getMLS()).toBool());
exportCloudsDialog->setMLSRadius(settings.value("mls_radius", exportCloudsDialog->getMLSRadius()).toDouble());
exportCloudsDialog->setMesh(settings.value("mesh", exportCloudsDialog->getMesh()).toBool());
exportCloudsDialog->setMeshNormalKSearch(settings.value("mesh_k", exportCloudsDialog->getMeshNormalKSearch()).toInt());
exportCloudsDialog->setMeshGp3Radius(settings.value("mesh_radius", exportCloudsDialog->getMeshGp3Radius()).toDouble());
}
else if(postProcessingDialog)
{
postProcessingDialog->setDetectMoreLoopClosures(settings.value("detect_more_lc", postProcessingDialog->isDetectMoreLoopClosures()).toBool());
postProcessingDialog->setClusterRadius(settings.value("cluster_radius", postProcessingDialog->clusterRadius()).toDouble());
postProcessingDialog->setClusterAngle(settings.value("cluster_angle", postProcessingDialog->clusterAngle()).toDouble());
postProcessingDialog->setIterations(settings.value("iterations", postProcessingDialog->iterations()).toInt());
postProcessingDialog->setReextractFeatures(settings.value("reextract_features", postProcessingDialog->isReextractFeatures()).toBool());
postProcessingDialog->setRefineNeighborLinks(settings.value("refine_neigbors", postProcessingDialog->isRefineNeighborLinks()).toBool());
postProcessingDialog->setRefineLoopClosureLinks(settings.value("refine_lc", postProcessingDialog->isRefineLoopClosureLinks()).toBool());
}
else if(graphViewer)
{
graphViewer->setNodeRadius(settings.value("node_radius", graphViewer->getNodeRadius()).toDouble());
graphViewer->setLinkWidth(settings.value("link_width", graphViewer->getLinkWidth()).toDouble());
graphViewer->setNodeColor(settings.value("node_color", graphViewer->getNodeColor()).value<QColor>());
graphViewer->setNeighborColor(settings.value("neighbor_color", graphViewer->getNeighborColor()).value<QColor>());
graphViewer->setGlobalLoopClosureColor(settings.value("global_color", graphViewer->getGlobalLoopClosureColor()).value<QColor>());
graphViewer->setLocalLoopClosureColor(settings.value("local_color", graphViewer->getLocalLoopClosureColor()).value<QColor>());
graphViewer->setUserLoopClosureColor(settings.value("user_color", graphViewer->getUserLoopClosureColor()).value<QColor>());
graphViewer->setVirtualLoopClosureColor(settings.value("virtual_color", graphViewer->getVirtualLoopClosureColor()).value<QColor>());
graphViewer->setLocalPathColor(settings.value("local_path_color", graphViewer->getLocalPathColor()).value<QColor>());
graphViewer->setGridMapVisible(settings.value("grid_visible", graphViewer->isGridMapVisible()).toBool());
}
else
{
UERROR("Widget \"%s\" cannot be loaded from config file.", widget->objectName().toStdString().c_str());
}
settings.endGroup(); //"name"
settings.endGroup(); // Gui
}
}
void PreferencesDialog::saveCustomConfig(const QString & section, const QString & key, const QString & value) void PreferencesDialog::saveCustomConfig(const QString & section, const QString & key, const QString & value)
{ {
QSettings settings(getIniFilePath(), QSettings::IniFormat); QSettings settings(getIniFilePath(), QSettings::IniFormat);
@@ -1708,23 +1929,10 @@ QString PreferencesDialog::loadCustomConfig(const QString & section, const QStri
return value; return value;
} }
void PreferencesDialog::selectSourceImage(Src src) void PreferencesDialog::selectSourceImage(Src src, bool ckecked)
{ {
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
if(_ui->general_checkBox_activateRGBD->isChecked())
{
int button = QMessageBox::information(this,
tr("Desactivate RGB-D SLAM?"),
tr("You've selected source input as images only and RGB-D SLAM mode is activated. "
"RGB-D SLAM cannot work with images only so do you want to desactivate it?"),
QMessageBox::Yes | QMessageBox::No);
if(button & QMessageBox::Yes)
{
_ui->general_checkBox_activateRGBD->setChecked(false);
}
}
bool fromPrefDialog = false; bool fromPrefDialog = false;
//bool modified = false; //bool modified = false;
if(src == kSrcUndef) if(src == kSrcUndef)
@@ -1743,56 +1951,85 @@ void PreferencesDialog::selectSourceImage(Src src)
src = kSrcUsbDevice; src = kSrcUsbDevice;
} }
} }
else
{
// from user
_ui->groupBox_sourceImage->setChecked(true);
}
if(src == kSrcImages) if(!fromPrefDialog)
{ {
QString path = QFileDialog::getExistingDirectory(this, QString(), _ui->source_images_lineEdit_path->text()); if(ckecked)
QDir dir(path);
if(!path.isEmpty() && dir.exists())
{ {
QStringList filters; if(_ui->general_checkBox_activateRGBD->isChecked())
filters << "*.jpg" << "*.ppm" << "*.bmp" << "*.png" << "*.pnm" << "*.tiff";
dir.setNameFilters(filters);
QFileInfoList files = dir.entryInfoList();
if(!files.empty())
{ {
_ui->source_comboBox_image_type->setCurrentIndex(1); int button = QMessageBox::information(this,
_ui->source_images_lineEdit_path->setText(path); tr("Desactivate RGB-D SLAM?"),
_ui->source_images_spinBox_startPos->setValue(1); tr("You've selected source input as images only and RGB-D SLAM mode is activated. "
_ui->source_images_refreshDir->setChecked(false); "RGB-D SLAM cannot work with images only so do you want to desactivate it?"),
} QMessageBox::Yes | QMessageBox::No);
else if(button & QMessageBox::Yes)
{ {
QMessageBox::information(this, _ui->general_checkBox_activateRGBD->setChecked(false);
tr("RTAB-Map"), }
tr("Images must be one of these formats: ") + filters.join(" "));
} }
} }
}
else if(src == kSrcVideo) _ui->groupBox_sourceImage->setChecked(false);
{
QString path = QFileDialog::getOpenFileName(this, tr("Select file"), _ui->source_video_lineEdit_path->text(), tr("Videos (*.avi *.mpg *.mp4)"));
QFile file(path);
if(!path.isEmpty() && file.exists())
{
_ui->source_comboBox_image_type->setCurrentIndex(2);
_ui->source_video_lineEdit_path->setText(path);
}
}
else // kSrcUsbDevice
{
_ui->source_comboBox_image_type->setCurrentIndex(0);
} }
if(!fromPrefDialog && _obsoletePanels) if(ckecked)
{
if(src == kSrcImages)
{
QString path = QFileDialog::getExistingDirectory(this, QString(), _ui->source_images_lineEdit_path->text());
QDir dir(path);
if(!path.isEmpty() && dir.exists())
{
QStringList filters;
filters << "*.jpg" << "*.ppm" << "*.bmp" << "*.png" << "*.pnm" << "*.tiff";
dir.setNameFilters(filters);
QFileInfoList files = dir.entryInfoList();
if(!files.empty())
{
_ui->source_comboBox_image_type->setCurrentIndex(1);
_ui->source_images_lineEdit_path->setText(path);
_ui->source_images_spinBox_startPos->setValue(1);
_ui->source_images_refreshDir->setChecked(false);
_ui->groupBox_sourceImage->setChecked(true);
}
else
{
QMessageBox::information(this,
tr("RTAB-Map"),
tr("Images must be one of these formats: ") + filters.join(" "));
}
}
}
else if(src == kSrcVideo)
{
QString path = QFileDialog::getOpenFileName(this, tr("Select file"), _ui->source_video_lineEdit_path->text(), tr("Videos (*.avi *.mpg *.mp4)"));
QFile file(path);
if(!path.isEmpty() && file.exists())
{
_ui->source_comboBox_image_type->setCurrentIndex(2);
_ui->source_video_lineEdit_path->setText(path);
_ui->groupBox_sourceImage->setChecked(true);
}
}
else // kSrcUsbDevice
{
_ui->source_comboBox_image_type->setCurrentIndex(0);
_ui->groupBox_sourceImage->setChecked(true);
}
}
if(_ui->groupBox_sourceImage->isChecked())
{ {
_ui->groupBox_sourceDatabase->setChecked(false); _ui->groupBox_sourceDatabase->setChecked(false);
_ui->groupBox_sourceOpenni->setChecked(false); _ui->groupBox_sourceOpenni->setChecked(false);
}
if(!fromPrefDialog)
{
// Even if there is no change, MainWindow should be notified
makeObsoleteSourcePanel();
if(validateForm()) if(validateForm())
{ {
this->writeSettings(); this->writeSettings();
@@ -1804,72 +2041,85 @@ void PreferencesDialog::selectSourceImage(Src src)
} }
} }
void PreferencesDialog::selectSourceDatabase(bool user) void PreferencesDialog::selectSourceDatabase(bool user, bool ckecked)
{ {
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
QString dir = _ui->source_database_lineEdit_path->text(); if(user)
if(dir.isEmpty())
{ {
dir = getWorkingDirectory(); _ui->groupBox_sourceDatabase->setChecked(false);
} }
QString path = QFileDialog::getOpenFileName(this, tr("Select file"), dir, tr("RTAB-Map database files (*.db)"));
QFile file(path); if(ckecked)
if(!path.isEmpty() && file.exists())
{ {
int r = QMessageBox::question(this, tr("Odometry in database..."), tr("Use odometry saved in database (if some saved)?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); QString dir = _ui->source_database_lineEdit_path->text();
if(dir.isEmpty())
if(user)
{ {
// from user dir = getWorkingDirectory();
_ui->groupBox_sourceDatabase->setChecked(true);
} }
QString path = QFileDialog::getOpenFileName(this, tr("Select file"), dir, tr("RTAB-Map database files (*.db)"));
_ui->source_checkBox_ignoreOdometry->setChecked(r != QMessageBox::Yes); QFile file(path);
_ui->source_database_lineEdit_path->setText(path); if(!path.isEmpty() && file.exists())
_ui->source_spinBox_databaseStartPos->setValue(0);
if(user && _obsoletePanels)
{ {
_ui->groupBox_sourceImage->setChecked(false); int r = QMessageBox::question(this, tr("Odometry in database..."), tr("Use odometry saved in database (if some saved)?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
_ui->groupBox_sourceOpenni->setChecked(false);
if(validateForm()) _ui->groupBox_sourceDatabase->setChecked(true);
{ _ui->source_checkBox_ignoreOdometry->setChecked(r != QMessageBox::Yes);
this->writeSettings(); _ui->source_database_lineEdit_path->setText(path);
} _ui->source_spinBox_databaseStartPos->setValue(0);
else }
{ }
this->readSettingsBegin();
} if(_ui->groupBox_sourceDatabase->isChecked())
{
_ui->groupBox_sourceImage->setChecked(false);
_ui->groupBox_sourceOpenni->setChecked(false);
}
if(user)
{
// Even if there is no change, MainWindow should be notified
makeObsoleteSourcePanel();
if(validateForm())
{
this->writeSettings();
}
else
{
this->readSettingsBegin();
} }
} }
} }
void PreferencesDialog::selectSourceRGBD(Src src) void PreferencesDialog::selectSourceRGBD(Src src, bool ckecked)
{ {
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
if(!_ui->general_checkBox_activateRGBD->isChecked()) if(ckecked)
{ {
int button = QMessageBox::information(this, if(!_ui->general_checkBox_activateRGBD->isChecked())
tr("Activate RGB-D SLAM?"),
tr("You've selected RGB-D camera as source input, "
"would you want to activate RGB-D SLAM mode?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if(button & QMessageBox::Yes)
{ {
_ui->general_checkBox_activateRGBD->setChecked(true); int button = QMessageBox::information(this,
tr("Activate RGB-D SLAM?"),
tr("You've selected RGB-D camera as source input, "
"would you want to activate RGB-D SLAM mode?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if(button & QMessageBox::Yes)
{
_ui->general_checkBox_activateRGBD->setChecked(true);
}
} }
} }
_ui->groupBox_sourceOpenni->setChecked(true); _ui->groupBox_sourceOpenni->setChecked(ckecked);
_ui->radioButton_opennipcl->setChecked(src == kSrcOpenNI_PCL); _ui->radioButton_opennipcl->setChecked(src == kSrcOpenNI_PCL);
_ui->radioButton_freenect->setChecked(src == kSrcFreenect); _ui->radioButton_freenect->setChecked(src == kSrcFreenect);
_ui->radioButton_opennicv->setChecked(src == kSrcOpenNI_CV); _ui->radioButton_opennicv->setChecked(src == kSrcOpenNI_CV);
_ui->radioButton_opennicvasus->setChecked(src == kSrcOpenNI_CV_ASUS); _ui->radioButton_opennicvasus->setChecked(src == kSrcOpenNI_CV_ASUS);
_ui->radioButton_openni2->setChecked(src == kSrcOpenNI2); _ui->radioButton_openni2->setChecked(src == kSrcOpenNI2);
if(_obsoletePanels) if(_ui->groupBox_sourceOpenni->isChecked())
{ {
_ui->groupBox_sourceImage->setChecked(false); _ui->groupBox_sourceImage->setChecked(false);
_ui->groupBox_sourceDatabase->setChecked(false); _ui->groupBox_sourceDatabase->setChecked(false);
@@ -1893,15 +2143,18 @@ void PreferencesDialog::selectSourceRGBD(Src src)
this->resetCalibration(); this->resetCalibration();
} }
} }
}
if(validateForm()) if(validateForm())
{ {
this->writeSettings(); // Even if there is no change, MainWindow should be notified
} makeObsoleteSourcePanel();
else
{ this->writeSettings();
this->readSettingsBegin(); }
} else
{
this->readSettingsBegin();
} }
} }
@@ -2636,10 +2889,6 @@ bool PreferencesDialog::isVerticalLayoutUsed() const
{ {
return _ui->checkBox_verticalLayoutUsed->isChecked(); return _ui->checkBox_verticalLayoutUsed->isChecked();
} }
bool PreferencesDialog::isImageFlipped() const
{
return _ui->checkBox_imageFlipped->isChecked();
}
bool PreferencesDialog::imageRejectedShown() const bool PreferencesDialog::imageRejectedShown() const
{ {
return _ui->checkBox_imageRejectedShown->isChecked(); return _ui->checkBox_imageRejectedShown->isChecked();
@@ -2652,10 +2901,6 @@ bool PreferencesDialog::beepOnPause() const
{ {
return _ui->checkBox_beep->isChecked(); return _ui->checkBox_beep->isChecked();
} }
int PreferencesDialog::getKeypointsOpacity() const
{
return _ui->horizontalSlider_keypointsOpacity->value();
}
int PreferencesDialog::getOdomQualityWarnThr() const int PreferencesDialog::getOdomQualityWarnThr() const
{ {
return _ui->spinBox_odomQualityWarnThr->value(); return _ui->spinBox_odomQualityWarnThr->value();
@@ -2792,9 +3037,20 @@ bool PreferencesDialog::isSourceOpenniUsed() const
} }
int PreferencesDialog::getSourceImageType() const PreferencesDialog::Src PreferencesDialog::getSourceImageType() const
{ {
return _ui->source_comboBox_image_type->currentIndex(); if(_ui->source_comboBox_image_type->currentIndex() == 1)
{
return kSrcImages;
}
else if(_ui->source_comboBox_image_type->currentIndex() == 2)
{
return kSrcVideo;
}
else
{
return kSrcUsbDevice;
}
} }
QString PreferencesDialog::getSourceImageTypeStr() const QString PreferencesDialog::getSourceImageTypeStr() const
{ {
+41 -7
View File
@@ -15,18 +15,42 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0"> <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0">
<item> <item>
<widget class="UImageView" name="image_view" native="true"/> <widget class="UImageView" name="image_view" native="true"/>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="checkBox_rectified"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="text"> <item>
<string>Show rectified</string> <widget class="QCheckBox" name="checkBox_rectified">
</property> <property name="text">
</widget> <string>Show rectified</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_mirror">
<property name="text">
<string>Mirror</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
</layout> </layout>
</item> </item>
@@ -88,7 +112,7 @@
<item row="2" column="1"> <item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_squareSize"> <widget class="QDoubleSpinBox" name="doubleSpinBox_squareSize">
<property name="decimals"> <property name="decimals">
<number>3</number> <number>4</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<double>1.000000000000000</double> <double>1.000000000000000</double>
@@ -199,6 +223,9 @@
<string>Camera intrinsic parameters</string> <string>Camera intrinsic parameters</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_2"> <layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
@@ -328,6 +355,13 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="pushButton_save">
<property name="text">
<string>Export calibration (*.yaml)</string>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
+1 -1
View File
@@ -301,7 +301,7 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
</property> </property>
</widget> </widget>
</item> </item>
+6
View File
@@ -178,6 +178,7 @@
<addaction name="action360p"/> <addaction name="action360p"/>
<addaction name="action240p"/> <addaction name="action240p"/>
</widget> </widget>
<addaction name="actionSave_GUI_config"/>
<addaction name="menuShow_view"/> <addaction name="menuShow_view"/>
<addaction name="menuFigures"/> <addaction name="menuFigures"/>
<addaction name="actionScreenshot"/> <addaction name="actionScreenshot"/>
@@ -1116,6 +1117,11 @@
<string>Post-processing...</string> <string>Post-processing...</string>
</property> </property>
</action> </action>
<action name="actionSave_GUI_config">
<property name="text">
<string>Save GUI config</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
+1 -1
View File
@@ -206,7 +206,7 @@
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
</property> </property>
</widget> </widget>
</item> </item>
+126 -103
View File
@@ -64,8 +64,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>732</width> <width>744</width>
<height>1302</height> <height>1074</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_16"> <layout class="QVBoxLayout" name="verticalLayout_16">
@@ -86,7 +86,7 @@
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="page_22"> <widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29"> <layout class="QVBoxLayout" name="verticalLayout_29">
@@ -143,7 +143,7 @@
<string>Loop closure detection view</string> <string>Loop closure detection view</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_31" columnstretch="0,1"> <layout class="QGridLayout" name="gridLayout_31" columnstretch="0,1">
<item row="2" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="checkBox_imageRejectedShown"> <widget class="QCheckBox" name="checkBox_imageRejectedShown">
<property name="text"> <property name="text">
<string/> <string/>
@@ -153,7 +153,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_105"> <widget class="QLabel" name="label_105">
<property name="text"> <property name="text">
<string>Show the image of the highest hypothesis.</string> <string>Show the image of the highest hypothesis.</string>
@@ -163,7 +163,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="checkBox_imageHighestHypShown"> <widget class="QCheckBox" name="checkBox_imageHighestHypShown">
<property name="text"> <property name="text">
<string/> <string/>
@@ -174,32 +174,6 @@
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLabel" name="label_80">
<property name="text">
<string>Flip image (like a mirror).</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QSlider" name="horizontalSlider_keypointsOpacity">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="sliderPosition">
<number>16</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_73"> <widget class="QLabel" name="label_73">
<property name="text"> <property name="text">
<string>Show image when an hypothesis is rejected.</string> <string>Show image when an hypothesis is rejected.</string>
@@ -229,24 +203,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="3" column="1">
<widget class="QCheckBox" name="checkBox_imageFlipped">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_102">
<property name="text">
<string>Keypoints opacity.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_222"> <widget class="QLabel" name="label_222">
<property name="text"> <property name="text">
<string>Show loop closure hypotheses on the graph view. Nodes are colorized from blue to red depending on the highest hypothesis (which is red). Posterior hypotheses should be published. </string> <string>Show loop closure hypotheses on the graph view. Nodes are colorized from blue to red depending on the highest hypothesis (which is red). Posterior hypotheses should be published. </string>
@@ -256,7 +213,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="checkBox_posteriorGraphView"> <widget class="QCheckBox" name="checkBox_posteriorGraphView">
<property name="text"> <property name="text">
<string/> <string/>
@@ -586,7 +543,7 @@ Show a yellow background when the number of odometry inliers goes under this thr
<number>10</number> <number>10</number>
</property> </property>
<property name="value"> <property name="value">
<number>0</number> <number>1</number>
</property> </property>
</widget> </widget>
</item> </item>
@@ -2811,6 +2768,16 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<layout class="QVBoxLayout" name="verticalLayout_10"> <layout class="QVBoxLayout" name="verticalLayout_10">
<item> <item>
<layout class="QGridLayout" name="gridLayout_42" columnstretch="0,1"> <layout class="QGridLayout" name="gridLayout_42" columnstretch="0,1">
<item row="5" column="1">
<widget class="QLabel" name="label_retrieved_4">
<property name="text">
<string>Initialize the Woking Memory with all nodes from Long-Term memory, instead of only nodes of the last session. This may be useful in localization mode, where less processing time is required than in SLAM mode, so more nodes can be kept in Working Memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QSpinBox" name="general_spinBox_maxStMemSize"> <widget class="QSpinBox" name="general_spinBox_maxStMemSize">
<property name="minimum"> <property name="minimum">
@@ -2936,16 +2903,26 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QLabel" name="label_retrieved_4"> <widget class="QLabel" name="label_retrieved_5">
<property name="text"> <property name="text">
<string>Initialize the Woking Memory with all nodes from Long-Term memory, instead of only nodes of the last session. This may be useful in localization mode, where less processing time is required than in SLAM mode, so more nodes can be kept in Working Memory.</string> <string>Keep raw sensor data. Only useful to save loop closure computation time when features re-extraction is enabled. Disable to save RAM memory.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0">
<widget class="QCheckBox" name="general_checkBox_keepRawData">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@@ -3021,7 +2998,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
<layout class="QGridLayout" name="gridLayout_11" columnstretch="0,1"> <layout class="QGridLayout" name="gridLayout_11" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="general_checkBox_keepRawData"> <widget class="QCheckBox" name="general_checkBox_keepBinaryData">
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
@@ -3033,7 +3010,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLabel" name="label_keepRawData"> <widget class="QLabel" name="label_keepRawData">
<property name="text"> <property name="text">
<string>Keep raw sensor data.</string> <string>Keep sensor compressed data.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@@ -4923,7 +4900,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<property name="title"> <property name="title">
<string>Map update</string> <string>Map update</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_20"> <layout class="QGridLayout" name="gridLayout_47" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDoubleSpinBox" name="rgdb_linearUpdate"> <widget class="QDoubleSpinBox" name="rgdb_linearUpdate">
<property name="suffix"> <property name="suffix">
@@ -4973,16 +4950,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QLabel" name="label_163">
<property name="text">
<string>Odometry change detected that triggers a new map (0 means whatever the odometry change, the detector will still link the new pose in the current map). Also by default, when an odometry with Identity transformation is detected, a new map is automatically created. </string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QDoubleSpinBox" name="rgdb_newMapOdomChange"> <widget class="QDoubleSpinBox" name="rgdb_newMapOdomChange">
<property name="suffix"> <property name="suffix">
@@ -5002,10 +4969,10 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_scanMatching"> <widget class="QLabel" name="label_163">
<property name="text"> <property name="text">
<string>Laser scan matching for odometry pose correction. ICP 2D only is used here.</string> <string>Odometry change detected that triggers a new map (0 means whatever the odometry change, the detector will still link the new pose in the current map). Also by default, when an odometry with Identity transformation is detected, a new map is automatically created. </string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@@ -5019,6 +4986,16 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1">
<widget class="QLabel" name="label_scanMatching">
<property name="text">
<string>Laser scan matching for odometry pose correction. ICP 2D only is used here.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -5077,10 +5054,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QFormLayout" name="formLayout_5"> <layout class="QGridLayout" name="gridLayout_48" columnstretch="0,1">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QComboBox" name="globalDetection_icpType"> <widget class="QComboBox" name="globalDetection_icpType">
<property name="sizeAdjustPolicy"> <property name="sizeAdjustPolicy">
@@ -5149,7 +5123,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<property name="title"> <property name="title">
<string>Graph optimization</string> <string>Graph optimization</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_19"> <layout class="QGridLayout" name="gridLayout_49" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QSpinBox" name="globalDetection_toroIterations"> <widget class="QSpinBox" name="globalDetection_toroIterations">
<property name="minimum"> <property name="minimum">
@@ -5170,13 +5144,17 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="0">
<widget class="QLabel" name="label_151"> <widget class="QCheckBox" name="globalDetection_toroIgnoreVariance">
<property name="text"> <property name="text">
<string>Optimize graph from the newest node. <string/>
-If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest mode of the current graph, but it can be useful to preserve the map referential from the oldest node). An odometry correction from frames /map to /odom is computed. </property>
-If true, there is no odometry correction computed since it is all previous poses in the map that are corrected, not the last one (which corresponds to latest odometry value). </widget>
Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation).</string> </item>
<item row="1" column="1">
<widget class="QLabel" name="label_141">
<property name="text">
<string>Ignore constraints' variance. If checked, identity information matrix is used for each constraint in TORO. Otherwise, an information matrix is generated from the variance saved in the links.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@@ -5190,17 +5168,13 @@ Warning when set to false: when some nodes are transferred, the first referentia
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="2" column="1">
<widget class="QCheckBox" name="globalDetection_toroIgnoreVariance"> <widget class="QLabel" name="label_151">
<property name="text"> <property name="text">
<string/> <string>Optimize graph from the newest node.
</property> -If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest mode of the current graph, but it can be useful to preserve the map referential from the oldest node). An odometry correction from frames /map to /odom is computed.
</widget> -If true, there is no odometry correction computed since it is all previous poses in the map that are corrected, not the last one (which corresponds to latest odometry value).
</item> Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation).</string>
<item row="1" column="1">
<widget class="QLabel" name="label_141">
<property name="text">
<string>Ignore constraints' variance. If checked, identity information matrix is used for each constraint in TORO. Otherwise, an information matrix is generated from the variance saved in the links.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@@ -5252,7 +5226,7 @@ Warning when set to false: when some nodes are transferred, the first referentia
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QFormLayout" name="formLayout_13"> <layout class="QGridLayout" name="gridLayout_50" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDoubleSpinBox" name="localDetection_radius"> <widget class="QDoubleSpinBox" name="localDetection_radius">
<property name="suffix"> <property name="suffix">
@@ -5290,16 +5264,6 @@ Warning when set to false: when some nodes are transferred, the first referentia
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QLabel" name="label_space3_2">
<property name="text">
<string>Maximum ID difference between the current/last loop closure location and the local loop closure hypotheses. Set 0 to ignore.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QSpinBox" name="localDetection_maxDiffID"> <widget class="QSpinBox" name="localDetection_maxDiffID">
<property name="maximum"> <property name="maximum">
@@ -5310,11 +5274,70 @@ Warning when set to false: when some nodes are transferred, the first referentia
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QLabel" name="label_space3_2">
<property name="text">
<string>Maximum ID difference between the current/last loop closure location and the local loop closure hypotheses. Set 0 to ignore.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_12">
<property name="title">
<string>Graph planning</string>
</property>
<layout class="QGridLayout" name="gridLayout_51" columnstretch="0,1">
<item row="0" column="0">
<widget class="QDoubleSpinBox" name="graphPlan_goalReachedRadius">
<property name="suffix">
<string> m</string>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_space2_2">
<property name="text">
<string>Goal reached radius.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="graphPlan_maxAnticipatedNodes">
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_space3_3">
<property name="text">
<string>Maximum anticipated nodes on the computed path that can be retrieved (the number of nodes actually retrieved at each iteration is limited by &quot;Maximum locations retrieved&quot; from the Memory panel).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<package> <package>
<name>rtabmap</name> <name>rtabmap</name>
<version>0.8.2</version> <version>0.8.3</version>
<description>RTAB-Map's standalone library. RTAB-Map is an RGB-D SLAM approach with real-time constraints.</description> <description>RTAB-Map's standalone library. RTAB-Map is an RGB-D SLAM approach with real-time constraints.</description>
<maintainer email="[email protected]">Mathieu Labbe</maintainer> <maintainer email="[email protected]">Mathieu Labbe</maintainer>
<author>Mathieu Labbe</author> <author>Mathieu Labbe</author>
+1
View File
@@ -2,6 +2,7 @@
ADD_SUBDIRECTORY( ConsoleApp ) ADD_SUBDIRECTORY( ConsoleApp )
ADD_SUBDIRECTORY( ImagesJoiner ) ADD_SUBDIRECTORY( ImagesJoiner )
ADD_SUBDIRECTORY( ExtractObject ) ADD_SUBDIRECTORY( ExtractObject )
ADD_SUBDIRECTORY( Camera )
ADD_SUBDIRECTORY( CameraRGBD ) ADD_SUBDIRECTORY( CameraRGBD )
IF(OPENCV_NONFREE_FOUND) IF(OPENCV_NONFREE_FOUND)
+88 -24
View File
@@ -25,17 +25,22 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "rtabmap/core/Camera.h"
#include "rtabmap/core/CameraRGBD.h" #include "rtabmap/core/CameraRGBD.h"
#include "rtabmap/core/CameraThread.h" #include "rtabmap/core/CameraThread.h"
#include "rtabmap/utilite/ULogger.h" #include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UConversion.h"
#include "rtabmap/gui/CalibrationDialog.h" #include "rtabmap/gui/CalibrationDialog.h"
#include <QtGui/QApplication> #include <QtGui/QApplication>
void showUsage() void showUsage()
{ {
printf("\nUsage:\n" printf("\nUsage:\n"
"rtabmap-calibration driver\n" "rtabmap-calibration [options]\n"
" driver Driver number to use: 0=OpenNI-PCL, 1=OpenNI2, 2=Freenect, 3=OpenNI-CV, 4=OpenNI-CV-ASUS\n\n"); "Options:\n"
" --driver # Driver number to use: 0=USB camera, 1=OpenNI-PCL, 2=OpenNI2,\n"
" 3=Freenect, 4=OpenNI-CV, 5=OpenNI-CV-ASUS\n"
" --device # Device id\n\n");
exit(1); exit(1);
} }
@@ -45,28 +50,72 @@ int main(int argc, char * argv[])
ULogger::setLevel(ULogger::kInfo); ULogger::setLevel(ULogger::kInfo);
int driver = 0; int driver = 0;
if(argc < 2) int device = 0;
for(int i=1; i<argc; ++i)
{ {
showUsage(); if(strcmp(argv[i], "--driver") == 0)
} {
else ++i;
{ if(i < argc)
driver = atoi(argv[argc-1]); {
if(driver < 0 || driver > 4) driver = std::atoi(argv[i]);
if(driver < 0)
{
showUsage();
}
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "--device") == 0)
{
++i;
if(i < argc)
{
device = std::atoi(argv[i]);
if(device < 0)
{
showUsage();
}
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "--help") == 0)
{ {
UERROR("driver should be between 0 and 4.");
showUsage(); showUsage();
} }
printf("Unrecognized option : %s\n", argv[i]);
showUsage();
} }
driver = atoi(argv[argc-1]);
if(driver < 0 || driver > 5)
{
UERROR("driver should be between 0 and 5.");
showUsage();
}
UINFO("Using driver %d", driver); UINFO("Using driver %d", driver);
UINFO("Using device %d", device);
float imageRate = 1.0f; float imageRate = 1.0f;
rtabmap::Camera * cameraUsb = 0;
rtabmap::CameraRGBD * camera = 0; rtabmap::CameraRGBD * camera = 0;
if(driver == 0) if(driver == 0)
{ {
camera = new rtabmap::CameraOpenni("", imageRate); cameraUsb = new rtabmap::CameraVideo(device, imageRate);
} }
else if(driver == 1) else if(driver == 1)
{
camera = new rtabmap::CameraOpenni(uNumber2Str(device), imageRate);
}
else if(driver == 2)
{ {
if(!rtabmap::CameraOpenNI2::available()) if(!rtabmap::CameraOpenNI2::available())
{ {
@@ -75,16 +124,16 @@ int main(int argc, char * argv[])
} }
camera = new rtabmap::CameraOpenNI2(imageRate); camera = new rtabmap::CameraOpenNI2(imageRate);
} }
else if(driver == 2) else if(driver == 3)
{ {
if(!rtabmap::CameraFreenect::available()) if(!rtabmap::CameraFreenect::available())
{ {
UERROR("Not built with Freenect support..."); UERROR("Not built with Freenect support...");
exit(-1); exit(-1);
} }
camera = new rtabmap::CameraFreenect(0, imageRate); camera = new rtabmap::CameraFreenect(device, imageRate);
} }
else if(driver == 3) else if(driver == 4)
{ {
if(!rtabmap::CameraOpenNICV::available()) if(!rtabmap::CameraOpenNICV::available())
{ {
@@ -93,7 +142,7 @@ int main(int argc, char * argv[])
} }
camera = new rtabmap::CameraOpenNICV(false, imageRate); camera = new rtabmap::CameraOpenNICV(false, imageRate);
} }
else if(driver == 4) else if(driver == 5)
{ {
if(!rtabmap::CameraOpenNICV::available()) if(!rtabmap::CameraOpenNICV::available())
{ {
@@ -107,21 +156,36 @@ int main(int argc, char * argv[])
UFATAL(""); UFATAL("");
} }
if(!camera->init()) rtabmap::CameraThread * cameraThread = 0;
{
printf("Camera init failed!\n");
delete camera;
exit(1);
}
rtabmap::CameraThread cameraThread(camera); if(cameraUsb)
{
if(!cameraUsb->init())
{
printf("Camera init failed!\n");
delete cameraUsb;
exit(1);
}
cameraThread = new rtabmap::CameraThread(cameraUsb);
}
else if(camera)
{
if(!camera->init())
{
printf("Camera init failed!\n");
delete camera;
exit(1);
}
cameraThread = new rtabmap::CameraThread(camera);
}
QApplication app(argc, argv); QApplication app(argc, argv);
rtabmap::CalibrationDialog dialog; rtabmap::CalibrationDialog dialog;
dialog.registerToEventsManager(); dialog.registerToEventsManager();
dialog.show(); dialog.show();
cameraThread.start(); cameraThread->start();
app.exec(); app.exec();
cameraThread.join(true); cameraThread->join(true);
delete cameraThread;
} }
+25
View File
@@ -0,0 +1,25 @@
SET(INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/corelib/include
${PROJECT_SOURCE_DIR}/utilite/include
${OpenCV_INCLUDE_DIRS}
${PCL_INCLUDE_DIRS}
)
SET(LIBRARIES
${OpenCV_LIBRARIES}
${PCL_LIBRARIES}
)
add_definitions(${PCL_DEFINITIONS})
INCLUDE_DIRECTORIES(${INCLUDE_DIRS})
ADD_EXECUTABLE(camera main.cpp)
TARGET_LINK_LIBRARIES(camera rtabmap_core rtabmap_utilite ${LIBRARIES})
SET_TARGET_PROPERTIES( camera
PROPERTIES OUTPUT_NAME ${PROJECT_PREFIX}-camera)
INSTALL(TARGETS camera
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime
BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" COMPONENT runtime)
+214
View File
@@ -0,0 +1,214 @@
/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "rtabmap/core/Camera.h"
#include "rtabmap/core/DBReader.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UFile.h"
#include "rtabmap/utilite/UDirectory.h"
#include "rtabmap/utilite/UConversion.h"
#include <opencv2/highgui/highgui.hpp>
#include <stdio.h>
void showUsage()
{
printf("\nUsage:\n"
"rtabmap-camera [option] \n"
" Options:\n"
" --device # USB camera device id (default 0).\n"
" --rate # Frame rate (default 30 Hz). 0 means as fast as possible.\n"
" --path "" Path to a directory of images or a video file.\n"
" --calibration "" Calibration file (*.yaml).\n\n");
exit(1);
}
int main(int argc, char * argv[])
{
ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kInfo);
int device = 0;
std::string path;
float rate = 30.0f;
std::string calibrationFile;
for(int i=1; i<argc; ++i)
{
if(strcmp(argv[i], "--rate") == 0)
{
++i;
if(i < argc)
{
rate = uStr2Float(argv[i]);
if(rate < 0)
{
showUsage();
}
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "--device") == 0)
{
++i;
if(i < argc)
{
device = std::atoi(argv[i]);
if(device < 0)
{
showUsage();
}
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "--path") == 0)
{
++i;
if(i < argc)
{
path = argv[i];
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "--calibration") == 0)
{
++i;
if(i < argc)
{
calibrationFile = argv[i];
}
else
{
showUsage();
}
continue;
}
printf("Unrecognized option : %s\n", argv[i]);
showUsage();
}
if(path.empty())
{
UINFO("Using device %d", device);
}
else
{
UINFO("Using path %s", path.c_str());
}
rtabmap::Camera * camera = 0;
rtabmap::DBReader * dbReader = 0;
if(!path.empty())
{
if(UFile::exists(path))
{
if(UFile::getExtension(path).compare("db") == 0)
{
dbReader = new rtabmap::DBReader(path, rate);
}
else
{
camera = new rtabmap::CameraVideo(path, rate);
}
}
else if(UDirectory::exists(path))
{
camera = new rtabmap::CameraImages(path, rate);
}
else
{
UERROR("Path not valid! \"%s\"", path.c_str());
return -1;
}
}
else
{
camera = new rtabmap::CameraVideo(device, rate);
}
if(camera)
{
if(!camera->init())
{
delete camera;
UERROR("Cannot initialize the camera.");
return -1;
}
if(!calibrationFile.empty())
{
UINFO("Set calibration: %s", calibrationFile.c_str());
camera->setCalibration(calibrationFile);
}
}
if(dbReader)
{
if(!dbReader->init())
{
delete dbReader;
UERROR("Cannot initialize the camera.");
return -1;
}
}
cv::Mat rgb;
rgb = camera?camera->takeImage():dbReader->getNextData().image();
cv::namedWindow("Video", CV_WINDOW_AUTOSIZE); // create window
while(!rgb.empty())
{
cv::imshow("Video", rgb); // show frame
int c = cv::waitKey(10); // wait 10 ms or for key stroke
if(c == 27)
break; // if ESC, break and quit
rgb = camera?camera->takeImage():dbReader->getNextData().image();
}
cv::destroyWindow("Video");
if(camera)
{
delete camera;
}
if(dbReader)
{
delete dbReader;
}
return 0;
}
+7 -27
View File
@@ -49,7 +49,6 @@ void showUsage()
" path For images, use the directory path. For videos or databases, use full\n " " path For images, use the directory path. For videos or databases, use full\n "
" path name\n" " path name\n"
"Options:\n" "Options:\n"
" -t #.## Time threshold (ms)\n"
" -rate #.## Acquisition time (seconds)\n" " -rate #.## Acquisition time (seconds)\n"
" -rateHz #.## Acquisition rate (Hz), for convenience\n" " -rateHz #.## Acquisition rate (Hz), for convenience\n"
" -repeat # Repeat the process on the data set # times (minimum of 1)\n" " -repeat # Repeat the process on the data set # times (minimum of 1)\n"
@@ -114,7 +113,6 @@ int main(int argc, char * argv[])
printf("\n"); printf("\n");
std::string path; std::string path;
float timeThreshold = 0.0;
float rate = 0.0; float rate = 0.0;
int loopDataset = 0; int loopDataset = 0;
int repeat = 0; int repeat = 0;
@@ -141,29 +139,12 @@ int main(int argc, char * argv[])
} }
break; break;
} }
if(strcmp(argv[i], "-t") == 0)
{
++i;
if(i < argc)
{
timeThreshold = std::atof(argv[i]);
if(timeThreshold < 0)
{
showUsage();
}
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "-rate") == 0) if(strcmp(argv[i], "-rate") == 0)
{ {
++i; ++i;
if(i < argc) if(i < argc)
{ {
rate = std::atof(argv[i]); rate = uStr2Float(argv[i]);
if(rate < 0) if(rate < 0)
{ {
showUsage(); showUsage();
@@ -180,7 +161,7 @@ int main(int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
rate = std::atof(argv[i]); rate = uStr2Float(argv[i]);
if(rate < 0) if(rate < 0)
{ {
showUsage(); showUsage();
@@ -401,7 +382,6 @@ int main(int argc, char * argv[])
pm.insert(ParametersPair(Parameters::kRGBDEnabled(), "false")); pm.insert(ParametersPair(Parameters::kRGBDEnabled(), "false"));
rtabmap.init(pm, inputDbPath); rtabmap.init(pm, inputDbPath);
rtabmap.setTimeThreshold(timeThreshold); // in ms
printf("Avpd init time = %fs\n", timer.ticks()); printf("Avpd init time = %fs\n", timer.ticks());
@@ -412,7 +392,7 @@ int main(int argc, char * argv[])
printf("\nParameters : \n"); printf("\nParameters : \n");
printf(" Data set : %s\n", path.c_str()); printf(" Data set : %s\n", path.c_str());
printf(" Time threshold = %1.2f ms\n", timeThreshold); printf(" Time threshold = %1.2f ms\n", rtabmap.getTimeThreshold());
printf(" Image rate = %1.2f s (%1.2f Hz)\n", rate, 1/rate); printf(" Image rate = %1.2f s (%1.2f Hz)\n", rate, 1/rate);
printf(" Repeating data set = %s\n", repeat?"true":"false"); printf(" Repeating data set = %s\n", repeat?"true":"false");
printf(" Camera width=%d, height=%d (0 is default)\n", imageWidth, imageHeight); printf(" Camera width=%d, height=%d (0 is default)\n", imageWidth, imageHeight);
@@ -502,19 +482,19 @@ int main(int argc, char * argv[])
if(rtabmap.getLoopClosureId()) if(rtabmap.getLoopClosureId())
{ {
printf(" iteration(%d) loop(%d) hyp(%.2f) time=%fs/%fs *\n", printf(" iteration(%d) loop(%d) hyp(%.2f) time=%fs/%fs *\n",
count, rtabmap.getLoopClosureId(), rtabmap.getLcHypValue(), rtabmapTime, iterationTime); count, rtabmap.getLoopClosureId(), rtabmap.getLoopClosureValue(), rtabmapTime, iterationTime);
} }
else if(rtabmap.getRetrievedId()) else if(rtabmap.getHighestHypothesisId())
{ {
printf(" iteration(%d) high(%d) hyp(%.2f) time=%fs/%fs\n", printf(" iteration(%d) high(%d) hyp(%.2f) time=%fs/%fs\n",
count, rtabmap.getRetrievedId(), rtabmap.getLcHypValue(), rtabmapTime, iterationTime); count, rtabmap.getHighestHypothesisId(), rtabmap.getHighestHypothesisValue(), rtabmapTime, iterationTime);
} }
else else
{ {
printf(" iteration(%d) time=%fs/%fs\n", count, rtabmapTime, iterationTime); printf(" iteration(%d) time=%fs/%fs\n", count, rtabmapTime, iterationTime);
} }
if(timeThreshold && rtabmapTime > timeThreshold*100.0f) if(rtabmap.getTimeThreshold() && rtabmapTime > rtabmap.getTimeThreshold()*100.0f)
{ {
printf(" ERROR, there is problem, too much time taken... %fs", rtabmapTime); printf(" ERROR, there is problem, too much time taken... %fs", rtabmapTime);
break; // there is problem, don't continue break; // there is problem, don't continue
+2 -1
View File
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/utilite/ULogger.h> #include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UFile.h> #include <rtabmap/utilite/UFile.h>
#include <rtabmap/utilite/UConversion.h>
#include <rtabmap/core/CameraThread.h> #include <rtabmap/core/CameraThread.h>
#include <rtabmap/core/CameraRGBD.h> #include <rtabmap/core/CameraRGBD.h>
#include <rtabmap/core/Camera.h> #include <rtabmap/core/Camera.h>
@@ -91,7 +92,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
rate = std::atof(argv[i]); rate = uStr2Float(argv[i]);
if(rate < 0.0f) if(rate < 0.0f)
{ {
showUsage(); showUsage();
+3 -3
View File
@@ -72,7 +72,7 @@ int main(int argc, char *argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
planeDistanceThreshold = std::atof(argv[i]); planeDistanceThreshold = uStr2Float(argv[i]);
if(planeDistanceThreshold < 0.0f) if(planeDistanceThreshold < 0.0f)
{ {
showUsage(); showUsage();
@@ -89,7 +89,7 @@ int main(int argc, char *argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
planeEpsAngle = std::atof(argv[i]); planeEpsAngle = uStr2Float(argv[i]);
if(planeEpsAngle < 0.0f) if(planeEpsAngle < 0.0f)
{ {
showUsage(); showUsage();
@@ -106,7 +106,7 @@ int main(int argc, char *argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
clusterTolerance = std::atof(argv[i]); clusterTolerance = uStr2Float(argv[i]);
if(clusterTolerance <= 0.0f) if(clusterTolerance <= 0.0f)
{ {
showUsage(); showUsage();
+40 -69
View File
@@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/utilite/ULogger.h" #include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h" #include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UDirectory.h" #include "rtabmap/utilite/UDirectory.h"
#include "rtabmap/utilite/UFile.h"
#include "rtabmap/utilite/UConversion.h" #include "rtabmap/utilite/UConversion.h"
#include <opencv2/core/core.hpp> #include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui.hpp>
@@ -35,97 +36,70 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
void showUsage() void showUsage()
{ {
printf("Usage:\n" printf("Usage:\n"
"imagesJoiner.exe \"# (see below)\" \"type\" [option]\n" "imagesJoiner.exe [option] path\n"
" # : Name pattern is how the file names are formatted (in number).\n"
" Examples: 4 for pictures with 0001.jpg, 0002.jpg, ..., 0010.jpg, 0100.jpg, 1000.jpg, ...\n"
" 1 for pictures with 1.jpg, 2.jpg, ..., 10.jpg, 100.jpg, 1000.jpg, ...\n"
" type : is the extension (jpg, bmp, png...)\n"
" Options:\n" " Options:\n"
" -inv option for copying odd images on the right\n" " -inv option for copying odd images on the right\n\n");
" -d # destination filename size\n");
exit(1); exit(1);
} }
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
if(argc < 3) if(argc < 2)
{ {
showUsage(); showUsage();
} }
bool inv = false; bool inv = false;
unsigned int sizeFileName = std::atoi(argv[1]); for(int i=1; i<argc-1; ++i)
int sizeTargetFileName = sizeFileName;
std::string type = argv[2];
for(int i=3; i<argc; ++i)
{ {
if(strcmp(argv[i], "-inv") == 0) if(strcmp(argv[i], "-inv") == 0)
{ {
inv = true; inv = true;
printf(" Inversing option activated...\n"); printf(" Inversing option activated...\n");
continue;
} }
if(strcmp(argv[i], "-d") == 0 && i+1<argc) showUsage();
{ printf(" Not recognized option: \"%s\"\n", argv[i]);
sizeTargetFileName = std::atoi(argv[i+1]);
if(sizeTargetFileName < 0)
{
showUsage();
}
printf(" Target size file name option activated=%d\n",sizeTargetFileName);
++i;
}
} }
printf(" Format = %s\n", argv[1]); std::string path = argv[argc-1];
printf(" Type = %s\n", argv[2]); printf(" Path = %s\n", path.c_str());
std::string targetDirectory = "imagesJoined/"; UDirectory dir(path, "jpg bmp png tiff jpeg");
UDirectory::makeDir((UDirectory::currentDir(true) + "imagesJoined").c_str()); if(!dir.isValid())
{
printf("Path invalid!\n");
exit(-1);
}
int counterJoined = 1; std::string targetDirectory = path+"_joined";
int counterImages = 1; UDirectory::makeDir(targetDirectory);
bool imagesExist = true; printf(" Creating directory \"%s\"\n", targetDirectory.c_str());
std::string fileNameA;
std::string fileNameB;
while(imagesExist) std::string fileNameA = dir.getNextFilePath();
std::string fileNameB = dir.getNextFilePath();
int i=1;
while(!fileNameA.empty() && !fileNameB.empty())
{ {
std::string fileNameTarget = uNumber2Str(counterJoined);
if(inv) if(inv)
{ {
fileNameA = uNumber2Str(counterImages+1); std::string tmp = fileNameA;
fileNameB = uNumber2Str(counterImages); fileNameA = fileNameB;
} fileNameB = tmp;
else
{
fileNameA = uNumber2Str(counterImages);
fileNameB = uNumber2Str(counterImages+1);
} }
while(fileNameA.size() < sizeFileName) std::string ext = UFile::getExtension(fileNameA);
{
fileNameA.insert(0, "0");
}
while(fileNameB.size() < sizeFileName) std::string targetFilePath = targetDirectory+UDirectory::separator()+uNumber2Str(i++)+"."+ext;
{
fileNameB.insert(0, "0");
}
while(fileNameTarget.size() < (unsigned int)sizeTargetFileName)
{
fileNameTarget.insert(0, "0");
}
(fileNameTarget.insert(0, targetDirectory) += ".") += type;
(fileNameA += ".") += type;
(fileNameB += ".") += type;
IplImage * imageA = cvLoadImage(fileNameA.c_str(), CV_LOAD_IMAGE_COLOR); IplImage * imageA = cvLoadImage(fileNameA.c_str(), CV_LOAD_IMAGE_COLOR);
IplImage * imageB = cvLoadImage(fileNameB.c_str(), CV_LOAD_IMAGE_COLOR); IplImage * imageB = cvLoadImage(fileNameB.c_str(), CV_LOAD_IMAGE_COLOR);
fileNameA.clear();
fileNameB.clear();
if(imageA && imageB) if(imageA && imageB)
{ {
CvSize sizeA = cvGetSize(imageA); CvSize sizeA = cvGetSize(imageA);
@@ -142,26 +116,28 @@ int main(int argc, char * argv[])
cvCopy( imageB, targetImage ); cvCopy( imageB, targetImage );
cvResetImageROI( targetImage ); cvResetImageROI( targetImage );
if(!cvSaveImage(fileNameTarget.c_str(), targetImage)) if(!cvSaveImage(targetFilePath.c_str(), targetImage))
{ {
printf("Error : saving to \"%s\" goes wrong...\n", fileNameTarget.c_str()); printf("Error : saving to \"%s\" goes wrong...\n", targetFilePath.c_str());
} }
else else
{ {
printf("Saved \"%s\" \n", fileNameTarget.c_str()); printf("Saved \"%s\" \n", targetFilePath.c_str());
} }
cvReleaseImage(&targetImage); cvReleaseImage(&targetImage);
fileNameA = dir.getNextFilePath();
fileNameB = dir.getNextFilePath();
} }
else else
{ {
printf("Error : can't allocated the target image with size (%d,%d)\n", targetSize.width, targetSize.height); printf("Error : can't allocated the target image with size (%d,%d)\n", targetSize.width, targetSize.height);
imagesExist = false;
} }
} }
else else
{ {
imagesExist = false; printf("Error: loading images failed!\n");
} }
if(imageA) if(imageA)
@@ -172,13 +148,8 @@ int main(int argc, char * argv[])
{ {
cvReleaseImage(&imageB); cvReleaseImage(&imageB);
} }
counterJoined++;
counterImages += 2;
} }
printf("%d files processed\n", i-1);
return 0; return 0;
} }
+7 -7
View File
@@ -175,7 +175,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
nndr = std::atof(argv[i]); nndr = uStr2Float(argv[i]);
if(nndr < 0.0f) if(nndr < 0.0f)
{ {
showUsage(); showUsage();
@@ -192,7 +192,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
rate = std::atof(argv[i]); rate = uStr2Float(argv[i]);
if(rate < 0) if(rate < 0)
{ {
showUsage(); showUsage();
@@ -244,7 +244,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
sec = std::atof(argv[i]); sec = uStr2Float(argv[i]);
if(sec < 0.0f) if(sec < 0.0f)
{ {
showUsage(); showUsage();
@@ -261,7 +261,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
distance = std::atof(argv[i]); distance = uStr2Float(argv[i]);
if(distance <= 0) if(distance <= 0)
{ {
showUsage(); showUsage();
@@ -312,7 +312,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
maxDepth = std::atof(argv[i]); maxDepth = uStr2Float(argv[i]);
if(maxDepth < 0) if(maxDepth < 0)
{ {
showUsage(); showUsage();
@@ -380,7 +380,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
voxel = std::atof(argv[i]); voxel = uStr2Float(argv[i]);
if(voxel < 0) if(voxel < 0)
{ {
showUsage(); showUsage();
@@ -414,7 +414,7 @@ int main (int argc, char * argv[])
++i; ++i;
if(i < argc) if(i < argc)
{ {
ratio = std::atof(argv[i]); ratio = uStr2Float(argv[i]);
if(ratio < 0.0f) if(ratio < 0.0f)
{ {
showUsage(); showUsage();
+1 -1
View File
@@ -117,7 +117,7 @@ int main(int argc, char * argv[])
//get descriptor //get descriptor
for(;i<dimension && iter != strList.end(); ++i, ++iter) for(;i<dimension && iter != strList.end(); ++i, ++iter)
{ {
descriptor[i] = std::atof(iter->c_str()); descriptor[i] = uStr2Float(*iter);
} }
if(i != dimension) if(i != dimension)
{ {
@@ -117,6 +117,22 @@ std::string UTILITE_EXP uNumber2Str(float number);
*/ */
std::string UTILITE_EXP uNumber2Str(double number); std::string UTILITE_EXP uNumber2Str(double number);
/**
* Convert a string to a float independent of the locale (comma/dot).
* @param the string
* @return the number
*/
float UTILITE_EXP uStr2Float(const std::string & str);
/**
* Convert a string to a double independent of the locale (comma/dot).
* @param the string
* @return the number
*/
double UTILITE_EXP uStr2Double(const std::string & str);
/** /**
* Convert a bool to a string. * Convert a bool to a string.
* The format used is "true" and "false". * The format used is "true" and "false".
@@ -115,6 +115,12 @@ public:
*/ */
std::string getNextFileName(); std::string getNextFileName();
/**
* Get the next file path.
* @return the next file path
*/
std::string getNextFilePath();
/** /**
* Get all file names. * Get all file names.
* @see UDirectory() * @see UDirectory()
+54 -4
View File
@@ -533,18 +533,68 @@ inline T uVariance(const std::vector<T> & v, const T & m)
} }
/** /**
* Get the norm of the vector : return sqrt(x1*x1 + x2*x2 + x3*x3 + ...) * Get the squared norm of the vector : return x1*x1 + x2*x2 + x3*x3 + ...
* @return the norm of the vector * @return the squared norm of the vector
*/ */
template<class T> template<class T>
inline T uNorm(const std::vector<T> & v) inline T uNormSquared(const std::vector<T> & v)
{ {
float sum = 0.0f; float sum = 0.0f;
for(unsigned int i=0; i<v.size(); ++i) for(unsigned int i=0; i<v.size(); ++i)
{ {
sum += v[i]*v[i]; sum += v[i]*v[i];
} }
return std::sqrt(sum); return sum;
}
/**
* Get the norm of the vector : return sqrt(x1*x1 + x2*x2 + x3*x3 + ...)
* @return the norm of the vector
*/
template<class T>
inline T uNorm(const std::vector<T> & v)
{
return std::sqrt(uNormSquared(v));
}
/**
* Get the squared norm of the vector : return x1*x1 + x2*x2
* @return the squared norm of the vector
*/
template<class T>
inline T uNormSquared(const T & x1, const T & x2)
{
return x1*x1 + x2*x2;
}
/**
* Get the norm of the vector : return sqrt(x1*x1 + x2*x2 + x3*x3)
* @return the norm of the vector
*/
template<class T>
inline T uNorm(const T & x1, const T & x2)
{
return std::sqrt(uNormSquared(x1, x2));
}
/**
* Get the squared norm of the vector : return x1*x1 + x2*x2 + x3*x3
* @return the squared norm of the vector
*/
template<class T>
inline T uNormSquared(const T & x1, const T & x2, const T & x3)
{
return x1*x1 + x2*x2 + x3*x3;
}
/**
* Get the norm of the vector : return sqrt(x1*x1 + x2*x2 + x3*x3)
* @return the norm of the vector
*/
template<class T>
inline T uNorm(const T & x1, const T & x2, const T & x3)
{
return std::sqrt(uNormSquared(x1, x2, x3));
} }
/** /**
+18
View File
@@ -113,6 +113,24 @@ std::string uNumber2Str(double number)
return s.str(); return s.str();
} }
float uStr2Float(const std::string & str)
{
float value = 0.0f;
std::istringstream istr(uReplaceChar(str, ',', '.').c_str());
istr.imbue(std::locale("C"));
istr >> value;
return value;
}
double uStr2Double(const std::string & str)
{
double value = 0.0;
std::istringstream istr(uReplaceChar(str, ',', '.').c_str());
istr.imbue(std::locale("C"));
istr >> value;
return value;
}
std::string uBool2Str(bool boolean) std::string uBool2Str(bool boolean)
{ {
std::string s; std::string s;
+11
View File
@@ -229,6 +229,17 @@ std::string UDirectory::getNextFileName()
return fileName; return fileName;
} }
std::string UDirectory::getNextFilePath()
{
std::string filePath;
if(iFileName_ != fileNames_.end())
{
filePath = path_+separator()+*iFileName_;
++iFileName_;
}
return filePath;
}
void UDirectory::rewind() void UDirectory::rewind()
{ {
iFileName_ = fileNames_.begin(); iFileName_ = fileNames_.begin();