pe2d.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2009, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Willow Garage nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include <posest/pe2d.h>
00036 #include <posest/planarSFM.h>
00037 #include <highgui.h>
00038 //#include <features_2d/draw.h>
00039 #include <posest/pnp_ransac.h>
00040 #include <posest/cxeigen.hpp>
00041 
00042 #include <iostream>
00043 #include <algorithm>
00044 
00045 using namespace cv;
00046 
00047 static void camParams2Mat(const fc::CamParams& params, Mat& intrinsics)
00048 {
00049   intrinsics = Mat::eye(3, 3, CV_32F);
00050   intrinsics.at<float> (0, 0) = params.fx;
00051   intrinsics.at<float> (1, 1) = params.fy;
00052   intrinsics.at<float> (0, 2) = params.cx;
00053   intrinsics.at<float> (1, 2) = params.cy;
00054 };
00055 
00056 namespace pe
00057 {
00058 void drawMatches(const Mat& img, const vector<KeyPoint>& kpts1, const vector<KeyPoint>& kpts2,
00059                  const vector<cv::DMatch>& matches, Mat& display)
00060 {
00061   img.copyTo(display);
00062   for (size_t i = 0; i < matches.size(); i++)
00063   {
00064     circle(display, kpts1[matches[i].queryIdx].pt, 3, CV_RGB(255, 0, 0));
00065     line(display, kpts1[matches[i].queryIdx].pt, kpts2[matches[i].trainIdx].pt, CV_RGB(0, 0, 255));
00066     line(display, kpts1[matches[i].queryIdx].pt, kpts2[matches[i].trainIdx].pt, CV_RGB(0, 0, 255));
00067   }
00068 }
00069 ;
00070 
00071 void extractPnPData(const fc::Frame& frame1, const fc::Frame& frame2, const std::vector<cv::DMatch> &matches, vector<
00072     cv::Point2f>& imagePoints, vector<cv::Point3f>& objectPoints)
00073 {
00074   std::cout << "extractPnPData: frame1.pts.size() = " << frame1.pts.size() << std::endl;
00075 //  float zsum = 0, zsum2 = 0;
00076   for (size_t i = 0; i < matches.size(); i++)
00077   {
00078     int i1 = matches[i].queryIdx;
00079     int i2 = matches[i].trainIdx;
00080 
00081     if (frame1.goodPts[i1] == false)
00082       continue;
00083 
00084     const Eigen::Vector4d& p = frame1.pts[i1];
00085 
00086     //printf("i1 = %d i2 = %d\n", i1, i2);
00087     //printf("%f %f %f\n", p(0), p(1), p(2));
00088     objectPoints.push_back(Point3f(p(0), p(1), p(2)));
00089     imagePoints.push_back(frame2.kpts[i2].pt);
00090 
00091 //    zsum += p(2);
00092 //    zsum2 += p(2)*p(2);
00093   }
00094 
00095   std::cout << "extractPnPData: good points " << imagePoints.size() << std::endl;
00096 
00097 //  int count = objectPoints.size();
00098 //  printf("ExtractPnPData: mean z = %f, std z = %f\n", zsum/count, sqrt(zsum2/count - zsum*zsum/(count*count)));
00099 };
00100 
00101 int PoseEstimator2d::estimate(const fc::Frame& frame1, const fc::Frame& frame2)
00102 {
00103   if(testMode == false)
00104   {
00105     return PoseEstimator::estimate(frame1, frame2);
00106   }
00107   else
00108   {
00109     return estimate(frame1, frame2, testMatches);
00110   }
00111 }
00112 
00113 void filterMatchesOpticalFlow(const fc::Frame& frame1, const fc::Frame& frame2, std::vector<cv::DMatch>& matches)
00114 {
00115   vector<Point2f> points1, points2, points2_of;
00116   for(size_t i = 0; i < matches.size(); i++)
00117   {
00118     points1.push_back(frame1.kpts[matches[i].queryIdx].pt);
00119     points2.push_back(frame2.kpts[matches[i].trainIdx].pt);
00120   }
00121 
00122   vector<unsigned char> status;
00123   vector<float> err;
00124 #if 0
00125   calcOpticalFlowPyrLK(frame1.img, frame2.img, points1, points2_of, status, err, cvSize(10, 10));
00126 #else
00127 #endif
00128   vector<cv::DMatch> matches_filtered;
00129   const float maxError = 2.0f;
00130   for(size_t i = 0; i < matches.size(); i++)
00131   {
00132     if(!status[i]) continue;
00133     if(norm(points2[matches[i].trainIdx] - points2_of[matches[i].trainIdx]) < maxError)
00134     {
00135       matches_filtered.push_back(matches[i]);
00136     }
00137   }
00138 
00139   matches = matches_filtered;
00140 
00141 #if 1
00142   Mat display;
00143   pe::drawMatches(frame1.img, frame1.kpts, frame2.kpts, matches_filtered, display);
00144   namedWindow("1", 1);
00145   imshow("1", display);
00146   waitKey();
00147 #endif
00148 }
00149 
00150 void filterMatchesByDistance(std::vector<cv::DMatch>& matches, float percentile = 0.1f)
00151 {
00152   std::sort(matches.begin(), matches.end());
00153 
00154   // matches.resize does not compile due to absence of Match default constructor
00155   vector<cv::DMatch> filtered;
00156   for(size_t i = 0; i < (size_t)floor(matches.size()*percentile); i++)
00157   {
00158     filtered.push_back(matches[i]);
00159   }
00160 
00161   matches = filtered;
00162 }
00163 
00164 int PoseEstimator2d::estimate(const fc::Frame& frame1, const fc::Frame& frame2, const std::vector<cv::DMatch>& _matches)
00165 {
00166   vector<cv::DMatch> matches = _matches;
00167   filterMatchesByDistance(matches, 0.5f);
00168 
00169   std::cout << "called PoseEstimator2d::estimate for frames " << frame1.frameId << " and " << frame2.frameId
00170         << std::endl;
00171 
00172   std::cout << "Number of points: " << frame1.kpts.size() << ", number of matches: " << matches.size() << std::endl;
00173 
00174   inliers.clear();
00175 
00176 //  filterMatchesOpticalFlow(frame1, frame2, matches);
00177 //  std::cout << "Number of matches after optical flow filtering: " << matches.size() << std::endl;
00178 
00179 #if 0
00180   Mat display;
00181   vector<int> sample_indices;
00182   sample(matches.size(), 50, sample_indices);
00183   vector<Match> sample_matches;
00184   vectorSubset(matches, sample_indices, sample_matches);
00185   features_2d::drawMatches(frame1.img, frame1.kpts, frame2.img, frame2.kpts, sample_matches, display);
00186   //pe::drawMatches(frame1.img, frame1.kpts, frame2.kpts, matches, display);
00187   namedWindow("1", 1);
00188   imshow("1", display);
00189   waitKey(0);
00190 #endif
00191 
00192   vector<Point2f> image_points1, image_points2;
00193   matchesFromIndices(frame1.kpts, frame2.kpts, matches, image_points1, image_points2);
00194 
00195   vector<Point2f> full_image_points1 = image_points1; // sfm now filters the array of points, need to keep the full version
00196   vector<Point2f> full_image_points2 = image_points2;
00197   Mat rvec, tvec, intrinsics;
00198   camParams2Mat(frame1.cam, intrinsics);
00199 
00200   if (image_points1.size() < 4)
00201   {
00202     return 0;
00203   }
00204 
00205   const int minGoodPts = 30;
00206   const float inlierPoseReprojError = 1.0f;
00207   const float inlierReprojError = 1.0f;
00208   const float maxInlierDist = 100.0f;
00209 
00210   // extract all good pts for PnP solver
00211   vector<Point2f> imagePoints;
00212   vector<Point3f> objectPoints;
00213   extractPnPData(frame1, frame2, matches, imagePoints, objectPoints);
00214   if(!initialized_)
00215   {
00216     // too few correspondences with valid 3d points, run structure from motion
00217     //std::cout << "The number of 3d points " << imagePoints.size() << ", running SFM" << std::endl;
00218 //    printf("number of source points: %d\n", image_points1.size());
00219     std::cout << "Running SFM" << std::endl;
00220     SFMwithSBA(intrinsics, image_points1, image_points2, rvec, tvec, 6.0);
00221 //    printf("number of inliers: %d\n", image_points1.size());
00222 
00223     // normalize translation vector
00224     tvec = tvec/norm(tvec);
00225 
00226     usedMethod = SFM;
00227   }
00228   else if(imagePoints.size() > minGoodPts)
00229   {
00230     std::cout << "Running PnP" << std::endl;
00231 //    std::cout << "The number of 3d points " << imagePoints.size() << ", running PnP" << std::endl;
00232     const int minIterCount = 10000;
00233     solvePnPRansac(objectPoints, imagePoints, intrinsics, Mat::zeros(5, 1, CV_32F), rvec, tvec, false, minIterCount, inlierPoseReprojError, minGoodPts);
00234     Mat _rvec, _tvec;
00235     rvec.convertTo(_rvec, CV_32F);
00236     tvec.convertTo(_tvec, CV_32F);
00237     rvec = _rvec;
00238     tvec = _tvec;
00239 
00240     usedMethod = PnP;
00241   }
00242   else
00243   {
00244     return 0;
00245   }
00246 
00247   std::cout << "finished pose estimation" << std::endl;
00248 
00249   setPose(rvec, tvec);
00250 
00251   Mat R;
00252   Rodrigues(rvec, R);
00253   vector<Point3f> cloud;
00254   vector<bool> valid;
00255   reprojectPoints(intrinsics, R, tvec, full_image_points1, full_image_points2, cloud, valid);
00256 
00257   // compute inliers for pose validation
00258   vector<bool> valid_pose = valid;
00259   filterInliers(cloud, full_image_points1, full_image_points2, R, tvec, intrinsics, inlierPoseReprojError, valid_pose);
00260 
00261   // compute inliers for 3d point cloud
00262   filterInliers(cloud, full_image_points1, full_image_points2, R, tvec, intrinsics, inlierReprojError, valid);
00263 
00264   float avgDist = 0;
00265   float avgZ = 0;
00266   int count = 0, count1 = 0, count2 = 0;
00267   for(size_t i = 0; i < cloud.size(); i++)
00268   {
00269     if(!valid_pose[i]) continue;
00270     const float infDist = 500.0f*norm(tvec);
00271 
00272     float dist2 = cloud[i].dot(cloud[i]);
00273     if(cvIsNaN(dist2)) continue;
00274 
00275     if(dist2 > infDist*infDist)
00276     {
00277       count1++;
00278       continue;
00279     }
00280 
00281     avgDist += dist2;
00282     avgZ += cloud[i].z;
00283     count++;
00284 
00285     if(cloud[i].z < 100) count2++;
00286   }
00287   avgDist = sqrt(avgDist/count)/norm(tvec);
00288   avgZ /= count;
00289   const float maxAvgDist = 50.0f;
00290   const float maxAvgZ = 100.0f;
00291 //  std::cout << "The number of inliers: " << count << std::endl;
00292 //  std::cout << "The number of inf points: " << count1 << std::endl;
00293 //  std::cout << "The number of close points: " << count2 << std::endl;
00294 
00295 #if 1
00296   if(getMethod() == SFM && !testMode && avgDist > maxAvgDist)
00297   {
00298     printf("pe::estimate: average point cloud z %f is higher than maximum acceptable %f\n", avgDist, maxAvgDist);
00299     return 0;
00300   }
00301 #endif
00302 
00303   Mat R_inv, T_inv;
00304   R_inv = R.t();
00305   T_inv = -R_inv * tvec;
00306 
00307   // convert cloud into frame format
00308   assert(matches.size() == cloud.size());
00309   fc::Frame& _frame1 = const_cast<fc::Frame&> (frame1);
00310   fc::Frame& _frame2 = const_cast<fc::Frame&> (frame2);
00311 
00312   if(getMethod() == SFM)
00313   {
00314     _frame1.pts.resize(frame1.kpts.size());
00315     _frame1.goodPts.assign(frame1.kpts.size(), false);
00316   }
00317   else
00318   {
00319     assert(frame1.pts.size() == frame1.goodPts.size() == frame1.kpts.size());
00320   }
00321   _frame2.pts.resize(frame2.kpts.size());
00322   _frame2.goodPts.assign(frame2.kpts.size(), false);
00323 
00324   assert(valid.size() == cloud.size() && cloud.size() == matches.size());
00325   int goodCount = 0;
00326   double minz = 1e10, maxz = 0;
00327   vector<cv::DMatch> _inliers;
00328 
00329   float z1sum = 0, z2sum = 0, z1sum2 = 0, z2sum2 = 0;
00330   int countFiltered = 0;
00331   for (size_t i = 0; i < matches.size(); i++)
00332   {
00333     if (!valid[i])
00334       continue;
00335 
00336     if(cvIsNaN(cloud[i].x) || cvIsNaN(cloud[i].y) || cvIsNaN(cloud[i].z))
00337       continue;
00338 
00339 
00340     minz = MIN(minz, cloud[i].z);
00341     maxz = MAX(maxz, cloud[i].z);
00342 
00343 #if 1
00344     const float maxDist = 100.0f;
00345     const float minDist = 10.0f;
00346     float dist = norm(cloud[i])/norm(tvec);
00347     if(!testMode && dist < minDist || dist > maxDist || cloud[i].z < 0)
00348     {
00349       countFiltered++;
00350       continue;
00351     }
00352 #endif
00353 
00354     inliers.push_back(matches[i]);
00355     int i1 = matches[i].queryIdx;
00356     int i2 = matches[i].trainIdx;
00357 
00358     if(norm(cloud[i]) < 10)
00359     {
00360       _inliers.push_back(matches[i]);
00361     }
00362 
00363     if(getMethod() == SFM)
00364     {
00365       _frame1.pts[i1] = Eigen::Vector4d(cloud[i].x, cloud[i].y, cloud[i].z, 1.0);
00366       _frame1.goodPts[i1] = true;
00367     }
00368 
00369     _frame2.goodPts[i2] = true;
00370 
00371     // convert to frame 2 coordinate system
00372     Mat p(3, 1, CV_32F, &cloud[i]);
00373 #if 0
00374     Mat r = R_inv * p + T_inv;
00375 #else
00376     Mat r = R * p + tvec;
00377 #endif
00378 
00379     Point3f _r = *r.ptr<Point3f> (0);
00380     //printf("assigning point %d to %f,%f,%f\n", i2, _r.x, _r.y, _r.z);
00381     _frame2.pts[i2] = Eigen::Vector4d(_r.x, _r.y, _r.z, 1.0);
00382     goodCount++;
00383 
00384     assert(cloud[i].z >= 0 && _r.z >= 0);
00385 
00386     z1sum += _frame1.pts[i1](2);
00387     z1sum2 += _frame1.pts[i1](2)*_frame1.pts[i1](2);
00388     z2sum += _frame2.pts[i2](2);
00389     z2sum2 += _frame2.pts[i2](2)*_frame2.pts[i2](2);
00390   }
00391 
00392   if(goodCount < minGoodPts)
00393   {
00394     printf("Returning a small amount of good points: %d, minGoodPts = %d\n", goodCount, minGoodPts);
00395     return 0;
00396   }
00397 
00398   printf("minz = %f, maxz = %f\n", minz, maxz);
00399   printf("Frame1: mean z = %f, std z = %f\n", z1sum/goodCount, sqrt(z1sum2/goodCount - z1sum*z1sum/(goodCount*goodCount)));
00400   printf("Frame2: mean z = %f, std z = %f\n", z2sum/goodCount, sqrt(z2sum2/goodCount - z2sum*z2sum/(goodCount*goodCount)));
00401 
00402   std::cout << "Input number of matches " << (int)full_image_points1.size() << ", inliers " << (int)inliers.size()
00403       << std::endl;
00404   std::cout << "goodCount " << goodCount << std::endl;
00405   std::cout << "filtered inliers: " << countFiltered << std::endl;
00406   std::cout << "_inliers.size() = " << _inliers.size() << std::endl;
00407 
00408 #if 1
00409   if(!frame1.img.empty())
00410   {
00411 #if 1
00412     Mat display1, display2;
00413     vector<cv::DMatch> match_samples;
00414     vector<int> match_indices;
00415     sample(matches.size(), 100, match_indices);
00416     vectorSubset(matches, match_indices, match_samples);
00417     drawMatches(frame1.img, frame1.kpts, frame2.img, frame2.kpts, match_samples, display1);
00418     //pe::drawMatches(frame1.img, frame1.kpts, frame2.kpts, inliers, display1);
00419     namedWindow("1", 1);
00420     imshow("1", display1);
00421 
00422     char buf1[1024];
00423     sprintf(buf1, "matches%04d.jpg", frame1.frameId);
00424     imwrite(buf1, display1);
00425 
00426 #if 0
00427     features_2d::drawMatches(frame1.img, frame1.kpts, frame2.img, frame2.kpts, _inliers, display2);
00428     namedWindow("2", 1);
00429     imshow("2", display2);
00430 #endif
00431 #endif
00432 
00434     Mat img_matches;
00435     vector<cv::DMatch> inlier_sample;
00436     vector<int> inlier_indices;
00437     sample(inliers.size(), 50, inlier_indices);
00438     vectorSubset(inliers, inlier_indices, inlier_sample);
00439 #if 1
00440     drawMatches(frame1.img, frame1.kpts, frame2.img, frame2.kpts, inlier_sample, img_matches);
00441     namedWindow("matches", 1);
00442     imshow("matches", img_matches);
00443 
00444     char buf[1024];
00445     sprintf(buf, "img%04d.jpg", frame1.frameId);
00446     imwrite(buf, img_matches);
00447 #endif
00448 
00449 #if 0
00450     namedWindow("frame1", 1);
00451     Mat img1;
00452     cv::drawKeypoints(frame1.img, frame1.kpts, img1);
00453     imshow("frame1", img1);
00454 
00455     namedWindow("frame2", 1);
00456     Mat img2;
00457     cv::drawKeypoints(frame2.img, frame2.kpts, img2);
00458     imshow("frame2", img2);
00459 #endif
00460   }
00461 //  waitKey(0);
00462 #endif
00463 
00464   initialized_ = true;
00465   return goodCount;//(int)inliers.size();
00466 
00467 };
00468 
00469 void PoseEstimator2d::setPose(const cv::Mat& rvec, const cv::Mat& tvec)
00470 {
00471   Mat R, tvec_inv;
00472   Rodrigues(rvec, R);
00473 
00474   // invert the pose for the frame
00475   R = R.t();
00476   tvec_inv = -R * tvec;
00477   //  cv2eigen(R, rot);
00478   //  cv2eigen(tvec_inv, trans);
00479 
00480   dumpFltMat("tvec in SetPose", tvec);
00481   dumpFltMat("rvec in SetPose", rvec);
00482 
00483 //  std::cout << "---------------------" << std::endl;
00484 //  std::cout << "translation: " << trans << std::endl;
00485 //  std::cout << "---------------------" << std::endl;
00486 };
00487 
00488 };


posest
Author(s): Kurt Konolige
autogenerated on Thu Jan 2 2014 12:12:17