ndt_frame.cpp
Go to the documentation of this file.
00001 //#include <sba/sba.h>
00002 #include <Eigen/SVD>
00003 #include <Eigen/LU>
00004 #include <iostream>
00005 #include <limits>
00006 #include "opencv2/core/core.hpp"
00007 #include "opencv2/features2d/features2d.hpp"
00008 #include "opencv2/highgui/highgui.hpp"
00009 #include <ndt_feature_reg/ndt_frame.h>
00010 
00011 namespace ndt_feature_reg
00012 {
00013 
00014 PoseEstimator::PoseEstimator(int NRansac,
00015         double maxidx, double maxidd)
00016 {
00017     numRansac = NRansac;
00018     maxInlierXDist2 = maxidx*maxidx;
00019     maxInlierDDist2 = maxidd*maxidd;
00020     rot.setIdentity();
00021     trans.setZero();
00022 
00023     // matcher
00024     matcher = new cv::BFMatcher(cv::NORM_L2);
00025     wx = 92;
00026     wy = 48;
00027     windowed = false;
00028     projectMatches = true;
00029 
00030     maxDist = std::numeric_limits<double>::max();
00031     minDist = -1.;
00032 }
00033 
00034 void
00035 PoseEstimator::matchFrames(const NDTFrame& f0, const NDTFrame& f1, std::vector<cv::DMatch>& fwd_matches)
00036 {
00037     cv::Mat mask;
00038     if (windowed)
00039         mask = cv::windowedMatchingMask(f0.kpts, f1.kpts, wx, wy);
00040 
00041     //require at least 3 kpts each
00042     if(f0.kpts.size() > 3 && f1.kpts.size() > 3) 
00043     {
00044         matcher->match(f0.dtors, f1.dtors, fwd_matches, mask);
00045     }
00046 }
00047 
00048 
00049 size_t PoseEstimator::estimate(const NDTFrame &f0, const NDTFrame &f1)
00050 {
00051     // set up match lists
00052     matches.clear();
00053     inliers.clear();
00054 
00055 //     std::cout<<"N KPTS: "<<f0.kpts.size()<<" "<<f1.kpts.size()<<std::endl;
00056 //     std::cout<<"descriptors: "<<f0.dtors.size().width<<"x"<<f0.dtors.size().height<<" "
00057 //                             <<f1.dtors.size().width<<"x"<<f1.dtors.size().height<<std::endl;
00058     // do forward and reverse matches
00059     std::vector<cv::DMatch> fwd_matches, rev_matches;
00060     matchFrames(f0, f1, fwd_matches);
00061     matchFrames(f1, f0, rev_matches);
00062 //     printf("**** Forward matches: %d, reverse matches: %d ****\n", (int)fwd_matches.size(), (int)rev_matches.size());
00063 
00064     // combine unique matches into one list
00065     for (int i = 0; i < (int)fwd_matches.size(); ++i)
00066     {
00067         if (fwd_matches[i].trainIdx >= 0)
00068             matches.push_back( cv::DMatch(i, fwd_matches[i].trainIdx, fwd_matches[i].distance) );
00069         //matches.push_back( cv::DMatch(fwd_matches[i].queryIdx, fwd_matches[i].trainIdx, fwd_matches[i].distance) );
00070     }
00071     for (int i = 0; i < (int)rev_matches.size(); ++i)
00072     {
00073         if (rev_matches[i].trainIdx >= 0 && i != fwd_matches[rev_matches[i].trainIdx].trainIdx)
00074             matches.push_back( cv::DMatch(rev_matches[i].trainIdx, i, rev_matches[i].distance) );
00075         //matches.push_back( cv::DMatch(rev_matches[i].trainIdx, rev_matches[i].queryIdx, rev_matches[i].distance) );
00076     }
00077 //     printf("**** Total unique matches: %d ****\n", (int)matches.size());
00078 
00079     // do it
00080     return estimate(f0, f1, matches);
00081 }
00082 
00083 
00084 size_t PoseEstimator::estimate(const NDTFrame& f0, const NDTFrame& f1,
00085         const std::vector<cv::DMatch> &matches)
00086 {
00087     // convert keypoints in match to 3d points
00088     std::vector<Eigen::Vector4d, Eigen::aligned_allocator<Eigen::Vector4d> > p0; // homogeneous coordinates
00089     std::vector<Eigen::Vector4d, Eigen::aligned_allocator<Eigen::Vector4d> > p1;
00090 
00091     int nmatch = matches.size();
00092     //srand(getDoubleTime());
00093 
00094     // set up data structures for fast processing
00095     // indices to good matches
00096     std::vector<int> m0, m1;
00097     for (int i=0; i<nmatch; i++)
00098     {
00099         m0.push_back(matches[i].queryIdx);
00100         m1.push_back(matches[i].trainIdx);
00101         //std::cout<<m0[i]<<" "<<m1[i]<<std::endl;
00102     }
00103 
00104     nmatch = m0.size();
00105 
00106     if (nmatch < 3) return 0;   // can't do it...
00107 
00108     int bestinl = 0;
00109 
00110     // RANSAC loop
00111 //#pragma omp parallel for shared( bestinl )
00112     for (int i=0; i<numRansac; i++)
00113     {
00114         //std::cout << "ransac loop : " << i << std::endl;
00115         // find a candidate
00116         int a=rand()%nmatch;
00117         int b = a;
00118         while (a==b)
00119             b=rand()%nmatch;
00120         int c = a;
00121         while (a==c || b==c)
00122             c=rand()%nmatch;
00123 
00124         int i0a = m0[a];
00125         int i0b = m0[b];
00126         int i0c = m0[c];
00127         int i1a = m1[a];
00128         int i1b = m1[b];
00129         int i1c = m1[c];
00130 
00131         if (i0a == i0b || i0a == i0c || i0b == i0c ||
00132                 i1a == i1b || i1a == i1c || i1b == i1c)
00133             continue;
00134 
00135         //std::cout<<a<<" "<<b<<" "<<c<<std::endl;
00136         //std::cout<<i0a<<" "<<i0b<<" "<<i0c<<std::endl;
00137         //std::cout<<i1a<<" "<<i1b<<" "<<i1c<<std::endl;
00138 
00139         // get centroids
00140         Eigen::Vector3d p0a = f0.pts[i0a].head(3);
00141         Eigen::Vector3d p0b = f0.pts[i0b].head(3);
00142         Eigen::Vector3d p0c = f0.pts[i0c].head(3);
00143         Eigen::Vector3d p1a = f1.pts[i1a].head(3);
00144         Eigen::Vector3d p1b = f1.pts[i1b].head(3);
00145         Eigen::Vector3d p1c = f1.pts[i1c].head(3);
00146 
00147         Eigen::Vector3d c0 = (p0a+p0b+p0c)*(1.0/3.0);
00148         Eigen::Vector3d c1 = (p1a+p1b+p1c)*(1.0/3.0);
00149 
00150         //std::cout<<c0.transpose()<<std::endl;
00151         //std::cout<<c1.transpose()<<std::endl;
00152         // subtract out
00153         p0a -= c0;
00154         p0b -= c0;
00155         p0c -= c0;
00156         p1a -= c1;
00157         p1b -= c1;
00158         p1c -= c1;
00159 
00160         Eigen::Matrix3d H = p1a*p0a.transpose() + p1b*p0b.transpose() +
00161                             p1c*p0c.transpose();
00162 
00163         // do the SVD thang
00164         Eigen::JacobiSVD<Eigen::Matrix3d> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
00165         Eigen::Matrix3d V = svd.matrixV();
00166         Eigen::Matrix3d R = V * svd.matrixU().transpose();
00167         double det = R.determinant();
00168         //ntot++;
00169         if (det < 0.0)
00170         {
00171             //nneg++;
00172             V.col(2) = V.col(2)*-1.0;
00173             R = V * svd.matrixU().transpose();
00174         }
00175         Eigen::Vector3d tr = c0-R*c1;    // translation
00176 
00177         // transformation matrix, 3x4
00178         Eigen::Matrix<double,3,4> tfm;
00179         //        tfm.block<3,3>(0,0) = R.transpose();
00180         //        tfm.col(3) = -R.transpose()*tr;
00181         tfm.block<3,3>(0,0) = R;
00182         tfm.col(3) = tr;
00183 
00184 #if 0
00185         // find inliers, based on image reprojection
00186         int inl = 0;
00187         for (int i=0; i<nmatch; i++)
00188         {
00189             Vector3d pt = tfm*f1.pts[m1[i]];
00190             Vector3d ipt = f0.cam2pix(pt);
00191             const cv::KeyPoint &kp = f0.kpts[m0[i]];
00192             double dx = kp.pt.x - ipt.x();
00193             double dy = kp.pt.y - ipt.y();
00194             double dd = f0.disps[m0[i]] - ipt.z();
00195             if (dx*dx < maxInlierXDist2 && dy*dy < maxInlierXDist2 &&
00196                     dd*dd < maxInlierDDist2)
00197             {
00198                 inl+=(int)sqrt(ipt.z()); // clever way to weight closer points
00199 //               inl+=(int)sqrt(ipt.z()/matches[i].distance);
00200 //               cout << "matches[i].distance : " << matches[i].distance << endl;
00201 //               inl++;
00202             }
00203         }
00204 #endif
00205         int inl = 0;
00206         for (int i=0; i<nmatch; i++)
00207         {
00208             Eigen::Vector3d pt1 = tfm*f1.pts[m1[i]];
00209             Eigen::Vector3d pt0 = f0.pts[m0[i]].head(3);
00210 
00211 //             double z = fabs(pt1.z() - pt0.z())*0.5;
00212             double z = pt1.z();
00213             double dx = pt1.x() - pt0.x();
00214             double dy = pt1.y() - pt0.y();
00215             double dd = pt1.z() - pt0.z();
00216 
00217             if (projectMatches)
00218             {
00219                 // The central idea here is to divide by the distance (this is essentially what cam2pix does).
00220                 dx = dx / z;
00221                 dy = dy / z;
00222             }
00223             if (dx*dx < maxInlierXDist2 && dy*dy < maxInlierXDist2 &&
00224                     dd*dd < maxInlierDDist2)
00225             {
00226 //----              inl+=(int)sqrt(pt0.z()); // clever way to weight closer points
00227 //               inl+=(int)sqrt(ipt.z()/matches[i].distance);
00228 //               cout << "matches[i].distance : " << matches[i].distance << endl;
00229                 inl++;
00230             }
00231         }
00232 
00233 //#pragma omp critical
00234         if (inl > bestinl)
00235         {
00236             bestinl = inl;
00237             rot = R;
00238             trans = tr;
00239 //             std::cout << "bestinl : " << bestinl << std::endl;
00240         }
00241     }
00242 
00243     //printf("Best inliers: %d\n", bestinl);
00244     //printf("Total ransac: %d  Neg det: %d\n", ntot, nneg);
00245 
00246     // reduce matches to inliers
00247     std::vector<cv::DMatch> inls;    // temporary for current inliers
00248     inliers.clear();
00249     Eigen::Matrix<double,3,4> tfm;
00250     tfm.block<3,3>(0,0) = rot;
00251     tfm.col(3) = trans;
00252 
00253     //std::cout<<"f0: "<<f0.pts.size()<<" "<<f0.kpts.size()<<" "<<f0.pc_kpts.size()<<std::endl;
00254     //std::cout<<"f1: "<<f1.pts.size()<<" "<<f1.kpts.size()<<" "<<f1.pc_kpts.size()<<std::endl;
00255 
00256     nmatch = matches.size();
00257     for (int i=0; i<nmatch; i++)
00258     {
00259         Eigen::Vector3d pt1 = tfm*f1.pts[matches[i].trainIdx];
00260         //Eigen::Vector3d pt1_unchanged = f1.pts[matches[i].trainIdx].head(3);
00261         //Vector3d pt1 = pt1_unchanged;
00262 #if 0
00263         Vector3d ipt = f0.cam2pix(pt);
00264         const cv::KeyPoint &kp = f0.kpts[matches[i].queryIdx];
00265         double dx = kp.pt.x - ipt.x();
00266         double dy = kp.pt.y - ipt.y();
00267         double dd = f0.disps[matches[i].queryIdx] - ipt.z();
00268 #endif
00269         Eigen::Vector3d pt0 = f0.pts[matches[i].queryIdx].head(3);
00270 
00271         //double z = fabs(pt1.z() - pt0.z())*0.5;
00272         double z = pt1.z();
00273         double dx = pt1.x() - pt0.x();
00274         double dy = pt1.y() - pt0.y();
00275         double dd = pt1.z() - pt0.z();
00276 
00277         if (projectMatches)
00278         {
00279             // The central idea here is to divide by the distance (this is essentially what cam2pix does).
00280             dx = dx / z;
00281             dy = dy / z;
00282         }
00283 
00284         if (dx*dx < maxInlierXDist2 && dy*dy < maxInlierXDist2 &&
00285                 dd*dd < maxInlierDDist2)
00286         {
00287             if (z < maxDist && z > minDist)
00288 
00289 //             if (fabs(f0.kpts[matches[i].queryIdx].pt.y - f1.kpts[matches[i].trainIdx].pt.y) > 300)
00290             {
00291 //                 std::cout << " ---------- " << dx << "," << dy << "," << dd << ",\npt0 " << pt0.transpose() << "\npt1 " << pt1.transpose() << f0.kpts[matches[i].queryIdx].pt << "," <<
00292 //                       f1.kpts[matches[i].trainIdx].pt << "\n unchanged pt1 " << pt1_unchanged.transpose() << std::endl;
00293                 inliers.push_back(matches[i]);
00294             }
00295         }
00296     }
00297 
00298 #if 0
00299     // Test with the SBA...
00300     {
00301         // system
00302         SysSBA sba;
00303         sba.verbose = 0;
00304 
00305 #if 0
00306         // set up nodes
00307         // should have a frame => node function
00308         Vector4d v0 = Vector4d(0,0,0,1);
00309         Quaterniond q0 = Quaternion<double>(Vector4d(0,0,0,1));
00310         sba.addNode(v0, q0, f0.cam, true);
00311 
00312         Quaterniond qr1(rot);   // from rotation matrix
00313         Vector4d temptrans = Vector4d(trans(0), trans(1), trans(2), 1.0);
00314 
00315         //        sba.addNode(temptrans, qr1.normalized(), f1.cam, false);
00316         qr1.normalize();
00317         sba.addNode(temptrans, qr1, f1.cam, false);
00318 
00319         int in = 3;
00320         if (in > (int)inls.size())
00321             in = inls.size();
00322 
00323         // set up projections
00324         for (int i=0; i<(int)inls.size(); i++)
00325         {
00326             // add point
00327             int i0 = inls[i].queryIdx;
00328             int i1 = inls[i].trainIdx;
00329             Vector4d pt = f0.pts[i0];
00330             sba.addPoint(pt);
00331 
00332             // projected point, ul,vl,ur
00333             Vector3d ipt;
00334             ipt(0) = f0.kpts[i0].pt.x;
00335             ipt(1) = f0.kpts[i0].pt.y;
00336             ipt(2) = ipt(0)-f0.disps[i0];
00337             sba.addStereoProj(0, i, ipt);
00338 
00339             // projected point, ul,vl,ur
00340             ipt(0) = f1.kpts[i1].pt.x;
00341             ipt(1) = f1.kpts[i1].pt.y;
00342             ipt(2) = ipt(0)-f1.disps[i1];
00343             sba.addStereoProj(1, i, ipt);
00344         }
00345 
00346         sba.huber = 2.0;
00347         sba.doSBA(5,10e-4,SBA_DENSE_CHOLESKY);
00348         int nbad = sba.removeBad(2.0); // 2.0
00349         cout << endl << "Removed " << nbad << " projections > 2 pixels error" << endl;
00350         sba.doSBA(5,10e-5,SBA_DENSE_CHOLESKY);
00351 
00352 //        cout << endl << sba.nodes[1].trans.transpose().head(3) << endl;
00353 
00354         // get the updated transform
00355         trans = sba.nodes[1].trans.head(3);
00356         Quaterniond q1;
00357         q1 = sba.nodes[1].qrot;
00358         quat = q1;
00359         rot = q1.toRotationMatrix();
00360 
00361         // set up inliers
00362         inliers.clear();
00363         for (int i=0; i<(int)inls.size(); i++)
00364         {
00365             ProjMap &prjs = sba.tracks[i].projections;
00366             if (prjs[0].isValid && prjs[1].isValid) // valid track
00367                 inliers.push_back(inls[i]);
00368         }
00369 
00370         printf("Inliers: %d   After polish: %d\n", (int)inls.size(), (int)inliers.size());
00371 #endif
00372     }
00373 #endif
00374 
00375 //     std::cout << std::endl << trans.transpose().head(3) << std::endl << std::endl;
00376 //     std::cout << rot << std::endl;
00377 
00378     return inliers.size();
00379 }
00380 }


ndt_feature_reg
Author(s): Henrik Andreasson, Todor Stoyanov, Jari Saarinen
autogenerated on Wed Aug 26 2015 15:25:07