1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

slam VO modular matcher changes/fixes

This commit is contained in:
Agrim Rai
2026-07-07 21:12:37 +05:30
parent 747a71fdfe
commit f552e8ac82
5 changed files with 86 additions and 23 deletions
@@ -998,6 +998,19 @@ public:
*/
CV_WRAP virtual bool isMaskSupported() const = 0;
/** @brief Provides keypoint and image-size context for matchers that need it (e.g. LightGlueMatcher).
Must be called before match()/knnMatch()/radiusMatch() for matchers that require this context.
Matchers that don't need it (e.g. BFMatcher, FlannBasedMatcher) ignore the call.
@param queryKpts Query image keypoints.
@param trainKpts Train image keypoints.
@param queryImageSize Size of the query image (width, height).
@param trainImageSize Size of the train image (width, height).
*/
CV_WRAP virtual void setImagePairInfo(const std::vector<KeyPoint>& queryKpts, const std::vector<KeyPoint>& trainKpts,
Size queryImageSize = Size(), Size trainImageSize = Size());
/** @brief Trains a descriptor matcher
Trains a descriptor matcher (for example, the flann index). In all methods to match, the method
@@ -1349,6 +1362,10 @@ public:
/** @brief Clears stored pair context information.
*/
CV_WRAP virtual void clearPairInfo() = 0;
/** @brief Convenience overload of setPairInfo() taking keypoints directly. */
CV_WRAP void setImagePairInfo(const std::vector<KeyPoint>& queryKpts, const std::vector<KeyPoint>& trainKpts,
Size queryImageSize = Size(), Size trainImageSize = Size()) CV_OVERRIDE;
};
//! @} features_match
+4
View File
@@ -580,6 +580,10 @@ bool DescriptorMatcher::empty() const
void DescriptorMatcher::train()
{}
void DescriptorMatcher::setImagePairInfo( const std::vector<KeyPoint>&, const std::vector<KeyPoint>&,
Size, Size )
{}
void DescriptorMatcher::match( InputArray queryDescriptors, InputArray trainDescriptors,
std::vector<DMatch>& matches, InputArray mask ) const
{
@@ -15,6 +15,26 @@ namespace cv
LightGlueMatcher::LightGlueMatcher() {}
LightGlueMatcher::~LightGlueMatcher() {}
void LightGlueMatcher::setImagePairInfo(const std::vector<KeyPoint>& queryKpts, const std::vector<KeyPoint>& trainKpts,
Size queryImageSize, Size trainImageSize)
{
Mat qk((int)queryKpts.size(), 2, CV_32F);
for (size_t i = 0; i < queryKpts.size(); ++i)
{
qk.at<float>((int)i, 0) = queryKpts[i].pt.x;
qk.at<float>((int)i, 1) = queryKpts[i].pt.y;
}
Mat tk((int)trainKpts.size(), 2, CV_32F);
for (size_t i = 0; i < trainKpts.size(); ++i)
{
tk.at<float>((int)i, 0) = trainKpts[i].pt.x;
tk.at<float>((int)i, 1) = trainKpts[i].pt.y;
}
setPairInfo(qk, tk, queryImageSize, trainImageSize);
}
#ifdef HAVE_OPENCV_DNN
struct LightGluePairContext
@@ -174,20 +174,7 @@ void VisualOdometryImpl::matchFrames(
if (qDesc.empty() || tDesc.empty()) return;
if (qKp.empty() || tKp.empty()) return;
LightGlueMatcher* lg = dynamic_cast<LightGlueMatcher*>(matcher.get());
if (lg)
{
Mat qk((int)qKp.size(), 2, CV_32F);
for (size_t i = 0; i < qKp.size(); ++i)
{ qk.at<float>((int)i,0) = qKp[i].pt.x; qk.at<float>((int)i,1) = qKp[i].pt.y; }
Mat tk((int)tKp.size(), 2, CV_32F);
for (size_t i = 0; i < tKp.size(); ++i)
{ tk.at<float>((int)i,0) = tKp[i].pt.x; tk.at<float>((int)i,1) = tKp[i].pt.y; }
lg->setPairInfo(qk, tk, qSz, tSz);
}
matcher->setImagePairInfo(qKp, tKp, qSz, tSz);
matcher->match(qDesc, tDesc, matches);
}
+44 -9
View File
@@ -3,15 +3,27 @@
// of this distribution and at http://opencv.org/license.html.
// Copyright (C) 2026, BigVision LLC, all rights reserved.
#include <opencv2/ptcloud/slam.hpp>
#include <opencv2/ptcloud.hpp>
#include <opencv2/features.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/core.hpp>
#include <iostream>
using namespace cv;
#include "../dnn/common.hpp"
static const char* keys =
using namespace cv;
using namespace std;
const string about =
"Monocular visual odometry using ALIKED + LightGlue.\n\n"
"Runs feature detection (ALIKED) and matching (LightGlue) from ONNX models over a\n"
"directory of images, estimates the camera trajectory and writes it to --output.\n"
"To run:\n"
"\t ./example_slam_visual_odometry --aliked=aliked.onnx --lightglue=lightglue.onnx --images=./seq\n"
"Sample command (run on the GPU):\n"
"\t ./example_slam_visual_odometry --aliked=aliked.onnx --lightglue=lightglue.onnx --images=./seq --target=cuda\n";
const string param_keys =
"{ help h | | Print help message }"
"{ aliked | <none> | Path to ALIKED ONNX model }"
"{ lightglue | <none> | Path to LightGlue ONNX model }"
@@ -24,11 +36,31 @@ static const char* keys =
"{ min-parallax | 1.5 | Minimum initialisation parallax in degrees }"
"{ min-points | 50 | Minimum initialisation map points }";
const string backend_keys = format(
"{ backend | default | Choose one of computation backends: "
"default: automatically (by default), "
"openvino: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), "
"opencv: OpenCV implementation, "
"vkcom: VKCOM, "
"cuda: CUDA, "
"webnn: WebNN }");
const string target_keys = format(
"{ target | cpu | Choose one of target computation devices: "
"cpu: CPU target (by default), "
"opencl: OpenCL, "
"opencl_fp16: OpenCL fp16 (half-float precision), "
"vpu: VPU, "
"vulkan: Vulkan, "
"cuda: CUDA, "
"cuda_fp16: CUDA fp16 (half-float preprocess) }");
string keys = param_keys + backend_keys + target_keys;
int main(int argc, char** argv)
{
CommandLineParser parser(argc, argv, keys);
parser.about("Monocular visual odometry using ALIKED + LightGlue\n"
" Example: visual_odometry --aliked=aliked.onnx --lightglue=lg.onnx --images=./seq\n");
parser.about(about);
if (parser.has("help"))
{
@@ -36,9 +68,11 @@ int main(int argc, char** argv)
return 0;
}
const String alikedPath = parser.get<String>("aliked");
const String alikedPath = parser.get<String>("aliked");
const String lightgluePath = parser.get<String>("lightglue");
const String imagesDir = parser.get<String>("images");
const String imagesDir = parser.get<String>("images");
const int backendId = getBackendID(parser.get<String>("backend"));
const int targetId = getTargetID(parser.get<String>("target"));
if (!parser.check() || alikedPath == "<none>" || lightgluePath == "<none>" || imagesDir == "<none>")
{
@@ -56,11 +90,12 @@ int main(int argc, char** argv)
ALIKED::Params detParams;
detParams.inputSize = Size(640, 640);
detParams.engine = dnn::ENGINE_NEW;
detParams.backend = backendId;
detParams.target = targetId;
auto detector = ALIKED::create(alikedPath, detParams);
auto matcher = LightGlueMatcher::create(lightgluePath, 0.0f,
dnn::DNN_BACKEND_DEFAULT,
dnn::DNN_TARGET_CPU);
backendId, targetId);
slam::OdometryParams voParams;
voParams.minInitParallaxDeg = parser.get<double>("min-parallax");