test_normal.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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. // This code is also subject to the license terms in the LICENSE_WillowGarage.md file found in this module's directory
  5. #include "test_precomp.hpp"
  6. #include <opencv2/rgbd.hpp>
  7. #include <opencv2/calib3d.hpp>
  8. namespace opencv_test { namespace {
  9. #if 0
  10. Point3f
  11. rayPlaneIntersection(Point2f uv, const Mat& centroid, const Mat& normal, const Mat_<float>& Kinv)
  12. {
  13. Matx33d dKinv(Kinv);
  14. Vec3d dNormal(normal);
  15. return rayPlaneIntersection(Vec3d(uv.x, uv.y, 1), centroid.dot(normal), dNormal, dKinv);
  16. }
  17. #endif
  18. Vec3f
  19. rayPlaneIntersection(const Vec3d& uv1, double centroid_dot_normal, const Vec3d& normal, const Matx33d& Kinv)
  20. {
  21. Matx31d L = Kinv * uv1; //a ray passing through camera optical center
  22. //and uv.
  23. L = L * (1.0 / cv::norm(L));
  24. double LdotNormal = L.dot(normal);
  25. double d;
  26. if (std::fabs(LdotNormal) > 1e-9)
  27. {
  28. d = centroid_dot_normal / LdotNormal;
  29. }
  30. else
  31. {
  32. d = 1.0;
  33. std::cout << "warning, LdotNormal nearly 0! " << LdotNormal << std::endl;
  34. std::cout << "contents of L, Normal: " << Mat(L) << ", " << Mat(normal) << std::endl;
  35. }
  36. Vec3f xyz((float)(d * L(0)), (float)(d * L(1)), (float)(d * L(2)));
  37. return xyz;
  38. }
  39. const int W = 640;
  40. const int H = 480;
  41. //int window_size = 5;
  42. float focal_length = 525;
  43. float cx = W / 2.f + 0.5f;
  44. float cy = H / 2.f + 0.5f;
  45. Mat K = (Mat_<double>(3, 3) << focal_length, 0, cx, 0, focal_length, cy, 0, 0, 1);
  46. Mat Kinv = K.inv();
  47. void points3dToDepth16U(const Mat_<Vec3f>& points3d, Mat& depthMap);
  48. void points3dToDepth16U(const Mat_<Vec3f>& points3d, Mat& depthMap)
  49. {
  50. std::vector<Point3f> points3dvec;
  51. for(int i = 0; i < H; i++)
  52. for(int j = 0; j < W; j++)
  53. points3dvec.push_back(Point3f(points3d(i,j)[0], points3d(i,j)[1], points3d(i,j)[2]));
  54. std::vector<Point2f> img_points;
  55. depthMap = Mat::zeros(H, W, CV_32F);
  56. Vec3f R(0.0,0.0,0.0);
  57. Vec3f T(0.0,0.0,0.0);
  58. cv::projectPoints(points3dvec, R, T, K, Mat(), img_points);
  59. int index = 0;
  60. for(int i = 0; i < H; i++)
  61. {
  62. for(int j = 0; j < W; j++)
  63. {
  64. float value = (points3d.at<Vec3f>(i, j))[2]; // value is the z
  65. depthMap.at<float>(cvRound(img_points[index].y), cvRound(img_points[index].x)) = value;
  66. index++;
  67. }
  68. }
  69. depthMap.convertTo(depthMap, CV_16U, 1000);
  70. }
  71. static RNG rng;
  72. struct Plane
  73. {
  74. Vec3d n, p;
  75. double p_dot_n;
  76. Plane()
  77. {
  78. n[0] = rng.uniform(-0.5, 0.5);
  79. n[1] = rng.uniform(-0.5, 0.5);
  80. n[2] = -0.3; //rng.uniform(-1.f, 0.5f);
  81. n = n / cv::norm(n);
  82. set_d((float)rng.uniform(-2.0, 0.6));
  83. }
  84. void
  85. set_d(float d)
  86. {
  87. p = Vec3d(0, 0, d / n[2]);
  88. p_dot_n = p.dot(n);
  89. }
  90. Vec3f
  91. intersection(float u, float v, const Matx33f& Kinv_in) const
  92. {
  93. return rayPlaneIntersection(Vec3d(u, v, 1), p_dot_n, n, Kinv_in);
  94. }
  95. };
  96. void
  97. gen_points_3d(std::vector<Plane>& planes_out, Mat_<unsigned char> &plane_mask, Mat& points3d, Mat& normals,
  98. int n_planes)
  99. {
  100. std::vector<Plane> planes;
  101. for (int i = 0; i < n_planes; i++)
  102. {
  103. Plane px;
  104. for (int j = 0; j < 1; j++)
  105. {
  106. px.set_d(rng.uniform(-3.f, -0.5f));
  107. planes.push_back(px);
  108. }
  109. }
  110. Mat_ < Vec3f > outp(H, W);
  111. Mat_ < Vec3f > outn(H, W);
  112. plane_mask.create(H, W);
  113. // n ( r - r_0) = 0
  114. // n * r_0 = d
  115. //
  116. // r_0 = (0,0,0)
  117. // r[0]
  118. for (int v = 0; v < H; v++)
  119. {
  120. for (int u = 0; u < W; u++)
  121. {
  122. unsigned int plane_index = (unsigned int)((u / float(W)) * planes.size());
  123. Plane plane = planes[plane_index];
  124. outp(v, u) = plane.intersection((float)u, (float)v, Kinv);
  125. outn(v, u) = plane.n;
  126. plane_mask(v, u) = (uchar)plane_index;
  127. }
  128. }
  129. planes_out = planes;
  130. points3d = outp;
  131. normals = outn;
  132. }
  133. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  134. class CV_RgbdNormalsTest: public cvtest::BaseTest
  135. {
  136. public:
  137. CV_RgbdNormalsTest()
  138. {
  139. }
  140. ~CV_RgbdNormalsTest()
  141. {
  142. }
  143. protected:
  144. void
  145. run(int)
  146. {
  147. try
  148. {
  149. Mat_<unsigned char> plane_mask;
  150. for (unsigned char i = 0; i < 3; ++i)
  151. {
  152. RgbdNormals::RGBD_NORMALS_METHOD method;
  153. // inner vector: whether it's 1 plane or 3 planes
  154. // outer vector: float or double
  155. std::vector<std::vector<float> > errors(2);
  156. errors[0].resize(4);
  157. errors[1].resize(4);
  158. switch (i)
  159. {
  160. case 0:
  161. method = RgbdNormals::RGBD_NORMALS_METHOD_FALS;
  162. std::cout << std::endl << "*** FALS" << std::endl;
  163. errors[0][0] = 0.006f;
  164. errors[0][1] = 0.03f;
  165. errors[1][0] = 0.0001f;
  166. errors[1][1] = 0.02f;
  167. break;
  168. case 1:
  169. method = RgbdNormals::RGBD_NORMALS_METHOD_LINEMOD;
  170. std::cout << std::endl << "*** LINEMOD" << std::endl;
  171. errors[0][0] = 0.04f;
  172. errors[0][1] = 0.07f;
  173. errors[0][2] = 0.04f; // depth 16U 1 plane
  174. errors[0][3] = 0.07f; // depth 16U 3 planes
  175. errors[1][0] = 0.05f;
  176. errors[1][1] = 0.08f;
  177. errors[1][2] = 0.05f; // depth 16U 1 plane
  178. errors[1][3] = 0.08f; // depth 16U 3 planes
  179. break;
  180. case 2:
  181. method = RgbdNormals::RGBD_NORMALS_METHOD_SRI;
  182. std::cout << std::endl << "*** SRI" << std::endl;
  183. errors[0][0] = 0.02f;
  184. errors[0][1] = 0.04f;
  185. errors[1][0] = 0.02f;
  186. errors[1][1] = 0.04f;
  187. break;
  188. default:
  189. method = (RgbdNormals::RGBD_NORMALS_METHOD)-1;
  190. CV_Error(0, "");
  191. }
  192. for (unsigned char j = 0; j < 2; ++j)
  193. {
  194. int depth = (j % 2 == 0) ? CV_32F : CV_64F;
  195. if (depth == CV_32F)
  196. std::cout << "* float" << std::endl;
  197. else
  198. std::cout << "* double" << std::endl;
  199. RgbdNormals normals_computer(H, W, depth, K, 5, method);
  200. normals_computer.initialize();
  201. std::vector<Plane> plane_params;
  202. Mat points3d, ground_normals;
  203. // 1 plane, continuous scene, very low error..
  204. std::cout << "1 plane - input 3d points" << std::endl;
  205. float err_mean = 0;
  206. for (int ii = 0; ii < 5; ++ii)
  207. {
  208. gen_points_3d(plane_params, plane_mask, points3d, ground_normals, 1);
  209. err_mean += testit(points3d, ground_normals, normals_computer);
  210. }
  211. std::cout << "mean diff: " << (err_mean / 5) << std::endl;
  212. EXPECT_LE(err_mean/5, errors[j][0])<< " thresh: " << errors[j][0] << std::endl;
  213. // 3 discontinuities, more error expected.
  214. std::cout << "3 planes" << std::endl;
  215. err_mean = 0;
  216. for (int ii = 0; ii < 5; ++ii)
  217. {
  218. gen_points_3d(plane_params, plane_mask, points3d, ground_normals, 3);
  219. err_mean += testit(points3d, ground_normals, normals_computer);
  220. }
  221. std::cout << "mean diff: " << (err_mean / 5) << std::endl;
  222. EXPECT_LE(err_mean/5, errors[j][1])<< "mean diff: " << (err_mean/5) << " thresh: " << errors[j][1] << std::endl;
  223. if(method == RgbdNormals::RGBD_NORMALS_METHOD_LINEMOD)
  224. {
  225. // depth 16U test
  226. std::cout << "** depth 16U - 1 plane" << std::endl;
  227. err_mean = 0;
  228. for (int ii = 0; ii < 5; ++ii)
  229. {
  230. gen_points_3d(plane_params, plane_mask, points3d, ground_normals, 1);
  231. Mat depthMap;
  232. points3dToDepth16U(points3d, depthMap);
  233. err_mean += testit(depthMap, ground_normals, normals_computer);
  234. }
  235. std::cout << "mean diff: " << (err_mean / 5) << std::endl;
  236. EXPECT_LE(err_mean/5, errors[j][2])<< " thresh: " << errors[j][2] << std::endl;
  237. std::cout << "** depth 16U - 3 plane" << std::endl;
  238. err_mean = 0;
  239. for (int ii = 0; ii < 5; ++ii)
  240. {
  241. gen_points_3d(plane_params, plane_mask, points3d, ground_normals, 3);
  242. Mat depthMap;
  243. points3dToDepth16U(points3d, depthMap);
  244. err_mean += testit(depthMap, ground_normals, normals_computer);
  245. }
  246. std::cout << "mean diff: " << (err_mean / 5) << std::endl;
  247. EXPECT_LE(err_mean/5, errors[j][3])<< "mean diff: " << (err_mean/5) << " thresh: " << errors[j][3] << std::endl;
  248. }
  249. }
  250. }
  251. //TODO test NaNs in data
  252. } catch (...)
  253. {
  254. ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
  255. }
  256. ts->set_failed_test_info(cvtest::TS::OK);
  257. }
  258. float
  259. testit(const Mat & points3d, const Mat & in_ground_normals, const RgbdNormals & normals_computer)
  260. {
  261. TickMeter tm;
  262. tm.start();
  263. Mat in_normals;
  264. if (normals_computer.getMethod() == RgbdNormals::RGBD_NORMALS_METHOD_LINEMOD && points3d.channels() == 3)
  265. {
  266. std::vector<Mat> channels;
  267. split(points3d, channels);
  268. normals_computer(channels[2], in_normals);
  269. }
  270. else
  271. normals_computer(points3d, in_normals);
  272. tm.stop();
  273. Mat_<Vec3f> normals, ground_normals;
  274. in_normals.convertTo(normals, CV_32FC3);
  275. in_ground_normals.convertTo(ground_normals, CV_32FC3);
  276. float err = 0;
  277. for (int y = 0; y < normals.rows; ++y)
  278. for (int x = 0; x < normals.cols; ++x)
  279. {
  280. Vec3f vec1 = normals(y, x), vec2 = ground_normals(y, x);
  281. vec1 = vec1 / cv::norm(vec1);
  282. vec2 = vec2 / cv::norm(vec2);
  283. float dot = vec1.dot(vec2);
  284. // Just for rounding errors
  285. if (std::abs(dot) < 1)
  286. err += std::min(std::acos(dot), std::acos(-dot));
  287. }
  288. err /= normals.rows * normals.cols;
  289. std::cout << "Average error: " << err << " Speed: " << tm.getTimeMilli() << " ms" << std::endl;
  290. return err;
  291. }
  292. };
  293. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  294. class CV_RgbdPlaneTest: public cvtest::BaseTest
  295. {
  296. public:
  297. CV_RgbdPlaneTest()
  298. {
  299. }
  300. ~CV_RgbdPlaneTest()
  301. {
  302. }
  303. protected:
  304. void
  305. run(int)
  306. {
  307. try
  308. {
  309. RgbdPlane plane_computer;
  310. std::vector<Plane> planes;
  311. Mat points3d, ground_normals;
  312. Mat_<unsigned char> plane_mask;
  313. gen_points_3d(planes, plane_mask, points3d, ground_normals, 1);
  314. testit(planes, plane_mask, points3d, plane_computer); // 1 plane, continuous scene, very low error..
  315. for (int ii = 0; ii < 10; ii++)
  316. {
  317. gen_points_3d(planes, plane_mask, points3d, ground_normals, 3); //three planes
  318. testit(planes, plane_mask, points3d, plane_computer); // 3 discontinuities, more error expected.
  319. }
  320. } catch (...)
  321. {
  322. ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
  323. }
  324. ts->set_failed_test_info(cvtest::TS::OK);
  325. }
  326. void
  327. testit(const std::vector<Plane> & gt_planes, const Mat & gt_plane_mask, const Mat & points3d,
  328. RgbdPlane & plane_computer)
  329. {
  330. for (char i_test = 0; i_test < 2; ++i_test)
  331. {
  332. TickMeter tm1, tm2;
  333. Mat plane_mask;
  334. std::vector<Vec4f> plane_coefficients;
  335. if (i_test == 0)
  336. {
  337. tm1.start();
  338. // First, get the normals
  339. int depth = CV_32F;
  340. RgbdNormals normals_computer(H, W, depth, K, 5, RgbdNormals::RGBD_NORMALS_METHOD_FALS);
  341. Mat normals;
  342. normals_computer(points3d, normals);
  343. tm1.stop();
  344. tm2.start();
  345. plane_computer(points3d, normals, plane_mask, plane_coefficients);
  346. tm2.stop();
  347. }
  348. else
  349. {
  350. tm2.start();
  351. plane_computer(points3d, plane_mask, plane_coefficients);
  352. tm2.stop();
  353. }
  354. // Compare each found plane to each ground truth plane
  355. int n_planes = (int)plane_coefficients.size();
  356. int n_gt_planes = (int)gt_planes.size();
  357. Mat_<int> matching(n_gt_planes, n_planes);
  358. for (int j = 0; j < n_gt_planes; ++j)
  359. {
  360. Mat gt_mask = gt_plane_mask == j;
  361. int n_gt = countNonZero(gt_mask);
  362. int n_max = 0, i_max = 0;
  363. for (int i = 0; i < n_planes; ++i)
  364. {
  365. Mat dst;
  366. bitwise_and(gt_mask, plane_mask == i, dst);
  367. matching(j, i) = countNonZero(dst);
  368. if (matching(j, i) > n_max)
  369. {
  370. n_max = matching(j, i);
  371. i_max = i;
  372. }
  373. }
  374. // Get the best match
  375. ASSERT_LE(float(n_max - n_gt) / n_gt, 0.001);
  376. // Compare the normals
  377. Vec3d normal(plane_coefficients[i_max][0], plane_coefficients[i_max][1], plane_coefficients[i_max][2]);
  378. ASSERT_GE(std::abs(gt_planes[j].n.dot(normal)), 0.95);
  379. }
  380. std::cout << " Speed: ";
  381. if (i_test == 0)
  382. std::cout << "normals " << tm1.getTimeMilli() << " ms and ";
  383. std::cout << "plane " << tm2.getTimeMilli() << " ms " << std::endl;
  384. }
  385. }
  386. };
  387. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  388. TEST(Rgbd_Normals, compute)
  389. {
  390. CV_RgbdNormalsTest test;
  391. test.safe_run();
  392. }
  393. TEST(Rgbd_Plane, compute)
  394. {
  395. CV_RgbdPlaneTest test;
  396. test.safe_run();
  397. }
  398. TEST(Rgbd_Plane, regression_2309_valgrind_check)
  399. {
  400. Mat points(640, 480, CV_32FC3, Scalar::all(0));
  401. rgbd::RgbdPlane plane_detector;
  402. plane_detector.setBlockSize(9); // Note, 640%9 is 1 and 480%9 is 3
  403. Mat mask;
  404. std::vector<cv::Vec4f> planes;
  405. plane_detector(points, mask, planes); // Will corrupt memory; valgrind gets triggered
  406. }
  407. }} // namespace