test_usac.cpp 23 KB

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