Added Monocular SLAM (experimental), Odometry classes refactoring

This commit is contained in:
Mathieu Labbe
2015-04-23 22:01:41 -04:00
parent 39dce825d6
commit fa3a2421f6
39 changed files with 4138 additions and 2702 deletions

View File

@@ -182,6 +182,7 @@ DatabaseViewer::DatabaseViewer(QWidget * parent) :
connect(ui_->horizontalSlider_neighbors, SIGNAL(sliderMoved(int)), this, SLOT(sliderNeighborValueChanged(int)));
connect(ui_->horizontalSlider_loops, SIGNAL(sliderMoved(int)), this, SLOT(sliderLoopValueChanged(int)));
connect(ui_->checkBox_showOptimized, SIGNAL(stateChanged(int)), this, SLOT(updateConstraintView()));
connect(ui_->checkBox_show3DWords, SIGNAL(stateChanged(int)), this, SLOT(updateConstraintView()));
ui_->checkBox_showOptimized->setEnabled(false);
ui_->horizontalSlider_iterations->setTracking(false);
@@ -1038,8 +1039,8 @@ void DatabaseViewer::view3DMap()
cloud = rtabmap::util3d::cloudFromDisparityRGB(
data.getImageRaw(),
util3d::disparityFromStereoImages(leftImg, data.getDepthRaw()),
data.getDepthCx(), data.getDepthCy(),
data.getDepthFx(), data.getDepthFy(),
data.getCx(), data.getCy(),
data.getFx(), data.getFy(),
decimation);
}
else
@@ -1047,8 +1048,8 @@ void DatabaseViewer::view3DMap()
cloud = rtabmap::util3d::cloudFromDepthRGB(
data.getImageRaw(),
data.getDepthRaw(),
data.getDepthCx(), data.getDepthCy(),
data.getDepthFx(), data.getDepthFy(),
data.getCx(), data.getCy(),
data.getFx(), data.getFy(),
decimation);
}
@@ -1148,8 +1149,8 @@ void DatabaseViewer::generate3DMap()
cloud = rtabmap::util3d::cloudFromDisparityRGB(
data.getImageRaw(),
util3d::disparityFromStereoImages(leftImg, data.getDepthRaw()),
data.getDepthCx(), data.getDepthCy(),
data.getDepthFx(), data.getDepthFy(),
data.getCx(), data.getCy(),
data.getFx(), data.getFy(),
decimation);
}
else
@@ -1157,8 +1158,8 @@ void DatabaseViewer::generate3DMap()
cloud = rtabmap::util3d::cloudFromDepthRGB(
data.getImageRaw(),
data.getDepthRaw(),
data.getDepthCx(), data.getDepthCy(),
data.getDepthFx(), data.getDepthFy(),
data.getCx(), data.getCy(),
data.getFx(), data.getFy(),
decimation);
}
@@ -1443,8 +1444,8 @@ void DatabaseViewer::update(int value,
cloud = util3d::cloudFromStereoImages(
data.getImageRaw(),
data.getDepthRaw(),
data.getDepthCx(), data.getDepthCy(),
data.getDepthFx(), data.getDepthFy(),
data.getCx(), data.getCy(),
data.getFx(), data.getFy(),
1);
}
else
@@ -1452,8 +1453,8 @@ void DatabaseViewer::update(int value,
cloud = util3d::cloudFromDepthRGB(
data.getImageRaw(),
data.getDepthRaw(),
data.getDepthCx(), data.getDepthCy(),
data.getDepthFx(), data.getDepthFy(),
data.getCx(), data.getCy(),
data.getFx(), data.getFy(),
1);
}
view3D->addOrUpdateCloud("0", cloud, data.getLocalTransform());
@@ -1605,11 +1606,11 @@ void DatabaseViewer::updateStereo(const Signature * data)
std::vector<cv::KeyPoint> kpts;
cv::Rect roi = Feature2D::computeRoi(leftMono, "0.03 0.03 0.04 0.04");
ParametersMap parameters;
parameters.insert(ParametersPair(Parameters::kGFTTMaxCorners(), "1000"));
parameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), "1000"));
parameters.insert(ParametersPair(Parameters::kGFTTMinDistance(), "5"));
Feature2D::Type type = Feature2D::kFeatureGfttBrief;
Feature2D * kptDetector = Feature2D::create(type, parameters);
kpts = kptDetector->generateKeypoints(leftMono, 0, roi);
kpts = kptDetector->generateKeypoints(leftMono, roi);
delete kptDetector;
float timeKpt = timer.ticks();
@@ -1651,7 +1652,7 @@ void DatabaseViewer::updateStereo(const Signature * data)
pcl::PointXYZ tmpPt = util3d::projectDisparityTo3D(
leftCorners[i],
disparity,
data->getDepthCx(), data->getDepthCy(), data->getDepthFx(), data->getDepthFy());
data->getCx(), data->getCy(), data->getFx(), data->getFy());
if(pcl::isFinite(tmpPt))
{
@@ -1833,7 +1834,15 @@ void DatabaseViewer::sliderLoopValueChanged(int value)
// only called when ui_->checkBox_showOptimized state changed
void DatabaseViewer::updateConstraintView()
{
this->updateConstraintView(neighborLinks_.at(ui_->horizontalSlider_neighbors->value()), false);
Link link = this->findActiveLink(ui_->horizontalSlider_A->value(), ui_->horizontalSlider_B->value());
if(link.type() == Link::kNeighbor)
{
this->updateConstraintView(neighborLinks_.at(ui_->horizontalSlider_neighbors->value()), false);
}
else
{
this->updateConstraintView(loopLinks_.at(ui_->horizontalSlider_loops->value()), false);
}
}
void DatabaseViewer::updateConstraintView(
@@ -1961,8 +1970,8 @@ void DatabaseViewer::updateConstraintView(
cloudFrom = rtabmap::util3d::cloudFromStereoImages(
dataFrom.getImageRaw(),
dataFrom.getDepthRaw(),
dataFrom.getDepthCx(), dataFrom.getDepthCy(),
dataFrom.getDepthFx(), dataFrom.getDepthFy(),
dataFrom.getCx(), dataFrom.getCy(),
dataFrom.getFx(), dataFrom.getFy(),
1);
}
else
@@ -1970,8 +1979,8 @@ void DatabaseViewer::updateConstraintView(
cloudFrom = rtabmap::util3d::cloudFromDepthRGB(
dataFrom.getImageRaw(),
dataFrom.getDepthRaw(),
dataFrom.getDepthCx(), dataFrom.getDepthCy(),
dataFrom.getDepthFx(), dataFrom.getDepthFy(),
dataFrom.getCx(), dataFrom.getCy(),
dataFrom.getFx(), dataFrom.getFy(),
1);
}
@@ -1984,8 +1993,8 @@ void DatabaseViewer::updateConstraintView(
cloudTo = rtabmap::util3d::cloudFromStereoImages(
dataTo.getImageRaw(),
dataTo.getDepthRaw(),
dataTo.getDepthCx(), dataTo.getDepthCy(),
dataTo.getDepthFx(), dataTo.getDepthFy(),
dataTo.getCx(), dataTo.getCy(),
dataTo.getFx(), dataTo.getFy(),
1);
}
else
@@ -1993,8 +2002,8 @@ void DatabaseViewer::updateConstraintView(
cloudTo = rtabmap::util3d::cloudFromDepthRGB(
dataTo.getImageRaw(),
dataTo.getDepthRaw(),
dataTo.getDepthCx(), dataTo.getDepthCy(),
dataTo.getDepthFx(), dataTo.getDepthFy(),
dataTo.getCx(), dataTo.getCy(),
dataTo.getFx(), dataTo.getFy(),
1);
}
@@ -2189,20 +2198,20 @@ void DatabaseViewer::sliderIterationsValueChanged(int value)
{
cloud = rtabmap::util3d::cloudFromDisparity(
util3d::disparityFromStereoImages(data.getImageRaw(), data.getDepthRaw()),
data.getDepthCx(),
data.getDepthCy(),
data.getDepthFx(),
data.getDepthFy(),
data.getCx(),
data.getCy(),
data.getFx(),
data.getFy(),
ui_->spinBox_projDecimation->value());
}
else
{
cloud = util3d::cloudFromDepth(
data.getDepthRaw(),
data.getDepthCx(),
data.getDepthCy(),
data.getDepthFx(),
data.getDepthFy(),
data.getCx(),
data.getCy(),
data.getFx(),
data.getFy(),
ui_->spinBox_projDecimation->value());
}
if(cloud->size())
@@ -2536,7 +2545,7 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
{
leftMono = left;
}
cloudA = util3d::cloudFromDisparity(util3d::disparityFromStereoImages(leftMono, depthA), dataFrom.getDepthCx(), dataFrom.getDepthCy(), dataFrom.getDepthFx(), dataFrom.getDepthFy(), ui_->spinBox_icp_decimation->value());
cloudA = util3d::cloudFromDisparity(util3d::disparityFromStereoImages(leftMono, depthA), dataFrom.getCx(), dataFrom.getCy(), dataFrom.getFx(), dataFrom.getFy(), ui_->spinBox_icp_decimation->value());
if(ui_->doubleSpinBox_icp_maxDepth->value() > 0)
{
cloudA = util3d::passThrough<pcl::PointXYZ>(cloudA, "z", 0, ui_->doubleSpinBox_icp_maxDepth->value());
@@ -2550,7 +2559,7 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
else
{
cloudA = util3d::getICPReadyCloud(depthA,
dataFrom.getDepthFx(), dataFrom.getDepthFy(), dataFrom.getDepthCx(), dataFrom.getDepthCy(),
dataFrom.getFx(), dataFrom.getFy(), dataFrom.getCx(), dataFrom.getCy(),
ui_->spinBox_icp_decimation->value(),
ui_->doubleSpinBox_icp_maxDepth->value(),
ui_->doubleSpinBox_icp_voxel->value(),
@@ -2569,7 +2578,7 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
{
leftMono = left;
}
cloudB = util3d::cloudFromDisparity(util3d::disparityFromStereoImages(leftMono, depthB), dataTo.getDepthCx(), dataTo.getDepthCy(), dataTo.getDepthFx(), dataTo.getDepthFy(), ui_->spinBox_icp_decimation->value());
cloudB = util3d::cloudFromDisparity(util3d::disparityFromStereoImages(leftMono, depthB), dataTo.getCx(), dataTo.getCy(), dataTo.getFx(), dataTo.getFy(), ui_->spinBox_icp_decimation->value());
if(ui_->doubleSpinBox_icp_maxDepth->value() > 0)
{
cloudB = util3d::passThrough<pcl::PointXYZ>(cloudB, "z", 0, ui_->doubleSpinBox_icp_maxDepth->value());
@@ -2583,7 +2592,7 @@ void DatabaseViewer::refineConstraint(int from, int to, bool updateGraph)
else
{
cloudB = util3d::getICPReadyCloud(depthB,
dataTo.getDepthFx(), dataTo.getDepthFy(), dataTo.getDepthCx(), dataTo.getDepthCy(),
dataTo.getFx(), dataTo.getFy(), dataTo.getCx(), dataTo.getCy(),
ui_->spinBox_icp_decimation->value(),
ui_->doubleSpinBox_icp_maxDepth->value(),
ui_->doubleSpinBox_icp_voxel->value(),
@@ -3075,6 +3084,11 @@ void DatabaseViewer::updateLoopClosuresSlider(int from, int to)
if(loopLinks_.size())
{
if(loopLinks_.size() == 1)
{
// just to be able to move the cursor of the loop slider
loopLinks_.push_back(loopLinks_.front());
}
ui_->horizontalSlider_loops->setMinimum(0);
ui_->horizontalSlider_loops->setMaximum(loopLinks_.size()-1);
ui_->horizontalSlider_loops->setEnabled(true);

View File

@@ -109,8 +109,8 @@ void LoopClosureViewer::updateView(const Transform & transform)
{
cloudA = util3d::cloudFromStereoImages(
sA_.getImageRaw(),
sA_.getDepthRaw(),
sA_.getDepthCx(), sA_.getDepthCy(),
sA_.getDepthRaw(),
sA_.getCx(), sA_.getCy(),
sA_.getFx(), sA_.getFy(),
decimation);
}
@@ -118,8 +118,8 @@ void LoopClosureViewer::updateView(const Transform & transform)
{
cloudA = util3d::cloudFromDepthRGB(
sA_.getImageRaw(),
sA_.getDepthRaw(),
sA_.getDepthCx(), sA_.getDepthCy(),
sA_.getDepthRaw(),
sA_.getCx(), sA_.getCy(),
sA_.getFx(), sA_.getFy(),
decimation);
}
@@ -141,8 +141,8 @@ void LoopClosureViewer::updateView(const Transform & transform)
{
cloudB = util3d::cloudFromStereoImages(
sB_.getImageRaw(),
sB_.getDepthRaw(),
sB_.getDepthCx(), sB_.getDepthCy(),
sB_.getDepthRaw(),
sB_.getCx(), sB_.getCy(),
sB_.getFx(), sB_.getFy(),
decimation);
}
@@ -150,8 +150,8 @@ void LoopClosureViewer::updateView(const Transform & transform)
{
cloudB = util3d::cloudFromDepthRGB(
sB_.getImageRaw(),
sB_.getDepthRaw(),
sB_.getDepthCx(), sB_.getDepthCy(),
sB_.getDepthRaw(),
sB_.getCx(), sB_.getCy(),
sB_.getFx(), sB_.getFy(),
decimation);
}

View File

@@ -81,6 +81,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//RGB-D stuff
#include "rtabmap/core/CameraRGBD.h"
#include "rtabmap/core/Odometry.h"
#include "rtabmap/core/OdometryThread.h"
#include "rtabmap/core/OdometryEvent.h"
#include "rtabmap/core/util3d.h"
#include "rtabmap/core/Graph.h"
@@ -849,7 +850,9 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
}
else if(info.type == 1)
{
_ui->imageView_odometry->setFeatures(info.refCorners, Qt::red);
std::vector<cv::KeyPoint> kpts;
cv::KeyPoint::convert(info.refCorners, kpts);
_ui->imageView_odometry->setFeatures(kpts, Qt::red);
}
}
@@ -895,37 +898,37 @@ void MainWindow::processOdometry(const rtabmap::SensorData & data, const rtabmap
}
}
}
else if(info.type == 1)
}
if(info.type == 1 && info.cornerInliers.size())
{
if(_ui->imageView_odometry->isFeaturesShown() || _ui->imageView_odometry->isLinesShown())
{
if(_ui->imageView_odometry->isFeaturesShown() || _ui->imageView_odometry->isLinesShown())
//draw lines
UASSERT(info.refCorners.size() == info.newCorners.size());
for(unsigned int i=0; i<info.cornerInliers.size(); ++i)
{
//draw lines
UASSERT(info.refCorners.size() == info.newCorners.size());
for(unsigned int i=0; i<info.cornerInliers.size(); ++i)
if(_ui->imageView_odometry->isFeaturesShown())
{
if(_ui->imageView_odometry->isFeaturesShown())
{
_ui->imageView_odometry->setFeatureColor(info.cornerInliers[i], Qt::green); // inliers
}
if(_ui->imageView_odometry->isLinesShown())
{
_ui->imageView_odometry->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,
Qt::blue);
}
_ui->imageView_odometry->setFeatureColor(info.cornerInliers[i], Qt::green); // inliers
}
if(_ui->imageView_odometry->isLinesShown())
{
_ui->imageView_odometry->addLine(
info.refCorners[info.cornerInliers[i]].x,
info.refCorners[info.cornerInliers[i]].y,
info.newCorners[info.cornerInliers[i]].x,
info.newCorners[info.cornerInliers[i]].y,
Qt::blue);
}
_ui->imageView_odometry->update();
}
}
}
if(!data.image().empty())
{
_ui->imageView_odometry->setSceneRect(QRectF(0,0,(float)data.image().cols, (float)data.image().rows));
}
_ui->imageView_odometry->update();
}
if(_ui->actionAuto_screen_capture->isChecked() && _autoScreenCaptureOdomSync)
@@ -1240,7 +1243,8 @@ void MainWindow::updateMapCloud(
{
if(!_ui->actionSave_point_cloud->isEnabled() &&
_cachedSignatures.size() &&
!(--_cachedSignatures.end())->getDepthCompressed().empty())
(!(--_cachedSignatures.end())->getDepthCompressed().empty() ||
!(--_cachedSignatures.end())->getWords3().empty()))
{
//enable save cloud action
_ui->actionSave_point_cloud->setEnabled(true);
@@ -1336,7 +1340,7 @@ void MainWindow::updateMapCloud(
else if(_cachedSignatures.contains(iter->first))
{
QMap<int, Signature>::iterator jter = _cachedSignatures.find(iter->first);
if(!jter->getImageCompressed().empty() && !jter->getDepthCompressed().empty())
if((!jter->getImageCompressed().empty() && !jter->getDepthCompressed().empty()) || jter->getWords3().size())
{
this->createAndAddCloudToMap(iter->first, iter->second, uValue(mapIds, iter->first, -1));
}
@@ -1567,98 +1571,125 @@ void MainWindow::createAndAddCloudToMap(int nodeId, const Transform & pose, int
return;
}
if(iter->getImageCompressed().empty() || iter->getDepthCompressed().empty())
if(!iter->getImageCompressed().empty() && !iter->getDepthCompressed().empty())
{
return;
}
cv::Mat image, depth;
iter->uncompressData(&image, &depth, 0);
cv::Mat image, depth;
iter->uncompressData(&image, &depth, 0);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
cloud = createCloud(nodeId,
image,
depth,
iter->getDepthFx(),
iter->getDepthFy(),
iter->getDepthCx(),
iter->getDepthCy(),
iter->getLocalTransform(),
Transform::getIdentity(),
_preferencesDialog->getCloudVoxelSize(0),
_preferencesDialog->getCloudDecimation(0),
_preferencesDialog->getCloudMaxDepth(0));
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
cloud = createCloud(nodeId,
image,
depth,
iter->getFx(),
iter->getFy(),
iter->getCx(),
iter->getCy(),
iter->getLocalTransform(),
Transform::getIdentity(),
_preferencesDialog->getCloudVoxelSize(0),
_preferencesDialog->getCloudDecimation(0),
_preferencesDialog->getCloudMaxDepth(0));
if(cloud->size() && _preferencesDialog->isGridMapFrom3DCloud())
{
UTimer timer;
float cellSize = _preferencesDialog->getGridMapResolution();
float groundNormalMaxAngle = M_PI_4;
int minClusterSize = 20;
cv::Mat ground, obstacles;
pcl::PointCloud<pcl::PointXYZRGB>::Ptr voxelizedCloud = cloud;
if(voxelizedCloud->size())
if(cloud->size() && _preferencesDialog->isGridMapFrom3DCloud())
{
voxelizedCloud = util3d::voxelize<pcl::PointXYZRGB>(cloud, cellSize);
UTimer timer;
float cellSize = _preferencesDialog->getGridMapResolution();
float groundNormalMaxAngle = M_PI_4;
int minClusterSize = 20;
cv::Mat ground, obstacles;
pcl::PointCloud<pcl::PointXYZRGB>::Ptr voxelizedCloud = cloud;
if(voxelizedCloud->size())
{
voxelizedCloud = util3d::voxelize<pcl::PointXYZRGB>(cloud, cellSize);
}
util3d::occupancy2DFromCloud3D<pcl::PointXYZRGB>(
voxelizedCloud,
ground, obstacles,
cellSize,
groundNormalMaxAngle,
minClusterSize);
if(!ground.empty() || !obstacles.empty())
{
_projectionLocalMaps.insert(std::make_pair(nodeId, std::make_pair(ground, obstacles)));
}
UDEBUG("time gridMapFrom2DCloud = %f s", timer.ticks());
}
util3d::occupancy2DFromCloud3D<pcl::PointXYZRGB>(
voxelizedCloud,
ground, obstacles,
cellSize,
groundNormalMaxAngle,
minClusterSize);
if(!ground.empty() || !obstacles.empty())
{
_projectionLocalMaps.insert(std::make_pair(nodeId, std::make_pair(ground, obstacles)));
}
UDEBUG("time gridMapFrom2DCloud = %f s", timer.ticks());
}
if(_preferencesDialog->isCloudMeshing())
{
pcl::PolygonMesh::Ptr mesh(new pcl::PolygonMesh);
if(cloud->size())
if(_preferencesDialog->isCloudMeshing())
{
pcl::PolygonMesh::Ptr mesh(new pcl::PolygonMesh);
if(cloud->size())
{
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
if(_preferencesDialog->getMeshSmoothing())
{
cloudWithNormals = util3d::computeNormalsSmoothed(cloud, (float)_preferencesDialog->getMeshSmoothingRadius());
}
else
{
cloudWithNormals = util3d::computeNormals(cloud, _preferencesDialog->getMeshNormalKSearch());
}
mesh = util3d::createMesh(cloudWithNormals, _preferencesDialog->getMeshGP3Radius());
}
if(mesh->polygons.size())
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::fromPCLPointCloud2(mesh->cloud, *tmp);
if(!_ui->widget_cloudViewer->addCloudMesh(cloudName, tmp, mesh->polygons, pose))
{
UERROR("Adding mesh cloud %d to viewer failed!", nodeId);
}
else
{
_createdClouds.insert(std::make_pair(nodeId, tmp));
}
}
}
else
{
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
if(_preferencesDialog->getMeshSmoothing())
{
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
cloudWithNormals = util3d::computeNormalsSmoothed(cloud, (float)_preferencesDialog->getMeshSmoothingRadius());
cloud->clear();
pcl::copyPointCloud(*cloudWithNormals, *cloud);
}
QColor color = Qt::gray;
if(mapId >= 0)
{
color = (Qt::GlobalColor)(mapId % 12 + 7 );
}
if(!_ui->widget_cloudViewer->addOrUpdateCloud(cloudName, cloud, pose, color))
{
UERROR("Adding cloud %d to viewer failed!", nodeId);
}
else
{
cloudWithNormals = util3d::computeNormals(cloud, _preferencesDialog->getMeshNormalKSearch());
}
mesh = util3d::createMesh(cloudWithNormals, _preferencesDialog->getMeshGP3Radius());
}
if(mesh->polygons.size())
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::fromPCLPointCloud2(mesh->cloud, *tmp);
if(!_ui->widget_cloudViewer->addCloudMesh(cloudName, tmp, mesh->polygons, pose))
{
UERROR("Adding mesh cloud %d to viewer failed!", nodeId);
}
else
{
_createdClouds.insert(std::make_pair(nodeId, tmp));
_createdClouds.insert(std::make_pair(nodeId, cloud));
}
}
}
else
else if(iter->getWords3().size())
{
if(_preferencesDialog->getMeshSmoothing())
{
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
cloudWithNormals = util3d::computeNormalsSmoothed(cloud, (float)_preferencesDialog->getMeshSmoothingRadius());
cloud->clear();
pcl::copyPointCloud(*cloudWithNormals, *cloud);
}
QColor color = Qt::gray;
if(mapId >= 0)
{
color = (Qt::GlobalColor)(mapId % 12 + 7 );
}
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
cloud->resize(iter->getWords3().size());
int oi=0;
for(std::multimap<int, pcl::PointXYZ>::const_iterator jter=iter->getWords3().begin(); jter!=iter->getWords3().end(); ++jter)
{
(*cloud)[oi].x = jter->second.x;
(*cloud)[oi].y = jter->second.y;
(*cloud)[oi].z = jter->second.z;
(*cloud)[oi].r = 255;
(*cloud)[oi].g = 255;
(*cloud)[oi++].b = 255;
}
if(!_ui->widget_cloudViewer->addOrUpdateCloud(cloudName, cloud, pose, color))
{
UERROR("Adding cloud %d to viewer failed!", nodeId);
@@ -1668,6 +1699,10 @@ void MainWindow::createAndAddCloudToMap(int nodeId, const Transform & pose, int
_createdClouds.insert(std::make_pair(nodeId, cloud));
}
}
else
{
return;
}
_ui->widget_cloudViewer->setCloudOpacity(cloudName, _preferencesDialog->getCloudOpacity(0));
_ui->widget_cloudViewer->setCloudPointSize(cloudName, _preferencesDialog->getCloudPointSize(0));
@@ -1992,6 +2027,7 @@ void MainWindow::applyPrefSettings(PreferencesDialog::PANEL_FLAGS flags)
if(_camera->cameraRGBD())
{
_camera->cameraRGBD()->setMirroringEnabled(_preferencesDialog->isSourceMirroring());
_camera->cameraRGBD()->setColorOnly(_preferencesDialog->isSourceRGBDColorOnly());
}
}
if(_dbReader)
@@ -2637,6 +2673,7 @@ void MainWindow::startDetection()
}
}
camera->setMirroringEnabled(_preferencesDialog->isSourceMirroring());
camera->setColorOnly(_preferencesDialog->isSourceRGBDColorOnly());
_camera = new CameraThread(camera);
@@ -2673,6 +2710,10 @@ void MainWindow::startDetection()
{
odom = new OdometryOpticalFlow(parameters);
}
else if(_preferencesDialog->getOdomStrategy() == 2)
{
odom = new OdometryMono(parameters);
}
else
{
odom = new OdometryBOW(parameters);
@@ -2705,6 +2746,10 @@ void MainWindow::startDetection()
{
odom = new OdometryOpticalFlow(parameters);
}
else if(_preferencesDialog->getOdomStrategy() == 2)
{
odom = new OdometryMono(parameters);
}
else
{
odom = new OdometryBOW(parameters);
@@ -3104,7 +3149,7 @@ void MainWindow::postProcessing()
if(refineNeighborLinks || refineLoopClosureLinks || reextractFeatures)
{
// depth data required
if(jter->getDepthCompressed().empty() || jter->getDepthFx() <= 0.0f || jter->getDepthFy() <= 0.0f)
if(jter->getDepthCompressed().empty() || jter->getFx() <= 0.0f || jter->getFy() <= 0.0f)
{
UWARN("Depth data of %d missing.", iter->first);
allDataAvailable = false;
@@ -3376,14 +3421,14 @@ void MainWindow::postProcessing()
}
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudA = util3d::getICPReadyCloud(depthA,
signatureFrom.getDepthFx(), signatureFrom.getDepthFy(), signatureFrom.getDepthCx(), signatureFrom.getDepthCy(),
signatureFrom.getFx(), signatureFrom.getFy(), signatureFrom.getCx(), signatureFrom.getCy(),
decimation,
maxDepth,
voxelSize,
samples,
signatureFrom.getLocalTransform());
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudB = util3d::getICPReadyCloud(depthB,
signatureTo.getDepthFx(), signatureTo.getDepthFy(), signatureTo.getDepthCx(), signatureTo.getDepthCy(),
signatureTo.getFx(), signatureTo.getFy(), signatureTo.getCx(), signatureTo.getCy(),
decimation,
maxDepth,
voxelSize,
@@ -4393,7 +4438,7 @@ void MainWindow::dataRecorder()
{
if(_dataRecorder == 0)
{
QString path = QFileDialog::getSaveFileName(this, tr("Save to..."), "output.db", "RTAB-Map database (*.db)");
QString path = QFileDialog::getSaveFileName(this, tr("Save to..."), _preferencesDialog->getWorkingDirectory()+"/output.db", "RTAB-Map database (*.db)");
if(!path.isEmpty())
{
int r = QMessageBox::question(this, tr("Hard drive or RAM?"), tr("Save in RAM?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
@@ -4867,18 +4912,35 @@ pcl::PointCloud<pcl::PointXYZRGB>::Ptr MainWindow::getAssembledCloud(
cv::Mat image, depth;
s.uncompressDataConst(&image, &depth, 0);
cloud = createCloud(iter->first,
image,
depth,
s.getDepthFx(),
s.getDepthFy(),
s.getDepthCx(),
s.getDepthCy(),
s.getLocalTransform(),
iter->second,
regenerateVoxelSize,
regenerateDecimation,
regenerateMaxDepth);
if(!image.empty() && !depth.empty())
{
cloud = createCloud(iter->first,
image,
depth,
s.getFx(),
s.getFy(),
s.getCx(),
s.getCy(),
s.getLocalTransform(),
iter->second,
regenerateVoxelSize,
regenerateDecimation,
regenerateMaxDepth);
}
else if(s.getWords3().size())
{
cloud->resize(s.getWords3().size());
int oi=0;
for(std::multimap<int, pcl::PointXYZ>::const_iterator jter=s.getWords3().begin(); jter!=s.getWords3().end(); ++jter)
{
(*cloud)[oi].x = jter->second.x;
(*cloud)[oi].y = jter->second.y;
(*cloud)[oi].z = jter->second.z;
(*cloud)[oi].r = 255;
(*cloud)[oi].g = 255;
(*cloud)[oi++].b = 255;
}
}
}
else
{
@@ -4953,18 +5015,35 @@ std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > MainWindow::getClouds(
const Signature & s = _cachedSignatures.find(iter->first).value();
cv::Mat image, depth;
s.uncompressDataConst(&image, &depth, 0);
cloud = createCloud(iter->first,
image,
depth,
s.getDepthFx(),
s.getDepthFy(),
s.getDepthCx(),
s.getDepthCy(),
s.getLocalTransform(),
Transform::getIdentity(),
regenerateVoxelSize,
regenerateDecimation,
regenerateMaxDepth);
if(!image.empty() && !depth.empty())
{
cloud = createCloud(iter->first,
image,
depth,
s.getFx(),
s.getFy(),
s.getCx(),
s.getCy(),
s.getLocalTransform(),
Transform::getIdentity(),
regenerateVoxelSize,
regenerateDecimation,
regenerateMaxDepth);
}
else if(s.getWords3().size())
{
cloud->resize(s.getWords3().size());
int oi=0;
for(std::multimap<int, pcl::PointXYZ>::const_iterator jter=s.getWords3().begin(); jter!=s.getWords3().end(); ++jter)
{
(*cloud)[oi].x = jter->second.x;
(*cloud)[oi].y = jter->second.y;
(*cloud)[oi].z = jter->second.z;
(*cloud)[oi].r = 255;
(*cloud)[oi].g = 255;
(*cloud)[oi++].b = 255;
}
}
}
else
{

View File

@@ -86,6 +86,7 @@ OdometryViewer::OdometryViewer(int maxClouds, int decimation, float voxelSize, i
decimationSpin_->setMinimum(1);
decimationSpin_->setMaximum(16);
decimationSpin_->setValue(decimation);
timeLabel_ = new QLabel(this);
QPushButton * clearButton = new QPushButton("clear", this);
QPushButton * closeButton = new QPushButton("close", this);
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
@@ -106,6 +107,7 @@ OdometryViewer::OdometryViewer(int maxClouds, int decimation, float voxelSize, i
hlayout2->addWidget(voxelSpin_);
hlayout2->addWidget(decimationLabel);
hlayout2->addWidget(decimationSpin_);
hlayout2->addWidget(timeLabel_);
hlayout2->addStretch(1);
hlayout2->addWidget(clearButton);
hlayout2->addWidget(closeButton);
@@ -166,6 +168,7 @@ void OdometryViewer::processData(const rtabmap::SensorData & data, const rtabmap
cloudView_->setBackgroundColor(Qt::black);
}
timeLabel_->setText(QString("%1 s").arg(info.time));
if(!data.image().empty() && !data.depthOrRightImage().empty() && data.fx()>0.0f && data.fyOrBaseline()>0.0f)
{
@@ -220,7 +223,6 @@ void OdometryViewer::processData(const rtabmap::SensorData & data, const rtabmap
if(!data.pose().isNull())
{
lastOdomPose_ = data.pose();
if(cloudView_->getAddedClouds().contains("cloudtmp"))
{
cloudView_->removeCloud("cloudtmp");
@@ -236,8 +238,6 @@ void OdometryViewer::processData(const rtabmap::SensorData & data, const rtabmap
std::string cloudName = uFormat("cloud%d", id_);
addedClouds_.push_back(cloudName);
UASSERT(cloudView_->addCloud(cloudName, cloud, data.pose()));
cloudView_->updateCameraTargetPosition(data.pose());
}
else
{
@@ -246,6 +246,26 @@ void OdometryViewer::processData(const rtabmap::SensorData & data, const rtabmap
}
}
if(!data.pose().isNull())
{
lastOdomPose_ = data.pose();
cloudView_->updateCameraTargetPosition(data.pose());
}
if(info.localMap.size())
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize(info.localMap.size());
int i=0;
for(std::multimap<int, cv::Point3f>::const_iterator iter=info.localMap.begin(); iter!=info.localMap.end(); ++iter)
{
(*cloud)[i].x = iter->second.x;
(*cloud)[i].y = iter->second.y;
(*cloud)[i++].z = iter->second.z;
}
cloudView_->addOrUpdateCloud("localmap", cloud);
}
if(!data.image().empty())
{
if(info.type == 0)
@@ -254,7 +274,9 @@ void OdometryViewer::processData(const rtabmap::SensorData & data, const rtabmap
}
else if(info.type == 1)
{
imageView_->setFeatures(info.refCorners, Qt::red);
std::vector<cv::KeyPoint> kpts;
cv::KeyPoint::convert(info.refCorners, kpts);
imageView_->setFeatures(kpts, Qt::red);
}
imageView_->clearLines();
@@ -299,32 +321,32 @@ void OdometryViewer::processData(const rtabmap::SensorData & data, const rtabmap
}
}
}
else if(info.type == 1)
}
if(info.type == 1 && info.cornerInliers.size())
{
if(imageView_->isFeaturesShown() || imageView_->isLinesShown())
{
if(imageView_->isFeaturesShown() || imageView_->isLinesShown())
//draw lines
UASSERT(info.refCorners.size() == info.newCorners.size());
for(unsigned int i=0; i<info.cornerInliers.size(); ++i)
{
//draw lines
UASSERT(info.refCorners.size() == info.newCorners.size());
for(unsigned int i=0; i<info.cornerInliers.size(); ++i)
if(imageView_->isFeaturesShown())
{
if(imageView_->isFeaturesShown())
{
imageView_->setFeatureColor(info.cornerInliers[i], Qt::green); // inliers
}
if(imageView_->isLinesShown())
{
imageView_->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,
Qt::blue);
}
imageView_->setFeatureColor(info.cornerInliers[i], Qt::green); // inliers
}
if(imageView_->isLinesShown())
{
imageView_->addLine(
info.refCorners[info.cornerInliers[i]].x,
info.refCorners[info.cornerInliers[i]].y,
info.newCorners[info.cornerInliers[i]].x,
info.newCorners[info.cornerInliers[i]].y,
Qt::blue);
}
}
}
}
if(!data.image().empty())
{
imageView_->setSceneRect(QRectF(0,0,(float)data.image().cols, (float)data.image().rows));

View File

@@ -48,6 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/Rtabmap.h"
#include "rtabmap/core/Parameters.h"
#include "rtabmap/core/Odometry.h"
#include "rtabmap/core/OdometryThread.h"
#include "rtabmap/core/CameraRGBD.h"
#include "rtabmap/core/CameraThread.h"
#include "rtabmap/core/Camera.h"
@@ -302,6 +303,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->openni2_exposure, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->openni2_gain, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->openni2_mirroring, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->checkbox_rgbd_colorOnly, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->lineEdit_openniDevice, SIGNAL(textChanged(const QString &)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->lineEdit_openniLocalTransform, SIGNAL(textChanged(const QString &)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->pushButton_calibrate, SIGNAL(clicked()), this, SLOT(calibrate()));
@@ -425,7 +427,6 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->fastKeypointRatio->setObjectName(Parameters::kFASTGpuKeypointsRatio().c_str());
//ORB detector
_ui->spinBox_ORBNFeatures->setObjectName(Parameters::kORBNFeatures().c_str());
_ui->doubleSpinBox_ORBScaleFactor->setObjectName(Parameters::kORBScaleFactor().c_str());
_ui->spinBox_ORBNLevels->setObjectName(Parameters::kORBNLevels().c_str());
_ui->spinBox_ORBEdgeThreshold->setObjectName(Parameters::kORBEdgeThreshold().c_str());
@@ -442,7 +443,6 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->spinBox_FREAKNOctaves->setObjectName(Parameters::kFREAKNOctaves().c_str());
//GFTT detector
_ui->spinBox_GFTT_maxCorners->setObjectName(Parameters::kGFTTMaxCorners().c_str());
_ui->doubleSpinBox_GFTT_qualityLevel->setObjectName(Parameters::kGFTTQualityLevel().c_str());
_ui->doubleSpinBox_GFTT_minDistance->setObjectName(Parameters::kGFTTMinDistance().c_str());
_ui->spinBox_GFTT_blockSize->setObjectName(Parameters::kGFTTBlockSize().c_str());
@@ -489,6 +489,8 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->loopClosure_bowIterations->setObjectName(Parameters::kLccBowIterations().c_str());
_ui->loopClosure_bowMaxDepth->setObjectName(Parameters::kLccBowMaxDepth().c_str());
_ui->loopClosure_bowForce2D->setObjectName(Parameters::kLccBowForce2D().c_str());
_ui->loopClosure_bowEpipolarGeometry->setObjectName(Parameters::kLccBowEpipolarGeometry().c_str());
_ui->loopClosure_bowEpipolarGeometryVar->setObjectName(Parameters::kLccBowEpipolarGeometryVar().c_str());
_ui->groupBox_reextract->setObjectName(Parameters::kLccReextractActivated().c_str());
_ui->reextract_nn->setObjectName(Parameters::kLccReextractNNType().c_str());
@@ -546,6 +548,12 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->odom_subpix_iterations->setObjectName(Parameters::kOdomSubPixIterations().c_str());
_ui->odom_subpix_eps->setObjectName(Parameters::kOdomSubPixEps().c_str());
//Odometry Mono
_ui->doubleSpinBox_minFlow->setObjectName(Parameters::kOdomMonoInitMinFlow().c_str());
_ui->doubleSpinBox_minInitTranslation->setObjectName(Parameters::kOdomMonoInitMinTranslation().c_str());
_ui->doubleSpinBox_minTranslation->setObjectName(Parameters::kOdomMonoMinTranslation().c_str());
_ui->doubleSpinBox_maxVariance->setObjectName(Parameters::kOdomMonoMaxVariance().c_str());
//Stereo
_ui->stereo_flow_winSize->setObjectName(Parameters::kStereoWinSize().c_str());
_ui->stereo_flow_maxLevel->setObjectName(Parameters::kStereoMaxLevel().c_str());
@@ -944,6 +952,7 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->openni2_exposure->setValue(0);
_ui->openni2_gain->setValue(100);
_ui->openni2_mirroring->setChecked(false);
_ui->checkbox_rgbd_colorOnly->setChecked(false);
_ui->lineEdit_openniDevice->setText("");
_ui->lineEdit_openniLocalTransform->setText("0 0 0 -PI_2 0 -PI_2");
}
@@ -1204,6 +1213,7 @@ void PreferencesDialog::readCameraSettings(const QString & filePath)
_ui->openni2_exposure->setValue(settings.value("openni2Exposure", _ui->openni2_exposure->value()).toInt());
_ui->openni2_gain->setValue(settings.value("openni2Gain", _ui->openni2_gain->value()).toInt());
_ui->openni2_mirroring->setChecked(settings.value("openni2Mirroring", _ui->openni2_mirroring->isChecked()).toBool());
_ui->checkbox_rgbd_colorOnly->setChecked(settings.value("rgbdColorOnly", _ui->checkbox_rgbd_colorOnly->isChecked()).toBool());
_ui->lineEdit_openniDevice->setText(settings.value("device",_ui->lineEdit_openniDevice->text()).toString());
_ui->lineEdit_openniLocalTransform->setText(settings.value("localTransform",_ui->lineEdit_openniLocalTransform->text()).toString());
_calibrationDialog->loadSettings(settings, "CalibrationDialog");
@@ -1469,6 +1479,7 @@ void PreferencesDialog::writeCameraSettings(const QString & filePath) const
settings.setValue("openni2Exposure", _ui->openni2_exposure->value());
settings.setValue("openni2Gain", _ui->openni2_gain->value());
settings.setValue("openni2Mirroring", _ui->openni2_mirroring->isChecked());
settings.setValue("rgbdColorOnly", _ui->checkbox_rgbd_colorOnly->isChecked());
settings.setValue("device", _ui->lineEdit_openniDevice->text());
settings.setValue("localTransform", _ui->lineEdit_openniLocalTransform->text());
_calibrationDialog->saveSettings(settings, "CalibrationDialog");
@@ -3054,6 +3065,10 @@ bool PreferencesDialog::getSourceOpenni2Mirroring() const
{
return _ui->openni2_mirroring->isChecked();
}
bool PreferencesDialog::isSourceRGBDColorOnly() const
{
return _ui->checkbox_rgbd_colorOnly->isChecked();
}
QString PreferencesDialog::getSourceOpenniDevice() const
{
return _ui->lineEdit_openniDevice->text();
@@ -3288,6 +3303,7 @@ void PreferencesDialog::testOdometry(int type)
}
}
camera->setMirroringEnabled(isSourceMirroring());
camera->setColorOnly(isSourceRGBDColorOnly());
ParametersMap parameters = this->getAllParameters();
Odometry * odometry;
@@ -3295,6 +3311,10 @@ void PreferencesDialog::testOdometry(int type)
{
odometry = new OdometryOpticalFlow(parameters);
}
else if(this->getOdomStrategy() == 2)
{
odometry = new OdometryMono(parameters);
}
else
{
odometry = new OdometryBOW(parameters);

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1035</width>
<height>702</height>
<width>1058</width>
<height>725</height>
</rect>
</property>
<property name="sizePolicy">
@@ -63,9 +63,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>737</width>
<height>948</height>
<y>-368</y>
<width>760</width>
<height>1009</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
@@ -86,7 +86,7 @@
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>8</number>
<number>3</number>
</property>
<widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29">
@@ -1928,6 +1928,26 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_229">
<property name="text">
<string>Only RGB images are published.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkbox_rgbd_colorOnly">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@@ -4171,26 +4191,6 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
<layout class="QGridLayout" name="gridLayout_14" columnstretch="0,1">
<item row="0" column="0">
<widget class="QSpinBox" name="spinBox_ORBNFeatures">
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_24">
<property name="text">
<string>The maximum number of features to retain.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_ORBScaleFactor">
<property name="decimals">
<number>1</number>
@@ -4200,7 +4200,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="1" column="1">
<item row="0" column="1">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Pyramid decimation ratio, greater than 1. scaleFactor==2 means the classical pyramid, where each next level has 4x less pixels than the previous, but such a big scale factor will degrade feature matching scores dramatically. On the other hand, too close to 1 scale factor will mean that to cover certain scale range you will need more pyramid levels and so the speed will suffer.</string>
@@ -4210,14 +4210,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QSpinBox" name="spinBox_ORBNLevels">
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="1">
<widget class="QLabel" name="label_43">
<property name="text">
<string>The number of pyramid levels. The smallest level will have linear size equal to input_image_linear_size/pow(scaleFactor, nlevels).</string>
@@ -4227,14 +4227,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="3" column="0">
<item row="2" column="0">
<widget class="QSpinBox" name="spinBox_ORBEdgeThreshold">
<property name="value">
<number>31</number>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="2" column="1">
<widget class="QLabel" name="label_95">
<property name="text">
<string>This is size of the border where the features are not detected. It should roughly match the patchSize parameter.</string>
@@ -4244,14 +4244,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<widget class="QSpinBox" name="spinBox_ORBFirstLevel">
<property name="maximum">
<number>0</number>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="3" column="1">
<widget class="QLabel" name="label_127">
<property name="text">
<string>It should be 0 in the current implementation.</string>
@@ -4261,14 +4261,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="5" column="0">
<item row="4" column="0">
<widget class="QSpinBox" name="spinBox_ORBWTA_K">
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="4" column="1">
<widget class="QLabel" name="label_128">
<property name="text">
<string>WTA_K: The number of points that produce each element of the oriented BRIEF descriptor. The default value 2 means the BRIEF where we take a random point pair and compare their brightnesses, so we get 0/1 response. Other possible values are 3 and 4. For example, 3 means that we take 3 random points (of course, those point coordinates are random, but they are generated from the pre-defined seed, so each element of BRIEF descriptor is computed deterministically from the pixel rectangle), find point of maximum brightness and output index of the winner (0, 1 or 2). Such output will occupy 2 bits, and therefore it will need a special variant of Hamming distance, denoted as NORM_HAMMING2 (2 bits per bin). When WTA_K=4, we take 4 random points to compute each bin (that will also occupy 2 bits with possible values 0, 1, 2 or 3).</string>
@@ -4278,10 +4278,10 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="6" column="0">
<item row="5" column="0">
<widget class="QSpinBox" name="spinBox_ORBScoreType"/>
</item>
<item row="6" column="1">
<item row="5" column="1">
<widget class="QLabel" name="label_140">
<property name="text">
<string>The default HARRIS_SCORE=0 means that Harris algorithm is used to rank features (the score is written to KeyPoint::score and is used to retain best nfeatures features); FAST_SCORE=1 is alternative value of the parameter that produces slightly less stable keypoints, but it is a little faster to compute.</string>
@@ -4291,14 +4291,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="7" column="0">
<item row="6" column="0">
<widget class="QSpinBox" name="spinBox_ORBPatchSize">
<property name="value">
<number>31</number>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="6" column="1">
<widget class="QLabel" name="label_147">
<property name="text">
<string>size of the patch used by the oriented BRIEF descriptor. Of course, on smaller pyramid layers the perceived image area covered by a feature will be larger.</string>
@@ -4308,14 +4308,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="8" column="0">
<item row="7" column="0">
<widget class="QCheckBox" name="checkBox_ORBGpu">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="7" column="1">
<widget class="QLabel" name="label_orbGpu">
<property name="text">
<string>GPU-ORB: Use GPU version of ORB. This option is enabled only if OpenCV is built with CUDA and GPUs are detected.</string>
@@ -4451,7 +4451,7 @@ When set to false, no new words are added to dictionary, so no more updates are
<string>GFTT</string>
</property>
<layout class="QGridLayout" name="gridLayout_21" columnstretch="0,1">
<item row="1" column="0">
<item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_GFTT_qualityLevel">
<property name="maximum">
<double>1.000000000000000</double>
@@ -4464,7 +4464,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_GFTT_minDistance">
<property name="decimals">
<number>1</number>
@@ -4474,7 +4474,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_GFTT_useHarrisDetector">
<property name="text">
<string/>
@@ -4484,7 +4484,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="4" column="1">
<item row="3" column="1">
<widget class="QLabel" name="label_172">
<property name="text">
<string>Use Harris detector.</string>
@@ -4494,7 +4494,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="5" column="0">
<item row="4" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_GFTT_k">
<property name="maximum">
<double>1.000000000000000</double>
@@ -4507,7 +4507,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="5" column="1">
<item row="4" column="1">
<widget class="QLabel" name="label_173">
<property name="text">
<string>K.</string>
@@ -4517,34 +4517,14 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="3" column="0">
<item row="2" column="0">
<widget class="QSpinBox" name="spinBox_GFTT_blockSize">
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QSpinBox" name="spinBox_GFTT_maxCorners">
<property name="maximum">
<number>100000</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_174">
<property name="text">
<string>Maximum corners.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_175">
<property name="text">
<string>Quality level.</string>
@@ -4554,7 +4534,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="1">
<widget class="QLabel" name="label_176">
<property name="text">
<string>Mininum distance.</string>
@@ -4564,7 +4544,7 @@ When set to false, no new words are added to dictionary, so no more updates are
</property>
</widget>
</item>
<item row="3" column="1">
<item row="2" column="1">
<widget class="QLabel" name="label_177">
<property name="text">
<string>Block size.</string>
@@ -5337,7 +5317,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<property name="title">
<string>Loop closure constraint</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_49">
<layout class="QVBoxLayout" name="verticalLayout_41">
<item>
<widget class="QLabel" name="label_16">
<property name="text">
@@ -5442,14 +5422,14 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property>
</widget>
</item>
<item row="4" column="0">
<item row="6" column="0">
<widget class="QCheckBox" name="loopClosure_bowForce2D">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="6" column="1">
<widget class="QLabel" name="label_184">
<property name="text">
<string>Force 2D transform (3DoF: x,y and yaw).</string>
@@ -5459,7 +5439,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property>
</widget>
</item>
<item row="5" column="0">
<item row="7" column="0">
<widget class="QComboBox" name="globalDetection_icpType">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
@@ -5481,7 +5461,7 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</item>
</widget>
</item>
<item row="5" column="1">
<item row="7" column="1">
<widget class="QLabel" name="label_57">
<property name="text">
<string>When enabled, the visual transform is used as a guess for ICP estimation (3D or 2D). See &quot;ICP&quot; panel for parameters.</string>
@@ -5491,6 +5471,52 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_658">
<property name="text">
<string>Use epipolar geometry to compute the loop closure transform. &quot;Maximum distance for visual word correspondences&quot; is not used in this mode.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="loopClosure_bowEpipolarGeometry">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_664">
<property name="text">
<string>Epipolar geometry maximum variance to accept the loop closure.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QDoubleSpinBox" name="loopClosure_bowEpipolarGeometryVar">
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -5525,6 +5551,16 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_174">
<property name="text">
<string>Epipolar geometry is ignored (if set above) by this option.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_25" columnstretch="0,1">
<item row="3" column="0">
@@ -6303,7 +6339,17 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
</item>
<item>
<layout class="QGridLayout" name="gridLayout_27" columnstretch="0,1">
<item row="2" column="1">
<item row="3" column="1">
<widget class="QLabel" name="label_160">
<property name="text">
<string>3-Mono is for single camera motion estimation (MonoSLAM). On initialization, the camera must be translated on the side until a first transform can be computed.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_47">
<property name="text">
<string>Automatically reset odometry after X consecutive images on which odometry cannot be computed (value=0 disables auto-reset). When reset, the odometry starts from the last pose computed.</string>
@@ -6328,16 +6374,28 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
<string>Optical Flow</string>
</property>
</item>
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<item row="5" column="0">
<widget class="QSpinBox" name="odom_countdown">
<property name="maximum">
<number>999999</number>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="7" column="0">
<widget class="QCheckBox" name="odom_fillInfoData">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QComboBox" name="odom_type">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
@@ -6384,10 +6442,10 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
</item>
</widget>
</item>
<item row="1" column="1">
<item row="4" column="1">
<widget class="QLabel" name="label_44">
<property name="text">
<string>Feature detector. In BOW mode, the related descriptor is also used. In Optical flow mode, only the keypoint detector is used.</string>
<string>Feature detector. In BOW/Mono modes, the related descriptor is also used. In Optical flow mode, only the keypoint detector is used.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -6397,23 +6455,21 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
<item row="0" column="1">
<widget class="QLabel" name="label_103">
<property name="text">
<string>Odometry strategy:
1-BOW matches features extracted from both frames using nearest neighbor with descriptors, then computes RANSAC transformation estimation with corresponding 3D features.
2-Optical flow estimate the location of 2D features from last frame to new frame, then computes RANSAC transformation with corresponding 3D features.</string>
<string>Odometry strategy:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="6" column="0">
<widget class="QCheckBox" name="odom_force2D">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="6" column="1">
<widget class="QLabel" name="label_196">
<property name="text">
<string>Force 2D transform (3DoF: x,y and yaw).</string>
@@ -6423,7 +6479,7 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
</property>
</widget>
</item>
<item row="4" column="1">
<item row="7" column="1">
<widget class="QLabel" name="label_221">
<property name="text">
<string>Fill info with data (inliers/outliers features to be shown in Odometry view).</string>
@@ -6433,20 +6489,33 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="odom_fillInfoData">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="0">
<item row="8" column="0">
<widget class="QPushButton" name="pushButton_testOdometry">
<property name="text">
<string>Test selected odometry</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_161">
<property name="text">
<string>2-Optical flow estimate the location of 2D features from last frame to new frame, then computes RANSAC transformation with corresponding 3D features.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_162">
<property name="text">
<string>1-BOW matches features extracted from both frames using nearest neighbor with descriptors, then computes RANSAC transformation estimation with corresponding 3D features.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -7116,6 +7185,158 @@ Lower the ratio -&gt; higher the precision. 0 means disabled, matching the neare
</item>
</layout>
</widget>
<widget class="QWidget" name="page_30">
<layout class="QVBoxLayout" name="verticalLayout_169">
<item>
<widget class="QGroupBox" name="groupBox_odometryMono2">
<property name="title">
<string>Mono</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_168">
<item>
<widget class="QLabel" name="label_661">
<property name="text">
<string>Parameters from BOW and OpticalFlow are also used here. PnP parameters on Odometry panel are used too.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_166" columnstretch="0,1">
<item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_minFlow">
<property name="suffix">
<string> pixels</string>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>10.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_659">
<property name="text">
<string>Minimum optical flow required for the initialization step.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_minTranslation">
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_660">
<property name="text">
<string>Minimum translation to add new points to local map. On initialization, translation x 5 is used as the required minimum transformation.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_662">
<property name="text">
<string>Minimum translation required for the initialization step.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_minInitTranslation">
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_663">
<property name="text">
<string>Maximum variance to add new points to local map.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_maxVariance">
<property name="suffix">
<string/>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>0.010000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_88">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>774</height>
</size>
</property>
</spacer>
</item>
</layout>
<zorder>groupBox_odometryMono2</zorder>
<zorder>verticalSpacer_88</zorder>
</widget>
</widget>
</item>
</layout>