mirror of
https://github.com/introlab/rtabmap.git
synced 2026-10-06 10:07:47 +08:00
* added doc and tests for util2d.h * updated cmake-ros ci * Added util3d.h doc and tests * util3d_transforms.h: Added doc and tests * util3d_filtering.h: started doc and test * util3d_filtering.h: more tests and doc * Added more doc/tests * finished util3d_filtering doc and tests * added test for util2d::depthBleedingFiltering * Added util3d_registration tests * Added util3d_features.h doc/tests * added doc/tests for util3d_correspondences.h * added doc/gtest for util3d_mapping.h (missing hpp functions) * finished testing util3d_mapping.hpp * Added util3d_motion_estimation.h tests (2D->3D done) * finished util3d_motion_estimation.h tests * minimal util3d_surface.h * Added Transform and VisualWord tests * Added doc for CameraModel and StereoCameraModel * Added more logs in ros ci * Passing tests on fical * improved all devcontainer * added devcontainer kilted, fixed source setup.bash, removed ldconfig in ros-cmake workflow * cleanup * source ros * Added utilite tests * Added testing to appveyor, github actions cancellable on re-commit on same branch * appveyor testing without all targets * appveyor: specifying ALL_BUILD target * Fixed Util2dTest.NMSImageBoundsRespected test * Fixing PCL Indices error on old pcl * Added VWDictionary tests and doc. Fixed LSH not working (fix from https://github.com/flann-lib/flann/pull/472 * fixing some appveyor CI errors, added test to check dictionary serialization against all type * Added StereoDense, StereoBM and StereoSGBM doc and tests * Added Stereo tests * Added CameraModel and StereoCameraModel tests * Added doc and test for Statistics * Added doc/tests for Signature * Added doc/test for SensorEvent, added doc for SensorCaptureInfo * Added doc to SensorData * Added SensorData tests * Added SensorCapture and SensorCaptureThread doc and tests * fixed sensordata test * updated SSC test and doc * Added doc and tests for BayesFilter class * Enabled testing on mac, updated windows testing like on linux * added test_link * fixed unresolved on windows * fixed ThreadHandle error on macos ci * Added GPS and GeodeticCoords tests * Added tests for compression * Added Odometry tests (base class only) * Added DBDriver tests * Added coverage report * uniformized test names * fixing concurancy and coverage ci * dont built tools, examples and app for coverage build * fixed report tool rebuilt without qt compilation error * updated coverage option * updated coverage config * added doc CI job * fixing windows and mac ci errors * Added DBDriverSqlite3 tests * Added IMU tests * Added Graph tests * fixing flaky macos test * Added IMUThread and IMUFilter tests * Added Landmarks tests * Added LASWriter tests * fixing seed flaky test * fixing flaky macos timing tests * Added LocalGrid tests * Added LocalGridMaker tests * fixing ci errors * Added GlobalMap tests * Added doc for EnvSensor * Added Features2D tests * Added Registration tests * Added RegistrationVis tests * Added doc for Rtabmap and Memory classes * Added Memory and Rtabmap tests * making some tests less flaky * lcov 1.14 support * updated compatible tool arguments * Added integration tests (RGB-D, Stereo, Lidar2d, Lidar3d) * More octomap checks * Refactored how/when python interpretor is created to simplify library usage * Added python tests * fixed some flaky tests * suppressed some third party related warnings * fixed ceres tests * more flaky fixes * Fixing tests without libpointmatcher * Added RANSAC rejection filter to PCL ICP * fixing multi platform flakiness * Added test to detect regression * Fixing windows pcl link error * fixed some macos flakiness * bigger 2D2D registration error on opencv 4.6.0 * flakiness * fixing flaky tests on windows and mac * flaky thread test on slow mac VM * windows slow test * fixing more ci erros * fxing temp dir on windows * Added Optimizer tests and discovered some bugs (fixed) * fixing flaky tests in mac and windows * Added Optimizer doc * Added GTSAM BA, updated Ceres to use g2o ba parameters. Renamed g2o's ba related parameters to Optimizer group and used by both gtsam and ceres. * fixing build without gtsam * fixing home dir * fixing python ci isssues * Added multicam ba tests * Added Ceres multicam BA support * Aligned BundleAdjustment parameters with Optimizer/Strategy to avoid confusion in the code * Added BA integration test * Added robust graph optimization integration test * Added loop3it test * Added stereo20Hz test * Added smartfactor gtsam * Fixed bugged check and warn if python didn't return any descriptors * Fixing gtsam version build issues * fixing tilt on windows ci * loosing ceres integration test for ci * mac ci flakiness * updating missing param in gui * updating test bound for mac * added appearance-based tests, set min gftt quality to quality level * testing more stuff * improving features2d tests * ci flakiness * fixing flaky ci * ci fixes * flaky fixes * Added RegistrationIcp tests * Added icp integration test with real-worl corridor like env * intermediate nodes * fixing enum * Updated test to catch #1714 * Fixed 2d corridor failing on pcl * flaky pnp test * flaky brisk test * Set rtabmap_integration test as long * updating loop closure test * flaky ci tests * TEsting roundtrip g2o/toro save/load * loosing test bound * fixed cuda capable checks * flaky tests * Debugging test hanging * more debugging stuff * updating limit * windows: disabled cuda on ci to avoid incompatible driver issue. Fixing a bad test mem allocation * trying fixing cuda hanging issue * fixing ci flakyness * flaky tests * Updated BOW flaky tests by checking min precision/recall instead of recall@100precision. Fixed signature test * CameraModel::load() test initRectificationMap param * test dbdriver load dictionary idsOnly * Memory: test keepLinkedInDb param * added dummyDictionary tests * test intermediate nodes count * Added MarkerDetector tests * reverted breaking change of UMutex and USemaphore * Features2d: fixed compiltion warnings with clang about override * clang warnings * fixing test build with pcl 1.8 * g2o and gtsam build errors on android * opencv5 test fixes * disabled testing for ios and android builds * normalized endline characters for easier diff * added LF CRLF rule * bump 0.23.10. fixing doc version * Publish rtabmap website doc from ci * fixing MSCVC build error * macos icp flaky test * fixing ceres macos test bound * ficing more flaky tests * fixing opencv5 related test errors. Also fixed an actual bug in ENU_WGS84ToGeocentric_WGS84() * added comment about mrpt change * removed rosdoc2 (will add it for rtabmap_ros later) * fixing website style * updated download links * locally deployable website with api * sweep doxygen issues * improved/revised doxygen main pages * removed examples empty page * Updated doxygen style * more concise doxygen groups * added api link on main readme * fixing utilite test error * fixing CommonFilteringGroundNormalsUp test * updated precisionRecall test bounds for Freak and brief descriptors * fixing scale check in ba tests * disabled tests on windows cuda build (missing dlls amd runner cannot test cuda anyway) * ceres: missing suitesparse dep in windows ci * adjusting recall thr for fast/freak * ficing more flaky tests * fixing flaky tests * disabled coverage in ros ci * Enable integration tests for ros ci jobs * loosing up some threshold for failing tests * trigger cache * fixing test data in ros ci. Updated flaky test for mac * slaking some test limit * Fixed rtabmap-detectMoreLoopClosures inverted output value * loosing up sift recall on mac * optimizer re-ordered distribution for reproducible results (mac g2o) * macos dump test crash log * combining all tests to save time on shared library reload. Also fixed Logs with missing arguments. * Added ENABLE_FORMAT_ERRORS cmake option * do test only one time * fixed all format warnings * format security android build errors * less verbose tests * updated ImuUThread test * fixed a log * Fixed libpointmatcher 2d normals eigen issue * Fixing libpointmatcher conversion issues * fixing libpointmatcher test on windows ci * cleanup comments, relax some test thr * disabled sequoia-intel ci build (too flaky, would need extensive testing directly on that machine)
544 lines
17 KiB
C++
544 lines
17 KiB
C++
/*
|
|
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "rtabmap/core/EpipolarGeometry.h"
|
|
#include "rtabmap/core/Signature.h"
|
|
#include "rtabmap/utilite/ULogger.h"
|
|
#include "rtabmap/utilite/UTimer.h"
|
|
#include "rtabmap/utilite/UStl.h"
|
|
#include "rtabmap/utilite/UMath.h"
|
|
|
|
#include <opencv2/core/core.hpp>
|
|
#if CV_MAJOR_VERSION < 5
|
|
#include <opencv2/calib3d/calib3d.hpp>
|
|
#else
|
|
#include <opencv2/geometry.hpp>
|
|
#endif
|
|
#include <iostream>
|
|
|
|
namespace rtabmap
|
|
{
|
|
|
|
/////////////////////////
|
|
// HypVerificatorEpipolarGeo
|
|
/////////////////////////
|
|
EpipolarGeometry::EpipolarGeometry(const ParametersMap & parameters) :
|
|
_matchCountMinAccepted(Parameters::defaultVhEpMatchCountMin()),
|
|
_ransacParam1(Parameters::defaultVhEpRansacParam1()),
|
|
_ransacParam2(Parameters::defaultVhEpRansacParam2())
|
|
{
|
|
this->parseParameters(parameters);
|
|
}
|
|
|
|
EpipolarGeometry::~EpipolarGeometry() {
|
|
|
|
}
|
|
|
|
void EpipolarGeometry::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Parameters::parse(parameters, Parameters::kVhEpMatchCountMin(), _matchCountMinAccepted);
|
|
Parameters::parse(parameters, Parameters::kVhEpRansacParam1(), _ransacParam1);
|
|
Parameters::parse(parameters, Parameters::kVhEpRansacParam2(), _ransacParam2);
|
|
}
|
|
|
|
bool EpipolarGeometry::check(const Signature * ssA, const Signature * ssB)
|
|
{
|
|
if(ssA == 0 || ssB == 0)
|
|
{
|
|
return false;
|
|
}
|
|
ULOGGER_DEBUG("id(%d,%d)", ssA->id(), ssB->id());
|
|
|
|
std::list<std::pair<int, std::pair<int, int> > > pairsId;
|
|
|
|
findPairsUnique(ssA->getWords(), ssB->getWords(), pairsId);
|
|
|
|
if((int)pairsId.size()<_matchCountMinAccepted)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::list<std::pair<int, std::pair<cv::KeyPoint, cv::KeyPoint> > > pairs;
|
|
for(std::list<std::pair<int, std::pair<int, int> > >::iterator iter = pairsId.begin(); iter!=pairsId.end(); ++iter)
|
|
{
|
|
pairs.push_back(std::make_pair(iter->first, std::make_pair(ssA->getWordsKpts()[iter->second.first], ssB->getWordsKpts()[iter->second.second])));
|
|
}
|
|
|
|
std::vector<uchar> status;
|
|
cv::Mat f = findFFromWords(pairs, status, _ransacParam1, _ransacParam2);
|
|
|
|
int inliers = uSum(status);
|
|
if(inliers < _matchCountMinAccepted)
|
|
{
|
|
ULOGGER_DEBUG("Epipolar constraint failed A : not enough inliers (%d/%d), min is %d", inliers, (int)pairs.size(), _matchCountMinAccepted);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
UDEBUG("inliers = %d/%d", inliers, (int)pairs.size());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//STATIC STUFF
|
|
//Epipolar geometry
|
|
void EpipolarGeometry::findEpipolesFromF(const cv::Mat & fundamentalMatrix, cv::Vec3d & e1, cv::Vec3d & e2)
|
|
{
|
|
if(fundamentalMatrix.rows != 3 || fundamentalMatrix.cols != 3)
|
|
{
|
|
ULOGGER_ERROR("The matrix is not the good size...");
|
|
return;
|
|
}
|
|
|
|
if(fundamentalMatrix.type() != CV_64FC1)
|
|
{
|
|
ULOGGER_ERROR("The matrix is not the good type...");
|
|
return;
|
|
}
|
|
|
|
|
|
cv::SVD svd(fundamentalMatrix);
|
|
cv::Mat u = svd.u;
|
|
cv::Mat v = svd.vt;
|
|
cv::Mat w = svd.w;
|
|
|
|
// v is for image 1
|
|
// u is for image 2
|
|
|
|
e1[0] = v.at<double>(0,2);// /v->data.db[2*3+2];
|
|
e1[1] = v.at<double>(1,2);// /v->data.db[2*3+2];
|
|
e1[2] = v.at<double>(2,2);// /v->data.db[2*3+2];
|
|
|
|
e2[0] = u.at<double>(0,2);// /u->data.db[2*3+2];
|
|
e2[1] = u.at<double>(1,2);// /u->data.db[2*3+2];
|
|
e2[2] = u.at<double>(2,2);// /u->data.db[2*3+2];
|
|
}
|
|
|
|
int inFrontOfBothCameras(const cv::Mat & x, const cv::Mat & xp, const cv::Mat & R, const cv::Mat & T)
|
|
{
|
|
// P0 matrix 3X4
|
|
cv::Mat p0 = cv::Mat::zeros(3, 4, CV_64FC1);
|
|
p0.at<double>(0,0) = 1;
|
|
p0.at<double>(1,1) = 1;
|
|
p0.at<double>(2,2) = 1;
|
|
cv::Mat p = cv::Mat::zeros(3, 4, CV_64FC1);
|
|
p.at<double>(0,0) = R.at<double>(0,0);
|
|
p.at<double>(0,1) = R.at<double>(0,1);
|
|
p.at<double>(0,2) = R.at<double>(0,2);
|
|
p.at<double>(1,0) = R.at<double>(1,0);
|
|
p.at<double>(1,1) = R.at<double>(1,1);
|
|
p.at<double>(1,2) = R.at<double>(1,2);
|
|
p.at<double>(2,0) = R.at<double>(2,0);
|
|
p.at<double>(2,1) = R.at<double>(2,1);
|
|
p.at<double>(2,2) = R.at<double>(2,2);
|
|
p.at<double>(0,3) = T.at<double>(0,0);
|
|
p.at<double>(1,3) = T.at<double>(1,0);
|
|
p.at<double>(2,3) = T.at<double>(2,0);
|
|
|
|
cv::Mat pts4D;
|
|
//std::vector<double> reprojErrors;
|
|
//pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
|
|
//EpipolarGeometry::triangulatePoints(x, xp, p0, p, cloud, reprojErrors);
|
|
cv::triangulatePoints(p0, p, x, xp, pts4D);
|
|
|
|
//http://en.wikipedia.org/wiki/Essential_matrix#3D_points_from_corresponding_image_points
|
|
int nValid = 0;
|
|
for(int i=0; i<x.cols; ++i)
|
|
{
|
|
// the five to ignore when all points are super close to the camera
|
|
if(pts4D.at<double>(2,i)/pts4D.at<double>(3,i) > 5)
|
|
//if(cloud->at(i).z > 5)
|
|
{
|
|
++nValid;
|
|
}
|
|
}
|
|
UDEBUG("nValid=%d/%d", nValid, x.cols);
|
|
|
|
return nValid;
|
|
}
|
|
|
|
//Assuming P0 = [eye(3) zeros(3,1)]
|
|
// x1 and x2 are 2D points
|
|
// return camera matrix P (3x4) matrix
|
|
//http://www.robots.ox.ac.uk/~vgg/hzbook/hzbook2/HZepipolar.pdf
|
|
cv::Mat EpipolarGeometry::findPFromE(const cv::Mat & E,
|
|
const cv::Mat & x,
|
|
const cv::Mat & xp)
|
|
{
|
|
UDEBUG("begin");
|
|
UASSERT(E.rows == 3 && E.cols == 3);
|
|
UASSERT(E.type() == CV_64FC1);
|
|
UASSERT(x.rows == 2 && x.cols>0 && x.type() == CV_64FC1);
|
|
UASSERT(xp.rows == 2 && xp.cols>0 && x.type() == CV_64FC1);
|
|
|
|
// skew matrix 3X3
|
|
cv::Mat w = cv::Mat::zeros( 3, 3, CV_64FC1);
|
|
w.at<double>(0,1) = -1;
|
|
w.at<double>(1,0) = 1;
|
|
w.at<double>(2,2) = 1;
|
|
//std::cout << "W=" << w << std::endl;
|
|
|
|
cv::Mat e = E;
|
|
cv::SVD svd(e,cv::SVD::MODIFY_A);
|
|
cv::Mat u = svd.u;
|
|
cv::Mat vt = svd.vt;
|
|
cv::Mat s = svd.w;
|
|
|
|
//std::cout << "u=" << u << std::endl;
|
|
//std::cout << "vt=" << vt << std::endl;
|
|
//std::cout << "s=" << s << std::endl;
|
|
|
|
// E = u*diag(1,1,0)*vt
|
|
cv::Mat diag = cv::Mat::eye(3,3,CV_64FC1);
|
|
diag.at<double>(2,2) = 0;
|
|
e = u*diag*vt;
|
|
svd(e,cv::SVD::MODIFY_A);
|
|
u = svd.u;
|
|
vt = svd.vt;
|
|
s = svd.w;
|
|
|
|
cv::Mat r = u*w*vt;
|
|
if(cv::determinant(r)+1.0 < 1e-09) {
|
|
//according to http://en.wikipedia.org/wiki/Essential_matrix#Showing_that_it_is_valid
|
|
UDEBUG("det(R) == -1 [%f]: flip E's sign", cv::determinant(r));
|
|
e = -E;
|
|
svd(e,cv::SVD::MODIFY_A);
|
|
u = svd.u;
|
|
vt = svd.vt;
|
|
s = svd.w;
|
|
}
|
|
cv::Mat wt = w.t();
|
|
|
|
// INFO: There 4 cases of P, only one have all the points in
|
|
// front of the two cameras (positive z).
|
|
|
|
cv::Mat r1 = u*w*vt;
|
|
cv::Mat r2 = u*wt*vt;
|
|
|
|
cv::Mat t1 = u.col(2);
|
|
cv::Mat t2 = u.col(2)*-1;
|
|
|
|
int max = 0;
|
|
int maxIndex = 1;
|
|
int maxTmp;
|
|
cv::Mat R=r1,T=t1;
|
|
|
|
// Case 1 : P = [U*W*V' t];
|
|
max = inFrontOfBothCameras(x, xp, r1, t1);
|
|
// Case 2 : P = [U*W*V' -t];
|
|
maxTmp = inFrontOfBothCameras(x, xp, r1, t2);
|
|
if(maxTmp > max)
|
|
{
|
|
maxIndex = 2;
|
|
max = maxTmp;
|
|
R=r1,T=t2;
|
|
}
|
|
// Case 3 : P = [U*W'*V' t];
|
|
maxTmp = inFrontOfBothCameras(x, xp, r2, t1);
|
|
if(maxTmp > max)
|
|
{
|
|
maxIndex = 3;
|
|
max = maxTmp;
|
|
R=r2,T=t1;
|
|
}
|
|
// Case 4 : P = [U*W'*V' -t];
|
|
maxTmp = inFrontOfBothCameras(x, xp, r2, t2);
|
|
if(maxTmp > max)
|
|
{
|
|
maxIndex = 4;
|
|
max = maxTmp;
|
|
R=r2,T=t2;
|
|
}
|
|
|
|
if(max > 0)
|
|
{
|
|
UDEBUG("Case %d", maxIndex);
|
|
|
|
// P matrix 3x4
|
|
cv::Mat p = cv::Mat::zeros(3, 4, CV_64FC1);
|
|
p.at<double>(0,0) = R.at<double>(0,0);
|
|
p.at<double>(0,1) = R.at<double>(0,1);
|
|
p.at<double>(0,2) = R.at<double>(0,2);
|
|
p.at<double>(1,0) = R.at<double>(1,0);
|
|
p.at<double>(1,1) = R.at<double>(1,1);
|
|
p.at<double>(1,2) = R.at<double>(1,2);
|
|
p.at<double>(2,0) = R.at<double>(2,0);
|
|
p.at<double>(2,1) = R.at<double>(2,1);
|
|
p.at<double>(2,2) = R.at<double>(2,2);
|
|
p.at<double>(0,3) = T.at<double>(0);
|
|
p.at<double>(1,3) = T.at<double>(1);
|
|
p.at<double>(2,3) = T.at<double>(2);
|
|
return p;
|
|
}
|
|
|
|
return cv::Mat();
|
|
}
|
|
|
|
cv::Mat EpipolarGeometry::findFFromWords(
|
|
const std::list<std::pair<int, std::pair<cv::KeyPoint, cv::KeyPoint> > > & pairs, // id, kpt1, kpt2
|
|
std::vector<uchar> & status,
|
|
double ransacReprojThreshold,
|
|
double ransacConfidence)
|
|
{
|
|
|
|
status = std::vector<uchar>(pairs.size(), 0);
|
|
//Convert Keypoints to a structure that OpenCV understands
|
|
//3 dimensions (Homogeneous vectors)
|
|
cv::Mat points1(1, pairs.size(), CV_32FC2);
|
|
cv::Mat points2(1, pairs.size(), CV_32FC2);
|
|
|
|
float * points1data = points1.ptr<float>(0);
|
|
float * points2data = points2.ptr<float>(0);
|
|
|
|
// Fill the points here ...
|
|
int i=0;
|
|
for(std::list<std::pair<int, std::pair<cv::KeyPoint, cv::KeyPoint> > >::const_iterator iter = pairs.begin();
|
|
iter != pairs.end();
|
|
++iter )
|
|
{
|
|
points1data[i*2] = (*iter).second.first.pt.x;
|
|
points1data[i*2+1] = (*iter).second.first.pt.y;
|
|
|
|
points2data[i*2] = (*iter).second.second.pt.x;
|
|
points2data[i*2+1] = (*iter).second.second.pt.y;
|
|
|
|
++i;
|
|
}
|
|
|
|
UTimer timer;
|
|
timer.start();
|
|
|
|
// Find the fundamental matrix
|
|
cv::Mat fundamentalMatrix = cv::findFundamentalMat(
|
|
points1,
|
|
points2,
|
|
status,
|
|
cv::FM_RANSAC,
|
|
ransacReprojThreshold,
|
|
ransacConfidence);
|
|
|
|
ULOGGER_DEBUG("Find fundamental matrix (OpenCV) time = %fs", timer.ticks());
|
|
|
|
// Fundamental matrix is valid ?
|
|
bool fundMatFound = false;
|
|
UASSERT(fundamentalMatrix.type() == CV_64FC1);
|
|
if(fundamentalMatrix.cols==3 && fundamentalMatrix.rows==3 &&
|
|
(fundamentalMatrix.at<double>(0,0) != 0.0 ||
|
|
fundamentalMatrix.at<double>(0,1) != 0.0 ||
|
|
fundamentalMatrix.at<double>(0,2) != 0.0 ||
|
|
fundamentalMatrix.at<double>(1,0) != 0.0 ||
|
|
fundamentalMatrix.at<double>(1,1) != 0.0 ||
|
|
fundamentalMatrix.at<double>(1,2) != 0.0 ||
|
|
fundamentalMatrix.at<double>(2,0) != 0.0 ||
|
|
fundamentalMatrix.at<double>(2,1) != 0.0 ||
|
|
fundamentalMatrix.at<double>(2,2) != 0.0) )
|
|
|
|
{
|
|
fundMatFound = true;
|
|
}
|
|
|
|
ULOGGER_DEBUG("fm_count=%d...", fundMatFound);
|
|
|
|
if(fundMatFound)
|
|
{
|
|
// Show the fundamental matrix
|
|
UDEBUG(
|
|
"F = [%f %f %f;%f %f %f;%f %f %f]",
|
|
fundamentalMatrix.ptr<double>(0)[0],
|
|
fundamentalMatrix.ptr<double>(0)[1],
|
|
fundamentalMatrix.ptr<double>(0)[2],
|
|
fundamentalMatrix.ptr<double>(0)[3],
|
|
fundamentalMatrix.ptr<double>(0)[4],
|
|
fundamentalMatrix.ptr<double>(0)[5],
|
|
fundamentalMatrix.ptr<double>(0)[6],
|
|
fundamentalMatrix.ptr<double>(0)[7],
|
|
fundamentalMatrix.ptr<double>(0)[8]);
|
|
}
|
|
return fundamentalMatrix;
|
|
}
|
|
|
|
void EpipolarGeometry::findRTFromP(
|
|
const cv::Mat & p,
|
|
cv::Mat & r,
|
|
cv::Mat & t)
|
|
{
|
|
UASSERT(p.cols == 4 && p.rows == 3);
|
|
r = cv::Mat(p, cv::Range(0,3), cv::Range(0,3));
|
|
//r = -r.inv();
|
|
//t = r*p.col(3);
|
|
t = p.col(3);
|
|
}
|
|
|
|
cv::Mat EpipolarGeometry::findFFromCalibratedStereoCameras(double fx, double fy, double cx, double cy, double Tx, double Ty)
|
|
{
|
|
cv::Mat R = cv::Mat::eye(3, 3, CV_64FC1);
|
|
|
|
double Bx = Tx/-fx;
|
|
double By = Ty/-fy;
|
|
|
|
cv::Mat tx = (cv::Mat_<double>(3,3) <<
|
|
0, 0, By,
|
|
0, 0, -Bx,
|
|
-By, Bx, 0);
|
|
|
|
cv::Mat K = (cv::Mat_<double>(3,3) <<
|
|
fx, 0, cx,
|
|
0, fy, cy,
|
|
0, 0, 1);
|
|
|
|
cv::Mat E = tx*R;
|
|
|
|
return K.inv().t()*E*K.inv();
|
|
}
|
|
|
|
|
|
/**
|
|
source = SfM toy library: https://github.com/royshil/SfM-Toy-Library
|
|
From "Triangulation", Hartley, R.I. and Sturm, P., Computer vision and image understanding, 1997
|
|
return 1x3 double
|
|
*/
|
|
cv::Mat EpipolarGeometry::linearLSTriangulation(
|
|
cv::Point3d u, //homogenous image point (u,v,1)
|
|
cv::Matx34d P, //camera 1 matrix 3x4 double
|
|
cv::Point3d u1, //homogenous image point in 2nd camera
|
|
cv::Matx34d P1 //camera 2 matrix 3x4 double
|
|
)
|
|
{
|
|
//build matrix A for homogenous equation system Ax = 0
|
|
//assume X = (x,y,z,1), for Linear-LS method
|
|
//which turns it into a AX = B system, where A is 4x3, X is 3x1 and B is 4x1
|
|
cv::Mat A = (cv::Mat_<double>(4,3) <<
|
|
u.x*P(2,0)-P(0,0), u.x*P(2,1)-P(0,1), u.x*P(2,2)-P(0,2),
|
|
u.y*P(2,0)-P(1,0), u.y*P(2,1)-P(1,1), u.y*P(2,2)-P(1,2),
|
|
u1.x*P1(2,0)-P1(0,0), u1.x*P1(2,1)-P1(0,1), u1.x*P1(2,2)-P1(0,2),
|
|
u1.y*P1(2,0)-P1(1,0), u1.y*P1(2,1)-P1(1,1), u1.y*P1(2,2)-P1(1,2)
|
|
);
|
|
cv::Mat B = (cv::Mat_<double>(4,1) <<
|
|
-(u.x*P(2,3) -P(0,3)),
|
|
-(u.y*P(2,3) -P(1,3)),
|
|
-(u1.x*P1(2,3) -P1(0,3)),
|
|
-(u1.y*P1(2,3) -P1(1,3)));
|
|
|
|
cv::Mat X;
|
|
solve(A,B,X,cv::DECOMP_SVD);
|
|
|
|
return X; // return 1x3 double
|
|
}
|
|
|
|
/**
|
|
source = SfM toy library: https://github.com/royshil/SfM-Toy-Library
|
|
From "Triangulation", Hartley, R.I. and Sturm, P., Computer vision and image understanding, 1997
|
|
return 4x1 double
|
|
*/
|
|
cv::Mat EpipolarGeometry::iterativeLinearLSTriangulation(
|
|
cv::Point3d u, //homogenous image point (u,v,1)
|
|
const cv::Matx34d & P, //camera 1 matrix 3x4 double
|
|
cv::Point3d u1, //homogenous image point in 2nd camera
|
|
const cv::Matx34d & P1) //camera 2 matrix 3x4 double
|
|
{
|
|
double wi = 1, wi1 = 1;
|
|
double EPSILON = 0.0001;
|
|
|
|
cv::Mat X(4,1,CV_64FC1);
|
|
cv::Mat X_ = linearLSTriangulation(u,P,u1,P1);
|
|
X.at<double>(0) = X_.at<double>(0);
|
|
X.at<double>(1) = X_.at<double>(1);
|
|
X.at<double>(2) = X_.at<double>(2);
|
|
X.at<double>(3) = 1.0;
|
|
for (int i=0; i<10; i++) //Hartley suggests 10 iterations at most
|
|
{
|
|
//recalculate weights
|
|
double p2x = cv::Mat(cv::Mat(P).row(2)*X).at<double>(0);
|
|
double p2x1 = cv::Mat(cv::Mat(P1).row(2)*X).at<double>(0);
|
|
|
|
//breaking point
|
|
if(fabs(wi - p2x) <= EPSILON && fabs(wi1 - p2x1) <= EPSILON) break;
|
|
|
|
wi = p2x;
|
|
wi1 = p2x1;
|
|
|
|
//reweight equations and solve
|
|
cv::Mat A = (cv::Mat_<double>(4,3) <<
|
|
(u.x*P(2,0)-P(0,0))/wi, (u.x*P(2,1)-P(0,1))/wi, (u.x*P(2,2)-P(0,2))/wi,
|
|
(u.y*P(2,0)-P(1,0))/wi, (u.y*P(2,1)-P(1,1))/wi, (u.y*P(2,2)-P(1,2))/wi,
|
|
(u1.x*P1(2,0)-P1(0,0))/wi1, (u1.x*P1(2,1)-P1(0,1))/wi1, (u1.x*P1(2,2)-P1(0,2))/wi1,
|
|
(u1.y*P1(2,0)-P1(1,0))/wi1, (u1.y*P1(2,1)-P1(1,1))/wi1, (u1.y*P1(2,2)-P1(1,2))/wi1);
|
|
cv::Mat B = (cv::Mat_<double>(4,1) <<
|
|
-(u.x*P(2,3) -P(0,3))/wi,
|
|
-(u.y*P(2,3) -P(1,3))/wi,
|
|
-(u1.x*P1(2,3) -P1(0,3))/wi1,
|
|
-(u1.y*P1(2,3) -P1(1,3))/wi1);
|
|
|
|
solve(A,B,X_,cv::DECOMP_SVD);
|
|
X.at<double>(0) = X_.at<double>(0);
|
|
X.at<double>(1) = X_.at<double>(1);
|
|
X.at<double>(2) = X_.at<double>(2);
|
|
X.at<double>(3) = 1.0;
|
|
}
|
|
return X; // return 4x1 double
|
|
}
|
|
|
|
/**
|
|
source = SfM toy library: https://github.com/royshil/SfM-Toy-Library
|
|
*/
|
|
//Triagulate points
|
|
double EpipolarGeometry::triangulatePoints(
|
|
const cv::Mat& pt_set, //2xN double
|
|
const cv::Mat& pt_set1, //2xN double
|
|
const cv::Mat& P, // 3x4 double
|
|
const cv::Mat& P1, // 3x4 double
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr & pointcloud,
|
|
std::vector<double> & reproj_errors)
|
|
{
|
|
pointcloud.reset(new pcl::PointCloud<pcl::PointXYZ>);
|
|
|
|
unsigned int pts_size = pt_set.cols;
|
|
|
|
pointcloud->resize(pts_size);
|
|
reproj_errors.resize(pts_size);
|
|
|
|
for(unsigned int i=0; i<pts_size; i++)
|
|
{
|
|
cv::Point3d u(pt_set.at<double>(0,i),pt_set.at<double>(1,i),1.0);
|
|
cv::Point3d u1(pt_set1.at<double>(0,i),pt_set1.at<double>(1,i),1.0);
|
|
|
|
cv::Mat X = iterativeLinearLSTriangulation(u,P,u1,P1);
|
|
|
|
cv::Mat x_proj = P * X; //reproject
|
|
x_proj = x_proj / x_proj.at<double>(2);
|
|
cv::Point3d xPt_img_(x_proj.at<double>(0), x_proj.at<double>(1), 1.0);
|
|
|
|
double reprj_err = norm(xPt_img_ - u);
|
|
reproj_errors[i] = reprj_err;
|
|
pointcloud->at(i) = pcl::PointXYZ(X.at<double>(0),X.at<double>(1),X.at<double>(2));
|
|
}
|
|
|
|
return cv::mean(reproj_errors)[0]; // mean reproj error
|
|
}
|
|
|
|
} // namespace rtabmap
|