merged master to 0.11.0

This commit is contained in:
matlabbe
2015-11-30 19:39:05 -05:00
9 changed files with 273 additions and 186 deletions
+1
View File
@@ -237,6 +237,7 @@ private:
bool _idUpdatedToNewOneRehearsal;
bool _generateIds;
bool _badSignaturesIgnored;
bool _mapLabelsAdded;
int _imageDecimation;
float _laserScanDownsampleStepSize;
bool _reextractLoopClosureFeatures;
@@ -186,6 +186,7 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(Mem, RehearsalSimilarity, float, 0.6, "Rehearsal similarity.");
RTABMAP_PARAM(Mem, ImageKept, bool, false, "Keep raw images in RAM.");
RTABMAP_PARAM(Mem, BinDataKept, bool, true, "Keep binary data in db.");
RTABMAP_PARAM(Mem, MapLabelsAdded, bool, true, "Create map labels. The first node of a map will be labelled as \"map#\" where # is the map ID.");
RTABMAP_PARAM(Mem, SaveDepth16Format, bool, true, "Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters).");
RTABMAP_PARAM(Mem, NotLinkedNodesKept, bool, true, "Keep not linked nodes in db (rehearsed nodes and deleted nodes).");
RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size.");
+28 -17
View File
@@ -80,6 +80,7 @@ Memory::Memory(const ParametersMap & parameters) :
_idUpdatedToNewOneRehearsal(Parameters::defaultMemRehearsalIdUpdatedToNewOne()),
_generateIds(Parameters::defaultMemGenerateIds()),
_badSignaturesIgnored(Parameters::defaultMemBadSignaturesIgnored()),
_mapLabelsAdded(Parameters::defaultMemMapLabelsAdded()),
_imageDecimation(Parameters::defaultMemImageDecimation()),
_laserScanDownsampleStepSize(Parameters::defaultMemLaserScanDownsampleStepSize()),
_reextractLoopClosureFeatures(Parameters::defaultRGBDLoopClosureReextractFeatures()),
@@ -132,7 +133,7 @@ bool Memory::init(const std::string & dbUrl, bool dbOverwritten, const Parameter
if(_postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Clearing memory..."));
DBDriver * tmpDriver = 0;
if(!_memoryChanged && !_linksChanged)
if((!_memoryChanged && !_linksChanged) || dbOverwritten)
{
if(_dbDriver)
{
@@ -392,6 +393,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
Parameters::parse(parameters, Parameters::kMemRehearsalIdUpdatedToNewOne(), _idUpdatedToNewOneRehearsal);
Parameters::parse(parameters, Parameters::kMemGenerateIds(), _generateIds);
Parameters::parse(parameters, Parameters::kMemBadSignaturesIgnored(), _badSignaturesIgnored);
Parameters::parse(parameters, Parameters::kMemMapLabelsAdded(), _mapLabelsAdded);
Parameters::parse(parameters, Parameters::kMemRehearsalSimilarity(), _similarityThreshold);
Parameters::parse(parameters, Parameters::kMemRecentWmRatio(), _recentWmRatio);
Parameters::parse(parameters, Parameters::kMemTransferSortingByWeightId(), _transferSortingByWeightId);
@@ -410,19 +412,6 @@ void Memory::parseParameters(const ParametersMap & parameters)
UASSERT(_rehearsalMaxDistance >= 0.0f);
UASSERT(_rehearsalMaxAngle >= 0.0f);
// SLAM mode vs Localization mode
iter = parameters.find(Parameters::kMemIncrementalMemory());
if(iter != parameters.end())
{
bool value = uStr2Bool(iter->second.c_str());
if(value == false && _incrementalMemory)
{
// From SLAM to localization, change map id
this->incrementMapId();
}
_incrementalMemory = value;
}
if(_dbDriver)
{
_dbDriver->parseParameters(parameters);
@@ -479,7 +468,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
else if(_feature2D)
{
_feature2D->parseParameters(parameters);
}
}
if(_registrationVis)
{
@@ -488,7 +477,29 @@ void Memory::parseParameters(const ParametersMap & parameters)
if(_registrationIcp)
{
_registrationIcp->parseParameters(parameters);
}
}
// do this after all parameters are parsed
// SLAM mode vs Localization mode
iter = parameters.find(Parameters::kMemIncrementalMemory());
if(iter != parameters.end())
{
bool value = uStr2Bool(iter->second.c_str());
if(value == false && _incrementalMemory)
{
// From SLAM to localization, change map id
this->incrementMapId();
// The easiest way to make sure that the mapping session is saved
// is to save the memory in the database and reload it.
if((_memoryChanged || _linksChanged) && _dbDriver)
{
UWARN("Switching from Mapping to Localization mode, the database will be saved and reloaded.");
this->init(_dbDriver->getUrl());
}
}
_incrementalMemory = value;
}
}
void Memory::preUpdate()
@@ -697,7 +708,7 @@ void Memory::addSignatureToStm(Signature * signature, const cv::Mat & covariance
}
}
}
else
else if(_mapLabelsAdded)
{
//Tag the first node of the map
std::string tag = uFormat("map%d", signature->mapId());
+64 -51
View File
@@ -982,72 +982,85 @@ bool Rtabmap::process(
// Update optimizedPoses with the newly added node
Transform newPose;
if(signature->getLinks().size() == 1 &&
!smallDisplacement &&
_memory->isIncremental()) // ignore pose matching in localization mode
if(_neighborLinkRefining &&
signature->getLinks().size() == 1 &&
_memory->isIncremental() && // ignore pose matching in localization mode
rehearsedId == 0) // don't do it if rehearsal happened
{
int oldId = signature->getLinks().begin()->first;
const Signature * oldS = _memory->getSignature(oldId);
UASSERT(oldS != 0);
//============================================================
// Scan matching
//============================================================
if(_neighborLinkRefining &&
!signature->sensorData().laserScanCompressed().empty() &&
rehearsedId == 0) // don't do it if rehearsal happened
{
UINFO("Odometry correction by scan matching");
Transform guess = signature->getLinks().begin()->second.transform().inverse();
float variance = 1.0f;
int inliers = 0;
float inliersRatio = 0;
std::string rejectedMsg;
Transform t = _memory->computeIcpTransform(oldId, signature->id(), guess, &rejectedMsg, &inliers, &variance, &inliersRatio);
if(!t.isNull())
{
UINFO("Scan matching: update neighbor link (%d->%d, variance=%f) from %s to %s",
signature->id(),
oldId,
variance,
signature->getLinks().at(oldId).transform().prettyPrint().c_str(),
t.prettyPrint().c_str());
UASSERT(variance > 0.0);
_memory->updateLink(oldId, signature->id(), t, variance, variance);
Transform guess = signature->getLinks().begin()->second.transform().inverse();
if(_optimizeFromGraphEnd)
if(smallDisplacement)
{
if(signature->getLinks().begin()->second.transVariance() == 1)
{
// set small variance
UDEBUG("Set small variance. The robot is not moving.");
_memory->updateLink(signature->id(), oldId, guess, 0.0001, 0.0001);
}
}
else
{
//============================================================
// Scan matching
//============================================================
if(!signature->sensorData().laserScanCompressed().empty())
{
UINFO("Odometry correction by scan matching");
Transform guess = signature->getLinks().begin()->second.transform().inverse();
float variance = 1.0f;
int inliers = 0;
float inliersRatio = 0;
std::string rejectedMsg;
Transform t = _memory->computeIcpTransform(oldId, signature->id(), guess, &rejectedMsg, &inliers, &variance, &inliersRatio);
if(!t.isNull())
{
// update all previous nodes
// Normally _mapCorrection should be identity, but if _optimizeFromGraphEnd
// parameters just changed state, we should put back all poses without map correction.
Transform u = guess * t.inverse();
std::map<int, Transform>::iterator jter = _optimizedPoses.find(oldId);
UASSERT(jter!=_optimizedPoses.end());
Transform up = jter->second * u * jter->second.inverse();
Transform mapCorrectionInv = _mapCorrection.inverse();
for(std::map<int, Transform>::iterator iter=_optimizedPoses.begin(); iter!=_optimizedPoses.end(); ++iter)
UINFO("Scan matching: update neighbor link (%d->%d, variance=%f) from %s to %s",
signature->id(),
oldId,
variance,
signature->getLinks().at(oldId).transform().prettyPrint().c_str(),
t.prettyPrint().c_str());
UASSERT(variance > 0.0);
_memory->updateLink(oldId, signature->id(), t, variance, variance);
if(_optimizeFromGraphEnd)
{
iter->second = mapCorrectionInv * up * iter->second;
// update all previous nodes
// Normally _mapCorrection should be identity, but if _optimizeFromGraphEnd
// parameters just changed state, we should put back all poses without map correction.
Transform u = guess * t.inverse();
std::map<int, Transform>::iterator jter = _optimizedPoses.find(oldId);
UASSERT(jter!=_optimizedPoses.end());
Transform up = jter->second * u * jter->second.inverse();
Transform mapCorrectionInv = _mapCorrection.inverse();
for(std::map<int, Transform>::iterator iter=_optimizedPoses.begin(); iter!=_optimizedPoses.end(); ++iter)
{
iter->second = mapCorrectionInv * up * iter->second;
}
}
}
}
else
{
UINFO("Scan matching rejected: %s", rejectedMsg.c_str());
if(variance > 0)
else
{
double sqrtVar = sqrt(variance);
_memory->updateLink(signature->id(), oldId, guess, sqrtVar, sqrtVar);
UINFO("Scan matching rejected: %s", rejectedMsg.c_str());
if(variance > 0)
{
double sqrtVar = sqrt(variance);
_memory->updateLink(signature->id(), oldId, guess, sqrtVar, sqrtVar);
}
}
statistics_.addStatistic(Statistics::kNeighborLinkRefiningAccepted(), !t.isNull()?1.0f:0);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningInliers(), inliers);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningInliers_ratio(), inliersRatio);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningVariance(), variance);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningPts(), signature->sensorData().laserScanRaw().cols);
}
statistics_.addStatistic(Statistics::kNeighborLinkRefiningAccepted(), !t.isNull()?1.0f:0);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningInliers(), inliers);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningInliers_ratio(), inliersRatio);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningVariance(), variance);
statistics_.addStatistic(Statistics::kNeighborLinkRefiningPts(), signature->sensorData().laserScanRaw().cols);
}
timeNeighborLinkRefining = timer.ticks();
ULOGGER_INFO("timeScanMatching=%fs", timeNeighborLinkRefining);
ULOGGER_INFO("timeScanMatching=%fs", timeNeighborLinkRefining);
UASSERT(oldS->hasLink(signature->id()));
UASSERT(uContains(_optimizedPoses, oldId));
+1
View File
@@ -73,6 +73,7 @@ bool DataRecorder::init(const QString & path, bool recordInRAM)
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
customParameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), "-1")); // desactivate keypoints extraction
customParameters.insert(ParametersPair(Parameters::kMemBinDataKept(), "true")); // to keep images
customParameters.insert(ParametersPair(Parameters::kMemMapLabelsAdded(), "false")); // don't create map labels
customParameters.insert(ParametersPair(Parameters::kMemBadSignaturesIgnored(), "true")); // make usre memory cleanup is done
if(!recordInRAM)
{
+45 -6
View File
@@ -1284,7 +1284,10 @@ void DatabaseViewer::updateIds()
if(ids_.size())
{
updateLoopClosuresSlider();
updateGraphView();
if(ui_->graphViewer->isVisible())
{
updateGraphView();
}
}
}
@@ -1391,6 +1394,16 @@ void DatabaseViewer::generateLocalGraph()
void DatabaseViewer::generateTOROGraph()
{
if(graphes_.empty())
{
this->updateGraphView();
if(graphes_.empty() || ui_->horizontalSlider_iterations->maximum() != (int)graphes_.size()-1)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("No graph in database?!"));
return;
}
}
if(!graphes_.size() || !graphLinks_.size())
{
QMessageBox::warning(this, tr("Cannot generate a TORO graph"), tr("No poses or no links..."));
@@ -1423,6 +1436,16 @@ void DatabaseViewer::generateTOROGraph()
void DatabaseViewer::generateG2OGraph()
{
if(graphes_.empty())
{
this->updateGraphView();
if(graphes_.empty() || ui_->horizontalSlider_iterations->maximum() != (int)graphes_.size()-1)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("No graph in database?!"));
return;
}
}
if(!graphes_.size() || !graphLinks_.size())
{
QMessageBox::warning(this, tr("Cannot generate a g2o graph"), tr("No poses or no links..."));
@@ -1676,6 +1699,17 @@ void DatabaseViewer::generate3DMap()
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("The database is empty..."));
return;
}
if(graphes_.empty())
{
this->updateGraphView();
if(graphes_.empty() || ui_->horizontalSlider_iterations->maximum() != (int)graphes_.size()-1)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("No graph in database?!"));
return;
}
}
bool ok = false;
QStringList items;
items.append("1");
@@ -1905,6 +1939,16 @@ void DatabaseViewer::generate3DLaserScans()
void DatabaseViewer::detectMoreLoopClosures()
{
if(graphes_.empty())
{
this->updateGraphView();
if(graphes_.empty() || ui_->horizontalSlider_iterations->maximum() != (int)graphes_.size()-1)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("No graph in database?!"));
return;
}
}
const std::map<int, Transform> & optimizedPoses = graphes_.back();
int iterations = ui_->spinBox_detectMore_iterations->value();
@@ -3326,11 +3370,6 @@ void DatabaseViewer::sliderIterationsValueChanged(int value)
}
void DatabaseViewer::updateGraphView()
{
if(!ui_->graphViewer->isVisible())
{
return;
}
ui_->label_loopClosures->clear();
ui_->label_poses->clear();
if(poses_.size())
+10 -13
View File
@@ -2257,9 +2257,10 @@ void MainWindow::processRtabmapGlobalPathEvent(const rtabmap::RtabmapGlobalPathE
tr("Setting goal failed!"),
tr("Setting goal to location %1%2 failed. "
"Some reasons: \n"
"1) the location doesn't exist in the graph,\n"
"2) the location is not linked to the global graph or\n"
"3) the location is too near of the current location (goal already reached).")
"1) the robot is not yet localized in the map,\n"
"2) the location doesn't exist in the map,\n"
"3) the location is not linked to the global map or\n"
"4) the location is too near of the current location (goal already reached).")
.arg(event.getGoal())
.arg(!event.getGoalLabel().empty()?QString(" \"%1\"").arg(event.getGoalLabel().c_str()):""),
QMessageBox::Ok,
@@ -2843,21 +2844,17 @@ void MainWindow::editDatabase()
viewer->setWindowModality(Qt::WindowModal);
viewer->setAttribute(Qt::WA_DeleteOnClose, true);
viewer->showCloseButton();
if(viewer->openDatabase(path))
if(viewer->isSavedMaximized())
{
if(viewer->isSavedMaximized())
{
viewer->showMaximized();
}
else
{
viewer->show();
}
viewer->showMaximized();
}
else
{
delete viewer;
viewer->show();
}
viewer->openDatabase(path);
}
}
+1
View File
@@ -477,6 +477,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->general_checkBox_RehearsalIdUpdatedToNewOne->setObjectName(Parameters::kMemRehearsalIdUpdatedToNewOne().c_str());
_ui->general_checkBox_generateIds->setObjectName(Parameters::kMemGenerateIds().c_str());
_ui->general_checkBox_badSignaturesIgnored->setObjectName(Parameters::kMemBadSignaturesIgnored().c_str());
_ui->general_checkBox_createMapLabels->setObjectName(Parameters::kMemMapLabelsAdded().c_str());
_ui->general_checkBox_initWMWithAllNodes->setObjectName(Parameters::kMemInitWMWithAllNodes().c_str());
_ui->checkBox_localSpaceScanMatchingIDsSaved->setObjectName(Parameters::kRGBDScanMatchingIdsSavedInLinks().c_str());
_ui->spinBox_imageDecimation->setObjectName(Parameters::kMemImageDecimation().c_str());
+122 -99
View File
@@ -63,9 +63,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>760</width>
<height>1802</height>
<y>-19</y>
<width>755</width>
<height>1846</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
@@ -86,7 +86,7 @@
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>21</number>
<number>8</number>
</property>
<widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29">
@@ -4468,13 +4468,75 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QSpinBox" name="spinBox_imageDecimation">
<item row="0" column="0">
<widget class="QSpinBox" name="general_spinBox_maxStMemSize">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>16</number>
<number>9999</number>
</property>
<property name="value">
<number>15</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_retrieved">
<property name="text">
<string>Maximum locations retrieved at the same time from LTM to WM.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="general_checkBox_generateIds">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="general_checkBox_transferSortingByWeightId">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLabel" name="label_retrieved_9">
<property name="text">
<string>Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_retrieved_10">
<property name="text">
<string>Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@@ -4497,20 +4559,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QSpinBox" name="general_spinBox_maxStMemSize">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>15</number>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QLabel" name="label_retrieved_4">
<property name="text">
<string>Initialize the Woking Memory with all nodes from Long-Term memory, instead of only nodes of the last session. This may be useful in localization mode, where less processing time is required than in SLAM mode, so more nodes can be kept in Working Memory.</string>
@@ -4562,30 +4611,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_retrieved">
<property name="text">
<string>Maximum locations retrieved at the same time from LTM to WM.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="general_checkBox_generateIds">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QCheckBox" name="general_checkBox_badSignaturesIgnored">
<property name="text">
<string/>
@@ -4595,7 +4621,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QLabel" name="label_retrieved_3">
<property name="text">
<string>Bad signatures are ignored.</string>
@@ -4608,7 +4634,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QCheckBox" name="general_checkBox_initWMWithAllNodes">
<property name="text">
<string/>
@@ -4618,7 +4644,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QLabel" name="label_retrieved_5">
<property name="text">
<string>Keep raw sensor data. Only useful to save loop closure computation time when features re-extraction is enabled. Disable to save RAM memory.</string>
@@ -4631,7 +4657,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QCheckBox" name="general_checkBox_keepRawData">
<property name="text">
<string/>
@@ -4641,7 +4667,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="12" column="1">
<widget class="QLabel" name="label_retrieved_6">
<property name="text">
<string>Image decimation. This feature can be used to save images in lower resolution (size/decimation).</string>
@@ -4667,17 +4693,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="general_checkBox_transferSortingByWeightId">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="11" column="1">
<item row="13" column="1">
<widget class="QLabel" name="label_retrieved_8">
<property name="text">
<string>If &gt; 1, downsample the laser scans when creating a location. This feature can be used to save laser scans already downsampled.</string>
@@ -4690,43 +4706,7 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_retrieved_9">
<property name="text">
<string>Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="general_checkBox_saveDepth16bits">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_retrieved_10">
<property name="text">
<string>Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QCheckBox" name="general_checkBox_reduceGraph">
<property name="text">
<string/>
@@ -4736,7 +4716,30 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="11" column="0">
<item row="5" column="1">
<widget class="QLabel" name="label_retrieved_11">
<property name="text">
<string>Create map labels. The first node of a map will be labelled as &quot;map#&quot; where # is the map ID.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="general_checkBox_createMapLabels">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QSpinBox" name="general_spinBox_laserScanDownsample">
<property name="minimumSize">
<size>
@@ -4752,6 +4755,26 @@ see Sqlite3 doc 'PRAGMA temp_store'.</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QSpinBox" name="spinBox_imageDecimation">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>16</number>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QCheckBox" name="general_checkBox_saveDepth16bits">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>