-Added the grid map in the 3D map view (pcl::TextureMesh, available only when pcl>=1.7.2). Refactored how 2d maps are created.

-Fixed export grid map on Mac OS X.


git-svn-id: http://rtabmap.googlecode.com/svn/trunk/rtabmap@1623 f169173b-cf89-36c8-b27e-44dbe73f0c83
This commit is contained in:
matlabbe
2014-07-29 21:48:37 +00:00
parent a81f5785a2
commit 69ad085f88
16 changed files with 517 additions and 249 deletions

View File

@@ -400,6 +400,8 @@ void RTABMAP_EXP rayTrace(const cv::Point2i & start,
cv::Mat & grid, cv::Mat & grid,
bool stopOnObstacle); bool stopOnObstacle);
cv::Mat RTABMAP_EXP convertMap2Image8U(const cv::Mat & map8S);
} // namespace util3d } // namespace util3d
} // namespace rtabmap } // namespace rtabmap

View File

@@ -2056,6 +2056,35 @@ void rayTrace(const cv::Point2i & start, const cv::Point2i & end, cv::Mat & grid
} }
} }
//convert to gray scaled map
cv::Mat convertMap2Image8U(const cv::Mat & map8S)
{
UASSERT(map8S.channels() == 1 && map8S.type() == CV_8S);
cv::Mat map8U = cv::Mat(map8S.rows, map8S.cols, CV_8U);
for (int i = 0; i < map8S.rows; ++i)
{
for (int j = 0; j < map8S.cols; ++j)
{
char v = map8S.at<char>(i, j);
unsigned char gray;
if(v == 0)
{
gray = 178;
}
else if(v == 100)
{
gray = 0;
}
else // -1
{
gray = 89;
}
map8U.at<unsigned char>(i, j) = gray;
}
}
return map8U;
}
} }
} }

View File

@@ -20,6 +20,8 @@
#include <QtCore/QSet> #include <QtCore/QSet>
#include <QtCore/qnamespace.h> #include <QtCore/qnamespace.h>
#include <opencv2/opencv.hpp>
#include <pcl/visualization/mouse_event.h> #include <pcl/visualization/mouse_event.h>
#include <pcl/PCLPointCloud2.h> #include <pcl/PCLPointCloud2.h>
@@ -92,6 +94,14 @@ public:
const pcl::PolygonMesh::Ptr & mesh, const pcl::PolygonMesh::Ptr & mesh,
const Transform & pose = Transform::getIdentity()); const Transform & pose = Transform::getIdentity());
bool addOccupancyGridMap(
const cv::Mat & map8U,
float resolution, // cell size
float xMin,
float yMin,
float opacity);
void removeOccupancyGridMap();
void updateCameraPosition( void updateCameraPosition(
const Transform & pose); const Transform & pose);
@@ -111,6 +121,7 @@ public:
void setCameraFree(); void setCameraFree();
void setCameraLockZ(bool enabled = true); void setCameraLockZ(bool enabled = true);
void setGridShown(bool shown); void setGridShown(bool shown);
void setWorkingDirectory(const QString & path) {_workingDirectory = path;}
public slots: public slots:
void render(); void render();
@@ -150,6 +161,7 @@ private:
Transform _lastPose; Transform _lastPose;
std::list<std::string> _gridLines; std::list<std::string> _gridLines;
QSet<Qt::Key> _keysPressed; QSet<Qt::Key> _keysPressed;
QString _workingDirectory;
}; };
} /* namespace rtabmap */ } /* namespace rtabmap */

View File

@@ -30,6 +30,8 @@
#include <opencv2/core/core.hpp> #include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp> #include <opencv2/features2d/features2d.hpp>
#include <set> #include <set>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <rtabmap/core/Link.h> #include <rtabmap/core/Link.h>
@@ -92,7 +94,7 @@ private:
std::list<std::map<int, rtabmap::Transform> > graphes_; std::list<std::map<int, rtabmap::Transform> > graphes_;
std::map<int, rtabmap::Transform> poses_; std::map<int, rtabmap::Transform> poses_;
std::multimap<int, rtabmap::Link> links_; std::multimap<int, rtabmap::Link> links_;
QMap<int, std::vector<unsigned char> > scans_; std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > scans_;
}; };
#endif /* DATABASEVIEWER_H_ */ #endif /* DATABASEVIEWER_H_ */

View File

@@ -176,7 +176,7 @@ signals:
private: private:
void update3DMapVisibility(bool cloudsShown, bool scansShown); void update3DMapVisibility(bool cloudsShown, bool scansShown);
void updateMapCloud(const std::map<int, Transform> & poses, const Transform & pose); void updateMapCloud(const std::map<int, Transform> & poses, const Transform & pose, const std::multimap<int, Link> & constraints);
void createAndAddCloudToMap(int nodeId, const Transform & pose); void createAndAddCloudToMap(int nodeId, const Transform & pose);
void createAndAddScanToMap(int nodeId, const Transform & pose); void createAndAddScanToMap(int nodeId, const Transform & pose);
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);
@@ -248,6 +248,7 @@ private:
QMap<int, Transform> _localTransformsMap; QMap<int, Transform> _localTransformsMap;
std::map<int, Transform> _currentPosesMap; std::map<int, Transform> _currentPosesMap;
QMap<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > _createdClouds; QMap<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > _createdClouds;
std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > _createdScans;
Transform _odometryCorrection; Transform _odometryCorrection;
Transform _lastOdomPose; Transform _lastOdomPose;
bool _lastOdometryProcessed; bool _lastOdometryProcessed;

View File

@@ -134,6 +134,11 @@ public:
double getCloudFilteringRadius() const; double getCloudFilteringRadius() const;
double getCloudFilteringAngle() const; double getCloudFilteringAngle() const;
bool getGridMapShown() const;
double getGridMapResolution() const;
bool getGridMapFillEmptySpace() const;
double getGridMapOpacity() const;
QString getWorkingDirectory() const; QString getWorkingDirectory() const;
QString getDatabasePath() const; QString getDatabasePath() const;

View File

@@ -65,6 +65,9 @@ inline QImage uCvMat2QImage(const cv::Mat & image, bool isBgr = true)
{ {
// mono grayscale // mono grayscale
qtemp = QImage(image.data, image.cols, image.rows, image.cols, QImage::Format_Indexed8).copy(); qtemp = QImage(image.data, image.cols, image.rows, image.cols, QImage::Format_Indexed8).copy();
QVector<QRgb> my_table;
for(int i = 0; i < 256; i++) my_table.push_back(qRgb(i,i,i));
qtemp.setColorTable(my_table);
} }
else else
{ {

View File

@@ -54,7 +54,8 @@ CloudViewer::CloudViewer(QWidget *parent) :
_menu(0), _menu(0),
_trajectory(new pcl::PointCloud<pcl::PointXYZ>), _trajectory(new pcl::PointCloud<pcl::PointXYZ>),
_maxTrajectorySize(100), _maxTrajectorySize(100),
_lastPose(Transform::getIdentity()) _lastPose(Transform::getIdentity()),
_workingDirectory(".")
{ {
this->setMinimumSize(200, 200); this->setMinimumSize(200, 200);
@@ -322,6 +323,80 @@ bool CloudViewer::addCloudMesh(
return false; return false;
} }
bool CloudViewer::addOccupancyGridMap(
const cv::Mat & map8U,
float resolution, // cell size
float xMin,
float yMin,
float opacity)
{
#if PCL_VERSION_COMPARE(>=, 1, 7, 2)
UASSERT(map8U.channels() == 1 && map8U.type() == CV_8U);
float xSize = float(map8U.cols) * resolution;
float ySize = float(map8U.rows) * resolution;
UDEBUG("resolution=%f, xSize=%f, ySize=%f, xMin=%f, yMin=%f", resolution, xSize, ySize, xMin, yMin);
if(_visualizer->getShapeActorMap()->find("map") == _visualizer->getShapeActorMap()->end())
{
_visualizer->removeShape("map");
}
if(xSize > 0.0f && ySize > 0.0f)
{
pcl::TextureMeshPtr mesh(new pcl::TextureMesh());
pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.push_back(pcl::PointXYZ(xMin, yMin, 0));
cloud.push_back(pcl::PointXYZ(xSize+xMin, yMin, 0));
cloud.push_back(pcl::PointXYZ(xSize+xMin, ySize+yMin, 0));
cloud.push_back(pcl::PointXYZ(xMin, ySize+yMin, 0));
pcl::toPCLPointCloud2(cloud, mesh->cloud);
std::vector<pcl::Vertices> polygons(1);
polygons[0].vertices.push_back(0);
polygons[0].vertices.push_back(1);
polygons[0].vertices.push_back(2);
polygons[0].vertices.push_back(3);
polygons[0].vertices.push_back(0);
mesh->tex_polygons.push_back(polygons);
// default texture materials parameters
pcl::TexMaterial material;
// hack, can we read from memory?
std::string tmpPath = (_workingDirectory+"/.tmp_map.png").toStdString();
cv::imwrite(tmpPath, map8U);
material.tex_file = tmpPath;
mesh->tex_materials.push_back(material);
std::vector<Eigen::Vector2f> coordinates;
coordinates.push_back(Eigen::Vector2f(0,1));
coordinates.push_back(Eigen::Vector2f(1,1));
coordinates.push_back(Eigen::Vector2f(1,0));
coordinates.push_back(Eigen::Vector2f(0,0));
mesh->tex_coordinates.push_back(coordinates);
_visualizer->addTextureMesh(*mesh, "map");
_visualizer->getCloudActorMap()->find("map")->second.actor->GetProperty()->LightingOff();
setCloudOpacity("map", 0.7);
//removed tmp texture file
QFile::remove(tmpPath.c_str());
}
return true;
#else
// not implemented on lower version of PCL
return false;
#endif
}
void CloudViewer::removeOccupancyGridMap()
{
if(_visualizer->getShapeActorMap()->find("map") == _visualizer->getShapeActorMap()->end())
{
_visualizer->removeShape("map");
}
}
void CloudViewer::setTrajectoryShown(bool shown) void CloudViewer::setTrajectoryShown(bool shown)
{ {
_aShowTrajectory->setChecked(shown); _aShowTrajectory->setChecked(shown);
@@ -448,8 +523,13 @@ void CloudViewer::updateCameraPosition(const Transform & pose)
cameras.front().view[2] = Fp[10]; cameras.front().view[2] = Fp[10];
} }
#if PCL_VERSION_COMPARE(>=, 1, 7, 2)
_visualizer->removeCoordinateSystem("reference", 0);
_visualizer->addCoordinateSystem(0.2, m, "reference", 0);
#else
_visualizer->removeCoordinateSystem(0); _visualizer->removeCoordinateSystem(0);
_visualizer->addCoordinateSystem(0.2, m, 0); _visualizer->addCoordinateSystem(0.2, m, 0);
#endif
_visualizer->setCameraPosition( _visualizer->setCameraPosition(
cameras.front().pos[0], cameras.front().pos[1], cameras.front().pos[2], 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().focal[0], cameras.front().focal[1], cameras.front().focal[2],

View File

@@ -914,13 +914,23 @@ void DatabaseViewer::sliderIterationsValueChanged(int value)
memory_->getImageDepth(ids_.at(i), imageBytes, depthBytes, depth2dBytes, fx, fy, cx, cy, localTransform); memory_->getImageDepth(ids_.at(i), imageBytes, depthBytes, depth2dBytes, fx, fy, cx, cy, localTransform);
if(depth2dBytes.size()) if(depth2dBytes.size())
{ {
scans_.insert(ids_.at(i), depth2dBytes); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
cv::Mat depth2d = rtabmap::util3d::uncompressData(depth2dBytes);
cloud = rtabmap::util3d::depth2DToPointCloud(depth2d);
scans_.insert(std::make_pair(ids_.at(i), cloud));
} }
} }
UINFO("Update scans list... done"); UINFO("Update scans list... done");
} }
std::map<int, rtabmap::Transform> & graph = uValueAt(graphes_, value); std::map<int, rtabmap::Transform> & graph = uValueAt(graphes_, value);
ui_->graphViewer->updateGraph(graph, links_, scans_); ui_->graphViewer->updateGraph(graph, links_);
if(graph.size() && scans_.size())
{
float xMin, yMin;
float cell = 0.05;
cv::Mat map = rtabmap::util3d::convertMap2Image8U(rtabmap::util3d::create2DMap(graph, scans_, cell, true, xMin, yMin));
ui_->graphViewer->updateMap(map, cell, xMin, yMin);
}
ui_->label_iterations->setNum(value); ui_->label_iterations->setNum(value);
//compute total length (neighbor links) //compute total length (neighbor links)

View File

@@ -27,6 +27,7 @@
#include <rtabmap/core/util3d.h> #include <rtabmap/core/util3d.h>
#include <rtabmap/gui/UCv2Qt.h> #include <rtabmap/gui/UCv2Qt.h>
#include <rtabmap/utilite/UStl.h> #include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/ULogger.h>
namespace rtabmap { namespace rtabmap {
@@ -34,11 +35,10 @@ class NodeItem: public QGraphicsEllipseItem
{ {
public: public:
// in meter // in meter
NodeItem(int id, const Transform & pose, float radius, const cv::Mat & scan) : NodeItem(int id, const Transform & pose, float radius) :
QGraphicsEllipseItem(QRectF(-radius,-radius,radius*2.0f,radius*2.0f)), QGraphicsEllipseItem(QRectF(-radius,-radius,radius*2.0f,radius*2.0f)),
_id(id), _id(id),
_pose(pose), _pose(pose)
_scan(scan)
{ {
this->setPos(-pose.y(),-pose.x()); this->setPos(-pose.y(),-pose.x());
this->setBrush(pen().color()); this->setBrush(pen().color());
@@ -56,8 +56,6 @@ public:
} }
void setPose(const Transform & pose) {this->setPos(-pose.y(),-pose.x()); _pose=pose;} void setPose(const Transform & pose) {this->setPos(-pose.y(),-pose.x()); _pose=pose;}
void setScan(const cv::Mat & scan) {_scan = scan;}
const cv::Mat & getScan() const {return _scan;}
protected: protected:
virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event ) virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
@@ -77,7 +75,6 @@ protected:
private: private:
int _id; int _id;
Transform _pose; Transform _pose;
cv::Mat _scan;
}; };
class LinkItem: public QGraphicsLineItem class LinkItem: public QGraphicsLineItem
@@ -136,11 +133,8 @@ GraphViewer::GraphViewer(QWidget * parent) :
_linkWidth(0), _linkWidth(0),
_gridMap(0), _gridMap(0),
_lastReferential(0), _lastReferential(0),
_gridCellSize(0.05f), _gridCellSize(0.0f)
_gridUnknownSpaceFilled(true)
{ {
Q_ASSERT(_gridCellSize > 0);
this->setScene(new QGraphicsScene(this)); this->setScene(new QGraphicsScene(this));
this->setDragMode(QGraphicsView::ScrollHandDrag); this->setDragMode(QGraphicsView::ScrollHandDrag);
_workingDirectory = QDir::homePath(); _workingDirectory = QDir::homePath();
@@ -170,8 +164,6 @@ GraphViewer::GraphViewer(QWidget * parent) :
_gridMap = this->scene()->addPixmap(QPixmap()); _gridMap = this->scene()->addPixmap(QPixmap());
_gridMap->scale(_gridCellSize, -_gridCellSize);
_gridMap->setRotation(90);
_gridMap->setZValue(0); _gridMap->setZValue(0);
_gridMap->setParentItem(_root); _gridMap->setParentItem(_root);
} }
@@ -181,8 +173,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)
const QMap<int, std::vector<unsigned char> > & scans)
{ {
//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)
@@ -199,40 +190,19 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
iter.value()->hide(); iter.value()->hide();
} }
std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > scanClouds;
for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter) for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
{ {
QMap<int, NodeItem*>::iterator itemIter = _nodeItems.find(iter->first); QMap<int, NodeItem*>::iterator itemIter = _nodeItems.find(iter->first);
if(itemIter != _nodeItems.end()) if(itemIter != _nodeItems.end())
{ {
itemIter.value()->setPose(iter->second); itemIter.value()->setPose(iter->second);
if(itemIter.value()->getScan().empty() && scans.contains(iter->first))
{
cv::Mat depth2d = util3d::uncompressData(scans.value(iter->first));
itemIter.value()->setScan(depth2d);
}
if(!itemIter.value()->getScan().empty() && _gridMap->isVisible())
{
scanClouds.insert(std::make_pair(iter->first, util3d::depth2DToPointCloud(itemIter.value()->getScan())));
}
itemIter.value()->show(); itemIter.value()->show();
} }
else else
{ {
// create node item // create node item
QMap<int, std::vector<unsigned char> >::const_iterator jter = scans.find(iter->first);
cv::Mat depth2d;
if(jter != scans.end())
{
depth2d = util3d::uncompressData(jter.value());
if(_gridMap->isVisible())
{
scanClouds.insert(std::make_pair(iter->first, util3d::depth2DToPointCloud(depth2d)));
}
}
const Transform & pose = iter->second; const Transform & pose = iter->second;
NodeItem * item = new NodeItem(iter->first, pose, _nodeRadius, depth2d); NodeItem * item = new NodeItem(iter->first, pose, _nodeRadius);
this->scene()->addItem(item); this->scene()->addItem(item);
item->setZValue(2); item->setZValue(2);
item->setColor(_nodeColor); item->setColor(_nodeColor);
@@ -324,41 +294,6 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
} }
} }
if(scanClouds.size())
{
float xMin=0.0f, yMin=0.0f;
cv::Mat map8S = util3d::create2DMap(poses, scanClouds, _gridCellSize, _gridUnknownSpaceFilled, xMin, yMin);
cv::Mat map8U(map8S.rows, map8S.cols, CV_8U);
//convert to gray scaled map
for (int i = 0; i < map8S.rows; ++i)
{
for (int j = 0; j < map8S.cols; ++j)
{
char v = map8S.at<char>(i, j);
unsigned char gray;
if(v == 0)
{
gray = 178;
}
else if(v == 100)
{
gray = 0;
}
else // -1
{
gray = 89;
}
map8U.at<unsigned char>(i, j) = gray;
}
}
QImage image = uCvMat2QImage(map8U, false);
_gridMap->resetTransform();
_gridMap->scale(_gridCellSize, -_gridCellSize);
_gridMap->setRotation(90);
_gridMap->setPixmap(QPixmap::fromImage(image));
_gridMap->setPos(-yMin, -xMin);
}
if(_nodeItems.size()) if(_nodeItems.size())
{ {
(--_nodeItems.end()).value()->setColor(Qt::green); (--_nodeItems.end()).value()->setColor(Qt::green);
@@ -374,6 +309,25 @@ void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
this->fitInView(this->scene()->itemsBoundingRect(), Qt::KeepAspectRatio); this->fitInView(this->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
} }
void GraphViewer::updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin)
{
UASSERT(map8U.empty() || (!map8U.empty() && resolution > 0.0f));
if(!map8U.empty())
{
_gridCellSize = resolution;
QImage image = uCvMat2QImage(map8U, false);
_gridMap->resetTransform();
_gridMap->scale(resolution, -resolution);
_gridMap->setRotation(90);
_gridMap->setPixmap(QPixmap::fromImage(image));
_gridMap->setPos(-yMin, -xMin);
}
else
{
this->clearMap();
}
}
void GraphViewer::clearGraph() void GraphViewer::clearGraph()
{ {
qDeleteAll(_nodeItems); qDeleteAll(_nodeItems);
@@ -382,11 +336,21 @@ void GraphViewer::clearGraph()
_neighborLinkItems.clear(); _neighborLinkItems.clear();
qDeleteAll(_loopLinkItems); qDeleteAll(_loopLinkItems);
_loopLinkItems.clear(); _loopLinkItems.clear();
_gridMap->setPixmap(QPixmap());
_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
} }
void GraphViewer::clearMap()
{
_gridMap->setPixmap(QPixmap());
}
void GraphViewer::clearAll()
{
clearMap();
clearGraph();
}
void GraphViewer::wheelEvent ( QWheelEvent * event ) void GraphViewer::wheelEvent ( QWheelEvent * event )
{ {
if(event->delta() > 0) if(event->delta() > 0)
@@ -412,10 +376,6 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
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..."));
menu.addSeparator(); menu.addSeparator();
QAction * aSetGridCellSize = menu.addAction(tr("Set grid cell size..."));
QAction * aSetGridUnknownSpaceFilled = menu.addAction(tr("Unknown grid space filled"));
aSetGridUnknownSpaceFilled->setCheckable(true);
aSetGridUnknownSpaceFilled->setChecked(_gridUnknownSpaceFilled);
QAction * aShowHideGridMap; QAction * aShowHideGridMap;
if(_gridMap->isVisible()) if(_gridMap->isVisible())
{ {
@@ -448,7 +408,10 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + (isPNG?".png":".svg")); QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + (isPNG?".png":".svg"));
//_root->setScale(this->transform().m11()); // current view //_root->setScale(this->transform().m11()); // current view
if(_gridCellSize)
{
_root->setScale(1.0f/_gridCellSize); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm) _root->setScale(1.0f/_gridCellSize); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm)
}
this->scene()->clearSelection(); // Selections would also render to the file this->scene()->clearSelection(); // Selections would also render to the file
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
@@ -575,19 +538,6 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
} }
} }
} }
else if(r == aSetGridCellSize)
{
bool ok;
double value = QInputDialog::getDouble(this, tr("Grid cell size"), tr("Width (m)"), _gridCellSize, 0.01, 10, 2, &ok);
if(ok)
{
_gridCellSize = value;
}
}
else if(r == aSetGridUnknownSpaceFilled)
{
_gridUnknownSpaceFilled = aSetGridUnknownSpaceFilled->isChecked();
}
else if(r == aShowHideGridMap) else if(r == aShowHideGridMap)
{ {
_gridMap->setVisible(!_gridMap->isVisible()); _gridMap->setVisible(!_gridMap->isVisible());

View File

@@ -11,6 +11,7 @@
#include <QtGui/QGraphicsView> #include <QtGui/QGraphicsView>
#include <QtCore/QMap> #include <QtCore/QMap>
#include <rtabmap/core/Link.h> #include <rtabmap/core/Link.h>
#include <opencv2/opencv.hpp>
#include <map> #include <map>
class QGraphicsItem; class QGraphicsItem;
@@ -28,12 +29,13 @@ public:
virtual ~GraphViewer(); virtual ~GraphViewer();
void updateGraph(const std::map<int, Transform> & poses, void updateGraph(const std::map<int, Transform> & poses,
const std::multimap<int, Link> & constraints, const std::multimap<int, Link> & constraints);
const QMap<int, std::vector<unsigned char> > & scans); void updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin);
void clearGraph(); void clearGraph();
void clearMap();
void clearAll();
void setWorkingDirectory(const QString & path) {_workingDirectory = path;} void setWorkingDirectory(const QString & path) {_workingDirectory = path;}
void setGridCellSize(float size) {Q_ASSERT(_gridCellSize>0.0f); _gridCellSize= size;}
protected: protected:
virtual void wheelEvent ( QWheelEvent * event ); virtual void wheelEvent ( QWheelEvent * event );
@@ -53,7 +55,6 @@ private:
QGraphicsPixmapItem * _gridMap; QGraphicsPixmapItem * _gridMap;
QGraphicsItemGroup * _lastReferential; QGraphicsItemGroup * _lastReferential;
float _gridCellSize; float _gridCellSize;
bool _gridUnknownSpaceFilled;
}; };
} /* namespace rtabmap */ } /* namespace rtabmap */

View File

@@ -342,6 +342,7 @@ 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());
splash.close(); splash.close();
@@ -953,27 +954,7 @@ void MainWindow::processStats(const rtabmap::Statistics & stat)
if(stat.poses().size()) if(stat.poses().size())
{ {
// update pose only if a odometry is not received // update pose only if a odometry is not received
updateMapCloud(stat.poses(), _odometryReceived?Transform():stat.currentPose()); updateMapCloud(stat.poses(), _odometryReceived?Transform():stat.currentPose(), stat.constraints());
// update some widgets
if(_ui->graphicsView_graphView->isVisible())
{
std::map<int, Transform> poses;
if(_preferencesDialog->isCloudFiltering() && stat.poses().size())
{
float radius = _preferencesDialog->getCloudFilteringRadius();
float angle = _preferencesDialog->getCloudFilteringAngle()*CV_PI/180.0; // convert to rad
poses = util3d::radiusPosesFiltering(stat.poses(), radius, angle);
// make sure the last is here
poses.insert(*stat.poses().rbegin());
}
else
{
poses = stat.poses();
}
_ui->graphicsView_graphView->updateGraph(poses, stat.constraints(), _depths2DMap);
}
_odometryReceived = false; _odometryReceived = false;
@@ -1055,7 +1036,10 @@ void MainWindow::processStats(const rtabmap::Statistics & stat)
_processingStatistics = false; _processingStatistics = false;
} }
void MainWindow::updateMapCloud(const std::map<int, Transform> & posesIn, const Transform & currentPose) void MainWindow::updateMapCloud(
const std::map<int, Transform> & posesIn,
const Transform & currentPose,
const std::multimap<int, Link> & constraints)
{ {
if(posesIn.size()) if(posesIn.size())
{ {
@@ -1140,7 +1124,7 @@ void MainWindow::updateMapCloud(const std::map<int, Transform> & posesIn, const
// 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)) if(_preferencesDialog->isScansShown(0) || _ui->graphicsView_graphView->isVisible() || _preferencesDialog->getGridMapShown())
{ {
if(viewerClouds.contains(scanName)) if(viewerClouds.contains(scanName))
{ {
@@ -1162,6 +1146,11 @@ void MainWindow::updateMapCloud(const std::map<int, Transform> & posesIn, const
{ {
this->createAndAddScanToMap(iter->first, iter->second); this->createAndAddScanToMap(iter->first, iter->second);
} }
if(!_preferencesDialog->isScansShown(0))
{
UDEBUG("Hide scan %s", scanName.c_str());
_ui->widget_cloudViewer->setCloudVisibility(scanName.c_str(), false);
}
} }
else if(viewerClouds.contains(scanName)) else if(viewerClouds.contains(scanName))
{ {
@@ -1192,6 +1181,39 @@ void MainWindow::updateMapCloud(const std::map<int, Transform> & posesIn, const
} }
} }
// Update occupancy grid map in 3D map view and graph view
if(_ui->graphicsView_graphView->isVisible() && constraints.size())
{
_ui->graphicsView_graphView->updateGraph(poses, constraints);
}
cv::Mat map8U;
if((_ui->graphicsView_graphView->isVisible() || _preferencesDialog->getGridMapShown()) && _depths2DMap.size())
{
float xMin, yMin;
float resolution = _preferencesDialog->getGridMapResolution();
bool fillEmptySpace = _preferencesDialog->getGridMapFillEmptySpace();
cv::Mat map8S = util3d::create2DMap(poses, _createdScans, resolution, fillEmptySpace, xMin, yMin);
if(!map8S.empty())
{
//convert to gray scaled map
map8U = util3d::convertMap2Image8U(map8S);
if(_preferencesDialog->getGridMapShown())
{
float opacity = _preferencesDialog->getGridMapOpacity();
_ui->widget_cloudViewer->addOccupancyGridMap(map8U, resolution, xMin, yMin, opacity);
}
if(_ui->graphicsView_graphView->isVisible())
{
_ui->graphicsView_graphView->updateMap(map8U, resolution, xMin, yMin);
}
}
}
if(!_preferencesDialog->getGridMapShown())
{
_ui->widget_cloudViewer->removeOccupancyGridMap();
}
if(viewerClouds.contains("cloudOdom")) if(viewerClouds.contains("cloudOdom"))
{ {
if(!_preferencesDialog->isCloudsShown(1)) if(!_preferencesDialog->isCloudsShown(1))
@@ -1318,6 +1340,10 @@ void MainWindow::createAndAddScanToMap(int nodeId, const Transform & pose)
{ {
UERROR("Adding cloud %d to viewer failed!", nodeId); UERROR("Adding cloud %d to viewer failed!", nodeId);
} }
else
{
_createdScans.insert(std::make_pair(nodeId, cloud));
}
_ui->widget_cloudViewer->setCloudOpacity(scanName, _preferencesDialog->getScanOpacity(0)); _ui->widget_cloudViewer->setCloudOpacity(scanName, _preferencesDialog->getScanOpacity(0));
_ui->widget_cloudViewer->setCloudPointSize(scanName, _preferencesDialog->getScanPointSize(0)); _ui->widget_cloudViewer->setCloudPointSize(scanName, _preferencesDialog->getScanPointSize(0));
} }
@@ -1502,19 +1528,8 @@ void MainWindow::processRtabmapEvent3DMap(const rtabmap::RtabmapEvent3DMap & eve
_initProgressDialog->appendText("Updating the 3D map cloud..."); _initProgressDialog->appendText("Updating the 3D map cloud...");
_initProgressDialog->incrementStep(); _initProgressDialog->incrementStep();
QApplication::processEvents(); QApplication::processEvents();
this->updateMapCloud(event.getPoses(), Transform()); this->updateMapCloud(event.getPoses(), Transform(), event.getConstraints());
_initProgressDialog->appendText("Updating the 3D map cloud... done."); _initProgressDialog->appendText("Updating the 3D map cloud... done.");
if(_ui->graphicsView_graphView->isVisible())
{
_initProgressDialog->appendText("Updating the graph view...");
_initProgressDialog->incrementStep();
_ui->graphicsView_graphView->updateGraph(
event.getPoses(),
event.getConstraints(),
_depths2DMap);
_initProgressDialog->appendText("Updating the graph view... done.");
}
} }
else else
{ {
@@ -1590,7 +1605,7 @@ void MainWindow::applyPrefSettings(PreferencesDialog::PANEL_FLAGS flags)
UDEBUG("Cloud rendering settings changed..."); UDEBUG("Cloud rendering settings changed...");
if(_currentPosesMap.size()) if(_currentPosesMap.size())
{ {
this->updateMapCloud(_currentPosesMap, Transform()); this->updateMapCloud(std::map<int, Transform>(_currentPosesMap), Transform(), std::multimap<int, Link>());
} }
} }
@@ -2682,6 +2697,7 @@ void MainWindow::clearTheCache()
_depthCysMap.clear(); _depthCysMap.clear();
_localTransformsMap.clear(); _localTransformsMap.clear();
_createdClouds.clear(); _createdClouds.clear();
_createdScans.clear();
_ui->widget_cloudViewer->removeAllClouds(); _ui->widget_cloudViewer->removeAllClouds();
_ui->widget_cloudViewer->render(); _ui->widget_cloudViewer->render();
_currentPosesMap.clear(); _currentPosesMap.clear();
@@ -2703,7 +2719,7 @@ void MainWindow::clearTheCache()
_ui->label_stats_loopClosuresRejected->setText("0"); _ui->label_stats_loopClosuresRejected->setText("0");
_refIds.clear(); _refIds.clear();
_loopClosureIds.clear(); _loopClosureIds.clear();
_ui->graphicsView_graphView->clearGraph(); _ui->graphicsView_graphView->clearAll();
} }
void MainWindow::updateElapsedTime() void MainWindow::updateElapsedTime()
@@ -2942,12 +2958,22 @@ void MainWindow::exportGridMap()
map8U.at<unsigned char>(i, j) = gray; map8U.at<unsigned char>(i, j) = gray;
} }
} }
QImage image = uCvMat2QImage(map8U, false); QImage image = uCvMat2QImage(map8U, false);
QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), "grid.png", tr("Image (*.bmp *.png)")); QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), "grid.png", tr("Image (*.png *.bmp)"));
if(!path.isEmpty()) if(!path.isEmpty())
{ {
QPixmap::fromImage(image.mirrored(false, true).transformed(QTransform().rotate(-90))).save(path); if(QFileInfo(path).suffix() != "png" && QFileInfo(path).suffix() != "bmp")
{
//use png by default
path += ".png";
}
QImage img = image.mirrored(false, true).transformed(QTransform().rotate(-90));
QPixmap::fromImage(img).save(path);
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
} }
} }
} }
@@ -3032,17 +3058,16 @@ bool MainWindow::getExportedScans(std::map<int, pcl::PointCloud<pcl::PointXYZ>::
_initProgressDialog->setAutoClose(true, 1); _initProgressDialog->setAutoClose(true, 1);
_initProgressDialog->resetProgress(); _initProgressDialog->resetProgress();
_initProgressDialog->show(); _initProgressDialog->show();
_initProgressDialog->setMaximumSteps(int(poses.size())*assemble?1:2+1); _initProgressDialog->setMaximumSteps(int(poses.size())*(assemble?1:2)+1);
int count = 1; int count = 1;
int i = 0; int i = 0;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter) for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{ {
bool inserted = false; bool inserted = false;
if(_depths2DMap.contains(iter->first)) if(_createdScans.find(iter->first) != _createdScans.end())
{ {
cv::Mat depth2d = util3d::uncompressData(_depths2DMap.value(iter->first)); pcl::PointCloud<pcl::PointXYZ>::Ptr scan = _createdScans.at(iter->first);
pcl::PointCloud<pcl::PointXYZ>::Ptr scan = util3d::depth2DToPointCloud(depth2d);
if(scan->size()) if(scan->size())
{ {
if(assemble) if(assemble)
@@ -3316,7 +3341,7 @@ void MainWindow::saveClouds(const std::map<int, pcl::PointCloud<pcl::PointXYZRGB
{ {
if(clouds.size() == 1) if(clouds.size() == 1)
{ {
QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), "cloud.ply", tr("Point cloud data (*.ply *.pcd)")); QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), _preferencesDialog->getWorkingDirectory()+"/cloud.ply", tr("Point cloud data (*.ply *.pcd)"));
if(!path.isEmpty()) if(!path.isEmpty())
{ {
if(clouds.begin()->second->size()) if(clouds.begin()->second->size())
@@ -3424,7 +3449,7 @@ void MainWindow::saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes)
{ {
if(meshes.size() == 1) if(meshes.size() == 1)
{ {
QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), "mesh.ply", tr("Mesh (*.ply)")); QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), _preferencesDialog->getWorkingDirectory()+"/mesh.ply", tr("Mesh (*.ply)"));
if(!path.isEmpty()) if(!path.isEmpty())
{ {
if(meshes.begin()->second->polygons.size()) if(meshes.begin()->second->polygons.size())
@@ -3523,7 +3548,7 @@ void MainWindow::saveScans(const std::map<int, pcl::PointCloud<pcl::PointXYZ>::P
{ {
if(scans.size() == 1) if(scans.size() == 1)
{ {
QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), "scan.ply", tr("Point cloud data (*.ply *.pcd)")); QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), _preferencesDialog->getWorkingDirectory()+"/scan.ply", tr("Point cloud data (*.ply *.pcd)"));
if(!path.isEmpty()) if(!path.isEmpty())
{ {
if(scans.begin()->second->size()) if(scans.begin()->second->size())

View File

@@ -96,6 +96,12 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->odom_bin_nn->removeItem(4); _ui->odom_bin_nn->removeItem(4);
} }
#if PCL_VERSION_COMPARE(<, 1, 7, 2)
_ui->checkBox_map_shown->setChecked(false);
_ui->checkBox_map_shown->setEnabled(false);
_ui->label_map_shown->setText(_ui->label_map_shown->text() + " (Disabled, PCL >=1.7.2 required)");
#endif
_ui->predictionPlot->showLegend(false); _ui->predictionPlot->showLegend(false);
QButtonGroup * buttonGroup = new QButtonGroup(this); QButtonGroup * buttonGroup = new QButtonGroup(this);
@@ -180,6 +186,11 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->doubleSpinBox_cloudFilterRadius, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteCloudRenderingPanel())); connect(_ui->doubleSpinBox_cloudFilterRadius, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteCloudRenderingPanel()));
connect(_ui->doubleSpinBox_cloudFilterAngle, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteCloudRenderingPanel())); connect(_ui->doubleSpinBox_cloudFilterAngle, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteCloudRenderingPanel()));
connect(_ui->checkBox_map_shown, SIGNAL(clicked(bool)), this, SLOT(makeObsoleteCloudRenderingPanel()));
connect(_ui->doubleSpinBox_map_resolution, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteCloudRenderingPanel()));
connect(_ui->checkBox_map_fillEmptySpace, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteCloudRenderingPanel()));
connect(_ui->doubleSpinBox_map_opacity, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteCloudRenderingPanel()));
//Logging panel //Logging panel
connect(_ui->comboBox_loggerLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteLoggingPanel())); connect(_ui->comboBox_loggerLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteLoggingPanel()));
connect(_ui->comboBox_loggerEventLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteLoggingPanel())); connect(_ui->comboBox_loggerEventLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteLoggingPanel()));
@@ -742,6 +753,11 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->groupBox_poseFiltering->setChecked(false); _ui->groupBox_poseFiltering->setChecked(false);
_ui->doubleSpinBox_cloudFilterRadius->setValue(0.1); _ui->doubleSpinBox_cloudFilterRadius->setValue(0.1);
_ui->doubleSpinBox_cloudFilterAngle->setValue(30); _ui->doubleSpinBox_cloudFilterAngle->setValue(30);
_ui->checkBox_map_shown->setChecked(false);
_ui->doubleSpinBox_map_resolution->setValue(0.05);
_ui->checkBox_map_fillEmptySpace->setChecked(true);
_ui->doubleSpinBox_map_opacity->setValue(0.75);
} }
else if(groupBox->objectName() == _ui->groupBox_logging1->objectName()) else if(groupBox->objectName() == _ui->groupBox_logging1->objectName())
{ {
@@ -972,6 +988,11 @@ void PreferencesDialog::readGuiSettings(const QString & filePath)
_ui->doubleSpinBox_cloudFilterRadius->setValue(settings.value("cloudFilteringRadius", _ui->doubleSpinBox_cloudFilterRadius->value()).toDouble()); _ui->doubleSpinBox_cloudFilterRadius->setValue(settings.value("cloudFilteringRadius", _ui->doubleSpinBox_cloudFilterRadius->value()).toDouble());
_ui->doubleSpinBox_cloudFilterAngle->setValue(settings.value("cloudFilteringAngle", _ui->doubleSpinBox_cloudFilterAngle->value()).toDouble()); _ui->doubleSpinBox_cloudFilterAngle->setValue(settings.value("cloudFilteringAngle", _ui->doubleSpinBox_cloudFilterAngle->value()).toDouble());
_ui->checkBox_map_shown->setChecked(settings.value("gridMapShown", _ui->checkBox_map_shown->isChecked()).toBool());
_ui->doubleSpinBox_map_resolution->setValue(settings.value("gridMapResolution", _ui->doubleSpinBox_map_resolution->value()).toDouble());
_ui->checkBox_map_fillEmptySpace->setChecked(settings.value("gridMapFillEmptySpace", _ui->checkBox_map_fillEmptySpace->isChecked()).toBool());
_ui->doubleSpinBox_map_opacity->setValue(settings.value("gridMapOpacity", _ui->doubleSpinBox_map_opacity->value()).toDouble());
settings.endGroup(); // General settings.endGroup(); // General
settings.endGroup(); // rtabmap settings.endGroup(); // rtabmap
@@ -1178,6 +1199,10 @@ void PreferencesDialog::writeGuiSettings(const QString & filePath)
settings.setValue("cloudFilteringRadius", _ui->doubleSpinBox_cloudFilterRadius->value()); settings.setValue("cloudFilteringRadius", _ui->doubleSpinBox_cloudFilterRadius->value());
settings.setValue("cloudFilteringAngle", _ui->doubleSpinBox_cloudFilterAngle->value()); settings.setValue("cloudFilteringAngle", _ui->doubleSpinBox_cloudFilterAngle->value());
settings.setValue("gridMapShown", _ui->checkBox_map_shown->isChecked());
settings.setValue("gridMapResolution", _ui->doubleSpinBox_map_resolution->value());
settings.setValue("gridMapFillEmptySpace", _ui->checkBox_map_fillEmptySpace->isChecked());
settings.setValue("gridMapOpacity", _ui->doubleSpinBox_map_opacity->value());
settings.endGroup(); // General settings.endGroup(); // General
settings.endGroup(); // rtabmap settings.endGroup(); // rtabmap
@@ -2467,6 +2492,23 @@ double PreferencesDialog::getCloudFilteringAngle() const
{ {
return _ui->doubleSpinBox_cloudFilterAngle->value(); return _ui->doubleSpinBox_cloudFilterAngle->value();
} }
bool PreferencesDialog::getGridMapShown() const
{
return _ui->checkBox_map_shown->isChecked();
}
double PreferencesDialog::getGridMapResolution() const
{
return _ui->doubleSpinBox_map_resolution->value();
}
bool PreferencesDialog::getGridMapFillEmptySpace() const
{
return _ui->checkBox_map_fillEmptySpace->isChecked();
}
double PreferencesDialog::getGridMapOpacity() const
{
return _ui->doubleSpinBox_map_opacity->value();
}
// Source // Source
double PreferencesDialog::getGeneralInputRate() const double PreferencesDialog::getGeneralInputRate() const

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>678</width> <width>678</width>
<height>550</height> <height>579</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -22,9 +22,7 @@
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize"> <widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize">
<property name="suffix"> <property name="suffix">
@@ -107,8 +105,6 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
@@ -119,7 +115,7 @@
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QFormLayout" name="formLayout_3"> <layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize_assembled"> <widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize_assembled">
<property name="suffix"> <property name="suffix">
@@ -163,7 +159,7 @@
<property name="checked"> <property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@@ -175,7 +171,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QFormLayout" name="formLayout_4"> <layout class="QGridLayout" name="gridLayout_3" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_mlsRadius"> <widget class="QDoubleSpinBox" name="doubleSpinBox_mlsRadius">
<property name="suffix"> <property name="suffix">
@@ -222,10 +218,7 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
<property name="checked"> <property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,1">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QSpinBox" name="spinBox_normalKSearch"> <widget class="QSpinBox" name="spinBox_normalKSearch">
<property name="minimum"> <property name="minimum">

View File

@@ -63,9 +63,9 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>-185</y> <y>0</y>
<width>737</width> <width>732</width>
<height>893</height> <height>1198</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_16"> <layout class="QVBoxLayout" name="verticalLayout_16">
@@ -361,7 +361,7 @@ Show a yellow background when the number of odometry inliers goes under this thr
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QFormLayout" name="formLayout_22"> <layout class="QGridLayout" name="gridLayout_17" rowstretch="0,0" columnstretch="0,1">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_cloudFilterRadius"> <widget class="QDoubleSpinBox" name="doubleSpinBox_cloudFilterRadius">
<property name="suffix"> <property name="suffix">
@@ -382,13 +382,6 @@ Show a yellow background when the number of odometry inliers goes under this thr
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QLabel" name="label_48">
<property name="text">
<string>Angle</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_cloudFilterAngle"> <widget class="QDoubleSpinBox" name="doubleSpinBox_cloudFilterAngle">
<property name="suffix"> <property name="suffix">
@@ -405,6 +398,122 @@ Show a yellow background when the number of odometry inliers goes under this thr
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QLabel" name="label_48">
<property name="text">
<string>Angle</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_gridMap">
<property name="title">
<string>Occupancy grid map</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_45">
<item>
<widget class="QLabel" name="label_167">
<property name="text">
<string>When laser scans are used, a 2D grid map can be created. When the Graph view is visible or if the &quot;Show in 3D map view&quot; below is checked, the grid map is generated.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_20" columnstretch="0,1">
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_map_resolution">
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_159">
<property name="text">
<string>Resolution (cell size)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_164">
<property name="text">
<string>Fill empty space</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBox_map_fillEmptySpace">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_map_opacity">
<property name="suffix">
<string/>
</property>
<property name="minimum">
<double>0.750000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>0.750000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_170">
<property name="text">
<string>Opacity</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_map_shown">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_map_shown">
<property name="text">
<string>Show in 3D map view</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>

View File

@@ -8,6 +8,7 @@ SET(INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/corelib/include ${PROJECT_SOURCE_DIR}/corelib/include
${PROJECT_SOURCE_DIR}/guilib/include ${PROJECT_SOURCE_DIR}/guilib/include
${OpenCV_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS}
${PCL_INCLUDE_DIRS}
) )
INCLUDE(${QT_USE_FILE}) INCLUDE(${QT_USE_FILE})
@@ -15,8 +16,11 @@ INCLUDE(${QT_USE_FILE})
SET(LIBRARIES SET(LIBRARIES
${OpenCV_LIBRARIES} ${OpenCV_LIBRARIES}
${QT_LIBRARIES} ${QT_LIBRARIES}
${PCL_LIBRARIES}
) )
add_definitions(${PCL_DEFINITIONS})
INCLUDE_DIRECTORIES(${INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${INCLUDE_DIRS})
IF(MINGW) IF(MINGW)