test_usac.cpp
20.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
// 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 "test_precomp.hpp"
namespace opencv_test { namespace {
enum TestSolver { Homogr, Fundam, Essen, PnP, Affine};
/*
* rng -- reference to random generator
* pts1 -- 2xN image points
* pts2 -- for PnP is 3xN object points, otherwise 2xN image points.
* two_calib -- True if two cameras have different calibration.
* K1 -- intrinsic matrix of the first camera. For PnP only one camera.
* K2 -- only if two_calib is True.
* pts_size -- required size of points.
* inlier_ratio -- required inlier ratio
* noise_std -- standard deviation of Gaussian noise of image points.
* gt_inliers -- has size of number of inliers. Contains indices of inliers.
*/
static int generatePoints (cv::RNG &rng, cv::Mat &pts1, cv::Mat &pts2, cv::Mat &K1, cv::Mat &K2,
bool two_calib, int pts_size, TestSolver test_case, double inlier_ratio, double noise_std,
std::vector<int> >_inliers) {
auto eulerAnglesToRotationMatrix = [] (double pitch, double yaw, double roll) {
// Calculate rotation about x axis
cv::Matx33d R_x (1, 0, 0, 0, cos(roll), -sin(roll), 0, sin(roll), cos(roll));
// Calculate rotation about y axis
cv::Matx33d R_y (cos(pitch), 0, sin(pitch), 0, 1, 0, -sin(pitch), 0, cos(pitch));
// Calculate rotation about z axis
cv::Matx33d R_z (cos(yaw), -sin(yaw), 0, sin(yaw), cos(yaw), 0, 0, 0, 1);
return cv::Mat(R_z * R_y * R_x); // Combined rotation matrix
};
const double pitch_min = -CV_PI / 6, pitch_max = CV_PI / 6; // 30 degrees
const double yaw_min = -CV_PI / 6, yaw_max = CV_PI / 6;
const double roll_min = -CV_PI / 6, roll_max = CV_PI / 6;
cv::Mat R = eulerAnglesToRotationMatrix(rng.uniform(pitch_min, pitch_max),
rng.uniform(yaw_min, yaw_max), rng.uniform(roll_min, roll_max));
// generate random translation,
// if test for homography fails try to fix translation to zero vec so H is related by transl.
cv::Vec3d t (rng.uniform(-0.5f, 0.5f), rng.uniform(-0.5f, 0.5f), rng.uniform(1.0f, 2.0f));
// generate random calibration
auto getRandomCalib = [&] () {
return cv::Mat(cv::Matx33d(rng.uniform(100.0, 1000.0), 0, rng.uniform(100.0, 100.0),
0, rng.uniform(100.0, 1000.0), rng.uniform(-100.0, 100.0),
0, 0, 1.));
};
K1 = getRandomCalib();
K2 = two_calib ? getRandomCalib() : K1.clone();
auto updateTranslation = [] (const cv::Mat &pts, const cv::Mat &R_, cv::Vec3d &t_) {
// Make sure the shape is in front of the camera
cv::Mat points3d_transformed = R_ * pts + t_ * cv::Mat::ones(1, pts.cols, pts.type());
double min_dist, max_dist;
cv::minMaxIdx(points3d_transformed.row(2), &min_dist, &max_dist);
if (min_dist < 0) t_(2) -= min_dist + 1.0;
};
// compute size of inliers and outliers
const int inl_size = static_cast<int>(inlier_ratio * pts_size);
const int out_size = pts_size - inl_size;
// all points will have top 'inl_size' of their points inliers
gt_inliers.clear(); gt_inliers.reserve(inl_size);
for (int i = 0; i < inl_size; i++)
gt_inliers.emplace_back(i);
// double precision to multiply points by models
const int pts_type = CV_64F;
cv::Mat points3d;
if (test_case == TestSolver::Homogr) {
points3d.create(2, inl_size, pts_type);
rng.fill(points3d, cv::RNG::UNIFORM, 0.0, 1.0); // keep small range
// inliers must be planar points, let their 3D coordinate be 1
cv::vconcat(points3d, cv::Mat::ones(1, inl_size, points3d.type()), points3d);
} else if (test_case == TestSolver::Fundam || test_case == TestSolver::Essen) {
// create 3D points which are inliers
points3d.create(3, inl_size, pts_type);
rng.fill(points3d, cv::RNG::UNIFORM, 0.0, 1.0);
} else if (test_case == TestSolver::PnP) {
//pts1 are image points, pts2 are object points
pts2.create(3, inl_size, pts_type); // 3D inliers
rng.fill(pts2, cv::RNG::UNIFORM, 0, 1);
updateTranslation(pts2, R, t);
// project 3D points (pts2) on image plane (pts1)
pts1 = K1 * (R * pts2 + t * cv::Mat::ones(1, pts2.cols, pts2.type()));
cv::divide(pts1.row(0), pts1.row(2), pts1.row(0));
cv::divide(pts1.row(1), pts1.row(2), pts1.row(1));
// make 2D points
pts1 = pts1.rowRange(0, 2);
// create random outliers
cv::Mat pts_outliers = cv::Mat(5, out_size, pts2.type());
rng.fill(pts_outliers, cv::RNG::UNIFORM, 0, 1000);
// merge inliers with random image points = outliers
cv::hconcat(pts1, pts_outliers.rowRange(0, 2), pts1);
// merge 3D inliers with 3D outliers
cv::hconcat(pts2, pts_outliers.rowRange(2, 5), pts2);
// add Gaussian noise to image points
cv::Mat noise(pts1.rows, pts1.cols, pts1.type());
rng.fill(noise, cv::RNG::NORMAL, 0, noise_std);
pts1 += noise;
return inl_size;
} else if (test_case == TestSolver::Affine) {
} else
CV_Error(cv::Error::StsBadArg, "Unknown solver!");
if (test_case != TestSolver::PnP) {
// project 3D point on image plane
// use two relative scenes. The first camera is P1 = K1 [I | 0], the second P2 = K2 [R | t]
if (test_case != TestSolver::Affine) {
updateTranslation(points3d, R, t);
pts1 = K1 * points3d;
pts2 = K2 * (R * points3d + t * cv::Mat::ones(1, points3d.cols, points3d.type()));
// normalize by 3 coordinate
cv::divide(pts1.row(0), pts1.row(2), pts1.row(0));
cv::divide(pts1.row(1), pts1.row(2), pts1.row(1));
cv::divide(pts2.row(0), pts2.row(2), pts2.row(0));
cv::divide(pts2.row(1), pts2.row(2), pts2.row(1));
} else {
pts1 = cv::Mat(2, inl_size, pts_type);
rng.fill(pts1, cv::RNG::UNIFORM, 0, 1000);
cv::Matx33d sc(rng.uniform(1., 5.),0,0,rng.uniform(1., 4.),0,0, 0, 0, 1);
cv::Matx33d tr(1,0,rng.uniform(50., 500.),0,1,rng.uniform(50., 500.), 0, 0, 1);
const double phi = rng.uniform(0., CV_PI);
cv::Matx33d rot(cos(phi), -sin(phi),0, sin(phi), cos(phi),0, 0, 0, 1);
cv::Matx33d A = sc * tr * rot;
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), points3d);
pts2 = A * points3d;
}
// get 2D points
pts1 = pts1.rowRange(0,2); pts2 = pts2.rowRange(0,2);
// generate random outliers as 2D image points
cv::Mat pts1_outliers(pts1.rows, out_size, pts1.type()),
pts2_outliers(pts2.rows, out_size, pts2.type());
rng.fill(pts1_outliers, cv::RNG::UNIFORM, 0, 1000);
rng.fill(pts2_outliers, cv::RNG::UNIFORM, 0, 1000);
// merge inliers and outliers
cv::hconcat(pts1, pts1_outliers, pts1);
cv::hconcat(pts2, pts2_outliers, pts2);
// add normal / Gaussian noise to image points
cv::Mat noise1 (pts1.rows, pts1.cols, pts1.type()), noise2 (pts2.rows, pts2.cols, pts2.type());
rng.fill(noise1, cv::RNG::NORMAL, 0, noise_std); pts1 += noise1;
rng.fill(noise2, cv::RNG::NORMAL, 0, noise_std); pts2 += noise2;
}
return inl_size;
}
/*
* for test case = 0, 1, 2 (homography and epipolar geometry): pts1 and pts2 are 3xN
* for test_case = 3 (PnP): pts1 are 3xN and pts2 are 4xN
* all points are of the same type as model
*/
static double getError (TestSolver test_case, int pt_idx, const cv::Mat &pts1, const cv::Mat &pts2, const cv::Mat &model) {
cv::Mat pt1 = pts1.col(pt_idx), pt2 = pts2.col(pt_idx);
if (test_case == TestSolver::Homogr) { // reprojection error
// compute Euclidean distance between given and reprojected points
cv::Mat est_pt2 = model * pt1; est_pt2 /= est_pt2.at<double>(2);
if (false) {
cv::Mat est_pt1 = model.inv() * pt2; est_pt1 /= est_pt1.at<double>(2);
return (cv::norm(est_pt1 - pt1) + cv::norm(est_pt2 - pt2)) / 2;
}
return cv::norm(est_pt2 - pt2);
} else
if (test_case == TestSolver::Fundam || test_case == TestSolver::Essen) {
cv::Mat l2 = model * pt1;
cv::Mat l1 = model.t() * pt2;
if (test_case == TestSolver::Fundam) // sampson error
return fabs(pt2.dot(l2)) / sqrt(pow(l1.at<double>(0), 2) + pow(l1.at<double>(1), 2) +
pow(l2.at<double>(0), 2) + pow(l2.at<double>(1), 2));
else // symmetric geometric distance
return sqrt(pow(pt1.dot(l1),2) / (pow(l1.at<double>(0),2) + pow(l1.at<double>(1),2)) +
pow(pt2.dot(l2),2) / (pow(l2.at<double>(0),2) + pow(l2.at<double>(1),2)));
} else
if (test_case == TestSolver::PnP) { // PnP, reprojection error
cv::Mat img_pt = model * pt2; img_pt /= img_pt.at<double>(2);
return cv::norm(pt1 - img_pt);
} else
CV_Error(cv::Error::StsBadArg, "Undefined test case!");
}
/*
* inl_size -- number of ground truth inliers
* pts1 and pts2 are of the same size as from function generatePoints(...)
*/
static void checkInliersMask (TestSolver test_case, int inl_size, double thr, const cv::Mat &pts1_,
const cv::Mat &pts2_, const cv::Mat &model, const cv::Mat &mask) {
ASSERT_TRUE(!model.empty() && !mask.empty());
cv::Mat pts1 = pts1_, pts2 = pts2_;
if (pts1.type() != model.type()) {
pts1.convertTo(pts1, model.type());
pts2.convertTo(pts2, model.type());
}
// convert to homogeneous
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), pts1);
cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), pts2);
thr *= 1.001; // increase a little threshold due to numerical imprecisions
const auto * const mask_ptr = mask.ptr<uchar>();
int num_found_inliers = 0;
for (int i = 0; i < pts1.cols; i++)
if (mask_ptr[i]) {
ASSERT_LT(getError(test_case, i, pts1, pts2, model), thr);
num_found_inliers++;
}
// check if RANSAC found at least 80% of inliers
ASSERT_GT(num_found_inliers, 0.8 * inl_size);
}
TEST(usac_Homography, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 1500;
cv::RNG &rng = cv::theRNG();
// do not test USAC_PARALLEL, because it is not deterministic
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.1; inl_ratio < 0.91; inl_ratio += 0.1) {
cv::Mat pts1, pts2, K1, K2;
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Homogr, inl_ratio/*inl ratio*/, 0.1 /*noise std*/, gt_inliers);
// compute max_iters with standard upper bound rule for RANSAC with 1.5x tolerance
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - pow(inl_ratio, 4 /* sample size */));
for (auto flag : flags) {
cv::Mat mask, H = cv::findHomography(pts1, pts2,flag, thr, mask,
int(max_iters), conf);
checkInliersMask(TestSolver::Homogr, inl_size, thr, pts1, pts2, H, mask);
}
}
}
TEST(usac_Fundamental, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 2000;
cv::RNG &rng = cv::theRNG();
// start from 25% otherwise max_iters will be too big
const std::vector<int> flags = {USAC_DEFAULT, USAC_FM_8PTS, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
const double conf = 0.99, thr = 1.;
for (double inl_ratio = 0.25; inl_ratio < 0.91; inl_ratio += 0.1) {
cv::Mat pts1, pts2, K1, K2;
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Fundam, inl_ratio, 0.1 /*noise std*/, gt_inliers);
for (auto flag : flags) {
const int sample_size = flag == USAC_FM_8PTS ? 8 : 7;
const double max_iters = 1.25 * log(1 - conf) /
log(1 - pow(inl_ratio, sample_size));
cv::Mat mask, F = cv::findFundamentalMat(pts1, pts2,flag, thr, conf,
int(max_iters), mask);
checkInliersMask(TestSolver::Fundam, inl_size, thr, pts1, pts2, F, mask);
}
}
}
TEST(usac_Fundamental, regression_19639)
{
double x_[] = {
941, 890,
596, 940,
898, 941,
894, 933,
586, 938,
902, 933,
887, 935
};
Mat x(7, 1, CV_64FC2, x_);
double y_[] = {
1416, 806,
1157, 852,
1380, 855,
1378, 843,
1145, 849,
1378, 843,
1378, 843
};
Mat y(7, 1, CV_64FC2, y_);
//std::cout << x << std::endl;
//std::cout << y << std::endl;
Mat m = cv::findFundamentalMat(x, y, USAC_MAGSAC, 3, 0.99);
EXPECT_TRUE(m.empty());
}
TEST(usac_Essential, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 1500;
cv::RNG &rng = cv::theRNG();
// findEssentilaMat has by default number of maximum iterations equal to 1000.
// It means that with 99% confidence we assume at least 34.08% of inliers
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.35; inl_ratio < 0.91; inl_ratio += 0.1) {
cv::Mat pts1, pts2, K1, K2;
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Fundam, inl_ratio, 0.01 /*noise std, works bad with high noise*/, gt_inliers);
const double conf = 0.99, thr = 1.;
for (auto flag : flags) {
cv::Mat mask, E;
try {
E = cv::findEssentialMat(pts1, pts2, K1, flag, conf, thr, mask);
} catch (cv::Exception &e) {
if (e.code != cv::Error::StsNotImplemented)
FAIL() << "Essential matrix estimation failed!\n";
else continue;
}
// calibrate points
cv::Mat cpts1_3d, cpts2_3d;
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), cpts1_3d);
cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), cpts2_3d);
cpts1_3d = K1.inv() * cpts1_3d; cpts2_3d = K1.inv() * cpts2_3d;
checkInliersMask(TestSolver::Essen, inl_size, thr / ((K1.at<double>(0,0) + K1.at<double>(1,1)) / 2),
cpts1_3d.rowRange(0,2), cpts2_3d.rowRange(0,2), E, mask);
}
}
}
TEST(usac_P3P, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 3000;
cv::Mat img_pts, obj_pts, K1, K2;
cv::RNG &rng = cv::theRNG();
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.1; inl_ratio < 0.91; inl_ratio += 0.1) {
int inl_size = generatePoints(rng, img_pts, obj_pts, K1, K2, false /*two calib*/,
pts_size, TestSolver ::PnP, inl_ratio, 0.15 /*noise std*/, gt_inliers);
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - pow(inl_ratio, 3 /* sample size */));
for (auto flag : flags) {
std::vector<int> inliers;
cv::Mat rvec, tvec, mask, R, P;
CV_Assert(cv::solvePnPRansac(obj_pts, img_pts, K1, cv::noArray(), rvec, tvec,
false, (int)max_iters, (float)thr, conf, inliers, flag));
cv::Rodrigues(rvec, R);
cv::hconcat(K1 * R, K1 * tvec, P);
mask.create(pts_size, 1, CV_8U);
mask.setTo(Scalar::all(0));
for (auto inl : inliers)
mask.at<uchar>(inl) = true;
checkInliersMask(TestSolver ::PnP, inl_size, thr, img_pts, obj_pts, P, mask);
}
}
}
TEST (usac_Affine2D, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 2000;
cv::Mat pts1, pts2, K1, K2;
cv::RNG &rng = cv::theRNG();
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.1; inl_ratio < 0.91; inl_ratio += 0.1) {
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Affine, inl_ratio, 0.15 /*noise std*/, gt_inliers);
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - pow(inl_ratio, 3 /* sample size */));
for (auto flag : flags) {
cv::Mat mask, A = cv::estimateAffine2D(pts1, pts2, mask, flag, thr, (size_t)max_iters, conf, 0);
cv::vconcat(A, cv::Mat(cv::Matx13d(0,0,1)), A);
checkInliersMask(TestSolver::Homogr /*use homography error*/, inl_size, thr, pts1, pts2, A, mask);
}
}
}
TEST(usac_testUsacParams, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 1500;
cv::RNG &rng = cv::theRNG();
const cv::UsacParams usac_params = cv::UsacParams();
cv::Mat pts1, pts2, K1, K2, mask, model, rvec, tvec, R;
int inl_size;
auto getInlierRatio = [] (int max_iters, int sample_size, double conf) {
return std::pow(1 - exp(log(1 - conf)/(double)max_iters), 1 / (double)sample_size);
};
cv::Vec4d dist_coeff (0, 0, 0, 0); // test with 0 distortion
// Homography matrix
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::Homogr,
getInlierRatio(usac_params.maxIterations, 4, usac_params.confidence), 0.1, gt_inliers);
model = cv::findHomography(pts1, pts2, mask, usac_params);
checkInliersMask(TestSolver::Homogr, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// Fundamental matrix
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::Fundam,
getInlierRatio(usac_params.maxIterations, 7, usac_params.confidence), 0.1, gt_inliers);
model = cv::findFundamentalMat(pts1, pts2, mask, usac_params);
checkInliersMask(TestSolver::Fundam, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// Essential matrix
inl_size = generatePoints(rng, pts1, pts2, K1, K2, true, pts_size, TestSolver::Essen,
getInlierRatio(usac_params.maxIterations, 5, usac_params.confidence), 0.01, gt_inliers);
try {
model = cv::findEssentialMat(pts1, pts2, K1, K2, dist_coeff, dist_coeff, mask, usac_params);
cv::Mat cpts1_3d, cpts2_3d;
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), cpts1_3d);
cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), cpts2_3d);
cpts1_3d = K1.inv() * cpts1_3d; cpts2_3d = K2.inv() * cpts2_3d;
checkInliersMask(TestSolver::Essen, inl_size, usac_params.threshold /
((K1.at<double>(0,0) + K1.at<double>(1,1) + K2.at<double>(0,0) + K2.at<double>(1,1)) / 4),
cpts1_3d.rowRange(0,2), cpts2_3d.rowRange(0,2), model, mask);
} catch (cv::Exception &e) {
if (e.code != cv::Error::StsNotImplemented)
FAIL() << "Essential matrix estimation failed!\n";
// CV_Error(cv::Error::StsError, "Essential matrix estimation failed!");
}
std::vector<int> inliers(pts_size);
// P3P
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::PnP,
getInlierRatio(usac_params.maxIterations, 3, usac_params.confidence), 0.01, gt_inliers);
CV_Assert(cv::solvePnPRansac(pts2, pts1, K1, dist_coeff, rvec, tvec, inliers, usac_params));
cv::Rodrigues(rvec, R); cv::hconcat(K1 * R, K1 * tvec, model);
mask.create(pts_size, 1, CV_8U);
mask.setTo(Scalar::all(0));
for (auto inl : inliers)
mask.at<uchar>(inl) = true;
checkInliersMask(TestSolver::PnP, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// P6P
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::PnP,
getInlierRatio(usac_params.maxIterations, 6, usac_params.confidence), 0.1, gt_inliers);
cv::Mat K_est;
CV_Assert(cv::solvePnPRansac(pts2, pts1, K_est, dist_coeff, rvec, tvec, inliers, usac_params));
cv::Rodrigues(rvec, R); cv::hconcat(K_est * R, K_est * tvec, model);
mask.setTo(Scalar::all(0));
for (auto inl : inliers)
mask.at<uchar>(inl) = true;
checkInliersMask(TestSolver::PnP, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// Affine2D
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::Affine,
getInlierRatio(usac_params.maxIterations, 3, usac_params.confidence), 0.1, gt_inliers);
model = cv::estimateAffine2D(pts1, pts2, mask, usac_params);
cv::vconcat(model, cv::Mat(cv::Matx13d(0,0,1)), model);
checkInliersMask(TestSolver::Homogr, inl_size, usac_params.threshold, pts1, pts2, model, mask);
}
}} // namespace