test_tsdf.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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 {
  6. namespace {
  7. using namespace cv;
  8. /** Reprojects screen point to camera space given z coord. */
  9. struct Reprojector
  10. {
  11. Reprojector() {}
  12. inline Reprojector(Matx33f intr)
  13. {
  14. fxinv = 1.f / intr(0, 0), fyinv = 1.f / intr(1, 1);
  15. cx = intr(0, 2), cy = intr(1, 2);
  16. }
  17. template<typename T>
  18. inline cv::Point3_<T> operator()(cv::Point3_<T> p) const
  19. {
  20. T x = p.z * (p.x - cx) * fxinv;
  21. T y = p.z * (p.y - cy) * fyinv;
  22. return cv::Point3_<T>(x, y, p.z);
  23. }
  24. float fxinv, fyinv, cx, cy;
  25. };
  26. template<class Scene>
  27. struct RenderInvoker : ParallelLoopBody
  28. {
  29. RenderInvoker(Mat_<float>& _frame, Affine3f _pose,
  30. Reprojector _reproj, float _depthFactor, bool _onlySemisphere)
  31. : ParallelLoopBody(),
  32. frame(_frame),
  33. pose(_pose),
  34. reproj(_reproj),
  35. depthFactor(_depthFactor),
  36. onlySemisphere(_onlySemisphere)
  37. { }
  38. virtual void operator ()(const cv::Range& r) const
  39. {
  40. for (int y = r.start; y < r.end; y++)
  41. {
  42. float* frameRow = frame[y];
  43. for (int x = 0; x < frame.cols; x++)
  44. {
  45. float pix = 0;
  46. Point3f orig = pose.translation();
  47. // direction through pixel
  48. Point3f screenVec = reproj(Point3f((float)x, (float)y, 1.f));
  49. float xyt = 1.f / (screenVec.x * screenVec.x +
  50. screenVec.y * screenVec.y + 1.f);
  51. Point3f dir = normalize(Vec3f(pose.rotation() * screenVec));
  52. // screen space axis
  53. dir.y = -dir.y;
  54. const float maxDepth = 20.f;
  55. const float maxSteps = 256;
  56. float t = 0.f;
  57. for (int step = 0; step < maxSteps && t < maxDepth; step++)
  58. {
  59. Point3f p = orig + dir * t;
  60. float d = Scene::map(p, onlySemisphere);
  61. if (d < 0.000001f)
  62. {
  63. float depth = std::sqrt(t * t * xyt);
  64. pix = depth * depthFactor;
  65. break;
  66. }
  67. t += d;
  68. }
  69. frameRow[x] = pix;
  70. }
  71. }
  72. }
  73. Mat_<float>& frame;
  74. Affine3f pose;
  75. Reprojector reproj;
  76. float depthFactor;
  77. bool onlySemisphere;
  78. };
  79. struct Scene
  80. {
  81. virtual ~Scene() {}
  82. static Ptr<Scene> create(Size sz, Matx33f _intr, float _depthFactor, bool onlySemisphere);
  83. virtual Mat depth(Affine3f pose) = 0;
  84. virtual std::vector<Affine3f> getPoses() = 0;
  85. };
  86. struct SemisphereScene : Scene
  87. {
  88. const int framesPerCycle = 72;
  89. const float nCycles = 0.25f;
  90. const Affine3f startPose = Affine3f(Vec3f(0.f, 0.f, 0.f), Vec3f(1.5f, 0.3f, -2.1f));
  91. Size frameSize;
  92. Matx33f intr;
  93. float depthFactor;
  94. bool onlySemisphere;
  95. SemisphereScene(Size sz, Matx33f _intr, float _depthFactor, bool _onlySemisphere) :
  96. frameSize(sz), intr(_intr), depthFactor(_depthFactor), onlySemisphere(_onlySemisphere)
  97. { }
  98. static float map(Point3f p, bool onlySemisphere)
  99. {
  100. float plane = p.y + 0.5f;
  101. Point3f spherePose = p - Point3f(-0.0f, 0.3f, 1.1f);
  102. float sphereRadius = 0.5f;
  103. float sphere = (float)cv::norm(spherePose) - sphereRadius;
  104. float sphereMinusBox = sphere;
  105. float subSphereRadius = 0.05f;
  106. Point3f subSpherePose = p - Point3f(0.3f, -0.1f, -0.3f);
  107. float subSphere = (float)cv::norm(subSpherePose) - subSphereRadius;
  108. float res;
  109. if (!onlySemisphere)
  110. res = min({ sphereMinusBox, subSphere, plane });
  111. else
  112. res = sphereMinusBox;
  113. return res;
  114. }
  115. Mat depth(Affine3f pose) override
  116. {
  117. Mat_<float> frame(frameSize);
  118. Reprojector reproj(intr);
  119. Range range(0, frame.rows);
  120. parallel_for_(range, RenderInvoker<SemisphereScene>(frame, pose, reproj, depthFactor, onlySemisphere));
  121. return std::move(frame);
  122. }
  123. std::vector<Affine3f> getPoses() override
  124. {
  125. std::vector<Affine3f> poses;
  126. for (int i = 0; i < framesPerCycle * nCycles; i++)
  127. {
  128. float angle = (float)(CV_2PI * i / framesPerCycle);
  129. Affine3f pose;
  130. pose = pose.rotate(startPose.rotation());
  131. pose = pose.rotate(Vec3f(0.f, -0.5f, 0.f) * angle);
  132. pose = pose.translate(Vec3f(startPose.translation()[0] * sin(angle),
  133. startPose.translation()[1],
  134. startPose.translation()[2] * cos(angle)));
  135. poses.push_back(pose);
  136. }
  137. return poses;
  138. }
  139. };
  140. Ptr<Scene> Scene::create(Size sz, Matx33f _intr, float _depthFactor, bool _onlySemisphere)
  141. {
  142. return makePtr<SemisphereScene>(sz, _intr, _depthFactor, _onlySemisphere);
  143. }
  144. // this is a temporary solution
  145. // ----------------------------
  146. typedef cv::Vec4f ptype;
  147. typedef cv::Mat_< ptype > Points;
  148. typedef Points Normals;
  149. typedef Size2i Size;
  150. template<int p>
  151. inline float specPow(float x)
  152. {
  153. if (p % 2 == 0)
  154. {
  155. float v = specPow<p / 2>(x);
  156. return v * v;
  157. }
  158. else
  159. {
  160. float v = specPow<(p - 1) / 2>(x);
  161. return v * v * x;
  162. }
  163. }
  164. template<>
  165. inline float specPow<0>(float /*x*/)
  166. {
  167. return 1.f;
  168. }
  169. template<>
  170. inline float specPow<1>(float x)
  171. {
  172. return x;
  173. }
  174. inline cv::Vec3f fromPtype(const ptype& x)
  175. {
  176. return cv::Vec3f(x[0], x[1], x[2]);
  177. }
  178. inline Point3f normalize(const Vec3f& v)
  179. {
  180. double nv = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  181. return v * (nv ? 1. / nv : 0.);
  182. }
  183. void renderPointsNormals(InputArray _points, InputArray _normals, OutputArray image, Affine3f lightPose)
  184. {
  185. Size sz = _points.size();
  186. image.create(sz, CV_8UC4);
  187. Points points = _points.getMat();
  188. Normals normals = _normals.getMat();
  189. Mat_<Vec4b> img = image.getMat();
  190. Range range(0, sz.height);
  191. const int nstripes = -1;
  192. parallel_for_(range, [&](const Range&)
  193. {
  194. for (int y = range.start; y < range.end; y++)
  195. {
  196. Vec4b* imgRow = img[y];
  197. const ptype* ptsRow = points[y];
  198. const ptype* nrmRow = normals[y];
  199. for (int x = 0; x < sz.width; x++)
  200. {
  201. Point3f p = fromPtype(ptsRow[x]);
  202. Point3f n = fromPtype(nrmRow[x]);
  203. Vec4b color;
  204. if (cvIsNaN(p.x) || cvIsNaN(p.y) || cvIsNaN(p.z))
  205. {
  206. color = Vec4b(0, 32, 0, 0);
  207. }
  208. else
  209. {
  210. const float Ka = 0.3f; //ambient coeff
  211. const float Kd = 0.5f; //diffuse coeff
  212. const float Ks = 0.2f; //specular coeff
  213. const int sp = 20; //specular power
  214. const float Ax = 1.f; //ambient color, can be RGB
  215. const float Dx = 1.f; //diffuse color, can be RGB
  216. const float Sx = 1.f; //specular color, can be RGB
  217. const float Lx = 1.f; //light color
  218. Point3f l = normalize(lightPose.translation() - Vec3f(p));
  219. Point3f v = normalize(-Vec3f(p));
  220. Point3f r = normalize(Vec3f(2.f * n * n.dot(l) - l));
  221. uchar ix = (uchar)((Ax * Ka * Dx + Lx * Kd * Dx * max(0.f, n.dot(l)) +
  222. Lx * Ks * Sx * specPow<sp>(max(0.f, r.dot(v)))) * 255.f);
  223. color = Vec4b(ix, ix, ix, 0);
  224. }
  225. imgRow[x] = color;
  226. }
  227. }
  228. }, nstripes);
  229. }
  230. // ----------------------------
  231. static const bool display = false;
  232. static const bool parallelCheck = false;
  233. class Settings
  234. {
  235. public:
  236. Ptr<kinfu::Params> params;
  237. Ptr<kinfu::Volume> volume;
  238. Ptr<Scene> scene;
  239. std::vector<Affine3f> poses;
  240. Settings(bool useHashTSDF, bool onlySemisphere)
  241. {
  242. if (useHashTSDF)
  243. params = kinfu::Params::hashTSDFParams(true);
  244. else
  245. params = kinfu::Params::coarseParams();
  246. volume = kinfu::makeVolume(params->volumeType, params->voxelSize, params->volumePose.matrix,
  247. params->raycast_step_factor, params->tsdf_trunc_dist, params->tsdf_max_weight,
  248. params->truncateThreshold, params->volumeDims);
  249. scene = Scene::create(params->frameSize, params->intr, params->depthFactor, onlySemisphere);
  250. poses = scene->getPoses();
  251. }
  252. };
  253. void displayImage(Mat depth, Mat points, Mat normals, float depthFactor, Vec3f lightPose)
  254. {
  255. Mat image;
  256. patchNaNs(points);
  257. imshow("depth", depth * (1.f / depthFactor / 4.f));
  258. renderPointsNormals(points, normals, image, lightPose);
  259. imshow("render", image);
  260. waitKey(2000);
  261. }
  262. void normalsCheck(Mat normals)
  263. {
  264. Vec4f vector;
  265. for (auto pvector = normals.begin<Vec4f>(); pvector < normals.end<Vec4f>(); pvector++)
  266. {
  267. vector = *pvector;
  268. if (!cvIsNaN(vector[0]))
  269. {
  270. float length = vector[0] * vector[0] +
  271. vector[1] * vector[1] +
  272. vector[2] * vector[2];
  273. ASSERT_LT(abs(1 - length), 0.0001f) << "There is normal with length != 1";
  274. }
  275. }
  276. }
  277. int counterOfValid(Mat points)
  278. {
  279. Vec4f* v;
  280. int i, j;
  281. int count = 0;
  282. for (i = 0; i < points.rows; ++i)
  283. {
  284. v = (points.ptr<Vec4f>(i));
  285. for (j = 0; j < points.cols; ++j)
  286. {
  287. if ((v[j])[0] != 0 ||
  288. (v[j])[1] != 0 ||
  289. (v[j])[2] != 0)
  290. {
  291. count++;
  292. }
  293. }
  294. }
  295. return count;
  296. }
  297. void normal_test(bool isHashTSDF, bool isRaycast, bool isFetchPointsNormals, bool isFetchNormals)
  298. {
  299. auto normalCheck = [](Vec4f& vector, const int*)
  300. {
  301. if (!cvIsNaN(vector[0]))
  302. {
  303. float length = vector[0] * vector[0] +
  304. vector[1] * vector[1] +
  305. vector[2] * vector[2];
  306. ASSERT_LT(abs(1 - length), 0.0001f) << "There is normal with length != 1";
  307. }
  308. };
  309. Settings settings(isHashTSDF, false);
  310. Mat depth = settings.scene->depth(settings.poses[0]);
  311. UMat _points, _normals, _tmpnormals;
  312. UMat _newPoints, _newNormals;
  313. Mat points, normals;
  314. AccessFlag af = ACCESS_READ;
  315. settings.volume->integrate(depth, settings.params->depthFactor, settings.poses[0].matrix, settings.params->intr);
  316. if (isRaycast)
  317. {
  318. settings.volume->raycast(settings.poses[0].matrix, settings.params->intr, settings.params->frameSize, _points, _normals);
  319. }
  320. if (isFetchPointsNormals)
  321. {
  322. settings.volume->fetchPointsNormals(_points, _normals);
  323. }
  324. if (isFetchNormals)
  325. {
  326. settings.volume->fetchPointsNormals(_points, _tmpnormals);
  327. settings.volume->fetchNormals(_points, _normals);
  328. }
  329. normals = _normals.getMat(af);
  330. points = _points.getMat(af);
  331. if (parallelCheck)
  332. normals.forEach<Vec4f>(normalCheck);
  333. else
  334. normalsCheck(normals);
  335. if (isRaycast && display)
  336. displayImage(depth, points, normals, settings.params->depthFactor, settings.params->lightPose);
  337. if (isRaycast)
  338. {
  339. settings.volume->raycast(settings.poses[17].matrix, settings.params->intr, settings.params->frameSize, _newPoints, _newNormals);
  340. normals = _newNormals.getMat(af);
  341. points = _newPoints.getMat(af);
  342. normalsCheck(normals);
  343. if (parallelCheck)
  344. normals.forEach<Vec4f>(normalCheck);
  345. else
  346. normalsCheck(normals);
  347. if (display)
  348. displayImage(depth, points, normals, settings.params->depthFactor, settings.params->lightPose);
  349. }
  350. points.release(); normals.release();
  351. }
  352. void valid_points_test(bool isHashTSDF)
  353. {
  354. Settings settings(isHashTSDF, true);
  355. Mat depth = settings.scene->depth(settings.poses[0]);
  356. UMat _points, _normals, _newPoints, _newNormals;
  357. AccessFlag af = ACCESS_READ;
  358. Mat points, normals;
  359. int anfas, profile;
  360. settings.volume->integrate(depth, settings.params->depthFactor, settings.poses[0].matrix, settings.params->intr);
  361. settings.volume->raycast(settings.poses[0].matrix, settings.params->intr, settings.params->frameSize, _points, _normals);
  362. normals = _normals.getMat(af);
  363. points = _points.getMat(af);
  364. patchNaNs(points);
  365. anfas = counterOfValid(points);
  366. if (display)
  367. displayImage(depth, points, normals, settings.params->depthFactor, settings.params->lightPose);
  368. settings.volume->raycast(settings.poses[17].matrix, settings.params->intr, settings.params->frameSize, _newPoints, _newNormals);
  369. normals = _newNormals.getMat(af);
  370. points = _newPoints.getMat(af);
  371. patchNaNs(points);
  372. profile = counterOfValid(points);
  373. if (display)
  374. displayImage(depth, points, normals, settings.params->depthFactor, settings.params->lightPose);
  375. // TODO: why profile == 2*anfas ?
  376. float percentValidity = float(anfas) / float(profile);
  377. ASSERT_NE(profile, 0) << "There is no points in profile";
  378. ASSERT_NE(anfas, 0) << "There is no points in anfas";
  379. ASSERT_LT(abs(0.5 - percentValidity), 0.3) << "percentValidity out of [0.3; 0.7] (percentValidity=" << percentValidity << ")";
  380. }
  381. #ifndef HAVE_OPENCL
  382. TEST(TSDF, raycast_normals) { normal_test(false, true, false, false); }
  383. TEST(TSDF, fetch_points_normals) { normal_test(false, false, true, false); }
  384. TEST(TSDF, fetch_normals) { normal_test(false, false, false, true); }
  385. TEST(TSDF, valid_points) { valid_points_test(false); }
  386. TEST(HashTSDF, raycast_normals) { normal_test(true, true, false, false); }
  387. TEST(HashTSDF, fetch_points_normals) { normal_test(true, false, true, false); }
  388. TEST(HashTSDF, fetch_normals) { normal_test(true, false, false, true); }
  389. TEST(HashTSDF, valid_points) { valid_points_test(true); }
  390. #else
  391. TEST(TSDF_CPU, raycast_normals)
  392. {
  393. cv::ocl::setUseOpenCL(false);
  394. normal_test(false, true, false, false);
  395. cv::ocl::setUseOpenCL(true);
  396. }
  397. TEST(TSDF_CPU, fetch_points_normals)
  398. {
  399. cv::ocl::setUseOpenCL(false);
  400. normal_test(false, false, true, false);
  401. cv::ocl::setUseOpenCL(true);
  402. }
  403. TEST(TSDF_CPU, fetch_normals)
  404. {
  405. cv::ocl::setUseOpenCL(false);
  406. normal_test(false, false, false, true);
  407. cv::ocl::setUseOpenCL(true);
  408. }
  409. TEST(TSDF_CPU, valid_points)
  410. {
  411. cv::ocl::setUseOpenCL(false);
  412. valid_points_test(false);
  413. cv::ocl::setUseOpenCL(true);
  414. }
  415. TEST(HashTSDF_CPU, raycast_normals)
  416. {
  417. cv::ocl::setUseOpenCL(false);
  418. normal_test(true, true, false, false);
  419. cv::ocl::setUseOpenCL(true);
  420. }
  421. TEST(HashTSDF_CPU, fetch_points_normals)
  422. {
  423. cv::ocl::setUseOpenCL(false);
  424. normal_test(true, false, true, false);
  425. cv::ocl::setUseOpenCL(true);
  426. }
  427. TEST(HashTSDF_CPU, fetch_normals)
  428. {
  429. cv::ocl::setUseOpenCL(false);
  430. normal_test(true, false, false, true);
  431. cv::ocl::setUseOpenCL(true);
  432. }
  433. TEST(HashTSDF_CPU, valid_points)
  434. {
  435. cv::ocl::setUseOpenCL(false);
  436. valid_points_test(true);
  437. cv::ocl::setUseOpenCL(true);
  438. }
  439. #endif
  440. }
  441. } // namespace