KannalaBrandt8.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /**
  2. * This file is part of ORB-SLAM3
  3. *
  4. * Copyright (C) 2017-2021 Carlos Campos, Richard Elvira, Juan J. Gómez Rodríguez, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
  5. * Copyright (C) 2014-2016 Raúl Mur-Artal, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
  6. *
  7. * ORB-SLAM3 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
  8. * License as published by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * ORB-SLAM3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
  12. * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with ORB-SLAM3.
  16. * If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "KannalaBrandt8.h"
  19. #include <boost/serialization/export.hpp>
  20. //BOOST_CLASS_EXPORT_IMPLEMENT(ORB_SLAM3::KannalaBrandt8)
  21. namespace ORB_SLAM3 {
  22. //BOOST_CLASS_EXPORT_GUID(KannalaBrandt8, "KannalaBrandt8")
  23. cv::Point2f KannalaBrandt8::project(const cv::Point3f &p3D) {
  24. const float x2_plus_y2 = p3D.x * p3D.x + p3D.y * p3D.y;
  25. const float theta = atan2f(sqrtf(x2_plus_y2), p3D.z);
  26. const float psi = atan2f(p3D.y, p3D.x);
  27. const float theta2 = theta * theta;
  28. const float theta3 = theta * theta2;
  29. const float theta5 = theta3 * theta2;
  30. const float theta7 = theta5 * theta2;
  31. const float theta9 = theta7 * theta2;
  32. const float r = theta + mvParameters[4] * theta3 + mvParameters[5] * theta5
  33. + mvParameters[6] * theta7 + mvParameters[7] * theta9;
  34. return cv::Point2f(mvParameters[0] * r * cos(psi) + mvParameters[2],
  35. mvParameters[1] * r * sin(psi) + mvParameters[3]);
  36. }
  37. Eigen::Vector2d KannalaBrandt8::project(const Eigen::Vector3d &v3D) {
  38. const double x2_plus_y2 = v3D[0] * v3D[0] + v3D[1] * v3D[1];
  39. const double theta = atan2f(sqrtf(x2_plus_y2), v3D[2]);
  40. const double psi = atan2f(v3D[1], v3D[0]);
  41. const double theta2 = theta * theta;
  42. const double theta3 = theta * theta2;
  43. const double theta5 = theta3 * theta2;
  44. const double theta7 = theta5 * theta2;
  45. const double theta9 = theta7 * theta2;
  46. const double r = theta + mvParameters[4] * theta3 + mvParameters[5] * theta5
  47. + mvParameters[6] * theta7 + mvParameters[7] * theta9;
  48. Eigen::Vector2d res;
  49. res[0] = mvParameters[0] * r * cos(psi) + mvParameters[2];
  50. res[1] = mvParameters[1] * r * sin(psi) + mvParameters[3];
  51. return res;
  52. }
  53. Eigen::Vector2f KannalaBrandt8::project(const Eigen::Vector3f &v3D) {
  54. const float x2_plus_y2 = v3D[0] * v3D[0] + v3D[1] * v3D[1];
  55. const float theta = atan2f(sqrtf(x2_plus_y2), v3D[2]);
  56. const float psi = atan2f(v3D[1], v3D[0]);
  57. const float theta2 = theta * theta;
  58. const float theta3 = theta * theta2;
  59. const float theta5 = theta3 * theta2;
  60. const float theta7 = theta5 * theta2;
  61. const float theta9 = theta7 * theta2;
  62. const float r = theta + mvParameters[4] * theta3 + mvParameters[5] * theta5
  63. + mvParameters[6] * theta7 + mvParameters[7] * theta9;
  64. Eigen::Vector2f res;
  65. res[0] = mvParameters[0] * r * cos(psi) + mvParameters[2];
  66. res[1] = mvParameters[1] * r * sin(psi) + mvParameters[3];
  67. return res;
  68. /*cv::Point2f cvres = this->project(cv::Point3f(v3D[0],v3D[1],v3D[2]));
  69. Eigen::Vector2d res;
  70. res[0] = cvres.x;
  71. res[1] = cvres.y;
  72. return res;*/
  73. }
  74. Eigen::Vector2f KannalaBrandt8::projectMat(const cv::Point3f &p3D) {
  75. cv::Point2f point = this->project(p3D);
  76. return Eigen::Vector2f(point.x, point.y);
  77. }
  78. float KannalaBrandt8::uncertainty2(const Eigen::Matrix<double,2,1> &p2D)
  79. {
  80. /*Eigen::Matrix<double,2,1> c;
  81. c << mvParameters[2], mvParameters[3];
  82. if ((p2D-c).squaredNorm()>57600) // 240*240 (256)
  83. return 100.f;
  84. else
  85. return 1.0f;*/
  86. return 1.f;
  87. }
  88. Eigen::Vector3f KannalaBrandt8::unprojectEig(const cv::Point2f &p2D) {
  89. cv::Point3f ray = this->unproject(p2D);
  90. return Eigen::Vector3f(ray.x, ray.y, ray.z);
  91. }
  92. cv::Point3f KannalaBrandt8::unproject(const cv::Point2f &p2D) {
  93. //Use Newthon method to solve for theta with good precision (err ~ e-6)
  94. cv::Point2f pw((p2D.x - mvParameters[2]) / mvParameters[0], (p2D.y - mvParameters[3]) / mvParameters[1]);
  95. float scale = 1.f;
  96. float theta_d = sqrtf(pw.x * pw.x + pw.y * pw.y);
  97. theta_d = fminf(fmaxf(-CV_PI / 2.f, theta_d), CV_PI / 2.f);
  98. if (theta_d > 1e-8) {
  99. //Compensate distortion iteratively
  100. float theta = theta_d;
  101. for (int j = 0; j < 10; j++) {
  102. float theta2 = theta * theta, theta4 = theta2 * theta2, theta6 = theta4 * theta2, theta8 =
  103. theta4 * theta4;
  104. float k0_theta2 = mvParameters[4] * theta2, k1_theta4 = mvParameters[5] * theta4;
  105. float k2_theta6 = mvParameters[6] * theta6, k3_theta8 = mvParameters[7] * theta8;
  106. float theta_fix = (theta * (1 + k0_theta2 + k1_theta4 + k2_theta6 + k3_theta8) - theta_d) /
  107. (1 + 3 * k0_theta2 + 5 * k1_theta4 + 7 * k2_theta6 + 9 * k3_theta8);
  108. theta = theta - theta_fix;
  109. if (fabsf(theta_fix) < precision)
  110. break;
  111. }
  112. //scale = theta - theta_d;
  113. scale = std::tan(theta) / theta_d;
  114. }
  115. return cv::Point3f(pw.x * scale, pw.y * scale, 1.f);
  116. }
  117. Eigen::Matrix<double, 2, 3> KannalaBrandt8::projectJac(const Eigen::Vector3d &v3D) {
  118. double x2 = v3D[0] * v3D[0], y2 = v3D[1] * v3D[1], z2 = v3D[2] * v3D[2];
  119. double r2 = x2 + y2;
  120. double r = sqrt(r2);
  121. double r3 = r2 * r;
  122. double theta = atan2(r, v3D[2]);
  123. double theta2 = theta * theta, theta3 = theta2 * theta;
  124. double theta4 = theta2 * theta2, theta5 = theta4 * theta;
  125. double theta6 = theta2 * theta4, theta7 = theta6 * theta;
  126. double theta8 = theta4 * theta4, theta9 = theta8 * theta;
  127. double f = theta + theta3 * mvParameters[4] + theta5 * mvParameters[5] + theta7 * mvParameters[6] +
  128. theta9 * mvParameters[7];
  129. double fd = 1 + 3 * mvParameters[4] * theta2 + 5 * mvParameters[5] * theta4 + 7 * mvParameters[6] * theta6 +
  130. 9 * mvParameters[7] * theta8;
  131. Eigen::Matrix<double, 2, 3> JacGood;
  132. JacGood(0, 0) = mvParameters[0] * (fd * v3D[2] * x2 / (r2 * (r2 + z2)) + f * y2 / r3);
  133. JacGood(1, 0) =
  134. mvParameters[1] * (fd * v3D[2] * v3D[1] * v3D[0] / (r2 * (r2 + z2)) - f * v3D[1] * v3D[0] / r3);
  135. JacGood(0, 1) =
  136. mvParameters[0] * (fd * v3D[2] * v3D[1] * v3D[0] / (r2 * (r2 + z2)) - f * v3D[1] * v3D[0] / r3);
  137. JacGood(1, 1) = mvParameters[1] * (fd * v3D[2] * y2 / (r2 * (r2 + z2)) + f * x2 / r3);
  138. JacGood(0, 2) = -mvParameters[0] * fd * v3D[0] / (r2 + z2);
  139. JacGood(1, 2) = -mvParameters[1] * fd * v3D[1] / (r2 + z2);
  140. return JacGood;
  141. }
  142. bool KannalaBrandt8::ReconstructWithTwoViews(const std::vector<cv::KeyPoint>& vKeys1, const std::vector<cv::KeyPoint>& vKeys2, const std::vector<int> &vMatches12,
  143. Sophus::SE3f &T21, std::vector<cv::Point3f> &vP3D, std::vector<bool> &vbTriangulated){
  144. if(!tvr){
  145. Eigen::Matrix3f K = this->toK_();
  146. tvr = new TwoViewReconstruction(K);
  147. }
  148. //Correct FishEye distortion
  149. std::vector<cv::KeyPoint> vKeysUn1 = vKeys1, vKeysUn2 = vKeys2;
  150. std::vector<cv::Point2f> vPts1(vKeys1.size()), vPts2(vKeys2.size());
  151. for(size_t i = 0; i < vKeys1.size(); i++) vPts1[i] = vKeys1[i].pt;
  152. for(size_t i = 0; i < vKeys2.size(); i++) vPts2[i] = vKeys2[i].pt;
  153. cv::Mat D = (cv::Mat_<float>(4,1) << mvParameters[4], mvParameters[5], mvParameters[6], mvParameters[7]);
  154. cv::Mat R = cv::Mat::eye(3,3,CV_32F);
  155. cv::Mat K = this->toK();
  156. cv::fisheye::undistortPoints(vPts1,vPts1,K,D,R,K);
  157. cv::fisheye::undistortPoints(vPts2,vPts2,K,D,R,K);
  158. for(size_t i = 0; i < vKeys1.size(); i++) vKeysUn1[i].pt = vPts1[i];
  159. for(size_t i = 0; i < vKeys2.size(); i++) vKeysUn2[i].pt = vPts2[i];
  160. return tvr->Reconstruct(vKeysUn1,vKeysUn2,vMatches12,T21,vP3D,vbTriangulated);
  161. }
  162. cv::Mat KannalaBrandt8::toK() {
  163. cv::Mat K = (cv::Mat_<float>(3, 3)
  164. << mvParameters[0], 0.f, mvParameters[2], 0.f, mvParameters[1], mvParameters[3], 0.f, 0.f, 1.f);
  165. return K;
  166. }
  167. Eigen::Matrix3f KannalaBrandt8::toK_() {
  168. Eigen::Matrix3f K;
  169. K << mvParameters[0], 0.f, mvParameters[2], 0.f, mvParameters[1], mvParameters[3], 0.f, 0.f, 1.f;
  170. return K;
  171. }
  172. bool KannalaBrandt8::epipolarConstrain(GeometricCamera* pCamera2, const cv::KeyPoint &kp1, const cv::KeyPoint &kp2,
  173. const Eigen::Matrix3f& R12, const Eigen::Vector3f& t12, const float sigmaLevel, const float unc) {
  174. Eigen::Vector3f p3D;
  175. return this->TriangulateMatches(pCamera2,kp1,kp2,R12,t12,sigmaLevel,unc,p3D) > 0.0001f;
  176. }
  177. bool KannalaBrandt8::matchAndtriangulate(const cv::KeyPoint& kp1, const cv::KeyPoint& kp2, GeometricCamera* pOther,
  178. Sophus::SE3f& Tcw1, Sophus::SE3f& Tcw2,
  179. const float sigmaLevel1, const float sigmaLevel2,
  180. Eigen::Vector3f& x3Dtriangulated){
  181. Eigen::Matrix<float,3,4> eigTcw1 = Tcw1.matrix3x4();
  182. Eigen::Matrix3f Rcw1 = eigTcw1.block<3,3>(0,0);
  183. Eigen::Matrix3f Rwc1 = Rcw1.transpose();
  184. Eigen::Matrix<float,3,4> eigTcw2 = Tcw2.matrix3x4();
  185. Eigen::Matrix3f Rcw2 = eigTcw2.block<3,3>(0,0);
  186. Eigen::Matrix3f Rwc2 = Rcw2.transpose();
  187. cv::Point3f ray1c = this->unproject(kp1.pt);
  188. cv::Point3f ray2c = pOther->unproject(kp2.pt);
  189. Eigen::Vector3f r1(ray1c.x, ray1c.y, ray1c.z);
  190. Eigen::Vector3f r2(ray2c.x, ray2c.y, ray2c.z);
  191. //Check parallax between rays
  192. Eigen::Vector3f ray1 = Rwc1 * r1;
  193. Eigen::Vector3f ray2 = Rwc2 * r2;
  194. const float cosParallaxRays = ray1.dot(ray2)/(ray1.norm() * ray2.norm());
  195. //If parallax is lower than 0.9998, reject this match
  196. if(cosParallaxRays > 0.9998){
  197. return false;
  198. }
  199. //Parallax is good, so we try to triangulate
  200. cv::Point2f p11,p22;
  201. p11.x = ray1c.x;
  202. p11.y = ray1c.y;
  203. p22.x = ray2c.x;
  204. p22.y = ray2c.y;
  205. Eigen::Vector3f x3D;
  206. Triangulate(p11,p22,eigTcw1,eigTcw2,x3D);
  207. //Check triangulation in front of cameras
  208. float z1 = Rcw1.row(2).dot(x3D)+Tcw1.translation()(2);
  209. if(z1<=0){ //Point is not in front of the first camera
  210. return false;
  211. }
  212. float z2 = Rcw2.row(2).dot(x3D)+Tcw2.translation()(2);
  213. if(z2<=0){ //Point is not in front of the first camera
  214. return false;
  215. }
  216. //Check reprojection error in first keyframe
  217. // -Transform point into camera reference system
  218. Eigen::Vector3f x3D1 = Rcw1 * x3D + Tcw1.translation();
  219. Eigen::Vector2f uv1 = this->project(x3D1);
  220. float errX1 = uv1(0) - kp1.pt.x;
  221. float errY1 = uv1(1) - kp1.pt.y;
  222. if((errX1*errX1+errY1*errY1)>5.991*sigmaLevel1){ //Reprojection error is high
  223. return false;
  224. }
  225. //Check reprojection error in second keyframe;
  226. // -Transform point into camera reference system
  227. Eigen::Vector3f x3D2 = Rcw2 * x3D + Tcw2.translation(); // avoid using q
  228. Eigen::Vector2f uv2 = pOther->project(x3D2);
  229. float errX2 = uv2(0) - kp2.pt.x;
  230. float errY2 = uv2(1) - kp2.pt.y;
  231. if((errX2*errX2+errY2*errY2)>5.991*sigmaLevel2){ //Reprojection error is high
  232. return false;
  233. }
  234. //Since parallax is big enough and reprojection errors are low, this pair of points
  235. //can be considered as a match
  236. x3Dtriangulated = x3D;
  237. return true;
  238. }
  239. float KannalaBrandt8::TriangulateMatches(GeometricCamera *pCamera2, const cv::KeyPoint &kp1, const cv::KeyPoint &kp2, const Eigen::Matrix3f& R12, const Eigen::Vector3f& t12, const float sigmaLevel, const float unc, Eigen::Vector3f& p3D) {
  240. Eigen::Vector3f r1 = this->unprojectEig(kp1.pt);
  241. Eigen::Vector3f r2 = pCamera2->unprojectEig(kp2.pt);
  242. //Check parallax
  243. Eigen::Vector3f r21 = R12 * r2;
  244. const float cosParallaxRays = r1.dot(r21)/(r1.norm() *r21.norm());
  245. if(cosParallaxRays > 0.9998){
  246. return -1;
  247. }
  248. //Parallax is good, so we try to triangulate
  249. cv::Point2f p11,p22;
  250. p11.x = r1[0];
  251. p11.y = r1[1];
  252. p22.x = r2[0];
  253. p22.y = r2[1];
  254. Eigen::Vector3f x3D;
  255. Eigen::Matrix<float,3,4> Tcw1;
  256. Tcw1 << Eigen::Matrix3f::Identity(), Eigen::Vector3f::Zero();
  257. Eigen::Matrix<float,3,4> Tcw2;
  258. Eigen::Matrix3f R21 = R12.transpose();
  259. Tcw2 << R21, -R21 * t12;
  260. Triangulate(p11,p22,Tcw1,Tcw2,x3D);
  261. // cv::Mat x3Dt = x3D.t();
  262. float z1 = x3D(2);
  263. if(z1 <= 0){
  264. return -2;
  265. }
  266. float z2 = R21.row(2).dot(x3D)+Tcw2(2,3);
  267. if(z2<=0){
  268. return -3;
  269. }
  270. //Check reprojection error
  271. Eigen::Vector2f uv1 = this->project(x3D);
  272. float errX1 = uv1(0) - kp1.pt.x;
  273. float errY1 = uv1(1) - kp1.pt.y;
  274. if((errX1*errX1+errY1*errY1)>5.991 * sigmaLevel){ //Reprojection error is high
  275. return -4;
  276. }
  277. Eigen::Vector3f x3D2 = R21 * x3D + Tcw2.col(3);
  278. Eigen::Vector2f uv2 = pCamera2->project(x3D2);
  279. float errX2 = uv2(0) - kp2.pt.x;
  280. float errY2 = uv2(1) - kp2.pt.y;
  281. if((errX2*errX2+errY2*errY2)>5.991 * unc){ //Reprojection error is high
  282. return -5;
  283. }
  284. p3D = x3D;
  285. return z1;
  286. }
  287. std::ostream & operator<<(std::ostream &os, const KannalaBrandt8 &kb) {
  288. os << kb.mvParameters[0] << " " << kb.mvParameters[1] << " " << kb.mvParameters[2] << " " << kb.mvParameters[3] << " "
  289. << kb.mvParameters[4] << " " << kb.mvParameters[5] << " " << kb.mvParameters[6] << " " << kb.mvParameters[7];
  290. return os;
  291. }
  292. std::istream & operator>>(std::istream &is, KannalaBrandt8 &kb) {
  293. float nextParam;
  294. for(size_t i = 0; i < 8; i++){
  295. assert(is.good()); //Make sure the input stream is good
  296. is >> nextParam;
  297. kb.mvParameters[i] = nextParam;
  298. }
  299. return is;
  300. }
  301. void KannalaBrandt8::Triangulate(const cv::Point2f &p1, const cv::Point2f &p2, const Eigen::Matrix<float,3,4> &Tcw1,
  302. const Eigen::Matrix<float,3,4> &Tcw2, Eigen::Vector3f &x3D)
  303. {
  304. Eigen::Matrix<float,4,4> A;
  305. A.row(0) = p1.x*Tcw1.row(2)-Tcw1.row(0);
  306. A.row(1) = p1.y*Tcw1.row(2)-Tcw1.row(1);
  307. A.row(2) = p2.x*Tcw2.row(2)-Tcw2.row(0);
  308. A.row(3) = p2.y*Tcw2.row(2)-Tcw2.row(1);
  309. Eigen::JacobiSVD<Eigen::Matrix4f> svd(A, Eigen::ComputeFullV);
  310. Eigen::Vector4f x3Dh = svd.matrixV().col(3);
  311. x3D = x3Dh.head(3)/x3Dh(3);
  312. }
  313. bool KannalaBrandt8::IsEqual(GeometricCamera* pCam)
  314. {
  315. if(pCam->GetType() != GeometricCamera::CAM_FISHEYE)
  316. return false;
  317. KannalaBrandt8* pKBCam = (KannalaBrandt8*) pCam;
  318. if(abs(precision - pKBCam->GetPrecision()) > 1e-6)
  319. return false;
  320. if(size() != pKBCam->size())
  321. return false;
  322. bool is_same_camera = true;
  323. for(size_t i=0; i<size(); ++i)
  324. {
  325. if(abs(mvParameters[i] - pKBCam->getParameter(i)) > 1e-6)
  326. {
  327. is_same_camera = false;
  328. break;
  329. }
  330. }
  331. return is_same_camera;
  332. }
  333. }