2020-08-15 06:42:26 +08:00
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
# include "../precomp.hpp"
# include "../usac.hpp"
# include <atomic>
2023-06-16 15:59:13 +08:00
namespace cv {
UsacParams : : UsacParams ( ) {
confidence = 0.99 ;
isParallel = false ;
loIterations = 5 ;
loMethod = LOCAL_OPTIM_INNER_LO ;
loSampleSize = 14 ;
maxIterations = 5000 ;
neighborsSearch = NEIGH_GRID ;
randomGeneratorState = 0 ;
sampler = SAMPLING_UNIFORM ;
score = SCORE_METHOD_MSAC ;
threshold = 1.5 ;
final_polisher = COV_POLISHER ;
final_polisher_iterations = 3 ;
}
namespace usac {
2020-08-15 06:42:26 +08:00
int mergePoints ( InputArray pts1_ , InputArray pts2_ , Mat & pts , bool ispnp ) ;
void setParameters ( int flag , Ptr < Model > & params , EstimationMethod estimator , double thr ,
int max_iters , double conf , bool mask_needed ) ;
class RansacOutputImpl : public RansacOutput {
private :
std : : vector < int > inliers ;
2023-06-16 15:59:13 +08:00
cv : : Mat model , K1 , K2 ;
// vector of number_inliers size
// vector of points size, true if inlier, false - outlier
2020-08-15 06:42:26 +08:00
std : : vector < bool > inliers_mask ;
// vector of points size, value of i-th index corresponds to error of i-th point if i is inlier.
2023-06-16 15:59:13 +08:00
std : : vector < float > residuals ;
int number_inliers , number_iterations ;
ModelConfidence conf ;
2020-08-15 06:42:26 +08:00
public :
2023-06-16 15:59:13 +08:00
RansacOutputImpl ( const cv : : Mat & model_ , const std : : vector < bool > & inliers_mask_ , int number_inliers_ ,
int number_iterations_ , ModelConfidence conf_ , const std : : vector < float > & errors_ ) {
2020-08-15 06:42:26 +08:00
model_ . copyTo ( model ) ;
inliers_mask = inliers_mask_ ;
number_inliers = number_inliers_ ;
number_iterations = number_iterations_ ;
2023-06-16 15:59:13 +08:00
residuals = errors_ ;
conf = conf_ ;
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
// Return inliers' indices of size = number of inliers
2020-08-15 06:42:26 +08:00
const std : : vector < int > & getInliers ( ) override {
if ( inliers . empty ( ) ) {
2023-06-16 15:59:13 +08:00
inliers . reserve ( number_inliers ) ;
2020-08-15 06:42:26 +08:00
int pt_cnt = 0 ;
for ( bool is_inlier : inliers_mask ) {
if ( is_inlier )
inliers . emplace_back ( pt_cnt ) ;
pt_cnt + + ;
}
}
return inliers ;
}
2023-06-16 15:59:13 +08:00
const std : : vector < bool > & getInliersMask ( ) const override {
return inliers_mask ;
}
int getNumberOfInliers ( ) const override {
return number_inliers ;
}
const Mat & getModel ( ) const override {
return model ;
}
int getNumberOfIters ( ) const override {
return number_iterations ;
}
ModelConfidence getConfidence ( ) const override {
return conf ;
}
const std : : vector < float > & getResiduals ( ) const override {
return residuals ;
}
2020-08-15 06:42:26 +08:00
} ;
2023-06-16 15:59:13 +08:00
Ptr < RansacOutput > RansacOutput : : create ( const cv : : Mat & model_ , const std : : vector < bool > & inliers_mask_ , int number_inliers_ ,
int number_iterations_ , ModelConfidence conf , const std : : vector < float > & errors_ ) {
return makePtr < RansacOutputImpl > ( model_ , inliers_mask_ , number_inliers_ ,
number_iterations_ , conf , errors_ ) ;
}
double getLambda ( std : : vector < int > & supports , double cdf_thr , int points_size ,
int sample_size , bool is_independent , int & min_non_random_inliers ) {
std : : sort ( supports . begin ( ) , supports . end ( ) ) ;
double lambda = supports . size ( ) % 2 ? ( supports [ supports . size ( ) / 2 ] + supports [ supports . size ( ) / 2 + 1 ] ) * 0.5 : supports [ supports . size ( ) / 2 ] ;
const double cdf = lambda + cdf_thr * sqrt ( lambda * ( 1 - lambda / ( is_independent ? points_size - sample_size : points_size ) ) ) ;
int lower_than_cdf = 0 ; lambda = 0 ;
for ( const auto & inl : supports )
if ( inl < cdf ) {
lambda + = inl ; lower_than_cdf + + ;
} else break ; // list is sorted
lambda / = lower_than_cdf ;
if ( lambda < 1 | | lower_than_cdf = = 0 ) lambda = 1 ;
// use 0.9999 quantile https://keisan.casio.com/exec/system/14060745333941
if ( ! is_independent ) // do not calculate it for all inliers
min_non_random_inliers = ( int ) ( lambda + 2.32 * sqrt ( lambda * ( 1 - lambda / points_size ) ) ) + 1 ;
return lambda ;
2020-08-15 06:42:26 +08:00
}
class Ransac {
public :
2023-06-16 15:59:13 +08:00
const Ptr < const Model > params ;
Ptr < Estimator > _estimator ;
Ptr < Error > _error ;
Ptr < Quality > _quality ;
Ptr < Sampler > _sampler ;
Ptr < Termination > _termination ;
Ptr < ModelVerifier > _model_verifier ;
Ptr < Degeneracy > _degeneracy ;
Ptr < LocalOptimization > _local_optimization ;
Ptr < FinalModelPolisher > polisher ;
Ptr < GammaValues > _gamma_generator ;
Ptr < MinimalSolver > _min_solver ;
Ptr < NonMinimalSolver > _lo_solver , _fo_solver ;
Ptr < RandomGenerator > _lo_sampler ;
Ptr < WeightFunction > _weight_fnc ;
int points_size , _state , filtered_points_size ;
double threshold , max_thr ;
bool parallel ;
Matx33d T1 , T2 ;
Mat points , K1 , K2 , calib_points , image_points , norm_points , filtered_points ;
Ptr < NeighborhoodGraph > graph ;
std : : vector < Ptr < NeighborhoodGraph > > layers ;
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
Ransac ( const Ptr < const Model > & params_ , cv : : InputArray points1 , cv : : InputArray points2 ,
cv : : InputArray K1_ , cv : : InputArray K2_ , cv : : InputArray dist_coeff1 , cv : : InputArray dist_coeff2 ) : params ( params_ ) {
_state = params - > getRandomGeneratorState ( ) ;
threshold = params - > getThreshold ( ) ;
max_thr = std : : max ( threshold , params - > getMaximumThreshold ( ) ) ;
parallel = params - > isParallel ( ) ;
Mat undist_points1 , undist_points2 ;
if ( params - > isPnP ( ) ) {
if ( ! K1_ . empty ( ) ) {
K1 = K1_ . getMat ( ) . clone ( ) ; K1 . convertTo ( K1 , CV_64F ) ;
if ( ! dist_coeff1 . empty ( ) ) {
// undistortPoints also calibrate points using K
undistortPoints ( points1 . isContinuous ( ) ? points1 : points1 . getMat ( ) . clone ( ) , undist_points1 , K1_ , dist_coeff1 ) ;
points_size = mergePoints ( undist_points1 , points2 , points , true ) ;
Utils : : normalizeAndDecalibPointsPnP ( K1 , points , calib_points ) ;
} else {
points_size = mergePoints ( points1 , points2 , points , true ) ;
Utils : : calibrateAndNormalizePointsPnP ( K1 , points , calib_points ) ;
}
} else points_size = mergePoints ( points1 , points2 , points , true ) ;
} else {
if ( params - > isEssential ( ) ) {
CV_CheckEQ ( ( int ) ( ! K1_ . empty ( ) & & ! K2_ . empty ( ) ) , 1 , " Intrinsic matrix must not be empty! " ) ;
K1 = K1_ . getMat ( ) ; K1 . convertTo ( K1 , CV_64F ) ;
K2 = K2_ . getMat ( ) ; K2 . convertTo ( K2 , CV_64F ) ;
if ( ! dist_coeff1 . empty ( ) | | ! dist_coeff2 . empty ( ) ) {
// undistortPoints also calibrate points using K
if ( ! dist_coeff1 . empty ( ) ) undistortPoints ( points1 . isContinuous ( ) ? points1 : points1 . getMat ( ) . clone ( ) , undist_points1 , K1_ , dist_coeff1 ) ;
else undist_points1 = points1 . getMat ( ) ;
if ( ! dist_coeff2 . empty ( ) ) undistortPoints ( points2 . isContinuous ( ) ? points2 : points2 . getMat ( ) . clone ( ) , undist_points2 , K2_ , dist_coeff2 ) ;
else undist_points2 = points2 . getMat ( ) ;
points_size = mergePoints ( undist_points1 , undist_points2 , calib_points , false ) ;
} else {
points_size = mergePoints ( points1 , points2 , points , false ) ;
Utils : : calibratePoints ( K1 , K2 , points , calib_points ) ;
}
threshold = Utils : : getCalibratedThreshold ( threshold , K1 , K2 ) ;
max_thr = Utils : : getCalibratedThreshold ( max_thr , K1 , K2 ) ;
} else {
points_size = mergePoints ( points1 , points2 , points , false ) ;
if ( params - > isFundamental ( ) & & ! K1_ . empty ( ) & & ! K2_ . empty ( ) ) {
K1 = K1_ . getMat ( ) ; K1 . convertTo ( K1 , CV_64F ) ;
K2 = K2_ . getMat ( ) ; K2 . convertTo ( K2 , CV_64F ) ;
Utils : : calibratePoints ( K1 , K2 , points , calib_points ) ;
}
}
}
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
if ( params - > getSampler ( ) = = SamplingMethod : : SAMPLING_NAPSAC | | params - > getLO ( ) = = LocalOptimMethod : : LOCAL_OPTIM_GC ) {
if ( params - > getNeighborsSearch ( ) = = NeighborSearchMethod : : NEIGH_GRID ) {
graph = GridNeighborhoodGraph : : create ( points , points_size ,
params - > getCellSize ( ) , params - > getCellSize ( ) , params - > getCellSize ( ) , params - > getCellSize ( ) , 10 ) ;
} else if ( params - > getNeighborsSearch ( ) = = NeighborSearchMethod : : NEIGH_FLANN_KNN ) {
graph = FlannNeighborhoodGraph : : create ( points , points_size , params - > getKNN ( ) , false , 5 , 1 ) ;
} else if ( params - > getNeighborsSearch ( ) = = NeighborSearchMethod : : NEIGH_FLANN_RADIUS ) {
graph = RadiusSearchNeighborhoodGraph : : create ( points , points_size , params - > getGraphRadius ( ) , 5 , 1 ) ;
} else CV_Error ( cv : : Error : : StsNotImplemented , " Graph type is not implemented! " ) ;
}
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
if ( params - > getSampler ( ) = = SamplingMethod : : SAMPLING_PROGRESSIVE_NAPSAC ) {
CV_CheckEQ ( ( int ) params - > isPnP ( ) , 0 , " ProgressiveNAPSAC for PnP is not implemented! " ) ;
const auto & cell_number_per_layer = params - > getGridCellNumber ( ) ;
layers . reserve ( cell_number_per_layer . size ( ) ) ;
const auto * const pts = ( float * ) points . data ;
float img1_width = 0 , img1_height = 0 , img2_width = 0 , img2_height = 0 ;
for ( int i = 0 ; i < 4 * points_size ; i + = 4 ) {
if ( pts [ i ] > img1_width ) img1_width = pts [ i ] ;
if ( pts [ i + 1 ] > img1_height ) img1_height = pts [ i + 1 ] ;
if ( pts [ i + 2 ] > img2_width ) img2_width = pts [ i + 2 ] ;
if ( pts [ i + 3 ] > img2_height ) img2_height = pts [ i + 3 ] ;
}
// Create grid graphs (overlapping layes of given cell numbers)
for ( int layer_idx = 0 ; layer_idx < ( int ) cell_number_per_layer . size ( ) ; layer_idx + + ) {
const int cell_number = cell_number_per_layer [ layer_idx ] ;
if ( layer_idx > 0 )
if ( cell_number_per_layer [ layer_idx - 1 ] < = cell_number )
CV_Error ( cv : : Error : : StsError , " Progressive NAPSAC sampler: "
" Cell number in layers must be in decreasing order! " ) ;
layers . emplace_back ( GridNeighborhoodGraph : : create ( points , points_size ,
( int ) ( img1_width / ( float ) cell_number ) , ( int ) ( img1_height / ( float ) cell_number ) ,
( int ) ( img2_width / ( float ) cell_number ) , ( int ) ( img2_height / ( float ) cell_number ) , 10 ) ) ;
}
}
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
// update points by calibrated for Essential matrix after graph is calculated
if ( params - > isEssential ( ) ) {
image_points = points ;
points = calib_points ;
// if maximum calibrated threshold significanlty differs threshold then set upper bound
if ( max_thr > 10 * threshold )
max_thr = 10 * threshold ;
}
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
// Since error function output squared error distance, so make
// threshold squared as well
threshold * = threshold ;
if ( ( params - > isHomography ( ) | | ( params - > isFundamental ( ) & & ( K1 . empty ( ) | | K2 . empty ( ) | | ! params - > isLarssonOptimization ( ) ) ) | |
params - > getEstimator ( ) = = EstimationMethod : : AFFINE ) & & ( params - > getLO ( ) ! = LOCAL_OPTIM_NULL | | params - > getFinalPolisher ( ) = = COV_POLISHER ) ) {
const auto normTr = NormTransform : : create ( points ) ;
std : : vector < int > sample ( points_size ) ;
for ( int i = 0 ; i < points_size ; i + + ) sample [ i ] = i ;
normTr - > getNormTransformation ( norm_points , sample , points_size , T1 , T2 ) ;
}
if ( params - > getScore ( ) = = SCORE_METHOD_MAGSAC | | params - > getLO ( ) = = LOCAL_OPTIM_SIGMA | | params - > getFinalPolisher ( ) = = MAGSAC )
_gamma_generator = GammaValues : : create ( params - > getDegreesOfFreedom ( ) ) ; // is thread safe
initialize ( _state , _min_solver , _lo_solver , _error , _estimator , _degeneracy , _quality ,
_model_verifier , _local_optimization , _termination , _sampler , _lo_sampler , _weight_fnc , false /*parallel*/ ) ;
if ( params - > getFinalPolisher ( ) ! = NONE_POLISHER ) {
polisher = NonMinimalPolisher : : create ( _quality , _fo_solver ,
params - > getFinalPolisher ( ) = = MAGSAC ? _weight_fnc : nullptr , params - > getFinalLSQIterations ( ) , 0.99 ) ;
}
}
void initialize ( int state , Ptr < MinimalSolver > & min_solver , Ptr < NonMinimalSolver > & non_min_solver ,
Ptr < Error > & error , Ptr < Estimator > & estimator , Ptr < Degeneracy > & degeneracy , Ptr < Quality > & quality ,
Ptr < ModelVerifier > & verifier , Ptr < LocalOptimization > & lo , Ptr < Termination > & termination ,
Ptr < Sampler > & sampler , Ptr < RandomGenerator > & lo_sampler , Ptr < WeightFunction > & weight_fnc , bool parallel_call ) {
const int min_sample_size = params - > getSampleSize ( ) , prosac_termination_length = std : : min ( ( int ) ( .5 * points_size ) , 100 ) ;
// inner inlier threshold will be used in LO to obtain inliers
// additionally in DEGENSAC for F
double inner_inlier_thr_sqr = threshold ;
if ( params - > isHomography ( ) & & inner_inlier_thr_sqr < 5.25 ) inner_inlier_thr_sqr = 5.25 ; // at least 2.5 px
else if ( params - > isFundamental ( ) & & inner_inlier_thr_sqr < 4 ) inner_inlier_thr_sqr = 4 ; // at least 2 px
if ( params - > getFinalPolisher ( ) = = MAGSAC | | params - > getLO ( ) = = LOCAL_OPTIM_SIGMA )
weight_fnc = MagsacWeightFunction : : create ( _gamma_generator , params - > getDegreesOfFreedom ( ) , params - > getUpperIncompleteOfSigmaQuantile ( ) , params - > getC ( ) , params - > getMaximumThreshold ( ) ) ;
else weight_fnc = nullptr ;
switch ( params - > getError ( ) ) {
case ErrorMetric : : SYMM_REPR_ERR :
error = ReprojectionErrorSymmetric : : create ( points ) ; break ;
case ErrorMetric : : FORW_REPR_ERR :
if ( params - > getEstimator ( ) = = EstimationMethod : : AFFINE )
error = ReprojectionErrorAffine : : create ( points ) ;
else error = ReprojectionErrorForward : : create ( points ) ;
break ;
case ErrorMetric : : SAMPSON_ERR :
error = SampsonError : : create ( points ) ; break ;
case ErrorMetric : : SGD_ERR :
error = SymmetricGeometricDistance : : create ( points ) ; break ;
case ErrorMetric : : RERPOJ :
error = ReprojectionErrorPmatrix : : create ( points ) ; break ;
default : CV_Error ( cv : : Error : : StsNotImplemented , " Error metric is not implemented! " ) ;
}
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
const double k_mlesac = params - > getKmlesac ( ) ;
switch ( params - > getScore ( ) ) {
case ScoreMethod : : SCORE_METHOD_RANSAC :
quality = RansacQuality : : create ( points_size , threshold , error ) ; break ;
case ScoreMethod : : SCORE_METHOD_MSAC :
quality = MsacQuality : : create ( points_size , threshold , error , k_mlesac ) ; break ;
case ScoreMethod : : SCORE_METHOD_MAGSAC :
quality = MagsacQuality : : create ( max_thr , points_size , error , _gamma_generator ,
threshold , params - > getDegreesOfFreedom ( ) , params - > getSigmaQuantile ( ) ,
params - > getUpperIncompleteOfSigmaQuantile ( ) ) ; break ;
case ScoreMethod : : SCORE_METHOD_LMEDS :
quality = LMedsQuality : : create ( points_size , threshold , error ) ; break ;
2023-07-03 17:08:12 +08:00
default : CV_Error ( cv : : Error : : StsNotImplemented , " Score is not implemented! " ) ;
2023-06-16 15:59:13 +08:00
}
const auto is_ge_solver = params - > getRansacSolver ( ) = = GEM_SOLVER ;
if ( params - > isHomography ( ) ) {
degeneracy = HomographyDegeneracy : : create ( points ) ;
min_solver = HomographyMinimalSolver4pts : : create ( points , is_ge_solver ) ;
non_min_solver = HomographyNonMinimalSolver : : create ( norm_points , T1 , T2 , true ) ;
estimator = HomographyEstimator : : create ( min_solver , non_min_solver , degeneracy ) ;
if ( ! parallel_call & & params - > getFinalPolisher ( ) ! = NONE_POLISHER ) {
if ( params - > getFinalPolisher ( ) = = COV_POLISHER )
_fo_solver = CovarianceHomographySolver : : create ( norm_points , T1 , T2 ) ;
else _fo_solver = HomographyNonMinimalSolver : : create ( points ) ;
}
} else if ( params - > isFundamental ( ) ) {
if ( K1 . empty ( ) | | K2 . empty ( ) ) {
degeneracy = FundamentalDegeneracy : : create ( state + + , quality , points , min_sample_size ,
params - > getPlaneAndParallaxIters ( ) , std : : max ( threshold , 8. ) /*sqr homogr thr*/ , inner_inlier_thr_sqr , K1 , K2 ) ;
} else degeneracy = FundamentalDegeneracyViaE : : create ( quality , points , calib_points , K1 , K2 , true /*is F*/ ) ;
if ( min_sample_size = = 7 ) {
min_solver = FundamentalMinimalSolver7pts : : create ( points , is_ge_solver ) ;
} else min_solver = FundamentalMinimalSolver8pts : : create ( points ) ;
if ( params - > isLarssonOptimization ( ) & & ! K1 . empty ( ) & & ! K2 . empty ( ) ) {
non_min_solver = LarssonOptimizer : : create ( calib_points , K1 , K2 , params - > getLevMarqItersLO ( ) , true /*F*/ ) ;
} else {
if ( weight_fnc )
non_min_solver = EpipolarNonMinimalSolver : : create ( points , true ) ;
else
non_min_solver = EpipolarNonMinimalSolver : : create ( norm_points , T1 , T2 , true ) ;
}
estimator = FundamentalEstimator : : create ( min_solver , non_min_solver , degeneracy ) ;
if ( ! parallel_call & & params - > getFinalPolisher ( ) ! = NONE_POLISHER ) {
if ( params - > isLarssonOptimization ( ) & & ! K1 . empty ( ) & & ! K2 . empty ( ) )
_fo_solver = LarssonOptimizer : : create ( calib_points , K1 , K2 , params - > getLevMarqIters ( ) , true /*F*/ ) ;
else if ( params - > getFinalPolisher ( ) = = COV_POLISHER )
_fo_solver = CovarianceEpipolarSolver : : create ( norm_points , T1 , T2 ) ;
else _fo_solver = EpipolarNonMinimalSolver : : create ( points , true ) ;
}
} else if ( params - > isEssential ( ) ) {
if ( params - > getEstimator ( ) = = EstimationMethod : : ESSENTIAL ) {
min_solver = EssentialMinimalSolver5pts : : create ( points , ! is_ge_solver , true /*Nister*/ ) ;
degeneracy = EssentialDegeneracy : : create ( points , min_sample_size ) ;
}
non_min_solver = LarssonOptimizer : : create ( calib_points , K1 , K2 , params - > getLevMarqItersLO ( ) , false /*E*/ ) ;
estimator = EssentialEstimator : : create ( min_solver , non_min_solver , degeneracy ) ;
if ( ! parallel_call & & params - > getFinalPolisher ( ) ! = NONE_POLISHER )
_fo_solver = LarssonOptimizer : : create ( calib_points , K1 , K2 , params - > getLevMarqIters ( ) , false /*E*/ ) ;
} else if ( params - > isPnP ( ) ) {
degeneracy = makePtr < Degeneracy > ( ) ;
if ( min_sample_size = = 3 ) {
min_solver = P3PSolver : : create ( points , calib_points , K1 ) ;
non_min_solver = DLSPnP : : create ( points , calib_points , K1 ) ;
} else {
if ( is_ge_solver )
min_solver = PnPMinimalSolver6Pts : : create ( points ) ;
else min_solver = PnPSVDSolver : : create ( points ) ;
non_min_solver = PnPNonMinimalSolver : : create ( points ) ;
}
estimator = PnPEstimator : : create ( min_solver , non_min_solver ) ;
if ( ! parallel_call & & params - > getFinalPolisher ( ) ! = NONE_POLISHER ) _fo_solver = non_min_solver ;
} else if ( params - > getEstimator ( ) = = EstimationMethod : : AFFINE ) {
degeneracy = makePtr < Degeneracy > ( ) ;
min_solver = AffineMinimalSolver : : create ( points ) ;
non_min_solver = AffineNonMinimalSolver : : create ( points , cv : : noArray ( ) , cv : : noArray ( ) ) ;
estimator = AffineEstimator : : create ( min_solver , non_min_solver ) ;
if ( ! parallel_call & & params - > getFinalPolisher ( ) ! = NONE_POLISHER ) {
if ( params - > getFinalPolisher ( ) = = COV_POLISHER )
_fo_solver = CovarianceAffineSolver : : create ( points ) ;
else _fo_solver = non_min_solver ;
}
} else CV_Error ( cv : : Error : : StsNotImplemented , " Estimator not implemented! " ) ;
switch ( params - > getSampler ( ) ) {
case SamplingMethod : : SAMPLING_UNIFORM :
sampler = UniformSampler : : create ( state + + , min_sample_size , points_size ) ;
break ;
case SamplingMethod : : SAMPLING_PROSAC :
if ( ! parallel_call ) // for parallel only one PROSAC sampler
sampler = ProsacSampler : : create ( state + + , points_size , min_sample_size , params - > getProsacMaxSamples ( ) ) ;
break ;
case SamplingMethod : : SAMPLING_PROGRESSIVE_NAPSAC :
sampler = ProgressiveNapsac : : create ( state + + , points_size , min_sample_size , layers , 20 ) ; break ;
case SamplingMethod : : SAMPLING_NAPSAC :
sampler = NapsacSampler : : create ( state + + , points_size , min_sample_size , graph ) ; break ;
default : CV_Error ( cv : : Error : : StsNotImplemented , " Sampler is not implemented! " ) ;
}
const bool is_sprt = params - > getVerifier ( ) = = VerificationMethod : : SPRT_VERIFIER | | params - > getVerifier ( ) = = VerificationMethod : : ASPRT ;
if ( is_sprt )
verifier = AdaptiveSPRT : : create ( state + + , quality , points_size , params - > getScore ( ) = = ScoreMethod : : SCORE_METHOD_MAGSAC ? max_thr : threshold ,
params - > getSPRTepsilon ( ) , params - > getSPRTdelta ( ) , params - > getTimeForModelEstimation ( ) ,
params - > getSPRTavgNumModels ( ) , params - > getScore ( ) , k_mlesac , params - > getVerifier ( ) = = VerificationMethod : : ASPRT ) ;
else if ( params - > getVerifier ( ) = = VerificationMethod : : NULL_VERIFIER )
verifier = ModelVerifier : : create ( quality ) ;
else CV_Error ( cv : : Error : : StsNotImplemented , " Verifier is not imeplemented! " ) ;
if ( params - > getSampler ( ) = = SamplingMethod : : SAMPLING_PROSAC ) {
if ( parallel_call ) {
termination = ProsacTerminationCriteria : : create ( nullptr , error ,
points_size , min_sample_size , params - > getConfidence ( ) , params - > getMaxIters ( ) , prosac_termination_length , 0.05 , 0.05 , threshold ,
_termination . dynamicCast < ProsacTerminationCriteria > ( ) - > getNonRandomInliers ( ) ) ;
} else {
termination = ProsacTerminationCriteria : : create ( sampler . dynamicCast < ProsacSampler > ( ) , error ,
points_size , min_sample_size , params - > getConfidence ( ) , params - > getMaxIters ( ) , prosac_termination_length , 0.05 , 0.05 , threshold ,
std : : vector < int > ( ) ) ;
}
} else if ( params - > getSampler ( ) = = SamplingMethod : : SAMPLING_PROGRESSIVE_NAPSAC ) {
if ( is_sprt )
termination = SPRTPNapsacTermination : : create ( verifier . dynamicCast < AdaptiveSPRT > ( ) ,
params - > getConfidence ( ) , points_size , min_sample_size ,
params - > getMaxIters ( ) , params - > getRelaxCoef ( ) ) ;
else termination = StandardTerminationCriteria : : create ( params - > getConfidence ( ) ,
points_size , min_sample_size , params - > getMaxIters ( ) ) ;
} else if ( is_sprt & & params - > getLO ( ) = = LocalOptimMethod : : LOCAL_OPTIM_NULL ) {
termination = SPRTTermination : : create ( verifier . dynamicCast < AdaptiveSPRT > ( ) ,
params - > getConfidence ( ) , points_size , min_sample_size , params - > getMaxIters ( ) ) ;
} else {
termination = StandardTerminationCriteria : : create
( params - > getConfidence ( ) , points_size , min_sample_size , params - > getMaxIters ( ) ) ;
}
// if normal ransac or parallel call, avoid redundant init
if ( ( ! params - > isParallel ( ) | | parallel_call ) & & params - > getLO ( ) ! = LocalOptimMethod : : LOCAL_OPTIM_NULL ) {
lo_sampler = UniformRandomGenerator : : create ( state , points_size , params - > getLOSampleSize ( ) ) ;
const auto lo_termination = StandardTerminationCriteria : : create ( params - > getConfidence ( ) , points_size , min_sample_size , params - > getMaxIters ( ) ) ;
switch ( params - > getLO ( ) ) {
case LocalOptimMethod : : LOCAL_OPTIM_INNER_LO : case LocalOptimMethod : : LOCAL_OPTIM_SIGMA :
lo = SimpleLocalOptimization : : create ( quality , non_min_solver , lo_termination , lo_sampler ,
weight_fnc , params - > getLOInnerMaxIters ( ) , inner_inlier_thr_sqr , true ) ; break ;
case LocalOptimMethod : : LOCAL_OPTIM_INNER_AND_ITER_LO :
lo = InnerIterativeLocalOptimization : : create ( estimator , quality , lo_sampler ,
points_size , threshold , true , params - > getLOIterativeSampleSize ( ) ,
params - > getLOInnerMaxIters ( ) , params - > getLOIterativeMaxIters ( ) ,
params - > getLOThresholdMultiplier ( ) ) ; break ;
case LocalOptimMethod : : LOCAL_OPTIM_GC :
lo = GraphCut : : create ( estimator , quality , graph , lo_sampler , threshold ,
params - > getGraphCutSpatialCoherenceTerm ( ) , params - > getLOInnerMaxIters ( ) , lo_termination ) ; break ;
default : CV_Error ( cv : : Error : : StsNotImplemented , " Local Optimization is not implemented! " ) ;
}
}
}
int getIndependentInliers ( const Mat & model_ , const std : : vector < int > & sample ,
std : : vector < int > & inliers , const int num_inliers_ ) {
bool is_F = params - > isFundamental ( ) ;
Mat model = model_ ;
int sample_size = 0 ;
if ( is_F ) sample_size = 7 ;
else if ( params - > isHomography ( ) ) sample_size = 4 ;
else if ( params - > isEssential ( ) ) {
is_F = true ;
// convert E to F
model = Mat ( Matx33d ( K2 ) . inv ( ) . t ( ) * Matx33d ( model ) * Matx33d ( K1 ) . inv ( ) ) ;
sample_size = 5 ;
} else if ( params - > isPnP ( ) | | params - > getEstimator ( ) = = EstimationMethod : : AFFINE ) sample_size = 3 ;
else
CV_Error ( cv : : Error : : StsNotImplemented , " Method for independent inliers is not implemented for this problem " ) ;
if ( num_inliers_ < = sample_size ) return 0 ; // minimal sample size generates model
model . convertTo ( model , CV_32F ) ;
int num_inliers = num_inliers_ , num_pts_near_ep = 0 ,
num_pts_validatin_or_constr = 0 , pt1 = 0 ;
const auto * const pts = params - > isEssential ( ) ? ( float * ) image_points . data : ( float * ) points . data ;
// scale for thresholds should be used
const float ep_thr_sqr = 0.000001f , line_thr = 0.01f , neigh_thr = 4.0f ;
float sign1 = 0 , a1 = 0 , b1 = 0 , c1 = 0 , a2 = 0 , b2 = 0 , c2 = 0 , ep1_x , ep1_y , ep2_x , ep2_y ;
const auto * const m = ( float * ) model . data ;
Vec3f ep1 ;
bool do_or_test = false , ep1_inf = false , ep2_inf = false ;
if ( is_F ) { // compute epipole and sign of the first point for orientation test
model * = ( 1 / norm ( model ) ) ;
ep1 = Utils : : getRightEpipole ( model ) ;
const Vec3f ep2 = Utils : : getLeftEpipole ( model ) ;
if ( fabsf ( ep1 [ 2 ] ) < DBL_EPSILON ) {
ep1_inf = true ;
} else {
ep1_x = ep1 [ 0 ] / ep1 [ 2 ] ;
ep1_y = ep1 [ 1 ] / ep1 [ 2 ] ;
}
if ( fabsf ( ep2 [ 2 ] ) < DBL_EPSILON ) {
ep2_inf = true ;
} else {
ep2_x = ep2 [ 0 ] / ep2 [ 2 ] ;
ep2_y = ep2 [ 1 ] / ep2 [ 2 ] ;
}
}
const auto * const e1 = ep1 . val ; // of size 3x1
// we move sample points to the end, so every inlier will be checked by sample point
int num_sample_in_inliers = 0 ;
if ( ! sample . empty ( ) ) {
num_sample_in_inliers = 0 ;
int temp_idx = num_inliers ;
for ( int i = 0 ; i < temp_idx ; i + + ) {
const int inl = inliers [ i ] ;
for ( int s : sample ) {
if ( inl = = s ) {
std : : swap ( inliers [ i ] , inliers [ - - temp_idx ] ) ;
i - - ; // we need to check inlier that we just swapped
num_sample_in_inliers + + ;
break ;
}
}
}
}
if ( is_F ) {
int MIN_TEST = std : : min ( 15 , num_inliers ) ;
for ( int i = 0 ; i < MIN_TEST ; i + + ) {
pt1 = 4 * inliers [ i ] ;
sign1 = ( m [ 0 ] * pts [ pt1 + 2 ] + m [ 3 ] * pts [ pt1 + 3 ] + m [ 6 ] ) * ( e1 [ 1 ] - e1 [ 2 ] * pts [ pt1 + 1 ] ) ;
int validate = 0 ;
for ( int j = 0 ; j < MIN_TEST ; j + + ) {
if ( i = = j ) continue ;
const int inl_idx = 4 * inliers [ j ] ;
if ( sign1 * ( m [ 0 ] * pts [ inl_idx + 2 ] + m [ 3 ] * pts [ inl_idx + 3 ] + m [ 6 ] ) * ( e1 [ 1 ] - e1 [ 2 ] * pts [ inl_idx + 1 ] ) < 0 )
validate + + ;
}
if ( validate < MIN_TEST / 2 ) {
do_or_test = true ; break ;
}
}
}
// verification does not include sample points as they are surely random
const int max_verify = num_inliers - num_sample_in_inliers ;
if ( max_verify < = 0 )
return 0 ;
int num_non_random_inliers = num_inliers - sample_size ;
auto removeDependentPoints = [ & ] ( bool do_orient_test , bool check_epipoles ) {
for ( int i = 0 ; i < max_verify ; i + + ) {
// checks over inliers if they are dependent to other inliers
const int inl_idx = 4 * inliers [ i ] ;
const auto x1 = pts [ inl_idx ] , y1 = pts [ inl_idx + 1 ] , x2 = pts [ inl_idx + 2 ] , y2 = pts [ inl_idx + 3 ] ;
if ( is_F ) {
// epipolar line on image 2 = l2
a2 = m [ 0 ] * x1 + m [ 1 ] * y1 + m [ 2 ] ;
b2 = m [ 3 ] * x1 + m [ 4 ] * y1 + m [ 5 ] ;
c2 = m [ 6 ] * x1 + m [ 7 ] * y1 + m [ 8 ] ;
// epipolar line on image 1 = l1
a1 = m [ 0 ] * x2 + m [ 3 ] * y2 + m [ 6 ] ;
b1 = m [ 1 ] * x2 + m [ 4 ] * y2 + m [ 7 ] ;
c1 = m [ 2 ] * x2 + m [ 5 ] * y2 + m [ 8 ] ;
if ( ( ! ep1_inf & & fabsf ( x1 - ep1_x ) + fabsf ( y1 - ep1_y ) < neigh_thr ) | |
( ! ep2_inf & & fabsf ( x2 - ep2_x ) + fabsf ( y2 - ep2_y ) < neigh_thr ) ) {
num_non_random_inliers - - ;
num_pts_near_ep + + ;
continue ; // is dependent, continue to the next point
} else if ( check_epipoles ) {
if ( a2 * a2 + b2 * b2 + c2 * c2 < ep_thr_sqr | |
a1 * a1 + b1 * b1 + c1 * c1 < ep_thr_sqr ) {
num_non_random_inliers - - ;
num_pts_near_ep + + ;
continue ; // is dependent, continue to the next point
}
}
else if ( do_orient_test & & pt1 ! = inl_idx & & sign1 * ( m [ 0 ] * x2 + m [ 3 ] * y2 + m [ 6 ] ) * ( e1 [ 1 ] - e1 [ 2 ] * y1 ) < 0 ) {
num_non_random_inliers - - ;
num_pts_validatin_or_constr + + ;
continue ;
}
const auto mag2 = 1 / sqrt ( a2 * a2 + b2 * b2 ) , mag1 = 1 / sqrt ( a1 * a1 + b1 * b1 ) ;
a2 * = mag2 ; b2 * = mag2 ; c2 * = mag2 ;
a1 * = mag1 ; b1 * = mag1 ; c1 * = mag1 ;
}
for ( int j = i + 1 ; j < num_inliers ; j + + ) { // verify through all including sample points
const int inl_idx_j = 4 * inliers [ j ] ;
const auto X1 = pts [ inl_idx_j ] , Y1 = pts [ inl_idx_j + 1 ] , X2 = pts [ inl_idx_j + 2 ] , Y2 = pts [ inl_idx_j + 3 ] ;
// use L1 norm instead of L2 for faster evaluation
if ( fabsf ( X1 - x1 ) + fabsf ( Y1 - y1 ) < neigh_thr | | fabsf ( X2 - x2 ) + fabsf ( Y2 - y2 ) < neigh_thr ) {
num_non_random_inliers - - ;
// num_pts_bad_conditioning++;
break ; // is dependent stop verification
} else if ( is_F ) {
if ( fabsf ( a2 * X2 + b2 * Y2 + c2 ) < line_thr & & //|| // xj'^T F xi
fabsf ( a1 * X1 + b1 * Y1 + c1 ) < line_thr ) { // xj^T F^T xi'
num_non_random_inliers - - ;
break ; // is dependent stop verification
}
}
}
}
} ;
if ( params - > isPnP ( ) ) {
for ( int i = 0 ; i < max_verify ; i + + ) {
const int inl_idx = 5 * inliers [ i ] ;
const auto x = pts [ inl_idx ] , y = pts [ inl_idx + 1 ] , X = pts [ inl_idx + 2 ] , Y = pts [ inl_idx + 3 ] , Z = pts [ inl_idx + 4 ] ;
for ( int j = i + 1 ; j < num_inliers ; j + + ) {
const int inl_idx_j = 5 * inliers [ j ] ;
if ( fabsf ( x - pts [ inl_idx_j ] ) + fabsf ( y - pts [ inl_idx_j + 1 ] ) < neigh_thr | |
fabsf ( X - pts [ inl_idx_j + 2 ] ) + fabsf ( Y - pts [ inl_idx_j + 3 ] ) + fabsf ( Z - pts [ inl_idx_j + 4 ] ) < neigh_thr ) {
num_non_random_inliers - - ;
break ;
}
}
}
} else {
removeDependentPoints ( do_or_test , ! ep1_inf & & ! ep2_inf ) ;
if ( is_F ) {
const bool is_pts_vald_constr_normal = ( double ) num_pts_validatin_or_constr / num_inliers < 0.6 ;
const bool is_pts_near_ep_normal = ( double ) num_pts_near_ep / num_inliers < 0.6 ;
if ( ! is_pts_near_ep_normal | | ! is_pts_vald_constr_normal ) {
num_non_random_inliers = num_inliers - sample_size ;
num_pts_near_ep = 0 ; num_pts_validatin_or_constr = 0 ;
removeDependentPoints ( is_pts_vald_constr_normal , is_pts_near_ep_normal ) ;
}
}
}
return num_non_random_inliers ;
}
bool run ( Ptr < RansacOutput > & ransac_output ) {
if ( points_size < params - > getSampleSize ( ) )
return false ;
const bool LO = params - > getLO ( ) ! = LocalOptimMethod : : LOCAL_OPTIM_NULL ,
IS_FUNDAMENTAL = params - > isFundamental ( ) , IS_NON_RAND_TEST = params - > isNonRandomnessTest ( ) ;
const int MAX_MODELS_ADAPT = 21 , MAX_ITERS_ADAPT = MAX_MODELS_ADAPT /*assume at least 1 model from 1 sample*/ ,
sample_size = params - > getSampleSize ( ) ;
const double IOU_SIMILARITY_THR = 0.80 ;
std : : vector < int > non_degen_sample , best_sample ;
double lambda_non_random_all_inliers = - 1 ;
int final_iters , num_total_tested_models = 0 ;
// non-random
const int MAX_TEST_MODELS_NONRAND = IS_NON_RAND_TEST ? MAX_MODELS_ADAPT : 0 ;
std : : vector < Mat > models_for_random_test ; models_for_random_test . reserve ( MAX_TEST_MODELS_NONRAND ) ;
std : : vector < std : : vector < int > > samples_for_random_test ; samples_for_random_test . reserve ( MAX_TEST_MODELS_NONRAND ) ;
bool last_model_from_LO = false ;
Mat best_model , best_model_not_from_LO , K1_approx , K2_approx ;
Score best_score , best_score_model_not_from_LO ;
std : : vector < bool > best_inliers_mask ( points_size ) ;
2020-08-15 06:42:26 +08:00
if ( ! parallel ) {
2023-06-16 15:59:13 +08:00
// adaptive sprt test
double IoU = 0 , mean_num_est_models = 0 ;
bool adapt = IS_NON_RAND_TEST | | params - > getVerifier ( ) = = VerificationMethod : : ASPRT , was_LO_run = false ;
int min_non_random_inliers = 30 , iters = 0 , num_estimations = 0 , max_iters = params - > getMaxIters ( ) ;
Mat non_degenerate_model , lo_model ;
Score current_score , non_degenerate_model_score , best_score_sample ;
std : : vector < bool > model_inliers_mask ( points_size ) ;
std : : vector < Mat > models ( _estimator - > getMaxNumSolutions ( ) ) ;
std : : vector < int > sample ( _estimator - > getMinimalSampleSize ( ) ) , supports ;
supports . reserve ( 3 * MAX_MODELS_ADAPT ) ; // store model supports during adaption
auto update_best = [ & ] ( const Mat & new_model , const Score & new_score , bool from_lo = false ) {
_quality - > getInliers ( new_model , model_inliers_mask ) ;
IoU = Utils : : intersectionOverUnion ( best_inliers_mask , model_inliers_mask ) ;
best_inliers_mask = model_inliers_mask ;
if ( ! best_model . empty ( ) & & ( int ) models_for_random_test . size ( ) < MAX_TEST_MODELS_NONRAND & & IoU < IOU_SIMILARITY_THR & & ! from_lo ) { // use IoU to not save similar models
// save old best model for non-randomness test if necessary
models_for_random_test . emplace_back ( best_model . clone ( ) ) ;
samples_for_random_test . emplace_back ( best_sample ) ;
}
// update score, model, inliers and max iterations
2020-10-07 04:37:49 +08:00
best_score = new_score ;
new_model . copyTo ( best_model ) ;
2023-06-16 15:59:13 +08:00
if ( ! from_lo ) {
best_sample = sample ;
if ( IS_FUNDAMENTAL ) { // avoid degeneracy after LO run
// save last model not from LO
best_model . copyTo ( best_model_not_from_LO ) ;
best_score_model_not_from_LO = best_score ;
}
}
_model_verifier - > update ( best_score , iters ) ;
max_iters = _termination - > update ( best_model , best_score . inlier_number ) ;
// max_iters = std::max(max_iters, std::min(10, params->getMaxIters()));
if ( ! adapt ) // update quality and verifier to save evaluation time of a model
_quality - > setBestScore ( best_score . score ) ;
last_model_from_LO = from_lo ;
2020-10-07 04:37:49 +08:00
} ;
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
auto run_lo = [ & ] ( const Mat & _model , const Score & _score , bool force_lo ) {
was_LO_run = true ;
_local_optimization - > setCurrentRANSACiter ( force_lo ? iters : - 1 ) ;
Score lo_score ;
if ( _local_optimization - > refineModel ( _model , _score , lo_model , lo_score ) & & lo_score . isBetter ( best_score ) )
update_best ( lo_model , lo_score , true ) ;
} ;
2020-08-15 06:42:26 +08:00
for ( ; iters < max_iters ; iters + + ) {
_sampler - > generateSample ( sample ) ;
2023-06-16 15:59:13 +08:00
int number_of_models ;
if ( adapt ) {
number_of_models = _estimator - > estimateModels ( sample , models ) ;
mean_num_est_models + = number_of_models ;
num_estimations + + ;
} else {
number_of_models = _estimator - > estimateModels ( sample , models ) ;
}
2020-08-15 06:42:26 +08:00
for ( int i = 0 ; i < number_of_models ; i + + ) {
2023-06-16 15:59:13 +08:00
num_total_tested_models + + ;
if ( adapt ) {
2020-10-07 04:37:49 +08:00
current_score = _quality - > getScore ( models [ i ] ) ;
2023-06-16 15:59:13 +08:00
supports . emplace_back ( current_score . inlier_number ) ;
if ( IS_NON_RAND_TEST & & best_score_sample . isBetter ( current_score ) ) {
models_for_random_test . emplace_back ( models [ i ] . clone ( ) ) ;
samples_for_random_test . emplace_back ( sample ) ;
}
2020-10-07 04:37:49 +08:00
} else {
2023-06-16 15:59:13 +08:00
if ( ! _model_verifier - > isModelGood ( models [ i ] , current_score ) )
continue ;
2020-10-07 04:37:49 +08:00
}
2023-06-16 15:59:13 +08:00
if ( current_score . isBetter ( best_score_sample ) ) {
if ( _degeneracy - > recoverIfDegenerate ( sample , models [ i ] , current_score ,
non_degenerate_model , non_degenerate_model_score ) ) {
2020-08-15 06:42:26 +08:00
// check if best non degenerate model is better than so far the best model
2023-06-16 15:59:13 +08:00
if ( non_degenerate_model_score . isBetter ( best_score ) ) {
update_best ( non_degenerate_model , non_degenerate_model_score ) ;
best_score_sample = current_score . isBetter ( best_score ) ? best_score : current_score ;
} else continue ;
} else {
best_score_sample = current_score ;
update_best ( models [ i ] , current_score ) ;
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
if ( LO & & ( ( iters < max_iters & & best_score . inlier_number > min_non_random_inliers & & IoU < IOU_SIMILARITY_THR ) ) )
run_lo ( best_model , best_score , false ) ;
2020-08-15 06:42:26 +08:00
} // end of if so far the best score
} // end loop of number of models
2023-06-16 15:59:13 +08:00
if ( adapt & & iters > = MAX_ITERS_ADAPT & & num_total_tested_models > = MAX_MODELS_ADAPT ) {
adapt = false ;
lambda_non_random_all_inliers = getLambda ( supports , 2.32 , points_size , sample_size , false , min_non_random_inliers ) ;
_model_verifier - > updateSPRT ( params - > getTimeForModelEstimation ( ) , 1.0 , mean_num_est_models / num_estimations , lambda_non_random_all_inliers / points_size , ( double ) std : : max ( min_non_random_inliers , best_score . inlier_number ) / points_size , best_score ) ;
2020-10-07 04:37:49 +08:00
}
2020-08-15 06:42:26 +08:00
} // end main while loop
final_iters = iters ;
2023-06-16 15:59:13 +08:00
if ( ! was_LO_run & & ! best_model . empty ( ) & & LO )
run_lo ( best_model , best_score , true ) ;
} else { // parallel VSAC
const int MAX_THREADS = getNumThreads ( ) , growth_max_samples = params - > getProsacMaxSamples ( ) ;
2020-08-15 06:42:26 +08:00
const bool is_prosac = params - > getSampler ( ) = = SamplingMethod : : SAMPLING_PROSAC ;
std : : atomic_bool success ( false ) ;
2023-06-16 15:59:13 +08:00
std : : atomic_int num_hypothesis_tested ( 0 ) , thread_cnt ( 0 ) , max_number_inliers ( 0 ) , subset_size , termination_length ;
std : : atomic < double > best_score_all ( std : : numeric_limits < double > : : max ( ) ) ;
std : : vector < Score > best_scores ( MAX_THREADS ) , best_scores_not_LO ;
std : : vector < Mat > best_models ( MAX_THREADS ) , best_models_not_LO , K1_apx , K2_apx ;
std : : vector < int > num_tested_models_threads ( MAX_THREADS ) , growth_function , non_random_inliers ;
std : : vector < std : : vector < Mat > > tested_models_threads ( MAX_THREADS ) ;
std : : vector < std : : vector < std : : vector < int > > > tested_samples_threads ( MAX_THREADS ) ;
std : : vector < std : : vector < int > > best_samples_threads ( MAX_THREADS ) ;
std : : vector < bool > last_model_from_LO_vec ;
std : : vector < double > lambda_non_random_all_inliers_vec ( MAX_THREADS ) ;
if ( IS_FUNDAMENTAL ) {
last_model_from_LO_vec = std : : vector < bool > ( MAX_THREADS ) ;
best_models_not_LO = std : : vector < Mat > ( MAX_THREADS ) ;
best_scores_not_LO = std : : vector < Score > ( MAX_THREADS ) ;
K1_apx = std : : vector < Mat > ( MAX_THREADS ) ;
K2_apx = std : : vector < Mat > ( MAX_THREADS ) ;
}
if ( is_prosac ) {
growth_function = _sampler . dynamicCast < ProsacSampler > ( ) - > getGrowthFunction ( ) ;
subset_size = 2 * sample_size ; // n, size of the current sampling pool
termination_length = points_size ;
}
2020-08-15 06:42:26 +08:00
///////////////////////////////////////////////////////////////////////////////////////////////////////
parallel_for_ ( Range ( 0 , MAX_THREADS ) , [ & ] ( const Range & /*range*/ ) {
if ( ! success ) { // cover all if not success to avoid thread creating new variables
const int thread_rng_id = thread_cnt + + ;
2023-06-16 15:59:13 +08:00
bool adapt = params - > getVerifier ( ) = = VerificationMethod : : ASPRT | | IS_NON_RAND_TEST ;
int thread_state = _state + thread_rng_id , min_non_random_inliers = 0 , num_tested_models = 0 ,
num_estimations = 0 , mean_num_est_models = 0 , iters , max_iters = params - > getMaxIters ( ) ;
double IoU = 0 , lambda_non_random_all_inliers_thread = - 1 ;
std : : vector < Mat > tested_models_thread ; tested_models_thread . reserve ( MAX_TEST_MODELS_NONRAND ) ;
std : : vector < std : : vector < int > > tested_samples_thread ; tested_samples_thread . reserve ( MAX_TEST_MODELS_NONRAND ) ;
Ptr < UniformRandomGenerator > random_gen ;
if ( is_prosac ) random_gen = UniformRandomGenerator : : create ( thread_state ) ;
Ptr < Error > error ;
Ptr < Estimator > estimator ;
Ptr < Degeneracy > degeneracy ;
Ptr < Quality > quality ;
Ptr < ModelVerifier > model_verifier ;
2020-08-15 06:42:26 +08:00
Ptr < Sampler > sampler ;
2023-06-16 15:59:13 +08:00
Ptr < RandomGenerator > lo_sampler ;
Ptr < Termination > termination ;
Ptr < LocalOptimization > local_optimization ;
Ptr < MinimalSolver > min_solver ;
Ptr < NonMinimalSolver > non_min_solver ;
Ptr < WeightFunction > weight_fnc ;
initialize ( thread_state , min_solver , non_min_solver , error , estimator , degeneracy , quality ,
model_verifier , local_optimization , termination , sampler , lo_sampler , weight_fnc , true ) ;
bool is_last_from_LO_thread = false ;
Mat best_model_thread , non_degenerate_model , lo_model , best_not_LO_thread ;
Score best_score_thread , current_score , non_denegenerate_model_score , lo_score , best_score_all_threads , best_not_LO_score_thread ;
std : : vector < int > sample ( estimator - > getMinimalSampleSize ( ) ) , best_sample_thread , supports ;
supports . reserve ( 3 * MAX_MODELS_ADAPT ) ; // store model supports
std : : vector < bool > best_inliers_mask_local ( points_size , false ) , model_inliers_mask ( points_size , false ) ;
2020-08-15 06:42:26 +08:00
std : : vector < Mat > models ( estimator - > getMaxNumSolutions ( ) ) ;
2023-06-16 15:59:13 +08:00
auto update_best = [ & ] ( const Score & new_score , const Mat & new_model , bool from_LO = false ) {
// update best score of all threads
if ( max_number_inliers < new_score . inlier_number ) max_number_inliers = new_score . inlier_number ;
if ( best_score_all > new_score . score ) best_score_all = new_score . score ;
best_score_all_threads = Score ( max_number_inliers , best_score_all ) ;
//
quality - > getInliers ( new_model , model_inliers_mask ) ;
IoU = Utils : : intersectionOverUnion ( best_inliers_mask_local , model_inliers_mask ) ;
if ( ! best_model_thread . empty ( ) & & ( int ) tested_models_thread . size ( ) < MAX_TEST_MODELS_NONRAND & & IoU < IOU_SIMILARITY_THR ) {
tested_models_thread . emplace_back ( best_model_thread . clone ( ) ) ;
tested_samples_thread . emplace_back ( best_sample_thread ) ;
}
if ( ! adapt ) { // update quality and verifier
quality - > setBestScore ( best_score_all ) ;
model_verifier - > update ( best_score_all_threads , iters ) ;
}
2020-08-15 06:42:26 +08:00
// copy new score to best score
best_score_thread = new_score ;
2023-06-16 15:59:13 +08:00
best_sample_thread = sample ;
best_inliers_mask_local = model_inliers_mask ;
2020-08-15 06:42:26 +08:00
// remember best model
new_model . copyTo ( best_model_thread ) ;
2023-06-16 15:59:13 +08:00
2020-10-07 04:37:49 +08:00
// update upper bound of iterations
2023-06-16 15:59:13 +08:00
if ( is_prosac ) {
int new_termination_length ;
max_iters = termination . dynamicCast < ProsacTerminationCriteria > ( ) - >
updateTerminationLength ( best_model_thread , best_score_thread . inlier_number , new_termination_length ) ;
// update termination length
if ( new_termination_length < termination_length )
termination_length = new_termination_length ;
} else max_iters = termination - > update ( best_model_thread , max_number_inliers ) ;
if ( IS_FUNDAMENTAL ) {
is_last_from_LO_thread = from_LO ;
if ( ! from_LO ) {
best_model_thread . copyTo ( best_not_LO_thread ) ;
best_not_LO_score_thread = best_score_thread ;
}
}
2020-08-15 06:42:26 +08:00
} ;
2020-10-07 04:37:49 +08:00
bool was_LO_run = false ;
2023-06-16 15:59:13 +08:00
auto runLO = [ & ] ( int current_ransac_iters ) {
was_LO_run = true ;
local_optimization - > setCurrentRANSACiter ( current_ransac_iters ) ;
if ( local_optimization - > refineModel ( best_model_thread , best_score_thread , lo_model ,
lo_score ) & & lo_score . isBetter ( best_score_thread ) )
update_best ( lo_score , lo_model , true ) ;
} ;
2020-08-15 06:42:26 +08:00
for ( iters = 0 ; iters < max_iters & & ! success ; iters + + ) {
success = num_hypothesis_tested + + > max_iters ;
2023-06-16 15:59:13 +08:00
if ( iters % 10 & & ! adapt ) {
2020-08-15 06:42:26 +08:00
// Synchronize threads. just to speed verification of model.
2023-06-16 15:59:13 +08:00
quality - > setBestScore ( std : : min ( best_score_thread . score , ( double ) best_score_all ) ) ;
model_verifier - > update ( best_score_thread . inlier_number > max_number_inliers ? best_score_thread : best_score_all_threads , iters ) ;
2020-08-15 06:42:26 +08:00
}
if ( is_prosac ) {
2023-06-16 15:59:13 +08:00
if ( num_hypothesis_tested > growth_max_samples ) {
// if PROSAC has not converged to solution then do uniform sampling.
random_gen - > generateUniqueRandomSet ( sample , sample_size , points_size ) ;
} else {
if ( num_hypothesis_tested > = growth_function [ subset_size - 1 ] & & subset_size < termination_length - MAX_THREADS ) {
subset_size + + ;
if ( subset_size > = points_size ) subset_size = points_size - 1 ;
}
if ( growth_function [ subset_size - 1 ] < num_hypothesis_tested ) {
// The sample contains m-1 points selected from U_(n-1) at random and u_n
random_gen - > generateUniqueRandomSet ( sample , sample_size - 1 , subset_size - 1 ) ;
sample [ sample_size - 1 ] = subset_size - 1 ;
} else
// Select m points from U_n at random.
random_gen - > generateUniqueRandomSet ( sample , sample_size , subset_size ) ;
}
2020-08-15 06:42:26 +08:00
} else sampler - > generateSample ( sample ) ; // use local sampler
const int number_of_models = estimator - > estimateModels ( sample , models ) ;
2023-06-16 15:59:13 +08:00
if ( adapt ) {
num_estimations + + ; mean_num_est_models + = number_of_models ;
}
2020-08-15 06:42:26 +08:00
for ( int i = 0 ; i < number_of_models ; i + + ) {
2023-06-16 15:59:13 +08:00
num_tested_models + + ;
if ( adapt ) {
2020-10-07 04:37:49 +08:00
current_score = quality - > getScore ( models [ i ] ) ;
2023-06-16 15:59:13 +08:00
supports . emplace_back ( current_score . inlier_number ) ;
} else if ( ! model_verifier - > isModelGood ( models [ i ] , current_score ) )
continue ;
2020-08-15 06:42:26 +08:00
if ( current_score . isBetter ( best_score_all_threads ) ) {
2023-06-16 15:59:13 +08:00
if ( degeneracy - > recoverIfDegenerate ( sample , models [ i ] , current_score ,
non_degenerate_model , non_denegenerate_model_score ) ) {
2020-08-15 06:42:26 +08:00
// check if best non degenerate model is better than so far the best model
if ( non_denegenerate_model_score . isBetter ( best_score_thread ) )
2023-06-16 15:59:13 +08:00
update_best ( non_denegenerate_model_score , non_degenerate_model ) ;
2020-10-07 04:37:49 +08:00
else continue ;
2023-06-16 15:59:13 +08:00
} else update_best ( current_score , models [ i ] ) ;
if ( LO & & num_hypothesis_tested < max_iters & & IoU < IOU_SIMILARITY_THR & &
best_score_thread . inlier_number > min_non_random_inliers )
runLO ( iters ) ;
2020-08-15 06:42:26 +08:00
} // end of if so far the best score
2023-06-16 15:59:13 +08:00
else if ( ( int ) tested_models_thread . size ( ) < MAX_TEST_MODELS_NONRAND ) {
tested_models_thread . emplace_back ( models [ i ] . clone ( ) ) ;
tested_samples_thread . emplace_back ( sample ) ;
}
if ( num_hypothesis_tested > max_iters ) {
success = true ; break ;
}
2020-08-15 06:42:26 +08:00
} // end loop of number of models
2023-06-16 15:59:13 +08:00
if ( adapt & & iters > = MAX_ITERS_ADAPT & & num_tested_models > = MAX_MODELS_ADAPT ) {
adapt = false ;
lambda_non_random_all_inliers_thread = getLambda ( supports , 2.32 , points_size , sample_size , false , min_non_random_inliers ) ;
model_verifier - > updateSPRT ( params - > getTimeForModelEstimation ( ) , 1 , ( double ) mean_num_est_models / num_estimations , lambda_non_random_all_inliers_thread / points_size ,
( double ) std : : max ( min_non_random_inliers , best_score . inlier_number ) / points_size , best_score_all_threads ) ;
2020-10-07 04:37:49 +08:00
}
2023-06-16 15:59:13 +08:00
if ( ! adapt & & LO & & num_hypothesis_tested < max_iters & & ! was_LO_run & & ! best_model_thread . empty ( ) & &
best_score_thread . inlier_number > min_non_random_inliers )
runLO ( iters ) ;
2020-08-15 06:42:26 +08:00
} // end of loop over iters
2023-06-16 15:59:13 +08:00
if ( ! was_LO_run & & ! best_model_thread . empty ( ) & & LO )
runLO ( - 1 /*use full iterations of LO*/ ) ;
best_model_thread . copyTo ( best_models [ thread_rng_id ] ) ;
best_scores [ thread_rng_id ] = best_score_thread ;
num_tested_models_threads [ thread_rng_id ] = num_tested_models ;
tested_models_threads [ thread_rng_id ] = tested_models_thread ;
tested_samples_threads [ thread_rng_id ] = tested_samples_thread ;
best_samples_threads [ thread_rng_id ] = best_sample_thread ;
if ( IS_FUNDAMENTAL ) {
best_scores_not_LO [ thread_rng_id ] = best_not_LO_score_thread ;
best_not_LO_thread . copyTo ( best_models_not_LO [ thread_rng_id ] ) ;
last_model_from_LO_vec [ thread_rng_id ] = is_last_from_LO_thread ;
}
lambda_non_random_all_inliers_vec [ thread_rng_id ] = lambda_non_random_all_inliers_thread ;
2020-08-15 06:42:26 +08:00
} } ) ; // end parallel
///////////////////////////////////////////////////////////////////////////////////////////////////////
// find best model from all threads' models
best_score = best_scores [ 0 ] ;
int best_thread_idx = 0 ;
2023-06-16 15:59:13 +08:00
for ( int i = 1 ; i < MAX_THREADS ; i + + )
2020-08-15 06:42:26 +08:00
if ( best_scores [ i ] . isBetter ( best_score ) ) {
best_score = best_scores [ i ] ;
best_thread_idx = i ;
}
best_model = best_models [ best_thread_idx ] ;
2023-06-16 15:59:13 +08:00
if ( IS_FUNDAMENTAL ) {
last_model_from_LO = last_model_from_LO_vec [ best_thread_idx ] ;
K1_approx = K1_apx [ best_thread_idx ] ;
K2_approx = K2_apx [ best_thread_idx ] ;
}
2020-08-15 06:42:26 +08:00
final_iters = num_hypothesis_tested ;
2023-06-16 15:59:13 +08:00
best_sample = best_samples_threads [ best_thread_idx ] ;
int num_lambdas = 0 ;
double avg_lambda = 0 ;
for ( int i = 0 ; i < MAX_THREADS ; i + + ) {
if ( IS_FUNDAMENTAL & & best_scores_not_LO [ i ] . isBetter ( best_score_model_not_from_LO ) ) {
best_score_model_not_from_LO = best_scores_not_LO [ i ] ;
best_models_not_LO [ i ] . copyTo ( best_model_not_from_LO ) ;
}
if ( IS_NON_RAND_TEST & & lambda_non_random_all_inliers_vec [ i ] > 0 ) {
num_lambdas + + ;
avg_lambda + = lambda_non_random_all_inliers_vec [ i ] ;
}
num_total_tested_models + = num_tested_models_threads [ i ] ;
if ( ( int ) models_for_random_test . size ( ) < MAX_TEST_MODELS_NONRAND ) {
for ( int m = 0 ; m < ( int ) tested_models_threads [ i ] . size ( ) ; m + + ) {
models_for_random_test . emplace_back ( tested_models_threads [ i ] [ m ] . clone ( ) ) ;
samples_for_random_test . emplace_back ( tested_samples_threads [ i ] [ m ] ) ;
if ( ( int ) models_for_random_test . size ( ) = = MAX_TEST_MODELS_NONRAND )
break ;
}
}
}
if ( IS_NON_RAND_TEST & & num_lambdas > 0 & & avg_lambda > 0 )
lambda_non_random_all_inliers = avg_lambda / num_lambdas ;
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
if ( best_model . empty ( ) ) {
ransac_output = RansacOutput : : create ( best_model , std : : vector < bool > ( ) , best_score . inlier_number , final_iters , ModelConfidence : : RANDOM , std : : vector < float > ( ) ) ;
2020-08-15 06:42:26 +08:00
return false ;
2023-06-16 15:59:13 +08:00
}
if ( last_model_from_LO & & IS_FUNDAMENTAL & & K1 . empty ( ) & & K2 . empty ( ) ) {
Score new_score ; Mat new_model ;
const double INL_THR = 0.80 ;
if ( parallel )
_quality - > getInliers ( best_model , best_inliers_mask ) ;
// run additional degeneracy check for F:
if ( _degeneracy . dynamicCast < FundamentalDegeneracy > ( ) - > verifyFundamental ( best_model , best_score , best_inliers_mask , new_model , new_score ) ) {
// so-far-the-best F is degenerate
// Update best F using non-degenerate F or the one which is not from LO
if ( new_score . isBetter ( best_score_model_not_from_LO ) & & new_score . inlier_number > INL_THR * best_score . inlier_number ) {
best_score = new_score ;
new_model . copyTo ( best_model ) ;
} else if ( best_score_model_not_from_LO . inlier_number > INL_THR * best_score . inlier_number ) {
best_score = best_score_model_not_from_LO ;
best_model_not_from_LO . copyTo ( best_model ) ;
}
} else { // so-far-the-best F is not degenerate
if ( new_score . isBetter ( best_score ) ) {
// if new model is better then update
best_score = new_score ;
new_model . copyTo ( best_model ) ;
}
}
}
if ( params - > getFinalPolisher ( ) ! = PolishingMethod : : NONE_POLISHER ) {
2020-08-15 06:42:26 +08:00
Mat polished_model ;
Score polisher_score ;
2023-06-16 15:59:13 +08:00
if ( polisher - > polishSoFarTheBestModel ( best_model , best_score , // polish final model
polished_model , polisher_score ) & & polisher_score . isBetter ( best_score ) ) {
best_score = polisher_score ;
polished_model . copyTo ( best_model ) ;
}
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
///////////////// get inliers of the best model and points' residuals ///////////////
std : : vector < bool > inliers_mask ; std : : vector < float > residuals ;
2020-08-15 06:42:26 +08:00
if ( params - > isMaskRequired ( ) ) {
inliers_mask = std : : vector < bool > ( points_size ) ;
2023-06-16 15:59:13 +08:00
residuals = _error - > getErrors ( best_model ) ;
_quality - > getInliers ( residuals , inliers_mask , threshold ) ;
}
ModelConfidence model_conf = ModelConfidence : : UNKNOWN ;
if ( IS_NON_RAND_TEST ) {
std : : vector < int > temp_inliers ( points_size ) ;
const int non_random_inls_best_model = getIndependentInliers ( best_model , best_sample , temp_inliers ,
_quality - > getInliers ( best_model , temp_inliers ) ) ;
// quick test on lambda from all inliers (= upper bound of independent inliers)
// if model with independent inliers is not random for Poisson with all inliers then it is not random using independent inliers too
if ( pow ( Utils : : getPoissonCDF ( lambda_non_random_all_inliers , non_random_inls_best_model ) , num_total_tested_models ) < 0.9999 ) {
std : : vector < int > inliers_list ( models_for_random_test . size ( ) ) ;
for ( int m = 0 ; m < ( int ) models_for_random_test . size ( ) ; m + + )
inliers_list [ m ] = getIndependentInliers ( models_for_random_test [ m ] , samples_for_random_test [ m ] ,
temp_inliers , _quality - > getInliers ( models_for_random_test [ m ] , temp_inliers ) ) ;
int min_non_rand_inliers ;
const double lambda = getLambda ( inliers_list , 1.644 , points_size , sample_size , true , min_non_rand_inliers ) ;
const double cdf_lambda = Utils : : getPoissonCDF ( lambda , non_random_inls_best_model ) , cdf_N = pow ( cdf_lambda , num_total_tested_models ) ;
model_conf = cdf_N < 0.9999 ? ModelConfidence : : RANDOM : ModelConfidence : : NON_RANDOM ;
} else model_conf = ModelConfidence : : NON_RANDOM ;
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
ransac_output = RansacOutput : : create ( best_model , inliers_mask , best_score . inlier_number , final_iters , model_conf , residuals ) ;
2020-08-15 06:42:26 +08:00
return true ;
}
} ;
/*
* pts1 , pts2 are matrices either N x a , N x b or a x N or b x N , where N > a and N > b
* pts1 are image points , if pnp pts2 are object points otherwise - image points as well .
* output is matrix of size N x ( a + b )
* return points_size = N
*/
int mergePoints ( InputArray pts1_ , InputArray pts2_ , Mat & pts , bool ispnp ) {
Mat pts1 = pts1_ . getMat ( ) , pts2 = pts2_ . getMat ( ) ;
auto convertPoints = [ ] ( Mat & points , int pt_dim ) {
points . convertTo ( points , CV_32F ) ; // convert points to have float precision
if ( points . channels ( ) > 1 )
points = points . reshape ( 1 , ( int ) points . total ( ) ) ; // convert point to have 1 channel
if ( points . rows < points . cols )
transpose ( points , points ) ; // transpose so points will be in rows
CV_CheckGE ( points . cols , pt_dim , " Invalid dimension of point " ) ;
if ( points . cols ! = pt_dim ) // in case when image points are 3D convert them to 2D
points = points . colRange ( 0 , pt_dim ) ;
} ;
convertPoints ( pts1 , 2 ) ; // pts1 are always image points
convertPoints ( pts2 , ispnp ? 3 : 2 ) ; // for PnP points are 3D
// points are of size [Nx2 Nx2] = Nx4 for H, F, E
// points are of size [Nx2 Nx3] = Nx5 for PnP
hconcat ( pts1 , pts2 , pts ) ;
return pts . rows ;
}
void saveMask ( OutputArray mask , const std : : vector < bool > & inliers_mask ) {
if ( mask . needed ( ) ) {
const int points_size = ( int ) inliers_mask . size ( ) ;
2021-04-20 17:57:42 +08:00
Mat tmp_mask ( points_size , 1 , CV_8U ) ;
auto * maskptr = tmp_mask . ptr < uchar > ( ) ;
2020-08-15 06:42:26 +08:00
for ( int i = 0 ; i < points_size ; i + + )
maskptr [ i ] = ( uchar ) inliers_mask [ i ] ;
2021-04-20 17:57:42 +08:00
tmp_mask . copyTo ( mask ) ;
2020-08-15 06:42:26 +08:00
}
}
void setParameters ( Ptr < Model > & params , EstimationMethod estimator , const UsacParams & usac_params ,
bool mask_needed ) {
params = Model : : create ( usac_params . threshold , estimator , usac_params . sampler ,
usac_params . confidence , usac_params . maxIterations , usac_params . score ) ;
params - > setLocalOptimization ( usac_params . loMethod ) ;
params - > setLOSampleSize ( usac_params . loSampleSize ) ;
params - > setLOIterations ( usac_params . loIterations ) ;
params - > setParallel ( usac_params . isParallel ) ;
params - > setNeighborsType ( usac_params . neighborsSearch ) ;
params - > setRandomGeneratorState ( usac_params . randomGeneratorState ) ;
params - > maskRequired ( mask_needed ) ;
}
void setParameters ( int flag , Ptr < Model > & params , EstimationMethod estimator , double thr ,
int max_iters , double conf , bool mask_needed ) {
switch ( flag ) {
case USAC_DEFAULT :
params = Model : : create ( thr , estimator , SamplingMethod : : SAMPLING_UNIFORM , conf , max_iters ,
ScoreMethod : : SCORE_METHOD_MSAC ) ;
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_INNER_AND_ITER_LO ) ;
break ;
case USAC_MAGSAC :
params = Model : : create ( thr , estimator , SamplingMethod : : SAMPLING_UNIFORM , conf , max_iters ,
ScoreMethod : : SCORE_METHOD_MAGSAC ) ;
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_SIGMA ) ;
2020-10-07 04:37:49 +08:00
params - > setLOSampleSize ( params - > isHomography ( ) ? 75 : 50 ) ;
params - > setLOIterations ( params - > isHomography ( ) ? 15 : 10 ) ;
2020-08-15 06:42:26 +08:00
break ;
case USAC_PARALLEL :
params = Model : : create ( thr , estimator , SamplingMethod : : SAMPLING_UNIFORM , conf , max_iters ,
ScoreMethod : : SCORE_METHOD_MSAC ) ;
params - > setParallel ( true ) ;
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_INNER_LO ) ;
break ;
case USAC_ACCURATE :
params = Model : : create ( thr , estimator , SamplingMethod : : SAMPLING_UNIFORM , conf , max_iters ,
ScoreMethod : : SCORE_METHOD_MSAC ) ;
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_GC ) ;
2020-10-07 04:37:49 +08:00
params - > setLOSampleSize ( 20 ) ;
params - > setLOIterations ( 25 ) ;
2020-08-15 06:42:26 +08:00
break ;
case USAC_FAST :
params = Model : : create ( thr , estimator , SamplingMethod : : SAMPLING_UNIFORM , conf , max_iters ,
2020-10-07 04:37:49 +08:00
ScoreMethod : : SCORE_METHOD_MSAC ) ;
2020-08-15 06:42:26 +08:00
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_INNER_AND_ITER_LO ) ;
2020-10-07 04:37:49 +08:00
params - > setLOIterations ( 5 ) ;
params - > setLOIterativeIters ( 3 ) ;
2020-08-15 06:42:26 +08:00
break ;
case USAC_PROSAC :
params = Model : : create ( thr , estimator , SamplingMethod : : SAMPLING_PROSAC , conf , max_iters ,
ScoreMethod : : SCORE_METHOD_MSAC ) ;
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_INNER_LO ) ;
break ;
case USAC_FM_8PTS :
2023-06-16 15:59:13 +08:00
params = Model : : create ( thr , EstimationMethod : : FUNDAMENTAL8 , SamplingMethod : : SAMPLING_UNIFORM ,
2020-08-15 06:42:26 +08:00
conf , max_iters , ScoreMethod : : SCORE_METHOD_MSAC ) ;
params - > setLocalOptimization ( LocalOptimMethod : : LOCAL_OPTIM_INNER_LO ) ;
break ;
default : CV_Error ( cv : : Error : : StsBadFlag , " Incorrect flag for USAC! " ) ;
}
2020-10-07 04:37:49 +08:00
// do not do too many iterations for PnP
if ( estimator = = EstimationMethod : : P3P ) {
2023-06-16 15:59:13 +08:00
if ( params - > getLOInnerMaxIters ( ) > 10 )
params - > setLOIterations ( 10 ) ;
2020-10-07 04:37:49 +08:00
params - > setLOIterativeIters ( 0 ) ;
2023-06-16 15:59:13 +08:00
params - > setFinalLSQ ( 3 ) ;
2020-10-07 04:37:49 +08:00
}
2020-08-15 06:42:26 +08:00
params - > maskRequired ( mask_needed ) ;
}
Mat findHomography ( InputArray srcPoints , InputArray dstPoints , int method , double thr ,
OutputArray mask , const int max_iters , const double confidence ) {
Ptr < Model > params ;
2023-06-16 15:59:13 +08:00
setParameters ( method , params , EstimationMethod : : HOMOGRAPHY , thr , max_iters , confidence , mask . needed ( ) ) ;
2020-08-15 06:42:26 +08:00
Ptr < RansacOutput > ransac_output ;
2023-06-16 15:59:13 +08:00
if ( run ( params , srcPoints , dstPoints ,
2020-08-15 06:42:26 +08:00
ransac_output , noArray ( ) , noArray ( ) , noArray ( ) , noArray ( ) ) ) {
saveMask ( mask , ransac_output - > getInliersMask ( ) ) ;
return ransac_output - > getModel ( ) / ransac_output - > getModel ( ) . at < double > ( 2 , 2 ) ;
2020-10-07 04:37:49 +08:00
}
if ( mask . needed ( ) ) {
mask . create ( std : : max ( srcPoints . getMat ( ) . rows , srcPoints . getMat ( ) . cols ) , 1 , CV_8U ) ;
mask . setTo ( Scalar : : all ( 0 ) ) ;
}
return Mat ( ) ;
2020-08-15 06:42:26 +08:00
}
Mat findFundamentalMat ( InputArray points1 , InputArray points2 , int method , double thr ,
double confidence , int max_iters , OutputArray mask ) {
Ptr < Model > params ;
2023-06-16 15:59:13 +08:00
setParameters ( method , params , EstimationMethod : : FUNDAMENTAL , thr , max_iters , confidence , mask . needed ( ) ) ;
2020-08-15 06:42:26 +08:00
Ptr < RansacOutput > ransac_output ;
2023-06-16 15:59:13 +08:00
if ( run ( params , points1 , points2 ,
2020-08-15 06:42:26 +08:00
ransac_output , noArray ( ) , noArray ( ) , noArray ( ) , noArray ( ) ) ) {
saveMask ( mask , ransac_output - > getInliersMask ( ) ) ;
return ransac_output - > getModel ( ) ;
2020-10-07 04:37:49 +08:00
}
if ( mask . needed ( ) ) {
mask . create ( std : : max ( points1 . getMat ( ) . rows , points1 . getMat ( ) . cols ) , 1 , CV_8U ) ;
mask . setTo ( Scalar : : all ( 0 ) ) ;
}
return Mat ( ) ;
2020-08-15 06:42:26 +08:00
}
Mat findEssentialMat ( InputArray points1 , InputArray points2 , InputArray cameraMatrix1 ,
2022-12-15 20:00:48 +08:00
int method , double prob , double thr , OutputArray mask , int maxIters ) {
2020-08-15 06:42:26 +08:00
Ptr < Model > params ;
2023-06-16 15:59:13 +08:00
setParameters ( method , params , EstimationMethod : : ESSENTIAL , thr , maxIters , prob , mask . needed ( ) ) ;
2020-08-15 06:42:26 +08:00
Ptr < RansacOutput > ransac_output ;
2023-06-16 15:59:13 +08:00
if ( run ( params , points1 , points2 ,
2020-08-15 06:42:26 +08:00
ransac_output , cameraMatrix1 , cameraMatrix1 , noArray ( ) , noArray ( ) ) ) {
saveMask ( mask , ransac_output - > getInliersMask ( ) ) ;
return ransac_output - > getModel ( ) ;
2020-10-07 04:37:49 +08:00
}
if ( mask . needed ( ) ) {
mask . create ( std : : max ( points1 . getMat ( ) . rows , points1 . getMat ( ) . cols ) , 1 , CV_8U ) ;
mask . setTo ( Scalar : : all ( 0 ) ) ;
}
return Mat ( ) ;
2020-08-15 06:42:26 +08:00
}
bool solvePnPRansac ( InputArray objectPoints , InputArray imagePoints ,
InputArray cameraMatrix , InputArray distCoeffs , OutputArray rvec , OutputArray tvec ,
bool /*useExtrinsicGuess*/ , int max_iters , float thr , double conf ,
2021-05-19 17:09:46 +08:00
OutputArray inliers , int method ) {
2020-08-15 06:42:26 +08:00
Ptr < Model > params ;
setParameters ( method , params , cameraMatrix . empty ( ) ? EstimationMethod : : P6P : EstimationMethod : : P3P ,
2021-05-19 17:09:46 +08:00
thr , max_iters , conf , inliers . needed ( ) ) ;
2020-08-15 06:42:26 +08:00
Ptr < RansacOutput > ransac_output ;
2023-06-16 15:59:13 +08:00
if ( run ( params , imagePoints , objectPoints ,
2020-08-15 06:42:26 +08:00
ransac_output , cameraMatrix , noArray ( ) , distCoeffs , noArray ( ) ) ) {
2021-05-19 17:09:46 +08:00
if ( inliers . needed ( ) ) {
const auto & inliers_mask = ransac_output - > getInliersMask ( ) ;
Mat inliers_ ;
for ( int i = 0 ; i < ( int ) inliers_mask . size ( ) ; i + + )
if ( inliers_mask [ i ] )
inliers_ . push_back ( i ) ;
inliers_ . copyTo ( inliers ) ;
}
2020-08-15 06:42:26 +08:00
const Mat & model = ransac_output - > getModel ( ) ;
model . col ( 0 ) . copyTo ( rvec ) ;
model . col ( 1 ) . copyTo ( tvec ) ;
return true ;
2020-10-07 04:37:49 +08:00
}
return false ;
2020-08-15 06:42:26 +08:00
}
Mat estimateAffine2D ( InputArray from , InputArray to , OutputArray mask , int method ,
double thr , int max_iters , double conf , int /*refineIters*/ ) {
Ptr < Model > params ;
2023-06-16 15:59:13 +08:00
setParameters ( method , params , EstimationMethod : : AFFINE , thr , max_iters , conf , mask . needed ( ) ) ;
2020-08-15 06:42:26 +08:00
Ptr < RansacOutput > ransac_output ;
2023-06-16 15:59:13 +08:00
if ( run ( params , from , to ,
2020-08-15 06:42:26 +08:00
ransac_output , noArray ( ) , noArray ( ) , noArray ( ) , noArray ( ) ) ) {
saveMask ( mask , ransac_output - > getInliersMask ( ) ) ;
return ransac_output - > getModel ( ) . rowRange ( 0 , 2 ) ;
2020-10-07 04:37:49 +08:00
}
if ( mask . needed ( ) ) {
mask . create ( std : : max ( from . getMat ( ) . rows , from . getMat ( ) . cols ) , 1 , CV_8U ) ;
mask . setTo ( Scalar : : all ( 0 ) ) ;
}
return Mat ( ) ;
2020-08-15 06:42:26 +08:00
}
class ModelImpl : public Model {
private :
// main parameters:
2023-06-16 15:59:13 +08:00
double threshold ;
2020-08-15 06:42:26 +08:00
EstimationMethod estimator ;
SamplingMethod sampler ;
2023-06-16 15:59:13 +08:00
double confidence ;
int max_iterations ;
2020-08-15 06:42:26 +08:00
ScoreMethod score ;
2023-06-16 15:59:13 +08:00
int sample_size ;
// Larsson parameters
bool is_larsson_optimization = true ;
int larsson_leven_marq_iters_lo = 10 , larsson_leven_marq_iters_fo = 15 ;
// solver for a null-space extraction
MethodSolver null_solver = GEM_SOLVER ;
// prosac
int prosac_max_samples = 200000 ;
2020-08-15 06:42:26 +08:00
// for neighborhood graph
int k_nearest_neighbors = 8 ; //, flann_search_params = 5, num_kd_trees = 1; // for FLANN
2020-10-07 04:37:49 +08:00
int cell_size = 50 ; // pixels, for grid neighbors searching
int radius = 30 ; // pixels, for radius-search neighborhood graph
2020-08-15 06:42:26 +08:00
NeighborSearchMethod neighborsType = NeighborSearchMethod : : NEIGH_GRID ;
// Local Optimization parameters
2023-06-16 15:59:13 +08:00
LocalOptimMethod lo = LocalOptimMethod : : LOCAL_OPTIM_INNER_LO ;
int lo_sample_size = 12 , lo_inner_iterations = 20 , lo_iterative_iterations = 8 ,
lo_thr_multiplier = 10 , lo_iter_sample_size = 30 ;
2020-08-15 06:42:26 +08:00
// Graph cut parameters
const double spatial_coherence_term = 0.975 ;
// apply polisher for final RANSAC model
2023-06-16 15:59:13 +08:00
PolishingMethod polisher = PolishingMethod : : COV_POLISHER ;
2020-08-15 06:42:26 +08:00
// preemptive verification test
2023-06-16 15:59:13 +08:00
VerificationMethod verifier = VerificationMethod : : ASPRT ;
2020-08-15 06:42:26 +08:00
// sprt parameters
2023-06-16 15:59:13 +08:00
// lower bound estimate is 2% of inliers
// model estimation to verification time = ratio of time needed to estimate model
// to verification of one point wrt the model
double sprt_eps = 0.02 , sprt_delta = 0.008 , avg_num_models , model_est_to_ver_time ;
2020-08-15 06:42:26 +08:00
// estimator error
ErrorMetric est_error ;
// progressive napsac
double relax_coef = 0.1 ;
// for building neighborhood graphs
2023-06-16 15:59:13 +08:00
const std : : vector < int > grid_cell_number = { 10 , 5 , 2 } ;
2020-08-15 06:42:26 +08:00
//for final least squares polisher
2023-06-16 15:59:13 +08:00
int final_lsq_iters = 7 ;
bool need_mask = true , // do we need inlier mask in the end
is_parallel = false , // use parallel RANSAC
is_nonrand_test = false ; // is test for the final model non-randomness
2020-08-15 06:42:26 +08:00
2023-06-16 15:59:13 +08:00
// state of pseudo-random number generator
2020-08-15 06:42:26 +08:00
int random_generator_state = 0 ;
2023-06-16 15:59:13 +08:00
// number of iterations of plane-and-parallax in DEGENSAC^+
int plane_and_parallax_max_iters = 300 ;
2020-08-15 06:42:26 +08:00
// magsac parameters:
2020-10-07 04:37:49 +08:00
int DoF = 2 ;
double sigma_quantile = 3.04 , upper_incomplete_of_sigma_quantile = 0.00419 ,
2023-06-16 15:59:13 +08:00
lower_incomplete_of_sigma_quantile = 0.8629 , C = 0.5 , maximum_thr = 7.5 ;
double k_mlesac = 2.25 ; // parameter for MLESAC model evaluation
2020-08-15 06:42:26 +08:00
public :
2023-06-16 15:59:13 +08:00
ModelImpl ( double threshold_ , EstimationMethod estimator_ , SamplingMethod sampler_ , double confidence_ ,
int max_iterations_ , ScoreMethod score_ ) :
threshold ( threshold_ ) , estimator ( estimator_ ) , sampler ( sampler_ ) , confidence ( confidence_ ) , max_iterations ( max_iterations_ ) , score ( score_ ) {
2020-08-15 06:42:26 +08:00
switch ( estimator_ ) {
2023-06-16 15:59:13 +08:00
case ( EstimationMethod : : AFFINE ) :
avg_num_models = 1 ; model_est_to_ver_time = 50 ;
2020-08-15 06:42:26 +08:00
sample_size = 3 ; est_error = ErrorMetric : : FORW_REPR_ERR ; break ;
2023-06-16 15:59:13 +08:00
case ( EstimationMethod : : HOMOGRAPHY ) :
avg_num_models = 0.8 ; model_est_to_ver_time = 200 ;
2020-08-15 06:42:26 +08:00
sample_size = 4 ; est_error = ErrorMetric : : FORW_REPR_ERR ; break ;
2023-06-16 15:59:13 +08:00
case ( EstimationMethod : : FUNDAMENTAL ) :
DoF = 4 ; C = 0.25 ; sigma_quantile = 3.64 , upper_incomplete_of_sigma_quantile = 0.003657 ; lower_incomplete_of_sigma_quantile = 1.3012 ;
maximum_thr = 2.5 ;
avg_num_models = 1.5 ; model_est_to_ver_time = 200 ;
2020-08-15 06:42:26 +08:00
sample_size = 7 ; est_error = ErrorMetric : : SAMPSON_ERR ; break ;
2023-06-16 15:59:13 +08:00
case ( EstimationMethod : : FUNDAMENTAL8 ) :
avg_num_models = 1 ; model_est_to_ver_time = 100 ; maximum_thr = 2.5 ;
2020-08-15 06:42:26 +08:00
sample_size = 8 ; est_error = ErrorMetric : : SAMPSON_ERR ; break ;
2023-06-16 15:59:13 +08:00
case ( EstimationMethod : : ESSENTIAL ) :
DoF = 4 ; C = 0.25 ; sigma_quantile = 3.64 , upper_incomplete_of_sigma_quantile = 0.003657 ; lower_incomplete_of_sigma_quantile = 1.3012 ;
avg_num_models = 3.93 ; model_est_to_ver_time = 1000 ; maximum_thr = 2 ;
sample_size = 5 ; est_error = ErrorMetric : : SAMPSON_ERR ; break ;
2020-08-15 06:42:26 +08:00
case ( EstimationMethod : : P3P ) :
2023-06-16 15:59:13 +08:00
avg_num_models = 1.38 ; model_est_to_ver_time = 800 ;
2020-08-15 06:42:26 +08:00
sample_size = 3 ; est_error = ErrorMetric : : RERPOJ ; break ;
case ( EstimationMethod : : P6P ) :
2023-06-16 15:59:13 +08:00
avg_num_models = 1 ; model_est_to_ver_time = 300 ;
2020-08-15 06:42:26 +08:00
sample_size = 6 ; est_error = ErrorMetric : : RERPOJ ; break ;
2020-10-07 04:37:49 +08:00
default : CV_Error ( cv : : Error : : StsNotImplemented , " Estimator has not implemented yet! " ) ;
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
if ( score_ = = ScoreMethod : : SCORE_METHOD_MAGSAC )
polisher = PolishingMethod : : MAGSAC ;
// for PnP problem we can use only KNN graph
2020-08-15 06:42:26 +08:00
if ( estimator_ = = EstimationMethod : : P3P | | estimator_ = = EstimationMethod : : P6P ) {
2023-06-16 15:59:13 +08:00
polisher = LSQ_POLISHER ;
2020-08-15 06:42:26 +08:00
neighborsType = NeighborSearchMethod : : NEIGH_FLANN_KNN ;
k_nearest_neighbors = 2 ;
}
}
2023-06-16 15:59:13 +08:00
// setters
void setNonRandomnessTest ( bool set ) override { is_nonrand_test = set ; }
2020-08-15 06:42:26 +08:00
void setVerifier ( VerificationMethod verifier_ ) override { verifier = verifier_ ; }
void setPolisher ( PolishingMethod polisher_ ) override { polisher = polisher_ ; }
void setParallel ( bool is_parallel_ ) override { is_parallel = is_parallel_ ; }
void setError ( ErrorMetric error_ ) override { est_error = error_ ; }
void setLocalOptimization ( LocalOptimMethod lo_ ) override { lo = lo_ ; }
void setKNearestNeighhbors ( int knn_ ) override { k_nearest_neighbors = knn_ ; }
void setNeighborsType ( NeighborSearchMethod neighbors ) override { neighborsType = neighbors ; }
void setCellSize ( int cell_size_ ) override { cell_size = cell_size_ ; }
void setLOIterations ( int iters ) override { lo_inner_iterations = iters ; }
void setLOSampleSize ( int lo_sample_size_ ) override { lo_sample_size = lo_sample_size_ ; }
void maskRequired ( bool need_mask_ ) override { need_mask = need_mask_ ; }
void setRandomGeneratorState ( int state ) override { random_generator_state = state ; }
2023-06-16 15:59:13 +08:00
void setLOIterativeIters ( int iters ) override { lo_iterative_iterations = iters ; }
void setFinalLSQ ( int iters ) override { final_lsq_iters = iters ; }
// getters
int getProsacMaxSamples ( ) const override { return prosac_max_samples ; }
int getLevMarqIters ( ) const override { return larsson_leven_marq_iters_fo ; }
int getLevMarqItersLO ( ) const override { return larsson_leven_marq_iters_lo ; }
bool isNonRandomnessTest ( ) const override { return is_nonrand_test ; }
2020-08-15 06:42:26 +08:00
bool isMaskRequired ( ) const override { return need_mask ; }
NeighborSearchMethod getNeighborsSearch ( ) const override { return neighborsType ; }
int getKNN ( ) const override { return k_nearest_neighbors ; }
ErrorMetric getError ( ) const override { return est_error ; }
EstimationMethod getEstimator ( ) const override { return estimator ; }
int getSampleSize ( ) const override { return sample_size ; }
int getFinalLSQIterations ( ) const override { return final_lsq_iters ; }
int getDegreesOfFreedom ( ) const override { return DoF ; }
double getSigmaQuantile ( ) const override { return sigma_quantile ; }
double getUpperIncompleteOfSigmaQuantile ( ) const override {
return upper_incomplete_of_sigma_quantile ;
}
double getLowerIncompleteOfSigmaQuantile ( ) const override {
return lower_incomplete_of_sigma_quantile ;
}
double getC ( ) const override { return C ; }
2023-06-16 15:59:13 +08:00
double getKmlesac ( ) const override { return k_mlesac ; }
2020-08-15 06:42:26 +08:00
double getMaximumThreshold ( ) const override { return maximum_thr ; }
double getGraphCutSpatialCoherenceTerm ( ) const override { return spatial_coherence_term ; }
int getLOSampleSize ( ) const override { return lo_sample_size ; }
2023-06-16 15:59:13 +08:00
MethodSolver getRansacSolver ( ) const override { return null_solver ; }
2020-08-15 06:42:26 +08:00
PolishingMethod getFinalPolisher ( ) const override { return polisher ; }
int getLOThresholdMultiplier ( ) const override { return lo_thr_multiplier ; }
int getLOIterativeSampleSize ( ) const override { return lo_iter_sample_size ; }
int getLOIterativeMaxIters ( ) const override { return lo_iterative_iterations ; }
int getLOInnerMaxIters ( ) const override { return lo_inner_iterations ; }
2023-06-16 15:59:13 +08:00
int getPlaneAndParallaxIters ( ) const override { return plane_and_parallax_max_iters ; }
2020-08-15 06:42:26 +08:00
LocalOptimMethod getLO ( ) const override { return lo ; }
ScoreMethod getScore ( ) const override { return score ; }
int getMaxIters ( ) const override { return max_iterations ; }
double getConfidence ( ) const override { return confidence ; }
double getThreshold ( ) const override { return threshold ; }
VerificationMethod getVerifier ( ) const override { return verifier ; }
SamplingMethod getSampler ( ) const override { return sampler ; }
int getRandomGeneratorState ( ) const override { return random_generator_state ; }
double getSPRTdelta ( ) const override { return sprt_delta ; }
double getSPRTepsilon ( ) const override { return sprt_eps ; }
double getSPRTavgNumModels ( ) const override { return avg_num_models ; }
int getCellSize ( ) const override { return cell_size ; }
int getGraphRadius ( ) const override { return radius ; }
2023-06-16 15:59:13 +08:00
double getTimeForModelEstimation ( ) const override { return model_est_to_ver_time ; }
2020-08-15 06:42:26 +08:00
double getRelaxCoef ( ) const override { return relax_coef ; }
const std : : vector < int > & getGridCellNumber ( ) const override { return grid_cell_number ; }
2023-06-16 15:59:13 +08:00
bool isLarssonOptimization ( ) const override { return is_larsson_optimization ; }
2020-08-15 06:42:26 +08:00
bool isParallel ( ) const override { return is_parallel ; }
bool isFundamental ( ) const override {
2023-06-16 15:59:13 +08:00
return estimator = = EstimationMethod : : FUNDAMENTAL | |
estimator = = EstimationMethod : : FUNDAMENTAL8 ;
2020-08-15 06:42:26 +08:00
}
2023-06-16 15:59:13 +08:00
bool isHomography ( ) const override { return estimator = = EstimationMethod : : HOMOGRAPHY ; }
bool isEssential ( ) const override { return estimator = = EstimationMethod : : ESSENTIAL ; }
2020-08-15 06:42:26 +08:00
bool isPnP ( ) const override {
return estimator = = EstimationMethod : : P3P | | estimator = = EstimationMethod : : P6P ;
}
} ;
Ptr < Model > Model : : create ( double threshold_ , EstimationMethod estimator_ , SamplingMethod sampler_ ,
double confidence_ , int max_iterations_ , ScoreMethod score_ ) {
return makePtr < ModelImpl > ( threshold_ , estimator_ , sampler_ , confidence_ ,
max_iterations_ , score_ ) ;
}
2023-06-16 15:59:13 +08:00
bool run ( const Ptr < const Model > & params , InputArray points1 , InputArray points2 ,
2020-08-15 06:42:26 +08:00
Ptr < RansacOutput > & ransac_output , InputArray K1_ , InputArray K2_ ,
InputArray dist_coeff1 , InputArray dist_coeff2 ) {
2023-06-16 15:59:13 +08:00
Ransac ransac ( params , points1 , points2 , K1_ , K2_ , dist_coeff1 , dist_coeff2 ) ;
2020-08-15 06:42:26 +08:00
if ( ransac . run ( ransac_output ) ) {
if ( params - > isPnP ( ) ) {
// convert R to rodrigues and back and recalculate inliers which due to numerical
// issues can differ
2023-06-16 15:59:13 +08:00
Mat out , newP ;
Matx33d R , newR , K1 ;
Vec3d t , rvec ;
2021-12-14 01:12:49 +08:00
if ( K1_ . empty ( ) ) {
2020-08-15 06:42:26 +08:00
usac : : Utils : : decomposeProjection ( ransac_output - > getModel ( ) , K1 , R , t ) ;
Rodrigues ( R , rvec ) ;
hconcat ( rvec , t , out ) ;
hconcat ( out , K1 , out ) ;
} else {
2023-06-16 15:59:13 +08:00
K1 = ransac . K1 ;
const Mat Rt = Mat ( Matx33d ( K1 ) . inv ( ) * Matx34d ( ransac_output - > getModel ( ) ) ) ;
2020-08-15 06:42:26 +08:00
t = Rt . col ( 3 ) ;
Rodrigues ( Rt . colRange ( 0 , 3 ) , rvec ) ;
hconcat ( rvec , t , out ) ;
}
2023-06-16 15:59:13 +08:00
// Matx33d _K1(K1);
2020-08-15 06:42:26 +08:00
Rodrigues ( rvec , newR ) ;
2023-06-16 15:59:13 +08:00
hconcat ( K1 * Matx33d ( newR ) , K1 * Vec3d ( t ) , newP ) ;
std : : vector < bool > inliers_mask ( ransac . points_size ) ;
ransac . _quality - > getInliers ( newP , inliers_mask ) ;
ransac_output = RansacOutput : : create ( out , inliers_mask , ransac_output - > getNumberOfInliers ( ) ,
ransac_output - > getNumberOfIters ( ) , ransac_output - > getConfidence ( ) , ransac_output - > getResiduals ( ) ) ;
2020-08-15 06:42:26 +08:00
}
return true ;
}
return false ;
}
2020-11-11 06:54:01 +08:00
} }