Added PyDetector (#677)

* Added PyDetector. Refactored PyMatcher.

* Fixed python freezing with multi-threading
This commit is contained in:
matlabbe
2021-01-17 01:56:27 -05:00
committed by GitHub
parent 0bc483b6d3
commit c49785061f
26 changed files with 733 additions and 115 deletions
+15 -14
View File
@@ -163,8 +163,9 @@ ELSE()
option(WITH_QT "Include Qt support" ON)
ENDIF()
option(WITH_ORB_OCTREE "Include ORB Octree feature support" ON)
option(WITH_SUPERPOINT_TORCH "Include SuperPoint Torch feature support" ON)
option(WITH_PYMATCHER "Include Python3 matchers support" OFF)
option(WITH_TORCH "Include Torch support (SuperPoint)" OFF)
option(WITH_PYTHON "Include Python3 support (PyMatcher, PyDetector)" OFF)
option(WITH_PYTHON_THREADING "Use more than one Python interpreter." OFF)
option(WITH_PDAL "Include PDAL support" ON)
option(WITH_FREENECT "Include Freenect support" ON)
option(WITH_FREENECT2 "Include Freenect2 support" ON)
@@ -323,19 +324,19 @@ IF(WITH_QT)
ENDIF(QT4_FOUND OR Qt5_FOUND)
ENDIF(WITH_QT)
IF(WITH_SUPERPOINT_TORCH)
IF(WITH_TORCH)
FIND_PACKAGE(Torch QUIET)
IF(TORCH_FOUND)
MESSAGE(STATUS "Found Torch: ${TORCH_INCLUDE_DIRS}")
ENDIF(TORCH_FOUND)
ENDIF(WITH_SUPERPOINT_TORCH)
ENDIF(WITH_TORCH)
IF(WITH_PYMATCHER)
IF(WITH_PYTHON)
FIND_PACKAGE(Python3 COMPONENTS Interpreter Development)
IF(Python3_FOUND)
MESSAGE(STATUS "Found Python3")
ENDIF(Python3_FOUND)
ENDIF(WITH_PYMATCHER)
ENDIF(WITH_PYTHON)
IF(WITH_PDAL)
FIND_PACKAGE(PDAL QUIET)
@@ -857,10 +858,10 @@ IF(NOT WITH_ORB_OCTREE)
SET(ORB_OCTREE "//")
ENDIF()
IF(NOT TORCH_FOUND)
SET(SUPERPOINT_TORCH "//")
SET(TORCH "//")
ENDIF()
IF(NOT Python3_FOUND)
SET(PYMATCHER "//")
SET(PYTHON "//")
ENDIF()
IF(ADD_VTK_GUI_SUPPORT_QT_TO_CONF)
SET(CONF_VTK_QT true)
@@ -1115,18 +1116,18 @@ ENDIF()
IF(TORCH_FOUND)
MESSAGE(STATUS " With SupertPoint = YES (License: GPLv3) libtorch=${Torch_VERSION}")
ELSEIF(NOT WITH_SUPERPOINT_TORCH)
MESSAGE(STATUS " With SupertPoint = NO (WITH_SUPERPOINT_TORCH=OFF)")
ELSEIF(NOT WITH_TORCH)
MESSAGE(STATUS " With SupertPoint = NO (WITH_TORCH=OFF)")
ELSE()
MESSAGE(STATUS " With SupertPoint = NO (libtorch not found)")
ENDIF()
IF(Python3_FOUND)
MESSAGE(STATUS " With Python3 = YES (License: PSF)")
ELSEIF(NOT WITH_PYMATCHER)
MESSAGE(STATUS " With Python3 = NO (WITH_PYMATCHER=OFF)")
MESSAGE(STATUS " With Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} = YES (License: PSF)")
ELSEIF(NOT WITH_PYTHON)
MESSAGE(STATUS " With Python3 = NO (WITH_PYTHON=OFF)")
ELSE()
MESSAGE(STATUS " With Python3 = NO (python3 not found)")
MESSAGE(STATUS " With Python3 = NO (python not found)")
ENDIF()
IF(WITH_MADGWICK)
+2 -2
View File
@@ -73,8 +73,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@VINS@#define RTABMAP_VINS
@ORB_SLAM2@#define RTABMAP_ORB_SLAM2
@ORB_OCTREE@#define RTABMAP_ORB_OCTREE
@SUPERPOINT_TORCH@#define RTABMAP_SUPERPOINT_TORCH
@PYMATCHER@#define RTABMAP_PYMATCHER
@TORCH@#define RTABMAP_TORCH
@PYTHON@#define RTABMAP_PYTHON
@MADGWICK@#define RTABMAP_MADGWICK
#include <pcl/pcl_config.h>
+8
View File
@@ -37,6 +37,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/utilite/UConversion.h"
#include "ObjDeletionHandler.h"
#ifdef RTABMAP_PYTHON
#include "rtabmap/core/PythonInterface.h"
#endif
using namespace rtabmap;
int main(int argc, char* argv[])
@@ -45,6 +49,10 @@ int main(int argc, char* argv[])
ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kWarning);
#ifdef RTABMAP_PYTHON
PythonInterface python; // Make sure we initialize python in main thread
#endif
/* Create tasks */
QApplication * app = new QApplication(argc, argv);
app->setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }"); // selectable message box
+2 -1
View File
@@ -120,7 +120,8 @@ public:
kFeatureSuperPointTorch=11, //new 0.19.7
kFeatureSurfFreak=12, //new 0.20.4
kFeatureGfttDaisy=13, //new 0.20.6
kFeatureSurfDaisy=14}; //new 0.20.6
kFeatureSurfDaisy=14, //new 0.20.6
kFeaturePyDetector=15}; //new 0.20.8
static std::string typeName(Type type)
{
+8 -5
View File
@@ -244,9 +244,9 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(Kp, NndrRatio, float, 0.8, "NNDR ratio (A matching pair is detected, if its distance is closer than X times the distance of the second nearest neighbor.)");
#if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D)
// OpenCV>2 without xFeatures2D module doesn't have BRIEF
RTABMAP_PARAM(Kp, DetectorStrategy, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY");
RTABMAP_PARAM(Kp, DetectorStrategy, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
#else
RTABMAP_PARAM(Kp, DetectorStrategy, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY");
RTABMAP_PARAM(Kp, DetectorStrategy, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
#endif
RTABMAP_PARAM(Kp, TfIdfLikelihoodUsed, bool, true, "Use of the td-idf strategy to compute the likelihood.");
RTABMAP_PARAM(Kp, Parallelized, bool, true, "If the dictionary update and signature creation were parallelized.");
@@ -332,6 +332,9 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(SuperPoint, NMSRadius, int, 4, uFormat("[%s=true] Minimum distance (pixels) between keypoints.", kSuperPointNMS().c_str()));
RTABMAP_PARAM(SuperPoint, Cuda, bool, true, "Use Cuda device for Torch, otherwise CPU device is used by default.");
RTABMAP_PARAM_STR(PyDetector, Path, "", "Path to python script file (see available ones in rtabmap/corelib/src/python/*). See the header to see where the script should be copied.");
RTABMAP_PARAM(PyDetector, Cuda, bool, true, "Use cuda.");
// BayesFilter
RTABMAP_PARAM(Bayes, VirtualPlacePriorThr, float, 0.9, "Virtual place prior");
RTABMAP_PARAM_STR(Bayes, PredictionLC, "0.1 0.36 0.30 0.16 0.062 0.0151 0.00255 0.000324 2.5e-05 1.3e-06 4.8e-08 1.2e-09 1.9e-11 2.2e-13 1.7e-15 8.5e-18 2.9e-20 6.9e-23", "Prediction of loop closures (Gaussian-like, here with sigma=1.6) - Format: {VirtualPlaceProb, LoopClosureProb, NeighborLvl1, NeighborLvl2, ...}.");
@@ -592,9 +595,9 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(Vis, Iterations, int, 300, "Maximum iterations to compute the transform.");
#if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D)
// OpenCV>2 without xFeatures2D module doesn't have BRIEF
RTABMAP_PARAM(Vis, FeatureType, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY");
RTABMAP_PARAM(Vis, FeatureType, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
#else
RTABMAP_PARAM(Vis, FeatureType, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY");
RTABMAP_PARAM(Vis, FeatureType, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
#endif
RTABMAP_PARAM(Vis, MaxFeatures, int, 1000, "0 no limits.");
RTABMAP_PARAM(Vis, MaxDepth, float, 0, "Max depth of the features (0 means no limit).");
@@ -622,7 +625,7 @@ class RTABMAP_EXP Parameters
#endif
// Features matching approaches
RTABMAP_PARAM_STR(PyMatcher, Path, "", "Path to python script file (see available ones in rtabmap/corelib/src/pymatcher/*). See the header to see where the script should be copied.");
RTABMAP_PARAM_STR(PyMatcher, Path, "", "Path to python script file (see available ones in rtabmap/corelib/src/python/*). See the header to see where the script should be copied.");
RTABMAP_PARAM(PyMatcher, Iterations, int, 20, "Sinkhorn iterations. Used by SuperGlue.");
RTABMAP_PARAM(PyMatcher, Threshold, float, 0.2, "Used by SuperGlue.");
RTABMAP_PARAM(PyMatcher, Cuda, bool, true, "Used by SuperGlue.");
@@ -0,0 +1,41 @@
/*
* PythonInterface.h
*
* Created on: Jan. 14, 2021
* Author: mathieu
*/
#ifndef CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_
#define CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_
#include <string>
#include <rtabmap/utilite/UMutex.h>
#include <Python.h>
namespace rtabmap {
class PythonInterface
{
public:
PythonInterface();
virtual ~PythonInterface();
protected:
std::string getTraceback(); // should be called between lock() and unlock()
void lock();
void unlock();
private:
static UMutex mutex_;
static int refCount_;
protected:
static PyThreadState * mainThreadState_;
static unsigned long mainThreadID_;
PyThreadState * threadState_;
};
}
#endif /* CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_ */
@@ -37,7 +37,7 @@ namespace rtabmap {
class Feature2D;
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
class PyMatcher;
#endif
@@ -105,7 +105,7 @@ private:
Feature2D * _detectorFrom;
Feature2D * _detectorTo;
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
PyMatcher * _pyMatcher;
#endif
};
+6 -4
View File
@@ -195,11 +195,13 @@ IF(Python3_FOUND)
)
SET(SRC_FILES
${SRC_FILES}
pymatcher/PyMatcher.cpp
python/PythonInterface.cpp
python/PyMatcher.cpp
python/PyDetector.cpp
)
SET(INCLUDE_DIRS
${TORCH_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/pymatcher
${CMAKE_CURRENT_SOURCE_DIR}/python
${INCLUDE_DIRS}
)
ENDIF(Python3_FOUND)
@@ -627,8 +629,8 @@ foreach(arg ${RESOURCES})
set(RESOURCES_HEADERS "${RESOURCES_HEADERS}" "${CMAKE_CURRENT_BINARY_DIR}/${output}.h")
endforeach(arg ${RESOURCES})
MESSAGE(STATUS "RESOURCES = ${RESOURCES}")
MESSAGE(STATUS "RESOURCES_HEADERS = ${RESOURCES_HEADERS}")
#MESSAGE(STATUS "RESOURCES = ${RESOURCES}")
#MESSAGE(STATUS "RESOURCES_HEADERS = ${RESOURCES_HEADERS}")
IF(ANDROID)
+19 -10
View File
@@ -44,10 +44,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "opencv/ORBextractor.h"
#endif
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
#include "superpoint_torch/SuperPoint.h"
#endif
#ifdef RTABMAP_PYTHON
#include "python/PyDetector.h"
#endif
#if CV_MAJOR_VERSION < 3
#include "opencv/Orb.h"
#ifdef HAVE_OPENCV_GPU
@@ -584,7 +588,7 @@ Feature2D * Feature2D::create(Feature2D::Type type, const ParametersMap & parame
}
#endif
#ifndef RTABMAP_SUPERPOINT_TORCH
#ifndef RTABMAP_TORCH
if(type == Feature2D::kFeatureSuperPointTorch)
{
UWARN("SupertPoint Torch feature cannot be used as RTAB-Map is not built with the option enabled. GFTT/ORB is used instead.");
@@ -628,7 +632,7 @@ Feature2D * Feature2D::create(Feature2D::Type type, const ParametersMap & parame
case Feature2D::kFeatureOrbOctree:
feature2D = new ORBOctree(parameters);
break;
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
case Feature2D::kFeatureSuperPointTorch:
feature2D = new SuperPointTorch(parameters);
break;
@@ -642,6 +646,11 @@ Feature2D * Feature2D::create(Feature2D::Type type, const ParametersMap & parame
case Feature2D::kFeatureSurfDaisy:
feature2D = new SURF_DAISY(parameters);
break;
#ifdef RTABMAP_PYTHON
case Feature2D::kFeaturePyDetector:
feature2D = new PyDetector(parameters);
break;
#endif
#ifdef RTABMAP_NONFREE
default:
feature2D = new SURF(parameters);
@@ -2051,7 +2060,7 @@ void SuperPointTorch::parseParameters(const ParametersMap & parameters)
Feature2D::parseParameters(parameters);
std::string previousPath = path_;
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
bool previousCuda = cuda_;
#endif
Parameters::parse(parameters, Parameters::kSuperPointModelPath(), path_);
@@ -2060,7 +2069,7 @@ void SuperPointTorch::parseParameters(const ParametersMap & parameters)
Parameters::parse(parameters, Parameters::kSuperPointNMSRadius(), minDistance_);
Parameters::parse(parameters, Parameters::kSuperPointCuda(), cuda_);
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
if(superPoint_.get() == 0 || path_.compare(previousPath) != 0 || previousCuda != cuda_)
{
superPoint_ = cv::Ptr<SPDetector>(new SPDetector(path_, threshold_, nms_, minDistance_, cuda_));
@@ -2072,29 +2081,29 @@ void SuperPointTorch::parseParameters(const ParametersMap & parameters)
superPoint_->setMinDistance(minDistance_);
}
#else
UWARN("RTAB-Map is not built with SuperPoint Torch support so SuperPoint Torch feature cannot be used!");
UWARN("RTAB-Map is not built with Torch support so SuperPoint Torch feature cannot be used!");
#endif
}
std::vector<cv::KeyPoint> SuperPointTorch::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
{
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
UASSERT_MSG(roi.x==0 && roi.y ==0, "Not supporting ROI");
return superPoint_->detect(image, mask);
#else
UWARN("RTAB-Map is not built with SuperPoint Torch support so SuperPoint Torch feature cannot be used!");
UWARN("RTAB-Map is not built with Torch support so SuperPoint Torch feature cannot be used!");
return std::vector<cv::KeyPoint>();
#endif
}
cv::Mat SuperPointTorch::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
{
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
return superPoint_->compute(keypoints);
#else
UWARN("RTAB-Map is not built with SuperPoint Torch support so SuperPoint Torch feature cannot be used!");
UWARN("RTAB-Map is not built with Torch support so SuperPoint Torch feature cannot be used!");
return cv::Mat();
#endif
}
+4 -3
View File
@@ -167,7 +167,8 @@ bool Parameters::isFeatureParameter(const std::string & parameter)
group.compare("GFTT") == 0 ||
group.compare("BRISK") == 0 ||
group.compare("KAZE") == 0 ||
group.compare("SuperPoint") == 0;
group.compare("SuperPoint") == 0 ||
group.compare("PyDetector") == 0;
}
rtabmap::ParametersMap Parameters::getDefaultOdometryParameters(bool stereo, bool vis, bool icp)
@@ -625,13 +626,13 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif
str = "With SuperPoint Torch:";
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif
str = "With Python3:";
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
+7 -7
View File
@@ -51,8 +51,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtflann/flann.hpp>
#ifdef RTABMAP_PYMATCHER
#include <pymatcher/PyMatcher.h>
#ifdef RTABMAP_PYTHON
#include "python/PyMatcher.h"
#endif
namespace rtabmap {
@@ -87,7 +87,7 @@ RegistrationVis::RegistrationVis(const ParametersMap & parameters, Registration
_maxInliersMeanDistance(Parameters::defaultVisMeanInliersDistance()),
_detectorFrom(0),
_detectorTo(0)
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
,
_pyMatcher(0)
#endif
@@ -153,7 +153,7 @@ void RegistrationVis::parseParameters(const ParametersMap & parameters)
if(_nnType == 6)
{
// verify that we have Python3 support
#ifndef RTABMAP_PYMATCHER
#ifndef RTABMAP_PYTHON
UWARN("%s is set to 6 but RTAB-Map is not built with Python3 support, using default %d.",
Parameters::kVisCorNNType().c_str(), Parameters::defaultVisCorNNType());
_nnType = Parameters::defaultVisCorNNType();
@@ -268,7 +268,7 @@ RegistrationVis::~RegistrationVis()
{
delete _detectorFrom;
delete _detectorTo;
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
delete _pyMatcher;
#endif
}
@@ -1152,7 +1152,7 @@ Transform RegistrationVis::computeTransformationImpl(
// match between all descriptors
std::list<int> fromWordIds;
std::list<int> toWordIds;
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
if(_nnType == 5 || (_nnType == 6 && _pyMatcher) || _nnType==7)
#else
if(_nnType == 5 || _nnType == 7) // bruteforce cross check or GMS
@@ -1173,7 +1173,7 @@ Transform RegistrationVis::computeTransformationImpl(
{
std::vector<int> toWordIdsV(descriptorsTo.rows, 0);
std::vector<cv::DMatch> matches;
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
if(_nnType == 6 && _pyMatcher &&
descriptorsTo.cols == descriptorsFrom.cols &&
descriptorsTo.rows == (int)kptsTo.size() &&
+234
View File
@@ -0,0 +1,234 @@
/**
* Python interface for SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
*/
#include "PyDetector.h"
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UFile.h>
#include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/UConversion.h>
#include <rtabmap/utilite/UTimer.h>
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#include <numpy/arrayobject.h>
namespace rtabmap
{
PyDetector::PyDetector(const ParametersMap & parameters) :
pModule_(0),
pFunc_(0),
path_(Parameters::defaultPyDetectorPath()),
cuda_(Parameters::defaultPyDetectorCuda())
{
this->parseParameters(parameters);
UDEBUG("path = %s", path_.c_str());
if(!UFile::exists(path_) || UFile::getExtension(path_).compare("py") != 0)
{
UERROR("Cannot initialize Python detector, the path is not valid: \"%s\"=\"%s\"",
Parameters::kPyDetectorPath().c_str(), path_.c_str());
return;
}
lock();
std::string matcherPythonDir = UDirectory::getDir(path_);
if(!matcherPythonDir.empty())
{
PyRun_SimpleString("import sys");
PyRun_SimpleString(uFormat("sys.path.append(\"%s\")", matcherPythonDir.c_str()).c_str());
}
_import_array();
std::string scriptName = uSplit(UFile::getName(path_), '.').front();
PyObject * pName = PyUnicode_FromString(scriptName.c_str());
UDEBUG("PyImport_Import() beg");
pModule_ = PyImport_Import(pName);
UDEBUG("PyImport_Import() end");
Py_DECREF(pName);
if(!pModule_)
{
UERROR("Module \"%s\" could not be imported! (File=\"%s\")", scriptName.c_str(), path_.c_str());
UERROR("%s", getTraceback().c_str());
}
unlock();
}
PyDetector::~PyDetector()
{
lock();
if(pFunc_)
{
Py_DECREF(pFunc_);
}
if(pModule_)
{
Py_DECREF(pModule_);
}
unlock();
}
void PyDetector::parseParameters(const ParametersMap & parameters)
{
Feature2D::parseParameters(parameters);
Parameters::parse(parameters, Parameters::kPyDetectorPath(), path_);
Parameters::parse(parameters, Parameters::kPyDetectorCuda(), cuda_);
path_ = uReplaceChar(path_, '~', UDirectory::homeDir());
}
std::vector<cv::KeyPoint> PyDetector::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
{
UDEBUG("");
descriptors_ = cv::Mat();
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
std::vector<cv::KeyPoint> keypoints;
cv::Mat imgRoi(image, roi);
UTimer timer;
if(!pModule_)
{
UERROR("Python detector module not loaded!");
return keypoints;
}
lock();
if(!pFunc_)
{
PyObject * pFunc = PyObject_GetAttrString(pModule_, "init");
if(pFunc)
{
if(PyCallable_Check(pFunc))
{
PyObject * result = PyObject_CallFunction(pFunc, "i", cuda_?1:0);
if(result == NULL)
{
UERROR("Call to \"init(...)\" in \"%s\" failed!", path_.c_str());
UERROR("%s", getTraceback().c_str());
return keypoints;
}
Py_DECREF(result);
pFunc_ = PyObject_GetAttrString(pModule_, "detect");
if(pFunc_ && PyCallable_Check(pFunc_))
{
// we are ready!
}
else
{
UERROR("Cannot find method \"detect(...)\" in %s", path_.c_str());
UERROR("%s", getTraceback().c_str());
if(pFunc_)
{
Py_DECREF(pFunc_);
pFunc_ = 0;
}
return keypoints;
}
}
else
{
UERROR("Cannot call method \"init(...)\" in %s", path_.c_str());
UERROR("%s", getTraceback().c_str());
return keypoints;
}
Py_DECREF(pFunc);
}
else
{
UERROR("Cannot find method \"init(...)\"");
UERROR("%s", getTraceback().c_str());
return keypoints;
}
UDEBUG("init time = %fs", timer.ticks());
}
if(pFunc_)
{
npy_intp dims[2] = {imgRoi.rows, imgRoi.cols};
PyObject* pImageBuffer = PyArray_SimpleNewFromData(2, dims, NPY_UBYTE, (void*)imgRoi.data);
UASSERT(pImageBuffer);
UDEBUG("Preparing data time = %fs", timer.ticks());
PyObject *pReturn = PyObject_CallFunctionObjArgs(pFunc_, pImageBuffer, NULL);
if(pReturn == NULL)
{
UERROR("Failed to call match() function!");
UERROR("%s", getTraceback().c_str());
}
else
{
UDEBUG("Python detector time = %fs", timer.ticks());
if (PyTuple_Check(pReturn) && PyTuple_GET_SIZE(pReturn) == 2)
{
PyObject *kptsPtr = PyTuple_GET_ITEM(pReturn, 0);
PyObject *descPtr = PyTuple_GET_ITEM(pReturn, 1);
if(PyArray_Check(kptsPtr) && PyArray_Check(descPtr))
{
PyArrayObject *arrayPtr = reinterpret_cast<PyArrayObject*>(kptsPtr);
int nKpts = PyArray_SHAPE(arrayPtr)[0];
int kptSize = PyArray_SHAPE(arrayPtr)[1];
int type = PyArray_TYPE(arrayPtr);
UDEBUG("Kpts array %dx%d (type=%d)", nKpts, kptSize, type);
UASSERT(kptSize == 3);
UASSERT_MSG(type == NPY_FLOAT, uFormat("Returned matches should type FLOAT=11, received type=%d", type).c_str());
float* c_out = reinterpret_cast<float*>(PyArray_DATA(arrayPtr));
keypoints.reserve(nKpts);
for (int i = 0; i < nKpts*kptSize; i+=kptSize)
{
cv::KeyPoint kpt(c_out[i], c_out[i+1], 8, -1, c_out[i+2]);
keypoints.push_back(kpt);
}
arrayPtr = reinterpret_cast<PyArrayObject*>(descPtr);
int nDesc = PyArray_SHAPE(arrayPtr)[0];
UASSERT(nDesc = nKpts);
int dim = PyArray_SHAPE(arrayPtr)[1];
type = PyArray_TYPE(arrayPtr);
UDEBUG("Desc array %dx%d (type=%d)", nDesc, dim, type);
UASSERT_MSG(type == NPY_FLOAT, uFormat("Returned matches should type FLOAT=11, received type=%d", type).c_str());
c_out = reinterpret_cast<float*>(PyArray_DATA(arrayPtr));
for (int i = 0; i < nDesc*dim; i+=dim)
{
cv::Mat descriptor = cv::Mat(1, dim, CV_32FC1, &c_out[i]).clone();
descriptors_.push_back(descriptor);
}
}
}
else
{
UWARN("Expected tuple (Kpts 3 x N, Descriptors dim x N), returning empty features.");
}
Py_DECREF(pReturn);
}
Py_DECREF(pImageBuffer);
}
unlock();
return keypoints;
}
cv::Mat PyDetector::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
{
UASSERT((int)keypoints.size() == descriptors_.rows);
return descriptors_;
}
}
+44
View File
@@ -0,0 +1,44 @@
/**
* Python interface for python matchers like:
* - SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
* - OANET https://github.com/zjhthu/OANet
*/
#ifndef PYMATCHER_H
#define PYMATCHER_H
#include <rtabmap/core/Features2d.h>
#include <opencv2/core/types.hpp>
#include <opencv2/core/mat.hpp>
#include <vector>
#include "rtabmap/core/PythonInterface.h"
#include <Python.h>
namespace rtabmap
{
class PyDetector : public Feature2D, public PythonInterface
{
public:
PyDetector(const ParametersMap & parameters = ParametersMap());
virtual ~PyDetector();
virtual void parseParameters(const ParametersMap & parameters);
virtual Feature2D::Type getType() const {return kFeaturePyDetector;}
private:
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat());
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
private:
PyObject * pModule_;
PyObject * pFunc_;
std::string path_;
bool cuda_;
cv::Mat descriptors_;
};
}
#endif
@@ -2,7 +2,7 @@
* Python interface for SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
*/
#include <pymatcher/PyMatcher.h>
#include <python/PyMatcher.h>
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UFile.h>
@@ -16,52 +16,6 @@
namespace rtabmap
{
class PythonSingleTon
{
public:
PythonSingleTon() : initialized_(false) {}
void init() {UScopeMutex lock(mutex_); if(!initialized_)Py_Initialize(); initialized_=true;}
bool initialized() const {return initialized_;}
virtual ~PythonSingleTon() {if(initialized_) Py_Finalize();}
private:
bool initialized_;
UMutex mutex_;
};
static PythonSingleTon g_python;
std::string getTraceback()
{
// Author: https://stackoverflow.com/questions/41268061/c-c-python-exception-traceback-not-being-generated
PyObject* type;
PyObject* value;
PyObject* traceback;
PyErr_Fetch(&type, &value, &traceback);
PyErr_NormalizeException(&type, &value, &traceback);
std::string fcn = "";
fcn += "def get_pretty_traceback(exc_type, exc_value, exc_tb):\n";
fcn += " import sys, traceback\n";
fcn += " lines = []\n";
fcn += " lines = traceback.format_exception(exc_type, exc_value, exc_tb)\n";
fcn += " output = '\\n'.join(lines)\n";
fcn += " return output\n";
PyRun_SimpleString(fcn.c_str());
PyObject* mod = PyImport_ImportModule("__main__");
PyObject* method = PyObject_GetAttrString(mod, "get_pretty_traceback");
PyObject* outStr = PyObject_CallObject(method, Py_BuildValue("OOO", type, value, traceback));
std::string pretty = PyBytes_AsString(PyUnicode_AsASCIIString(outStr));
Py_DECREF(method);
Py_DECREF(outStr);
Py_DECREF(mod);
return pretty;
}
PyMatcher::PyMatcher(
const std::string & pythonMatcherPath,
float matchThreshold,
@@ -85,10 +39,7 @@ PyMatcher::PyMatcher(
return;
}
if(!g_python.initialized())
{
g_python.init();
}
lock();
std::string matcherPythonDir = UDirectory::getDir(path_);
if(!matcherPythonDir.empty())
@@ -101,6 +52,7 @@ PyMatcher::PyMatcher(
std::string scriptName = uSplit(UFile::getName(path_), '.').front();
PyObject * pName = PyUnicode_FromString(scriptName.c_str());
UDEBUG("PyImport_Import");
pModule_ = PyImport_Import(pName);
Py_DECREF(pName);
@@ -109,10 +61,13 @@ PyMatcher::PyMatcher(
UERROR("Module \"%s\" could not be imported! (File=\"%s\")", scriptName.c_str(), path_.c_str());
UERROR("%s", getTraceback().c_str());
}
unlock();
}
PyMatcher::~PyMatcher()
{
lock();
if(pFunc_)
{
Py_DECREF(pFunc_);
@@ -121,6 +76,7 @@ PyMatcher::~PyMatcher()
{
Py_DECREF(pModule_);
}
unlock();
}
std::vector<cv::DMatch> PyMatcher::match(
@@ -148,6 +104,8 @@ std::vector<cv::DMatch> PyMatcher::match(
imageSize.width>0 && imageSize.height>0)
{
lock();
UDEBUG("matchThreshold=%f, iterations=%d, cuda=%d", matchThreshold_, iterations_, cuda_?1:0);
if(!pFunc_)
@@ -302,6 +260,7 @@ std::vector<cv::DMatch> PyMatcher::match(
UDEBUG("Fill matches (%d/%d) and cleanup time = %fs", matches.size(), std::min(descriptorsQuery.rows, descriptorsTrain.rows), timer.ticks());
}
unlock();
}
else
{
@@ -9,14 +9,14 @@
#include <opencv2/core/types.hpp>
#include <opencv2/core/mat.hpp>
#include "rtabmap/core/PythonInterface.h"
#include <vector>
#include <Python.h>
namespace rtabmap
{
class PyMatcher
class PyMatcher : public PythonInterface
{
public:
PyMatcher(const std::string & pythonMatcherPath,
+119
View File
@@ -0,0 +1,119 @@
/*
* PythonSingleTon.h
*
* Created on: Jan. 14, 2021
* Author: mathieu
*/
#include <rtabmap/core/PythonInterface.h>
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UThread.h>
namespace rtabmap {
UMutex PythonInterface::mutex_;
int PythonInterface::refCount_ = 0;
PyThreadState * PythonInterface::mainThreadState_ = 0;
unsigned long PythonInterface::mainThreadID_ = 0;
PythonInterface::PythonInterface() :
threadState_(0)
{
UScopeMutex lockM(mutex_);
if(refCount_ == 0)
{
// initialize Python
Py_Initialize();
// initialize thread support
PyEval_InitThreads();
Py_DECREF(PyImport_ImportModule("threading"));
//release the GIL, store thread state, set the current thread state to NULL
mainThreadState_ = PyEval_SaveThread();
UASSERT(mainThreadState_);
mainThreadID_ = UThread::currentThreadId();
}
++refCount_;
}
PythonInterface::~PythonInterface()
{
UScopeMutex lock(mutex_);
if(refCount_>0 && --refCount_==0)
{
// shut down the interpreter
PyEval_RestoreThread(mainThreadState_);
Py_Finalize();
}
}
void PythonInterface::lock()
{
mutex_.lock();
if(UThread::currentThreadId() == mainThreadID_)
{
PyEval_RestoreThread(mainThreadState_);
}
else
{
// create a thread state object for this thread
threadState_ = PyThreadState_New(mainThreadState_->interp);
UASSERT(threadState_);
PyEval_RestoreThread(threadState_);
}
}
void PythonInterface::unlock()
{
if(UThread::currentThreadId() == mainThreadID_)
{
mainThreadState_ = PyEval_SaveThread();
}
else
{
PyThreadState_Clear(threadState_);
PyThreadState_DeleteCurrent();
}
mutex_.unlock();
}
std::string PythonInterface::getTraceback()
{
// Author: https://stackoverflow.com/questions/41268061/c-c-python-exception-traceback-not-being-generated
PyObject* type;
PyObject* value;
PyObject* traceback;
PyErr_Fetch(&type, &value, &traceback);
PyErr_NormalizeException(&type, &value, &traceback);
std::string fcn = "";
fcn += "def get_pretty_traceback(exc_type, exc_value, exc_tb):\n";
fcn += " import sys, traceback\n";
fcn += " lines = []\n";
fcn += " lines = traceback.format_exception(exc_type, exc_value, exc_tb)\n";
fcn += " output = '\\n'.join(lines)\n";
fcn += " return output\n";
PyRun_SimpleString(fcn.c_str());
PyObject* mod = PyImport_ImportModule("__main__");
UASSERT(mod);
PyObject* method = PyObject_GetAttrString(mod, "get_pretty_traceback");
UASSERT(method);
PyObject* outStr = PyObject_CallObject(method, Py_BuildValue("OOO", type, value, traceback));
std::string pretty;
if(outStr)
pretty = PyBytes_AsString(PyUnicode_AsASCIIString(outStr));
Py_DECREF(method);
Py_DECREF(outStr);
Py_DECREF(mod);
return pretty;
}
}
+56
View File
@@ -0,0 +1,56 @@
#! /usr/bin/env python3
#
# Drop this file in the root folder of SuperPoint git: https://github.com/magicleap/SuperPointPretrainedNetwork
# To use with rtabmap:
# --Vis/FeatureType 15 --PyDetector/Path "~/SuperPointPretrainedNetwork/rtabmap_superpoint.py" --PyDetector/Model "~/SuperPointPretrainedNetwork/superpoint_v1.pth"
#
import random
import numpy as np
import torch
#import sys
#import os
#print(os.sys.path)
#print(sys.version)
from demo_superpoint import SuperPointFrontend
torch.set_grad_enabled(False)
device = 'cpu'
superpoint = []
def init(cuda):
#print("SuperPoint python init()")
global device
device = 'cuda' if torch.cuda.is_available() and cuda else 'cpu'
# This class runs the SuperPoint network and processes its outputs.
global superpoint
superpoint = SuperPointFrontend(weights_path="superpoint_v1.pth",
nms_dist=4,
conf_thresh=0.015,
nn_thresh=1,
cuda=cuda)
def detect(imageBuffer):
#print("SuperPoint python detect()")
global device
image = np.asarray(imageBuffer)
image = (image.astype('float32') / 255.)
global superpoint
pts, desc, heatmap = superpoint.run(image)
# return float: Kpts:Nx3, Desc:NxDim
# use copy to make sure memory is correctly re-ordered
pts = np.float32(np.transpose(pts)).copy()
desc = np.float32(np.transpose(desc)).copy()
return pts, desc
if __name__ == '__main__':
#test
init(True)
detect(np.random.rand(640,480)*255)
+3 -3
View File
@@ -168,9 +168,9 @@ std::vector<cv::KeyPoint> SPDetector::detect(const cv::Mat &img, const cv::Mat &
auto kpts = (prob_ > threshold_);
kpts = torch::nonzero(kpts); // [n_keypoints, 2] (y, x)
//convert back to cpu if in gpu
auto kpts_cpu = kpts.to(torch::kCPU);
auto prob_cpu = prob_.to(torch::kCPU);
//convert back to cpu if in gpu
auto kpts_cpu = kpts.to(torch::kCPU);
auto prob_cpu = prob_.to(torch::kCPU);
std::vector<cv::KeyPoint> keypoints_no_nms;
for (int i = 0; i < kpts_cpu.size(0); i++) {
+8
View File
@@ -40,6 +40,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <pcl/io/ply_io.h>
#include <pcl/filters/filter.h>
#ifdef RTABMAP_PYTHON
#include "rtabmap/core/PythonInterface.h"
#endif
#include "MapBuilder.h"
void showUsage()
@@ -56,6 +60,10 @@ int main(int argc, char * argv[])
ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kWarning);
#ifdef RTABMAP_PYTHON
PythonInterface python; // Make sure we initialize python in main thread
#endif
int driver = 0;
if(argc < 2)
{
+8
View File
@@ -36,6 +36,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QApplication>
#include <stdio.h>
#ifdef RTABMAP_PYTHON
#include "rtabmap/core/PythonInterface.h"
#endif
#include "MapBuilderWifi.h"
#include "WifiThread.h"
@@ -57,6 +61,10 @@ int main(int argc, char * argv[])
ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kWarning);
#ifdef RTABMAP_PYTHON
PythonInterface python; // Make sure we initialize python in main thread
#endif
std::string interfaceName = "wlan0";
int driver = 0;
bool mirroring = false;
@@ -342,6 +342,7 @@ private Q_SLOTS:
void changeSuperPointModelPath();
void changePyMatcherPath();
void changePyMatcherModel();
void changePyDetectorPath();
void readSettingsEnd();
void setupTreeView();
void updateBasicParameter();
+2 -2
View File
@@ -79,14 +79,14 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_orboctree->setText("No");
_ui->label_orboctree_license->setEnabled(false);
#endif
#ifdef RTABMAP_SUPERPOINT_TORCH
#ifdef RTABMAP_TORCH
_ui->label_sptorch->setText("Yes");
_ui->label_sptorch_license->setEnabled(true);
#else
_ui->label_sptorch->setText("No");
_ui->label_sptorch_license->setEnabled(false);
#endif
#ifdef RTABMAP_PYMATCHER
#ifdef RTABMAP_PYTHON
_ui->label_pymatcher->setText("Yes");
_ui->label_pymatcher_license->setEnabled(true);
#else
+29 -5
View File
@@ -231,12 +231,14 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->vis_feature_detector->setItemData(10, 0, Qt::UserRole - 1);
#endif
#ifndef RTABMAP_SUPERPOINT_TORCH
#ifndef RTABMAP_TORCH
_ui->comboBox_detector_strategy->setItemData(11, 0, Qt::UserRole - 1);
_ui->vis_feature_detector->setItemData(11, 0, Qt::UserRole - 1);
#endif
#ifndef RTABMAP_PYMATCHER
#ifndef RTABMAP_PYTHON
_ui->comboBox_detector_strategy->setItemData(15, 0, Qt::UserRole - 1);
_ui->vis_feature_detector->setItemData(15, 0, Qt::UserRole - 1);
_ui->reextract_nn->setItemData(6, 0, Qt::UserRole - 1);
#endif
@@ -1009,7 +1011,12 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->pymatcher_iterations->setObjectName(Parameters::kPyMatcherIterations().c_str());
_ui->checkBox_pymatcher_cuda->setObjectName(Parameters::kPyMatcherCuda().c_str());
_ui->lineEdit_pymatcher_model->setObjectName(Parameters::kPyMatcherModel().c_str());
connect(_ui->toolButton_pymatcher_model, SIGNAL(clicked()), this, SLOT(changePyMatcherPath()));
connect(_ui->toolButton_pymatcher_model, SIGNAL(clicked()), this, SLOT(changePyMatcherModel()));
// PyDetector
_ui->lineEdit_pydetector_path->setObjectName(Parameters::kPyDetectorPath().c_str());
connect(_ui->toolButton_pydetector_path, SIGNAL(clicked()), this, SLOT(changePyDetectorPath()));
_ui->checkBox_pydetector_cuda->setObjectName(Parameters::kPyDetectorCuda().c_str());
// GMS
_ui->checkBox_gms_withRotation->setObjectName(Parameters::kGMSWithRotation().c_str());
@@ -4922,11 +4929,11 @@ void PreferencesDialog::changePyMatcherModel()
QString path;
if(_ui->lineEdit_pymatcher_model->text().isEmpty())
{
path = QFileDialog::getOpenFileName(this, tr("Select file"), this->getWorkingDirectory(), tr("PyTorch model (*.pth, *.pt)"));
path = QFileDialog::getOpenFileName(this, tr("Select file"), this->getWorkingDirectory(), tr("PyTorch model (*.pth *.pt)"));
}
else
{
path = QFileDialog::getOpenFileName(this, tr("Select file"), _ui->lineEdit_pymatcher_model->text(), tr("PyTorch model (*.pth, *.pt)"));
path = QFileDialog::getOpenFileName(this, tr("Select file"), _ui->lineEdit_pymatcher_model->text(), tr("PyTorch model (*.pth *.pt)"));
}
if(!path.isEmpty())
{
@@ -4934,6 +4941,23 @@ void PreferencesDialog::changePyMatcherModel()
}
}
void PreferencesDialog::changePyDetectorPath()
{
QString path;
if(_ui->lineEdit_pydetector_path->text().isEmpty())
{
path = QFileDialog::getOpenFileName(this, tr("Select file"), this->getWorkingDirectory(), tr("Python wrapper (*.py)"));
}
else
{
path = QFileDialog::getOpenFileName(this, tr("Select file"), _ui->lineEdit_pydetector_path->text(), tr("Python wrapper (*.py)"));
}
if(!path.isEmpty())
{
_ui->lineEdit_pydetector_path->setText(path);
}
}
void PreferencesDialog::updateSourceGrpVisibility()
{
_ui->groupBox_sourceRGBD->setVisible(_ui->comboBox_sourceType->currentIndex() == 0);
+103 -4
View File
@@ -63,7 +63,7 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-687</y>
<y>0</y>
<width>686</width>
<height>3357</height>
</rect>
@@ -95,7 +95,7 @@
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>5</number>
<number>37</number>
</property>
<widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,1">
@@ -9159,6 +9159,11 @@ generate the number of words requested.</string>
<string>SURF+DAISY</string>
</property>
</item>
<item>
<property name="text">
<string>PyDetector</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0">
@@ -17786,7 +17791,7 @@ Lower the ratio -&gt; higher the precision.</string>
<item>
<widget class="QLabel" name="label_586">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Python wrapper for python matcher like &lt;a href=&quot;https://github.com/magicleap/SuperGluePretrainedNetwork&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SuperGlue&lt;/span&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/zjhthu/OANet&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;OANet&lt;/span&gt;&lt;/a&gt;. Download the &lt;a href=&quot;https://github.com/introlab/rtabmap/tree/master/corelib/src/pymatcher&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;scripts&lt;/span&gt;&lt;/a&gt; and copy them at the root folder of SuperGlue git or in demo folder of OANet respectively, then set its path below. &lt;span style=&quot; font-weight:600;&quot;&gt;Important&lt;/span&gt;: Only float descriptors are allowed and SuperGlue works only with SuperPoint descriptors! You may have to train a new model for the feature used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Python wrapper for python matcher like &lt;a href=&quot;https://github.com/magicleap/SuperGluePretrainedNetwork&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SuperGlue&lt;/span&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/zjhthu/OANet&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;OANet&lt;/span&gt;&lt;/a&gt;. Download the &lt;a href=&quot;https://github.com/introlab/rtabmap/tree/master/corelib/src/python&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;scripts&lt;/span&gt;&lt;/a&gt; and copy them at the root folder of SuperGlue git or in demo folder of OANet respectively, then set its path below. &lt;span style=&quot; font-weight:600;&quot;&gt;Important&lt;/span&gt;: Only float descriptors are allowed and SuperGlue works only with SuperPoint descriptors! You may have to train a new model for the feature used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -18539,6 +18544,11 @@ Lower the ratio -&gt; higher the precision.</string>
<string>SURF+DAISY</string>
</property>
</item>
<item>
<property name="text">
<string>PyDetector</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
@@ -22349,7 +22359,96 @@ Lower the ratio -&gt; higher the precision.</string>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>2964</height>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_87">
<layout class="QVBoxLayout" name="verticalLayout_153">
<item>
<widget class="QGroupBox" name="groupBox_pydetector2">
<property name="title">
<string>PyDetector</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_152">
<item>
<widget class="QLabel" name="label_614">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Python wrapper for python feature detector like &lt;a href=&quot;https://github.com/magicleap/SuperPointPretrainedNetwork&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SuperPoint&lt;/span&gt;&lt;/a&gt;. Download the &lt;a href=&quot;https://github.com/introlab/rtabmap/tree/master/corelib/src/python&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;script&lt;/span&gt;&lt;/a&gt; and copy it at the root folder of SuperPoint git, then set its path below.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_118" columnstretch="0,0,1">
<item row="0" column="2">
<widget class="QLabel" name="label_615">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;[Required] Path to python script file.&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 row="0" column="1">
<widget class="QLineEdit" name="lineEdit_pydetector_path"/>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="toolButton_pydetector_path">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_pydetector_cuda">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_618">
<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>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_88">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>3128</height>
</size>
</property>
</spacer>