mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 08:13:04 +04:00
better vo samples
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
// of this distribution and at http://opencv.org/license.html.
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
// Copyright (C) 2026, BigVision LLC, all rights reserved.
|
// Copyright (C) 2026, BigVision LLC, all rights reserved.
|
||||||
// Third party copyrights are property of their respective owners.
|
|
||||||
|
|
||||||
#include <opencv2/slam.hpp>
|
#include <opencv2/slam.hpp>
|
||||||
#include <opencv2/features.hpp>
|
#include <opencv2/features.hpp>
|
||||||
@@ -12,52 +11,73 @@
|
|||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
static const char* ALIKED_MODEL = "/media/user/path/to/models/aliked-n16rot-top1k-640.onnx";
|
static const char* keys =
|
||||||
static const char* LIGHTGLUE_MODEL = "/media/user/path/to/models/aliked_lightglue.onnx";
|
"{ help h | | Print help message }"
|
||||||
static const char* IMAGES_DIR = "/media/user/path/to/dataset";
|
"{ aliked | <none> | Path to ALIKED ONNX model }"
|
||||||
static const char* OUTPUT_DIR = "vo_out";
|
"{ lightglue | <none> | Path to LightGlue ONNX model }"
|
||||||
|
"{ images | <none> | Path to directory with input images }"
|
||||||
|
"{ output | vo_out | Output directory for trajectory and map }"
|
||||||
|
"{ fx | 718.856 | Camera focal length X }"
|
||||||
|
"{ fy | 718.856 | Camera focal length Y }"
|
||||||
|
"{ cx | 607.1928 | Camera principal point X }"
|
||||||
|
"{ cy | 185.2157 | Camera principal point Y }"
|
||||||
|
"{ min-parallax | 1.5 | Minimum initialisation parallax in degrees }"
|
||||||
|
"{ min-points | 50 | Minimum initialisation map points }";
|
||||||
|
|
||||||
// KITTI-00: fx, fy, cx, cy
|
int main(int argc, char** argv)
|
||||||
static const Matx33d K(718.856, 0., 607.1928,
|
|
||||||
0., 718.856, 185.2157,
|
|
||||||
0., 0., 1.);
|
|
||||||
|
|
||||||
// k1, k2, p1, p2, k3
|
|
||||||
static const std::vector<double> DIST = { -0.2811, 0.0723, -0.0003, 0.0001, 0.0 };
|
|
||||||
|
|
||||||
static Ptr<Feature2D> makeDetector()
|
|
||||||
{
|
{
|
||||||
ALIKED::Params p;
|
CommandLineParser parser(argc, argv, keys);
|
||||||
p.inputSize = Size(640, 640);
|
parser.about("Monocular visual odometry using ALIKED + LightGlue\n"
|
||||||
p.engine = dnn::ENGINE_NEW;
|
" Example: visual_odometry --aliked=aliked.onnx --lightglue=lg.onnx --images=./seq\n");
|
||||||
return ALIKED::create(ALIKED_MODEL, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Ptr<DescriptorMatcher> makeMatcher()
|
if (parser.has("help"))
|
||||||
{
|
{
|
||||||
return LightGlueMatcher::create(LIGHTGLUE_MODEL, 0.0f,
|
parser.printMessage();
|
||||||
dnn::DNN_BACKEND_DEFAULT,
|
return 0;
|
||||||
dnn::DNN_TARGET_CPU);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
const String alikedPath = parser.get<String>("aliked");
|
||||||
{
|
const String lightgluePath = parser.get<String>("lightglue");
|
||||||
slam::OdometryParams params;
|
const String imagesDir = parser.get<String>("images");
|
||||||
params.minInitParallaxDeg = 1.5;
|
|
||||||
params.minInitPoints = 50;
|
if (!parser.check() || alikedPath == "<none>" || lightgluePath == "<none>" || imagesDir == "<none>")
|
||||||
|
{
|
||||||
|
parser.printErrors();
|
||||||
|
parser.printMessage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String outputDir = parser.get<String>("output");
|
||||||
|
|
||||||
|
const Matx33d K(parser.get<double>("fx"), 0., parser.get<double>("cx"),
|
||||||
|
0., parser.get<double>("fy"), parser.get<double>("cy"),
|
||||||
|
0., 0., 1.);
|
||||||
|
|
||||||
|
ALIKED::Params detParams;
|
||||||
|
detParams.inputSize = Size(640, 640);
|
||||||
|
detParams.engine = dnn::ENGINE_NEW;
|
||||||
|
auto detector = ALIKED::create(alikedPath, detParams);
|
||||||
|
|
||||||
|
auto matcher = LightGlueMatcher::create(lightgluePath, 0.0f,
|
||||||
|
dnn::DNN_BACKEND_DEFAULT,
|
||||||
|
dnn::DNN_TARGET_CPU);
|
||||||
|
|
||||||
|
slam::OdometryParams voParams;
|
||||||
|
voParams.minInitParallaxDeg = parser.get<double>("min-parallax");
|
||||||
|
voParams.minInitPoints = parser.get<int>("min-points");
|
||||||
|
|
||||||
auto vo = slam::VisualOdometry::create(
|
auto vo = slam::VisualOdometry::create(
|
||||||
makeDetector(), makeMatcher(),
|
detector, matcher,
|
||||||
IMAGES_DIR, OUTPUT_DIR,
|
imagesDir, outputDir,
|
||||||
Mat(K), Mat(DIST), params);
|
Mat(K), Mat(), voParams);
|
||||||
|
|
||||||
const int64 t0 = getTickCount();
|
const int64 t0 = getTickCount();
|
||||||
const bool ok = vo->run();
|
const bool ok = vo->run();
|
||||||
const double elapsed = (getTickCount() - t0) / getTickFrequency();
|
const double elapsed = (getTickCount() - t0) / getTickFrequency();
|
||||||
|
|
||||||
std::cout << "run=" << (ok ? "ok" : "FAILED")
|
std::cout << "run=" << (ok ? "ok" : "FAILED")
|
||||||
<< " frames=" << vo->getTrajectory().size()
|
<< " frames=" << vo->getTrajectory().size()
|
||||||
<< " elapsed=" << elapsed << "s\n"
|
<< " elapsed=" << elapsed << "s\n"
|
||||||
<< "output -> " << OUTPUT_DIR << "\n";
|
<< "output -> " << outputDir << "\n";
|
||||||
return ok ? 0 : 1;
|
return ok ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,74 @@
|
|||||||
'''
|
'''
|
||||||
Monocular visual odometry with cv.slam.VisualOdometry (ALIKED + LightGlue).
|
Monocular visual odometry with cv.slam.VisualOdometry (ALIKED + LightGlue).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python visual_odometry.py --aliked aliked.onnx --lightglue lg.onnx --images ./seq
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import argparse
|
||||||
import time
|
import time
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import cv2 as cv
|
import cv2 as cv
|
||||||
|
|
||||||
ALIKED_MODEL = '/media/user/path/to/models/aliked-n16rot-top1k-640.onnx'
|
|
||||||
LIGHTGLUE_MODEL = '/media/user/path/to/models/aliked_lightglue.onnx'
|
|
||||||
IMAGES_DIR = '/media/user/path/to/dataset'
|
|
||||||
OUTPUT_DIR = 'vo_out'
|
|
||||||
|
|
||||||
# KITTI-00: fx, fy, cx, cy
|
def build_K(fx, fy, cx, cy):
|
||||||
K = np.array([[718.856, 0., 607.1928],
|
return np.array([[fx, 0., cx],
|
||||||
[0., 718.856, 185.2157],
|
[0., fy, cy],
|
||||||
[0., 0., 1. ]], dtype=np.float64)
|
[0., 0., 1.]], dtype=np.float64)
|
||||||
|
|
||||||
# k1, k2, p1, p2, k3
|
|
||||||
DIST = np.array([-0.2811, 0.0723, -0.0003, 0.0001, 0.0], dtype=np.float64)
|
|
||||||
|
|
||||||
|
|
||||||
def make_detector():
|
|
||||||
p = cv.ALIKED.Params()
|
|
||||||
p.inputSize = (640, 640)
|
|
||||||
p.engine = cv.dnn.ENGINE_NEW
|
|
||||||
return cv.ALIKED.create(ALIKED_MODEL, p)
|
|
||||||
|
|
||||||
|
|
||||||
def make_matcher():
|
|
||||||
return cv.LightGlueMatcher.create(
|
|
||||||
LIGHTGLUE_MODEL, 0.0,
|
|
||||||
cv.dnn.DNN_BACKEND_DEFAULT,
|
|
||||||
cv.dnn.DNN_TARGET_CPU)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
params = cv.slam.OdometryParams()
|
parser = argparse.ArgumentParser(
|
||||||
params.minInitParallaxDeg = 1.5
|
description='Monocular visual odometry using ALIKED + LightGlue')
|
||||||
params.minInitPoints = 50
|
parser.add_argument('--aliked', required=True,
|
||||||
|
help='Path to ALIKED ONNX model')
|
||||||
|
parser.add_argument('--lightglue', required=True,
|
||||||
|
help='Path to LightGlue ONNX model')
|
||||||
|
parser.add_argument('--images', required=True,
|
||||||
|
help='Path to directory with input images')
|
||||||
|
parser.add_argument('--output', default='vo_out',
|
||||||
|
help='Output directory for trajectory and map (default: vo_out)')
|
||||||
|
parser.add_argument('--fx', type=float, default=718.856,
|
||||||
|
help='Camera focal length X (default: KITTI-00)')
|
||||||
|
parser.add_argument('--fy', type=float, default=718.856,
|
||||||
|
help='Camera focal length Y (default: KITTI-00)')
|
||||||
|
parser.add_argument('--cx', type=float, default=607.1928,
|
||||||
|
help='Camera principal point X (default: KITTI-00)')
|
||||||
|
parser.add_argument('--cy', type=float, default=185.2157,
|
||||||
|
help='Camera principal point Y (default: KITTI-00)')
|
||||||
|
parser.add_argument('--min-parallax', type=float, default=1.5,
|
||||||
|
help='Minimum initialisation parallax in degrees (default: 1.5)')
|
||||||
|
parser.add_argument('--min-points', type=int, default=50,
|
||||||
|
help='Minimum initialisation map points (default: 50)')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
det_params = cv.ALIKED.Params()
|
||||||
|
det_params.inputSize = (640, 640)
|
||||||
|
det_params.engine = cv.dnn.ENGINE_NEW
|
||||||
|
detector = cv.ALIKED.create(args.aliked, det_params)
|
||||||
|
|
||||||
|
matcher = cv.LightGlueMatcher.create(
|
||||||
|
args.lightglue, 0.0,
|
||||||
|
cv.dnn.DNN_BACKEND_DEFAULT,
|
||||||
|
cv.dnn.DNN_TARGET_CPU)
|
||||||
|
|
||||||
|
vo_params = cv.slam.OdometryParams()
|
||||||
|
vo_params.minInitParallaxDeg = args.min_parallax
|
||||||
|
vo_params.minInitPoints = args.min_points
|
||||||
|
|
||||||
|
K = build_K(args.fx, args.fy, args.cx, args.cy)
|
||||||
|
|
||||||
vo = cv.slam.VisualOdometry.create(
|
vo = cv.slam.VisualOdometry.create(
|
||||||
make_detector(), make_matcher(),
|
detector, matcher,
|
||||||
IMAGES_DIR, OUTPUT_DIR,
|
args.images, args.output,
|
||||||
K, DIST, params)
|
K, np.array([]), vo_params)
|
||||||
|
|
||||||
t0 = time.perf_counter()
|
t0 = time.perf_counter()
|
||||||
ok = vo.run()
|
ok = vo.run()
|
||||||
elapsed = time.perf_counter() - t0
|
elapsed = time.perf_counter() - t0
|
||||||
|
|
||||||
print(f"run={'ok' if ok else 'FAILED'} frames={len(vo.getTrajectory())} elapsed={elapsed:.2f}s")
|
print(f"run={'ok' if ok else 'FAILED'} frames={len(vo.getTrajectory())} elapsed={elapsed:.2f}s")
|
||||||
print(f"output -> {OUTPUT_DIR}")
|
print(f"output -> {args.output}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Reference in New Issue
Block a user