00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 #include <posest/pe2d.h>
00036 #include <posest/planarSFM.h>
00037 #include <highgui.h>
00038 
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 
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     
00087     
00088     objectPoints.push_back(Point3f(p(0), p(1), p(2)));
00089     imagePoints.push_back(frame2.kpts[i2].pt);
00090 
00091 
00092 
00093   }
00094 
00095   std::cout << "extractPnPData: good points " << imagePoints.size() << std::endl;
00096 
00097 
00098 
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   
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 
00177 
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   
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; 
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   
00211   vector<Point2f> imagePoints;
00212   vector<Point3f> objectPoints;
00213   extractPnPData(frame1, frame2, matches, imagePoints, objectPoints);
00214   if(!initialized_)
00215   {
00216     
00217     
00218 
00219     std::cout << "Running SFM" << std::endl;
00220     SFMwithSBA(intrinsics, image_points1, image_points2, rvec, tvec, 6.0);
00221 
00222 
00223     
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 
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   
00258   vector<bool> valid_pose = valid;
00259   filterInliers(cloud, full_image_points1, full_image_points2, R, tvec, intrinsics, inlierPoseReprojError, valid_pose);
00260 
00261   
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 
00292 
00293 
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   
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     
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     
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     
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 
00462 #endif
00463 
00464   initialized_ = true;
00465   return goodCount;
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   
00475   R = R.t();
00476   tvec_inv = -R * tvec;
00477   
00478   
00479 
00480   dumpFltMat("tvec in SetPose", tvec);
00481   dumpFltMat("rvec in SetPose", rvec);
00482 
00483 
00484 
00485 
00486 };
00487 
00488 };