Added floor filtering options for features extraction (#1408)

* Added floor filtering options for features extraction

* cleanup
This commit is contained in:
matlabbe
2024-12-10 12:36:21 -08:00
committed by GitHub
parent a613998652
commit 38cacb7978
11 changed files with 579 additions and 317 deletions
+1
View File
@@ -314,6 +314,7 @@ private:
bool _badSignaturesIgnored;
bool _mapLabelsAdded;
bool _depthAsMask;
float _maskFloorThreshold;
bool _stereoFromMotion;
unsigned int _imagePreDecimation;
unsigned int _imagePostDecimation;
@@ -221,6 +221,7 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(Mem, BadSignaturesIgnored, bool, false, "Bad signatures are ignored.");
RTABMAP_PARAM(Mem, InitWMWithAllNodes, bool, false, "Initialize the Working Memory with all nodes in Long-Term Memory. When false, it is initialized with nodes of the previous session.");
RTABMAP_PARAM(Mem, DepthAsMask, bool, true, "Use depth image as mask when extracting features for vocabulary.");
RTABMAP_PARAM(Mem, DepthMaskFloorThr, float, 0.0, uFormat("Filter floor from depth mask below specified threshold (m) before extracting features. 0 means disabled, negative means remove all objects above the floor threshold instead. Ignored if %s is false.", kMemDepthAsMask().c_str()));
RTABMAP_PARAM(Mem, StereoFromMotion, bool, false, uFormat("Triangulate features without depth using stereo from motion (odometry). It would be ignored if %s is true and the feature detector used supports masking.", kMemDepthAsMask().c_str()));
RTABMAP_PARAM(Mem, ImagePreDecimation, unsigned int, 1, uFormat("Decimation of the RGB image before visual feature detection. If depth size is larger than decimated RGB size, depth is decimated to be always at most equal to RGB size. If %s is true and if depth is smaller than decimated RGB, depth may be interpolated to match RGB size for feature detection.",kMemDepthAsMask().c_str()));
RTABMAP_PARAM(Mem, ImagePostDecimation, unsigned int, 1, uFormat("Decimation of the RGB image before saving it to database. If depth size is larger than decimated RGB size, depth is decimated to be always at most equal to RGB size. Decimation is done from the original image. If set to same value than %s, data already decimated is saved (no need to re-decimate the image).", kMemImagePreDecimation().c_str()));
@@ -703,6 +704,7 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(Vis, MaxDepth, float, 0, "Max depth of the features (0 means no limit).");
RTABMAP_PARAM(Vis, MinDepth, float, 0, "Min depth of the features (0 means no limit).");
RTABMAP_PARAM(Vis, DepthAsMask, bool, true, "Use depth image as mask when extracting features.");
RTABMAP_PARAM(Vis, DepthMaskFloorThr, float, 0.0, uFormat("Filter floor from depth mask below specified threshold (m) before extracting features. 0 means disabled, negative means remove all objects above the floor threshold instead. Ignored if %s is false.", kVisDepthAsMask().c_str()));
RTABMAP_PARAM_STR(Vis, RoiRatios, "0.0 0.0 0.0 0.0", "Region of interest ratios [left, right, top, bottom].");
RTABMAP_PARAM(Vis, SubPixWinSize, int, 3, "See cv::cornerSubPix().");
RTABMAP_PARAM(Vis, SubPixIterations, int, 0, "See cv::cornerSubPix(). 0 disables sub pixel refining.");
@@ -101,6 +101,7 @@ private:
bool _guessMatchToProjection;
int _bundleAdjustment;
bool _depthAsMask;
float _maskFloorThreshold;
float _minInliersDistributionThr;
float _maxInliersMeanDistance;
+16
View File
@@ -364,6 +364,22 @@ void RTABMAP_CORE_EXPORT fillProjectedCloudHoles(
bool verticalDirection,
bool fillToBorder);
/**
* @brief Remove values below a floor threshold in a depth image.
*
* @param depth the depth image to filter (can be a multi-camera depth image).
* @param cameraModels corresponding camera model(s) to depth image, with valid
* local transform between base frame to camera frame.
* @param threshold height from base frame at which pixels below it are set to 0.
* @param depthBelow depth image of the pixels below the floor theshold.
* @return cv::Mat depth image of the pixels above the floor theshold.
*/
cv::Mat RTABMAP_CORE_EXPORT filterFloor(
const cv::Mat & depth,
const std::vector<CameraModel> & cameraModels,
float threshold,
cv::Mat * depthBelow = 0);
/**
* For each point, return pixel of the best camera (NodeID->CameraIndex)
* looking at it based on the policy and parameters
+11 -6
View File
@@ -1315,6 +1315,11 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
std::vector<cv::KeyPoint> keypoints;
cv::Mat imgRoi(image, roi);
cv::Mat maskRoi;
if(!mask.empty())
{
maskRoi = cv::Mat(mask, roi);
}
#ifdef RTABMAP_CUDASIFT
if(gpu_)
{
@@ -1383,6 +1388,12 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
//std::cout << cv::Mat(1, 128*4, CV_8UC1, desc) << std::endl;
continue;
}
// Ignore keypoints not in the mask
if(!maskRoi.empty() && maskRoi.at<unsigned char>(cudaSiftData_->h_data[i].ypos, cudaSiftData_->h_data[i].xpos) == 0)
{
continue;
}
//Keep track of the data, to be easier to manage the data in the next step
hessianMap.insert(std::pair<float, int>(cudaSiftData_->h_data[i].sharpness, i));
}
@@ -1412,12 +1423,6 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
else
#endif
{
cv::Mat maskRoi;
if(!mask.empty())
{
maskRoi = cv::Mat(mask, roi);
}
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
#ifdef RTABMAP_NONFREE
sift_->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
+22 -1
View File
@@ -91,6 +91,7 @@ Memory::Memory(const ParametersMap & parameters) :
_badSignaturesIgnored(Parameters::defaultMemBadSignaturesIgnored()),
_mapLabelsAdded(Parameters::defaultMemMapLabelsAdded()),
_depthAsMask(Parameters::defaultMemDepthAsMask()),
_maskFloorThreshold(Parameters::defaultMemDepthMaskFloorThr()),
_stereoFromMotion(Parameters::defaultMemStereoFromMotion()),
_imagePreDecimation(Parameters::defaultMemImagePreDecimation()),
_imagePostDecimation(Parameters::defaultMemImagePostDecimation()),
@@ -576,6 +577,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
Parameters::parse(params, Parameters::kMemTransferSortingByWeightId(), _transferSortingByWeightId);
Parameters::parse(params, Parameters::kMemSTMSize(), _maxStMemSize);
Parameters::parse(params, Parameters::kMemDepthAsMask(), _depthAsMask);
Parameters::parse(params, Parameters::kMemDepthMaskFloorThr(), _maskFloorThreshold);
Parameters::parse(params, Parameters::kMemStereoFromMotion(), _stereoFromMotion);
Parameters::parse(params, Parameters::kMemImagePreDecimation(), _imagePreDecimation);
Parameters::parse(params, Parameters::kMemImagePostDecimation(), _imagePostDecimation);
@@ -4884,7 +4886,26 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
imageMono.cols % decimatedData.depthRaw().cols == 0 &&
imageMono.rows/decimatedData.depthRaw().rows == imageMono.cols/decimatedData.depthRaw().cols)
{
depthMask = util2d::interpolate(decimatedData.depthRaw(), imageMono.rows/decimatedData.depthRaw().rows, 0.1f);
depthMask = decimatedData.depthRaw();
if(_maskFloorThreshold != 0.0f)
{
UASSERT(!decimatedData.cameraModels().empty());
UDEBUG("Masking floor (threshold=%f)", _maskFloorThreshold);
if(_maskFloorThreshold<0.0f)
{
cv::Mat depthBelow;
util3d::filterFloor(depthMask, decimatedData.cameraModels(), _maskFloorThreshold*-1.0f, &depthBelow);
depthMask = depthBelow;
}
else
{
depthMask = util3d::filterFloor(depthMask, decimatedData.cameraModels(), _maskFloorThreshold);
}
UDEBUG("Masking floor done.");
}
depthMask = util2d::interpolate(depthMask, imageMono.rows/depthMask.rows, 0.1f);
}
else
{
+44 -4
View File
@@ -94,6 +94,7 @@ RegistrationVis::RegistrationVis(const ParametersMap & parameters, Registration
_guessMatchToProjection(Parameters::defaultVisCorGuessMatchToProjection()),
_bundleAdjustment(Parameters::defaultVisBundleAdjustment()),
_depthAsMask(Parameters::defaultVisDepthAsMask()),
_maskFloorThreshold(Parameters::defaultVisDepthMaskFloorThr()),
_minInliersDistributionThr(Parameters::defaultVisMinInliersDistribution()),
_maxInliersMeanDistance(Parameters::defaultVisMeanInliersDistance()),
_detectorFrom(0),
@@ -155,6 +156,7 @@ void RegistrationVis::parseParameters(const ParametersMap & parameters)
Parameters::parse(parameters, Parameters::kVisCorGuessMatchToProjection(), _guessMatchToProjection);
Parameters::parse(parameters, Parameters::kVisBundleAdjustment(), _bundleAdjustment);
Parameters::parse(parameters, Parameters::kVisDepthAsMask(), _depthAsMask);
Parameters::parse(parameters, Parameters::kVisDepthMaskFloorThr(), _maskFloorThreshold);
Parameters::parse(parameters, Parameters::kVisMinInliersDistribution(), _minInliersDistributionThr);
Parameters::parse(parameters, Parameters::kVisMeanInliersDistance(), _maxInliersMeanDistance);
uInsert(_bundleParameters, parameters);
@@ -423,13 +425,32 @@ Transform RegistrationVis::computeTransformationImpl(
imageFrom.cols % fromSignature.sensorData().depthRaw().cols == 0 &&
imageFrom.rows/fromSignature.sensorData().depthRaw().rows == fromSignature.sensorData().imageRaw().cols/fromSignature.sensorData().depthRaw().cols)
{
depthMask = util2d::interpolate(fromSignature.sensorData().depthRaw(), fromSignature.sensorData().imageRaw().rows/fromSignature.sensorData().depthRaw().rows, 0.1f);
depthMask = fromSignature.sensorData().depthRaw();
if(_maskFloorThreshold != 0.0f)
{
UASSERT(!fromSignature.sensorData().cameraModels().empty());
UDEBUG("Masking floor (threshold=%f)", _maskFloorThreshold);
if(_maskFloorThreshold<0.0f)
{
cv::Mat depthBelow;
util3d::filterFloor(depthMask, fromSignature.sensorData().cameraModels(), _maskFloorThreshold*-1.0f, &depthBelow);
depthMask = depthBelow;
}
else
{
depthMask = util3d::filterFloor(depthMask, fromSignature.sensorData().cameraModels(), _maskFloorThreshold);
}
UDEBUG("Masking floor done.");
}
depthMask = util2d::interpolate(depthMask, imageFrom.rows/depthMask.rows, 0.1f);
}
else
{
UWARN("%s is true, but RGB size (%dx%d) modulo depth size (%dx%d) is not 0. Ignoring depth mask for feature detection.",
Parameters::kVisDepthAsMask().c_str(),
fromSignature.sensorData().imageRaw().rows, fromSignature.sensorData().imageRaw().cols,
imageFrom.rows, imageFrom.cols,
fromSignature.sensorData().depthRaw().rows, fromSignature.sensorData().depthRaw().cols);
}
}
@@ -770,13 +791,32 @@ Transform RegistrationVis::computeTransformationImpl(
imageTo.cols % toSignature.sensorData().depthRaw().cols == 0 &&
imageTo.rows/toSignature.sensorData().depthRaw().rows == imageTo.cols/toSignature.sensorData().depthRaw().cols)
{
depthMask = util2d::interpolate(toSignature.sensorData().depthRaw(), imageTo.rows/toSignature.sensorData().depthRaw().rows, 0.1f);
depthMask = toSignature.sensorData().depthRaw();
if(_maskFloorThreshold != 0.0f)
{
UASSERT(!toSignature.sensorData().cameraModels().empty());
UDEBUG("Masking floor (threshold=%f)", _maskFloorThreshold);
if(_maskFloorThreshold<0.0f)
{
cv::Mat depthBelow;
util3d::filterFloor(depthMask, toSignature.sensorData().cameraModels(), _maskFloorThreshold*-1.0f, &depthBelow);
depthMask = depthBelow;
}
else
{
depthMask = util3d::filterFloor(depthMask, toSignature.sensorData().cameraModels(), _maskFloorThreshold);
}
UDEBUG("Masking floor done.");
}
depthMask = util2d::interpolate(depthMask, imageTo.rows/depthMask.rows, 0.1f);
}
else
{
UWARN("%s is true, but RGB size (%dx%d) modulo depth size (%dx%d) is not 0. Ignoring depth mask for feature detection.",
Parameters::kVisDepthAsMask().c_str(),
toSignature.sensorData().imageRaw().rows, toSignature.sensorData().imageRaw().cols,
imageTo.rows, imageTo.cols,
toSignature.sensorData().depthRaw().rows, toSignature.sensorData().depthRaw().cols);
}
}
+109
View File
@@ -3007,6 +3007,115 @@ void fillProjectedCloudHoles(cv::Mat & registeredDepth, bool verticalDirection,
}
}
cv::Mat filterFloor(const cv::Mat & depth, const std::vector<CameraModel> & cameraModels, float threshold, cv::Mat * depthBelow)
{
cv::Mat output = depth.clone();
if(depth.empty())
{
return output;
}
if(depthBelow)
{
*depthBelow = cv::Mat::zeros(output.size(), output.type());
}
UASSERT(!cameraModels.empty());
UASSERT(cameraModels[0].isValidForReprojection());
// Support camera model with different resolution than depth image
float rgbToDepthFactorX = float(cameraModels[0].imageWidth()) / float(output.cols/cameraModels.size());
float rgbToDepthFactorY = float(cameraModels[0].imageHeight()) / float(output.rows);
int depthWidth = output.cols/cameraModels.size();
UASSERT(depthWidth*(int)cameraModels.size() == output.cols);
// for each camera
for(size_t i=0; i<cameraModels.size(); ++i)
{
const CameraModel & cam = cameraModels[i];
UASSERT(cam.isValidForReprojection());
const Transform & localTransform = cam.localTransform();
UASSERT(!localTransform.isNull());
if(i>0)
{
// Make sure all models are the same resolution
UASSERT(cam.imageWidth() == cameraModels[i-1].imageWidth());
UASSERT(cam.imageHeight() == cameraModels[i-1].imageHeight());
}
float depthFx = cam.fx() / rgbToDepthFactorX;
float depthFy = cam.fy() / rgbToDepthFactorY;
float depthCx = cam.cx() / rgbToDepthFactorX;
float depthCy = cam.cy() / rgbToDepthFactorY;
cv::Mat subImage = output.colRange(cv::Range(i*depthWidth, (i+1)*depthWidth));
cv::Mat subImageBelow;
if(depthBelow)
subImageBelow = depthBelow->colRange(cv::Range(i*depthWidth, (i+1)*depthWidth));
for(int y=0; y<subImage.rows; ++y)
{
if(subImage.type() == CV_16UC1)
{
unsigned short * ptr = (unsigned short *)subImage.row(y).ptr();
unsigned short * ptrBelow = 0;
if(depthBelow)
{
ptrBelow = (unsigned short *)subImageBelow.row(y).ptr();
}
for(int x=0; x<subImage.cols; ++x)
{
if(ptr[x] > 0)
{
float d = float(ptr[x])/1000.0f;
cv::Point3f pt;
pt.x = (x - depthCx) * d / depthFx;
pt.y = (y - depthCy) * d / depthFy;
pt.z = d;
pt = util3d::transformPoint(pt, localTransform);
if(pt.z < threshold)
{
if(ptrBelow)
{
ptrBelow[x] = ptr[x];
}
ptr[x] = 0;
}
}
}
}
else // CV_32FC1
{
float * ptr = (float *)subImage.row(y).ptr();
float * ptrBelow = 0;
if(depthBelow)
{
ptrBelow = (float *)subImageBelow.row(y).ptr();
}
for(int x=0; x<subImage.cols; ++x)
{
if(ptr[x] > 0.0f)
{
float & d = ptr[x];
cv::Point3f pt;
pt.x = (x - depthCx) * d / depthFx;
pt.y = (y - depthCy) * d / depthFy;
pt.z = d;
pt = util3d::transformPoint(pt, localTransform);
if(pt.z < threshold)
{
if(ptrBelow)
{
ptrBelow[x] = ptr[x];
}
d = 0;
}
}
}
}
}
}
return output;
}
class ProjectionInfo {
public:
ProjectionInfo():
+2 -1
View File
@@ -405,7 +405,8 @@ void ParametersToolBox::addParameter(QVBoxLayout * layout,
// set minimum for selected parameters
if(key.compare(Parameters::kGridMinGroundHeight().c_str()) == 0 ||
key.compare(Parameters::kGridMaxGroundHeight().c_str()) == 0 ||
key.compare(Parameters::kGridMaxObstacleHeight().c_str()) == 0)
key.compare(Parameters::kGridMaxObstacleHeight().c_str()) == 0 ||
key.compare(Parameters::kVisDepthMaskFloorThr().c_str()) == 0)
{
widget->setMinimum(-1000000.0);
}
+2
View File
@@ -1057,6 +1057,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->surf_doubleSpinBox_maxDepth->setObjectName(Parameters::kKpMaxDepth().c_str());
_ui->surf_doubleSpinBox_minDepth->setObjectName(Parameters::kKpMinDepth().c_str());
_ui->checkBox_memDepthAsMask->setObjectName(Parameters::kMemDepthAsMask().c_str());
_ui->doubleSpinBox_memDepthMaskFloorThr->setObjectName(Parameters::kMemDepthMaskFloorThr().c_str());
_ui->checkBox_memStereoFromMotion->setObjectName(Parameters::kMemStereoFromMotion().c_str());
_ui->surf_spinBox_wordsPerImageTarget->setObjectName(Parameters::kKpMaxFeatures().c_str());
_ui->checkBox_kp_ssc->setObjectName(Parameters::kKpSSC().c_str());
@@ -1289,6 +1290,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->loopClosure_bowMaxDepth->setObjectName(Parameters::kVisMaxDepth().c_str());
_ui->loopClosure_bowMinDepth->setObjectName(Parameters::kVisMinDepth().c_str());
_ui->checkBox_visDepthAsMask->setObjectName(Parameters::kVisDepthAsMask().c_str());
_ui->doubleSpinBox_visDepthMaskFloorThr->setObjectName(Parameters::kVisDepthMaskFloorThr().c_str());
_ui->loopClosure_roi->setObjectName(Parameters::kVisRoiRatios().c_str());
_ui->subpix_winSize->setObjectName(Parameters::kVisSubPixWinSize().c_str());
_ui->subpix_iterations->setObjectName(Parameters::kVisSubPixIterations().c_str());
+369 -305
View File
@@ -95,7 +95,7 @@
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>3</number>
<number>9</number>
</property>
<widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,0">
@@ -10666,16 +10666,10 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<layout class="QVBoxLayout" name="verticalLayout_60">
<item>
<layout class="QGridLayout" name="gridLayout_32" columnstretch="0,1">
<item row="5" column="1">
<widget class="QLabel" name="label_22">
<property name="toolTip">
<string>0 means that the response (hessian) threshold
used for the detector will not be adapted.
Otherwise, the threshold is modified to
generate the number of words requested.</string>
</property>
<item row="1" column="1">
<widget class="QLabel" name="label_53">
<property name="text">
<string>Maximum words per image (0=no maximum). Setting to -1 will disable features extraction, so disabling loop closure detection indirectly.</string>
<string>Maximum words depth (0 means inf). Only used when a depth image is provided. Applied before &quot;Maximum words per image&quot;.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -10685,61 +10679,26 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_81">
<property name="text">
<string>Bad signature ratio (less than Ratio x AverageWordsPerImage = bad).</string>
<item row="12" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi2">
<property name="suffix">
<string> %</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLabel" name="label_99">
<property name="text">
<string>Top ROI ratio (0 = no change).</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="0">
<widget class="QDoubleSpinBox" name="surf_doubleSpinBox_ratioBadSign">
<property name="decimals">
<number>2</number>
<number>0</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QSpinBox" name="surf_spinBox_wordsPerImageTarget">
<property name="minimum">
<double>0.000000000000000</double>
<number>-1</number>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
<number>2000</number>
</property>
<property name="value">
<double>0.250000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_57">
<property name="text">
<string>Minimum words depth. Only used when a depth image is provided. Applied before &quot;Maximum words per image&quot;.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<number>500</number>
</property>
</widget>
</item>
@@ -10827,17 +10786,75 @@ generate the number of words requested.</string>
</item>
</widget>
</item>
<item row="15" column="1">
<widget class="QLabel" name="label_86">
<property name="text">
<string>Number of columns of the grid used to extract uniformly &quot;max words / grid cells&quot; features from each cell.</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="1">
<widget class="QLabel" name="label_57">
<property name="text">
<string>Minimum words depth. Only used when a depth image is provided. Applied before &quot;Maximum words per image&quot;.</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_7551">
<property name="text">
<string>If true, SSC (Suppression via Square Covering) is applied to limit keypoints.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QDoubleSpinBox" name="surf_doubleSpinBox_ratioBadSign">
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.250000000000000</double>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLineEdit" name="lineEdit_kp_roi">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_101">
<item row="5" column="1">
<widget class="QLabel" name="label_582">
<property name="text">
<string>ROI ratios [left, right, top, bottom] between 0 and 1.</string>
<string>Triangulate features without depth using stereo from motion (odometry). It would be ignored if depth as mask is checked and the feature detector used supports masking.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -10847,21 +10864,8 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_53">
<property name="text">
<string>Maximum words depth (0 means inf). Only used when a depth image is provided. Applied before &quot;Maximum words per image&quot;.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi1">
<item row="13" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi3">
<property name="suffix">
<string> %</string>
</property>
@@ -10870,19 +10874,6 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_117">
<property name="text">
<string>Visual word type.</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="QDoubleSpinBox" name="surf_doubleSpinBox_maxDepth">
<property name="suffix">
@@ -10905,30 +10896,10 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QSpinBox" name="surf_spinBox_wordsPerImageTarget">
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>2000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkBox_kp_ssc">
<item row="3" column="1">
<widget class="QLabel" name="label_262">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_7551">
<property name="text">
<string>If true, SSC (Suppression via Square Covering) is applied to limit keypoints.</string>
<string>Use depth image as mask when extracting features.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -10938,30 +10909,7 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi0">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>0</number>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_97">
<property name="text">
<string>Left ROI ratio (0 = no change).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QLabel" name="label_98">
<property name="text">
<string>Right ROI ratio (0 = no change).</string>
@@ -10974,30 +10922,10 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi2">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>0</number>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi3">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>0</number>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="label_100">
<widget class="QLabel" name="label_99">
<property name="text">
<string>Bottom ROI ratio (0 = no change).</string>
<string>Top ROI ratio (0 = no change).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -11007,6 +10935,131 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_22">
<property name="toolTip">
<string>0 means that the response (hessian) threshold
used for the detector will not be adapted.
Otherwise, the threshold is modified to
generate the number of words requested.</string>
</property>
<property name="text">
<string>Maximum words per image (0=no maximum). Setting to -1 will disable features extraction, so disabling loop closure detection indirectly.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_117">
<property name="text">
<string>Visual word type.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="label_97">
<property name="text">
<string>Left ROI ratio (0 = no change).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi0">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>0</number>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QSpinBox" name="spinBox_KPGridRows">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="checkBox_memStereoFromMotion">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_101">
<property name="text">
<string>ROI ratios [left, right, top, bottom] between 0 and 1.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_kp_roi1">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_memDepthAsMask">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_81">
<property name="text">
<string>Bad signature ratio (less than Ratio x AverageWordsPerImage = bad).</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="0">
<widget class="QCheckBox" name="checkBox_kp_ssc">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QDoubleSpinBox" name="surf_doubleSpinBox_minDepth">
<property name="suffix">
@@ -11029,7 +11082,7 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="13" column="1">
<item row="14" column="1">
<widget class="QLabel" name="label_50">
<property name="text">
<string>Number of rows of the grid used to extract uniformly &quot;max words / grid cells&quot; features from each cell.</string>
@@ -11042,20 +11095,20 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QSpinBox" name="spinBox_KPGridRows">
<property name="minimum">
<number>1</number>
<item row="13" column="1">
<widget class="QLabel" name="label_100">
<property name="text">
<string>Bottom ROI ratio (0 = no change).</string>
</property>
<property name="maximum">
<number>99</number>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="value">
<number>1</number>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="14" column="0">
<item row="15" column="0">
<widget class="QSpinBox" name="spinBox_KPGridCols">
<property name="minimum">
<number>1</number>
@@ -11068,43 +11121,10 @@ generate the number of words requested.</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QLabel" name="label_86">
<property name="text">
<string>Number of columns of the grid used to extract uniformly &quot;max words / grid cells&quot; features from each cell.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_262">
<property name="text">
<string>Use depth image as mask when extracting features.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_memDepthAsMask">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_582">
<widget class="QLabel" name="label_591">
<property name="text">
<string>Triangulate features without depth using stereo from motion (odometry). It would be ignored if depth as mask is checked and the feature detector used supports masking.</string>
<string>Filter floor from depth mask. 0 means disabled, negative means keeping pixels below the floor theshold instead.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -11115,9 +11135,24 @@ generate the number of words requested.</string>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBox_memStereoFromMotion">
<property name="text">
<string/>
<widget class="QDoubleSpinBox" name="doubleSpinBox_memDepthMaskFloorThr">
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>-99.000000000000000</double>
</property>
<property name="maximum">
<double>99.000000000000000</double>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
@@ -22553,10 +22588,10 @@ Lower the ratio -&gt; higher the precision.</string>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_25" columnstretch="0,1">
<item row="4" column="1">
<widget class="QLabel" name="label_237">
<item row="6" column="1">
<widget class="QLabel" name="label_450">
<property name="text">
<string>Maximum feature depth.</string>
<string>Use depth image as mask when extracting features.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -22566,49 +22601,6 @@ Lower the ratio -&gt; higher the precision.</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_261">
<property name="text">
<string>ROI ratios [left right top bottom] between 0 and 1.</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="0">
<widget class="QLineEdit" name="loopClosure_roi"/>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="reextract_maxFeatures">
<property name="maximum">
<number>999999</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_180">
<property name="text">
<string>Max features extracted from the images (0 means inf).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_visSSC">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_756">
<property name="text">
@@ -22622,6 +22614,32 @@ Lower the ratio -&gt; higher the precision.</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_237">
<property name="text">
<string>Maximum feature depth.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QSpinBox" name="reextract_gridcols">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_275">
<property name="text">
@@ -22635,6 +22653,79 @@ Lower the ratio -&gt; higher the precision.</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_438">
<property name="text">
<string>Number of rows of the grid used to extract uniformly &quot;max features / grid cells&quot; features from each cell.</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="QDoubleSpinBox" name="loopClosure_bowMinDepth">
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_visSSC">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkBox_visDepthAsMask">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="label_439">
<property name="text">
<string>Number of columns of the grid used to extract uniformly &quot;max features / grid cells&quot; features from each cell.</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="QSpinBox" name="reextract_maxFeatures">
<property name="maximum">
<number>999999</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_180">
<property name="text">
<string>Max features extracted from the images (0 means inf).</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="QComboBox" name="vis_feature_detector">
<property name="currentIndex">
@@ -22725,6 +22816,16 @@ Lower the ratio -&gt; higher the precision.</string>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_178">
<property name="text">
<string>Feature detector </string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QDoubleSpinBox" name="loopClosure_bowMaxDepth">
<property name="suffix">
@@ -22738,30 +22839,10 @@ Lower the ratio -&gt; higher the precision.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_178">
<property name="text">
<string>Feature detector </string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QDoubleSpinBox" name="loopClosure_bowMinDepth">
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLineEdit" name="loopClosure_roi"/>
</item>
<item row="9" column="0">
<widget class="QSpinBox" name="reextract_gridrows">
<property name="minimum">
<number>1</number>
@@ -22775,9 +22856,9 @@ Lower the ratio -&gt; higher the precision.</string>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_438">
<widget class="QLabel" name="label_261">
<property name="text">
<string>Number of rows of the grid used to extract uniformly &quot;max features / grid cells&quot; features from each cell.</string>
<string>ROI ratios [left right top bottom] between 0 and 1.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -22787,10 +22868,10 @@ Lower the ratio -&gt; higher the precision.</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_439">
<item row="7" column="1">
<widget class="QLabel" name="label_759">
<property name="text">
<string>Number of columns of the grid used to extract uniformly &quot;max features / grid cells&quot; features from each cell.</string>
<string>Filter floor from depth mask. 0 means disabled, negative means keeping pixels below the floor theshold instead.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -22800,36 +22881,19 @@ Lower the ratio -&gt; higher the precision.</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QSpinBox" name="reextract_gridcols">
<item row="7" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_visDepthMaskFloorThr">
<property name="suffix">
<string> m</string>
</property>
<property name="minimum">
<number>1</number>
<double>-99.000000000000000</double>
</property>
<property name="maximum">
<number>99</number>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_450">
<property name="text">
<string>Use depth image as mask when extracting features.</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">
<widget class="QCheckBox" name="checkBox_visDepthAsMask">
<property name="text">
<string/>
<double>0.000000000000000</double>
</property>
</widget>
</item>