Parameters: Renamed SuperGlue group to PyMatcher group. Added OANet python script.

This commit is contained in:
matlabbe
2020-05-20 09:45:39 -04:00
parent 63d40b2240
commit 360a153565
18 changed files with 255 additions and 148 deletions

View File

@@ -579,7 +579,7 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(Vis, PnPRefineIterations, int, 1, uFormat("[%s = 1] Refine iterations. Set to 0 if \"%s\" is also used.", kVisEstimationType().c_str(), kVisBundleAdjustment().c_str()));
#endif
RTABMAP_PARAM(Vis, EpipolarGeometryVar, float, 0.05, uFormat("[%s = 2] Epipolar geometry maximum variance to accept the transformation.", kVisEstimationType().c_str()));
RTABMAP_PARAM(Vis, EpipolarGeometryVar, float, 0.1, uFormat("[%s = 2] Epipolar geometry maximum variance to accept the transformation.", kVisEstimationType().c_str()));
RTABMAP_PARAM(Vis, MinInliers, int, 20, "Minimum feature correspondences to compute/accept the transformation.");
RTABMAP_PARAM(Vis, MeanInliersDistance, float, 0.0, "Maximum distance (m) of the mean distance of inliers from the camera to accept the transformation. 0 means disabled.");
RTABMAP_PARAM(Vis, MinInliersDistribution, float, 0.0, "Minimum distribution value of the inliers in the image to accept the transformation. The distribution is the second eigen value of the PCA (Principal Component Analysis) on the keypoints of the normalized image [-0.5, 0.5]. The value would be between 0 and 0.5. 0 means disabled.");
@@ -621,11 +621,11 @@ class RTABMAP_EXP Parameters
#endif
// Features matching approaches
RTABMAP_PARAM_STR(SuperGlue, Path, "", "Path to python script file \"rtabmap_superglue.py\" (rtabmap/corelib/src/superglue_pytorch/rtabmap_superglue.py) copied in SuperGlue's Git folder.");
RTABMAP_PARAM(SuperGlue, Iterations, int, 20, "Sinkhorn iterations.");
RTABMAP_PARAM(SuperGlue, MatchThreshold, float, 0.2, "");
RTABMAP_PARAM(SuperGlue, Cuda, bool, true, "");
RTABMAP_PARAM(SuperGlue, Indoor, bool, true, "Use indoor model, otherwise outdoor model is used.");
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(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.");
RTABMAP_PARAM_STR(PyMatcher, Model, "indoor", "For SuperGlue, set only \"indoor\" or \"outdoor\". For OANet, set path to one of the pth file (e.g., \"OANet/model/gl3d/sift-4000/model_best.pth\").");
RTABMAP_PARAM(GMS, WithRotation, bool, false, "Take rotation transformation into account.");
RTABMAP_PARAM(GMS, WithScale, bool, false, "Take scale transformation into account.");

View File

@@ -37,8 +37,8 @@ namespace rtabmap {
class Feature2D;
#ifdef RTABMAP_SUPERGLUE_PYTORCH
class SuperGlue;
#ifdef RTABMAP_PYMATCHER
class PyMatcher;
#endif
// Visual registration
@@ -105,8 +105,8 @@ private:
Feature2D * _detectorFrom;
Feature2D * _detectorTo;
#ifdef RTABMAP_SUPERGLUE_PYTORCH
SuperGlue * _superGlueMatcher;
#ifdef RTABMAP_PYMATCHER
PyMatcher * _pyMatcher;
#endif
};

View File

@@ -195,11 +195,11 @@ IF(Python3_FOUND)
)
SET(SRC_FILES
${SRC_FILES}
superglue_pytorch/SuperGlue.cpp
pymatcher/PyMatcher.cpp
)
SET(INCLUDE_DIRS
${TORCH_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/superglue_pytorch
${CMAKE_CURRENT_SOURCE_DIR}/pymatcher
${INCLUDE_DIRS}
)
ENDIF(Python3_FOUND)

View File

@@ -184,7 +184,7 @@ rtabmap::ParametersMap Parameters::getDefaultOdometryParameters(bool stereo, boo
group.compare("Optimizer") == 0 ||
group.compare("g2o") == 0 ||
group.compare("GTSAM") == 0 ||
(vis && (group.compare("Vis") == 0 || group.compare("SuperGlue") == 0 || group.compare("GMS") == 0)) ||
(vis && (group.compare("Vis") == 0 || group.compare("PyMatcher") == 0 || group.compare("GMS") == 0)) ||
iter->first.compare(kRtabmapPublishRAMUsage())==0)
{
odomParameters.insert(*iter);
@@ -232,6 +232,12 @@ const std::map<std::string, std::pair<bool, std::string> > & Parameters::getRemo
// removed parameters
// 0.20.
removedParameters_.insert(std::make_pair("SuperGlue/Path", std::make_pair(true, Parameters::kPyMatcherPath())));
removedParameters_.insert(std::make_pair("SuperGlue/Iterations", std::make_pair(true, Parameters::kPyMatcherIterations())));
removedParameters_.insert(std::make_pair("SuperGlue/MatchThreshold", std::make_pair(true, Parameters::kPyMatcherThreshold())));
removedParameters_.insert(std::make_pair("SuperGlue/Cuda", std::make_pair(true, Parameters::kPyMatcherCuda())));
removedParameters_.insert(std::make_pair("SuperGlue/Indoor", std::make_pair(false, Parameters::kPyMatcherModel())));
removedParameters_.insert(std::make_pair("Vis/CorCrossCheck", std::make_pair(false, Parameters::kVisCorNNType())));
removedParameters_.insert(std::make_pair("SPTorch/ModelPath", std::make_pair(true, Parameters::kSuperPointModelPath())));
removedParameters_.insert(std::make_pair("SPTorch/Threshold", std::make_pair(true, Parameters::kSuperPointThreshold())));
@@ -614,8 +620,8 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
#else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif
str = "With SuperGlue PyTorch:";
#ifdef RTABMAP_SUPERGLUE_PYTORCH
str = "With Python3:";
#ifdef RTABMAP_PYMATCHER
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;

View File

@@ -51,8 +51,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtflann/flann.hpp>
#ifdef RTABMAP_SUPERGLUE_PYTORCH
#include "superglue_pytorch/SuperGlue.h"
#ifdef RTABMAP_PYMATCHER
#include <pymatcher/PyMatcher.h>
#endif
namespace rtabmap {
@@ -87,9 +87,9 @@ RegistrationVis::RegistrationVis(const ParametersMap & parameters, Registration
_maxInliersMeanDistance(Parameters::defaultVisMeanInliersDistance()),
_detectorFrom(0),
_detectorTo(0)
#ifdef RTABMAP_SUPERGLUE_PYTORCH
#ifdef RTABMAP_PYMATCHER
,
_superGlueMatcher(0)
_pyMatcher(0)
#endif
{
_featureParameters = Parameters::getDefaultParameters();
@@ -152,34 +152,34 @@ void RegistrationVis::parseParameters(const ParametersMap & parameters)
if(_nnType == 6)
{
// verify that we have SuperGlue support
#ifndef RTABMAP_SUPERGLUE_PYTORCH
UWARN("%s is set to 6 but RTAB-Map is not built with SuperGlue support, using default %d.",
// verify that we have Python3 support
#ifndef RTABMAP_PYMATCHER
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();
#else
int iterations = _superGlueMatcher?_superGlueMatcher->iterations():Parameters::defaultSuperGlueIterations();
float matchThr = _superGlueMatcher?_superGlueMatcher->matchThreshold():Parameters::defaultSuperGlueMatchThreshold();
std::string path = _superGlueMatcher?_superGlueMatcher->path():Parameters::defaultSuperGluePath();
bool cuda = _superGlueMatcher?_superGlueMatcher->cuda():Parameters::defaultSuperGlueCuda();
bool indoor = _superGlueMatcher?_superGlueMatcher->indoor():Parameters::defaultSuperGlueIndoor();
Parameters::parse(parameters, Parameters::kSuperGlueIterations(), iterations);
Parameters::parse(parameters, Parameters::kSuperGlueMatchThreshold(), matchThr);
Parameters::parse(parameters, Parameters::kSuperGluePath(), path);
Parameters::parse(parameters, Parameters::kSuperGlueCuda(), cuda);
Parameters::parse(parameters, Parameters::kSuperGlueIndoor(), indoor);
int iterations = _pyMatcher?_pyMatcher->iterations():Parameters::defaultPyMatcherIterations();
float matchThr = _pyMatcher?_pyMatcher->matchThreshold():Parameters::defaultPyMatcherThreshold();
std::string path = _pyMatcher?_pyMatcher->path():Parameters::defaultPyMatcherPath();
bool cuda = _pyMatcher?_pyMatcher->cuda():Parameters::defaultPyMatcherCuda();
std::string model = _pyMatcher?_pyMatcher->model():Parameters::defaultPyMatcherModel();
Parameters::parse(parameters, Parameters::kPyMatcherIterations(), iterations);
Parameters::parse(parameters, Parameters::kPyMatcherThreshold(), matchThr);
Parameters::parse(parameters, Parameters::kPyMatcherPath(), path);
Parameters::parse(parameters, Parameters::kPyMatcherCuda(), cuda);
Parameters::parse(parameters, Parameters::kPyMatcherModel(), model);
if(path.empty())
{
UERROR("%s parameter should be set to use SuperGlue matching (%s=6), using default %d.",
Parameters::kSuperGluePath().c_str(),
UERROR("%s parameter should be set to use Python3 matching (%s=6), using default %d.",
Parameters::kPyMatcherPath().c_str(),
Parameters::kVisCorNNType().c_str(),
Parameters::defaultVisCorNNType());
_nnType = Parameters::defaultVisCorNNType();
}
else
{
delete _superGlueMatcher;
_superGlueMatcher = new SuperGlue(path, matchThr, iterations, cuda, indoor);
delete _pyMatcher;
_pyMatcher = new PyMatcher(path, matchThr, iterations, cuda, model);
}
#endif
}
@@ -264,8 +264,8 @@ RegistrationVis::~RegistrationVis()
{
delete _detectorFrom;
delete _detectorTo;
#ifdef RTABMAP_SUPERGLUE_PYTORCH
delete _superGlueMatcher;
#ifdef RTABMAP_PYMATCHER
delete _pyMatcher;
#endif
}
@@ -1137,8 +1137,8 @@ Transform RegistrationVis::computeTransformationImpl(
// match between all descriptors
std::list<int> fromWordIds;
std::list<int> toWordIds;
#ifdef RTABMAP_SUPERGLUE_PYTORCH
if(_nnType == 5 || (_nnType == 6 && _superGlueMatcher) || _nnType==7)
#ifdef RTABMAP_PYMATCHER
if(_nnType == 5 || (_nnType == 6 && _pyMatcher) || _nnType==7)
#else
if(_nnType == 5 || _nnType == 7) // bruteforce cross check or GMS
#endif
@@ -1158,8 +1158,8 @@ Transform RegistrationVis::computeTransformationImpl(
{
std::vector<int> toWordIdsV(descriptorsTo.rows, 0);
std::vector<cv::DMatch> matches;
#ifdef RTABMAP_SUPERGLUE_PYTORCH
if(_nnType == 6 && _superGlueMatcher &&
#ifdef RTABMAP_PYMATCHER
if(_nnType == 6 && _pyMatcher &&
descriptorsTo.cols == descriptorsFrom.cols &&
descriptorsTo.rows == (int)kptsTo.size() &&
descriptorsTo.type() == CV_32F &&
@@ -1167,14 +1167,14 @@ Transform RegistrationVis::computeTransformationImpl(
descriptorsFrom.rows == (int)kptsFrom.size() &&
imageSize.width > 0 && imageSize.height > 0)
{
UDEBUG("SuperGlue matching");
matches = _superGlueMatcher->match(descriptorsTo, descriptorsFrom, kptsTo, kptsFrom, imageSize);
UDEBUG("Python matching");
matches = _pyMatcher->match(descriptorsTo, descriptorsFrom, kptsTo, kptsFrom, imageSize);
}
else
{
if(_nnType == 6 && _superGlueMatcher)
if(_nnType == 6 && _pyMatcher)
{
UDEBUG("Invalid inputs for SuperGlue (desc type=%d, only float descriptors supported), doing bruteforce matching instead.", descriptorsFrom.type());
UDEBUG("Invalid inputs for Python matching (desc type=%d, only float descriptors supported), doing bruteforce matching instead.", descriptorsFrom.type());
}
#else
{

View File

@@ -2,7 +2,7 @@
* Python interface for SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
*/
#include <superglue_pytorch/SuperGlue.h>
#include <pymatcher/PyMatcher.h>
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UFile.h>
@@ -62,20 +62,26 @@ std::string getTraceback()
return pretty;
}
SuperGlue::SuperGlue(const std::string & path, float matchThreshold, int iterations, bool cuda, bool indoor) :
pModule_(0),
pFunc_(0),
matchThreshold_(matchThreshold),
iterations_(iterations),
cuda_(cuda),
indoor_(indoor)
PyMatcher::PyMatcher(
const std::string & pythonMatcherPath,
float matchThreshold,
int iterations,
bool cuda,
const std::string & model) :
pModule_(0),
pFunc_(0),
matchThreshold_(matchThreshold),
iterations_(iterations),
cuda_(cuda)
{
path_ = uReplaceChar(path, '~', UDirectory::homeDir());
path_ = uReplaceChar(pythonMatcherPath, '~', UDirectory::homeDir());
model_ = uReplaceChar(model, '~', UDirectory::homeDir());
UINFO("path = %s", path_.c_str());
UINFO("model = %s", model_.c_str());
if(!UFile::exists(path_) || UFile::getExtension(path_).compare("py") != 0)
{
UERROR("Cannot initialize SuperGlue, the path is not valid: \"%s\"", path_.c_str());
UERROR("Cannot initialize Python matcher, the path is not valid: \"%s\"", path_.c_str());
return;
}
@@ -84,11 +90,11 @@ SuperGlue::SuperGlue(const std::string & path, float matchThreshold, int iterati
g_python.init();
}
std::string superGluePythonDir = UDirectory::getDir(path_);
if(!superGluePythonDir.empty())
std::string matcherPythonDir = UDirectory::getDir(path_);
if(!matcherPythonDir.empty())
{
PyRun_SimpleString("import sys");
PyRun_SimpleString(uFormat("sys.path.append(\"%s\")", superGluePythonDir.c_str()).c_str());
PyRun_SimpleString(uFormat("sys.path.append(\"%s\")", matcherPythonDir.c_str()).c_str());
}
_import_array();
@@ -105,7 +111,7 @@ SuperGlue::SuperGlue(const std::string & path, float matchThreshold, int iterati
}
}
SuperGlue::~SuperGlue()
PyMatcher::~PyMatcher()
{
if(pFunc_)
{
@@ -117,7 +123,7 @@ SuperGlue::~SuperGlue()
}
}
std::vector<cv::DMatch> SuperGlue::match(
std::vector<cv::DMatch> PyMatcher::match(
const cv::Mat & descriptorsQuery,
const cv::Mat & descriptorsTrain,
const std::vector<cv::KeyPoint> & keypointsQuery,
@@ -129,11 +135,11 @@ std::vector<cv::DMatch> SuperGlue::match(
if(!pModule_)
{
UERROR("SuperGlue python module not loaded!");
UERROR("Python matcher module not loaded!");
return matches;
}
if(descriptorsQuery.cols == 256 && // Only SuperPoint is supported!
if(!descriptorsQuery.empty() &&
descriptorsQuery.cols == descriptorsTrain.cols &&
descriptorsQuery.type() == CV_32F &&
descriptorsTrain.type() == CV_32F &&
@@ -151,7 +157,15 @@ std::vector<cv::DMatch> SuperGlue::match(
{
if(PyCallable_Check(pFunc))
{
PyObject_CallFunction(pFunc, "ifiii", descriptorsQuery.cols, matchThreshold_, iterations_, cuda_?1:0, indoor_?1:0);
PyObject * result = PyObject_CallFunction(pFunc, "ifiis", descriptorsQuery.cols, matchThreshold_, iterations_, cuda_?1:0, model_.c_str());
if(result == NULL)
{
UERROR("Call to \"init(...)\" in \"%s\" failed!", path_.c_str());
UERROR("%s", getTraceback().c_str());
return matches;
}
Py_DECREF(result);
pFunc_ = PyObject_GetAttrString(pModule_, "match");
if(pFunc_ && PyCallable_Check(pFunc_))
@@ -255,11 +269,24 @@ std::vector<cv::DMatch> SuperGlue::match(
// Convert back to C++ array and print.
int len1 = PyArray_SHAPE(np_ret)[0];
int len2 = PyArray_SHAPE(np_ret)[1];
//int type = PyArray_TYPE(np_ret); // Should be long
long* c_out = reinterpret_cast<long*>(PyArray_DATA(np_ret));
for (int i = 0; i < len1*len2; i+=2)
int type = PyArray_TYPE(np_ret);
UDEBUG("Matches array %dx%d (type=%d)", len1, len2, type);
UASSERT_MSG(type == NPY_LONG || type == NPY_INT, uFormat("Returned matches should type INT=5 or LONG=7, received type=%d", type).c_str());
if(type == NPY_LONG)
{
matches.push_back(cv::DMatch(c_out[i], c_out[i+1], 0));
long* c_out = reinterpret_cast<long*>(PyArray_DATA(np_ret));
for (int i = 0; i < len1*len2; i+=2)
{
matches.push_back(cv::DMatch(c_out[i], c_out[i+1], 0));
}
}
else // INT
{
int* c_out = reinterpret_cast<int*>(PyArray_DATA(np_ret));
for (int i = 0; i < len1*len2; i+=2)
{
matches.push_back(cv::DMatch(c_out[i], c_out[i+1], 0));
}
}
Py_DECREF(pReturn);
}
@@ -276,15 +303,9 @@ std::vector<cv::DMatch> SuperGlue::match(
UDEBUG("Fill matches (%d/%d) and cleanup time = %fs", matches.size(), std::min(descriptorsQuery.rows, descriptorsTrain.rows), timer.ticks());
}
}
else if(descriptorsQuery.cols != 256)
{
UERROR("Only descriptor size of 256 (SuperPoint) is "
"supported with SuperGlue! Current descriptor size=%d.",
descriptorsQuery.cols);
}
else
{
UERROR("Invalid inputs! SuperGlue requires SuperPoint descriptors (dim=256).");
UERROR("Invalid inputs! Supported python matchers require float descriptors.");
}
return matches;
}

View File

@@ -1,9 +1,11 @@
/**
* Python interface for SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
* Python interface for python matchers like:
* - SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
* - OANET https://github.com/zjhthu/OANet
*/
#ifndef SUPERGLUE_H
#define SUPERGLUE_H
#ifndef PYMATCHER_H
#define PYMATCHER_H
#include <opencv2/core/types.hpp>
#include <opencv2/core/mat.hpp>
@@ -14,17 +16,21 @@
namespace rtabmap
{
class SuperGlue
class PyMatcher
{
public:
SuperGlue(const std::string & supergluePythonPath, float matchThreshold = 0.2f, int iterations = 20, bool cuda = true, bool indoor=true);
virtual ~SuperGlue();
PyMatcher(const std::string & pythonMatcherPath,
float matchThreshold = 0.2f,
int iterations = 20,
bool cuda = true,
const std::string & model = "indoor");
virtual ~PyMatcher();
const std::string & path() const {return path_;}
float matchThreshold() const {return matchThreshold_;}
int iterations() const {return iterations_;}
bool cuda() const {return cuda_;}
bool indoor() const {return indoor_;}
const std::string & model() const {return model_;}
std::vector<cv::DMatch> match(
const cv::Mat & descriptorsQuery,
@@ -40,7 +46,7 @@ private:
float matchThreshold_;
int iterations_;
bool cuda_;
bool indoor_;
std::string model_;
};
}

View File

@@ -0,0 +1,44 @@
#! /usr/bin/env python3
#
# Drop this file in the "demo" folder of OANet git: https://github.com/zjhthu/OANet
# To use with rtabmap:
# --Vis/CorNNType 6 --PyMatcher/Path ~/OANet/demo/rtabmap_oanet.py --PyMatcher/Model ~/OANet/model/gl3d/sift-4000/model_best.pth
#
import sys
import os
sys.path.append(os.path.dirname(os.path.realpath(__file__))+'/../core')
if not hasattr(sys, 'argv'):
sys.argv = ['']
#print(os.sys.path)
#print(sys.version)
import numpy as np
from learnedmatcher import LearnedMatcher
lm = None
def init(descriptorDim, matchThreshold, iterations, cuda, model_path):
print("OANet python init()")
global lm
lm = LearnedMatcher(model_path, inlier_threshold=1, use_ratio=0, use_mutual=0)
def match(kptsFrom, kptsTo, scoresFrom, scoresTo, descriptorsFrom, descriptorsTo, imageWidth, imageHeight):
#print("OANet python match()")
kpt1 = np.asarray(kptsFrom)
kpt2 = np.asarray(kptsTo)
desc1 = np.asarray(descriptorsFrom)
desc2 = np.asarray(descriptorsTo)
global lm
matches, _, _ = lm.infer([kpt1, kpt2], [desc1, desc2])
return matches
if __name__ == '__main__':
#test
init(128, 0.2, 20, False, True)
match([[1, 2], [1,3], [4,6]], [[1, 3], [1,2], [16,2]], [1, 3,6], [1,3,5], np.full((3, 128), 1), np.full((3, 128), 1), 640, 480)

View File

@@ -1,6 +1,6 @@
#! /usr/bin/env python3
#
# Drop this file in the folder of SuperGlue git: https://github.com/magicleap/SuperGluePretrainedNetwork
# Drop this file in the root folder of SuperGlue git: https://github.com/magicleap/SuperGluePretrainedNetwork
# To use with rtabmap:
# --Vis/CorNNType 6 --SuperGlue/Path "~/SuperGluePretrainedNetwork/rtabmap_superglue.py"
#
@@ -21,14 +21,15 @@ torch.set_grad_enabled(False)
device = 'cpu'
superglue = []
def init(descriptorDim, matchThreshold, iterations, cuda, indoor):
print("Python init()")
# Load the SuperPoint and SuperGlue models.
def init(descriptorDim, matchThreshold, iterations, cuda, model):
print("SuperGlue python init()")
# Load the SuperGlue model.
global device
device = 'cuda' if torch.cuda.is_available() and cuda else 'cpu'
assert model == "indoor" or model == "outdoor", "Available models for SuperGlue are 'indoor' or 'outdoor'"
config = {
'superglue': {
'weights': 'indoor' if indoor else 'outdoor',
'weights': model,
'sinkhorn_iterations': iterations,
'match_threshold': matchThreshold,
'descriptor_dim' : descriptorDim
@@ -39,7 +40,7 @@ def init(descriptorDim, matchThreshold, iterations, cuda, indoor):
def match(kptsFrom, kptsTo, scoresFrom, scoresTo, descriptorsFrom, descriptorsTo, imageWidth, imageHeight):
#print("Python match()")
#print("SuperGlue python match()")
global device
kptsFrom = np.asarray(kptsFrom)
kptsFrom = kptsFrom[None, :, :]
@@ -81,5 +82,5 @@ def match(kptsFrom, kptsTo, scoresFrom, scoresTo, descriptorsFrom, descriptorsTo
if __name__ == '__main__':
#test
init(256, 0.2, 20, True, True)
init(256, 0.2, 20, True, 'indoor')
match([[1, 2], [1,3]], [[1, 3], [1,2]], [1, 3], [1,3], np.full((2, 256), 1),np.full((2, 256), 1), 640, 480)