SuperPoint Rpautrat (MIT license) (#1603)

* initial python implementation of superpoint rpautrat

* working python implementation of superpoint

* small tweaks to try and work around the GIL issue

* fix missing os import

* begging cpp impl of superpoint python model

* finishing cpp superpoint impl using the same SPDetector interface

* finalizing superpoint cpp impl, working with cpu but needs to be cleaned

* fixing feature matching by reworking nms and filtering logic

* speeding up nms with batched operations and cleaning up

* updating conversion script

* adding args for image dimensions and cuda usage to model tracer

* wiring up UI to parse superpoint params

* adding label to superpoint rpautrat ui

* reverting some unintended ui changes

* typo

* oneline revert

* removing nms and threshold filtering from cpp, this is handled internally by the superpoint model

* using python interface to run the superpoint _to_torchscript.py script at runtime

* generate and load model file on the first incoming frame

* reverting unintented change

* ui changes appear mysteriously again, reverting

* remove topk from model to prevent issue when number of detected keypoints is lower then the k value (scripting fails in this case)

* cleaning up for review

* change dest for model file and remove debug logs

* bump patch and add version comment

* rm unucessary comment

* use resources to load file to ensure it works when rtabmap isn't built from source

* execute with pybind runpy instead of system call

* only build superpoint rpautrat if we have torch and python support

* changing param to accept a path to the weights .pth file directly

* parse the default working directory to save the model file in

* rm extraneous change

* rm setters and re-initialize the detector whenever params change. We cannot support changing params after the model is constructed

* update UI text

* only enable superpoint rpautrat when built with python and torch

* more build information regarding superpoint rpautrat

* generate temporary python script in the working dir

* rm unecessary changes to rtabmap_superpoint.py

* fix comment and only add repo root to sys.path

* introduce a new parameter for the superpoint python model definition

* execute script from string instead of writing to a file

* wrap parse params in a single compiler directive

* remove descriptor spatial matching logic and rely on upstream RTAB-Map processes to take the top-K desc and kpts

* only add python script to resources if built with superpoint rpautrat support. supress warning with type casting

* isolate pybind11 setup so it can't affect any other modules or potential regnerations of the model file

* update about dialog to show superpoint rpautrat

* remove debug logs

---------

Co-authored-by: Felix Toft <[email protected]>
This commit is contained in:
Felix Toft
2025-11-07 17:22:35 -08:00
committed by GitHub
co-authored by Felix Toft
parent f44a4fc478
commit 5e4fd171e2
16 changed files with 2083 additions and 1178 deletions
+7
View File
@@ -86,6 +86,13 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_sptorch->setText("No");
_ui->label_sptorch_license->setEnabled(false);
#endif
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
_ui->label_sprpautrat->setText("Yes");
_ui->label_sprpautrat_license->setEnabled(true);
#else
_ui->label_sprpautrat->setText("No");
_ui->label_sprpautrat_license->setEnabled(false);
#endif
#ifdef RTABMAP_PYTHON
_ui->label_pymatcher->setText("Yes");
_ui->label_pymatcher_license->setEnabled(true);
+49
View File
@@ -294,6 +294,10 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->comboBox_detector_strategy->setItemData(11, 0, Qt::UserRole - 1);
_ui->vis_feature_detector->setItemData(11, 0, Qt::UserRole - 1);
#endif
#if !defined(RTABMAP_TORCH) || !defined(RTABMAP_PYTHON)
_ui->comboBox_detector_strategy->setItemData(16, 0, Qt::UserRole - 1);
_ui->vis_feature_detector->setItemData(16, 0, Qt::UserRole - 1);
#endif
#ifndef RTABMAP_PYTHON
_ui->comboBox_detector_strategy->setItemData(15, 0, Qt::UserRole - 1);
@@ -1197,6 +1201,16 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->spinBox_sptorch_minDistance->setObjectName(Parameters::kSuperPointNMSRadius().c_str());
_ui->checkBox_sptorch_cuda->setObjectName(Parameters::kSuperPointCuda().c_str());
// SuperPoint Rpautrat
_ui->lineEdit_sprpautrat_weights_path->setObjectName(Parameters::kSuperPointRpautratWeightsPath().c_str());
connect(_ui->toolButton_sprpautrat_weights_path, SIGNAL(clicked()), this, SLOT(changeSuperPointRpautratWeightsPath()));
_ui->lineEdit_sprpautrat_model_path->setObjectName(Parameters::kSuperPointRpautratModelPath().c_str());
connect(_ui->toolButton_sprpautrat_model_path, SIGNAL(clicked()), this, SLOT(changeSuperPointRpautratModelPath()));
_ui->doubleSpinBox_sprpautrat_threshold->setObjectName(Parameters::kSuperPointRpautratThreshold().c_str());
_ui->checkBox_sprpautrat_nms->setObjectName(Parameters::kSuperPointRpautratNMS().c_str());
_ui->spinBox_sprpautrat_minDistance->setObjectName(Parameters::kSuperPointRpautratNMSRadius().c_str());
_ui->checkBox_sprpautrat_cuda->setObjectName(Parameters::kSuperPointRpautratCuda().c_str());
// PyMatcher
_ui->lineEdit_pymatcher_path->setObjectName(Parameters::kPyMatcherPath().c_str());
connect(_ui->toolButton_pymatcher_path, SIGNAL(clicked()), this, SLOT(changePyMatcherPath()));
@@ -5701,6 +5715,41 @@ void PreferencesDialog::changeSuperPointModelPath()
}
}
void PreferencesDialog::changeSuperPointRpautratWeightsPath()
{
QString path;
if(_ui->lineEdit_sprpautrat_weights_path->text().isEmpty())
{
path = QFileDialog::getOpenFileName(this, tr("Select SuperPoint weights"), this->getWorkingDirectory(), tr("SuperPoint weights (*.pth)"));
}
else
{
path = QFileDialog::getOpenFileName(this, tr("Select SuperPoint weights"), _ui->lineEdit_sprpautrat_weights_path->text(), tr("SuperPoint weights (*.pth)"));
}
if(!path.isEmpty())
{
_ui->lineEdit_sprpautrat_weights_path->setText(path);
}
}
void PreferencesDialog::changeSuperPointRpautratModelPath()
{
QString path;
if(_ui->lineEdit_sprpautrat_model_path->text().isEmpty())
{
path = QFileDialog::getOpenFileName(this, tr("Select SuperPoint Python Model"), this->getWorkingDirectory(), tr("SuperPoint Python Model (*.py)"));
}
else
{
path = QFileDialog::getOpenFileName(this, tr("Select SuperPoint Python Model"), _ui->lineEdit_sprpautrat_model_path->text(), tr("SuperPoint Python Model (*.py)"));
}
if(!path.isEmpty())
{
_ui->lineEdit_sprpautrat_model_path->setText(path);
}
}
void PreferencesDialog::changePyMatcherPath()
{
QString path;
+1184 -1163
View File
File diff suppressed because it is too large Load Diff
+187
View File
@@ -11232,6 +11232,11 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<string>PyDetector</string>
</property>
</item>
<item>
<property name="text">
<string>Superpoint Rpautrat</string>
</property>
</item>
</widget>
</item>
<item row="15" column="1">
@@ -23491,6 +23496,11 @@ Lower the ratio -&gt; higher the precision.</string>
<string>PyDetector</string>
</property>
</item>
<item>
<property name="text">
<string>SuperPoint Rpautrat</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
@@ -27861,6 +27871,183 @@ Lower the ratio -&gt; higher the precision.</string>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_101">
<layout class="QVBoxLayout" name="verticalLayout_183">
<item>
<widget class="QGroupBox" name="groupBox_42">
<property name="title">
<string>SuperPoint Rpautrat</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_184">
<item>
<widget class="QLabel" name="label_131">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;SuperPoint C++ implementation from the &lt;a href=&quot;https://github.com/rpautrat/SuperPoint&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;rpautrat/SuperPoint&lt;/span&gt;&lt;/a&gt; project.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;A &amp;quot;pt&amp;quot; model file with same name than the &amp;quot;pth&amp;quot; file will be generated automatically in RTAB-Map's working directory so that the model can be called by c++ libtorch directly.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;RTAB-Map must be built with libtorch and python to use this feature.&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_139" columnstretch="0,0,1">
<item row="5" column="1">
<widget class="QCheckBox" name="checkBox_sprpautrat_cuda">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_sprpautrat_weights_path"/>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBox_sprpautrat_minDistance"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_768">
<property name="text">
<string>[Required] SuperPoint weights file (*.pth).</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="2">
<widget class="QLabel" name="label_769">
<property name="text">
<string>Detector response threshold to accept keypoint.</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="2">
<widget class="QLabel" name="label_772">
<property name="text">
<string>Use Cuda device for Torch, otherwise CPU device is used by default.</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="0">
<widget class="QToolButton" name="toolButton_sprpautrat_weights_path">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_sprpautrat_threshold">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>0.000100000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>0.001000000000000</double>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_771">
<property name="text">
<string>If true, non-maximum suppression is applied to detected keypoints.</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="QCheckBox" name="checkBox_sprpautrat_nms">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_773">
<property name="text">
<string>Minimum distance (pixels) between keypoints (when non-maximum suppression is enabled).</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="2">
<widget class="QLabel" name="label_781">
<property name="text">
<string>[Required] SuperPoint python model file (superpoint_pytorch.py).</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_sprpautrat_model_path"/>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="toolButton_sprpautrat_model_path">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_91">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>4561</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>