test_tsdf.cpp 14 KB

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