util3d_motion_estimation.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007     * Redistributions of source code must retain the above copyright
00008       notice, this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright
00010       notice, this list of conditions and the following disclaimer in the
00011       documentation and/or other materials provided with the distribution.
00012     * Neither the name of the Universite de Sherbrooke nor the
00013       names of its contributors may be used to endorse or promote products
00014       derived from this software without specific prior written permission.
00015 
00016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
00020 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026 */
00027 
00028 #include "rtabmap/core/util3d_motion_estimation.h"
00029 
00030 #include "rtabmap/utilite/UStl.h"
00031 #include "rtabmap/utilite/UMath.h"
00032 #include "rtabmap/utilite/ULogger.h"
00033 #include "rtabmap/core/util3d_transforms.h"
00034 #include "rtabmap/core/util3d_registration.h"
00035 #include "rtabmap/core/util3d_correspondences.h"
00036 #include "rtabmap/core/util3d.h"
00037 
00038 #include <pcl/common/common.h>
00039 
00040 #include "opencv/solvepnp.h"
00041 
00042 namespace rtabmap
00043 {
00044 
00045 namespace util3d
00046 {
00047 
00048 Transform estimateMotion3DTo2D(
00049                         const std::map<int, cv::Point3f> & words3A,
00050                         const std::map<int, cv::KeyPoint> & words2B,
00051                         const CameraModel & cameraModel,
00052                         int minInliers,
00053                         int iterations,
00054                         double reprojError,
00055                         int flagsPnP,
00056                         int refineIterations,
00057                         const Transform & guess,
00058                         const std::map<int, cv::Point3f> & words3B,
00059                         cv::Mat * covariance,
00060                         std::vector<int> * matchesOut,
00061                         std::vector<int> * inliersOut)
00062 {
00063         UASSERT(cameraModel.isValidForProjection());
00064         UASSERT(!guess.isNull());
00065         Transform transform;
00066         std::vector<int> matches, inliers;
00067 
00068         if(covariance)
00069         {
00070                 *covariance = cv::Mat::eye(6,6,CV_64FC1);
00071         }
00072 
00073         // find correspondences
00074         std::vector<int> ids = uKeys(words2B);
00075         std::vector<cv::Point3f> objectPoints(ids.size());
00076         std::vector<cv::Point2f> imagePoints(ids.size());
00077         int oi=0;
00078         matches.resize(ids.size());
00079         for(unsigned int i=0; i<ids.size(); ++i)
00080         {
00081                 std::map<int, cv::Point3f>::const_iterator iter=words3A.find(ids[i]);
00082                 if(iter != words3A.end() && util3d::isFinite(iter->second))
00083                 {
00084                         const cv::Point3f & pt = iter->second;
00085                         objectPoints[oi].x = pt.x;
00086                         objectPoints[oi].y = pt.y;
00087                         objectPoints[oi].z = pt.z;
00088                         imagePoints[oi] = words2B.find(ids[i])->second.pt;
00089                         matches[oi++] = ids[i];
00090                 }
00091         }
00092 
00093         objectPoints.resize(oi);
00094         imagePoints.resize(oi);
00095         matches.resize(oi);
00096 
00097         UDEBUG("words3A=%d words2B=%d matches=%d words3B=%d guess=%s",
00098                         (int)words3A.size(), (int)words2B.size(), (int)matches.size(), (int)words3B.size(), guess.prettyPrint().c_str());
00099 
00100         if((int)matches.size() >= minInliers)
00101         {
00102                 //PnPRansac
00103                 cv::Mat K = cameraModel.K();
00104                 cv::Mat D = cameraModel.D();
00105                 Transform guessCameraFrame = (guess * cameraModel.localTransform()).inverse();
00106                 cv::Mat R = (cv::Mat_<double>(3,3) <<
00107                                 (double)guessCameraFrame.r11(), (double)guessCameraFrame.r12(), (double)guessCameraFrame.r13(),
00108                                 (double)guessCameraFrame.r21(), (double)guessCameraFrame.r22(), (double)guessCameraFrame.r23(),
00109                                 (double)guessCameraFrame.r31(), (double)guessCameraFrame.r32(), (double)guessCameraFrame.r33());
00110 
00111                 cv::Mat rvec(1,3, CV_64FC1);
00112                 cv::Rodrigues(R, rvec);
00113                 cv::Mat tvec = (cv::Mat_<double>(1,3) <<
00114                                 (double)guessCameraFrame.x(), (double)guessCameraFrame.y(), (double)guessCameraFrame.z());
00115 
00116                 util3d::solvePnPRansac(
00117                                 objectPoints,
00118                                 imagePoints,
00119                                 K,
00120                                 D,
00121                                 rvec,
00122                                 tvec,
00123                                 true,
00124                                 iterations,
00125                                 reprojError,
00126                                 minInliers, // min inliers
00127                                 inliers,
00128                                 flagsPnP,
00129                                 refineIterations);
00130 
00131                 if((int)inliers.size() >= minInliers)
00132                 {
00133                         cv::Rodrigues(rvec, R);
00134                         Transform pnp(R.at<double>(0,0), R.at<double>(0,1), R.at<double>(0,2), tvec.at<double>(0),
00135                                                    R.at<double>(1,0), R.at<double>(1,1), R.at<double>(1,2), tvec.at<double>(1),
00136                                                    R.at<double>(2,0), R.at<double>(2,1), R.at<double>(2,2), tvec.at<double>(2));
00137 
00138                         transform = (cameraModel.localTransform() * pnp).inverse();
00139 
00140                         // compute variance (like in PCL computeVariance() method of sac_model.h)
00141                         if(covariance && words3B.size())
00142                         {
00143                                 std::vector<float> errorSqrdDists(inliers.size());
00144                                 std::vector<float> errorSqrdAngles(inliers.size());
00145                                 oi = 0;
00146                                 for(unsigned int i=0; i<inliers.size(); ++i)
00147                                 {
00148                                         std::map<int, cv::Point3f>::const_iterator iter = words3B.find(matches[inliers[i]]);
00149                                         if(iter != words3B.end() && util3d::isFinite(iter->second))
00150                                         {
00151                                                 const cv::Point3f & objPt = objectPoints[inliers[i]];
00152                                                 cv::Point3f newPt = util3d::transformPoint(iter->second, transform);
00153                                                 errorSqrdDists[oi] = uNormSquared(objPt.x-newPt.x, objPt.y-newPt.y, objPt.z-newPt.z);
00154 
00155                                                 Eigen::Vector4f v1(objPt.x - transform.x(), objPt.y - transform.y(), objPt.z - transform.z(), 0);
00156                                                 Eigen::Vector4f v2(newPt.x - transform.x(), newPt.y - transform.y(), newPt.z - transform.z(), 0);
00157                                                 errorSqrdAngles[oi++] = pcl::getAngle3D(v1, v2);
00158                                         }
00159                                 }
00160 
00161                                 errorSqrdDists.resize(oi);
00162                                 errorSqrdAngles.resize(oi);
00163                                 if(errorSqrdDists.size())
00164                                 {
00165                                         std::sort(errorSqrdDists.begin(), errorSqrdDists.end());
00166                                         //divide by 4 instead of 2 to ignore very very far features (stereo)
00167                                         double median_error_sqr = 2.1981 * (double)errorSqrdDists[errorSqrdDists.size () >> 2];
00168                                         UASSERT(uIsFinite(median_error_sqr));
00169                                         (*covariance)(cv::Range(0,3), cv::Range(0,3)) *= median_error_sqr;
00170                                         std::sort(errorSqrdAngles.begin(), errorSqrdAngles.end());
00171                                         median_error_sqr = 2.1981 * (double)errorSqrdAngles[errorSqrdAngles.size () >> 2];
00172                                         UASSERT(uIsFinite(median_error_sqr));
00173                                         (*covariance)(cv::Range(3,6), cv::Range(3,6)) *= median_error_sqr;
00174                                 }
00175                                 else
00176                                 {
00177                                         UWARN("Not enough close points to compute covariance!");
00178                                 }
00179                         }
00180                         else if(covariance)
00181                         {
00182                                 // compute variance, which is the rms of reprojection errors
00183                                 std::vector<cv::Point2f> imagePointsReproj;
00184                                 cv::projectPoints(objectPoints, rvec, tvec, K, cv::Mat(), imagePointsReproj);
00185                                 float err = 0.0f;
00186                                 for(unsigned int i=0; i<inliers.size(); ++i)
00187                                 {
00188                                         err += uNormSquared(imagePoints.at(inliers[i]).x - imagePointsReproj.at(inliers[i]).x, imagePoints.at(inliers[i]).y - imagePointsReproj.at(inliers[i]).y);
00189                                 }
00190                                 UASSERT(uIsFinite(err));
00191                                 *covariance *= std::sqrt(err/float(inliers.size()));
00192                         }
00193                 }
00194         }
00195 
00196         if(matchesOut)
00197         {
00198                 *matchesOut = matches;
00199         }
00200         if(inliersOut)
00201         {
00202                 inliersOut->resize(inliers.size());
00203                 for(unsigned int i=0; i<inliers.size(); ++i)
00204                 {
00205                         inliersOut->at(i) = matches[inliers[i]];
00206                 }
00207         }
00208 
00209         return transform;
00210 }
00211 
00212 Transform estimateMotion3DTo3D(
00213                         const std::map<int, cv::Point3f> & words3A,
00214                         const std::map<int, cv::Point3f> & words3B,
00215                         int minInliers,
00216                         double inliersDistance,
00217                         int iterations,
00218                         int refineIterations,
00219                         cv::Mat * covariance,
00220                         std::vector<int> * matchesOut,
00221                         std::vector<int> * inliersOut)
00222 {
00223         Transform transform;
00224         std::vector<cv::Point3f> inliers1; // previous
00225         std::vector<cv::Point3f> inliers2; // new
00226 
00227         std::vector<int> matches;
00228         util3d::findCorrespondences(
00229                         words3A,
00230                         words3B,
00231                         inliers1,
00232                         inliers2,
00233                         0,
00234                         &matches);
00235         UASSERT(inliers1.size() == inliers2.size());
00236         UDEBUG("Unique correspondences = %d", (int)inliers1.size());
00237 
00238         if(covariance)
00239         {
00240                 *covariance = cv::Mat::eye(6,6,CV_64FC1);
00241         }
00242 
00243         std::vector<int> inliers;
00244         if((int)inliers1.size() >= minInliers)
00245         {
00246                 pcl::PointCloud<pcl::PointXYZ>::Ptr inliers1cloud(new pcl::PointCloud<pcl::PointXYZ>);
00247                 pcl::PointCloud<pcl::PointXYZ>::Ptr inliers2cloud(new pcl::PointCloud<pcl::PointXYZ>);
00248                 inliers1cloud->resize(inliers1.size());
00249                 inliers2cloud->resize(inliers1.size());
00250                 for(unsigned int i=0; i<inliers1.size(); ++i)
00251                 {
00252                         (*inliers1cloud)[i].x = inliers1[i].x;
00253                         (*inliers1cloud)[i].y = inliers1[i].y;
00254                         (*inliers1cloud)[i].z = inliers1[i].z;
00255                         (*inliers2cloud)[i].x = inliers2[i].x;
00256                         (*inliers2cloud)[i].y = inliers2[i].y;
00257                         (*inliers2cloud)[i].z = inliers2[i].z;
00258                 }
00259                 Transform t = util3d::transformFromXYZCorrespondences(
00260                                 inliers2cloud,
00261                                 inliers1cloud,
00262                                 inliersDistance,
00263                                 iterations,
00264                                 refineIterations,
00265                                 3.0,
00266                                 &inliers,
00267                                 covariance);
00268 
00269                 if(!t.isNull() && (int)inliers.size() >= minInliers)
00270                 {
00271                         transform = t;
00272                 }
00273         }
00274 
00275         if(matchesOut)
00276         {
00277                 *matchesOut = matches;
00278         }
00279         if(inliersOut)
00280         {
00281                 inliersOut->resize(inliers.size());
00282                 for(unsigned int i=0; i<inliers.size(); ++i)
00283                 {
00284                         inliersOut->at(i) = matches[inliers[i]];
00285                 }
00286         }
00287 
00288         return transform;
00289 }
00290 
00291 
00292 std::vector<float> computeReprojErrors(
00293                 std::vector<cv::Point3f> opoints,
00294                 std::vector<cv::Point2f> ipoints,
00295                 const cv::Mat & cameraMatrix,
00296                 const cv::Mat & distCoeffs,
00297                 const cv::Mat & rvec,
00298                 const cv::Mat & tvec,
00299                 float reprojErrorThreshold,
00300                 std::vector<int> & inliers)
00301 {
00302         UASSERT(opoints.size() == ipoints.size());
00303         int count = (int)opoints.size();
00304 
00305         std::vector<cv::Point2f> projpoints;
00306         projectPoints(opoints, rvec, tvec, cameraMatrix, distCoeffs, projpoints);
00307 
00308         inliers.resize(count,0);
00309         std::vector<float> err(count);
00310         int oi=0;
00311         for (int i = 0; i < count; ++i)
00312         {
00313                 float e = (float)cv::norm( ipoints[i] - projpoints[i]);
00314                 if(e <= reprojErrorThreshold)
00315                 {
00316                         inliers[oi] = i;
00317                         err[oi++] = e;
00318                 }
00319         }
00320         inliers.resize(oi);
00321         err.resize(oi);
00322         return err;
00323 }
00324 
00325 void solvePnPRansac(
00326                 const std::vector<cv::Point3f> & objectPoints,
00327                 const std::vector<cv::Point2f> & imagePoints,
00328                 const cv::Mat & cameraMatrix,
00329                 const cv::Mat & distCoeffs,
00330                 cv::Mat & rvec,
00331                 cv::Mat & tvec,
00332                 bool useExtrinsicGuess,
00333         int iterationsCount,
00334         float reprojectionError,
00335         int minInliersCount,
00336         std::vector<int> & inliers,
00337         int flags,
00338         int refineIterations,
00339         float refineSigma)
00340 {
00341         if(minInliersCount < 4)
00342         {
00343                 minInliersCount = 4;
00344         }
00345 
00346         // Use OpenCV3 version of solvePnPRansac in OpenCV2.
00347         // FIXME: we should use this version of solvePnPRansac in newer 3.3.1 too, which seems a lot less stable!?!? Why!?
00348         cv3::solvePnPRansac(
00349                         objectPoints,
00350                         imagePoints,
00351                         cameraMatrix,
00352                         distCoeffs,
00353                         rvec,
00354                         tvec,
00355                         useExtrinsicGuess,
00356                         iterationsCount,
00357                         reprojectionError,
00358                         0.99, // confidence
00359                         inliers,
00360                         flags);
00361 
00362         float inlierThreshold = reprojectionError;
00363         if((int)inliers.size() >= minInliersCount && refineIterations>0)
00364         {
00365                 float error_threshold = inlierThreshold;
00366                 int refine_iterations = 0;
00367                 bool inlier_changed = false, oscillating = false;
00368                 std::vector<int> new_inliers, prev_inliers = inliers;
00369                 std::vector<size_t> inliers_sizes;
00370                 //Eigen::VectorXf new_model_coefficients = model_coefficients;
00371                 cv::Mat new_model_rvec = rvec;
00372                 cv::Mat new_model_tvec = tvec;
00373 
00374                 do
00375                 {
00376                         // Get inliers from the current model
00377                         std::vector<cv::Point3f> opoints_inliers(prev_inliers.size());
00378                         std::vector<cv::Point2f> ipoints_inliers(prev_inliers.size());
00379                         for(unsigned int i=0; i<prev_inliers.size(); ++i)
00380                         {
00381                                 opoints_inliers[i] = objectPoints[prev_inliers[i]];
00382                                 ipoints_inliers[i] = imagePoints[prev_inliers[i]];
00383                         }
00384 
00385                         UDEBUG("inliers=%d refine_iterations=%d, rvec=%f,%f,%f tvec=%f,%f,%f", (int)prev_inliers.size(), refine_iterations,
00386                                         *new_model_rvec.ptr<double>(0), *new_model_rvec.ptr<double>(1), *new_model_rvec.ptr<double>(2),
00387                                         *new_model_tvec.ptr<double>(0), *new_model_tvec.ptr<double>(1), *new_model_tvec.ptr<double>(2));
00388 
00389                         // Optimize the model coefficients
00390                         cv::solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix, distCoeffs, new_model_rvec, new_model_tvec, true, flags);
00391                         inliers_sizes.push_back(prev_inliers.size());
00392 
00393                         UDEBUG("rvec=%f,%f,%f tvec=%f,%f,%f",
00394                                         *new_model_rvec.ptr<double>(0), *new_model_rvec.ptr<double>(1), *new_model_rvec.ptr<double>(2),
00395                                         *new_model_tvec.ptr<double>(0), *new_model_tvec.ptr<double>(1), *new_model_tvec.ptr<double>(2));
00396 
00397                         // Select the new inliers based on the optimized coefficients and new threshold
00398                         std::vector<float> err = computeReprojErrors(objectPoints, imagePoints, cameraMatrix, distCoeffs, new_model_rvec, new_model_tvec, error_threshold, new_inliers);
00399                         UDEBUG("RANSAC refineModel: Number of inliers found (before/after): %d/%d, with an error threshold of %f.",
00400                                         (int)prev_inliers.size (), (int)new_inliers.size (), error_threshold);
00401 
00402                         if ((int)new_inliers.size() < minInliersCount)
00403                         {
00404                                 ++refine_iterations;
00405                                 if (refine_iterations >= refineIterations)
00406                                 {
00407                                         break;
00408                                 }
00409                                 continue;
00410                         }
00411 
00412                         // Estimate the variance and the new threshold
00413                         float m = uMean(err.data(), err.size());
00414                         float variance = uVariance(err.data(), err.size());
00415                         error_threshold = std::min(inlierThreshold, refineSigma * float(sqrt(variance)));
00416 
00417                         UDEBUG ("RANSAC refineModel: New estimated error threshold: %f (variance=%f mean=%f) on iteration %d out of %d.",
00418                                   error_threshold, variance, m, refine_iterations, refineIterations);
00419                         inlier_changed = false;
00420                         std::swap (prev_inliers, new_inliers);
00421 
00422                         // If the number of inliers changed, then we are still optimizing
00423                         if (new_inliers.size () != prev_inliers.size ())
00424                         {
00425                                 // Check if the number of inliers is oscillating in between two values
00426                                 if ((int)inliers_sizes.size () >= minInliersCount)
00427                                 {
00428                                         if (inliers_sizes[inliers_sizes.size () - 1] == inliers_sizes[inliers_sizes.size () - 3] &&
00429                                         inliers_sizes[inliers_sizes.size () - 2] == inliers_sizes[inliers_sizes.size () - 4])
00430                                         {
00431                                                 oscillating = true;
00432                                                 break;
00433                                         }
00434                                 }
00435                                 inlier_changed = true;
00436                                 continue;
00437                         }
00438 
00439                         // Check the values of the inlier set
00440                         for (size_t i = 0; i < prev_inliers.size (); ++i)
00441                         {
00442                                 // If the value of the inliers changed, then we are still optimizing
00443                                 if (prev_inliers[i] != new_inliers[i])
00444                                 {
00445                                         inlier_changed = true;
00446                                         break;
00447                                 }
00448                         }
00449                 }
00450                 while (inlier_changed && ++refine_iterations < refineIterations);
00451 
00452                 // If the new set of inliers is empty, we didn't do a good job refining
00453                 if ((int)prev_inliers.size() < minInliersCount)
00454                 {
00455                         UWARN ("RANSAC refineModel: Refinement failed: got very low inliers (%d)!", (int)prev_inliers.size());
00456                 }
00457 
00458                 if (oscillating)
00459                 {
00460                         UDEBUG("RANSAC refineModel: Detected oscillations in the model refinement.");
00461                 }
00462 
00463                 std::swap (inliers, new_inliers);
00464                 rvec = new_model_rvec;
00465                 tvec = new_model_tvec;
00466         }
00467 
00468 }
00469 
00470 }
00471 
00472 }


rtabmap
Author(s): Mathieu Labbe
autogenerated on Thu Jun 6 2019 21:59:32