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 };