Parameters: Added "Reg/VarianceNormalized". Registration: Transforms are now only normalized in Registration. Added rgbd_dataset tool. MainWindow: odom inliers-only shown option. ExportCloudsDialog: ignoring intermediate nodes for texturing (if they don't have data). DatabaseViewer: "optimized" checkbox enabled in Constraints view for non-neighbor links too.

This commit is contained in:
matlabbe
2017-05-22 11:48:01 -04:00
parent 58e6424bf7
commit 385b7e3389
20 changed files with 758 additions and 155 deletions
+21 -20
View File
@@ -2206,6 +2206,10 @@ void DatabaseViewer::detectMoreLoopClosures()
}
}
progressDialog->incrementStep();
if(i%100)
{
QApplication::processEvents();
}
}
UINFO("Iteration %d/%d: added %d loop closures.", n+1, iterations, (int)addedLinks.size()/2);
progressDialog->appendText(tr("Iteration %1/%2: Detected %3 loop closures!").arg(n+1).arg(iterations).arg(addedLinks.size()/2));
@@ -3321,30 +3325,27 @@ void DatabaseViewer::updateConstraintView(
.arg(sqrt(link.rotVariance()))
.arg(sqrt(link.transVariance())));
ui_->label_constraint->setText(QString("%1").arg(t.prettyPrint().c_str()).replace(" ", "\n"));
if((link.type() == Link::kNeighbor || link.type() == Link::kNeighborMerged) &&
graphes_.size() &&
if(graphes_.size() &&
(int)graphes_.size()-1 == ui_->horizontalSlider_iterations->maximum())
{
std::map<int, rtabmap::Transform> & graph = uValueAt(graphes_, ui_->horizontalSlider_iterations->value());
if(link.type() == Link::kNeighbor || link.type() == Link::kNeighborMerged)
{
std::map<int, rtabmap::Transform>::iterator iterFrom = graph.find(link.from());
std::map<int, rtabmap::Transform>::iterator iterTo = graph.find(link.to());
if(iterFrom != graph.end() && iterTo != graph.end())
{
ui_->checkBox_showOptimized->setEnabled(true);
Transform topt = iterFrom->second.inverse()*iterTo->second;
float diff = topt.getDistance(t);
Transform v1 = t.rotation()*Transform(1,0,0,0,0,0);
Transform v2 = topt.rotation()*Transform(1,0,0,0,0,0);
float a = pcl::getAngle3D(Eigen::Vector4f(v1.x(), v1.y(), v1.z(), 0), Eigen::Vector4f(v2.x(), v2.y(), v2.z(), 0));
a = (a *180.0f) / CV_PI;
ui_->label_constraint_opt->setText(QString("%1\n(error=%2% a=%3)").arg(QString(topt.prettyPrint().c_str()).replace(" ", "\n")).arg((diff/t.getNorm())*100.0f).arg(a));
if(ui_->checkBox_showOptimized->isChecked())
{
t = topt;
}
std::map<int, rtabmap::Transform>::iterator iterFrom = graph.find(link.from());
std::map<int, rtabmap::Transform>::iterator iterTo = graph.find(link.to());
if(iterFrom != graph.end() && iterTo != graph.end())
{
ui_->checkBox_showOptimized->setEnabled(true);
Transform topt = iterFrom->second.inverse()*iterTo->second;
float diff = topt.getDistance(t);
Transform v1 = t.rotation()*Transform(1,0,0,0,0,0);
Transform v2 = topt.rotation()*Transform(1,0,0,0,0,0);
float a = pcl::getAngle3D(Eigen::Vector4f(v1.x(), v1.y(), v1.z(), 0), Eigen::Vector4f(v2.x(), v2.y(), v2.z(), 0));
a = (a *180.0f) / CV_PI;
ui_->label_constraint_opt->setText(QString("%1\n(error=%2% a=%3)").arg(QString(topt.prettyPrint().c_str()).replace(" ", "\n")).arg((diff/t.getNorm())*100.0f).arg(a));
if(ui_->checkBox_showOptimized->isChecked())
{
t = topt;
}
}
}
+54 -49
View File
@@ -1071,6 +1071,8 @@ bool ExportCloudsDialog::getExportedClouds(
parameters,
has2dScans);
std::set<int> validCameras = uKeysSet(clouds);
UDEBUG("");
if(_canceled)
{
@@ -1915,60 +1917,63 @@ bool ExportCloudsDialog::getExportedClouds(
std::map<int, std::vector<CameraModel> > cameraModels;
for(std::map<int, Transform>::iterator jter=cameras.begin(); jter!=cameras.end(); ++jter)
{
std::vector<CameraModel> models;
StereoCameraModel stereoModel;
bool cacheHasCompressedImage = false;
if(cachedSignatures.contains(jter->first))
if(validCameras.find(jter->first) != validCameras.end())
{
const SensorData & data = cachedSignatures.find(jter->first)->sensorData();
models = data.cameraModels();
stereoModel = data.stereoCameraModel();
cacheHasCompressedImage = !data.imageCompressed().empty();
}
else if(_dbDriver)
{
_dbDriver->getCalibration(jter->first, models, stereoModel);
}
if(stereoModel.isValidForProjection())
{
models.clear();
models.push_back(stereoModel.left());
}
else if(models.size() == 0 || !models[0].isValidForProjection())
{
models.clear();
}
if(!jter->second.isNull() && models.size())
{
if(models[0].imageWidth() == 0 || models[0].imageHeight() == 0)
std::vector<CameraModel> models;
StereoCameraModel stereoModel;
bool cacheHasCompressedImage = false;
if(cachedSignatures.contains(jter->first))
{
// we are using an old database format (image size not saved in calibrations), we should
// uncompress images to get their size
cv::Mat img;
if(cacheHasCompressedImage)
{
cachedSignatures.find(jter->first)->sensorData().uncompressDataConst(&img, 0);
}
else if(_dbDriver)
{
SensorData data;
_dbDriver->getNodeData(jter->first, data, true, false, false, false);
data.uncompressDataConst(&img, 0);
}
cv::Size imageSize = img.size();
imageSize.width /= models.size();
for(unsigned int i=0; i<models.size(); ++i)
{
models[i].setImageSize(imageSize);
}
const SensorData & data = cachedSignatures.find(jter->first)->sensorData();
models = data.cameraModels();
stereoModel = data.stereoCameraModel();
cacheHasCompressedImage = !data.imageCompressed().empty();
}
else if(_dbDriver)
{
_dbDriver->getCalibration(jter->first, models, stereoModel);
}
if(models[0].imageWidth() != 0 && models[0].imageHeight() != 0)
if(stereoModel.isValidForProjection())
{
cameraPoses.insert(std::make_pair(jter->first, jter->second));
cameraModels.insert(std::make_pair(jter->first, models));
models.clear();
models.push_back(stereoModel.left());
}
else if(models.size() == 0 || !models[0].isValidForProjection())
{
models.clear();
}
if(!jter->second.isNull() && models.size())
{
if(models[0].imageWidth() == 0 || models[0].imageHeight() == 0)
{
// we are using an old database format (image size not saved in calibrations), we should
// uncompress images to get their size
cv::Mat img;
if(cacheHasCompressedImage)
{
cachedSignatures.find(jter->first)->sensorData().uncompressDataConst(&img, 0);
}
else if(_dbDriver)
{
SensorData data;
_dbDriver->getNodeData(jter->first, data, true, false, false, false);
data.uncompressDataConst(&img, 0);
}
cv::Size imageSize = img.size();
imageSize.width /= models.size();
for(unsigned int i=0; i<models.size(); ++i)
{
models[i].setImageSize(imageSize);
}
}
if(models[0].imageWidth() != 0 && models[0].imageHeight() != 0)
{
cameraPoses.insert(std::make_pair(jter->first, jter->second));
cameraModels.insert(std::make_pair(jter->first, models));
}
}
}
}
+30 -10
View File
@@ -1120,8 +1120,13 @@ void MainWindow::processOdometry(const rtabmap::OdometryEvent & odom, bool dataI
bool inlier = odom.info().words.find(iter->first) != odom.info().words.end();
(*cloud)[i].r = inlier?0:255;
(*cloud)[i].g = 255;
(*cloud)[i++].b = 0;
(*cloud)[i].b = 0;
if(!_preferencesDialog->isOdomOnlyInliersShown() || inlier)
{
++i;
}
}
cloud->resize(i);
_cloudViewer->addCloud("featuresOdom", cloud, _odometryCorrection);
_cloudViewer->setCloudVisibility("featuresOdom", true);
@@ -1185,10 +1190,25 @@ void MainWindow::processOdometry(const rtabmap::OdometryEvent & odom, bool dataI
{
if(odom.info().type == 0)
{
_ui->imageView_odometry->setFeatures(
odom.info().words,
odom.data().depthRaw(),
Qt::yellow);
if(_preferencesDialog->isOdomOnlyInliersShown())
{
std::multimap<int, cv::KeyPoint> kpInliers;
for(unsigned int i=0; i<odom.info().wordInliers.size(); ++i)
{
kpInliers.insert(*odom.info().words.find(odom.info().wordInliers[i]));
}
_ui->imageView_odometry->setFeatures(
kpInliers,
odom.data().depthRaw(),
Qt::green);
}
else
{
_ui->imageView_odometry->setFeatures(
odom.info().words,
odom.data().depthRaw(),
Qt::yellow);
}
}
else if(odom.info().type == 1)
{
@@ -1238,7 +1258,7 @@ void MainWindow::processOdometry(const rtabmap::OdometryEvent & odom, bool dataI
if(odom.info().type == 0)
{
if(_ui->imageView_odometry->isFeaturesShown())
if(_ui->imageView_odometry->isFeaturesShown() && !_preferencesDialog->isOdomOnlyInliersShown())
{
for(unsigned int i=0; i<odom.info().wordMatches.size(); ++i)
{
@@ -5179,7 +5199,7 @@ void MainWindow::postProcessing()
if(!msg.empty())
{
UWARN("%s", msg.c_str());
_initProgressDialog->appendText(tr("%s").arg(msg.c_str()));
_initProgressDialog->appendText(tr("%1").arg(msg.c_str()));
QApplication::processEvents();
updateConstraint = false;
}
@@ -6678,7 +6698,7 @@ void MainWindow::changeState(MainWindow::State newState)
}
}
actions = _ui->menuFile->actions();
if(actions.size()==17)
if(actions.size()==16)
{
if(actions.at(2)->isSeparator())
{
@@ -6688,9 +6708,9 @@ void MainWindow::changeState(MainWindow::State newState)
{
UWARN("Menu File separators have not the same order.");
}
if(actions.at(13)->isSeparator())
if(actions.at(12)->isSeparator())
{
actions.at(13)->setVisible(!monitoring);
actions.at(12)->setVisible(!monitoring);
}
else
{
+9
View File
@@ -306,6 +306,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->checkBox_cacheStatistics, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_notifyWhenNewGlobalPathIsReceived, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->spinBox_odomQualityWarnThr, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_odom_onlyInliersShown, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkBox_posteriorGraphView, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->checkbox_odomDisabled, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
connect(_ui->odom_registration, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteGeneralPanel()));
@@ -785,6 +786,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
// Registration
_ui->loopClosure_bowVarianceFromInliersCount->setObjectName(Parameters::kRegVarianceFromInliersCount().c_str());
_ui->reg_varianceNormalized->setObjectName(Parameters::kRegVarianceNormalized().c_str());
_ui->comboBox_registrationStrategy->setObjectName(Parameters::kRegStrategy().c_str());
_ui->loopClosure_bowForce2D->setObjectName(Parameters::kRegForce3DoF().c_str());
@@ -1255,6 +1257,7 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->checkBox_imageRejectedShown->setChecked(true);
_ui->checkBox_imageHighestHypShown->setChecked(false);
_ui->spinBox_odomQualityWarnThr->setValue(50);
_ui->checkBox_odom_onlyInliersShown->setChecked(false);
_ui->checkBox_posteriorGraphView->setChecked(true);
_ui->checkbox_odomDisabled->setChecked(false);
_ui->checkbox_groundTruthAlign->setChecked(true);
@@ -1651,6 +1654,7 @@ void PreferencesDialog::readGuiSettings(const QString & filePath)
_ui->checkBox_cacheStatistics->setChecked(settings.value("figure_cache", _ui->checkBox_cacheStatistics->isChecked()).toBool());
_ui->checkBox_notifyWhenNewGlobalPathIsReceived->setChecked(settings.value("notifyNewGlobalPath", _ui->checkBox_notifyWhenNewGlobalPathIsReceived->isChecked()).toBool());
_ui->spinBox_odomQualityWarnThr->setValue(settings.value("odomQualityThr", _ui->spinBox_odomQualityWarnThr->value()).toInt());
_ui->checkBox_odom_onlyInliersShown->setChecked(settings.value("odomOnlyInliersShown", _ui->checkBox_odom_onlyInliersShown->isChecked()).toBool());
_ui->checkBox_posteriorGraphView->setChecked(settings.value("posteriorGraphView", _ui->checkBox_posteriorGraphView->isChecked()).toBool());
_ui->checkbox_odomDisabled->setChecked(settings.value("odomDisabled", _ui->checkbox_odomDisabled->isChecked()).toBool());
_ui->odom_registration->setCurrentIndex(settings.value("odomRegistration", _ui->odom_registration->currentIndex()).toInt());
@@ -2037,6 +2041,7 @@ void PreferencesDialog::writeGuiSettings(const QString & filePath) const
settings.setValue("figure_cache", _ui->checkBox_cacheStatistics->isChecked());
settings.setValue("notifyNewGlobalPath", _ui->checkBox_notifyWhenNewGlobalPathIsReceived->isChecked());
settings.setValue("odomQualityThr", _ui->spinBox_odomQualityWarnThr->value());
settings.setValue("odomOnlyInliersShown", _ui->checkBox_odom_onlyInliersShown->isChecked());
settings.setValue("posteriorGraphView", _ui->checkBox_posteriorGraphView->isChecked());
settings.setValue("odomDisabled", _ui->checkbox_odomDisabled->isChecked());
settings.setValue("odomRegistration", _ui->odom_registration->currentIndex());
@@ -4026,6 +4031,10 @@ int PreferencesDialog::getOdomQualityWarnThr() const
{
return _ui->spinBox_odomQualityWarnThr->value();
}
bool PreferencesDialog::isOdomOnlyInliersShown() const
{
return _ui->checkBox_odom_onlyInliersShown->isChecked();
}
bool PreferencesDialog::isPosteriorGraphView() const
{
return _ui->checkBox_posteriorGraphView->isChecked();
+55 -12
View File
@@ -63,7 +63,7 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-667</y>
<y>0</y>
<width>673</width>
<height>2747</height>
</rect>
@@ -86,7 +86,7 @@
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>18</number>
<number>19</number>
</property>
<widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,1">
@@ -272,7 +272,7 @@
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Loop Closure Detection View</string>
<string>Loop Closure Detection</string>
</property>
<layout class="QGridLayout" name="gridLayout_31" columnstretch="0,1">
<item row="1" column="0">
@@ -371,16 +371,15 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="groupBox_21">
<property name="title">
<string>3D Map View</string>
<string>Odometry</string>
</property>
<layout class="QGridLayout" name="gridLayout_38" columnstretch="0,1">
<layout class="QGridLayout" name="gridLayout_85" columnstretch="0,1">
<item row="0" column="1">
<widget class="QLabel" name="label_86">
<widget class="QLabel" name="label_369">
<property name="text">
<string>Odometry warning theshold:
Show a yellow background when the number of odometry inliers goes under this threshold. If 0, it is ignored. You can see the current odometry inliers count under Statistics view -&gt; General -&gt; Odom inliers.</string>
<string>Show only inliers.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -391,6 +390,30 @@ Show a yellow background when the number of odometry inliers goes under this thr
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_odom_onlyInliersShown">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_159">
<property name="text">
<string>Warning theshold:
Show a yellow background when the number of odometry inliers goes under this threshold. If 0, it is ignored. You can see the current odometry inliers count under Statistics view -&gt; General -&gt; Odom inliers.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="spinBox_odomQualityWarnThr">
<property name="maximum">
<number>9999</number>
@@ -2678,7 +2701,7 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<item>
<widget class="QStackedWidget" name="stackedWidget_src">
<property name="currentIndex">
<number>2</number>
<number>0</number>
</property>
<widget class="QWidget" name="page_41">
<layout class="QVBoxLayout" name="verticalLayout_64">
@@ -2788,7 +2811,7 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<item>
<widget class="QStackedWidget" name="stackedWidget_rgbd">
<property name="currentIndex">
<number>6</number>
<number>7</number>
</property>
<widget class="QWidget" name="page_32">
<layout class="QVBoxLayout" name="verticalLayout_63">
@@ -3480,7 +3503,7 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<string>For RGBD-SLAM datasets, it is 5.</string>
</property>
<property name="decimals">
<number>1</number>
<number>3</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
@@ -10763,6 +10786,26 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_370">
<property name="text">
<string>Normalize covariance values. Position variances are multiplied by norm of the transform and orientation variances are multiplied by angle of the transform.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="reg_varianceNormalized">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>