2015-11-22 18:08:32 -05:00
/*
2016-07-17 21:57:10 -04:00
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
2015-11-22 18:08:32 -05:00
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <rtabmap/core/RegistrationVis.h>
#include <rtabmap/core/util3d_motion_estimation.h>
#include <rtabmap/core/util3d_features.h>
2023-10-25 14:54:20 -07:00
#include <rtabmap/core/util3d_transforms.h>
2015-12-21 17:27:05 -05:00
#include <rtabmap/core/util3d.h>
#include <rtabmap/core/VWDictionary.h>
2016-02-15 19:35:24 -05:00
#include <rtabmap/core/util2d.h>
2015-12-21 17:27:05 -05:00
#include <rtabmap/core/Features2d.h>
2016-11-14 19:54:31 -05:00
#include <rtabmap/core/VisualWord.h>
#include <rtabmap/core/Optimizer.h>
#include <rtabmap/core/util3d_transforms.h>
2026-08-16 19:51:39 +03:00
#include <rtabmap/core/FlannIndex.h>
2015-11-22 18:08:32 -05:00
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UConversion.h>
#include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/UTimer.h>
2015-12-21 17:27:05 -05:00
#include <rtabmap/utilite/UMath.h>
2026-07-29 22:48:39 -07:00
#if CV_MAJOR_VERSION > 4
#include <opencv2/geometry.hpp>
#endif
2015-11-22 18:08:32 -05:00
2020-05-11 13:23:26 -04:00
#if defined(HAVE_OPENCV_XFEATURES2D) && (CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION==3 && CV_MINOR_VERSION >=4 && CV_SUBMINOR_VERSION >= 1))
#include <opencv2/xfeatures2d.hpp> // For GMS matcher
#endif
2024-09-13 14:22:54 -07:00
#ifdef HAVE_OPENCV_CUDAOPTFLOW
#include <opencv2/cudaoptflow.hpp>
#include <opencv2/cudaimgproc.hpp>
#endif
2016-02-22 16:13:14 -05:00
2020-05-09 21:13:41 -04:00
2021-01-17 01:56:27 -05:00
#ifdef RTABMAP_PYTHON
#include "python/PyMatcher.h"
2020-05-09 21:13:41 -04:00
#endif
2015-11-22 18:08:32 -05:00
namespace rtabmap {
2026-08-16 19:51:39 +03:00
// The dictionary strategy a Vis/CorNNType value stands for. Vis/CorNNType
// shares the values of Kp/NNStrategy for the strategies the dictionary
// implements, and extends them with matching approaches of its own, hence the
// mapping. Return VWDictionary::kNNUndef for the values RegistrationVis handles
// itself (BruteForceCrossCheck, SuperGlue, GMS).
static VWDictionary :: NNStrategy nnStrategyFromCorNNType ( int nnType )
{
// 0 to 4 are the dictionary strategies themselves, 5, 6 and 7 are the
// approaches RegistrationVis implements (BruteForceCrossCheck, SuperGlue
// and GMS), and the ones after them are dictionary strategies again, at an
// offset of the three above.
if ( nnType >= 0 && nnType <= VWDictionary :: kNNBruteForceGPU )
{
return ( VWDictionary :: NNStrategy ) nnType ;
}
if ( nnType > 7 )
{
const int strategy = nnType - 3 ;
if ( strategy < VWDictionary :: kNNUndef )
{
return ( VWDictionary :: NNStrategy ) strategy ;
}
}
return VWDictionary :: kNNUndef ;
}
std :: string RegistrationVis :: getNNTypeName ( int nnType )
{
const VWDictionary :: NNStrategy strategy = nnStrategyFromCorNNType ( nnType );
if ( strategy != VWDictionary :: kNNUndef )
{
return VWDictionary :: nnStrategyName ( strategy );
}
switch ( nnType )
{
case 5 :
return "BRUTE FORCE CROSS CHECK" ;
case 6 :
return "PY MATCHER" ;
case 7 :
return "GMS" ;
default :
return "Unknown" ;
}
}
2016-01-06 17:26:55 -05:00
RegistrationVis :: RegistrationVis ( const ParametersMap & parameters , Registration * child ) :
Registration ( parameters , child ),
2015-11-25 12:15:41 -05:00
_minInliers ( Parameters :: defaultVisMinInliers ()),
_inlierDistance ( Parameters :: defaultVisInlierDistance ()),
_iterations ( Parameters :: defaultVisIterations ()),
_refineIterations ( Parameters :: defaultVisRefineIterations ()),
_epipolarGeometryVar ( Parameters :: defaultVisEpipolarGeometryVar ()),
_estimationType ( Parameters :: defaultVisEstimationType ()),
_PnPReprojError ( Parameters :: defaultVisPnPReprojError ()),
2015-12-21 17:27:05 -05:00
_PnPFlags ( Parameters :: defaultVisPnPFlags ()),
2015-12-22 19:32:52 -05:00
_PnPRefineIterations ( Parameters :: defaultVisPnPRefineIterations ()),
2023-07-27 16:13:30 -07:00
_PnPVarMedianRatio ( Parameters :: defaultVisPnPVarianceMedianRatio ()),
2022-09-24 12:29:42 -07:00
_PnPMaxVar ( Parameters :: defaultVisPnPMaxVariance ()),
2024-02-07 14:43:16 -08:00
_PnPSplitLinearCovarianceComponents ( Parameters :: defaultVisPnPSplitLinearCovComponents ()),
2023-07-22 11:05:21 -04:00
_multiSamplingPolicy ( Parameters :: defaultVisPnPSamplingPolicy ()),
2015-12-21 17:27:05 -05:00
_correspondencesApproach ( Parameters :: defaultVisCorType ()),
_flowWinSize ( Parameters :: defaultVisCorFlowWinSize ()),
_flowIterations ( Parameters :: defaultVisCorFlowIterations ()),
_flowEps ( Parameters :: defaultVisCorFlowEps ()),
2016-01-13 09:20:15 -05:00
_flowMaxLevel ( Parameters :: defaultVisCorFlowMaxLevel ()),
2024-09-13 14:22:54 -07:00
_flowGpu ( Parameters :: defaultVisCorFlowGpu ()),
2026-03-18 12:53:14 +08:00
_flowUseMinEigenVals ( Parameters :: defaultVisCorFlowUseMinEigenVals ()),
_flowMinEigThreshold ( Parameters :: defaultVisCorFlowMinEigThreshold ()),
_flowErrorThreshold ( Parameters :: defaultVisCorFlowErrorThreshold ()),
2016-02-22 16:13:14 -05:00
_nndr ( Parameters :: defaultVisCorNNDR ()),
2020-05-09 21:13:41 -04:00
_nnType ( Parameters :: defaultVisCorNNType ()),
2020-05-11 13:23:26 -04:00
_gmsWithRotation ( Parameters :: defaultGMSWithRotation ()),
_gmsWithScale ( Parameters :: defaultGMSWithScale ()),
_gmsThresholdFactor ( Parameters :: defaultGMSThresholdFactor ()),
2016-11-14 19:54:31 -05:00
_guessWinSize ( Parameters :: defaultVisCorGuessWinSize ()),
2017-12-09 21:44:53 -05:00
_guessMatchToProjection ( Parameters :: defaultVisCorGuessMatchToProjection ()),
2018-02-01 22:17:46 -05:00
_bundleAdjustment ( Parameters :: defaultVisBundleAdjustment ()),
2019-06-16 18:54:39 -04:00
_depthAsMask ( Parameters :: defaultVisDepthAsMask ()),
2024-12-10 12:36:21 -08:00
_maskFloorThreshold ( Parameters :: defaultVisDepthMaskFloorThr ()),
2019-06-16 18:54:39 -04:00
_minInliersDistributionThr ( Parameters :: defaultVisMinInliersDistribution ()),
2020-04-16 17:59:45 -04:00
_maxInliersMeanDistance ( Parameters :: defaultVisMeanInliersDistance ()),
_detectorFrom ( 0 ),
2020-05-09 21:41:38 -04:00
_detectorTo ( 0 )
2021-01-17 01:56:27 -05:00
#ifdef RTABMAP_PYTHON
2020-05-09 21:41:38 -04:00
,
2020-05-20 09:45:39 -04:00
_pyMatcher ( 0 )
2020-05-09 21:41:38 -04:00
#endif
2015-11-22 18:08:32 -05:00
{
2015-11-25 19:56:14 -05:00
_featureParameters = Parameters :: getDefaultParameters ();
2015-12-21 17:27:05 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpNNStrategy (), _featureParameters . at ( Parameters :: kVisCorNNType ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpNndrRatio (), _featureParameters . at ( Parameters :: kVisCorNNDR ())));
2015-11-25 19:56:14 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpDetectorStrategy (), _featureParameters . at ( Parameters :: kVisFeatureType ())));
2015-12-21 17:27:05 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpMaxFeatures (), _featureParameters . at ( Parameters :: kVisMaxFeatures ())));
2024-05-20 09:21:00 +08:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSSC (), _featureParameters . at ( Parameters :: kVisSSC ())));
2015-11-25 19:56:14 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpMaxDepth (), _featureParameters . at ( Parameters :: kVisMaxDepth ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpMinDepth (), _featureParameters . at ( Parameters :: kVisMinDepth ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpRoiRatios (), _featureParameters . at ( Parameters :: kVisRoiRatios ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSubPixEps (), _featureParameters . at ( Parameters :: kVisSubPixWinSize ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSubPixIterations (), _featureParameters . at ( Parameters :: kVisSubPixIterations ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSubPixWinSize (), _featureParameters . at ( Parameters :: kVisSubPixEps ())));
2017-11-30 16:52:03 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpGridRows (), _featureParameters . at ( Parameters :: kVisGridRows ())));
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpGridCols (), _featureParameters . at ( Parameters :: kVisGridCols ())));
2016-02-15 19:35:24 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpNewWordsComparedTogether (), "false" ));
2026-08-16 19:51:39 +03:00
// The dictionary used to match descriptors (see computeTransformationImpl())
// is built once and searched once, then thrown away: the words added while
// searching it are never indexed. Nothing is gained by keeping its index
// ready to be added to, and the bookkeeping that needs costs a descriptor
// reference per feature on every registration.
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpIncrementalFlann (), "false" ));
2015-11-25 19:56:14 -05:00
2015-11-22 18:08:32 -05:00
this -> parseParameters ( parameters );
}
void RegistrationVis :: parseParameters ( const ParametersMap & parameters )
{
Registration :: parseParameters ( parameters );
2015-11-25 12:15:41 -05:00
Parameters :: parse ( parameters , Parameters :: kVisMinInliers (), _minInliers );
Parameters :: parse ( parameters , Parameters :: kVisInlierDistance (), _inlierDistance );
Parameters :: parse ( parameters , Parameters :: kVisIterations (), _iterations );
Parameters :: parse ( parameters , Parameters :: kVisRefineIterations (), _refineIterations );
Parameters :: parse ( parameters , Parameters :: kVisEstimationType (), _estimationType );
Parameters :: parse ( parameters , Parameters :: kVisEpipolarGeometryVar (), _epipolarGeometryVar );
Parameters :: parse ( parameters , Parameters :: kVisPnPReprojError (), _PnPReprojError );
Parameters :: parse ( parameters , Parameters :: kVisPnPFlags (), _PnPFlags );
2015-12-22 19:32:52 -05:00
Parameters :: parse ( parameters , Parameters :: kVisPnPRefineIterations (), _PnPRefineIterations );
2023-07-27 16:13:30 -07:00
Parameters :: parse ( parameters , Parameters :: kVisPnPVarianceMedianRatio (), _PnPVarMedianRatio );
2022-09-24 12:29:42 -07:00
Parameters :: parse ( parameters , Parameters :: kVisPnPMaxVariance (), _PnPMaxVar );
2024-02-07 14:43:16 -08:00
Parameters :: parse ( parameters , Parameters :: kVisPnPSplitLinearCovComponents (), _PnPSplitLinearCovarianceComponents );
2023-07-22 11:05:21 -04:00
Parameters :: parse ( parameters , Parameters :: kVisPnPSamplingPolicy (), _multiSamplingPolicy );
2015-12-21 17:27:05 -05:00
Parameters :: parse ( parameters , Parameters :: kVisCorType (), _correspondencesApproach );
Parameters :: parse ( parameters , Parameters :: kVisCorFlowWinSize (), _flowWinSize );
Parameters :: parse ( parameters , Parameters :: kVisCorFlowIterations (), _flowIterations );
Parameters :: parse ( parameters , Parameters :: kVisCorFlowEps (), _flowEps );
Parameters :: parse ( parameters , Parameters :: kVisCorFlowMaxLevel (), _flowMaxLevel );
2024-09-13 14:22:54 -07:00
Parameters :: parse ( parameters , Parameters :: kVisCorFlowGpu (), _flowGpu );
2026-03-18 12:53:14 +08:00
Parameters :: parse ( parameters , Parameters :: kVisCorFlowUseMinEigenVals (), _flowUseMinEigenVals );
Parameters :: parse ( parameters , Parameters :: kVisCorFlowMinEigThreshold (), _flowMinEigThreshold );
Parameters :: parse ( parameters , Parameters :: kVisCorFlowErrorThreshold (), _flowErrorThreshold );
2016-02-22 16:13:14 -05:00
Parameters :: parse ( parameters , Parameters :: kVisCorNNDR (), _nndr );
2020-05-09 21:13:41 -04:00
Parameters :: parse ( parameters , Parameters :: kVisCorNNType (), _nnType );
2020-05-11 13:23:26 -04:00
Parameters :: parse ( parameters , Parameters :: kGMSWithRotation (), _gmsWithRotation );
Parameters :: parse ( parameters , Parameters :: kGMSWithScale (), _gmsWithScale );
Parameters :: parse ( parameters , Parameters :: kGMSThresholdFactor (), _gmsThresholdFactor );
2016-02-22 16:13:14 -05:00
Parameters :: parse ( parameters , Parameters :: kVisCorGuessWinSize (), _guessWinSize );
2017-12-09 21:44:53 -05:00
Parameters :: parse ( parameters , Parameters :: kVisCorGuessMatchToProjection (), _guessMatchToProjection );
2016-11-14 19:54:31 -05:00
Parameters :: parse ( parameters , Parameters :: kVisBundleAdjustment (), _bundleAdjustment );
2018-02-01 22:17:46 -05:00
Parameters :: parse ( parameters , Parameters :: kVisDepthAsMask (), _depthAsMask );
2024-12-10 12:36:21 -08:00
Parameters :: parse ( parameters , Parameters :: kVisDepthMaskFloorThr (), _maskFloorThreshold );
2019-06-16 18:54:39 -04:00
Parameters :: parse ( parameters , Parameters :: kVisMinInliersDistribution (), _minInliersDistributionThr );
Parameters :: parse ( parameters , Parameters :: kVisMeanInliersDistance (), _maxInliersMeanDistance );
2016-11-14 19:54:31 -05:00
uInsert ( _bundleParameters , parameters );
2015-11-22 18:08:32 -05:00
2019-11-03 14:10:02 -05:00
if ( _minInliers < 6 )
{
UWARN ( "%s should be >= 6 but it is set to %d, setting to 6." , Parameters :: kVisMinInliers (). c_str (), _minInliers );
_minInliers = 6 ;
}
2015-11-25 12:15:41 -05:00
UASSERT_MSG ( _inlierDistance > 0.0f , uFormat ( "value=%f" , _inlierDistance ). c_str ());
UASSERT_MSG ( _iterations > 0 , uFormat ( "value=%d" , _iterations ). c_str ());
2015-11-25 19:56:14 -05:00
2024-09-13 14:22:54 -07:00
#ifndef HAVE_OPENCV_CUDAOPTFLOW
if ( _flowGpu )
{
UERROR ( "%s is enabled but RTAB-Map is not built with OpenCV CUDA, disabling it." , Parameters :: kVisCorFlowGpu (). c_str ());
_flowGpu = false ;
}
#endif
2020-05-09 21:13:41 -04:00
if ( _nnType == 6 )
{
2020-05-20 09:45:39 -04:00
// verify that we have Python3 support
2021-01-17 01:56:27 -05:00
#ifndef RTABMAP_PYTHON
2020-05-20 09:45:39 -04:00
UWARN ( "%s is set to 6 but RTAB-Map is not built with Python3 support, using default %d." ,
2020-05-09 21:13:41 -04:00
Parameters :: kVisCorNNType (). c_str (), Parameters :: defaultVisCorNNType ());
_nnType = Parameters :: defaultVisCorNNType ();
#else
2020-05-20 09:45:39 -04:00
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 );
2020-05-09 21:13:41 -04:00
if ( path . empty ())
{
2020-05-20 09:45:39 -04:00
UERROR ( "%s parameter should be set to use Python3 matching (%s=6), using default %d." ,
Parameters :: kPyMatcherPath (). c_str (),
2020-05-09 21:13:41 -04:00
Parameters :: kVisCorNNType (). c_str (),
Parameters :: defaultVisCorNNType ());
_nnType = Parameters :: defaultVisCorNNType ();
}
else
{
2020-05-20 09:45:39 -04:00
delete _pyMatcher ;
_pyMatcher = new PyMatcher ( path , matchThr , iterations , cuda , model );
2020-05-09 21:13:41 -04:00
}
#endif
}
2020-05-11 13:23:26 -04:00
#if !defined(HAVE_OPENCV_XFEATURES2D) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION<4 || CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<1))
else if ( _nnType == 7 )
2020-05-12 10:23:29 -04:00
{
2020-05-11 13:23:26 -04:00
UWARN ( "%s is set to 7 but RTAB-Map is not built with OpenCV's xfeatures2d support (OpenCV >= 3.4.1 also required), using default %d." ,
Parameters :: kVisCorNNType (). c_str (), Parameters :: defaultVisCorNNType ());
_nnType = Parameters :: defaultVisCorNNType ();
}
#endif
2020-05-09 21:13:41 -04:00
2015-11-25 19:56:14 -05:00
// override feature parameters
for ( ParametersMap :: const_iterator iter = parameters . begin (); iter != parameters . end (); ++ iter )
{
2015-12-17 14:13:19 -05:00
std :: string group = uSplit ( iter -> first , '/' ). front ();
if ( Parameters :: isFeatureParameter ( iter -> first ) || group . compare ( "Stereo" ) == 0 )
2015-11-25 19:56:14 -05:00
{
uInsert ( _featureParameters , ParametersPair ( iter -> first , iter -> second ));
}
}
2015-12-21 17:27:05 -05:00
if ( uContains ( parameters , Parameters :: kVisCorNNType ()))
2015-11-25 19:56:14 -05:00
{
2026-08-16 19:51:39 +03:00
const VWDictionary :: NNStrategy strategy = nnStrategyFromCorNNType ( _nnType );
if ( strategy != VWDictionary :: kNNUndef )
2020-05-09 21:13:41 -04:00
{
2026-08-16 19:51:39 +03:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpNNStrategy (), uNumber2Str (( int ) strategy )));
2020-05-09 21:13:41 -04:00
}
2015-11-25 19:56:14 -05:00
}
2015-12-21 17:27:05 -05:00
if ( uContains ( parameters , Parameters :: kVisCorNNDR ()))
2015-11-25 19:56:14 -05:00
{
2015-12-21 17:27:05 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpNndrRatio (), parameters . at ( Parameters :: kVisCorNNDR ())));
2015-11-25 19:56:14 -05:00
}
2020-09-28 10:29:14 -04:00
if ( uContains ( parameters , Parameters :: kKpByteToFloat ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpByteToFloat (), parameters . at ( Parameters :: kKpByteToFloat ())));
}
2015-11-25 19:56:14 -05:00
if ( uContains ( parameters , Parameters :: kVisFeatureType ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpDetectorStrategy (), parameters . at ( Parameters :: kVisFeatureType ())));
}
if ( uContains ( parameters , Parameters :: kVisMaxFeatures ()))
{
2015-12-21 17:27:05 -05:00
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpMaxFeatures (), parameters . at ( Parameters :: kVisMaxFeatures ())));
2015-11-25 19:56:14 -05:00
}
2024-05-20 09:21:00 +08:00
if ( uContains ( parameters , Parameters :: kVisSSC ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSSC (), parameters . at ( Parameters :: kVisSSC ())));
}
2015-11-25 19:56:14 -05:00
if ( uContains ( parameters , Parameters :: kVisMaxDepth ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpMaxDepth (), parameters . at ( Parameters :: kVisMaxDepth ())));
}
if ( uContains ( parameters , Parameters :: kVisMinDepth ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpMinDepth (), parameters . at ( Parameters :: kVisMinDepth ())));
}
if ( uContains ( parameters , Parameters :: kVisRoiRatios ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpRoiRatios (), parameters . at ( Parameters :: kVisRoiRatios ())));
}
if ( uContains ( parameters , Parameters :: kVisSubPixEps ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSubPixEps (), parameters . at ( Parameters :: kVisSubPixEps ())));
}
if ( uContains ( parameters , Parameters :: kVisSubPixIterations ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSubPixIterations (), parameters . at ( Parameters :: kVisSubPixIterations ())));
}
if ( uContains ( parameters , Parameters :: kVisSubPixWinSize ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpSubPixWinSize (), parameters . at ( Parameters :: kVisSubPixWinSize ())));
}
2017-11-30 16:52:03 -05:00
if ( uContains ( parameters , Parameters :: kVisGridRows ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpGridRows (), parameters . at ( Parameters :: kVisGridRows ())));
}
if ( uContains ( parameters , Parameters :: kVisGridCols ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kKpGridCols (), parameters . at ( Parameters :: kVisGridCols ())));
}
2025-11-07 17:22:35 -08:00
if ( uContains ( parameters , Parameters :: kRtabmapWorkingDirectory ()))
{
uInsert ( _featureParameters , ParametersPair ( Parameters :: kRtabmapWorkingDirectory (), parameters . at ( Parameters :: kRtabmapWorkingDirectory ())));
}
2020-04-16 17:59:45 -04:00
delete _detectorFrom ;
delete _detectorTo ;
_detectorFrom = Feature2D :: create ( _featureParameters );
_detectorTo = Feature2D :: create ( _featureParameters );
2015-11-25 19:56:14 -05:00
}
RegistrationVis ::~ RegistrationVis ()
{
2020-04-16 17:59:45 -04:00
delete _detectorFrom ;
delete _detectorTo ;
2021-01-17 01:56:27 -05:00
#ifdef RTABMAP_PYTHON
2020-05-20 09:45:39 -04:00
delete _pyMatcher ;
2020-05-09 21:13:41 -04:00
#endif
2016-02-22 16:13:14 -05:00
}
2016-01-06 17:26:55 -05:00
Transform RegistrationVis :: computeTransformationImpl (
2015-12-21 17:27:05 -05:00
Signature & fromSignature ,
Signature & toSignature ,
2016-02-22 16:13:14 -05:00
Transform guess , // (flowMaxLevel is set to 0 when guess is used)
2016-01-06 17:26:55 -05:00
RegistrationInfo & info ) const
2015-11-22 18:08:32 -05:00
{
2015-12-21 17:27:05 -05:00
UDEBUG ( "%s=%d" , Parameters :: kVisMinInliers (). c_str (), _minInliers );
UDEBUG ( "%s=%f" , Parameters :: kVisInlierDistance (). c_str (), _inlierDistance );
UDEBUG ( "%s=%d" , Parameters :: kVisIterations (). c_str (), _iterations );
UDEBUG ( "%s=%d" , Parameters :: kVisEstimationType (). c_str (), _estimationType );
UDEBUG ( "%s=%f" , Parameters :: kVisEpipolarGeometryVar (). c_str (), _epipolarGeometryVar );
UDEBUG ( "%s=%f" , Parameters :: kVisPnPReprojError (). c_str (), _PnPReprojError );
UDEBUG ( "%s=%d" , Parameters :: kVisPnPFlags (). c_str (), _PnPFlags );
2022-09-24 12:29:42 -07:00
UDEBUG ( "%s=%f" , Parameters :: kVisPnPMaxVariance (). c_str (), _PnPMaxVar );
2026-08-06 13:32:20 -07:00
UDEBUG ( "%s=%f" , Parameters :: kVisPnPSplitLinearCovComponents (). c_str (), ( double ) _PnPSplitLinearCovarianceComponents );
UDEBUG ( "%s=%f" , Parameters :: kVisPnPVarianceMedianRatio (). c_str (), ( double ) _PnPVarMedianRatio );
2016-03-11 23:27:41 -05:00
UDEBUG ( "%s=%d" , Parameters :: kVisCorType (). c_str (), _correspondencesApproach );
2015-12-21 17:27:05 -05:00
UDEBUG ( "%s=%d" , Parameters :: kVisCorFlowWinSize (). c_str (), _flowWinSize );
UDEBUG ( "%s=%d" , Parameters :: kVisCorFlowIterations (). c_str (), _flowIterations );
UDEBUG ( "%s=%f" , Parameters :: kVisCorFlowEps (). c_str (), _flowEps );
UDEBUG ( "%s=%d" , Parameters :: kVisCorFlowMaxLevel (). c_str (), _flowMaxLevel );
2020-05-01 22:12:56 -04:00
UDEBUG ( "%s=%f" , Parameters :: kVisCorNNDR (). c_str (), _nndr );
2020-05-09 21:13:41 -04:00
UDEBUG ( "%s=%d" , Parameters :: kVisCorNNType (). c_str (), _nnType );
2020-05-19 15:01:50 -04:00
UDEBUG ( "%s=%d" , Parameters :: kVisCorGuessWinSize (). c_str (), _guessWinSize );
UDEBUG ( "%s=%d" , Parameters :: kVisCorGuessMatchToProjection (). c_str (), _guessMatchToProjection ? 1 : 0 );
2020-05-09 21:13:41 -04:00
UDEBUG ( "Feature Detector = %d" , ( int ) _detectorFrom -> getType ());
2016-11-09 16:44:08 -05:00
UDEBUG ( "guess=%s" , guess . prettyPrint (). c_str ());
2015-12-21 17:27:05 -05:00
2025-12-18 16:38:13 -08:00
UDEBUG ( "Input(%d): from=%d words, %d 3D words, %d words descriptors, %d words kpts, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d" ,
2015-12-21 17:27:05 -05:00
fromSignature . id (),
( int ) fromSignature . getWords (). size (),
( int ) fromSignature . getWords3 (). size (),
2020-10-05 17:34:32 -04:00
( int ) fromSignature . getWordsDescriptors (). rows ,
2025-12-18 16:38:13 -08:00
( int ) fromSignature . getWordsKpts (). size (),
2015-12-21 17:27:05 -05:00
( int ) fromSignature . sensorData (). keypoints (). size (),
2017-01-03 21:05:00 -05:00
( int ) fromSignature . sensorData (). keypoints3D (). size (),
2017-01-05 19:31:18 -05:00
fromSignature . sensorData (). descriptors (). rows ,
fromSignature . sensorData (). imageRaw (). cols ,
2020-04-30 14:17:46 -04:00
fromSignature . sensorData (). imageRaw (). rows ,
( int ) fromSignature . sensorData (). cameraModels (). size (),
2022-07-20 15:20:14 -04:00
( int ) fromSignature . sensorData (). stereoCameraModels (). size ());
2015-12-21 17:27:05 -05:00
2025-12-18 16:38:13 -08:00
UDEBUG ( "Input(%d): to=%d words, %d 3D words, %d words descriptors, %d words kpts, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d" ,
2015-12-21 17:27:05 -05:00
toSignature . id (),
( int ) toSignature . getWords (). size (),
( int ) toSignature . getWords3 (). size (),
2020-10-05 17:34:32 -04:00
( int ) toSignature . getWordsDescriptors (). rows ,
2025-12-18 16:38:13 -08:00
( int ) toSignature . getWordsKpts (). size (),
2015-12-21 17:27:05 -05:00
( int ) toSignature . sensorData (). keypoints (). size (),
2017-01-03 21:05:00 -05:00
( int ) toSignature . sensorData (). keypoints3D (). size (),
2017-01-05 19:31:18 -05:00
toSignature . sensorData (). descriptors (). rows ,
toSignature . sensorData (). imageRaw (). cols ,
2020-04-30 14:17:46 -04:00
toSignature . sensorData (). imageRaw (). rows ,
( int ) toSignature . sensorData (). cameraModels (). size (),
2022-07-20 15:20:14 -04:00
( int ) toSignature . sensorData (). stereoCameraModels (). size ());
2015-12-21 17:27:05 -05:00
2015-11-22 18:08:32 -05:00
std :: string msg ;
2018-02-01 22:17:46 -05:00
info . projectedIDs . clear ();
2015-11-22 18:08:32 -05:00
2015-12-21 17:27:05 -05:00
////////////////////
// Find correspondences
////////////////////
2016-02-15 19:35:24 -05:00
//recompute correspondences if descriptors are provided
if (( fromSignature . getWordsDescriptors (). empty () && toSignature . getWordsDescriptors (). empty ()) &&
( _estimationType < 2 || fromSignature . getWords (). size ()) && // required only for 2D->2D
2016-01-07 13:59:21 -05:00
( _estimationType == 0 || toSignature . getWords (). size ()) && // required only for 3D->2D or 2D->2D
fromSignature . getWords3 (). size () && // required in all estimation approaches
( _estimationType == 1 || toSignature . getWords3 (). size ())) // required only for 3D->3D and 2D->2D
2015-11-22 18:08:32 -05:00
{
2015-12-21 17:27:05 -05:00
// no need to extract new features, we have all the data we need
2025-04-13 15:58:21 -07:00
UDEBUG ( "Bypassing feature matching as descriptors are empty. We assume features are already matched." );
2015-11-22 18:08:32 -05:00
}
else
{
2015-12-21 17:27:05 -05:00
UDEBUG ( "" );
// just some checks to make sure that input data are ok
2025-09-03 16:55:54 -07:00
UASSERT ( fromSignature . getWords (). empty () ||
fromSignature . getWordsKpts (). empty () ||
( fromSignature . getWords (). size () == fromSignature . getWordsKpts (). size ()));
2016-02-22 16:13:14 -05:00
UASSERT ( fromSignature . getWords (). empty () ||
fromSignature . getWords3 (). empty () ||
2015-12-21 17:27:05 -05:00
( fromSignature . getWords (). size () == fromSignature . getWords3 (). size ()));
2016-01-05 15:44:34 -05:00
UASSERT (( int ) fromSignature . sensorData (). keypoints (). size () == fromSignature . sensorData (). descriptors (). rows ||
2020-10-05 17:34:32 -04:00
( int ) fromSignature . getWords (). size () == fromSignature . getWordsDescriptors (). rows ||
fromSignature . sensorData (). descriptors (). empty () ||
fromSignature . getWordsDescriptors (). empty () == 0 );
2025-09-03 16:55:54 -07:00
UASSERT ( toSignature . getWords (). empty () ||
toSignature . getWordsKpts (). empty () ||
( toSignature . getWords (). size () == toSignature . getWordsKpts (). size ()));
UASSERT ( toSignature . getWords (). empty () ||
toSignature . getWords3 (). empty () ||
2015-12-21 17:27:05 -05:00
( toSignature . getWords (). size () == toSignature . getWords3 (). size ()));
2016-01-05 15:44:34 -05:00
UASSERT (( int ) toSignature . sensorData (). keypoints (). size () == toSignature . sensorData (). descriptors (). rows ||
2020-10-05 17:34:32 -04:00
( int ) toSignature . getWords (). size () == toSignature . getWordsDescriptors (). rows ||
toSignature . sensorData (). descriptors (). empty () ||
toSignature . getWordsDescriptors (). empty ());
2016-02-22 16:13:14 -05:00
UASSERT ( fromSignature . sensorData (). imageRaw (). empty () ||
fromSignature . sensorData (). imageRaw (). type () == CV_8UC1 ||
2015-12-21 17:27:05 -05:00
fromSignature . sensorData (). imageRaw (). type () == CV_8UC3 );
2016-02-22 16:13:14 -05:00
UASSERT ( toSignature . sensorData (). imageRaw (). empty () ||
toSignature . sensorData (). imageRaw (). type () == CV_8UC1 ||
2015-12-21 17:27:05 -05:00
toSignature . sensorData (). imageRaw (). type () == CV_8UC3 );
std :: vector < cv :: KeyPoint > kptsFrom ;
2016-04-08 16:15:08 -04:00
cv :: Mat imageFrom = fromSignature . sensorData (). imageRaw ();
cv :: Mat imageTo = toSignature . sensorData (). imageRaw ();
2016-11-14 19:54:31 -05:00
std :: vector < int > orignalWordsFromIds ;
2020-10-05 17:34:32 -04:00
int kptsFromSource = 0 ;
2015-12-21 17:27:05 -05:00
if ( fromSignature . getWords (). empty ())
{
if ( fromSignature . sensorData (). keypoints (). empty ())
{
2016-04-08 16:15:08 -04:00
if ( ! imageFrom . empty ())
2015-12-21 17:27:05 -05:00
{
2016-04-08 16:15:08 -04:00
if ( imageFrom . channels () > 1 )
2016-02-15 19:35:24 -05:00
{
2016-02-22 16:13:14 -05:00
cv :: Mat tmp ;
2016-04-08 16:15:08 -04:00
cv :: cvtColor ( imageFrom , tmp , cv :: COLOR_BGR2GRAY );
imageFrom = tmp ;
2016-02-15 19:35:24 -05:00
}
2016-02-22 16:13:14 -05:00
cv :: Mat depthMask ;
2018-02-01 22:17:46 -05:00
if ( ! fromSignature . sensorData (). depthRaw (). empty () && _depthAsMask )
2016-02-22 16:13:14 -05:00
{
2016-04-08 16:15:08 -04:00
if ( imageFrom . rows % fromSignature . sensorData (). depthRaw (). rows == 0 &&
imageFrom . cols % fromSignature . sensorData (). depthRaw (). cols == 0 &&
imageFrom . rows / fromSignature . sensorData (). depthRaw (). rows == fromSignature . sensorData (). imageRaw (). cols / fromSignature . sensorData (). depthRaw (). cols )
2016-02-22 16:13:14 -05:00
{
2024-12-10 12:36:21 -08:00
depthMask = fromSignature . sensorData (). depthRaw ();
if ( _maskFloorThreshold != 0.0f )
{
UASSERT ( ! fromSignature . sensorData (). cameraModels (). empty ());
UDEBUG ( "Masking floor (threshold=%f)" , _maskFloorThreshold );
2026-03-28 18:47:46 -07:00
depthMask = util3d :: filterFloor ( depthMask , fromSignature . sensorData (). cameraModels (), _maskFloorThreshold );
2024-12-10 12:36:21 -08:00
UDEBUG ( "Masking floor done." );
}
depthMask = util2d :: interpolate ( depthMask , imageFrom . rows / depthMask . rows , 0.1f );
2016-02-22 16:13:14 -05:00
}
2021-06-25 12:54:52 -04:00
else
{
UWARN ( "%s is true, but RGB size (%dx%d) modulo depth size (%dx%d) is not 0. Ignoring depth mask for feature detection." ,
Parameters :: kVisDepthAsMask (). c_str (),
2024-12-10 12:36:21 -08:00
imageFrom . rows , imageFrom . cols ,
2021-06-25 12:54:52 -04:00
fromSignature . sensorData (). depthRaw (). rows , fromSignature . sensorData (). depthRaw (). cols );
}
2016-02-22 16:13:14 -05:00
}
2020-04-16 17:59:45 -04:00
kptsFrom = _detectorFrom -> generateKeypoints (
2016-04-08 16:15:08 -04:00
imageFrom ,
2016-02-22 16:13:14 -05:00
depthMask );
}
2015-12-21 17:27:05 -05:00
}
else
{
kptsFrom = fromSignature . sensorData (). keypoints ();
2020-10-05 17:34:32 -04:00
kptsFromSource = 1 ;
2015-12-21 17:27:05 -05:00
}
}
else
{
2020-10-05 17:34:32 -04:00
kptsFromSource = 2 ;
2016-11-14 19:54:31 -05:00
orignalWordsFromIds . resize ( fromSignature . getWords (). size ());
int i = 0 ;
2017-02-15 19:07:10 -05:00
bool allUniques = true ;
2020-10-05 17:34:32 -04:00
int previousIdAdded = 0 ;
kptsFrom = fromSignature . getWordsKpts ();
for ( std :: multimap < int , int >:: const_iterator iter = fromSignature . getWords (). begin (); iter != fromSignature . getWords (). end (); ++ iter )
2016-11-14 19:54:31 -05:00
{
2020-10-05 17:34:32 -04:00
UASSERT ( iter -> second >= 0 && iter -> second < ( int ) orignalWordsFromIds . size ());
orignalWordsFromIds [ iter -> second ] = iter -> first ;
if ( i > 0 && iter -> first == previousIdAdded )
2017-02-15 19:07:10 -05:00
{
allUniques = false ;
}
2020-10-05 17:34:32 -04:00
previousIdAdded = iter -> first ;
2017-02-15 19:07:10 -05:00
++ i ;
}
if ( ! allUniques )
{
UDEBUG ( "IDs are not unique, IDs will be regenerated!" );
orignalWordsFromIds . clear ();
2016-11-14 19:54:31 -05:00
}
2015-12-21 17:27:05 -05:00
}
2020-10-05 17:34:32 -04:00
std :: multimap < int , int > wordsFrom ;
std :: multimap < int , int > wordsTo ;
std :: vector < cv :: KeyPoint > wordsKptsFrom ;
std :: vector < cv :: KeyPoint > wordsKptsTo ;
std :: vector < cv :: Point3f > words3From ;
std :: vector < cv :: Point3f > words3To ;
cv :: Mat wordsDescFrom ;
cv :: Mat wordsDescTo ;
2017-11-14 16:28:47 -05:00
if ( _correspondencesApproach == 1 ) //Optical Flow
2015-12-21 17:27:05 -05:00
{
UDEBUG ( "" );
2024-09-13 14:22:54 -07:00
#ifdef HAVE_OPENCV_CUDAOPTFLOW
cv :: cuda :: GpuMat d_imageFrom ;
cv :: cuda :: GpuMat d_imageTo ;
if ( _flowGpu )
2015-12-21 17:27:05 -05:00
{
2024-09-13 14:22:54 -07:00
UDEBUG ( "GPU optical flow: preparing GPU image data..." );
d_imageFrom = fromSignature . sensorData (). imageRawGpu ();
if ( d_imageFrom . empty () && ! imageFrom . empty ()) {
d_imageFrom = cv :: cuda :: GpuMat ( imageFrom );
}
// convert to grayscale
if ( d_imageFrom . channels () > 1 ) {
cv :: cuda :: GpuMat tmp ;
cv :: cuda :: cvtColor ( d_imageFrom , tmp , cv :: COLOR_BGR2GRAY );
d_imageFrom = tmp ;
}
if ( fromSignature . sensorData (). imageRawGpu (). empty ())
{
fromSignature . sensorData (). setImageRawGpu ( d_imageFrom ); // buffer it
}
d_imageTo = toSignature . sensorData (). imageRawGpu ();
if ( d_imageTo . empty () && ! imageTo . empty ()) {
d_imageTo = cv :: cuda :: GpuMat ( imageTo );
}
// convert to grayscale
if ( d_imageTo . channels () > 1 ) {
cv :: cuda :: GpuMat tmp ;
cv :: cuda :: cvtColor ( d_imageTo , tmp , cv :: COLOR_BGR2GRAY );
d_imageTo = tmp ;
}
if ( toSignature . sensorData (). imageRawGpu (). empty ())
{
toSignature . sensorData (). setImageRawGpu ( d_imageTo ); // buffer it
}
UDEBUG ( "GPU optical flow: preparing GPU image data... done!" );
2015-12-21 17:27:05 -05:00
}
2024-09-13 14:22:54 -07:00
else
#endif
2015-12-21 17:27:05 -05:00
{
2024-09-13 14:22:54 -07:00
// convert to grayscale
if ( imageFrom . channels () > 1 )
{
cv :: Mat tmp ;
cv :: cvtColor ( imageFrom , tmp , cv :: COLOR_BGR2GRAY );
imageFrom = tmp ;
}
if ( imageTo . channels () > 1 )
{
cv :: Mat tmp ;
cv :: cvtColor ( imageTo , tmp , cv :: COLOR_BGR2GRAY );
imageTo = tmp ;
}
2015-12-21 17:27:05 -05:00
}
std :: vector < cv :: Point3f > kptsFrom3D ;
2017-01-03 21:05:00 -05:00
if ( kptsFrom . size () == fromSignature . getWords3 (). size ())
2015-12-21 17:27:05 -05:00
{
2020-10-05 17:34:32 -04:00
kptsFrom3D = fromSignature . getWords3 ();
2017-01-03 21:05:00 -05:00
}
else if ( kptsFrom . size () == fromSignature . sensorData (). keypoints3D (). size ())
{
kptsFrom3D = fromSignature . sensorData (). keypoints3D ();
2015-12-21 17:27:05 -05:00
}
else
{
2020-04-16 17:59:45 -04:00
kptsFrom3D = _detectorFrom -> generateKeypoints3D ( fromSignature . sensorData (), kptsFrom );
2015-12-21 17:27:05 -05:00
}
2017-11-14 16:28:47 -05:00
if ( ! imageFrom . empty () && ! imageTo . empty ())
2015-12-21 17:27:05 -05:00
{
2023-10-25 14:54:20 -07:00
UASSERT ( ! toSignature . sensorData (). cameraModels (). empty () || ! toSignature . sensorData (). stereoCameraModels (). empty ());
2015-12-21 17:27:05 -05:00
std :: vector < cv :: Point2f > cornersFrom ;
cv :: KeyPoint :: convert ( kptsFrom , cornersFrom );
std :: vector < cv :: Point2f > cornersTo ;
bool guessSet = ! guess . isIdentity () && ! guess . isNull ();
if ( guessSet )
{
2022-12-20 20:51:56 -08:00
if ( toSignature . sensorData (). cameraModels (). size () == 1 || toSignature . sensorData (). stereoCameraModels (). size () == 1 )
2022-07-20 15:20:14 -04:00
{
2022-12-20 20:51:56 -08:00
Transform localTransform = toSignature . sensorData (). cameraModels (). size () ? toSignature . sensorData (). cameraModels ()[ 0 ]. localTransform () : toSignature . sensorData (). stereoCameraModels ()[ 0 ]. left (). localTransform ();
2022-07-20 15:20:14 -04:00
Transform guessCameraRef = ( guess * localTransform ). inverse ();
cv :: Mat R = ( cv :: Mat_ < double > ( 3 , 3 ) <<
( double ) guessCameraRef . r11 (), ( double ) guessCameraRef . r12 (), ( double ) guessCameraRef . r13 (),
( double ) guessCameraRef . r21 (), ( double ) guessCameraRef . r22 (), ( double ) guessCameraRef . r23 (),
( double ) guessCameraRef . r31 (), ( double ) guessCameraRef . r32 (), ( double ) guessCameraRef . r33 ());
cv :: Mat rvec ( 1 , 3 , CV_64FC1 );
cv :: Rodrigues ( R , rvec );
cv :: Mat tvec = ( cv :: Mat_ < double > ( 1 , 3 ) << ( double ) guessCameraRef . x (), ( double ) guessCameraRef . y (), ( double ) guessCameraRef . z ());
2022-12-20 20:51:56 -08:00
cv :: Mat K = toSignature . sensorData (). cameraModels (). size () ? toSignature . sensorData (). cameraModels ()[ 0 ]. K () : toSignature . sensorData (). stereoCameraModels ()[ 0 ]. left (). K ();
2022-07-20 15:20:14 -04:00
cv :: projectPoints ( kptsFrom3D , rvec , tvec , K , cv :: Mat (), cornersTo );
}
else
{
2023-10-25 14:54:20 -07:00
UTimer t ;
int nCameras = toSignature . sensorData (). cameraModels (). size () ? toSignature . sensorData (). cameraModels (). size () : toSignature . sensorData (). stereoCameraModels (). size ();
cornersTo = cornersFrom ;
// compute inverse transforms one time
std :: vector < Transform > inverseTransforms ( nCameras );
for ( int c = 0 ; c < nCameras ; ++ c )
{
Transform localTransform = toSignature . sensorData (). cameraModels (). size () ? toSignature . sensorData (). cameraModels ()[ c ]. localTransform () : toSignature . sensorData (). stereoCameraModels ()[ c ]. left (). localTransform ();
inverseTransforms [ c ] = ( guess * localTransform ). inverse ();
UDEBUG ( "inverse transforms: cam %d -> %s" , c , inverseTransforms [ c ]. prettyPrint (). c_str ());
}
// Project 3D points in each camera
int inFrame = 0 ;
UASSERT ( kptsFrom3D . size () == cornersTo . size ());
int subImageWidth = toSignature . sensorData (). cameraModels (). size () ? toSignature . sensorData (). cameraModels ()[ 0 ]. imageWidth () : toSignature . sensorData (). stereoCameraModels ()[ 0 ]. left (). imageWidth ();
UASSERT ( subImageWidth > 0 );
for ( size_t i = 0 ; i < kptsFrom3D . size (); ++ i )
{
// Start from camera having the reference corner first (in case there is overlap between the cameras)
int startIndex = cornersFrom [ i ]. x / subImageWidth ;
UASSERT ( startIndex < nCameras );
2024-10-30 14:14:29 -07:00
for ( int ci = 0 ; ci < nCameras ; ++ ci )
2023-10-25 14:54:20 -07:00
{
2024-10-30 14:14:29 -07:00
int c = ( ci + startIndex ) % nCameras ;
2023-10-25 14:54:20 -07:00
const CameraModel & model = toSignature . sensorData (). cameraModels (). size () ? toSignature . sensorData (). cameraModels ()[ c ] : toSignature . sensorData (). stereoCameraModels ()[ c ]. left ();
cv :: Point3f ptsInCamFrame = util3d :: transformPoint ( kptsFrom3D [ i ], inverseTransforms [ c ]);
if ( ptsInCamFrame . z > 0 )
{
float u , v ;
model . reproject ( ptsInCamFrame . x , ptsInCamFrame . y , ptsInCamFrame . z , u , v );
if ( model . inFrame ( u , v ))
{
cornersTo [ i ]. x = u + model . imageWidth () * c ;
cornersTo [ i ]. y = v ;
++ inFrame ;
break ;
}
}
}
}
2023-10-27 17:10:10 -07:00
UDEBUG ( "Projected %d/%ld points inside %d cameras (time=%fs)" ,
2023-10-25 14:54:20 -07:00
inFrame , cornersTo . size (), nCameras , t . ticks ());
2022-07-20 15:20:14 -04:00
}
2015-12-21 17:27:05 -05:00
}
// Find features in the new left image
UDEBUG ( "guessSet = %d" , guessSet ? 1 : 0 );
std :: vector < unsigned char > status ;
2026-03-18 12:53:14 +08:00
std :: vector < float > err ;
2024-09-13 14:22:54 -07:00
#ifdef HAVE_OPENCV_CUDAOPTFLOW
if ( _flowGpu )
{
UDEBUG ( "cv::cuda::SparsePyrLKOpticalFlow transfer host to device begin" );
cv :: cuda :: GpuMat d_cornersFrom ( cornersFrom );
cv :: cuda :: GpuMat d_cornersTo ( cornersTo );
UDEBUG ( "cv::cuda::SparsePyrLKOpticalFlow transfer host to device end" );
cv :: cuda :: GpuMat d_status ;
cv :: Ptr < cv :: cuda :: SparsePyrLKOpticalFlow > d_pyrLK_sparse = cv :: cuda :: SparsePyrLKOpticalFlow :: create (
cv :: Size ( _flowWinSize , _flowWinSize ), guessSet ? 0 : _flowMaxLevel , _flowIterations , guessSet );
UDEBUG ( "cv::cuda::SparsePyrLKOpticalFlow calc begin" );
d_pyrLK_sparse -> calc ( d_imageFrom , d_imageTo , d_cornersFrom , d_cornersTo , d_status );
UDEBUG ( "cv::cuda::SparsePyrLKOpticalFlow calc end" );
UDEBUG ( "cv::cuda::SparsePyrLKOpticalFlow transfer device to host begin" );
// Transfer back data to CPU
cornersTo = std :: vector < cv :: Point2f > ( d_cornersTo . cols );
cv :: Mat matCornersTo ( 1 , d_cornersTo . cols , CV_32FC2 , ( void * ) & cornersTo [ 0 ]);
d_cornersTo . download ( matCornersTo );
status = std :: vector < unsigned char > ( d_status . cols );
cv :: Mat matStatus ( 1 , d_status . cols , CV_8UC1 , ( void * ) & status [ 0 ]);
d_status . download ( matStatus );
UDEBUG ( "cv::cuda::SparsePyrLKOpticalFlow transfer device to host end" );
}
else
#endif
{
UDEBUG ( "cv::calcOpticalFlowPyrLK() begin" );
cv :: calcOpticalFlowPyrLK (
2016-04-08 16:15:08 -04:00
imageFrom ,
imageTo ,
2015-12-21 17:27:05 -05:00
cornersFrom ,
cornersTo ,
status ,
err ,
2016-02-22 16:13:14 -05:00
cv :: Size ( _flowWinSize , _flowWinSize ),
2024-09-13 14:22:54 -07:00
guessSet ? 0 : _flowMaxLevel ,
cv :: TermCriteria ( cv :: TermCriteria :: COUNT + cv :: TermCriteria :: EPS , _flowIterations , _flowEps ),
2026-03-18 12:53:14 +08:00
( _flowUseMinEigenVals ? cv :: OPTFLOW_LK_GET_MIN_EIGENVALS : 0 ) | ( guessSet ? cv :: OPTFLOW_USE_INITIAL_FLOW : 0 ),
_flowMinEigThreshold );
2024-09-13 14:22:54 -07:00
UDEBUG ( "cv::calcOpticalFlowPyrLK() end" );
}
2015-12-21 17:27:05 -05:00
UASSERT ( kptsFrom . size () == kptsFrom3D . size ());
std :: vector < cv :: KeyPoint > kptsTo ( kptsFrom . size ());
std :: vector < cv :: Point3f > kptsFrom3DKept ( kptsFrom3D . size ());
2016-11-14 19:54:31 -05:00
std :: vector < int > orignalWordsFromIdsCpy = orignalWordsFromIds ;
2015-12-21 17:27:05 -05:00
int ki = 0 ;
2026-03-18 12:53:14 +08:00
UASSERT (( status . empty () || cornersTo . size () == status . size ()) &&
( err . empty () || cornersTo . size () == err . size ()));
2015-12-21 17:27:05 -05:00
for ( unsigned int i = 0 ; i < status . size (); ++ i )
{
if ( status [ i ] &&
2016-04-08 16:15:08 -04:00
uIsInBounds ( cornersTo [ i ]. x , 0.0f , float ( imageTo . cols )) &&
2026-03-18 12:53:14 +08:00
uIsInBounds ( cornersTo [ i ]. y , 0.0f , float ( imageTo . rows )) &&
( _flowUseMinEigenVals || err . empty () || err [ i ] < _flowErrorThreshold ))
2015-12-21 17:27:05 -05:00
{
2016-11-14 19:54:31 -05:00
if ( orignalWordsFromIdsCpy . size ())
{
orignalWordsFromIds [ ki ] = orignalWordsFromIdsCpy [ i ];
}
2015-12-21 17:27:05 -05:00
kptsFrom [ ki ] = cv :: KeyPoint ( cornersFrom [ i ], 1 );
kptsFrom3DKept [ ki ] = kptsFrom3D [ i ];
kptsTo [ ki ++ ] = cv :: KeyPoint ( cornersTo [ i ], 1 );
}
}
2016-11-14 19:54:31 -05:00
if ( orignalWordsFromIds . size ())
{
orignalWordsFromIds . resize ( ki );
}
2015-12-21 17:27:05 -05:00
kptsFrom . resize ( ki );
kptsTo . resize ( ki );
kptsFrom3DKept . resize ( ki );
2017-01-03 21:05:00 -05:00
kptsFrom3D = kptsFrom3DKept ;
2015-12-21 17:27:05 -05:00
std :: vector < cv :: Point3f > kptsTo3D ;
2025-04-14 09:52:41 -07:00
if ( _estimationType == 0 || _estimationType == 1 )
2015-12-21 17:27:05 -05:00
{
2020-04-16 17:59:45 -04:00
kptsTo3D = _detectorTo -> generateKeypoints3D ( toSignature . sensorData (), kptsTo );
2015-12-21 17:27:05 -05:00
}
UASSERT ( kptsFrom . size () == kptsFrom3DKept . size ());
UASSERT ( kptsFrom . size () == kptsTo . size ());
UASSERT ( kptsTo3D . size () == 0 || kptsTo . size () == kptsTo3D . size ());
for ( unsigned int i = 0 ; i < kptsFrom3DKept . size (); ++ i )
{
2020-10-05 17:34:32 -04:00
int id = ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ i ] : i ;
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( id , wordsFrom . size ()));
wordsKptsFrom . push_back ( kptsFrom [ i ]);
words3From . push_back ( kptsFrom3DKept [ i ]);
wordsTo . insert ( wordsTo . end (), std :: make_pair ( id , wordsTo . size ()));
wordsKptsTo . push_back ( kptsTo [ i ]);
if ( ! kptsTo3D . empty ())
2015-12-21 17:27:05 -05:00
{
2020-10-05 17:34:32 -04:00
words3To . push_back ( kptsTo3D [ i ]);
2015-12-21 17:27:05 -05:00
}
}
2017-01-03 21:05:00 -05:00
toSignature . sensorData (). setFeatures ( kptsTo , kptsTo3D , cv :: Mat ());
2015-12-21 17:27:05 -05:00
}
else
{
2017-11-14 16:28:47 -05:00
if ( imageFrom . empty ())
{
UERROR ( "Optical flow correspondences requires images in data!" );
}
2015-12-21 17:27:05 -05:00
UASSERT ( kptsFrom . size () == kptsFrom3D . size ());
for ( unsigned int i = 0 ; i < kptsFrom3D . size (); ++ i )
{
if ( util3d :: isFinite ( kptsFrom3D [ i ]))
{
2020-10-05 17:34:32 -04:00
int id = ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ i ] : i ;
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( id , wordsFrom . size ()));
wordsKptsFrom . push_back ( kptsFrom [ i ]);
words3From . push_back ( kptsFrom3D [ i ]);
2015-12-21 17:27:05 -05:00
}
}
2017-01-03 21:05:00 -05:00
toSignature . sensorData (). setFeatures ( std :: vector < cv :: KeyPoint > (), std :: vector < cv :: Point3f > (), cv :: Mat ());
2015-12-21 17:27:05 -05:00
}
2017-01-03 21:05:00 -05:00
fromSignature . sensorData (). setFeatures ( kptsFrom , kptsFrom3D , cv :: Mat ());
2015-12-21 17:27:05 -05:00
}
else // Features Matching
{
UDEBUG ( "" );
std :: vector < cv :: KeyPoint > kptsTo ;
2020-10-05 17:34:32 -04:00
int kptsToSource = 0 ;
2015-12-21 17:27:05 -05:00
if ( toSignature . getWords (). empty ())
{
if ( toSignature . sensorData (). keypoints (). empty () &&
2016-04-08 16:15:08 -04:00
! imageTo . empty ())
2015-12-21 17:27:05 -05:00
{
2016-04-08 16:15:08 -04:00
if ( imageTo . channels () > 1 )
2015-12-21 17:27:05 -05:00
{
cv :: Mat tmp ;
2016-04-08 16:15:08 -04:00
cv :: cvtColor ( imageTo , tmp , cv :: COLOR_BGR2GRAY );
imageTo = tmp ;
2015-12-21 17:27:05 -05:00
}
2016-02-15 19:35:24 -05:00
cv :: Mat depthMask ;
2018-02-01 22:17:46 -05:00
if ( ! toSignature . sensorData (). depthRaw (). empty () && _depthAsMask )
2016-02-15 19:35:24 -05:00
{
2016-04-08 16:15:08 -04:00
if ( imageTo . rows % toSignature . sensorData (). depthRaw (). rows == 0 &&
imageTo . cols % toSignature . sensorData (). depthRaw (). cols == 0 &&
imageTo . rows / toSignature . sensorData (). depthRaw (). rows == imageTo . cols / toSignature . sensorData (). depthRaw (). cols )
2016-02-15 19:35:24 -05:00
{
2024-12-10 12:36:21 -08:00
depthMask = toSignature . sensorData (). depthRaw ();
if ( _maskFloorThreshold != 0.0f )
{
UASSERT ( ! toSignature . sensorData (). cameraModels (). empty ());
UDEBUG ( "Masking floor (threshold=%f)" , _maskFloorThreshold );
2026-03-28 18:47:46 -07:00
depthMask = util3d :: filterFloor ( depthMask , toSignature . sensorData (). cameraModels (), _maskFloorThreshold );
2024-12-10 12:36:21 -08:00
UDEBUG ( "Masking floor done." );
}
depthMask = util2d :: interpolate ( depthMask , imageTo . rows / depthMask . rows , 0.1f );
2016-02-15 19:35:24 -05:00
}
2021-06-25 12:54:52 -04:00
else
{
UWARN ( "%s is true, but RGB size (%dx%d) modulo depth size (%dx%d) is not 0. Ignoring depth mask for feature detection." ,
Parameters :: kVisDepthAsMask (). c_str (),
2024-12-10 12:36:21 -08:00
imageTo . rows , imageTo . cols ,
2021-06-25 12:54:52 -04:00
toSignature . sensorData (). depthRaw (). rows , toSignature . sensorData (). depthRaw (). cols );
}
2016-02-15 19:35:24 -05:00
}
2020-04-16 17:59:45 -04:00
kptsTo = _detectorTo -> generateKeypoints (
2016-04-08 16:15:08 -04:00
imageTo ,
2016-02-15 19:35:24 -05:00
depthMask );
2015-12-21 17:27:05 -05:00
}
else
{
kptsTo = toSignature . sensorData (). keypoints ();
2020-10-05 17:34:32 -04:00
kptsToSource = 1 ;
2015-12-21 17:27:05 -05:00
}
}
else
{
2020-10-05 17:34:32 -04:00
kptsTo = toSignature . getWordsKpts ();
kptsToSource = 2 ;
2015-12-21 17:27:05 -05:00
}
// extract descriptors
2020-10-05 17:34:32 -04:00
UDEBUG ( "kptsFrom=%d kptsFromSource=%d" , ( int ) kptsFrom . size (), kptsFromSource );
UDEBUG ( "kptsTo=%d kptsToSource=%d" , ( int ) kptsTo . size (), kptsToSource );
2015-12-21 17:27:05 -05:00
cv :: Mat descriptorsFrom ;
2020-10-05 17:34:32 -04:00
if ( kptsFromSource == 2 &&
fromSignature . getWordsDescriptors (). rows &&
(( kptsFrom . empty () && fromSignature . getWordsDescriptors (). rows ) ||
fromSignature . getWordsDescriptors (). rows == ( int ) kptsFrom . size ()))
2016-02-15 19:35:24 -05:00
{
2020-10-05 17:34:32 -04:00
descriptorsFrom = fromSignature . getWordsDescriptors ();
2016-02-15 19:35:24 -05:00
}
2020-10-05 17:34:32 -04:00
else if ( kptsFromSource == 1 &&
fromSignature . sensorData (). descriptors (). rows == ( int ) kptsFrom . size ())
2016-02-22 16:13:14 -05:00
{
descriptorsFrom = fromSignature . sensorData (). descriptors ();
}
2016-04-08 16:15:08 -04:00
else if ( ! imageFrom . empty ())
2016-02-22 16:13:14 -05:00
{
2016-04-08 16:15:08 -04:00
if ( imageFrom . channels () > 1 )
2016-03-09 19:17:29 -05:00
{
cv :: Mat tmp ;
2016-04-08 16:15:08 -04:00
cv :: cvtColor ( imageFrom , tmp , cv :: COLOR_BGR2GRAY );
imageFrom = tmp ;
2016-03-09 19:17:29 -05:00
}
2019-06-16 18:54:39 -04:00
UDEBUG ( "cleared orignalWordsFromIds" );
2017-01-03 21:05:00 -05:00
orignalWordsFromIds . clear ();
2020-04-16 17:59:45 -04:00
descriptorsFrom = _detectorFrom -> generateDescriptors ( imageFrom , kptsFrom );
2016-02-22 16:13:14 -05:00
}
2016-02-15 19:35:24 -05:00
2015-12-21 17:27:05 -05:00
cv :: Mat descriptorsTo ;
2016-02-15 19:35:24 -05:00
if ( kptsTo . size ())
2015-12-21 17:27:05 -05:00
{
2020-10-05 17:34:32 -04:00
if ( kptsToSource == 2 &&
toSignature . getWordsDescriptors (). rows == ( int ) kptsTo . size ())
2016-02-15 19:35:24 -05:00
{
2020-10-05 17:34:32 -04:00
descriptorsTo = toSignature . getWordsDescriptors ();
2016-02-15 19:35:24 -05:00
}
2020-10-05 17:34:32 -04:00
else if ( kptsToSource == 1 &&
toSignature . sensorData (). descriptors (). rows == ( int ) kptsTo . size ())
2016-02-15 19:35:24 -05:00
{
descriptorsTo = toSignature . sensorData (). descriptors ();
}
2016-04-08 16:15:08 -04:00
else if ( ! imageTo . empty ())
2016-02-15 19:35:24 -05:00
{
2016-04-08 16:15:08 -04:00
if ( imageTo . channels () > 1 )
2016-03-09 19:17:29 -05:00
{
cv :: Mat tmp ;
2016-04-08 16:15:08 -04:00
cv :: cvtColor ( imageTo , tmp , cv :: COLOR_BGR2GRAY );
imageTo = tmp ;
2016-03-09 19:17:29 -05:00
}
2020-04-16 17:59:45 -04:00
descriptorsTo = _detectorTo -> generateDescriptors ( imageTo , kptsTo );
2016-02-15 19:35:24 -05:00
}
2015-12-21 17:27:05 -05:00
}
// create 3D keypoints
std :: vector < cv :: Point3f > kptsFrom3D ;
std :: vector < cv :: Point3f > kptsTo3D ;
2020-10-05 17:34:32 -04:00
if ( kptsFromSource == 2 &&
kptsFrom . size () == fromSignature . getWords3 (). size ())
2017-01-03 21:05:00 -05:00
{
2020-10-05 17:34:32 -04:00
kptsFrom3D = fromSignature . getWords3 ();
2017-01-03 21:05:00 -05:00
}
2020-10-05 17:34:32 -04:00
else if ( kptsFromSource == 1 &&
kptsFrom . size () == fromSignature . sensorData (). keypoints3D (). size ())
2017-01-03 21:05:00 -05:00
{
kptsFrom3D = fromSignature . sensorData (). keypoints3D ();
}
else
2015-12-21 17:27:05 -05:00
{
2016-03-09 19:17:29 -05:00
if ( fromSignature . getWords3 (). size () && kptsFrom . size () != fromSignature . getWords3 (). size ())
{
UWARN ( "kptsFrom (%d) is not the same size as fromSignature.getWords3() (%d), there "
2018-02-01 22:17:46 -05:00
"is maybe a problem with the logic above (getWords3() should be null or equal to kptsfrom). Regenerating kptsFrom3D..." ,
2026-08-06 13:32:20 -07:00
( int ) kptsFrom . size (),
( int ) fromSignature . getWords3 (). size ());
2017-01-03 21:05:00 -05:00
}
else if ( fromSignature . sensorData (). keypoints3D (). size () && kptsFrom . size () != fromSignature . sensorData (). keypoints3D (). size ())
{
UWARN ( "kptsFrom (%d) is not the same size as fromSignature.sensorData().keypoints3D() (%d), there "
2018-02-01 22:17:46 -05:00
"is maybe a problem with the logic above (keypoints3D should be null or equal to kptsfrom). Regenerating kptsFrom3D..." ,
2026-08-06 13:32:20 -07:00
( int ) kptsFrom . size (),
( int ) fromSignature . sensorData (). keypoints3D (). size ());
2016-03-09 19:17:29 -05:00
}
2020-04-16 17:59:45 -04:00
kptsFrom3D = _detectorFrom -> generateKeypoints3D ( fromSignature . sensorData (), kptsFrom );
2017-01-03 21:05:00 -05:00
UDEBUG ( "generated kptsFrom3D=%d" , ( int ) kptsFrom3D . size ());
2020-04-30 15:07:32 -04:00
}
2016-03-13 16:58:36 -04:00
2020-04-30 15:07:32 -04:00
if ( ! kptsFrom3D . empty () &&
( _detectorFrom -> getMinDepth () > 0.0f || _detectorFrom -> getMaxDepth () > 0.0f ) &&
2022-07-20 15:20:14 -04:00
( ! fromSignature . sensorData (). cameraModels (). empty () || ! fromSignature . sensorData (). stereoCameraModels (). empty ())) // Ignore local map from OdometryF2M
2020-04-30 15:07:32 -04:00
{
_detectorFrom -> filterKeypointsByDepth ( kptsFrom , descriptorsFrom , kptsFrom3D , _detectorFrom -> getMinDepth (), _detectorFrom -> getMaxDepth ());
2015-12-21 17:27:05 -05:00
}
2017-01-03 21:05:00 -05:00
2020-10-05 17:34:32 -04:00
if ( kptsToSource == 2 && kptsTo . size () == toSignature . getWords3 (). size ())
2015-12-21 17:27:05 -05:00
{
2020-10-05 17:34:32 -04:00
kptsTo3D = toSignature . getWords3 ();
2015-12-21 17:27:05 -05:00
}
2020-10-05 17:34:32 -04:00
else if ( kptsToSource == 1 &&
kptsTo . size () == toSignature . sensorData (). keypoints3D (). size ())
2017-01-03 21:05:00 -05:00
{
kptsTo3D = toSignature . sensorData (). keypoints3D ();
}
else
2015-12-21 17:27:05 -05:00
{
2016-03-09 19:17:29 -05:00
if ( toSignature . getWords3 (). size () && kptsTo . size () != toSignature . getWords3 (). size ())
{
UWARN ( "kptsTo (%d) is not the same size as toSignature.getWords3() (%d), there "
2018-02-01 22:17:46 -05:00
"is maybe a problem with the logic above (getWords3() should be null or equal to kptsTo). Regenerating kptsTo3D..." ,
2017-01-03 21:05:00 -05:00
( int ) kptsTo . size (),
( int ) toSignature . getWords3 (). size ());
}
else if ( toSignature . sensorData (). keypoints3D (). size () && kptsTo . size () != toSignature . sensorData (). keypoints3D (). size ())
{
UWARN ( "kptsTo (%d) is not the same size as toSignature.sensorData().keypoints3D() (%d), there "
2018-02-01 22:17:46 -05:00
"is maybe a problem with the logic above (keypoints3D() should be null or equal to kptsTo). Regenerating kptsTo3D..." ,
2017-01-03 21:05:00 -05:00
( int ) kptsTo . size (),
( int ) toSignature . sensorData (). keypoints3D (). size ());
2016-03-09 19:17:29 -05:00
}
2020-04-16 17:59:45 -04:00
kptsTo3D = _detectorTo -> generateKeypoints3D ( toSignature . sensorData (), kptsTo );
2020-04-30 15:07:32 -04:00
}
2016-03-13 16:58:36 -04:00
2020-04-30 15:07:32 -04:00
if ( kptsTo3D . size () &&
( _detectorTo -> getMinDepth () > 0.0f || _detectorTo -> getMaxDepth () > 0.0f ) &&
2022-07-20 15:20:14 -04:00
( ! toSignature . sensorData (). cameraModels (). empty () || ! toSignature . sensorData (). stereoCameraModels (). empty ())) // Ignore local map from OdometryF2M
2020-04-30 15:07:32 -04:00
{
_detectorTo -> filterKeypointsByDepth ( kptsTo , descriptorsTo , kptsTo3D , _detectorTo -> getMinDepth (), _detectorTo -> getMaxDepth ());
2015-12-21 17:27:05 -05:00
}
2016-08-12 10:59:43 -04:00
UASSERT ( kptsFrom . empty () || descriptorsFrom . rows == 0 || int ( kptsFrom . size ()) == descriptorsFrom . rows );
2016-03-08 12:31:32 -05:00
2017-01-03 21:05:00 -05:00
fromSignature . sensorData (). setFeatures ( kptsFrom , kptsFrom3D , descriptorsFrom );
toSignature . sensorData (). setFeatures ( kptsTo , kptsTo3D , descriptorsTo );
2016-02-22 16:13:14 -05:00
UDEBUG ( "descriptorsFrom=%d" , descriptorsFrom . rows );
UDEBUG ( "descriptorsTo=%d" , descriptorsTo . rows );
2019-06-16 18:54:39 -04:00
UDEBUG ( "orignalWordsFromIds=%d" , ( int ) orignalWordsFromIds . size ());
2016-02-22 16:13:14 -05:00
// We have all data we need here, so match!
if ( descriptorsFrom . rows > 0 && descriptorsTo . rows > 0 )
{
2022-07-20 15:20:14 -04:00
std :: vector < CameraModel > models ;
if ( ! toSignature . sensorData (). stereoCameraModels (). empty ())
2016-03-09 17:15:05 -05:00
{
2022-07-20 15:20:14 -04:00
for ( size_t i = 0 ; i < toSignature . sensorData (). stereoCameraModels (). size (); ++ i )
{
models . push_back ( toSignature . sensorData (). stereoCameraModels ()[ i ]. left ());
}
}
else
{
models = toSignature . sensorData (). cameraModels ();
2016-03-09 17:15:05 -05:00
}
2017-11-30 16:52:03 -05:00
2022-07-20 15:20:14 -04:00
bool isCalibrated = ! models . empty ();
for ( size_t i = 0 ; i < models . size () && isCalibrated ; ++ i )
{
isCalibrated = models [ i ]. isValidForProjection ();
// For old database formats
if ( isCalibrated && ( models [ i ]. imageWidth () == 0 || models [ i ]. imageHeight () == 0 ))
{
if ( ! toSignature . sensorData (). imageRaw (). empty ())
{
models [ i ]. setImageSize ( cv :: Size ( toSignature . sensorData (). imageRaw (). cols / models . size (), toSignature . sensorData (). imageRaw (). rows ));
}
else
{
isCalibrated = false ;
}
}
}
2016-03-09 17:15:05 -05:00
2016-02-22 16:13:14 -05:00
// If guess is set, limit the search of matches using optical flow window size
bool guessSet = ! guess . isIdentity () && ! guess . isNull ();
2016-03-09 17:15:05 -05:00
if ( guessSet && _guessWinSize > 0 && kptsFrom3D . size () &&
2020-05-19 15:01:50 -04:00
isCalibrated && // needed for projection
_estimationType != 2 ) // To make sure we match all features for 2D->2D
2016-02-22 16:13:14 -05:00
{
2022-07-20 15:20:14 -04:00
// Use guess to project 3D "from" keypoints into "to" image
2016-02-22 16:13:14 -05:00
UDEBUG ( "" );
2016-02-23 11:06:24 -05:00
UASSERT (( int ) kptsTo . size () == descriptorsTo . rows );
2016-02-24 12:45:00 -05:00
UASSERT (( int ) kptsFrom3D . size () == descriptorsFrom . rows );
2016-02-22 16:13:14 -05:00
2022-07-20 15:20:14 -04:00
std :: vector < cv :: Point2f > cornersProjected ;
std :: vector < int > projectedIndexToDescIndex ;
float subImageWidth = models [ 0 ]. imageWidth ();
std :: set < int > added ;
int duplicates = 0 ;
for ( size_t m = 0 ; m < models . size (); ++ m )
2016-02-22 16:13:14 -05:00
{
2022-07-20 15:20:14 -04:00
Transform guessCameraRef = ( guess * models [ m ]. localTransform ()). inverse ();
cv :: Mat R = ( cv :: Mat_ < double > ( 3 , 3 ) <<
( double ) guessCameraRef . r11 (), ( double ) guessCameraRef . r12 (), ( double ) guessCameraRef . r13 (),
( double ) guessCameraRef . r21 (), ( double ) guessCameraRef . r22 (), ( double ) guessCameraRef . r23 (),
( double ) guessCameraRef . r31 (), ( double ) guessCameraRef . r32 (), ( double ) guessCameraRef . r33 ());
cv :: Mat rvec ( 1 , 3 , CV_64FC1 );
cv :: Rodrigues ( R , rvec );
cv :: Mat tvec = ( cv :: Mat_ < double > ( 1 , 3 ) << ( double ) guessCameraRef . x (), ( double ) guessCameraRef . y (), ( double ) guessCameraRef . z ());
cv :: Mat K = models [ m ]. K ();
std :: vector < cv :: Point2f > projected ;
cv :: projectPoints ( kptsFrom3D , rvec , tvec , K , cv :: Mat (), projected );
UDEBUG ( "Projected points=%d" , ( int ) projected . size ());
2016-06-15 18:13:11 -04:00
2022-07-20 15:20:14 -04:00
//remove projected points outside of the image
UASSERT (( int ) projected . size () == descriptorsFrom . rows );
int cornersInFrame = 0 ;
for ( unsigned int i = 0 ; i < projected . size (); ++ i )
2016-02-22 16:13:14 -05:00
{
2022-07-20 15:20:14 -04:00
if ( uIsInBounds ( projected [ i ]. x , 0.0f , float ( models [ m ]. imageWidth () - 1 )) &&
uIsInBounds ( projected [ i ]. y , 0.0f , float ( models [ m ]. imageHeight () - 1 )) &&
util3d :: transformPoint ( kptsFrom3D [ i ], guessCameraRef ). z > 0.0 )
{
if ( added . find ( i ) != added . end ())
{
++ duplicates ;
continue ;
}
projectedIndexToDescIndex . push_back ( i );
projected [ i ]. x += subImageWidth * float ( m ); // Convert in multicam stitched image
cornersProjected . push_back ( projected [ i ]);
++ cornersInFrame ;
added . insert ( i );
}
2016-02-22 16:13:14 -05:00
}
2022-07-20 15:20:14 -04:00
UDEBUG ( "corners in frame=%d (camera index=%ld)" , cornersInFrame , m );
2016-02-22 16:13:14 -05:00
}
// For each projected feature guess of "from" in "to", find its matching feature in
// the radius around the projected guess.
// TODO: do cross-check?
2022-07-20 15:20:14 -04:00
UDEBUG ( "guessMatchToProjection=%d, cornersProjected=%d orignalWordsFromIds=%d (added=%ld, duplicates=%d)" ,
_guessMatchToProjection ? 1 : 0 , ( int ) cornersProjected . size (), ( int ) orignalWordsFromIds . size (),
added . size (), duplicates );
2016-02-22 16:13:14 -05:00
if ( cornersProjected . size ())
{
2017-12-09 21:44:53 -05:00
if ( _guessMatchToProjection )
2016-02-22 16:13:14 -05:00
{
2020-05-01 22:12:56 -04:00
UDEBUG ( "match frame to projected" );
2026-08-16 19:51:39 +03:00
// Index the projected keypoints. A rebalancing factor of 1:
// the index is thrown away with the frame, nothing is ever
// added to or removed from it. cv::Point2f being two floats,
// the points are indexed where they are.
cv :: Mat cornersProjectedMat (( int ) cornersProjected . size (), 2 , CV_32FC1 , ( void * ) cornersProjected . data ());
FlannIndex flannIndex ;
flannIndex . buildIndex ( FlannIndex :: NANOFLANN_INDEX_KDTREE_SINGLE , cornersProjectedMat , false , 1.0f );
2017-11-30 16:52:03 -05:00
std :: vector < std :: vector < size_t > > indices ;
std :: vector < std :: vector < float > > dists ;
float radius = ( float ) _guessWinSize ; // pixels
std :: vector < cv :: Point2f > pointsTo ;
cv :: KeyPoint :: convert ( kptsTo , pointsTo );
2026-08-16 19:51:39 +03:00
cv :: Mat pointsToMat (( int ) pointsTo . size (), 2 , CV_32FC1 , ( void * ) pointsTo . data ());
flannIndex . radiusSearch ( pointsToMat , indices , dists , radius );
2017-11-30 16:52:03 -05:00
2026-08-16 19:51:39 +03:00
UASSERT ( indices . size () == ( size_t ) pointsToMat . rows );
2017-11-30 16:52:03 -05:00
UASSERT ( descriptorsFrom . cols == descriptorsTo . cols );
UASSERT ( descriptorsFrom . rows == ( int ) kptsFrom . size ());
UASSERT (( int ) pointsToMat . rows == descriptorsTo . rows );
2026-08-16 19:51:39 +03:00
UASSERT ( pointsToMat . rows == ( int ) kptsTo . size ());
2017-11-30 16:52:03 -05:00
UDEBUG ( "radius search done for guess" );
// Process results (Nearest Neighbor Distance Ratio)
2020-10-05 17:34:32 -04:00
int newToId = ! orignalWordsFromIds . empty () ? fromSignature . getWords (). rbegin () -> first + 1 : descriptorsFrom . rows ;
2017-11-30 16:52:03 -05:00
std :: map < int , int > addedWordsFrom ; //<id, index>
std :: map < int , int > duplicates ; //<fromId, toId>
int newWords = 0 ;
2026-08-16 19:51:39 +03:00
// The projected words that a keypoint of the frame was found
// near, as the other branch collects them: several keypoints
// can be near the same one, hence the set. OdometryF2M uses
// them to know which words of its map are still seen.
std :: set < int > projectedIDs ;
2017-12-09 21:44:53 -05:00
cv :: Mat descriptors ( 10 , descriptorsTo . cols , descriptorsTo . type ());
2026-08-16 19:51:39 +03:00
for ( int i = 0 ; i < pointsToMat . rows ; ++ i )
2016-02-22 16:13:14 -05:00
{
2026-08-16 19:51:39 +03:00
for ( unsigned int j = 0 ; j < indices [ i ]. size (); ++ j )
{
const int projectedIndexFrom = projectedIndexToDescIndex [ indices [ i ]. at ( j )];
projectedIDs . insert ( ! orignalWordsFromIds . empty () ?
orignalWordsFromIds [ projectedIndexFrom ] : projectedIndexFrom );
}
2019-03-06 12:35:54 -05:00
int matchedIndex = - 1 ;
if ( indices [ i ]. size () >= 2 )
2016-02-22 16:13:14 -05:00
{
2019-03-06 12:35:54 -05:00
std :: vector < int > descriptorsIndices ( indices [ i ]. size ());
int oi = 0 ;
if (( int ) indices [ i ]. size () > descriptors . rows )
2016-02-24 12:45:00 -05:00
{
2019-03-06 12:35:54 -05:00
descriptors . resize ( indices [ i ]. size ());
2016-02-24 12:45:00 -05:00
}
2019-03-06 12:35:54 -05:00
for ( unsigned int j = 0 ; j < indices [ i ]. size (); ++ j )
2016-02-24 12:45:00 -05:00
{
2019-04-24 01:24:55 -04:00
descriptorsFrom . row ( projectedIndexToDescIndex [ indices [ i ]. at ( j )]). copyTo ( descriptors . row ( oi ));
descriptorsIndices [ oi ++ ] = indices [ i ]. at ( j );
2017-11-30 16:52:03 -05:00
}
2019-03-06 12:35:54 -05:00
descriptorsIndices . resize ( oi );
2019-04-24 01:24:55 -04:00
UASSERT ( oi >= 2 );
2020-05-09 21:13:41 -04:00
cv :: BFMatcher matcher ( descriptors . type () == CV_8U ? cv :: NORM_HAMMING : cv :: NORM_L2SQR , _nnType == 5 );
if ( _nnType == 5 ) // bruteforce cross check
2017-11-30 16:52:03 -05:00
{
2020-05-01 22:12:56 -04:00
std :: vector < cv :: DMatch > matches ;
matcher . match ( descriptorsTo . row ( i ), cv :: Mat ( descriptors , cv :: Range ( 0 , oi )), matches );
if ( ! matches . empty ())
{
matchedIndex = descriptorsIndices . at ( matches . at ( 0 ). trainIdx );
}
2019-03-06 12:35:54 -05:00
}
2020-05-09 21:13:41 -04:00
else // bruteforce knn
2020-05-01 22:12:56 -04:00
{
std :: vector < std :: vector < cv :: DMatch > > matches ;
matcher . knnMatch ( descriptorsTo . row ( i ), cv :: Mat ( descriptors , cv :: Range ( 0 , oi )), matches , 2 );
UASSERT ( matches . size () == 1 );
UASSERT ( matches [ 0 ]. size () == 2 );
if ( matches [ 0 ]. at ( 0 ). distance < _nndr * matches [ 0 ]. at ( 1 ). distance )
{
matchedIndex = descriptorsIndices . at ( matches [ 0 ]. at ( 0 ). trainIdx );
}
}
2019-03-06 12:35:54 -05:00
}
else if ( indices [ i ]. size () == 1 )
{
2019-04-24 01:24:55 -04:00
matchedIndex = indices [ i ]. at ( 0 );
2019-03-06 12:35:54 -05:00
}
2016-02-22 16:13:14 -05:00
2019-03-06 12:35:54 -05:00
if ( matchedIndex >= 0 )
{
matchedIndex = projectedIndexToDescIndex [ matchedIndex ];
2020-10-05 17:34:32 -04:00
int id = ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ matchedIndex ] : matchedIndex ;
2016-02-22 16:13:14 -05:00
2019-03-06 12:35:54 -05:00
if ( addedWordsFrom . find ( matchedIndex ) != addedWordsFrom . end ())
{
id = addedWordsFrom . at ( matchedIndex );
duplicates . insert ( std :: make_pair ( matchedIndex , id ));
2016-02-24 12:45:00 -05:00
}
else
{
2019-03-06 12:35:54 -05:00
addedWordsFrom . insert ( std :: make_pair ( matchedIndex , id ));
2016-02-22 16:13:14 -05:00
2020-10-05 17:34:32 -04:00
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( id , wordsFrom . size ()));
if ( ! kptsFrom . empty ())
2019-03-06 12:35:54 -05:00
{
2020-10-05 17:34:32 -04:00
wordsKptsFrom . push_back ( kptsFrom [ matchedIndex ]);
2019-03-06 12:35:54 -05:00
}
2020-10-05 17:34:32 -04:00
words3From . push_back ( kptsFrom3D [ matchedIndex ]);
wordsDescFrom . push_back ( descriptorsFrom . row ( matchedIndex ));
2016-02-24 12:45:00 -05:00
}
2019-03-06 12:35:54 -05:00
2020-10-05 17:34:32 -04:00
wordsTo . insert ( wordsTo . end (), std :: make_pair ( id , wordsTo . size ()));
wordsKptsTo . push_back ( kptsTo [ i ]);
wordsDescTo . push_back ( descriptorsTo . row ( i ));
if ( ! kptsTo3D . empty ())
2019-03-06 12:35:54 -05:00
{
2020-10-05 17:34:32 -04:00
words3To . push_back ( kptsTo3D [ i ]);
2019-03-06 12:35:54 -05:00
}
}
else
{
// gen fake ids
2020-10-05 17:34:32 -04:00
wordsTo . insert ( wordsTo . end (), std :: make_pair ( newToId , wordsTo . size ()));
wordsKptsTo . push_back ( kptsTo [ i ]);
wordsDescTo . push_back ( descriptorsTo . row ( i ));
if ( ! kptsTo3D . empty ())
2019-03-06 12:35:54 -05:00
{
2020-10-05 17:34:32 -04:00
words3To . push_back ( kptsTo3D [ i ]);
2019-03-06 12:35:54 -05:00
}
++ newToId ;
++ newWords ;
2016-02-24 12:45:00 -05:00
}
2017-11-30 16:52:03 -05:00
}
2026-08-16 19:51:39 +03:00
info . projectedIDs = std :: vector < int > ( projectedIDs . begin (), projectedIDs . end ());
UDEBUG ( "addedWordsFrom=%d/%d (duplicates=%d, newWords=%d), kptsTo=%d, wordsTo=%d, words3From=%d, projectedIDs=%d" ,
2017-11-30 16:52:03 -05:00
( int ) addedWordsFrom . size (), ( int ) cornersProjected . size (), ( int ) duplicates . size (), newWords ,
2026-08-16 19:51:39 +03:00
( int ) kptsTo . size (), ( int ) wordsTo . size (), ( int ) words3From . size (), ( int ) info . projectedIDs . size ());
2017-11-30 16:52:03 -05:00
// create fake ids for not matched words from "from"
int addWordsFromNotMatched = 0 ;
for ( unsigned int i = 0 ; i < kptsFrom3D . size (); ++ i )
{
if ( util3d :: isFinite ( kptsFrom3D [ i ]) && addedWordsFrom . find ( i ) == addedWordsFrom . end ())
{
2020-10-05 17:34:32 -04:00
int id = ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ i ] : i ;
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( id , wordsFrom . size ()));
wordsKptsFrom . push_back ( kptsFrom [ i ]);
wordsDescFrom . push_back ( descriptorsFrom . row ( i ));
words3From . push_back ( kptsFrom3D [ i ]);
2017-11-30 16:52:03 -05:00
++ addWordsFromNotMatched ;
}
}
UDEBUG ( "addWordsFromNotMatched=%d -> words3From=%d" , addWordsFromNotMatched , ( int ) words3From . size ());
}
else
{
2020-05-01 22:12:56 -04:00
UDEBUG ( "match projected to frame" );
2026-08-16 19:51:39 +03:00
// Index the frame's keypoints. A rebalancing factor of 1:
// the index is thrown away with the frame, nothing is ever
// added to or removed from it. cv::Point2f being two floats,
// the points are indexed where they are.
2017-11-30 16:52:03 -05:00
std :: vector < cv :: Point2f > pointsTo ;
cv :: KeyPoint :: convert ( kptsTo , pointsTo );
2026-08-16 19:51:39 +03:00
cv :: Mat pointsToMat (( int ) pointsTo . size (), 2 , CV_32FC1 , ( void * ) pointsTo . data ());
FlannIndex flannIndex ;
flannIndex . buildIndex ( FlannIndex :: NANOFLANN_INDEX_KDTREE_SINGLE , pointsToMat , false , 1.0f );
2017-11-30 16:52:03 -05:00
2026-08-16 19:51:39 +03:00
cv :: Mat queryMat (( int ) cornersProjected . size (), 2 , CV_32FC1 , ( void * ) cornersProjected . data ());
std :: vector < std :: vector < size_t >> indices ;
std :: vector < std :: vector < float >> dists ;
2017-11-30 16:52:03 -05:00
float radius = ( float ) _guessWinSize ; // pixels
2026-08-16 19:51:39 +03:00
flannIndex . radiusSearch ( queryMat , indices , dists , radius , 0 , 32 , 0.0 , false );
UASSERT ( indices . size () == cornersProjected . size ());
2017-11-30 16:52:03 -05:00
UASSERT (( int ) pointsToMat . rows == descriptorsTo . rows );
2026-08-16 19:51:39 +03:00
UASSERT ( pointsToMat . rows == ( int ) kptsTo . size ());
2017-11-30 16:52:03 -05:00
UDEBUG ( "radius search done for guess" );
2026-08-16 19:51:39 +03:00
2017-11-30 16:52:03 -05:00
// Process results (Nearest Neighbor Distance Ratio)
std :: set < int > addedWordsTo ;
std :: set < int > addedWordsFrom ;
2017-12-09 21:44:53 -05:00
double bruteForceTotalTime = 0.0 ;
double bruteForceDescCopy = 0.0 ;
UTimer bruteForceTimer ;
cv :: Mat descriptors ( 10 , descriptorsTo . cols , descriptorsTo . type ());
2026-08-16 19:51:39 +03:00
for ( unsigned int i = 0 ; i < cornersProjected . size (); ++ i )
2017-11-30 16:52:03 -05:00
{
int matchedIndexFrom = projectedIndexToDescIndex [ i ];
2018-02-01 22:17:46 -05:00
if ( indices [ i ]. size ())
{
2020-10-05 17:34:32 -04:00
info . projectedIDs . push_back ( ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ matchedIndexFrom ] : matchedIndexFrom );
2018-02-01 22:17:46 -05:00
}
2017-11-30 16:52:03 -05:00
if ( util3d :: isFinite ( kptsFrom3D [ matchedIndexFrom ]))
{
int matchedIndexTo = - 1 ;
if ( indices [ i ]. size () >= 2 )
{
2017-12-09 21:44:53 -05:00
bruteForceTimer . restart ();
2017-11-30 16:52:03 -05:00
std :: vector < int > descriptorsIndices ( indices [ i ]. size ());
int oi = 0 ;
2017-12-09 21:44:53 -05:00
if (( int ) indices [ i ]. size () > descriptors . rows )
{
descriptors . resize ( indices [ i ]. size ());
}
2017-11-30 16:52:03 -05:00
for ( unsigned int j = 0 ; j < indices [ i ]. size (); ++ j )
{
2019-04-24 01:24:55 -04:00
descriptorsTo . row ( indices [ i ]. at ( j )). copyTo ( descriptors . row ( oi ));
descriptorsIndices [ oi ++ ] = indices [ i ]. at ( j );
2017-11-30 16:52:03 -05:00
}
2017-12-09 21:44:53 -05:00
bruteForceDescCopy += bruteForceTimer . ticks ();
2019-04-24 01:24:55 -04:00
UASSERT ( oi >= 2 );
2020-05-09 21:13:41 -04:00
cv :: BFMatcher matcher ( descriptors . type () == CV_8U ? cv :: NORM_HAMMING : cv :: NORM_L2SQR , _nnType == 5 );
if ( _nnType == 5 ) // bruteforce cross check
2017-11-30 16:52:03 -05:00
{
2020-05-01 22:12:56 -04:00
std :: vector < cv :: DMatch > matches ;
matcher . match ( descriptorsFrom . row ( matchedIndexFrom ), cv :: Mat ( descriptors , cv :: Range ( 0 , oi )), matches );
if ( ! matches . empty ())
{
matchedIndexTo = descriptorsIndices . at ( matches . at ( 0 ). trainIdx );
}
}
2020-05-09 21:13:41 -04:00
else // bruteforce knn
2020-05-01 22:12:56 -04:00
{
std :: vector < std :: vector < cv :: DMatch > > matches ;
matcher . knnMatch ( descriptorsFrom . row ( matchedIndexFrom ), cv :: Mat ( descriptors , cv :: Range ( 0 , oi )), matches , 2 );
UASSERT ( matches . size () == 1 );
UASSERT ( matches [ 0 ]. size () == 2 );
bruteForceTotalTime += bruteForceTimer . elapsed ();
if ( matches [ 0 ]. at ( 0 ). distance < _nndr * matches [ 0 ]. at ( 1 ). distance )
{
matchedIndexTo = descriptorsIndices . at ( matches [ 0 ]. at ( 0 ). trainIdx );
}
2017-11-30 16:52:03 -05:00
}
}
else if ( indices [ i ]. size () == 1 )
{
2019-04-24 01:24:55 -04:00
matchedIndexTo = indices [ i ]. at ( 0 );
2017-11-30 16:52:03 -05:00
}
2020-10-05 17:34:32 -04:00
int id = ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ matchedIndexFrom ] : matchedIndexFrom ;
2017-12-09 21:44:53 -05:00
addedWordsFrom . insert ( addedWordsFrom . end (), matchedIndexFrom );
2017-11-30 16:52:03 -05:00
2020-10-05 17:34:32 -04:00
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( id , wordsFrom . size ()));
if ( ! kptsFrom . empty ())
2017-11-30 16:52:03 -05:00
{
2020-10-05 17:34:32 -04:00
wordsKptsFrom . push_back ( kptsFrom [ matchedIndexFrom ]);
2017-11-30 16:52:03 -05:00
}
2020-10-05 17:34:32 -04:00
words3From . push_back ( kptsFrom3D [ matchedIndexFrom ]);
wordsDescFrom . push_back ( descriptorsFrom . row ( matchedIndexFrom ));
2017-11-30 16:52:03 -05:00
2019-03-06 12:35:54 -05:00
if ( matchedIndexTo >= 0 &&
2017-11-30 16:52:03 -05:00
addedWordsTo . find ( matchedIndexTo ) == addedWordsTo . end ())
{
addedWordsTo . insert ( matchedIndexTo );
2020-10-05 17:34:32 -04:00
wordsTo . insert ( wordsTo . end (), std :: make_pair ( id , wordsTo . size ()));
wordsKptsTo . push_back ( kptsTo [ matchedIndexTo ]);
wordsDescTo . push_back ( descriptorsTo . row ( matchedIndexTo ));
if ( ! kptsTo3D . empty ())
2017-11-30 16:52:03 -05:00
{
2020-10-05 17:34:32 -04:00
words3To . push_back ( kptsTo3D [ matchedIndexTo ]);
2017-11-30 16:52:03 -05:00
}
}
}
}
2017-12-09 21:44:53 -05:00
UDEBUG ( "bruteForceDescCopy=%fs, bruteForceTotalTime=%fs" , bruteForceDescCopy , bruteForceTotalTime );
2017-11-30 16:52:03 -05:00
// create fake ids for not matched words from "from"
for ( unsigned int i = 0 ; i < kptsFrom3D . size (); ++ i )
{
if ( util3d :: isFinite ( kptsFrom3D [ i ]) && addedWordsFrom . find ( i ) == addedWordsFrom . end ())
{
2020-10-05 17:34:32 -04:00
int id = ! orignalWordsFromIds . empty () ? orignalWordsFromIds [ i ] : i ;
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( id , wordsFrom . size ()));
wordsKptsFrom . push_back ( kptsFrom [ i ]);
wordsDescFrom . push_back ( descriptorsFrom . row ( i ));
words3From . push_back ( kptsFrom3D [ i ]);
2017-11-30 16:52:03 -05:00
}
}
2020-10-05 17:34:32 -04:00
int newToId = ! orignalWordsFromIds . empty () ? fromSignature . getWords (). rbegin () -> first + 1 : descriptorsFrom . rows ;
2017-11-30 16:52:03 -05:00
for ( unsigned int i = 0 ; i < kptsTo . size (); ++ i )
{
2019-04-22 19:50:41 -04:00
if ( addedWordsTo . find ( i ) == addedWordsTo . end ())
2016-02-24 12:45:00 -05:00
{
2020-10-05 17:34:32 -04:00
wordsTo . insert ( wordsTo . end (), std :: make_pair ( newToId , wordsTo . size ()));
wordsKptsTo . push_back ( kptsTo [ i ]);
wordsDescTo . push_back ( descriptorsTo . row ( i ));
if ( ! kptsTo3D . empty ())
2016-02-24 12:45:00 -05:00
{
2020-10-05 17:34:32 -04:00
words3To . push_back ( kptsTo3D [ i ]);
2016-02-24 12:45:00 -05:00
}
2016-02-24 17:42:23 -05:00
++ newToId ;
2016-02-24 12:45:00 -05:00
}
2016-02-22 16:13:14 -05:00
}
}
}
2016-11-27 20:10:05 -05:00
else
{
UWARN ( "All projected points are outside the camera. Guess (%s) is wrong or images are not overlapping." , guess . prettyPrint (). c_str ());
}
2016-02-22 16:13:14 -05:00
UDEBUG ( "" );
}
else
{
2016-03-09 17:15:05 -05:00
if ( guessSet && _guessWinSize > 0 && kptsFrom3D . size () && ! isCalibrated )
{
2022-07-20 15:20:14 -04:00
UWARN ( "Calibration not found! Finding correspondences "
"with the guess cannot be done, global matching is "
"done instead." );
2016-03-09 17:15:05 -05:00
}
2016-02-22 16:13:14 -05:00
UDEBUG ( "" );
// match between all descriptors
2016-11-14 19:54:31 -05:00
std :: list < int > fromWordIds ;
2020-05-01 22:12:56 -04:00
std :: list < int > toWordIds ;
2021-01-17 01:56:27 -05:00
#ifdef RTABMAP_PYTHON
2020-05-20 09:45:39 -04:00
if ( _nnType == 5 || ( _nnType == 6 && _pyMatcher ) || _nnType == 7 )
2020-05-09 21:13:41 -04:00
#else
2020-05-11 13:23:26 -04:00
if ( _nnType == 5 || _nnType == 7 ) // bruteforce cross check or GMS
2020-05-09 21:13:41 -04:00
#endif
2016-11-14 19:54:31 -05:00
{
2020-05-01 22:12:56 -04:00
std :: vector < int > fromWordIdsV ( descriptorsFrom . rows );
for ( int i = 0 ; i < descriptorsFrom . rows ; ++ i )
{
int id = i + 1 ;
if ( ! orignalWordsFromIds . empty ())
{
id = orignalWordsFromIds [ i ];
}
fromWordIds . push_back ( id );
fromWordIdsV [ i ] = id ;
}
if ( descriptorsTo . rows )
{
std :: vector < int > toWordIdsV ( descriptorsTo . rows , 0 );
std :: vector < cv :: DMatch > matches ;
2021-01-17 01:56:27 -05:00
#ifdef RTABMAP_PYTHON
2020-05-20 09:45:39 -04:00
if ( _nnType == 6 && _pyMatcher &&
2020-05-09 21:13:41 -04:00
descriptorsTo . cols == descriptorsFrom . cols &&
descriptorsTo . rows == ( int ) kptsTo . size () &&
descriptorsTo . type () == CV_32F &&
descriptorsFrom . type () == CV_32F &&
descriptorsFrom . rows == ( int ) kptsFrom . size () &&
2022-07-20 15:20:14 -04:00
models . size () == 1 )
2020-05-09 21:13:41 -04:00
{
2020-05-20 09:45:39 -04:00
UDEBUG ( "Python matching" );
2022-07-20 15:20:14 -04:00
matches = _pyMatcher -> match ( descriptorsTo , descriptorsFrom , kptsTo , kptsFrom , models [ 0 ]. imageSize ());
2020-05-09 21:13:41 -04:00
}
else
{
2020-05-20 09:45:39 -04:00
if ( _nnType == 6 && _pyMatcher )
2020-05-09 21:13:41 -04:00
{
2022-07-20 15:20:14 -04:00
UDEBUG ( "Invalid inputs for Python matching (desc type=%d, only float descriptors supported, multicam not supported), doing bruteforce matching instead." , descriptorsFrom . type ());
2020-05-09 21:13:41 -04:00
}
#else
{
#endif
2020-05-11 13:23:26 -04:00
bool doCrossCheck = true ;
#ifdef HAVE_OPENCV_XFEATURES2D
#if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION==3 && CV_MINOR_VERSION >=4 && CV_SUBMINOR_VERSION >= 1)
cv :: Size imageSizeFrom ;
if ( _nnType == 7 )
{
imageSizeFrom = imageFrom . size ();
2022-07-20 15:20:14 -04:00
if (( imageSizeFrom . height == 0 || imageSizeFrom . width == 0 ) && ( fromSignature . sensorData (). cameraModels (). size () || fromSignature . sensorData (). stereoCameraModels (). size ()))
2020-05-11 13:23:26 -04:00
{
2022-07-20 15:20:14 -04:00
imageSizeFrom = fromSignature . sensorData (). cameraModels (). size () == 1 ? fromSignature . sensorData (). cameraModels ()[ 0 ]. imageSize () : fromSignature . sensorData (). stereoCameraModels ()[ 0 ]. left (). imageSize ();
2020-05-11 13:23:26 -04:00
}
2022-07-20 15:20:14 -04:00
if ( ! models . empty () && models [ 0 ]. imageSize (). height > 0 && models [ 0 ]. imageSize (). width > 0 &&
2020-05-11 13:23:26 -04:00
imageSizeFrom . height > 0 && imageSizeFrom . width > 0 )
{
doCrossCheck = false ;
}
else
{
UDEBUG ( "Invalid inputs for GMS matching, image size should be set for both inputs, doing bruteforce matching instead." );
}
}
#endif
#endif
UDEBUG ( "BruteForce matching%s" , _nnType != 7 ? " with crosscheck" : " with GMS" );
cv :: BFMatcher matcher ( descriptorsFrom . type () == CV_8U ? cv :: NORM_HAMMING : cv :: NORM_L2SQR , doCrossCheck );
2020-05-09 21:13:41 -04:00
matcher . match ( descriptorsTo , descriptorsFrom , matches );
2020-05-11 13:23:26 -04:00
#if defined(HAVE_OPENCV_XFEATURES2D) && (CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION==3 && CV_MINOR_VERSION >=4 && CV_SUBMINOR_VERSION >= 1))
if ( ! doCrossCheck )
{
2022-07-20 15:20:14 -04:00
UASSERT ( ! models . empty ());
2020-05-11 13:23:26 -04:00
std :: vector < cv :: DMatch > matchesGMS ;
2022-07-20 15:20:14 -04:00
cv :: xfeatures2d :: matchGMS ( models [ 0 ]. imageSize (), imageSizeFrom , kptsTo , kptsFrom , matches , matchesGMS , _gmsWithRotation , _gmsWithScale , _gmsThresholdFactor );
2020-05-11 13:23:26 -04:00
matches = matchesGMS ;
}
#endif
2020-05-09 21:13:41 -04:00
}
2020-05-01 22:12:56 -04:00
for ( size_t i = 0 ; i < matches . size (); ++ i )
{
toWordIdsV [ matches [ i ]. queryIdx ] = fromWordIdsV [ matches [ i ]. trainIdx ];
}
for ( size_t i = 0 ; i < toWordIdsV . size (); ++ i )
{
int toId = toWordIdsV [ i ];
if ( toId == 0 )
{
toId = fromWordIds . back () + i + 1 ;
}
toWordIds . push_back ( toId );
}
}
2019-04-24 01:24:55 -04:00
}
else
{
2020-05-09 21:13:41 -04:00
UDEBUG ( "VWDictionary knn matching" );
2020-05-01 22:12:56 -04:00
VWDictionary dictionary ( _featureParameters );
if ( orignalWordsFromIds . empty ())
2019-04-24 01:24:55 -04:00
{
2020-05-01 22:12:56 -04:00
fromWordIds = dictionary . addNewWords ( descriptorsFrom , 1 );
}
else
{
for ( int i = 0 ; i < descriptorsFrom . rows ; ++ i )
{
int id = orignalWordsFromIds [ i ];
dictionary . addWord ( new VisualWord ( id , descriptorsFrom . row ( i ), 1 ));
fromWordIds . push_back ( id );
}
2019-04-24 01:24:55 -04:00
}
2016-02-22 16:13:14 -05:00
2020-05-01 22:12:56 -04:00
if ( descriptorsTo . rows )
{
dictionary . update ();
toWordIds = dictionary . addNewWords ( descriptorsTo , 2 );
}
dictionary . clear ( false );
2016-02-22 16:13:14 -05:00
}
std :: multiset < int > fromWordIdsSet ( fromWordIds . begin (), fromWordIds . end ());
std :: multiset < int > toWordIdsSet ( toWordIds . begin (), toWordIds . end ());
UASSERT ( kptsFrom3D . empty () || fromWordIds . size () == kptsFrom3D . size ());
2016-02-23 11:06:24 -05:00
UASSERT ( int ( fromWordIds . size ()) == descriptorsFrom . rows );
2016-02-22 16:13:14 -05:00
int i = 0 ;
for ( std :: list < int >:: iterator iter = fromWordIds . begin (); iter != fromWordIds . end (); ++ iter )
{
if ( fromWordIdsSet . count ( * iter ) == 1 )
{
2020-10-05 17:34:32 -04:00
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( * iter , wordsFrom . size ()));
if ( ! kptsFrom . empty ())
2016-11-25 19:52:34 -05:00
{
2020-10-05 17:34:32 -04:00
wordsKptsFrom . push_back ( kptsFrom [ i ]);
2016-11-25 19:52:34 -05:00
}
2020-10-05 17:34:32 -04:00
if ( ! kptsFrom3D . empty ())
2016-02-22 16:13:14 -05:00
{
2020-10-05 17:34:32 -04:00
words3From . push_back ( kptsFrom3D [ i ]);
2016-02-22 16:13:14 -05:00
}
2020-10-05 17:34:32 -04:00
wordsDescFrom . push_back ( descriptorsFrom . row ( i ));
2016-02-22 16:13:14 -05:00
}
++ i ;
}
UASSERT ( kptsTo3D . size () == 0 || kptsTo3D . size () == kptsTo . size ());
UASSERT ( toWordIds . size () == kptsTo . size ());
2016-02-23 11:06:24 -05:00
UASSERT ( int ( toWordIds . size ()) == descriptorsTo . rows );
2016-02-22 16:13:14 -05:00
i = 0 ;
for ( std :: list < int >:: iterator iter = toWordIds . begin (); iter != toWordIds . end (); ++ iter )
{
if ( toWordIdsSet . count ( * iter ) == 1 )
{
2020-10-05 17:34:32 -04:00
wordsTo . insert ( wordsTo . end (), std :: make_pair ( * iter , wordsTo . size ()));
wordsKptsTo . push_back ( kptsTo [ i ]);
wordsDescTo . push_back ( descriptorsTo . row ( i ));
if ( ! kptsTo3D . empty ())
2016-02-22 16:13:14 -05:00
{
2020-10-05 17:34:32 -04:00
words3To . push_back ( kptsTo3D [ i ]);
2016-02-22 16:13:14 -05:00
}
}
++ i ;
}
}
}
else if ( descriptorsFrom . rows )
{
//just create fake words
2016-03-01 17:33:27 -05:00
UASSERT ( kptsFrom3D . empty () || int ( kptsFrom3D . size ()) == descriptorsFrom . rows );
for ( int i = 0 ; i < descriptorsFrom . rows ; ++ i )
2016-02-22 16:13:14 -05:00
{
2020-10-05 17:34:32 -04:00
wordsFrom . insert ( wordsFrom . end (), std :: make_pair ( i , wordsFrom . size ()));
wordsKptsFrom . push_back ( kptsFrom [ i ]);
wordsDescFrom . push_back ( descriptorsFrom . row ( i ));
if ( ! kptsFrom3D . empty ())
2016-02-22 16:13:14 -05:00
{
2020-10-05 17:34:32 -04:00
words3From . push_back ( kptsFrom3D [ i ]);
2016-02-22 16:13:14 -05:00
}
}
}
2015-12-21 17:27:05 -05:00
}
2020-10-05 17:34:32 -04:00
fromSignature . setWords ( wordsFrom , wordsKptsFrom , words3From , wordsDescFrom );
toSignature . setWords ( wordsTo , wordsKptsTo , words3To , wordsDescTo );
2015-11-22 18:08:32 -05:00
}
2015-12-21 17:27:05 -05:00
/////////////////////
// Motion estimation
/////////////////////
Transform transform ;
2017-05-04 16:13:45 -04:00
cv :: Mat covariance = cv :: Mat :: eye ( 6 , 6 , CV_64FC1 );
2016-01-06 17:26:55 -05:00
int inliersCount = 0 ;
2016-01-07 13:59:21 -05:00
int matchesCount = 0 ;
2018-02-01 22:17:46 -05:00
info . inliersIDs . clear ();
info . matchesIDs . clear ();
2016-04-08 16:15:08 -04:00
if ( toSignature . getWords (). size ())
2015-11-22 18:08:32 -05:00
{
2025-04-14 09:52:41 -07:00
std :: vector < int > inliers ;
std :: vector < int > matches ;
if ( _estimationType == 2 ) // Epipolar Geometry
2015-11-22 18:08:32 -05:00
{
2025-04-14 09:52:41 -07:00
UDEBUG ( "" );
if (( toSignature . sensorData (). stereoCameraModels (). size () != 1 ||
! toSignature . sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ()) &&
( toSignature . sensorData (). cameraModels (). size () != 1 ||
! toSignature . sensorData (). cameraModels ()[ 0 ]. isValidForProjection ()))
2015-11-22 18:08:32 -05:00
{
2025-04-14 09:52:41 -07:00
UERROR ( "Calibrated camera required (multi-cameras not supported)." );
2015-12-21 17:27:05 -05:00
}
2025-04-14 09:52:41 -07:00
else if (( int ) fromSignature . getWords (). size () >= _minInliers &&
( int ) toSignature . getWords (). size () >= _minInliers )
2015-12-21 17:27:05 -05:00
{
2025-04-14 09:52:41 -07:00
UASSERT (( fromSignature . sensorData (). stereoCameraModels (). size () == 1 && fromSignature . sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ()) || ( fromSignature . sensorData (). cameraModels (). size () == 1 && fromSignature . sensorData (). cameraModels ()[ 0 ]. isValidForProjection ()));
const CameraModel & cameraModel = fromSignature . sensorData (). stereoCameraModels (). size () ? fromSignature . sensorData (). stereoCameraModels ()[ 0 ]. left () : fromSignature . sensorData (). cameraModels ()[ 0 ];
// we only need the camera transform, send guess words3 for scale estimation
Transform cameraTransform ;
double variance = 1.0f ;
std :: vector < int > matchesV ;
std :: map < int , int > uniqueWordsA = uMultimapToMapUnique ( fromSignature . getWords ());
std :: map < int , int > uniqueWordsB = uMultimapToMapUnique ( toSignature . getWords ());
std :: map < int , cv :: KeyPoint > wordsA ;
std :: map < int , cv :: Point3f > words3A ;
std :: map < int , cv :: KeyPoint > wordsB ;
for ( std :: map < int , int >:: iterator iter = uniqueWordsA . begin (); iter != uniqueWordsA . end (); ++ iter )
2015-11-22 18:08:32 -05:00
{
2025-04-14 09:52:41 -07:00
wordsA . insert ( std :: make_pair ( iter -> first , fromSignature . getWordsKpts ()[ iter -> second ]));
if ( ! fromSignature . getWords3 (). empty ())
{
words3A . insert ( std :: make_pair ( iter -> first , fromSignature . getWords3 ()[ iter -> second ]));
}
2015-12-21 17:27:05 -05:00
}
2025-04-14 09:52:41 -07:00
for ( std :: map < int , int >:: iterator iter = uniqueWordsB . begin (); iter != uniqueWordsB . end (); ++ iter )
2015-12-21 17:27:05 -05:00
{
2025-04-14 09:52:41 -07:00
wordsB . insert ( std :: make_pair ( iter -> first , toSignature . getWordsKpts ()[ iter -> second ]));
}
std :: map < int , cv :: Point3f > inliers3D = util3d :: generateWords3DMono (
wordsA ,
wordsB ,
cameraModel ,
cameraTransform ,
_PnPReprojError ,
0.99f ,
2026-01-17 18:21:16 -08:00
_PnPVarMedianRatio ,
2025-04-14 09:52:41 -07:00
words3A , // for scale estimation
& variance ,
& matchesV );
covariance *= variance ;
inliers = uKeys ( inliers3D );
matches = matchesV ;
2015-12-21 17:27:05 -05:00
2025-04-14 09:52:41 -07:00
if ( ! cameraTransform . isNull ())
{
if (( int ) inliers3D . size () >= _minInliers )
2020-10-05 17:34:32 -04:00
{
2025-04-14 09:52:41 -07:00
if ( variance <= _epipolarGeometryVar )
2020-10-05 17:34:32 -04:00
{
2025-04-14 09:52:41 -07:00
if ( this -> force3DoF ())
2015-12-21 17:27:05 -05:00
{
2025-04-14 09:52:41 -07:00
transform = cameraTransform . to3DoF ();
2015-12-21 17:27:05 -05:00
}
else
{
2025-04-14 09:52:41 -07:00
transform = cameraTransform ;
2015-12-21 17:27:05 -05:00
}
}
else
{
2025-04-14 09:52:41 -07:00
msg = uFormat ( "Variance is too high! (Max %s=%f, variance=%f)" , Parameters :: kVisEpipolarGeometryVar (). c_str (), _epipolarGeometryVar , variance );
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2015-12-21 17:27:05 -05:00
}
2015-11-22 18:08:32 -05:00
}
else
{
2025-04-14 09:52:41 -07:00
msg = uFormat ( "Not enough inliers %d < %d" , ( int ) inliers3D . size (), _minInliers );
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2015-12-21 17:27:05 -05:00
}
}
else
{
2025-04-14 09:52:41 -07:00
msg = uFormat ( "No camera transform found" );
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2015-12-21 17:27:05 -05:00
}
}
2025-12-03 21:52:47 -08:00
else
2015-12-21 17:27:05 -05:00
{
2025-12-03 21:52:47 -08:00
msg = uFormat ( "No enough features < %s=%d (from=%d to=%d)" ,
Parameters :: kVisMinInliers (). c_str (), _minInliers , ( int ) fromSignature . getWords (). size (), ( int ) toSignature . getWords (). size ());
2026-08-06 13:32:20 -07:00
UWARN ( "%s" , msg . c_str ());
2025-04-14 09:52:41 -07:00
}
}
else if ( _estimationType == 1 ) // PnP
{
UDEBUG ( "" );
if (( toSignature . sensorData (). stereoCameraModels (). empty () || ! toSignature . sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ()) &&
( toSignature . sensorData (). cameraModels (). empty () || ! toSignature . sensorData (). cameraModels ()[ 0 ]. isValidForProjection ()))
{
UERROR ( "Calibrated camera required. Id=%d Models=%d StereoModels=%d weight=%d" ,
toSignature . id (),
( int ) toSignature . sensorData (). cameraModels (). size (),
2026-08-06 13:32:20 -07:00
( int ) toSignature . sensorData (). stereoCameraModels (). size (),
2025-04-14 09:52:41 -07:00
toSignature . getWeight ());
}
#ifndef RTABMAP_OPENGV
else if ( toSignature . sensorData (). cameraModels (). size () > 1 )
{
UERROR ( "Multi-camera 2D-3D PnP registration is only available if rtabmap is built "
"with OpenGV dependency. Use 3D-3D registration approach instead for multi-camera." );
}
#endif
else
{
UDEBUG ( "words from3D=%d to2D=%d" , ( int ) fromSignature . getWords3 (). size (), ( int ) toSignature . getWords (). size ());
// 3D to 2D
if (( int ) fromSignature . getWords3 (). size () >= _minInliers &&
( int ) toSignature . getWords (). size () >= _minInliers )
2015-12-21 17:27:05 -05:00
{
std :: vector < int > inliersV ;
2016-01-07 13:59:21 -05:00
std :: vector < int > matchesV ;
2025-04-14 09:52:41 -07:00
std :: map < int , int > uniqueWordsA = uMultimapToMapUnique ( fromSignature . getWords ());
std :: map < int , int > uniqueWordsB = uMultimapToMapUnique ( toSignature . getWords ());
2020-10-05 17:34:32 -04:00
std :: map < int , cv :: Point3f > words3A ;
std :: map < int , cv :: Point3f > words3B ;
2025-04-14 09:52:41 -07:00
std :: map < int , cv :: KeyPoint > wordsB ;
2020-10-05 17:34:32 -04:00
for ( std :: map < int , int >:: iterator iter = uniqueWordsA . begin (); iter != uniqueWordsA . end (); ++ iter )
{
2025-04-14 09:52:41 -07:00
words3A . insert ( std :: make_pair ( iter -> first , fromSignature . getWords3 ()[ iter -> second ]));
2020-10-05 17:34:32 -04:00
}
for ( std :: map < int , int >:: iterator iter = uniqueWordsB . begin (); iter != uniqueWordsB . end (); ++ iter )
{
2025-04-14 09:52:41 -07:00
wordsB . insert ( std :: make_pair ( iter -> first , toSignature . getWordsKpts ()[ iter -> second ]));
if ( ! toSignature . getWords3 (). empty ())
{
words3B . insert ( std :: make_pair ( iter -> first , toSignature . getWords3 ()[ iter -> second ]));
}
}
std :: vector < CameraModel > models ;
if ( toSignature . sensorData (). stereoCameraModels (). size ())
{
for ( size_t i = 0 ; i < toSignature . sensorData (). stereoCameraModels (). size (); ++ i )
{
models . push_back ( toSignature . sensorData (). stereoCameraModels ()[ i ]. left ());
}
}
else
{
models = toSignature . sensorData (). cameraModels ();
}
if ( models . size () > 1 )
{
// Multi-Camera
UASSERT ( models [ 0 ]. isValidForProjection ());
std :: vector < std :: vector < int > > matchesPerCam ;
std :: vector < std :: vector < int > > inliersPerCam ;
transform = util3d :: estimateMotion3DTo2D (
words3A ,
wordsB ,
models ,
_multiSamplingPolicy ,
_minInliers ,
_iterations ,
_PnPReprojError ,
_PnPFlags ,
_PnPRefineIterations ,
_PnPVarMedianRatio ,
_PnPMaxVar ,
! guess . isNull () ? guess : Transform :: getIdentity (),
words3B ,
& covariance ,
& matchesPerCam ,
& inliersPerCam ,
_PnPSplitLinearCovarianceComponents );
info . matchesPerCam . resize ( matchesPerCam . size ());
for ( size_t i = 0 ; i < matchesPerCam . size (); ++ i )
{
matches . insert ( matches . end (), matchesPerCam [ i ]. begin (), matchesPerCam [ i ]. end ());
info . matchesPerCam [ i ] = matchesPerCam [ i ]. size ();
}
info . inliersPerCam . resize ( inliersPerCam . size ());
for ( size_t i = 0 ; i < inliersPerCam . size (); ++ i )
{
inliers . insert ( inliers . end (), inliersPerCam [ i ]. begin (), inliersPerCam [ i ]. end ());
info . inliersPerCam [ i ] = inliersPerCam [ i ]. size ();
}
}
else
{
UASSERT ( models . size () == 1 && models [ 0 ]. isValidForProjection ());
transform = util3d :: estimateMotion3DTo2D (
words3A ,
wordsB ,
models [ 0 ],
_minInliers ,
_iterations ,
_PnPReprojError ,
_PnPFlags ,
_PnPRefineIterations ,
_PnPVarMedianRatio ,
_PnPMaxVar ,
! guess . isNull () ? guess : Transform :: getIdentity (),
words3B ,
& covariance ,
& matchesV ,
& inliersV ,
_PnPSplitLinearCovarianceComponents );
inliers = inliersV ;
matches = matchesV ;
2020-10-05 17:34:32 -04:00
}
2019-03-06 12:35:54 -05:00
UDEBUG ( "inliers: %d/%d" , ( int ) inliersV . size (), ( int ) matchesV . size ());
2025-04-14 09:52:41 -07:00
if ( transform . isNull ())
2015-12-21 17:27:05 -05:00
{
2017-02-09 18:32:44 -05:00
msg = uFormat ( "Not enough inliers %d/%d (matches=%d) between %d and %d" ,
2025-04-14 09:52:41 -07:00
( int ) inliers . size (), _minInliers , ( int ) matches . size (), fromSignature . id (), toSignature . id ());
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2015-11-22 18:08:32 -05:00
}
2017-12-21 10:16:46 -05:00
else if ( this -> force3DoF ())
{
2025-04-14 09:52:41 -07:00
transform = transform . to3DoF ();
2017-12-21 10:16:46 -05:00
}
2015-11-22 18:08:32 -05:00
}
else
{
2025-04-14 09:52:41 -07:00
msg = uFormat ( "Not enough features in images (old=%d, new=%d, min=%d)" ,
( int ) fromSignature . getWords3 (). size (), ( int ) toSignature . getWords (). size (), _minInliers );
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2015-11-22 18:08:32 -05:00
}
}
2017-01-03 21:05:00 -05:00
}
2025-04-14 09:52:41 -07:00
else
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
UDEBUG ( "" );
// 3D -> 3D
if (( int ) fromSignature . getWords3 (). size () >= _minInliers &&
( int ) toSignature . getWords3 (). size () >= _minInliers )
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
std :: vector < int > inliersV ;
std :: vector < int > matchesV ;
std :: map < int , int > uniqueWordsA = uMultimapToMapUnique ( fromSignature . getWords ());
std :: map < int , int > uniqueWordsB = uMultimapToMapUnique ( toSignature . getWords ());
std :: map < int , cv :: Point3f > words3A ;
std :: map < int , cv :: Point3f > words3B ;
for ( std :: map < int , int >:: iterator iter = uniqueWordsA . begin (); iter != uniqueWordsA . end (); ++ iter )
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
words3A . insert ( std :: make_pair ( iter -> first , fromSignature . getWords3 ()[ iter -> second ]));
}
for ( std :: map < int , int >:: iterator iter = uniqueWordsB . begin (); iter != uniqueWordsB . end (); ++ iter )
{
words3B . insert ( std :: make_pair ( iter -> first , toSignature . getWords3 ()[ iter -> second ]));
}
transform = util3d :: estimateMotion3DTo3D (
words3A ,
words3B ,
_minInliers ,
_inlierDistance ,
_iterations ,
_refineIterations ,
& covariance ,
& matchesV ,
& inliersV );
inliers = inliersV ;
matches = matchesV ;
UDEBUG ( "inliers: %d/%d" , ( int ) inliersV . size (), ( int ) matchesV . size ());
if ( transform . isNull ())
{
msg = uFormat ( "Not enough inliers %d/%d (matches=%d) between %d and %d" ,
( int ) inliers . size (), _minInliers , ( int ) matches . size (), fromSignature . id (), toSignature . id ());
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2025-04-14 09:52:41 -07:00
}
else if ( this -> force3DoF ())
{
transform = transform . to3DoF ();
2017-01-03 21:05:00 -05:00
}
}
2025-04-14 09:52:41 -07:00
else
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
msg = uFormat ( "Not enough 3D features in images (old=%d, new=%d, min=%d)" ,
( int ) fromSignature . getWords3 (). size (), ( int ) toSignature . getWords3 (). size (), _minInliers );
2026-08-06 13:32:20 -07:00
UINFO ( "%s" , msg . c_str ());
2017-01-03 21:05:00 -05:00
}
2016-01-19 17:43:33 -05:00
}
2016-11-14 19:54:31 -05:00
if ( _bundleAdjustment > 0 &&
_estimationType < 2 &&
2025-04-14 09:52:41 -07:00
! transform . isNull () &&
inliers . size () &&
2016-11-14 19:54:31 -05:00
fromSignature . getWords3 (). size () &&
toSignature . getWords (). size () &&
2022-07-20 15:20:14 -04:00
( fromSignature . sensorData (). stereoCameraModels (). size () >= 1 || fromSignature . sensorData (). cameraModels (). size () >= 1 ) &&
( toSignature . sensorData (). stereoCameraModels (). size () >= 1 || toSignature . sensorData (). cameraModels (). size () >= 1 ))
2016-11-14 19:54:31 -05:00
{
2019-03-06 12:35:54 -05:00
UDEBUG ( "Refine with bundle adjustment" );
2026-08-06 13:32:20 -07:00
// _bundleAdjustment matches the Optimizer/Strategy parameter 1:1
// (1=g2o, 2=GTSAM, 3=Ceres, 4=cvsba); 0 was filtered out above.
Optimizer * sba = Optimizer :: create (
static_cast < Optimizer :: Type > ( _bundleAdjustment ), _bundleParameters );
2017-01-03 21:05:00 -05:00
2016-11-14 19:54:31 -05:00
std :: map < int , Transform > poses ;
std :: multimap < int , Link > links ;
std :: map < int , cv :: Point3f > points3DMap ;
poses . insert ( std :: make_pair ( 1 , Transform :: getIdentity ()));
2025-04-14 09:52:41 -07:00
poses . insert ( std :: make_pair ( 2 , transform ));
2016-11-14 19:54:31 -05:00
2025-04-14 09:52:41 -07:00
UASSERT ( covariance . cols == 6 && covariance . rows == 6 && covariance . type () == CV_64FC1 );
if ( covariance . at < double > ( 0 , 0 ) <= COVARIANCE_LINEAR_EPSILON )
covariance . at < double > ( 0 , 0 ) = COVARIANCE_LINEAR_EPSILON ; // epsilon if exact transform
if ( covariance . at < double > ( 1 , 1 ) <= COVARIANCE_LINEAR_EPSILON )
covariance . at < double > ( 1 , 1 ) = COVARIANCE_LINEAR_EPSILON ; // epsilon if exact transform
if ( covariance . at < double > ( 2 , 2 ) <= COVARIANCE_LINEAR_EPSILON )
covariance . at < double > ( 2 , 2 ) = COVARIANCE_LINEAR_EPSILON ; // epsilon if exact transform
if ( covariance . at < double > ( 3 , 3 ) <= COVARIANCE_ANGULAR_EPSILON )
covariance . at < double > ( 3 , 3 ) = COVARIANCE_ANGULAR_EPSILON ; // epsilon if exact transform
if ( covariance . at < double > ( 4 , 4 ) <= COVARIANCE_ANGULAR_EPSILON )
covariance . at < double > ( 4 , 4 ) = COVARIANCE_ANGULAR_EPSILON ; // epsilon if exact transform
if ( covariance . at < double > ( 5 , 5 ) <= COVARIANCE_ANGULAR_EPSILON )
covariance . at < double > ( 5 , 5 ) = COVARIANCE_ANGULAR_EPSILON ; // epsilon if exact transform
2016-11-14 19:54:31 -05:00
2025-04-14 09:52:41 -07:00
links . insert ( std :: make_pair ( 1 , Link ( 1 , 2 , Link :: kNeighbor , transform , covariance . inv ())));
2017-01-03 21:05:00 -05:00
std :: map < int , Transform > optimizedPoses ;
2022-07-20 15:20:14 -04:00
UASSERT (( toSignature . sensorData (). stereoCameraModels (). size () >= 1 && toSignature . sensorData (). stereoCameraModels ()[ 0 ]. isValidForProjection ()) ||
( toSignature . sensorData (). cameraModels (). size () >= 1 && toSignature . sensorData (). cameraModels ()[ 0 ]. isValidForProjection ()));
2017-01-03 21:05:00 -05:00
2022-07-20 15:20:14 -04:00
std :: map < int , std :: vector < CameraModel > > models ;
2017-01-03 21:05:00 -05:00
2022-07-20 15:20:14 -04:00
std :: vector < CameraModel > cameraModelsFrom ;
if ( fromSignature . sensorData (). stereoCameraModels (). size ())
2016-11-14 19:54:31 -05:00
{
2022-07-20 15:20:14 -04:00
for ( size_t i = 0 ; i < fromSignature . sensorData (). stereoCameraModels (). size (); ++ i )
{
CameraModel cameraModel = fromSignature . sensorData (). stereoCameraModels ()[ i ]. left ();
// Set Tx=-baseline*fx for Stereo BA
cameraModel = CameraModel ( cameraModel . fx (),
cameraModel . fy (),
cameraModel . cx (),
cameraModel . cy (),
cameraModel . localTransform (),
- fromSignature . sensorData (). stereoCameraModels ()[ 0 ]. baseline () * cameraModel . fx (),
cameraModel . imageSize ());
cameraModelsFrom . push_back ( cameraModel );
}
2017-01-03 21:05:00 -05:00
}
2022-07-20 15:20:14 -04:00
else
2017-01-03 21:05:00 -05:00
{
2022-07-20 15:20:14 -04:00
cameraModelsFrom = fromSignature . sensorData (). cameraModels ();
2016-11-14 19:54:31 -05:00
}
2022-07-20 15:20:14 -04:00
std :: vector < CameraModel > cameraModelsTo ;
if ( toSignature . sensorData (). stereoCameraModels (). size ())
2016-11-14 19:54:31 -05:00
{
2022-07-20 15:20:14 -04:00
for ( size_t i = 0 ; i < toSignature . sensorData (). stereoCameraModels (). size (); ++ i )
{
CameraModel cameraModel = toSignature . sensorData (). stereoCameraModels ()[ i ]. left ();
// Set Tx=-baseline*fx for Stereo BA
cameraModel = CameraModel ( cameraModel . fx (),
cameraModel . fy (),
cameraModel . cx (),
cameraModel . cy (),
cameraModel . localTransform (),
- toSignature . sensorData (). stereoCameraModels ()[ 0 ]. baseline () * cameraModel . fx (),
cameraModel . imageSize ());
cameraModelsTo . push_back ( cameraModel );
}
2017-01-03 21:05:00 -05:00
}
2022-07-20 15:20:14 -04:00
else
2017-01-03 21:05:00 -05:00
{
2022-07-20 15:20:14 -04:00
cameraModelsTo = toSignature . sensorData (). cameraModels ();
2016-11-14 19:54:31 -05:00
}
2022-07-20 15:20:14 -04:00
models . insert ( std :: make_pair ( 1 , cameraModelsFrom ));
models . insert ( std :: make_pair ( 2 , cameraModelsTo ));
2017-01-03 21:05:00 -05:00
2018-10-24 20:01:57 -04:00
std :: map < int , std :: map < int , FeatureBA > > wordReferences ;
2026-08-10 08:36:27 +08:00
BAOutliers sbaOutliers ;
2022-07-20 15:20:14 -04:00
UDEBUG ( "" );
2025-04-14 09:52:41 -07:00
for ( unsigned int i = 0 ; i < inliers . size (); ++ i )
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
int wordId = inliers [ i ];
2020-10-05 17:34:32 -04:00
int indexFrom = fromSignature . getWords (). find ( wordId ) -> second ;
const cv :: Point3f & pt3D = fromSignature . getWords3 ()[ indexFrom ];
2025-04-14 09:52:41 -07:00
UASSERT_MSG ( util3d :: isFinite ( pt3D ), uFormat ( "3D point %d is not finite!?" , wordId ). c_str ());
2020-09-15 15:18:49 -04:00
2017-01-03 21:05:00 -05:00
points3DMap . insert ( std :: make_pair ( wordId , pt3D ));
2018-10-24 20:01:57 -04:00
std :: map < int , FeatureBA > ptMap ;
2022-07-20 15:20:14 -04:00
if ( ! fromSignature . getWordsKpts (). empty ())
2017-01-03 21:05:00 -05:00
{
2022-07-20 15:20:14 -04:00
cv :: KeyPoint kpt = fromSignature . getWordsKpts ()[ indexFrom ];
int cameraIndex = 0 ;
const std :: vector < CameraModel > & cam = models . at ( 1 );
if ( cam . size () > 1 )
{
UASSERT ( cam [ 0 ]. imageWidth () > 0 );
float subImageWidth = cam [ 0 ]. imageWidth ();
cameraIndex = int ( kpt . pt . x / subImageWidth );
kpt . pt . x = kpt . pt . x - ( subImageWidth * float ( cameraIndex ));
}
UASSERT ( cam [ cameraIndex ]. isValidForProjection ());
float depthFrom = util3d :: transformPoint ( pt3D , cam [ cameraIndex ]. localTransform (). inverse ()). z ;
ptMap . insert ( std :: make_pair ( 1 , FeatureBA ( kpt , depthFrom , cv :: Mat (), cameraIndex )));
2017-01-03 21:05:00 -05:00
}
2022-07-20 15:20:14 -04:00
if ( ! toSignature . getWordsKpts (). empty ())
2017-01-03 21:05:00 -05:00
{
2020-10-05 17:34:32 -04:00
int indexTo = toSignature . getWords (). find ( wordId ) -> second ;
2022-07-20 15:20:14 -04:00
cv :: KeyPoint kpt = toSignature . getWordsKpts ()[ indexTo ];
int cameraIndex = 0 ;
const std :: vector < CameraModel > & cam = models . at ( 2 );
if ( cam . size () > 1 )
{
UASSERT ( cam [ 0 ]. imageWidth () > 0 );
float subImageWidth = cam [ 0 ]. imageWidth ();
cameraIndex = int ( kpt . pt . x / subImageWidth );
kpt . pt . x = kpt . pt . x - ( subImageWidth * float ( cameraIndex ));
}
UASSERT ( cam [ cameraIndex ]. isValidForProjection ());
2019-01-16 19:29:57 -05:00
float depthTo = 0.0f ;
2020-10-05 17:34:32 -04:00
if ( ! toSignature . getWords3 (). empty ())
2019-01-16 19:29:57 -05:00
{
2022-07-20 15:20:14 -04:00
depthTo = util3d :: transformPoint ( toSignature . getWords3 ()[ indexTo ], cam [ cameraIndex ]. localTransform (). inverse ()). z ;
2019-01-16 19:29:57 -05:00
}
2022-07-20 15:20:14 -04:00
ptMap . insert ( std :: make_pair ( 2 , FeatureBA ( kpt , depthTo , cv :: Mat (), cameraIndex )));
2017-01-03 21:05:00 -05:00
}
wordReferences . insert ( std :: make_pair ( wordId , ptMap ));
//UDEBUG("%d (%f,%f,%f)", wordId, points3DMap.at(wordId).x, points3DMap.at(wordId).y, points3DMap.at(wordId).z);
//for(std::map<int, cv::Point3f>::iterator iter=ptMap.begin(); iter!=ptMap.end(); ++iter)
//{
// UDEBUG("%d (%f,%f) d=%f", iter->first, iter->second.x, iter->second.y, iter->second.z);
//}
}
optimizedPoses = sba -> optimizeBA ( 1 , poses , links , models , points3DMap , wordReferences , & sbaOutliers );
2016-11-14 19:54:31 -05:00
delete sba ;
//update transform
if ( optimizedPoses . size () == 2 &&
! optimizedPoses . begin () -> second . isNull () &&
! optimizedPoses . rbegin () -> second . isNull ())
{
2025-04-14 09:52:41 -07:00
UDEBUG ( "Pose optimization: %s -> %s" , transform . prettyPrint (). c_str (), optimizedPoses . rbegin () -> second . prettyPrint (). c_str ());
2017-01-03 21:05:00 -05:00
2026-08-10 08:36:27 +08:00
int sbaOutliersCount = 0 ;
for ( unsigned int i = 0 ; i < inliers . size (); ++ i )
{
BAOutliers :: const_iterator iter = sbaOutliers . find ( inliers [ i ]);
if ( iter != sbaOutliers . end () && iter -> second . find ( 2 ) != iter -> second . end ())
{
++ sbaOutliersCount ;
}
}
if ( sbaOutliersCount )
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
std :: vector < int > newInliers ( inliers . size ());
2017-01-03 21:05:00 -05:00
int oi = 0 ;
2025-04-14 09:52:41 -07:00
for ( unsigned int i = 0 ; i < inliers . size (); ++ i )
2017-01-03 21:05:00 -05:00
{
2026-08-10 08:36:27 +08:00
BAOutliers :: const_iterator iter = sbaOutliers . find ( inliers [ i ]);
if ( iter == sbaOutliers . end () || iter -> second . find ( 2 ) == iter -> second . end ())
2017-01-03 21:05:00 -05:00
{
2025-04-14 09:52:41 -07:00
newInliers [ oi ++ ] = inliers [ i ];
2017-01-03 21:05:00 -05:00
}
}
newInliers . resize ( oi );
2026-08-10 08:36:27 +08:00
UDEBUG ( "BA outliers ratio %f" , float ( sbaOutliersCount ) / float ( inliers . size ()));
2025-04-14 09:52:41 -07:00
inliers = newInliers ;
2017-01-03 21:05:00 -05:00
}
2025-04-14 09:52:41 -07:00
if (( int ) inliers . size () < _minInliers )
2018-02-01 22:17:46 -05:00
{
msg = uFormat ( "Not enough inliers after bundle adjustment %d/%d (matches=%d) between %d and %d" ,
2026-08-10 08:36:27 +08:00
( int ) inliers . size (), _minInliers , ( int )( int ) inliers . size () + sbaOutliersCount , fromSignature . id (), toSignature . id ());
2025-04-14 09:52:41 -07:00
transform . setNull ();
2018-02-01 22:17:46 -05:00
}
else
{
2025-04-14 09:52:41 -07:00
transform = optimizedPoses . rbegin () -> second ;
2018-02-01 22:17:46 -05:00
}
// update 3D points, both from and to signatures
/*std::multimap<int, cv::Point3f> cpyWordsFrom3 = fromSignature.getWords3();
std::multimap<int, cv::Point3f> cpyWordsTo3 = toSignature.getWords3();
Transform invT = transforms[0].inverse();
for(std::map<int, cv::Point3f>::iterator iter=points3DMap.begin(); iter!=points3DMap.end(); ++iter)
{
cpyWordsFrom3.find(iter->first)->second = iter->second;
if(cpyWordsTo3.find(iter->first) != cpyWordsTo3.end())
{
cpyWordsTo3.find(iter->first)->second = util3d::transformPoint(iter->second, invT);
}
}
fromSignature.setWords3(cpyWordsFrom3);
toSignature.setWords3(cpyWordsTo3);*/
2016-11-14 19:54:31 -05:00
}
2018-02-11 16:07:26 -05:00
else
{
2025-04-14 09:52:41 -07:00
transform . setNull ();
2018-02-11 16:07:26 -05:00
}
2016-11-14 19:54:31 -05:00
}
2025-04-14 09:52:41 -07:00
info . inliersIDs = inliers ;
info . matchesIDs = matches ;
inliersCount = ( int ) inliers . size ();
matchesCount = ( int ) matches . size ();
2019-06-16 18:54:39 -04:00
2025-04-14 09:52:41 -07:00
if ( ! transform . isNull () && ! inliers . empty () && ( _minInliersDistributionThr > 0.0f || _maxInliersMeanDistance > 0.0f ))
2019-06-16 18:54:39 -04:00
{
cv :: Mat pcaData ;
2022-09-24 12:29:42 -07:00
std :: vector < CameraModel > cameraModelsTo ;
if ( toSignature . sensorData (). stereoCameraModels (). size ())
{
for ( size_t i = 0 ; i < toSignature . sensorData (). stereoCameraModels (). size (); ++ i )
{
cameraModelsTo . push_back ( toSignature . sensorData (). stereoCameraModels ()[ i ]. left ());
}
}
else
{
cameraModelsTo = toSignature . sensorData (). cameraModels ();
}
2019-06-16 18:54:39 -04:00
if ( _minInliersDistributionThr > 0 )
{
2022-09-24 12:29:42 -07:00
if ( cameraModelsTo . size () >= 1 && cameraModelsTo [ 0 ]. isValidForReprojection ())
2019-06-16 18:54:39 -04:00
{
2022-09-24 12:29:42 -07:00
if ( cameraModelsTo [ 0 ]. imageWidth () > 0 && cameraModelsTo [ 0 ]. imageHeight () > 0 )
2019-06-16 18:54:39 -04:00
{
2025-04-14 09:52:41 -07:00
pcaData = cv :: Mat ( inliers . size (), 2 , CV_32FC1 );
2019-06-16 18:54:39 -04:00
}
else
{
2022-09-24 12:29:42 -07:00
UERROR ( "Invalid calibration image size (%dx%d), cannot compute inliers distribution! (see %s=%f)" , cameraModelsTo [ 0 ]. imageWidth (), cameraModelsTo [ 0 ]. imageHeight (), Parameters :: kVisMinInliersDistribution (). c_str (), _minInliersDistributionThr );
2019-06-16 18:54:39 -04:00
}
}
else
{
UERROR ( "Calibration not valid, cannot compute inliers distribution! (see %s=%f)" , Parameters :: kVisMinInliersDistribution (). c_str (), _minInliersDistributionThr );
}
}
Transform transformInv = transform . inverse ();
std :: vector < float > distances ;
if ( _maxInliersMeanDistance > 0.0f )
{
2025-04-14 09:52:41 -07:00
distances . reserve ( inliers . size ());
2019-06-16 18:54:39 -04:00
}
2025-04-14 09:52:41 -07:00
for ( unsigned int i = 0 ; i < inliers . size (); ++ i )
2019-06-16 18:54:39 -04:00
{
2025-04-14 09:52:41 -07:00
std :: multimap < int , int >:: const_iterator wordsIter = toSignature . getWords (). find ( inliers [ i ]);
2023-02-10 17:29:10 -08:00
if ( wordsIter != toSignature . getWords (). end () && ! toSignature . getWordsKpts (). empty ())
2019-06-16 18:54:39 -04:00
{
2022-09-24 12:29:42 -07:00
const cv :: KeyPoint & kpt = toSignature . getWordsKpts ()[ wordsIter -> second ];
int cameraIndex = ( int )( kpt . pt . x / cameraModelsTo [ 0 ]. imageWidth ());
UASSERT_MSG ( cameraIndex < ( int ) cameraModelsTo . size (), uFormat ( "cameraIndex=%d (x=%f models=%d camera width = %d)" , cameraIndex , kpt . pt . x , ( int ) cameraModelsTo . size (), cameraModelsTo [ 0 ]. imageWidth ()). c_str ());
2023-02-10 17:29:10 -08:00
if ( _maxInliersMeanDistance > 0.0f && ! toSignature . getWords3 (). empty ())
{
const cv :: Point3f & pt = toSignature . getWords3 ()[ wordsIter -> second ];
if ( util3d :: isFinite ( pt ))
{
UASSERT ( cameraModelsTo [ cameraIndex ]. isValidForProjection ());
float depth = util3d :: transformPoint ( pt , cameraModelsTo [ cameraIndex ]. localTransform (). inverse ()). z ;
distances . push_back ( depth );
}
}
if ( ! pcaData . empty ())
{
float * ptr = pcaData . ptr < float > ( i , 0 );
ptr [ 0 ] = ( kpt . pt . x - cameraIndex * cameraModelsTo [ cameraIndex ]. imageWidth () - cameraModelsTo [ cameraIndex ]. cx ()) / cameraModelsTo [ cameraIndex ]. imageWidth ();
ptr [ 1 ] = ( kpt . pt . y - cameraModelsTo [ cameraIndex ]. cy ()) / cameraModelsTo [ cameraIndex ]. imageHeight ();
}
2019-06-16 18:54:39 -04:00
}
}
if ( ! distances . empty ())
{
info . inliersMeanDistance = uMean ( distances );
if ( info . inliersMeanDistance > _maxInliersMeanDistance )
{
2026-08-06 13:32:20 -07:00
msg = uFormat ( "The mean distance of the inliers (%f) is over %s threshold (%f)" ,
2019-06-16 18:54:39 -04:00
info . inliersMeanDistance , Parameters :: kVisMeanInliersDistance (). c_str (), _maxInliersMeanDistance );
transform . setNull ();
}
}
if ( ! transform . isNull () && ! pcaData . empty ())
{
cv :: Mat pcaEigenVectors , pcaEigenValues ;
2026-07-29 22:48:39 -07:00
cv :: PCA pca_analysis ( pcaData , cv :: Mat (), cv :: PCA :: DATA_AS_ROW );
2019-06-16 18:54:39 -04:00
// We take the second eigen value
info . inliersDistribution = pca_analysis . eigenvalues . at < float > ( 0 , 1 );
2026-02-05 15:45:29 -08:00
UDEBUG ( "Visual distribution: %f (eigen values = %f %f)" , info . inliersDistribution , pca_analysis . eigenvalues . at < float > ( 0 , 0 ), pca_analysis . eigenvalues . at < float > ( 0 , 1 ));
2019-06-16 18:54:39 -04:00
if ( info . inliersDistribution < _minInliersDistributionThr )
{
msg = uFormat ( "The distribution (%f) of inliers is under %s threshold (%f)" ,
info . inliersDistribution , Parameters :: kVisMinInliersDistribution (). c_str (), _minInliersDistributionThr );
transform . setNull ();
}
}
}
2015-11-22 18:08:32 -05:00
}
2016-03-02 18:11:19 -05:00
else if ( toSignature . sensorData (). isValid ())
{
2020-05-21 21:26:29 -04:00
msg = uFormat ( "Missing correspondences for registration (%d->%d). fromWords = %d fromImageEmpty=%d toWords = %d toImageEmpty=%d" ,
2017-01-05 19:31:18 -05:00
fromSignature . id (), toSignature . id (),
( int ) fromSignature . getWords (). size (), fromSignature . sensorData (). imageRaw (). empty () ? 1 : 0 ,
( int ) toSignature . getWords (). size (), toSignature . sensorData (). imageRaw (). empty () ? 1 : 0 );
2016-03-02 18:11:19 -05:00
}
2015-11-22 18:08:32 -05:00
2016-01-06 17:26:55 -05:00
info . inliers = inliersCount ;
2020-10-05 17:34:32 -04:00
info . inliersRatio = ! toSignature . getWords (). empty () ? float ( inliersCount ) / float ( toSignature . getWords (). size ()) : 0 ;
2016-01-07 13:59:21 -05:00
info . matches = matchesCount ;
info . rejectedMsg = msg ;
2017-05-22 11:48:01 -04:00
info . covariance = covariance ;
2026-01-17 18:21:16 -08:00
if ( ! covariance . empty ())
{
info . variance = covariance . at < double > ( 0 , 0 );
}
2016-01-06 17:26:55 -05:00
2020-05-09 21:13:41 -04:00
UDEBUG ( "inliers=%d/%d" , info . inliers , info . matches );
2015-11-22 18:08:32 -05:00
UDEBUG ( "transform=%s" , transform . prettyPrint (). c_str ());
return transform ;
}
}