From d8d1cc8c58b873f5a02f8d73ac9c3af99edb0a02 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 22 May 2026 17:25:13 +0300 Subject: [PATCH] Try to remove features dependency from 3d module. --- modules/3d/CMakeLists.txt | 2 +- modules/3d/src/precomp.hpp | 1 - modules/3d/src/usac/utils.cpp | 65 ++++++++++++++++++++++++++++- modules/3d/test/test_homography.cpp | 53 ++++++++++++++++++----- 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/modules/3d/CMakeLists.txt b/modules/3d/CMakeLists.txt index 323db6bc4b..95b45b1248 100644 --- a/modules/3d/CMakeLists.txt +++ b/modules/3d/CMakeLists.txt @@ -6,7 +6,7 @@ set(debug_modules "") if(DEBUG_opencv_3d) list(APPEND debug_modules opencv_highgui) endif() -ocv_define_module(3d opencv_imgproc opencv_features opencv_flann ${debug_modules} +ocv_define_module(3d opencv_imgproc opencv_flann ${debug_modules} WRAP java objc python js ) ocv_target_link_libraries(${the_module} ${LAPACK_LIBRARIES}) diff --git a/modules/3d/src/precomp.hpp b/modules/3d/src/precomp.hpp index 05d4f7ae99..4e0f2b83a6 100755 --- a/modules/3d/src/precomp.hpp +++ b/modules/3d/src/precomp.hpp @@ -64,7 +64,6 @@ #include "opencv2/3d.hpp" #include "opencv2/imgproc.hpp" -#include "opencv2/features.hpp" #include "opencv2/core/ocl.hpp" #include "opencv2/core/hal/intrin.hpp" diff --git a/modules/3d/src/usac/utils.cpp b/modules/3d/src/usac/utils.cpp index 886280928d..b5afd29104 100644 --- a/modules/3d/src/usac/utils.cpp +++ b/modules/3d/src/usac/utils.cpp @@ -611,6 +611,69 @@ float Utils::findMedian (std::vector &array) { } } +// Flann radiusMatch +struct FlannMatcher +{ + Ptr indexParams; + Ptr searchParams; + Ptr flannIndex; + + FlannMatcher( const Ptr& _indexParams, const Ptr& _searchParams ) + : indexParams(_indexParams), searchParams(_searchParams) + { + CV_Assert( _indexParams ); + CV_Assert( _searchParams ); + } + + void radiusMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, + std::vector >& matches, float maxDistance) + { + CV_Assert( maxDistance > std::numeric_limits::epsilon() ); + + if( trainDescriptors.empty() || queryDescriptors.empty() ) + return; + + matches.clear(); + + flannIndex = makePtr( trainDescriptors, *indexParams ); + + Mat indices( queryDescriptors.rows, 1, CV_32SC1, Scalar::all(-1) ); + Mat dists( queryDescriptors.rows, 1, CV_32FC1, Scalar::all(-1) ); + for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) + { + Mat queryDescriptorsRow = queryDescriptors.row(qIdx); + Mat indicesRow = indices.row(qIdx); + Mat distsRow = dists.row(qIdx); + flannIndex->radiusSearch( queryDescriptorsRow, indicesRow, distsRow, maxDistance*maxDistance, 1, *searchParams ); + } + + convertToDMatches( indices, dists, matches ); + } + + void convertToDMatches( const Mat& indices, const Mat& dists, std::vector >& matches ) + { + matches.resize( indices.rows ); + for( int i = 0; i < indices.rows; i++ ) + { + for( int j = 0; j < indices.cols; j++ ) + { + int idx = indices.at(i, j); + if( idx >= 0 ) + { + int imgIdx = 0; + int trainIdx = idx; + float dist = 0; + if (dists.type() == CV_32S) + dist = static_cast( dists.at(i,j) ); + else + dist = std::sqrt(dists.at(i,j)); + matches[i].push_back( DMatch( i, trainIdx, imgIdx, dist ) ); + } + } + } + } +}; + ///////////////////////////////// Radius Search Graph ///////////////////////////////////////////// class RadiusSearchNeighborhoodGraphImpl : public RadiusSearchNeighborhoodGraph { private: @@ -621,7 +684,7 @@ public: // Radius search OpenCV works only with float data CV_Assert(container_.type() == CV_32F); - FlannBasedMatcher flann(makePtr(num_kd_trees), makePtr(flann_search_params)); + FlannMatcher flann(makePtr(num_kd_trees), makePtr(flann_search_params)); std::vector> neighbours; flann.radiusMatch(container_, container_, neighbours, (float)radius); diff --git a/modules/3d/test/test_homography.cpp b/modules/3d/test/test_homography.cpp index ba746783dd..95a2e2fd93 100644 --- a/modules/3d/test/test_homography.cpp +++ b/modules/3d/test/test_homography.cpp @@ -42,7 +42,12 @@ //M*/ #include "test_precomp.hpp" -#include + +// #define SAVE_FEATURES 1 + +#ifdef SAVE_FEATURES +# include "opencv2/features.hpp" +#endif namespace opencv_test { namespace { @@ -604,13 +609,39 @@ TEST(Calib3d_Homography, EKcase) ASSERT_LE(err, 0.01); } +#ifdef SAVE_FEATURES +static void saveKeypoints(const std::string& fname, const std::vector points) +{ + FileStorage fs(fname, cv::FileStorage::WRITE); + ASSERT_TRUE(fs.isOpened()); + fs << "keypoints" << points; + fs.release(); +} +#else +static void loadKeypoints(const std::string& fname, std::vector& points) +{ + FileStorage fs(fname, cv::FileStorage::READ); + ASSERT_TRUE(fs.isOpened()); + fs["keypoints"] >> points; + fs.release(); +} +#endif + TEST(Calib3d_Homography, fromImages) { + const std::string points_file1 = cvtest::TS::ptr()->get_data_path() + "cv/optflow/image1.ORB.yaml.gz"; + const std::string points_file2 = cvtest::TS::ptr()->get_data_path() + "cv/optflow/image2.ORB.yaml.gz"; + + std::vector pointframe1; + std::vector pointframe2; + +#ifdef SAVE_FEATURES + vector keypoints_1, keypoints_2; + Mat descriptors_1, descriptors_2; + Mat img_1 = imread(cvtest::TS::ptr()->get_data_path() + "cv/optflow/image1.png", 0); Mat img_2 = imread(cvtest::TS::ptr()->get_data_path() + "cv/optflow/image2.png", 0); Ptr orb = ORB::create(); - vector keypoints_1, keypoints_2; - Mat descriptors_1, descriptors_2; orb->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1, false ); orb->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2, false ); @@ -637,8 +668,6 @@ TEST(Calib3d_Homography, fromImages) } //-- Localize the model - std::vector pointframe1; - std::vector pointframe2; for( int i = 0; i < (int)good_matches.size(); i++ ) { //-- Get the keypoints from the good matches @@ -646,6 +675,15 @@ TEST(Calib3d_Homography, fromImages) pointframe2.push_back( keypoints_2[ good_matches[i].trainIdx ].pt ); } + saveKeypoints(points_file1, pointframe1); + saveKeypoints(points_file2, pointframe2); +#else + loadKeypoints(points_file1, pointframe1); + loadKeypoints(points_file2, pointframe2); +#endif + ASSERT_FALSE(pointframe1.empty()); + ASSERT_FALSE(pointframe1.empty()); + Mat H0, H1, inliers0, inliers1; double min_t0 = DBL_MAX, min_t1 = DBL_MAX; for( int i = 0; i < 10; i++ ) @@ -664,11 +702,6 @@ TEST(Calib3d_Homography, fromImages) min_t1 = std::min(min_t1, t); } int ninliers1 = countNonZero(inliers1); - double freq = getTickFrequency(); - printf("nfeatures1 = %d, nfeatures2=%d, matches=%d, ninliers(RANSAC)=%d, " - "time(RANSAC)=%.2fmsec, ninliers(RHO)=%d, time(RHO)=%.2fmsec\n", - (int)keypoints_1.size(), (int)keypoints_2.size(), - (int)good_matches.size(), ninliers0, min_t0*1000./freq, ninliers1, min_t1*1000./freq); ASSERT_TRUE(!H0.empty()); ASSERT_GE(ninliers0, 80);