From a02eb6ad0f9b00a015bc3eafddc32dc570def6c2 Mon Sep 17 00:00:00 2001 From: Agrim Rai Date: Tue, 23 Jun 2026 15:14:58 +0530 Subject: [PATCH] better vo samples --- samples/slam/visual_odometry.cpp | 94 +++++++++++++++++++------------- samples/slam/visual_odometry.py | 83 +++++++++++++++++----------- 2 files changed, 108 insertions(+), 69 deletions(-) diff --git a/samples/slam/visual_odometry.cpp b/samples/slam/visual_odometry.cpp index e762686d37..001b7fb958 100644 --- a/samples/slam/visual_odometry.cpp +++ b/samples/slam/visual_odometry.cpp @@ -2,7 +2,6 @@ // 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. // Copyright (C) 2026, BigVision LLC, all rights reserved. -// Third party copyrights are property of their respective owners. #include #include @@ -12,52 +11,73 @@ using namespace cv; -static const char* ALIKED_MODEL = "/media/user/path/to/models/aliked-n16rot-top1k-640.onnx"; -static const char* LIGHTGLUE_MODEL = "/media/user/path/to/models/aliked_lightglue.onnx"; -static const char* IMAGES_DIR = "/media/user/path/to/dataset"; -static const char* OUTPUT_DIR = "vo_out"; +static const char* keys = + "{ help h | | Print help message }" + "{ aliked | | Path to ALIKED ONNX model }" + "{ lightglue | | Path to LightGlue ONNX model }" + "{ images | | 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 -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 DIST = { -0.2811, 0.0723, -0.0003, 0.0001, 0.0 }; - -static Ptr makeDetector() +int main(int argc, char** argv) { - ALIKED::Params p; - p.inputSize = Size(640, 640); - p.engine = dnn::ENGINE_NEW; - return ALIKED::create(ALIKED_MODEL, p); -} + 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"); -static Ptr makeMatcher() -{ - return LightGlueMatcher::create(LIGHTGLUE_MODEL, 0.0f, - dnn::DNN_BACKEND_DEFAULT, - dnn::DNN_TARGET_CPU); -} + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } -int main() -{ - slam::OdometryParams params; - params.minInitParallaxDeg = 1.5; - params.minInitPoints = 50; + const String alikedPath = parser.get("aliked"); + const String lightgluePath = parser.get("lightglue"); + const String imagesDir = parser.get("images"); + + if (!parser.check() || alikedPath == "" || lightgluePath == "" || imagesDir == "") + { + parser.printErrors(); + parser.printMessage(); + return 1; + } + + const String outputDir = parser.get("output"); + + const Matx33d K(parser.get("fx"), 0., parser.get("cx"), + 0., parser.get("fy"), parser.get("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("min-parallax"); + voParams.minInitPoints = parser.get("min-points"); auto vo = slam::VisualOdometry::create( - makeDetector(), makeMatcher(), - IMAGES_DIR, OUTPUT_DIR, - Mat(K), Mat(DIST), params); + detector, matcher, + imagesDir, outputDir, + Mat(K), Mat(), voParams); - const int64 t0 = getTickCount(); - const bool ok = vo->run(); + const int64 t0 = getTickCount(); + const bool ok = vo->run(); const double elapsed = (getTickCount() - t0) / getTickFrequency(); - std::cout << "run=" << (ok ? "ok" : "FAILED") + std::cout << "run=" << (ok ? "ok" : "FAILED") << " frames=" << vo->getTrajectory().size() << " elapsed=" << elapsed << "s\n" - << "output -> " << OUTPUT_DIR << "\n"; + << "output -> " << outputDir << "\n"; return ok ? 0 : 1; } diff --git a/samples/slam/visual_odometry.py b/samples/slam/visual_odometry.py index af4499da37..c949fffa04 100644 --- a/samples/slam/visual_odometry.py +++ b/samples/slam/visual_odometry.py @@ -1,55 +1,74 @@ ''' 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 numpy as np 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 -K = np.array([[718.856, 0., 607.1928], - [0., 718.856, 185.2157], - [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 build_K(fx, fy, cx, cy): + return np.array([[fx, 0., cx], + [0., fy, cy], + [0., 0., 1.]], dtype=np.float64) def main(): - params = cv.slam.OdometryParams() - params.minInitParallaxDeg = 1.5 - params.minInitPoints = 50 + parser = argparse.ArgumentParser( + description='Monocular visual odometry using ALIKED + LightGlue') + 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( - make_detector(), make_matcher(), - IMAGES_DIR, OUTPUT_DIR, - K, DIST, params) + detector, matcher, + args.images, args.output, + K, np.array([]), vo_params) t0 = time.perf_counter() ok = vo.run() elapsed = time.perf_counter() - t0 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__':