| 1 | // This file is part of OpenCV project. |
| 2 | // It is subject to the license terms in the LICENSE file found in the top-level directory |
| 3 | // of this distribution and at http://opencv.org/license.html. |
| 4 | |
| 5 | #include "../precomp.hpp" |
| 6 | #include "../usac.hpp" |
| 7 | #include <atomic> |
| 8 | |
| 9 | namespace cv { |
| 10 | UsacParams::UsacParams() { |
| 11 | confidence=0.99; |
| 12 | isParallel=false; |
| 13 | loIterations=5; |
| 14 | loMethod=LOCAL_OPTIM_INNER_LO; |
| 15 | loSampleSize=14; |
| 16 | maxIterations=5000; |
| 17 | neighborsSearch=NEIGH_GRID; |
| 18 | randomGeneratorState=0; |
| 19 | sampler=SAMPLING_UNIFORM; |
| 20 | score=SCORE_METHOD_MSAC; |
| 21 | threshold=1.5; |
| 22 | final_polisher=COV_POLISHER; |
| 23 | final_polisher_iterations=3; |
| 24 | } |
| 25 | |
| 26 | namespace usac { |
| 27 | int mergePoints (InputArray pts1_, InputArray pts2_, Mat &pts, bool ispnp); |
| 28 | void setParameters (int flag, Ptr<Model> ¶ms, EstimationMethod estimator, double thr, |
| 29 | int max_iters, double conf, bool mask_needed); |
| 30 | |
| 31 | class RansacOutputImpl : public RansacOutput { |
| 32 | private: |
| 33 | std::vector<int> inliers; |
| 34 | cv::Mat model, K1, K2; |
| 35 | // vector of number_inliers size |
| 36 | // vector of points size, true if inlier, false - outlier |
| 37 | std::vector<bool> inliers_mask; |
| 38 | // vector of points size, value of i-th index corresponds to error of i-th point if i is inlier. |
| 39 | std::vector<float> residuals; |
| 40 | int number_inliers, number_iterations; |
| 41 | ModelConfidence conf; |
| 42 | public: |
| 43 | RansacOutputImpl (const cv::Mat &model_, const std::vector<bool> &inliers_mask_, int number_inliers_, |
| 44 | int number_iterations_, ModelConfidence conf_, const std::vector<float> &errors_) { |
| 45 | model_.copyTo(m: model); |
| 46 | inliers_mask = inliers_mask_; |
| 47 | number_inliers = number_inliers_; |
| 48 | number_iterations = number_iterations_; |
| 49 | residuals = errors_; |
| 50 | conf = conf_; |
| 51 | } |
| 52 | |
| 53 | // Return inliers' indices of size = number of inliers |
| 54 | const std::vector<int> &getInliers() override { |
| 55 | if (inliers.empty()) { |
| 56 | inliers.reserve(n: number_inliers); |
| 57 | int pt_cnt = 0; |
| 58 | for (bool is_inlier : inliers_mask) { |
| 59 | if (is_inlier) |
| 60 | inliers.emplace_back(args&: pt_cnt); |
| 61 | pt_cnt++; |
| 62 | } |
| 63 | } |
| 64 | return inliers; |
| 65 | } |
| 66 | const std::vector<bool> &getInliersMask() const override { |
| 67 | return inliers_mask; |
| 68 | } |
| 69 | int getNumberOfInliers() const override { |
| 70 | return number_inliers; |
| 71 | } |
| 72 | const Mat &getModel() const override { |
| 73 | return model; |
| 74 | } |
| 75 | int getNumberOfIters() const override { |
| 76 | return number_iterations; |
| 77 | } |
| 78 | ModelConfidence getConfidence() const override { |
| 79 | return conf; |
| 80 | } |
| 81 | const std::vector<float> &getResiduals() const override { |
| 82 | return residuals; |
| 83 | } |
| 84 | }; |
| 85 | |
| 86 | Ptr<RansacOutput> RansacOutput::create(const cv::Mat &model_, const std::vector<bool> &inliers_mask_, int number_inliers_, |
| 87 | int number_iterations_, ModelConfidence conf, const std::vector<float> &errors_) { |
| 88 | return makePtr<RansacOutputImpl>(a1: model_, a1: inliers_mask_, a1: number_inliers_, |
| 89 | a1: number_iterations_, a1: conf, a1: errors_); |
| 90 | } |
| 91 | |
| 92 | double getLambda (std::vector<int> &supports, double cdf_thr, int points_size, |
| 93 | int sample_size, bool is_independent, int &min_non_random_inliers) { |
| 94 | std::sort(first: supports.begin(), last: supports.end()); |
| 95 | double lambda = supports.size() % 2 ? (supports[supports.size()/2] + supports[supports.size()/2+1])*0.5 : supports[supports.size()/2]; |
| 96 | const double cdf = lambda + cdf_thr*sqrt(x: lambda * (1 - lambda / (is_independent ? points_size - sample_size : points_size))); |
| 97 | int lower_than_cdf = 0; lambda = 0; |
| 98 | for (const auto &inl : supports) |
| 99 | if (inl < cdf) { |
| 100 | lambda += inl; lower_than_cdf++; |
| 101 | } else break; // list is sorted |
| 102 | lambda /= lower_than_cdf; |
| 103 | if (lambda < 1 || lower_than_cdf == 0) lambda = 1; |
| 104 | // use 0.9999 quantile https://keisan.casio.com/exec/system/14060745333941 |
| 105 | if (! is_independent) // do not calculate it for all inliers |
| 106 | min_non_random_inliers = (int)(lambda + 2.32*sqrt(x: lambda * (1 - lambda / points_size))) + 1; |
| 107 | return lambda; |
| 108 | } |
| 109 | |
| 110 | class Ransac { |
| 111 | public: |
| 112 | const Ptr<const Model> params; |
| 113 | Ptr<Estimator> _estimator; |
| 114 | Ptr<Error> _error; |
| 115 | Ptr<Quality> _quality; |
| 116 | Ptr<Sampler> _sampler; |
| 117 | Ptr<Termination> _termination; |
| 118 | Ptr<ModelVerifier> _model_verifier; |
| 119 | Ptr<Degeneracy> _degeneracy; |
| 120 | Ptr<LocalOptimization> _local_optimization; |
| 121 | Ptr<FinalModelPolisher> polisher; |
| 122 | Ptr<GammaValues> _gamma_generator; |
| 123 | Ptr<MinimalSolver> _min_solver; |
| 124 | Ptr<NonMinimalSolver> _lo_solver, _fo_solver; |
| 125 | Ptr<RandomGenerator> _lo_sampler; |
| 126 | Ptr<WeightFunction> _weight_fnc; |
| 127 | |
| 128 | int points_size, _state, filtered_points_size; |
| 129 | double threshold, max_thr; |
| 130 | bool parallel; |
| 131 | |
| 132 | Matx33d T1, T2; |
| 133 | Mat points, K1, K2, calib_points, image_points, norm_points, filtered_points; |
| 134 | Ptr<NeighborhoodGraph> graph; |
| 135 | std::vector<Ptr<NeighborhoodGraph>> layers; |
| 136 | |
| 137 | Ransac (const Ptr<const Model> ¶ms_, cv::InputArray points1, cv::InputArray points2, |
| 138 | cv::InputArray K1_, cv::InputArray K2_, cv::InputArray dist_coeff1, cv::InputArray dist_coeff2) : params(params_) { |
| 139 | _state = params->getRandomGeneratorState(); |
| 140 | threshold = params->getThreshold(); |
| 141 | max_thr = std::max(a: threshold, b: params->getMaximumThreshold()); |
| 142 | parallel = params->isParallel(); |
| 143 | Mat undist_points1, undist_points2; |
| 144 | if (params->isPnP()) { |
| 145 | if (! K1_.empty()) { |
| 146 | K1 = K1_.getMat().clone(); K1.convertTo(m: K1, CV_64F); |
| 147 | if (! dist_coeff1.empty()) { |
| 148 | // undistortPoints also calibrate points using K |
| 149 | undistortPoints(src: points1.isContinuous() ? points1 : points1.getMat().clone(), dst: undist_points1, cameraMatrix: K1_, distCoeffs: dist_coeff1); |
| 150 | points_size = mergePoints(pts1_: undist_points1, pts2_: points2, pts&: points, ispnp: true); |
| 151 | Utils::normalizeAndDecalibPointsPnP (K: K1, pts&: points, calib_norm_pts&: calib_points); |
| 152 | } else { |
| 153 | points_size = mergePoints(pts1_: points1, pts2_: points2, pts&: points, ispnp: true); |
| 154 | Utils::calibrateAndNormalizePointsPnP(K: K1, pts: points, calib_norm_pts&: calib_points); |
| 155 | } |
| 156 | } else points_size = mergePoints(pts1_: points1, pts2_: points2, pts&: points, ispnp: true); |
| 157 | } else { |
| 158 | if (params->isEssential()) { |
| 159 | CV_CheckEQ((int)(!K1_.empty() && !K2_.empty()), 1, "Intrinsic matrix must not be empty!" ); |
| 160 | K1 = K1_.getMat(); K1.convertTo(m: K1, CV_64F); |
| 161 | K2 = K2_.getMat(); K2.convertTo(m: K2, CV_64F); |
| 162 | if (! dist_coeff1.empty() || ! dist_coeff2.empty()) { |
| 163 | // undistortPoints also calibrate points using K |
| 164 | if (! dist_coeff1.empty()) undistortPoints(src: points1.isContinuous() ? points1 : points1.getMat().clone(), dst: undist_points1, cameraMatrix: K1_, distCoeffs: dist_coeff1); |
| 165 | else undist_points1 = points1.getMat(); |
| 166 | if (! dist_coeff2.empty()) undistortPoints(src: points2.isContinuous() ? points2 : points2.getMat().clone(), dst: undist_points2, cameraMatrix: K2_, distCoeffs: dist_coeff2); |
| 167 | else undist_points2 = points2.getMat(); |
| 168 | points_size = mergePoints(pts1_: undist_points1, pts2_: undist_points2, pts&: calib_points, ispnp: false); |
| 169 | } else { |
| 170 | points_size = mergePoints(pts1_: points1, pts2_: points2, pts&: points, ispnp: false); |
| 171 | Utils::calibratePoints(K1, K2, points, norm_points&: calib_points); |
| 172 | } |
| 173 | threshold = Utils::getCalibratedThreshold(threshold, K1, K2); |
| 174 | max_thr = Utils::getCalibratedThreshold(threshold: max_thr, K1, K2); |
| 175 | } else { |
| 176 | points_size = mergePoints(pts1_: points1, pts2_: points2, pts&: points, ispnp: false); |
| 177 | if (params->isFundamental() && ! K1_.empty() && ! K2_.empty()) { |
| 178 | K1 = K1_.getMat(); K1.convertTo(m: K1, CV_64F); |
| 179 | K2 = K2_.getMat(); K2.convertTo(m: K2, CV_64F); |
| 180 | Utils::calibratePoints(K1, K2, points, norm_points&: calib_points); |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | if (params->getSampler() == SamplingMethod::SAMPLING_NAPSAC || params->getLO() == LocalOptimMethod::LOCAL_OPTIM_GC) { |
| 186 | if (params->getNeighborsSearch() == NeighborSearchMethod::NEIGH_GRID) { |
| 187 | graph = GridNeighborhoodGraph::create(points, points_size, |
| 188 | cell_size_x_img1_: params->getCellSize(), cell_size_y_img1_: params->getCellSize(), cell_size_x_img2_: params->getCellSize(), cell_size_y_img2_: params->getCellSize(), max_neighbors: 10); |
| 189 | } else if (params->getNeighborsSearch() == NeighborSearchMethod::NEIGH_FLANN_KNN) { |
| 190 | graph = FlannNeighborhoodGraph::create(points, points_size,k_nearest_neighbors_: params->getKNN(), get_distances: false, flann_search_params: 5, num_kd_trees: 1); |
| 191 | } else if (params->getNeighborsSearch() == NeighborSearchMethod::NEIGH_FLANN_RADIUS) { |
| 192 | graph = RadiusSearchNeighborhoodGraph::create(points, points_size, radius_: params->getGraphRadius(), flann_search_params: 5, num_kd_trees: 1); |
| 193 | } else CV_Error(cv::Error::StsNotImplemented, "Graph type is not implemented!" ); |
| 194 | } |
| 195 | |
| 196 | if (params->getSampler() == SamplingMethod::SAMPLING_PROGRESSIVE_NAPSAC) { |
| 197 | CV_CheckEQ((int)params->isPnP(), 0, "ProgressiveNAPSAC for PnP is not implemented!" ); |
| 198 | const auto &cell_number_per_layer = params->getGridCellNumber(); |
| 199 | layers.reserve(n: cell_number_per_layer.size()); |
| 200 | const auto * const pts = (float *) points.data; |
| 201 | float img1_width = 0, img1_height = 0, img2_width = 0, img2_height = 0; |
| 202 | for (int i = 0; i < 4 * points_size; i += 4) { |
| 203 | if (pts[i ] > img1_width ) img1_width = pts[i ]; |
| 204 | if (pts[i + 1] > img1_height) img1_height = pts[i + 1]; |
| 205 | if (pts[i + 2] > img2_width ) img2_width = pts[i + 2]; |
| 206 | if (pts[i + 3] > img2_height) img2_height = pts[i + 3]; |
| 207 | } |
| 208 | // Create grid graphs (overlapping layes of given cell numbers) |
| 209 | for (int layer_idx = 0; layer_idx < (int)cell_number_per_layer.size(); layer_idx++) { |
| 210 | const int cell_number = cell_number_per_layer[layer_idx]; |
| 211 | if (layer_idx > 0) |
| 212 | if (cell_number_per_layer[layer_idx-1] <= cell_number) |
| 213 | CV_Error(cv::Error::StsError, "Progressive NAPSAC sampler: " |
| 214 | "Cell number in layers must be in decreasing order!" ); |
| 215 | layers.emplace_back(args: GridNeighborhoodGraph::create(points, points_size, |
| 216 | cell_size_x_img1_: (int)(img1_width / (float)cell_number), cell_size_y_img1_: (int)(img1_height / (float)cell_number), |
| 217 | cell_size_x_img2_: (int)(img2_width / (float)cell_number), cell_size_y_img2_: (int)(img2_height / (float)cell_number), max_neighbors: 10)); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | // update points by calibrated for Essential matrix after graph is calculated |
| 222 | if (params->isEssential()) { |
| 223 | image_points = points; |
| 224 | points = calib_points; |
| 225 | // if maximum calibrated threshold significanlty differs threshold then set upper bound |
| 226 | if (max_thr > 10*threshold) |
| 227 | max_thr = 10*threshold; |
| 228 | } |
| 229 | |
| 230 | // Since error function output squared error distance, so make |
| 231 | // threshold squared as well |
| 232 | threshold *= threshold; |
| 233 | |
| 234 | if ((params->isHomography() || (params->isFundamental() && (K1.empty() || K2.empty() || !params->isLarssonOptimization())) || |
| 235 | params->getEstimator() == EstimationMethod::AFFINE) && (params->getLO() != LOCAL_OPTIM_NULL || params->getFinalPolisher() == COV_POLISHER)) { |
| 236 | const auto normTr = NormTransform::create(points); |
| 237 | std::vector<int> sample (points_size); |
| 238 | for (int i = 0; i < points_size; i++) sample[i] = i; |
| 239 | normTr->getNormTransformation(norm_points, sample, sample_number: points_size, T1, T2); |
| 240 | } |
| 241 | |
| 242 | if (params->getScore() == SCORE_METHOD_MAGSAC || params->getLO() == LOCAL_OPTIM_SIGMA || params->getFinalPolisher() == MAGSAC) |
| 243 | _gamma_generator = GammaValues::create(DoF: params->getDegreesOfFreedom()); // is thread safe |
| 244 | initialize (state: _state, min_solver&: _min_solver, non_min_solver&: _lo_solver, error&: _error, estimator&: _estimator, degeneracy&: _degeneracy, quality&: _quality, |
| 245 | verifier&: _model_verifier, lo&: _local_optimization, termination&: _termination, sampler&: _sampler, lo_sampler&: _lo_sampler, weight_fnc&: _weight_fnc, parallel_call: false/*parallel*/); |
| 246 | if (params->getFinalPolisher() != NONE_POLISHER) { |
| 247 | polisher = NonMinimalPolisher::create(quality_: _quality, solver_: _fo_solver, |
| 248 | weight_fnc_: params->getFinalPolisher() == MAGSAC ? _weight_fnc : nullptr, max_iters_: params->getFinalLSQIterations(), iou_thr_: 0.99); |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | void initialize (int state, Ptr<MinimalSolver> &min_solver, Ptr<NonMinimalSolver> &non_min_solver, |
| 253 | Ptr<Error> &error, Ptr<Estimator> &estimator, Ptr<Degeneracy> °eneracy, Ptr<Quality> &quality, |
| 254 | Ptr<ModelVerifier> &verifier, Ptr<LocalOptimization> &lo, Ptr<Termination> &termination, |
| 255 | Ptr<Sampler> &sampler, Ptr<RandomGenerator> &lo_sampler, Ptr<WeightFunction> &weight_fnc, bool parallel_call) { |
| 256 | |
| 257 | const int min_sample_size = params->getSampleSize(), prosac_termination_length = std::min(a: (int)(.5*points_size), b: 100); |
| 258 | // inner inlier threshold will be used in LO to obtain inliers |
| 259 | // additionally in DEGENSAC for F |
| 260 | double inner_inlier_thr_sqr = threshold; |
| 261 | if (params->isHomography() && inner_inlier_thr_sqr < 5.25) inner_inlier_thr_sqr = 5.25; // at least 2.5 px |
| 262 | else if (params->isFundamental() && inner_inlier_thr_sqr < 4) inner_inlier_thr_sqr = 4; // at least 2 px |
| 263 | |
| 264 | if (params->getFinalPolisher() == MAGSAC || params->getLO() == LOCAL_OPTIM_SIGMA) |
| 265 | weight_fnc = MagsacWeightFunction::create(gamma_generator_: _gamma_generator, DoF_: params->getDegreesOfFreedom(), upper_incomplete_of_sigma_quantile: params->getUpperIncompleteOfSigmaQuantile(), C_: params->getC(), max_sigma_: params->getMaximumThreshold()); |
| 266 | else weight_fnc = nullptr; |
| 267 | |
| 268 | switch (params->getError()) { |
| 269 | case ErrorMetric::SYMM_REPR_ERR: |
| 270 | error = ReprojectionErrorSymmetric::create(points); break; |
| 271 | case ErrorMetric::FORW_REPR_ERR: |
| 272 | if (params->getEstimator() == EstimationMethod::AFFINE) |
| 273 | error = ReprojectionErrorAffine::create(points); |
| 274 | else error = ReprojectionErrorForward::create(points); |
| 275 | break; |
| 276 | case ErrorMetric::SAMPSON_ERR: |
| 277 | error = SampsonError::create(points); break; |
| 278 | case ErrorMetric::SGD_ERR: |
| 279 | error = SymmetricGeometricDistance::create(points); break; |
| 280 | case ErrorMetric::RERPOJ: |
| 281 | error = ReprojectionErrorPmatrix::create(points); break; |
| 282 | default: CV_Error(cv::Error::StsNotImplemented , "Error metric is not implemented!" ); |
| 283 | } |
| 284 | |
| 285 | const double k_mlesac = params->getKmlesac (); |
| 286 | switch (params->getScore()) { |
| 287 | case ScoreMethod::SCORE_METHOD_RANSAC : |
| 288 | quality = RansacQuality::create(points_size_: points_size, threshold_: threshold, error_: error); break; |
| 289 | case ScoreMethod::SCORE_METHOD_MSAC : |
| 290 | quality = MsacQuality::create(points_size_: points_size, threshold_: threshold, error_: error, k_msac: k_mlesac); break; |
| 291 | case ScoreMethod::SCORE_METHOD_MAGSAC : |
| 292 | quality = MagsacQuality::create(maximum_thr: max_thr, points_size_: points_size, error_: error, gamma_generator: _gamma_generator, |
| 293 | tentative_inlier_threshold_: threshold, DoF: params->getDegreesOfFreedom(), sigma_quantile: params->getSigmaQuantile(), |
| 294 | upper_incomplete_of_sigma_quantile: params->getUpperIncompleteOfSigmaQuantile()); break; |
| 295 | case ScoreMethod::SCORE_METHOD_LMEDS : |
| 296 | quality = LMedsQuality::create(points_size_: points_size, threshold_: threshold, error_: error); break; |
| 297 | default: CV_Error(cv::Error::StsNotImplemented, "Score is not implemented!" ); |
| 298 | } |
| 299 | |
| 300 | const auto is_ge_solver = params->getRansacSolver() == GEM_SOLVER; |
| 301 | if (params->isHomography()) { |
| 302 | degeneracy = HomographyDegeneracy::create(points_: points); |
| 303 | min_solver = HomographyMinimalSolver4pts::create(points, use_ge: is_ge_solver); |
| 304 | non_min_solver = HomographyNonMinimalSolver::create(points_: norm_points, T1, T2, use_ge: true); |
| 305 | estimator = HomographyEstimator::create(min_solver_: min_solver, non_min_solver_: non_min_solver, degeneracy_: degeneracy); |
| 306 | if (!parallel_call && params->getFinalPolisher() != NONE_POLISHER) { |
| 307 | if (params->getFinalPolisher() == COV_POLISHER) |
| 308 | _fo_solver = CovarianceHomographySolver::create(points: norm_points, T1, T2); |
| 309 | else _fo_solver = HomographyNonMinimalSolver::create(points_: points); |
| 310 | } |
| 311 | } else if (params->isFundamental()) { |
| 312 | if (K1.empty() || K2.empty()) { |
| 313 | degeneracy = FundamentalDegeneracy::create(state: state++, quality_: quality, points_: points, sample_size_: min_sample_size, |
| 314 | max_iters_plane_and_parallax: params->getPlaneAndParallaxIters(), homography_threshold: std::max(a: threshold, b: 8.) /*sqr homogr thr*/, f_inlier_thr_sqr: inner_inlier_thr_sqr, true_K1: K1, true_K2: K2); |
| 315 | } else degeneracy = FundamentalDegeneracyViaE::create(quality, pts: points, calib_pts: calib_points, K1, K2, is_f_objective: true/*is F*/); |
| 316 | if (min_sample_size == 7) { |
| 317 | min_solver = FundamentalMinimalSolver7pts::create(points, use_ge: is_ge_solver); |
| 318 | } else min_solver = FundamentalMinimalSolver8pts::create(points_: points); |
| 319 | if (params->isLarssonOptimization() && !K1.empty() && !K2.empty()) { |
| 320 | non_min_solver = LarssonOptimizer::create(calib_points_: calib_points, K1_: K1, K2_: K2, max_iters_: params->getLevMarqItersLO(), is_fundamental_: true/*F*/); |
| 321 | } else { |
| 322 | if (weight_fnc) |
| 323 | non_min_solver = EpipolarNonMinimalSolver::create(points_: points, is_fundamental: true); |
| 324 | else |
| 325 | non_min_solver = EpipolarNonMinimalSolver::create(points_: norm_points, T1, T2, use_ge: true); |
| 326 | } |
| 327 | estimator = FundamentalEstimator::create(min_solver_: min_solver, non_min_solver_: non_min_solver, degeneracy_: degeneracy); |
| 328 | if (!parallel_call && params->getFinalPolisher() != NONE_POLISHER) { |
| 329 | if (params->isLarssonOptimization() && !K1.empty() && !K2.empty()) |
| 330 | _fo_solver = LarssonOptimizer::create(calib_points_: calib_points, K1_: K1, K2_: K2, max_iters_: params->getLevMarqIters(), is_fundamental_: true/*F*/); |
| 331 | else if (params->getFinalPolisher() == COV_POLISHER) |
| 332 | _fo_solver = CovarianceEpipolarSolver::create(points: norm_points, T1, T2); |
| 333 | else _fo_solver = EpipolarNonMinimalSolver::create(points_: points, is_fundamental: true); |
| 334 | } |
| 335 | } else if (params->isEssential()) { |
| 336 | if (params->getEstimator() == EstimationMethod::ESSENTIAL) { |
| 337 | min_solver = EssentialMinimalSolver5pts::create(points, use_svd: !is_ge_solver, is_nister: true/*Nister*/); |
| 338 | degeneracy = EssentialDegeneracy::create(points, sample_size: min_sample_size); |
| 339 | } |
| 340 | non_min_solver = LarssonOptimizer::create(calib_points_: calib_points, K1_: K1, K2_: K2, max_iters_: params->getLevMarqItersLO(), is_fundamental_: false/*E*/); |
| 341 | estimator = EssentialEstimator::create(min_solver_: min_solver, non_min_solver_: non_min_solver, degeneracy_: degeneracy); |
| 342 | if (!parallel_call && params->getFinalPolisher() != NONE_POLISHER) |
| 343 | _fo_solver = LarssonOptimizer::create(calib_points_: calib_points, K1_: K1, K2_: K2, max_iters_: params->getLevMarqIters(), is_fundamental_: false/*E*/); |
| 344 | } else if (params->isPnP()) { |
| 345 | degeneracy = makePtr<Degeneracy>(); |
| 346 | if (min_sample_size == 3) { |
| 347 | min_solver = P3PSolver::create(points_: points, calib_norm_pts: calib_points, K: K1); |
| 348 | non_min_solver = DLSPnP::create(points_: points, calib_norm_pts: calib_points, K: K1); |
| 349 | } else { |
| 350 | if (is_ge_solver) |
| 351 | min_solver = PnPMinimalSolver6Pts::create(points_: points); |
| 352 | else min_solver = PnPSVDSolver::create(points); |
| 353 | non_min_solver = PnPNonMinimalSolver::create(points); |
| 354 | } |
| 355 | estimator = PnPEstimator::create(min_solver_: min_solver, non_min_solver_: non_min_solver); |
| 356 | if (!parallel_call && params->getFinalPolisher() != NONE_POLISHER) _fo_solver = non_min_solver; |
| 357 | } else if (params->getEstimator() == EstimationMethod::AFFINE) { |
| 358 | degeneracy = makePtr<Degeneracy>(); |
| 359 | min_solver = AffineMinimalSolver::create(points_: points); |
| 360 | non_min_solver = AffineNonMinimalSolver::create(points, T1: cv::noArray(), T2: cv::noArray()); |
| 361 | estimator = AffineEstimator::create(min_solver_: min_solver, non_min_solver_: non_min_solver); |
| 362 | if (!parallel_call && params->getFinalPolisher() != NONE_POLISHER) { |
| 363 | if (params->getFinalPolisher() == COV_POLISHER) |
| 364 | _fo_solver = CovarianceAffineSolver::create(points); |
| 365 | else _fo_solver = non_min_solver; |
| 366 | } |
| 367 | } else CV_Error(cv::Error::StsNotImplemented, "Estimator not implemented!" ); |
| 368 | |
| 369 | switch (params->getSampler()) { |
| 370 | case SamplingMethod::SAMPLING_UNIFORM: |
| 371 | sampler = UniformSampler::create(state: state++, sample_size_: min_sample_size, points_size_: points_size); |
| 372 | break; |
| 373 | case SamplingMethod::SAMPLING_PROSAC: |
| 374 | if (!parallel_call) // for parallel only one PROSAC sampler |
| 375 | sampler = ProsacSampler::create(state: state++, points_size_: points_size, sample_size_: min_sample_size, growth_max_samples: params->getProsacMaxSamples()); |
| 376 | break; |
| 377 | case SamplingMethod::SAMPLING_PROGRESSIVE_NAPSAC: |
| 378 | sampler = ProgressiveNapsac::create(state: state++, points_size_: points_size, sample_size_: min_sample_size, layers, sampler_length: 20); break; |
| 379 | case SamplingMethod::SAMPLING_NAPSAC: |
| 380 | sampler = NapsacSampler::create(state: state++, points_size_: points_size, sample_size_: min_sample_size, neighborhood_graph_: graph); break; |
| 381 | default: CV_Error(cv::Error::StsNotImplemented, "Sampler is not implemented!" ); |
| 382 | } |
| 383 | |
| 384 | const bool is_sprt = params->getVerifier() == VerificationMethod::SPRT_VERIFIER || params->getVerifier() == VerificationMethod::ASPRT; |
| 385 | if (is_sprt) |
| 386 | verifier = AdaptiveSPRT::create(state: state++, quality, points_size_: points_size, inlier_threshold_: params->getScore() == ScoreMethod ::SCORE_METHOD_MAGSAC ? max_thr : threshold, |
| 387 | prob_pt_of_good_model: params->getSPRTepsilon(), prob_pt_of_bad_model: params->getSPRTdelta(), time_sample: params->getTimeForModelEstimation(), |
| 388 | avg_num_models: params->getSPRTavgNumModels(), score_type_: params->getScore(), k_mlesac, is_adaptive: params->getVerifier() == VerificationMethod::ASPRT); |
| 389 | else if (params->getVerifier() == VerificationMethod::NULL_VERIFIER) |
| 390 | verifier = ModelVerifier::create(qualtiy: quality); |
| 391 | else CV_Error(cv::Error::StsNotImplemented, "Verifier is not imeplemented!" ); |
| 392 | |
| 393 | if (params->getSampler() == SamplingMethod::SAMPLING_PROSAC) { |
| 394 | if (parallel_call) { |
| 395 | termination = ProsacTerminationCriteria::create(sampler_: nullptr, error_: error, |
| 396 | points_size_: points_size, sample_size: min_sample_size, confidence: params->getConfidence(), max_iters: params->getMaxIters(), min_termination_length: prosac_termination_length, beta: 0.05, non_randomness_phi: 0.05, inlier_thresh: threshold, |
| 397 | non_rand_inliers: _termination.dynamicCast<ProsacTerminationCriteria>()->getNonRandomInliers()); |
| 398 | } else { |
| 399 | termination = ProsacTerminationCriteria::create(sampler_: sampler.dynamicCast<ProsacSampler>(), error_: error, |
| 400 | points_size_: points_size, sample_size: min_sample_size, confidence: params->getConfidence(), max_iters: params->getMaxIters(), min_termination_length: prosac_termination_length, beta: 0.05, non_randomness_phi: 0.05, inlier_thresh: threshold, |
| 401 | non_rand_inliers: std::vector<int>()); |
| 402 | } |
| 403 | } else if (params->getSampler() == SamplingMethod::SAMPLING_PROGRESSIVE_NAPSAC) { |
| 404 | if (is_sprt) |
| 405 | termination = SPRTPNapsacTermination::create(sprt: verifier.dynamicCast<AdaptiveSPRT>(), |
| 406 | confidence: params->getConfidence(), points_size_: points_size, sample_size_: min_sample_size, |
| 407 | max_iterations_: params->getMaxIters(), relax_coef_: params->getRelaxCoef()); |
| 408 | else termination = StandardTerminationCriteria::create (confidence: params->getConfidence(), |
| 409 | points_size_: points_size, sample_size_: min_sample_size, max_iterations_: params->getMaxIters()); |
| 410 | } else if (is_sprt && params->getLO() == LocalOptimMethod::LOCAL_OPTIM_NULL) { |
| 411 | termination = SPRTTermination::create(sprt: verifier.dynamicCast<AdaptiveSPRT>(), |
| 412 | confidence: params->getConfidence(), points_size_: points_size, sample_size_: min_sample_size, max_iterations_: params->getMaxIters()); |
| 413 | } else { |
| 414 | termination = StandardTerminationCriteria::create |
| 415 | (confidence: params->getConfidence(), points_size_: points_size, sample_size_: min_sample_size, max_iterations_: params->getMaxIters()); |
| 416 | } |
| 417 | |
| 418 | // if normal ransac or parallel call, avoid redundant init |
| 419 | if ((! params->isParallel() || parallel_call) && params->getLO() != LocalOptimMethod::LOCAL_OPTIM_NULL) { |
| 420 | lo_sampler = UniformRandomGenerator::create(state, max_range: points_size, subset_size_: params->getLOSampleSize()); |
| 421 | const auto lo_termination = StandardTerminationCriteria::create(confidence: params->getConfidence(), points_size_: points_size, sample_size_: min_sample_size, max_iterations_: params->getMaxIters()); |
| 422 | switch (params->getLO()) { |
| 423 | case LocalOptimMethod::LOCAL_OPTIM_INNER_LO: case LocalOptimMethod::LOCAL_OPTIM_SIGMA: |
| 424 | lo = SimpleLocalOptimization::create(quality_: quality, estimator_: non_min_solver, termination_: lo_termination, random_gen: lo_sampler, |
| 425 | weight_fnc_: weight_fnc, max_lo_iters_: params->getLOInnerMaxIters(), inlier_thr_sqr: inner_inlier_thr_sqr, updated_lo: true); break; |
| 426 | case LocalOptimMethod::LOCAL_OPTIM_INNER_AND_ITER_LO: |
| 427 | lo = InnerIterativeLocalOptimization::create(estimator_: estimator, quality_: quality, lo_sampler_: lo_sampler, |
| 428 | pts_size: points_size, threshold_: threshold, is_iterative_: true, lo_iter_sample_size_: params->getLOIterativeSampleSize(), |
| 429 | lo_inner_iterations: params->getLOInnerMaxIters(), lo_iter_max_iterations: params->getLOIterativeMaxIters(), |
| 430 | threshold_multiplier: params->getLOThresholdMultiplier()); break; |
| 431 | case LocalOptimMethod::LOCAL_OPTIM_GC: |
| 432 | lo = GraphCut::create(estimator_: estimator, quality_: quality, neighborhood_graph_: graph, lo_sampler_: lo_sampler, threshold_: threshold, |
| 433 | spatial_coherence_term: params->getGraphCutSpatialCoherenceTerm(), gc_iters: params->getLOInnerMaxIters(), termination_: lo_termination); break; |
| 434 | default: CV_Error(cv::Error::StsNotImplemented , "Local Optimization is not implemented!" ); |
| 435 | } |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | int getIndependentInliers (const Mat &model_, const std::vector<int> &sample, |
| 440 | std::vector<int> &inliers, const int num_inliers_) { |
| 441 | bool is_F = params->isFundamental(); |
| 442 | Mat model = model_; |
| 443 | int sample_size = 0; |
| 444 | if (is_F) sample_size = 7; |
| 445 | else if (params->isHomography()) sample_size = 4; |
| 446 | else if (params->isEssential()) { |
| 447 | is_F = true; |
| 448 | // convert E to F |
| 449 | model = Mat(Matx33d(K2).inv().t() * Matx33d(model) * Matx33d(K1).inv()); |
| 450 | sample_size = 5; |
| 451 | } else if (params->isPnP() || params->getEstimator() == EstimationMethod::AFFINE) sample_size = 3; |
| 452 | else |
| 453 | CV_Error(cv::Error::StsNotImplemented, "Method for independent inliers is not implemented for this problem" ); |
| 454 | if (num_inliers_ <= sample_size) return 0; // minimal sample size generates model |
| 455 | model.convertTo(m: model, CV_32F); |
| 456 | int num_inliers = num_inliers_, num_pts_near_ep = 0, |
| 457 | num_pts_validatin_or_constr = 0, pt1 = 0; |
| 458 | const auto * const pts = params->isEssential() ? (float *) image_points.data : (float *) points.data; |
| 459 | // scale for thresholds should be used |
| 460 | const float ep_thr_sqr = 0.000001f, line_thr = 0.01f, neigh_thr = 4.0f; |
| 461 | float sign1=0,a1=0, b1=0, c1=0, a2=0, b2=0, c2=0, ep1_x, ep1_y, ep2_x, ep2_y; |
| 462 | const auto * const m = (float *) model.data; |
| 463 | Vec3f ep1; |
| 464 | bool do_or_test = false, ep1_inf = false, ep2_inf = false; |
| 465 | if (is_F) { // compute epipole and sign of the first point for orientation test |
| 466 | model *= (1/norm(src1: model)); |
| 467 | ep1 = Utils::getRightEpipole(F: model); |
| 468 | const Vec3f ep2 = Utils::getLeftEpipole(F: model); |
| 469 | if (fabsf(x: ep1[2]) < DBL_EPSILON) { |
| 470 | ep1_inf = true; |
| 471 | } else { |
| 472 | ep1_x = ep1[0] / ep1[2]; |
| 473 | ep1_y = ep1[1] / ep1[2]; |
| 474 | } |
| 475 | if (fabsf(x: ep2[2]) < DBL_EPSILON) { |
| 476 | ep2_inf = true; |
| 477 | } else { |
| 478 | ep2_x = ep2[0] / ep2[2]; |
| 479 | ep2_y = ep2[1] / ep2[2]; |
| 480 | } |
| 481 | } |
| 482 | const auto * const e1 = ep1.val; // of size 3x1 |
| 483 | |
| 484 | // we move sample points to the end, so every inlier will be checked by sample point |
| 485 | int num_sample_in_inliers = 0; |
| 486 | if (!sample.empty()) { |
| 487 | num_sample_in_inliers = 0; |
| 488 | int temp_idx = num_inliers; |
| 489 | for (int i = 0; i < temp_idx; i++) { |
| 490 | const int inl = inliers[i]; |
| 491 | for (int s : sample) { |
| 492 | if (inl == s) { |
| 493 | std::swap(a&: inliers[i], b&: inliers[--temp_idx]); |
| 494 | i--; // we need to check inlier that we just swapped |
| 495 | num_sample_in_inliers++; |
| 496 | break; |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | if (is_F) { |
| 503 | int MIN_TEST = std::min(a: 15, b: num_inliers); |
| 504 | for (int i = 0; i < MIN_TEST; i++) { |
| 505 | pt1 = 4*inliers[i]; |
| 506 | sign1 = (m[0]*pts[pt1+2]+m[3]*pts[pt1+3]+m[6])*(e1[1]-e1[2]*pts[pt1+1]); |
| 507 | int validate = 0; |
| 508 | for (int j = 0; j < MIN_TEST; j++) { |
| 509 | if (i == j) continue; |
| 510 | const int inl_idx = 4*inliers[j]; |
| 511 | 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) |
| 512 | validate++; |
| 513 | } |
| 514 | if (validate < MIN_TEST/2) { |
| 515 | do_or_test = true; break; |
| 516 | } |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | // verification does not include sample points as they are surely random |
| 521 | const int max_verify = num_inliers - num_sample_in_inliers; |
| 522 | if (max_verify <= 0) |
| 523 | return 0; |
| 524 | int num_non_random_inliers = num_inliers - sample_size; |
| 525 | auto removeDependentPoints = [&] (bool do_orient_test, bool check_epipoles) { |
| 526 | for (int i = 0; i < max_verify; i++) { |
| 527 | // checks over inliers if they are dependent to other inliers |
| 528 | const int inl_idx = 4*inliers[i]; |
| 529 | const auto x1 = pts[inl_idx], y1 = pts[inl_idx+1], x2 = pts[inl_idx+2], y2 = pts[inl_idx+3]; |
| 530 | if (is_F) { |
| 531 | // epipolar line on image 2 = l2 |
| 532 | a2 = m[0] * x1 + m[1] * y1 + m[2]; |
| 533 | b2 = m[3] * x1 + m[4] * y1 + m[5]; |
| 534 | c2 = m[6] * x1 + m[7] * y1 + m[8]; |
| 535 | // epipolar line on image 1 = l1 |
| 536 | a1 = m[0] * x2 + m[3] * y2 + m[6]; |
| 537 | b1 = m[1] * x2 + m[4] * y2 + m[7]; |
| 538 | c1 = m[2] * x2 + m[5] * y2 + m[8]; |
| 539 | if ((!ep1_inf && fabsf(x: x1-ep1_x)+fabsf(x: y1-ep1_y) < neigh_thr) || |
| 540 | (!ep2_inf && fabsf(x: x2-ep2_x)+fabsf(x: y2-ep2_y) < neigh_thr)) { |
| 541 | num_non_random_inliers--; |
| 542 | num_pts_near_ep++; |
| 543 | continue; // is dependent, continue to the next point |
| 544 | } else if (check_epipoles) { |
| 545 | if (a2 * a2 + b2 * b2 + c2 * c2 < ep_thr_sqr || |
| 546 | a1 * a1 + b1 * b1 + c1 * c1 < ep_thr_sqr) { |
| 547 | num_non_random_inliers--; |
| 548 | num_pts_near_ep++; |
| 549 | continue; // is dependent, continue to the next point |
| 550 | } |
| 551 | } |
| 552 | else if (do_orient_test && pt1 != inl_idx && sign1*(m[0]*x2+m[3]*y2+m[6])*(e1[1]-e1[2]*y1)<0) { |
| 553 | num_non_random_inliers--; |
| 554 | num_pts_validatin_or_constr++; |
| 555 | continue; |
| 556 | } |
| 557 | const auto mag2 = 1 / sqrt(x: a2 * a2 + b2 * b2), mag1 = 1/sqrt(x: a1 * a1 + b1 * b1); |
| 558 | a2 *= mag2; b2 *= mag2; c2 *= mag2; |
| 559 | a1 *= mag1; b1 *= mag1; c1 *= mag1; |
| 560 | } |
| 561 | |
| 562 | for (int j = i+1; j < num_inliers; j++) {// verify through all including sample points |
| 563 | const int inl_idx_j = 4*inliers[j]; |
| 564 | 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]; |
| 565 | // use L1 norm instead of L2 for faster evaluation |
| 566 | if (fabsf(x: X1-x1) + fabsf(x: Y1 - y1) < neigh_thr || fabsf(x: X2-x2) + fabsf(x: Y2 - y2) < neigh_thr) { |
| 567 | num_non_random_inliers--; |
| 568 | // num_pts_bad_conditioning++; |
| 569 | break; // is dependent stop verification |
| 570 | } else if (is_F) { |
| 571 | if (fabsf(x: a2 * X2 + b2 * Y2 + c2) < line_thr && //|| // xj'^T F xi |
| 572 | fabsf(x: a1 * X1 + b1 * Y1 + c1) < line_thr) { // xj^T F^T xi' |
| 573 | num_non_random_inliers--; |
| 574 | break; // is dependent stop verification |
| 575 | } |
| 576 | } |
| 577 | } |
| 578 | } |
| 579 | }; |
| 580 | if (params->isPnP()) { |
| 581 | for (int i = 0; i < max_verify; i++) { |
| 582 | const int inl_idx = 5*inliers[i]; |
| 583 | 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]; |
| 584 | for (int j = i+1; j < num_inliers; j++) { |
| 585 | const int inl_idx_j = 5*inliers[j]; |
| 586 | if (fabsf(x: x-pts[inl_idx_j ]) + fabsf(x: y-pts[inl_idx_j+1]) < neigh_thr || |
| 587 | fabsf(x: X-pts[inl_idx_j+2]) + fabsf(x: Y-pts[inl_idx_j+3]) + fabsf(x: Z-pts[inl_idx_j+4]) < neigh_thr) { |
| 588 | num_non_random_inliers--; |
| 589 | break; |
| 590 | } |
| 591 | } |
| 592 | } |
| 593 | } else { |
| 594 | removeDependentPoints(do_or_test, !ep1_inf && !ep2_inf); |
| 595 | if (is_F) { |
| 596 | const bool is_pts_vald_constr_normal = (double)num_pts_validatin_or_constr / num_inliers < 0.6; |
| 597 | const bool is_pts_near_ep_normal = (double)num_pts_near_ep / num_inliers < 0.6; |
| 598 | if (!is_pts_near_ep_normal || !is_pts_vald_constr_normal) { |
| 599 | num_non_random_inliers = num_inliers-sample_size; |
| 600 | num_pts_near_ep = 0; num_pts_validatin_or_constr = 0; |
| 601 | removeDependentPoints(is_pts_vald_constr_normal, is_pts_near_ep_normal); |
| 602 | } |
| 603 | } |
| 604 | } |
| 605 | return num_non_random_inliers; |
| 606 | } |
| 607 | |
| 608 | bool run(Ptr<RansacOutput> &ransac_output) { |
| 609 | if (points_size < params->getSampleSize()) |
| 610 | return false; |
| 611 | const bool LO = params->getLO() != LocalOptimMethod::LOCAL_OPTIM_NULL, |
| 612 | IS_FUNDAMENTAL = params->isFundamental(), IS_NON_RAND_TEST = params->isNonRandomnessTest(); |
| 613 | const int MAX_MODELS_ADAPT = 21, MAX_ITERS_ADAPT = MAX_MODELS_ADAPT/*assume at least 1 model from 1 sample*/, |
| 614 | sample_size = params->getSampleSize(); |
| 615 | const double IOU_SIMILARITY_THR = 0.80; |
| 616 | std::vector<int> non_degen_sample, best_sample; |
| 617 | |
| 618 | double lambda_non_random_all_inliers = -1; |
| 619 | int final_iters, num_total_tested_models = 0; |
| 620 | |
| 621 | // non-random |
| 622 | const int MAX_TEST_MODELS_NONRAND = IS_NON_RAND_TEST ? MAX_MODELS_ADAPT : 0; |
| 623 | std::vector<Mat> models_for_random_test; models_for_random_test.reserve(n: MAX_TEST_MODELS_NONRAND); |
| 624 | std::vector<std::vector<int>> samples_for_random_test; samples_for_random_test.reserve(n: MAX_TEST_MODELS_NONRAND); |
| 625 | |
| 626 | bool last_model_from_LO = false; |
| 627 | Mat best_model, best_model_not_from_LO, K1_approx, K2_approx; |
| 628 | Score best_score, best_score_model_not_from_LO; |
| 629 | std::vector<bool> best_inliers_mask(points_size); |
| 630 | if (! parallel) { |
| 631 | // adaptive sprt test |
| 632 | double IoU = 0, mean_num_est_models = 0; |
| 633 | bool adapt = IS_NON_RAND_TEST || params->getVerifier() == VerificationMethod ::ASPRT, was_LO_run = false; |
| 634 | int min_non_random_inliers = 30, iters = 0, num_estimations = 0, max_iters = params->getMaxIters(); |
| 635 | Mat non_degenerate_model, lo_model; |
| 636 | Score current_score, non_degenerate_model_score, best_score_sample; |
| 637 | std::vector<bool> model_inliers_mask (points_size); |
| 638 | std::vector<Mat> models(_estimator->getMaxNumSolutions()); |
| 639 | std::vector<int> sample(_estimator->getMinimalSampleSize()), supports; |
| 640 | supports.reserve(n: 3*MAX_MODELS_ADAPT); // store model supports during adaption |
| 641 | auto update_best = [&] (const Mat &new_model, const Score &new_score, bool from_lo=false) { |
| 642 | _quality->getInliers(model: new_model, inliers_mask&: model_inliers_mask); |
| 643 | IoU = Utils::intersectionOverUnion(a: best_inliers_mask, b: model_inliers_mask); |
| 644 | best_inliers_mask = model_inliers_mask; |
| 645 | |
| 646 | 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 |
| 647 | // save old best model for non-randomness test if necessary |
| 648 | models_for_random_test.emplace_back(args: best_model.clone()); |
| 649 | samples_for_random_test.emplace_back(args&: best_sample); |
| 650 | } |
| 651 | |
| 652 | // update score, model, inliers and max iterations |
| 653 | best_score = new_score; |
| 654 | new_model.copyTo(m: best_model); |
| 655 | |
| 656 | if (!from_lo) { |
| 657 | best_sample = sample; |
| 658 | if (IS_FUNDAMENTAL) { // avoid degeneracy after LO run |
| 659 | // save last model not from LO |
| 660 | best_model.copyTo(m: best_model_not_from_LO); |
| 661 | best_score_model_not_from_LO = best_score; |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | _model_verifier->update(score: best_score, iteration: iters); |
| 666 | max_iters = _termination->update(model: best_model, inlier_number: best_score.inlier_number); |
| 667 | // max_iters = std::max(max_iters, std::min(10, params->getMaxIters())); |
| 668 | if (!adapt) // update quality and verifier to save evaluation time of a model |
| 669 | _quality->setBestScore(best_score.score); |
| 670 | last_model_from_LO = from_lo; |
| 671 | }; |
| 672 | |
| 673 | auto run_lo = [&] (const Mat &_model, const Score &_score, bool force_lo) { |
| 674 | was_LO_run = true; |
| 675 | _local_optimization->setCurrentRANSACiter(force_lo ? iters : -1); |
| 676 | Score lo_score; |
| 677 | if (_local_optimization->refineModel(best_model: _model, best_model_score: _score, new_model&: lo_model, new_model_score&: lo_score) && lo_score.isBetter(score2: best_score)) |
| 678 | update_best(lo_model, lo_score, true); |
| 679 | }; |
| 680 | |
| 681 | for (; iters < max_iters; iters++) { |
| 682 | _sampler->generateSample(sample); |
| 683 | int number_of_models; |
| 684 | if (adapt) { |
| 685 | number_of_models = _estimator->estimateModels(sample, models); |
| 686 | mean_num_est_models += number_of_models; |
| 687 | num_estimations++; |
| 688 | } else { |
| 689 | number_of_models = _estimator->estimateModels(sample, models); |
| 690 | } |
| 691 | for (int i = 0; i < number_of_models; i++) { |
| 692 | num_total_tested_models++; |
| 693 | if (adapt) { |
| 694 | current_score = _quality->getScore(model: models[i]); |
| 695 | supports.emplace_back(args&: current_score.inlier_number); |
| 696 | if (IS_NON_RAND_TEST && best_score_sample.isBetter(score2: current_score)) { |
| 697 | models_for_random_test.emplace_back(args: models[i].clone()); |
| 698 | samples_for_random_test.emplace_back(args&: sample); |
| 699 | } |
| 700 | } else { |
| 701 | if (! _model_verifier->isModelGood(model: models[i], score&: current_score)) |
| 702 | continue; |
| 703 | } |
| 704 | if (current_score.isBetter(score2: best_score_sample)) { |
| 705 | if (_degeneracy->recoverIfDegenerate(sample, best_model: models[i], score: current_score, |
| 706 | non_degenerate_model, non_degenerate_model_score)) { |
| 707 | // check if best non degenerate model is better than so far the best model |
| 708 | if (non_degenerate_model_score.isBetter(score2: best_score)) { |
| 709 | update_best(non_degenerate_model, non_degenerate_model_score); |
| 710 | best_score_sample = current_score.isBetter(score2: best_score) ? best_score : current_score; |
| 711 | } else continue; |
| 712 | } else { |
| 713 | best_score_sample = current_score; |
| 714 | update_best(models[i], current_score); |
| 715 | } |
| 716 | |
| 717 | if (LO && ((iters < max_iters && best_score.inlier_number > min_non_random_inliers && IoU < IOU_SIMILARITY_THR))) |
| 718 | run_lo(best_model, best_score, false); |
| 719 | |
| 720 | } // end of if so far the best score |
| 721 | } // end loop of number of models |
| 722 | if (adapt && iters >= MAX_ITERS_ADAPT && num_total_tested_models >= MAX_MODELS_ADAPT) { |
| 723 | adapt = false; |
| 724 | lambda_non_random_all_inliers = getLambda(supports, cdf_thr: 2.32, points_size, sample_size, is_independent: false, min_non_random_inliers); |
| 725 | _model_verifier->updateSPRT(time_model_est: params->getTimeForModelEstimation(), time_corr_ver: 1.0, new_avg_models: mean_num_est_models/num_estimations, new_delta: lambda_non_random_all_inliers/points_size,new_epsilon: (double)std::max(a: min_non_random_inliers, b: best_score.inlier_number)/points_size, best_score); |
| 726 | } |
| 727 | } // end main while loop |
| 728 | final_iters = iters; |
| 729 | if (! was_LO_run && !best_model.empty() && LO) |
| 730 | run_lo(best_model, best_score, true); |
| 731 | } else { // parallel VSAC |
| 732 | const int MAX_THREADS = getNumThreads(), growth_max_samples = params->getProsacMaxSamples(); |
| 733 | const bool is_prosac = params->getSampler() == SamplingMethod::SAMPLING_PROSAC; |
| 734 | std::atomic_bool success(false); |
| 735 | std::atomic_int num_hypothesis_tested(0), thread_cnt(0), max_number_inliers(0), subset_size, termination_length; |
| 736 | std::atomic<float> best_score_all(std::numeric_limits<float>::max()); |
| 737 | std::vector<Score> best_scores(MAX_THREADS), best_scores_not_LO; |
| 738 | std::vector<Mat> best_models(MAX_THREADS), best_models_not_LO, K1_apx, K2_apx; |
| 739 | std::vector<int> num_tested_models_threads(MAX_THREADS), growth_function, non_random_inliers; |
| 740 | std::vector<std::vector<Mat>> tested_models_threads(MAX_THREADS); |
| 741 | std::vector<std::vector<std::vector<int>>> tested_samples_threads(MAX_THREADS); |
| 742 | std::vector<std::vector<int>> best_samples_threads(MAX_THREADS); |
| 743 | std::vector<bool> last_model_from_LO_vec; |
| 744 | std::vector<double> lambda_non_random_all_inliers_vec(MAX_THREADS); |
| 745 | if (IS_FUNDAMENTAL) { |
| 746 | last_model_from_LO_vec = std::vector<bool>(MAX_THREADS); |
| 747 | best_models_not_LO = std::vector<Mat>(MAX_THREADS); |
| 748 | best_scores_not_LO = std::vector<Score>(MAX_THREADS); |
| 749 | K1_apx = std::vector<Mat>(MAX_THREADS); |
| 750 | K2_apx = std::vector<Mat>(MAX_THREADS); |
| 751 | } |
| 752 | if (is_prosac) { |
| 753 | growth_function = _sampler.dynamicCast<ProsacSampler>()->getGrowthFunction(); |
| 754 | subset_size = 2*sample_size; // n, size of the current sampling pool |
| 755 | termination_length = points_size; |
| 756 | } |
| 757 | /////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 758 | parallel_for_(range: Range(0, MAX_THREADS), functor: [&](const Range & /*range*/) { |
| 759 | if (!success) { // cover all if not success to avoid thread creating new variables |
| 760 | const int thread_rng_id = thread_cnt++; |
| 761 | bool adapt = params->getVerifier() == VerificationMethod ::ASPRT || IS_NON_RAND_TEST; |
| 762 | int thread_state = _state + thread_rng_id, min_non_random_inliers = 0, num_tested_models = 0, |
| 763 | num_estimations = 0, mean_num_est_models = 0, iters, max_iters = params->getMaxIters(); |
| 764 | double IoU = 0, lambda_non_random_all_inliers_thread = -1; |
| 765 | std::vector<Mat> tested_models_thread; tested_models_thread.reserve(n: MAX_TEST_MODELS_NONRAND); |
| 766 | std::vector<std::vector<int>> tested_samples_thread; tested_samples_thread.reserve(n: MAX_TEST_MODELS_NONRAND); |
| 767 | Ptr<UniformRandomGenerator> random_gen; |
| 768 | if (is_prosac) random_gen = UniformRandomGenerator::create(state: thread_state); |
| 769 | Ptr<Error> error; |
| 770 | Ptr<Estimator> estimator; |
| 771 | Ptr<Degeneracy> degeneracy; |
| 772 | Ptr<Quality> quality; |
| 773 | Ptr<ModelVerifier> model_verifier; |
| 774 | Ptr<Sampler> sampler; |
| 775 | Ptr<RandomGenerator> lo_sampler; |
| 776 | Ptr<Termination> termination; |
| 777 | Ptr<LocalOptimization> local_optimization; |
| 778 | Ptr<MinimalSolver> min_solver; |
| 779 | Ptr<NonMinimalSolver> non_min_solver; |
| 780 | Ptr<WeightFunction> weight_fnc; |
| 781 | initialize (state: thread_state, min_solver, non_min_solver, error, estimator, degeneracy, quality, |
| 782 | verifier&: model_verifier, lo&: local_optimization, termination, sampler, lo_sampler, weight_fnc, parallel_call: true); |
| 783 | bool is_last_from_LO_thread = false; |
| 784 | Mat best_model_thread, non_degenerate_model, lo_model, best_not_LO_thread; |
| 785 | Score best_score_thread, current_score, non_denegenerate_model_score, lo_score, best_score_all_threads, best_not_LO_score_thread; |
| 786 | std::vector<int> sample(estimator->getMinimalSampleSize()), best_sample_thread, supports; |
| 787 | supports.reserve(n: 3*MAX_MODELS_ADAPT); // store model supports |
| 788 | std::vector<bool> best_inliers_mask_local(points_size, false), model_inliers_mask(points_size, false); |
| 789 | std::vector<Mat> models(estimator->getMaxNumSolutions()); |
| 790 | auto update_best = [&] (const Score &new_score, const Mat &new_model, bool from_LO=false) { |
| 791 | // update best score of all threads |
| 792 | if (max_number_inliers < new_score.inlier_number) max_number_inliers = new_score.inlier_number; |
| 793 | if (best_score_all > new_score.score) |
| 794 | best_score_all = new_score.score; |
| 795 | best_score_all_threads = Score(max_number_inliers, best_score_all); |
| 796 | // |
| 797 | quality->getInliers(model: new_model, inliers_mask&: model_inliers_mask); |
| 798 | IoU = Utils::intersectionOverUnion(a: best_inliers_mask_local, b: model_inliers_mask); |
| 799 | if (!best_model_thread.empty() && (int)tested_models_thread.size() < MAX_TEST_MODELS_NONRAND && IoU < IOU_SIMILARITY_THR) { |
| 800 | tested_models_thread.emplace_back(args: best_model_thread.clone()); |
| 801 | tested_samples_thread.emplace_back(args&: best_sample_thread); |
| 802 | } |
| 803 | if (!adapt) { // update quality and verifier |
| 804 | quality->setBestScore(best_score_all); |
| 805 | model_verifier->update(score: best_score_all_threads, iteration: iters); |
| 806 | } |
| 807 | // copy new score to best score |
| 808 | best_score_thread = new_score; |
| 809 | best_sample_thread = sample; |
| 810 | best_inliers_mask_local = model_inliers_mask; |
| 811 | // remember best model |
| 812 | new_model.copyTo(m: best_model_thread); |
| 813 | |
| 814 | // update upper bound of iterations |
| 815 | if (is_prosac) { |
| 816 | int new_termination_length; |
| 817 | max_iters = termination.dynamicCast<ProsacTerminationCriteria>()-> |
| 818 | updateTerminationLength(model: best_model_thread, inliers_size: best_score_thread.inlier_number, found_termination_length&: new_termination_length); |
| 819 | // update termination length |
| 820 | if (new_termination_length < termination_length) |
| 821 | termination_length = new_termination_length; |
| 822 | } else max_iters = termination->update(model: best_model_thread, inlier_number: max_number_inliers); |
| 823 | if (IS_FUNDAMENTAL) { |
| 824 | is_last_from_LO_thread = from_LO; |
| 825 | if (!from_LO) { |
| 826 | best_model_thread.copyTo(m: best_not_LO_thread); |
| 827 | best_not_LO_score_thread = best_score_thread; |
| 828 | } |
| 829 | } |
| 830 | }; |
| 831 | bool was_LO_run = false; |
| 832 | auto runLO = [&] (int current_ransac_iters) { |
| 833 | was_LO_run = true; |
| 834 | local_optimization->setCurrentRANSACiter(current_ransac_iters); |
| 835 | if (local_optimization->refineModel(best_model: best_model_thread, best_model_score: best_score_thread, new_model&: lo_model, |
| 836 | new_model_score&: lo_score) && lo_score.isBetter(score2: best_score_thread)) |
| 837 | update_best(lo_score, lo_model, true); |
| 838 | }; |
| 839 | for (iters = 0; iters < max_iters && !success; iters++) { |
| 840 | success = num_hypothesis_tested++ > max_iters; |
| 841 | if (iters % 10 && !adapt) { |
| 842 | // Synchronize threads. just to speed verification of model. |
| 843 | quality->setBestScore(std::min(a: best_score_thread.score, b: (float)best_score_all)); |
| 844 | model_verifier->update(score: best_score_thread.inlier_number > max_number_inliers ? best_score_thread : best_score_all_threads, iteration: iters); |
| 845 | } |
| 846 | |
| 847 | if (is_prosac) { |
| 848 | if (num_hypothesis_tested > growth_max_samples) { |
| 849 | // if PROSAC has not converged to solution then do uniform sampling. |
| 850 | random_gen->generateUniqueRandomSet(sample, subset_size: sample_size, max_range: points_size); |
| 851 | } else { |
| 852 | if (num_hypothesis_tested >= growth_function[subset_size-1] && subset_size < termination_length-MAX_THREADS) { |
| 853 | subset_size++; |
| 854 | if (subset_size >= points_size) subset_size = points_size-1; |
| 855 | } |
| 856 | if (growth_function[subset_size-1] < num_hypothesis_tested) { |
| 857 | // The sample contains m-1 points selected from U_(n-1) at random and u_n |
| 858 | random_gen->generateUniqueRandomSet(sample, subset_size: sample_size-1, max_range: subset_size-1); |
| 859 | sample[sample_size-1] = subset_size-1; |
| 860 | } else |
| 861 | // Select m points from U_n at random. |
| 862 | random_gen->generateUniqueRandomSet(sample, subset_size: sample_size, max_range: subset_size); |
| 863 | } |
| 864 | } else sampler->generateSample(sample); // use local sampler |
| 865 | |
| 866 | const int number_of_models = estimator->estimateModels(sample, models); |
| 867 | if (adapt) { |
| 868 | num_estimations++; mean_num_est_models += number_of_models; |
| 869 | } |
| 870 | for (int i = 0; i < number_of_models; i++) { |
| 871 | num_tested_models++; |
| 872 | if (adapt) { |
| 873 | current_score = quality->getScore(model: models[i]); |
| 874 | supports.emplace_back(args&: current_score.inlier_number); |
| 875 | } else if (! model_verifier->isModelGood(model: models[i], score&: current_score)) |
| 876 | continue; |
| 877 | |
| 878 | if (current_score.isBetter(score2: best_score_all_threads)) { |
| 879 | if (degeneracy->recoverIfDegenerate(sample, best_model: models[i], score: current_score, |
| 880 | non_degenerate_model, non_degenerate_model_score&: non_denegenerate_model_score)) { |
| 881 | // check if best non degenerate model is better than so far the best model |
| 882 | if (non_denegenerate_model_score.isBetter(score2: best_score_thread)) |
| 883 | update_best(non_denegenerate_model_score, non_degenerate_model); |
| 884 | else continue; |
| 885 | } else update_best(current_score, models[i]); |
| 886 | if (LO && num_hypothesis_tested < max_iters && IoU < IOU_SIMILARITY_THR && |
| 887 | best_score_thread.inlier_number > min_non_random_inliers) |
| 888 | runLO(iters); |
| 889 | } // end of if so far the best score |
| 890 | else if ((int)tested_models_thread.size() < MAX_TEST_MODELS_NONRAND) { |
| 891 | tested_models_thread.emplace_back(args: models[i].clone()); |
| 892 | tested_samples_thread.emplace_back(args&: sample); |
| 893 | } |
| 894 | if (num_hypothesis_tested > max_iters) { |
| 895 | success = true; break; |
| 896 | } |
| 897 | } // end loop of number of models |
| 898 | if (adapt && iters >= MAX_ITERS_ADAPT && num_tested_models >= MAX_MODELS_ADAPT) { |
| 899 | adapt = false; |
| 900 | lambda_non_random_all_inliers_thread = getLambda(supports, cdf_thr: 2.32, points_size, sample_size, is_independent: false, min_non_random_inliers); |
| 901 | model_verifier->updateSPRT(time_model_est: params->getTimeForModelEstimation(), time_corr_ver: 1, new_avg_models: (double)mean_num_est_models/num_estimations, new_delta: lambda_non_random_all_inliers_thread/points_size, |
| 902 | new_epsilon: (double)std::max(a: min_non_random_inliers, b: best_score.inlier_number)/points_size, best_score: best_score_all_threads); |
| 903 | } |
| 904 | if (!adapt && LO && num_hypothesis_tested < max_iters && !was_LO_run && !best_model_thread.empty() && |
| 905 | best_score_thread.inlier_number > min_non_random_inliers) |
| 906 | runLO(iters); |
| 907 | } // end of loop over iters |
| 908 | if (! was_LO_run && !best_model_thread.empty() && LO) |
| 909 | runLO(-1 /*use full iterations of LO*/); |
| 910 | best_model_thread.copyTo(m: best_models[thread_rng_id]); |
| 911 | best_scores[thread_rng_id] = best_score_thread; |
| 912 | num_tested_models_threads[thread_rng_id] = num_tested_models; |
| 913 | tested_models_threads[thread_rng_id] = tested_models_thread; |
| 914 | tested_samples_threads[thread_rng_id] = tested_samples_thread; |
| 915 | best_samples_threads[thread_rng_id] = best_sample_thread; |
| 916 | if (IS_FUNDAMENTAL) { |
| 917 | best_scores_not_LO[thread_rng_id] = best_not_LO_score_thread; |
| 918 | best_not_LO_thread.copyTo(m: best_models_not_LO[thread_rng_id]); |
| 919 | last_model_from_LO_vec[thread_rng_id] = is_last_from_LO_thread; |
| 920 | } |
| 921 | lambda_non_random_all_inliers_vec[thread_rng_id] = lambda_non_random_all_inliers_thread; |
| 922 | }}); // end parallel |
| 923 | /////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 924 | // find best model from all threads' models |
| 925 | best_score = best_scores[0]; |
| 926 | int best_thread_idx = 0; |
| 927 | for (int i = 1; i < MAX_THREADS; i++) |
| 928 | if (best_scores[i].isBetter(score2: best_score)) { |
| 929 | best_score = best_scores[i]; |
| 930 | best_thread_idx = i; |
| 931 | } |
| 932 | best_model = best_models[best_thread_idx]; |
| 933 | if (IS_FUNDAMENTAL) { |
| 934 | last_model_from_LO = last_model_from_LO_vec[best_thread_idx]; |
| 935 | K1_approx = K1_apx[best_thread_idx]; |
| 936 | K2_approx = K2_apx[best_thread_idx]; |
| 937 | } |
| 938 | final_iters = num_hypothesis_tested; |
| 939 | best_sample = best_samples_threads[best_thread_idx]; |
| 940 | int num_lambdas = 0; |
| 941 | double avg_lambda = 0; |
| 942 | for (int i = 0; i < MAX_THREADS; i++) { |
| 943 | if (IS_FUNDAMENTAL && best_scores_not_LO[i].isBetter(score2: best_score_model_not_from_LO)) { |
| 944 | best_score_model_not_from_LO = best_scores_not_LO[i]; |
| 945 | best_models_not_LO[i].copyTo(m: best_model_not_from_LO); |
| 946 | } |
| 947 | if (IS_NON_RAND_TEST && lambda_non_random_all_inliers_vec[i] > 0) { |
| 948 | num_lambdas ++; |
| 949 | avg_lambda += lambda_non_random_all_inliers_vec[i]; |
| 950 | } |
| 951 | num_total_tested_models += num_tested_models_threads[i]; |
| 952 | if ((int)models_for_random_test.size() < MAX_TEST_MODELS_NONRAND) { |
| 953 | for (int m = 0; m < (int)tested_models_threads[i].size(); m++) { |
| 954 | models_for_random_test.emplace_back(args: tested_models_threads[i][m].clone()); |
| 955 | samples_for_random_test.emplace_back(args&: tested_samples_threads[i][m]); |
| 956 | if ((int)models_for_random_test.size() == MAX_TEST_MODELS_NONRAND) |
| 957 | break; |
| 958 | } |
| 959 | } |
| 960 | } |
| 961 | if (IS_NON_RAND_TEST && num_lambdas > 0 && avg_lambda > 0) |
| 962 | lambda_non_random_all_inliers = avg_lambda / num_lambdas; |
| 963 | } |
| 964 | if (best_model.empty()) { |
| 965 | ransac_output = RansacOutput::create(model_: best_model, inliers_mask_: std::vector<bool>(), number_inliers_: best_score.inlier_number, number_iterations_: final_iters, conf: ModelConfidence::RANDOM, errors_: std::vector<float>()); |
| 966 | return false; |
| 967 | } |
| 968 | if (last_model_from_LO && IS_FUNDAMENTAL && K1.empty() && K2.empty()) { |
| 969 | Score new_score; Mat new_model; |
| 970 | const double INL_THR = 0.80; |
| 971 | if (parallel) |
| 972 | _quality->getInliers(model: best_model, inliers_mask&: best_inliers_mask); |
| 973 | // run additional degeneracy check for F: |
| 974 | if (_degeneracy.dynamicCast<FundamentalDegeneracy>()->verifyFundamental(F_best: best_model, F_score: best_score, inliers_mask: best_inliers_mask, F_new&: new_model, new_score)) { |
| 975 | // so-far-the-best F is degenerate |
| 976 | // Update best F using non-degenerate F or the one which is not from LO |
| 977 | if (new_score.isBetter(score2: best_score_model_not_from_LO) && new_score.inlier_number > INL_THR*best_score.inlier_number) { |
| 978 | best_score = new_score; |
| 979 | new_model.copyTo(m: best_model); |
| 980 | } else if (best_score_model_not_from_LO.inlier_number > INL_THR*best_score.inlier_number) { |
| 981 | best_score = best_score_model_not_from_LO; |
| 982 | best_model_not_from_LO.copyTo(m: best_model); |
| 983 | } |
| 984 | } else { // so-far-the-best F is not degenerate |
| 985 | if (new_score.isBetter(score2: best_score)) { |
| 986 | // if new model is better then update |
| 987 | best_score = new_score; |
| 988 | new_model.copyTo(m: best_model); |
| 989 | } |
| 990 | } |
| 991 | } |
| 992 | if (params->getFinalPolisher() != PolishingMethod::NONE_POLISHER) { |
| 993 | Mat polished_model; |
| 994 | Score polisher_score; |
| 995 | if (polisher->polishSoFarTheBestModel(model: best_model, best_model_score: best_score, // polish final model |
| 996 | new_model&: polished_model, new_model_score&: polisher_score) && polisher_score.isBetter(score2: best_score)) { |
| 997 | best_score = polisher_score; |
| 998 | polished_model.copyTo(m: best_model); |
| 999 | } |
| 1000 | } |
| 1001 | |
| 1002 | ///////////////// get inliers of the best model and points' residuals /////////////// |
| 1003 | std::vector<bool> inliers_mask; std::vector<float> residuals; |
| 1004 | if (params->isMaskRequired()) { |
| 1005 | inliers_mask = std::vector<bool>(points_size); |
| 1006 | residuals = _error->getErrors(model: best_model); |
| 1007 | _quality->getInliers(errors: residuals, inliers&: inliers_mask, threshold); |
| 1008 | } |
| 1009 | |
| 1010 | ModelConfidence model_conf = ModelConfidence::UNKNOWN; |
| 1011 | if (IS_NON_RAND_TEST) { |
| 1012 | std::vector<int> temp_inliers(points_size); |
| 1013 | const int non_random_inls_best_model = getIndependentInliers(model_: best_model, sample: best_sample, inliers&: temp_inliers, |
| 1014 | num_inliers_: _quality->getInliers(model: best_model, inliers&: temp_inliers)); |
| 1015 | // quick test on lambda from all inliers (= upper bound of independent inliers) |
| 1016 | // if model with independent inliers is not random for Poisson with all inliers then it is not random using independent inliers too |
| 1017 | if (pow(x: Utils::getPoissonCDF(lambda: lambda_non_random_all_inliers, tentative_inliers: non_random_inls_best_model), y: num_total_tested_models) < 0.9999) { |
| 1018 | std::vector<int> inliers_list(models_for_random_test.size()); |
| 1019 | for (int m = 0; m < (int)models_for_random_test.size(); m++) |
| 1020 | inliers_list[m] = getIndependentInliers(model_: models_for_random_test[m], sample: samples_for_random_test[m], |
| 1021 | inliers&: temp_inliers, num_inliers_: _quality->getInliers(model: models_for_random_test[m], inliers&: temp_inliers)); |
| 1022 | int min_non_rand_inliers; |
| 1023 | const double lambda = getLambda(supports&: inliers_list, cdf_thr: 1.644, points_size, sample_size, is_independent: true, min_non_random_inliers&: min_non_rand_inliers); |
| 1024 | const double cdf_lambda = Utils::getPoissonCDF(lambda, tentative_inliers: non_random_inls_best_model), cdf_N = pow(x: cdf_lambda, y: num_total_tested_models); |
| 1025 | model_conf = cdf_N < 0.9999 ? ModelConfidence ::RANDOM : ModelConfidence ::NON_RANDOM; |
| 1026 | } else model_conf = ModelConfidence ::NON_RANDOM; |
| 1027 | } |
| 1028 | ransac_output = RansacOutput::create(model_: best_model, inliers_mask_: inliers_mask, number_inliers_: best_score.inlier_number, number_iterations_: final_iters, conf: model_conf, errors_: residuals); |
| 1029 | return true; |
| 1030 | } |
| 1031 | }; |
| 1032 | |
| 1033 | /* |
| 1034 | * 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 |
| 1035 | * pts1 are image points, if pnp pts2 are object points otherwise - image points as well. |
| 1036 | * output is matrix of size N x (a + b) |
| 1037 | * return points_size = N |
| 1038 | */ |
| 1039 | int mergePoints (InputArray pts1_, InputArray pts2_, Mat &pts, bool ispnp) { |
| 1040 | Mat pts1 = pts1_.getMat(), pts2 = pts2_.getMat(); |
| 1041 | auto convertPoints = [] (Mat &points, int pt_dim) { |
| 1042 | points.convertTo(m: points, CV_32F); // convert points to have float precision |
| 1043 | if (points.channels() > 1) |
| 1044 | points = points.reshape(cn: 1, rows: (int)points.total()); // convert point to have 1 channel |
| 1045 | if (points.rows < points.cols) |
| 1046 | transpose(src: points, dst: points); // transpose so points will be in rows |
| 1047 | CV_CheckGE(points.cols, pt_dim, "Invalid dimension of point" ); |
| 1048 | if (points.cols != pt_dim) // in case when image points are 3D convert them to 2D |
| 1049 | points = points.colRange(startcol: 0, endcol: pt_dim); |
| 1050 | }; |
| 1051 | |
| 1052 | convertPoints(pts1, 2); // pts1 are always image points |
| 1053 | convertPoints(pts2, ispnp ? 3 : 2); // for PnP points are 3D |
| 1054 | |
| 1055 | // points are of size [Nx2 Nx2] = Nx4 for H, F, E |
| 1056 | // points are of size [Nx2 Nx3] = Nx5 for PnP |
| 1057 | hconcat(src1: pts1, src2: pts2, dst: pts); |
| 1058 | return pts.rows; |
| 1059 | } |
| 1060 | |
| 1061 | void saveMask (OutputArray mask, const std::vector<bool> &inliers_mask) { |
| 1062 | if (mask.needed()) { |
| 1063 | const int points_size = (int) inliers_mask.size(); |
| 1064 | Mat tmp_mask(points_size, 1, CV_8U); |
| 1065 | auto * maskptr = tmp_mask.ptr<uchar>(); |
| 1066 | for (int i = 0; i < points_size; i++) |
| 1067 | maskptr[i] = (uchar) inliers_mask[i]; |
| 1068 | tmp_mask.copyTo(m: mask); |
| 1069 | } |
| 1070 | } |
| 1071 | void setParameters (Ptr<Model> ¶ms, EstimationMethod estimator, const UsacParams &usac_params, |
| 1072 | bool mask_needed) { |
| 1073 | params = Model::create(threshold_: usac_params.threshold, estimator_: estimator, sampler_: usac_params.sampler, |
| 1074 | confidence_: usac_params.confidence, max_iterations_: usac_params.maxIterations, score_: usac_params.score); |
| 1075 | params->setLocalOptimization(usac_params.loMethod); |
| 1076 | params->setLOSampleSize(usac_params.loSampleSize); |
| 1077 | params->setLOIterations(usac_params.loIterations); |
| 1078 | params->setParallel(usac_params.isParallel); |
| 1079 | params->setNeighborsType(usac_params.neighborsSearch); |
| 1080 | params->setRandomGeneratorState(usac_params.randomGeneratorState); |
| 1081 | params->maskRequired(required: mask_needed); |
| 1082 | } |
| 1083 | |
| 1084 | void setParameters (int flag, Ptr<Model> ¶ms, EstimationMethod estimator, double thr, |
| 1085 | int max_iters, double conf, bool mask_needed) { |
| 1086 | switch (flag) { |
| 1087 | case USAC_DEFAULT: |
| 1088 | params = Model::create(threshold_: thr, estimator_: estimator, sampler_: SamplingMethod::SAMPLING_UNIFORM, confidence_: conf, max_iterations_: max_iters, |
| 1089 | score_: ScoreMethod::SCORE_METHOD_MSAC); |
| 1090 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_INNER_AND_ITER_LO); |
| 1091 | break; |
| 1092 | case USAC_MAGSAC: |
| 1093 | params = Model::create(threshold_: thr, estimator_: estimator, sampler_: SamplingMethod::SAMPLING_UNIFORM, confidence_: conf, max_iterations_: max_iters, |
| 1094 | score_: ScoreMethod::SCORE_METHOD_MAGSAC); |
| 1095 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_SIGMA); |
| 1096 | params->setLOSampleSize(params->isHomography() ? 75 : 50); |
| 1097 | params->setLOIterations(params->isHomography() ? 15 : 10); |
| 1098 | break; |
| 1099 | case USAC_PARALLEL: |
| 1100 | params = Model::create(threshold_: thr, estimator_: estimator, sampler_: SamplingMethod::SAMPLING_UNIFORM, confidence_: conf, max_iterations_: max_iters, |
| 1101 | score_: ScoreMethod::SCORE_METHOD_MSAC); |
| 1102 | params->setParallel(true); |
| 1103 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_INNER_LO); |
| 1104 | break; |
| 1105 | case USAC_ACCURATE: |
| 1106 | params = Model::create(threshold_: thr, estimator_: estimator, sampler_: SamplingMethod::SAMPLING_UNIFORM, confidence_: conf, max_iterations_: max_iters, |
| 1107 | score_: ScoreMethod::SCORE_METHOD_MSAC); |
| 1108 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_GC); |
| 1109 | params->setLOSampleSize(20); |
| 1110 | params->setLOIterations(25); |
| 1111 | break; |
| 1112 | case USAC_FAST: |
| 1113 | params = Model::create(threshold_: thr, estimator_: estimator, sampler_: SamplingMethod::SAMPLING_UNIFORM, confidence_: conf, max_iterations_: max_iters, |
| 1114 | score_: ScoreMethod::SCORE_METHOD_MSAC); |
| 1115 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_INNER_AND_ITER_LO); |
| 1116 | params->setLOIterations(5); |
| 1117 | params->setLOIterativeIters(3); |
| 1118 | break; |
| 1119 | case USAC_PROSAC: |
| 1120 | params = Model::create(threshold_: thr, estimator_: estimator, sampler_: SamplingMethod::SAMPLING_PROSAC, confidence_: conf, max_iterations_: max_iters, |
| 1121 | score_: ScoreMethod::SCORE_METHOD_MSAC); |
| 1122 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_INNER_LO); |
| 1123 | break; |
| 1124 | case USAC_FM_8PTS: |
| 1125 | params = Model::create(threshold_: thr, estimator_: EstimationMethod::FUNDAMENTAL8,sampler_: SamplingMethod::SAMPLING_UNIFORM, |
| 1126 | confidence_: conf, max_iterations_: max_iters,score_: ScoreMethod::SCORE_METHOD_MSAC); |
| 1127 | params->setLocalOptimization(LocalOptimMethod ::LOCAL_OPTIM_INNER_LO); |
| 1128 | break; |
| 1129 | default: CV_Error(cv::Error::StsBadFlag, "Incorrect flag for USAC!" ); |
| 1130 | } |
| 1131 | // do not do too many iterations for PnP |
| 1132 | if (estimator == EstimationMethod::P3P) { |
| 1133 | if (params->getLOInnerMaxIters() > 10) |
| 1134 | params->setLOIterations(10); |
| 1135 | params->setLOIterativeIters(0); |
| 1136 | params->setFinalLSQ(3); |
| 1137 | } |
| 1138 | |
| 1139 | params->maskRequired(required: mask_needed); |
| 1140 | } |
| 1141 | |
| 1142 | Mat findHomography (InputArray srcPoints, InputArray dstPoints, int method, double thr, |
| 1143 | OutputArray mask, const int max_iters, const double confidence) { |
| 1144 | Ptr<Model> params; |
| 1145 | setParameters(flag: method, params, estimator: EstimationMethod::HOMOGRAPHY, thr, max_iters, conf: confidence, mask_needed: mask.needed()); |
| 1146 | Ptr<RansacOutput> ransac_output; |
| 1147 | if (run(params, points1: srcPoints, points2: dstPoints, |
| 1148 | ransac_output, K1_: noArray(), K2_: noArray(), dist_coeff1: noArray(), dist_coeff2: noArray())) { |
| 1149 | saveMask(mask, inliers_mask: ransac_output->getInliersMask()); |
| 1150 | return ransac_output->getModel() / ransac_output->getModel().at<double>(i0: 2,i1: 2); |
| 1151 | } |
| 1152 | if (mask.needed()){ |
| 1153 | mask.create(rows: std::max(a: srcPoints.getMat().rows, b: srcPoints.getMat().cols), cols: 1, CV_8U); |
| 1154 | mask.setTo(value: Scalar::all(v0: 0)); |
| 1155 | } |
| 1156 | return Mat(); |
| 1157 | } |
| 1158 | |
| 1159 | Mat findFundamentalMat( InputArray points1, InputArray points2, int method, double thr, |
| 1160 | double confidence, int max_iters, OutputArray mask ) { |
| 1161 | Ptr<Model> params; |
| 1162 | setParameters(flag: method, params, estimator: EstimationMethod::FUNDAMENTAL, thr, max_iters, conf: confidence, mask_needed: mask.needed()); |
| 1163 | Ptr<RansacOutput> ransac_output; |
| 1164 | if (run(params, points1, points2, |
| 1165 | ransac_output, K1_: noArray(), K2_: noArray(), dist_coeff1: noArray(), dist_coeff2: noArray())) { |
| 1166 | saveMask(mask, inliers_mask: ransac_output->getInliersMask()); |
| 1167 | return ransac_output->getModel(); |
| 1168 | } |
| 1169 | if (mask.needed()){ |
| 1170 | mask.create(rows: std::max(a: points1.getMat().rows, b: points1.getMat().cols), cols: 1, CV_8U); |
| 1171 | mask.setTo(value: Scalar::all(v0: 0)); |
| 1172 | } |
| 1173 | return Mat(); |
| 1174 | } |
| 1175 | |
| 1176 | Mat findEssentialMat (InputArray points1, InputArray points2, InputArray cameraMatrix1, |
| 1177 | int method, double prob, double thr, OutputArray mask, int maxIters) { |
| 1178 | Ptr<Model> params; |
| 1179 | setParameters(flag: method, params, estimator: EstimationMethod::ESSENTIAL, thr, max_iters: maxIters, conf: prob, mask_needed: mask.needed()); |
| 1180 | Ptr<RansacOutput> ransac_output; |
| 1181 | if (run(params, points1, points2, |
| 1182 | ransac_output, K1_: cameraMatrix1, K2_: cameraMatrix1, dist_coeff1: noArray(), dist_coeff2: noArray())) { |
| 1183 | saveMask(mask, inliers_mask: ransac_output->getInliersMask()); |
| 1184 | return ransac_output->getModel(); |
| 1185 | } |
| 1186 | if (mask.needed()){ |
| 1187 | mask.create(rows: std::max(a: points1.getMat().rows, b: points1.getMat().cols), cols: 1, CV_8U); |
| 1188 | mask.setTo(value: Scalar::all(v0: 0)); |
| 1189 | } |
| 1190 | return Mat(); |
| 1191 | } |
| 1192 | |
| 1193 | bool solvePnPRansac( InputArray objectPoints, InputArray imagePoints, |
| 1194 | InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, |
| 1195 | bool /*useExtrinsicGuess*/, int max_iters, float thr, double conf, |
| 1196 | OutputArray inliers, int method) { |
| 1197 | Ptr<Model> params; |
| 1198 | setParameters(flag: method, params, estimator: cameraMatrix.empty() ? EstimationMethod ::P6P : EstimationMethod ::P3P, |
| 1199 | thr, max_iters, conf, mask_needed: inliers.needed()); |
| 1200 | Ptr<RansacOutput> ransac_output; |
| 1201 | if (run(params, points1: imagePoints, points2: objectPoints, |
| 1202 | ransac_output, K1_: cameraMatrix, K2_: noArray(), dist_coeff1: distCoeffs, dist_coeff2: noArray())) { |
| 1203 | if (inliers.needed()) { |
| 1204 | const auto &inliers_mask = ransac_output->getInliersMask(); |
| 1205 | Mat inliers_; |
| 1206 | for (int i = 0; i < (int)inliers_mask.size(); i++) |
| 1207 | if (inliers_mask[i]) |
| 1208 | inliers_.push_back(elem: i); |
| 1209 | inliers_.copyTo(m: inliers); |
| 1210 | } |
| 1211 | const Mat &model = ransac_output->getModel(); |
| 1212 | model.col(x: 0).copyTo(m: rvec); |
| 1213 | model.col(x: 1).copyTo(m: tvec); |
| 1214 | return true; |
| 1215 | } |
| 1216 | return false; |
| 1217 | } |
| 1218 | |
| 1219 | Mat estimateAffine2D(InputArray from, InputArray to, OutputArray mask, int method, |
| 1220 | double thr, int max_iters, double conf, int /*refineIters*/) { |
| 1221 | Ptr<Model> params; |
| 1222 | setParameters(flag: method, params, estimator: EstimationMethod::AFFINE, thr, max_iters, conf, mask_needed: mask.needed()); |
| 1223 | Ptr<RansacOutput> ransac_output; |
| 1224 | if (run(params, points1: from, points2: to, |
| 1225 | ransac_output, K1_: noArray(), K2_: noArray(), dist_coeff1: noArray(), dist_coeff2: noArray())) { |
| 1226 | saveMask(mask, inliers_mask: ransac_output->getInliersMask()); |
| 1227 | return ransac_output->getModel().rowRange(startrow: 0,endrow: 2); |
| 1228 | } |
| 1229 | if (mask.needed()){ |
| 1230 | mask.create(rows: std::max(a: from.getMat().rows, b: from.getMat().cols), cols: 1, CV_8U); |
| 1231 | mask.setTo(value: Scalar::all(v0: 0)); |
| 1232 | } |
| 1233 | return Mat(); |
| 1234 | } |
| 1235 | |
| 1236 | class ModelImpl : public Model { |
| 1237 | private: |
| 1238 | // main parameters: |
| 1239 | double threshold; |
| 1240 | EstimationMethod estimator; |
| 1241 | SamplingMethod sampler; |
| 1242 | double confidence; |
| 1243 | int max_iterations; |
| 1244 | ScoreMethod score; |
| 1245 | int sample_size; |
| 1246 | |
| 1247 | // Larsson parameters |
| 1248 | bool = true; |
| 1249 | int = 10, = 15; |
| 1250 | |
| 1251 | // solver for a null-space extraction |
| 1252 | MethodSolver null_solver = GEM_SOLVER; |
| 1253 | |
| 1254 | // prosac |
| 1255 | int prosac_max_samples = 200000; |
| 1256 | |
| 1257 | // for neighborhood graph |
| 1258 | int k_nearest_neighbors = 8;//, flann_search_params = 5, num_kd_trees = 1; // for FLANN |
| 1259 | int cell_size = 50; // pixels, for grid neighbors searching |
| 1260 | int radius = 30; // pixels, for radius-search neighborhood graph |
| 1261 | NeighborSearchMethod neighborsType = NeighborSearchMethod::NEIGH_GRID; |
| 1262 | |
| 1263 | // Local Optimization parameters |
| 1264 | LocalOptimMethod lo = LocalOptimMethod ::LOCAL_OPTIM_INNER_LO; |
| 1265 | int lo_sample_size=12, lo_inner_iterations=20, lo_iterative_iterations=8, |
| 1266 | lo_thr_multiplier=10, lo_iter_sample_size = 30; |
| 1267 | |
| 1268 | // Graph cut parameters |
| 1269 | const double spatial_coherence_term = 0.975; |
| 1270 | |
| 1271 | // apply polisher for final RANSAC model |
| 1272 | PolishingMethod polisher = PolishingMethod ::COV_POLISHER; |
| 1273 | |
| 1274 | // preemptive verification test |
| 1275 | VerificationMethod verifier = VerificationMethod ::ASPRT; |
| 1276 | |
| 1277 | // sprt parameters |
| 1278 | // lower bound estimate is 2% of inliers |
| 1279 | // model estimation to verification time = ratio of time needed to estimate model |
| 1280 | // to verification of one point wrt the model |
| 1281 | double sprt_eps = 0.02, sprt_delta = 0.008, avg_num_models, model_est_to_ver_time; |
| 1282 | |
| 1283 | // estimator error |
| 1284 | ErrorMetric est_error; |
| 1285 | |
| 1286 | // progressive napsac |
| 1287 | double relax_coef = 0.1; |
| 1288 | // for building neighborhood graphs |
| 1289 | const std::vector<int> grid_cell_number = {10, 5, 2}; |
| 1290 | |
| 1291 | //for final least squares polisher |
| 1292 | int final_lsq_iters = 7; |
| 1293 | |
| 1294 | bool need_mask = true, // do we need inlier mask in the end |
| 1295 | is_parallel = false, // use parallel RANSAC |
| 1296 | is_nonrand_test = false; // is test for the final model non-randomness |
| 1297 | |
| 1298 | // state of pseudo-random number generator |
| 1299 | int random_generator_state = 0; |
| 1300 | |
| 1301 | // number of iterations of plane-and-parallax in DEGENSAC^+ |
| 1302 | int plane_and_parallax_max_iters = 300; |
| 1303 | |
| 1304 | // magsac parameters: |
| 1305 | int DoF = 2; |
| 1306 | double sigma_quantile = 3.04, upper_incomplete_of_sigma_quantile = 0.00419, |
| 1307 | lower_incomplete_of_sigma_quantile = 0.8629, C = 0.5, maximum_thr = 7.5; |
| 1308 | double k_mlesac = 2.25; // parameter for MLESAC model evaluation |
| 1309 | public: |
| 1310 | ModelImpl (double threshold_, EstimationMethod estimator_, SamplingMethod sampler_, double confidence_, |
| 1311 | int max_iterations_, ScoreMethod score_) : |
| 1312 | threshold(threshold_), estimator(estimator_), sampler(sampler_), confidence(confidence_), max_iterations(max_iterations_), score(score_) { |
| 1313 | switch (estimator_) { |
| 1314 | case (EstimationMethod::AFFINE): |
| 1315 | avg_num_models = 1; model_est_to_ver_time = 50; |
| 1316 | sample_size = 3; est_error = ErrorMetric ::FORW_REPR_ERR; break; |
| 1317 | case (EstimationMethod::HOMOGRAPHY): |
| 1318 | avg_num_models = 0.8; model_est_to_ver_time = 200; |
| 1319 | sample_size = 4; est_error = ErrorMetric ::FORW_REPR_ERR; break; |
| 1320 | case (EstimationMethod::FUNDAMENTAL): |
| 1321 | DoF = 4; C = 0.25; sigma_quantile = 3.64, upper_incomplete_of_sigma_quantile = 0.003657; lower_incomplete_of_sigma_quantile = 1.3012; |
| 1322 | maximum_thr = 2.5; |
| 1323 | avg_num_models = 1.5; model_est_to_ver_time = 200; |
| 1324 | sample_size = 7; est_error = ErrorMetric ::SAMPSON_ERR; break; |
| 1325 | case (EstimationMethod::FUNDAMENTAL8): |
| 1326 | avg_num_models = 1; model_est_to_ver_time = 100; maximum_thr = 2.5; |
| 1327 | sample_size = 8; est_error = ErrorMetric ::SAMPSON_ERR; break; |
| 1328 | case (EstimationMethod::ESSENTIAL): |
| 1329 | DoF = 4; C = 0.25; sigma_quantile = 3.64, upper_incomplete_of_sigma_quantile = 0.003657; lower_incomplete_of_sigma_quantile = 1.3012; |
| 1330 | avg_num_models = 3.93; model_est_to_ver_time = 1000; maximum_thr = 2; |
| 1331 | sample_size = 5; est_error = ErrorMetric ::SAMPSON_ERR; break; |
| 1332 | case (EstimationMethod::P3P): |
| 1333 | avg_num_models = 1.38; model_est_to_ver_time = 800; |
| 1334 | sample_size = 3; est_error = ErrorMetric ::RERPOJ; break; |
| 1335 | case (EstimationMethod::P6P): |
| 1336 | avg_num_models = 1; model_est_to_ver_time = 300; |
| 1337 | sample_size = 6; est_error = ErrorMetric ::RERPOJ; break; |
| 1338 | default: CV_Error(cv::Error::StsNotImplemented, "Estimator has not implemented yet!" ); |
| 1339 | } |
| 1340 | |
| 1341 | if (score_ == ScoreMethod::SCORE_METHOD_MAGSAC) |
| 1342 | polisher = PolishingMethod::MAGSAC; |
| 1343 | |
| 1344 | // for PnP problem we can use only KNN graph |
| 1345 | if (estimator_ == EstimationMethod::P3P || estimator_ == EstimationMethod::P6P) { |
| 1346 | polisher = LSQ_POLISHER; |
| 1347 | neighborsType = NeighborSearchMethod::NEIGH_FLANN_KNN; |
| 1348 | k_nearest_neighbors = 2; |
| 1349 | } |
| 1350 | } |
| 1351 | |
| 1352 | // setters |
| 1353 | void setNonRandomnessTest (bool set) override { is_nonrand_test = set; } |
| 1354 | void setVerifier (VerificationMethod verifier_) override { verifier = verifier_; } |
| 1355 | void setPolisher (PolishingMethod polisher_) override { polisher = polisher_; } |
| 1356 | void setParallel (bool is_parallel_) override { is_parallel = is_parallel_; } |
| 1357 | void setError (ErrorMetric error_) override { est_error = error_; } |
| 1358 | void setLocalOptimization (LocalOptimMethod lo_) override { lo = lo_; } |
| 1359 | void setKNearestNeighhbors (int knn_) override { k_nearest_neighbors = knn_; } |
| 1360 | void setNeighborsType (NeighborSearchMethod neighbors) override { neighborsType = neighbors; } |
| 1361 | void setCellSize (int cell_size_) override { cell_size = cell_size_; } |
| 1362 | void setLOIterations (int iters) override { lo_inner_iterations = iters; } |
| 1363 | void setLOSampleSize (int lo_sample_size_) override { lo_sample_size = lo_sample_size_; } |
| 1364 | void maskRequired (bool need_mask_) override { need_mask = need_mask_; } |
| 1365 | void setRandomGeneratorState (int state) override { random_generator_state = state; } |
| 1366 | void setLOIterativeIters (int iters) override { lo_iterative_iterations = iters; } |
| 1367 | void setFinalLSQ (int iters) override { final_lsq_iters = iters; } |
| 1368 | |
| 1369 | // getters |
| 1370 | int getProsacMaxSamples() const override { return prosac_max_samples; } |
| 1371 | int getLevMarqIters () const override { return larsson_leven_marq_iters_fo; } |
| 1372 | int getLevMarqItersLO () const override { return larsson_leven_marq_iters_lo; } |
| 1373 | bool isNonRandomnessTest () const override { return is_nonrand_test; } |
| 1374 | bool isMaskRequired () const override { return need_mask; } |
| 1375 | NeighborSearchMethod () const override { return neighborsType; } |
| 1376 | int getKNN () const override { return k_nearest_neighbors; } |
| 1377 | ErrorMetric getError () const override { return est_error; } |
| 1378 | EstimationMethod getEstimator () const override { return estimator; } |
| 1379 | int getSampleSize () const override { return sample_size; } |
| 1380 | int getFinalLSQIterations () const override { return final_lsq_iters; } |
| 1381 | int getDegreesOfFreedom () const override { return DoF; } |
| 1382 | double getSigmaQuantile () const override { return sigma_quantile; } |
| 1383 | double getUpperIncompleteOfSigmaQuantile () const override { |
| 1384 | return upper_incomplete_of_sigma_quantile; |
| 1385 | } |
| 1386 | double getLowerIncompleteOfSigmaQuantile () const override { |
| 1387 | return lower_incomplete_of_sigma_quantile; |
| 1388 | } |
| 1389 | double getC () const override { return C; } |
| 1390 | double getKmlesac () const override { return k_mlesac; } |
| 1391 | double getMaximumThreshold () const override { return maximum_thr; } |
| 1392 | double getGraphCutSpatialCoherenceTerm () const override { return spatial_coherence_term; } |
| 1393 | int getLOSampleSize () const override { return lo_sample_size; } |
| 1394 | MethodSolver getRansacSolver () const override { return null_solver; } |
| 1395 | PolishingMethod getFinalPolisher () const override { return polisher; } |
| 1396 | int getLOThresholdMultiplier() const override { return lo_thr_multiplier; } |
| 1397 | int getLOIterativeSampleSize() const override { return lo_iter_sample_size; } |
| 1398 | int getLOIterativeMaxIters() const override { return lo_iterative_iterations; } |
| 1399 | int getLOInnerMaxIters() const override { return lo_inner_iterations; } |
| 1400 | int getPlaneAndParallaxIters () const override { return plane_and_parallax_max_iters; } |
| 1401 | LocalOptimMethod getLO () const override { return lo; } |
| 1402 | ScoreMethod getScore () const override { return score; } |
| 1403 | int getMaxIters () const override { return max_iterations; } |
| 1404 | double getConfidence () const override { return confidence; } |
| 1405 | double getThreshold () const override { return threshold; } |
| 1406 | VerificationMethod getVerifier () const override { return verifier; } |
| 1407 | SamplingMethod getSampler () const override { return sampler; } |
| 1408 | int getRandomGeneratorState () const override { return random_generator_state; } |
| 1409 | double getSPRTdelta () const override { return sprt_delta; } |
| 1410 | double getSPRTepsilon () const override { return sprt_eps; } |
| 1411 | double getSPRTavgNumModels () const override { return avg_num_models; } |
| 1412 | int getCellSize () const override { return cell_size; } |
| 1413 | int getGraphRadius() const override { return radius; } |
| 1414 | double getTimeForModelEstimation () const override { return model_est_to_ver_time; } |
| 1415 | double getRelaxCoef () const override { return relax_coef; } |
| 1416 | const std::vector<int> &getGridCellNumber () const override { return grid_cell_number; } |
| 1417 | bool () const override { return is_larsson_optimization; } |
| 1418 | bool isParallel () const override { return is_parallel; } |
| 1419 | bool isFundamental () const override { |
| 1420 | return estimator == EstimationMethod::FUNDAMENTAL || |
| 1421 | estimator == EstimationMethod::FUNDAMENTAL8; |
| 1422 | } |
| 1423 | bool isHomography () const override { return estimator == EstimationMethod::HOMOGRAPHY; } |
| 1424 | bool isEssential () const override { return estimator == EstimationMethod::ESSENTIAL; } |
| 1425 | bool isPnP() const override { |
| 1426 | return estimator == EstimationMethod ::P3P || estimator == EstimationMethod ::P6P; |
| 1427 | } |
| 1428 | }; |
| 1429 | |
| 1430 | Ptr<Model> Model::create(double threshold_, EstimationMethod estimator_, SamplingMethod sampler_, |
| 1431 | double confidence_, int max_iterations_, ScoreMethod score_) { |
| 1432 | return makePtr<ModelImpl>(a1: threshold_, a1: estimator_, a1: sampler_, a1: confidence_, |
| 1433 | a1: max_iterations_, a1: score_); |
| 1434 | } |
| 1435 | |
| 1436 | bool run (const Ptr<const Model> ¶ms, InputArray points1, InputArray points2, |
| 1437 | Ptr<RansacOutput> &ransac_output, InputArray K1_, InputArray K2_, |
| 1438 | InputArray dist_coeff1, InputArray dist_coeff2) { |
| 1439 | Ransac ransac (params, points1, points2, K1_, K2_, dist_coeff1, dist_coeff2); |
| 1440 | if (ransac.run(ransac_output)) { |
| 1441 | if (params->isPnP()) { |
| 1442 | // convert R to rodrigues and back and recalculate inliers which due to numerical |
| 1443 | // issues can differ |
| 1444 | Mat out, newP; |
| 1445 | Matx33d R, newR, K1; |
| 1446 | Vec3d t, rvec; |
| 1447 | if (K1_.empty()) { |
| 1448 | usac::Utils::decomposeProjection (P: ransac_output->getModel(), K_&: K1, R, t); |
| 1449 | Rodrigues(src: R, dst: rvec); |
| 1450 | hconcat(src1: rvec, src2: t, dst: out); |
| 1451 | hconcat(src1: out, src2: K1, dst: out); |
| 1452 | } else { |
| 1453 | K1 = ransac.K1; |
| 1454 | const Mat Rt = Mat(Matx33d(K1).inv() * Matx34d(ransac_output->getModel())); |
| 1455 | t = Rt.col(x: 3); |
| 1456 | Rodrigues(src: Rt.colRange(startcol: 0,endcol: 3), dst: rvec); |
| 1457 | hconcat(src1: rvec, src2: t, dst: out); |
| 1458 | } |
| 1459 | // Matx33d _K1(K1); |
| 1460 | Rodrigues(src: rvec, dst: newR); |
| 1461 | hconcat(src1: K1 * Matx33d(newR), src2: K1 * Vec3d(t), dst: newP); |
| 1462 | std::vector<bool> inliers_mask(ransac.points_size); |
| 1463 | ransac._quality->getInliers(model: newP, inliers_mask); |
| 1464 | ransac_output = RansacOutput::create(model_: out, inliers_mask_: inliers_mask, number_inliers_: ransac_output->getNumberOfInliers(), |
| 1465 | number_iterations_: ransac_output->getNumberOfIters(), conf: ransac_output->getConfidence(), errors_: ransac_output->getResiduals()); |
| 1466 | } |
| 1467 | return true; |
| 1468 | } |
| 1469 | return false; |
| 1470 | } |
| 1471 | }} |
| 1472 | |