pe3d.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 
00036 #include <posest/pe3d.h>
00037 #include <sba/sba.h>
00038 #include <Eigen/SVD>
00039 #include <Eigen/LU>
00040 #include <iostream>
00041 
00042 
00043 using namespace Eigen;
00044 using namespace sba;
00045 using namespace frame_common;
00046 using namespace std;
00047 
00048 namespace pe
00049 {
00050 
00051 
00052   //
00053   // find the best estimate for a geometrically-consistent match
00054   // assumes stereo points have been calculated, and matches filtered,
00055   //   and frames set up
00056   // uses the SVD procedure for aligning point clouds
00057   //   SEE: Arun, Huang, Blostein: Least-Squares Fitting of Two 3D Point Sets
00058   // final SBA polishing step
00059   //
00060 
00061   int PoseEstimator3d::estimate(const Frame& f0, const Frame& f1, 
00062                                 const std::vector<cv::DMatch> &matches)
00063   {
00064     // convert keypoints in match to 3d points
00065     std::vector<Vector4d, aligned_allocator<Vector4d> > p0; // homogeneous coordinates
00066     std::vector<Vector4d, aligned_allocator<Vector4d> > p1;
00067 
00068     int nmatch = matches.size();
00069 
00070     // set up data structures for fast processing
00071     // indices to good matches
00072     vector<int> m0, m1;
00073     for (int i=0; i<nmatch; i++)
00074       {
00075         if (f0.disps[matches[i].queryIdx] > minMatchDisp && 
00076             f1.disps[matches[i].trainIdx] > minMatchDisp)
00077           {
00078             m0.push_back(matches[i].queryIdx);
00079             m1.push_back(matches[i].trainIdx);
00080           }
00081       }
00082 
00083     nmatch = m0.size();
00084     if (nmatch < 3) return 0;   // can't do it...
00085 
00086     int bestinl = 0;
00087 
00088     // RANSAC loop
00089     #pragma omp parallel for shared( bestinl )
00090     for (int i=0; i<numRansac; i++) 
00091       {
00092         // find a candidate
00093         int a=rand()%nmatch;
00094         int b = a;
00095         while (a==b)
00096           b=rand()%nmatch;
00097         int c = a;
00098         while (a==c || b==c)
00099           c=rand()%nmatch;
00100 
00101         int i0a = m0[a];
00102         int i0b = m0[b];
00103         int i0c = m0[c];
00104         int i1a = m1[a];
00105         int i1b = m1[b];
00106         int i1c = m1[c];
00107 
00108         if (i0a == i0b || i0a == i0c || i0b == i0c ||
00109             i1a == i1b || i1a == i1c || i1b == i1c)
00110           continue;
00111 
00112         // get centroids
00113         Vector3d p0a = f0.pts[i0a].head<3>();
00114         Vector3d p0b = f0.pts[i0b].head<3>();
00115         Vector3d p0c = f0.pts[i0c].head<3>();
00116         Vector3d p1a = f1.pts[i1a].head<3>();
00117         Vector3d p1b = f1.pts[i1b].head<3>();
00118         Vector3d p1c = f1.pts[i1c].head<3>();
00119 
00120         Vector3d c0 = (p0a+p0b+p0c)*(1.0/3.0);
00121         Vector3d c1 = (p1a+p1b+p1c)*(1.0/3.0);
00122 
00123         // subtract out
00124         p0a -= c0;
00125         p0b -= c0;
00126         p0c -= c0;
00127         p1a -= c1;
00128         p1b -= c1;
00129         p1c -= c1;
00130 
00131         Matrix3d H = p1a*p0a.transpose() + p1b*p0b.transpose() +
00132                      p1c*p0c.transpose();
00133 
00134 #if 0
00135         cout << p0a.transpose() << endl;
00136         cout << p0b.transpose() << endl;
00137         cout << p0c.transpose() << endl;
00138 #endif
00139 
00140         // do the SVD thang
00141         JacobiSVD<Matrix3d> svd(H, ComputeFullU | ComputeFullV);
00142         Matrix3d V = svd.matrixV();
00143         Matrix3d R = V * svd.matrixU().transpose();          
00144         double det = R.determinant();
00145         //ntot++;
00146         if (det < 0.0)
00147           {
00148             //nneg++;
00149             V.col(2) = V.col(2)*-1.0;
00150             R = V * svd.matrixU().transpose();
00151           }
00152         Vector3d tr = c0-R*c1;    // translation 
00153 
00154         //        cout << "[PE test] R: " << endl << R << endl;
00155         //        cout << "[PE test] t: " << tr.transpose() << endl;
00156 
00157 #if 0
00158         R << 0.9997, 0.002515, 0.02418,
00159           -0.002474, .9999, -0.001698,
00160           -0.02418, 0.001638, 0.9997;
00161         tr << -0.005697, -0.01753, 0.05666;
00162         R = R.transpose();
00163         tr = -R*tr;
00164 #endif
00165 
00166         // transformation matrix, 3x4
00167         Matrix<double,3,4> tfm;
00168         //        tfm.block<3,3>(0,0) = R.transpose();
00169         //        tfm.col(3) = -R.transpose()*tr;
00170         tfm.block<3,3>(0,0) = R;
00171         tfm.col(3) = tr;
00172         
00173         //        cout << "[PE test] T: " << endl << tfm << endl;
00174 
00175         // find inliers, based on image reprojection
00176         int inl = 0;
00177         for (int i=0; i<nmatch; i++)
00178           {
00179             Vector3d pt = tfm*f1.pts[m1[i]];
00180             Vector3d ipt = f0.cam2pix(pt);
00181             const cv::KeyPoint &kp = f0.kpts[m0[i]];
00182             double dx = kp.pt.x - ipt.x();
00183             double dy = kp.pt.y - ipt.y();
00184             double dd = f0.disps[m0[i]] - ipt.z();
00185             if (dx*dx < maxInlierXDist2 && dy*dy < maxInlierXDist2 && 
00186                 dd*dd < maxInlierDDist2)
00187               inl+=(int)sqrt(ipt.z()); // clever way to weight closer points
00188               //              inl++;
00189           }
00190         
00191         #pragma omp critical
00192         if (inl > bestinl) 
00193           {
00194             bestinl = inl;
00195             rot = R;
00196             trans = tr;
00197           }
00198       }
00199 
00200     //    printf("Best inliers: %d\n", bestinl);
00201     //    printf("Total ransac: %d  Neg det: %d\n", ntot, nneg);
00202 
00203     // reduce matches to inliers
00204     vector<cv::DMatch> inls;    // temporary for current inliers
00205     inliers.clear();
00206     Matrix<double,3,4> tfm;
00207     tfm.block<3,3>(0,0) = rot;
00208     tfm.col(3) = trans;
00209 
00210     nmatch = matches.size();
00211     for (int i=0; i<nmatch; i++)
00212       {
00213         if (!f0.goodPts[matches[i].queryIdx] || !f1.goodPts[matches[i].trainIdx])
00214           continue;
00215         Vector3d pt = tfm*f1.pts[matches[i].trainIdx];
00216         Vector3d ipt = f0.cam2pix(pt);
00217         const cv::KeyPoint &kp = f0.kpts[matches[i].queryIdx];
00218         double dx = kp.pt.x - ipt.x();
00219         double dy = kp.pt.y - ipt.y();
00220         double dd = f0.disps[matches[i].queryIdx] - ipt.z();
00221         if (dx*dx < maxInlierXDist2 && dy*dy < maxInlierXDist2 && 
00222             dd*dd < maxInlierDDist2)
00223           inls.push_back(matches[i]);
00224       }
00225 
00226 #if 0
00227     cout << endl << trans.transpose().head(3) << endl << endl;
00228     cout << rot << endl;
00229 #endif
00230 
00231     // polish with stereo sba
00232     if (polish)
00233       {
00234         // system
00235         SysSBA sba;
00236         sba.verbose = 0;
00237 
00238         // set up nodes
00239         // should have a frame => node function        
00240         Vector4d v0 = Vector4d(0,0,0,1);
00241         Quaterniond q0 = Quaternion<double>(Vector4d(0,0,0,1));
00242         sba.addNode(v0, q0, f0.cam, true);
00243         
00244         Quaterniond qr1(rot);   // from rotation matrix
00245         Vector4d temptrans = Vector4d(trans(0), trans(1), trans(2), 1.0);
00246 
00247         //        sba.addNode(temptrans, qr1.normalized(), f1.cam, false);
00248         qr1.normalize();
00249         sba.addNode(temptrans, qr1, f1.cam, false);
00250 
00251         int in = 3;
00252         if (in > (int)inls.size())
00253           in = inls.size();
00254 
00255         // set up projections
00256         for (int i=0; i<(int)inls.size(); i++)
00257           {
00258             // add point
00259             int i0 = inls[i].queryIdx;
00260             int i1 = inls[i].trainIdx;
00261             Vector4d pt = f0.pts[i0];
00262             sba.addPoint(pt);
00263             
00264             // projected point, ul,vl,ur
00265             Vector3d ipt;
00266             ipt(0) = f0.kpts[i0].pt.x;
00267             ipt(1) = f0.kpts[i0].pt.y;
00268             ipt(2) = ipt(0)-f0.disps[i0];
00269             sba.addStereoProj(0, i, ipt);
00270 
00271             // projected point, ul,vl,ur
00272             ipt(0) = f1.kpts[i1].pt.x;
00273             ipt(1) = f1.kpts[i1].pt.y;
00274             ipt(2) = ipt(0)-f1.disps[i1];
00275             sba.addStereoProj(1, i, ipt);
00276           }
00277 
00278         sba.huber = 2.0;
00279         sba.doSBA(5,10e-4,SBA_DENSE_CHOLESKY);
00280         int nbad = sba.removeBad(2.0);
00281 //        cout << endl << "Removed " << nbad << " projections > 2 pixels error" << endl;
00282         sba.doSBA(5,10e-5,SBA_DENSE_CHOLESKY);
00283 
00284 //        cout << endl << sba.nodes[1].trans.transpose().head(3) << endl;
00285 
00286         // get the updated transform
00287               trans = sba.nodes[1].trans.head(3);
00288               Quaterniond q1;
00289               q1 = sba.nodes[1].qrot;
00290               rot = q1.toRotationMatrix();
00291 
00292         // set up inliers
00293         inliers.clear();
00294         for (int i=0; i<(int)inls.size(); i++)
00295           {
00296             ProjMap &prjs = sba.tracks[i].projections;
00297             if (prjs[0].isValid && prjs[1].isValid) // valid track
00298               inliers.push_back(inls[i]);
00299           }
00300 #if 0
00301         printf("Inliers: %d   After polish: %d\n", (int)inls.size(), (int)inliers.size());
00302 #endif
00303       }
00304 
00305     return (int)inliers.size();
00306   }
00307 } // ends namespace pe


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