initial_homography_estimation.cpp
Go to the documentation of this file.
00001 #include <vector>
00002 #include <iostream>
00003 #include <iomanip>
00004 
00005 #include <Eigen/SVD>
00006 #include <unsupported/Eigen/MatrixFunctions>
00007 
00008 #include "tictoc.hpp"
00009 #include "initial_homography_estimation.hpp"
00010 
00011 using namespace std;
00012 using namespace Eigen;
00013 
00014 namespace fovis
00015 {
00016 
00017 #define dump(var) //(cerr<<" "#var<<" =[\n"<< setprecision (12)<<var<<"];"<<endl)
00018 Eigen::ArrayXf InitialHomographyEstimator::flattenMatrix(Eigen::MatrixXf &m)
00019 {
00020   return Eigen::Map<Eigen::ArrayXf>(m.data(), m.rows() * m.cols());
00021 }
00022 
00023 static void
00024 grayToEigen(const uint8_t * grayData, int width, int height, int stride,
00025     int downsampleFactor, Eigen::MatrixXf* result)
00026 {
00027   // truncate the image before downsampling.
00028   width = (width >> downsampleFactor) << downsampleFactor;
00029   height = (height >> downsampleFactor) << downsampleFactor;
00030 
00031   Eigen::MatrixXf& eig_imf = *result;
00032   if (downsampleFactor > 0) {
00033     int cols = width >> downsampleFactor;
00034     int rows = height >> downsampleFactor;
00035     eig_imf = Eigen::MatrixXf::Zero(rows, cols);
00036     for (int y = 0; y < height; y++) {
00037       int ey = y >> downsampleFactor;
00038       for (int x = 0; x < width; x++) {
00039         int ex = x >> downsampleFactor;
00040         eig_imf(ey, ex) += grayData[y * stride + x];
00041       }
00042     }
00043     double pixelFactor = (1 << downsampleFactor);
00044     eig_imf /= pixelFactor * pixelFactor;
00045   }
00046   else {
00047     eig_imf.resize(height, width);
00048     const uint8_t* row_start = grayData;
00049     for(int row=0; row<height; row++) {
00050       for(int col=0; col<width; col++) {
00051         eig_imf(row, col) = row_start[col];
00052       }
00053       row_start += stride;
00054     }
00055   }
00056 }
00057 
00058 void InitialHomographyEstimator::setTestImage(const uint8_t * grayData, int width, int height, int stride, int downsampleFactor)
00059 {
00060   grayToEigen(grayData, width, height, stride, downsampleFactor, &testImage);
00061 }
00062 
00063 void InitialHomographyEstimator::setTemplateImage(const uint8_t * grayData, int width, int height, int stride,
00064     int downsampleFactor)
00065 {
00066   grayToEigen(grayData, width, height, stride, downsampleFactor, &templateImage);
00067   template_rows = templateImage.rows();
00068   template_cols = templateImage.cols();
00069 
00070   //compute template gradients
00071   Eigen::MatrixXf templateDx, templateDy;
00072   computeGradient(templateImage, &templateDx, &templateDy);
00073 
00074   templateDxRow = flattenMatrix(templateDx);
00075   templateDyRow = flattenMatrix(templateDy);
00076 
00077   //setup the utility matrices
00078   Eigen::MatrixXf x = VectorXf::LinSpaced(template_cols, 0, template_cols - 1).transpose().replicate(template_rows, 1);
00079   Eigen::MatrixXf y = VectorXf::LinSpaced(template_rows, 0, template_rows - 1).replicate(1, template_cols);
00080   xx = flattenMatrix(x);
00081   yy = flattenMatrix(y);
00082 
00083   templatePoints.resize(3, xx.rows());
00084   templatePoints.row(0) = xx;
00085   templatePoints.row(1) = yy;
00086   templatePoints.row(2).setOnes();
00087 
00088   xx += 1;
00089   yy += 1;
00090 
00091 }
00092 
00093 double InitialHomographyEstimator::computeError(const Eigen::MatrixXf &error)
00094 {
00095   return error.norm() / sqrt((double) error.rows() * error.cols());
00096 }
00097 
00098 Eigen::Matrix3f InitialHomographyEstimator::track(const Eigen::Matrix3f & initH, int nIters, double * finalRMS)
00099 {
00100 
00101   double minError = INFINITY;
00102   Eigen::Matrix3f H = initH;
00103   Eigen::Matrix3f bestH = H;
00104   int bestIter = 0;
00105   int lastImproved = 0;
00106   double lastRMS = INFINITY;
00107 
00108   for (int iter = 0; iter < nIters; iter++) {
00109     tictoc("track_iter");
00110     tictoc("warpPoints");
00111     Eigen::MatrixXf warpedHomogeneousPoints;
00112     warpedHomogeneousPoints = H * templatePoints;
00113     tictoc("warpPoints");
00114 
00115     tictoc("constructWarpedImage");
00116     warpedTestImage = constructWarpedImage(testImage, warpedHomogeneousPoints);
00117     tictoc("constructWarpedImage");
00118 
00119     errorIm = warpedTestImage - templateImage;
00120     Eigen::VectorXf errorRow;
00121     errorRow = flattenMatrix(errorIm);
00122 
00123     double rmsError = computeError(errorRow);
00124 
00125     if (rmsError < minError) {
00126       minError = rmsError;
00127       bestH = H;
00128       bestIter = iter;
00129     }
00130 
00131     tictoc("computeJacobian");
00132     tictoc("computeGradient");
00133     Eigen::MatrixXf warpedTestImageDx, warpedTestImageDy;
00134     computeGradient(warpedTestImage, &warpedTestImageDx, &warpedTestImageDy);
00135     tictoc("computeGradient");
00136 
00137     Eigen::ArrayXf warpedTestImageDxRow, warpedTestImageDyRow;
00138     warpedTestImageDxRow = flattenMatrix(warpedTestImageDx);
00139     warpedTestImageDyRow = flattenMatrix(warpedTestImageDy);
00140 
00141     Eigen::MatrixXf Jt = computeJacobian(templateDxRow + warpedTestImageDxRow, templateDyRow + warpedTestImageDyRow);
00142     tictoc("computeJacobian");
00143 
00144     //compute the pseudo-inverse
00145     tictoc("update");
00146     Eigen::VectorXf lie_d = -2*(Jt.transpose() * Jt).ldlt().solve(Jt.transpose() * errorRow);
00147     tictoc("update");
00148 
00149     tictoc("lieToH");
00150     H = H * lieToH(lie_d);
00151     tictoc("lieToH");
00152 
00153     if (rmsError < lastRMS)
00154       lastImproved = iter;
00155 
00156     tictoc("track_iter");
00157 
00158     //        cout << iter << ") rmsError= " << rmsError << " minError = " << minError << " d.norm() =" << lie_d.norm() << endl;
00159     //    exit(1);
00160     if (lie_d.norm() < 1e-6 || (rmsError - minError > 3 && iter - bestIter > 2) || iter - bestIter > 4 || iter
00161         - lastImproved > 2) {
00162       //      printf("breaking after %d iters\n", iter);
00163       break;
00164     }
00165     lastRMS = rmsError;
00166 
00167   }
00168   if (finalRMS != NULL)
00169     *finalRMS = minError;
00170   return bestH;
00171 
00172 }
00173 
00174 void InitialHomographyEstimator::computeGradient(const Eigen::MatrixXf &image, Eigen::MatrixXf *dxp, Eigen::MatrixXf *dyp)
00175 {
00176   Eigen::MatrixXf & dx = *dxp;
00177   Eigen::MatrixXf & dy = *dyp;
00178   dx = Eigen::MatrixXf::Zero(image.rows(), image.cols());
00179   dy = Eigen::MatrixXf::Zero(image.rows(), image.cols());
00180 
00181   dx.block(0, 1, dx.rows(), dx.cols() - 2) = image.block(0, 2, dx.rows(), dx.cols() - 2) - image.block(0, 0, dx.rows(),
00182       dx.cols() - 2);
00183   //handle border
00184 
00185   dy.block(1, 0, dy.rows() - 2, dy.cols()) = image.block(2, 0, dy.rows() - 2, dy.cols()) - image.block(0, 0, dy.rows()
00186       - 2, dy.cols());
00187   //normalize
00188   dx /= 2.0;
00189   dy /= 2.0;
00190 
00191   //handle borders
00192   dx.col(0) = image.col(1) - image.col(0);
00193   dx.col(image.cols() - 1) = image.col(image.cols() - 1) - image.col(image.cols() - 2);
00194   dy.row(0) = image.row(1) - image.row(0);
00195   dy.row(image.rows() - 1) = image.row(image.rows() - 1) - image.row(image.rows() - 2);
00196 
00197 }
00198 
00199 Eigen::MatrixXf InitialHomographyEstimator::computeJacobian(const Eigen::ArrayXf &dx, const Eigen::ArrayXf &dy) const
00200 {
00201   Eigen::MatrixXf Jt(dx.rows(), 3);
00202   Jt.col(0) = dx;
00203   Jt.col(1) = dy;
00204   Jt.col(2) = dx * yy - dy * xx;
00205   return Jt;
00206 }
00207 
00208 Eigen::Matrix3f InitialHomographyEstimator::lieToH(const Eigen::VectorXf &lie) const
00209 {
00210   //TODO: support more parameters?
00211   Eigen::Matrix3f M;
00212   M << 0, lie(2), lie(0),
00213       -lie(2), 0, lie(1),
00214        0, 0, 0;
00215   return M.exp();
00216 }
00217 
00218 Eigen::MatrixXf InitialHomographyEstimator::constructWarpedImage(const Eigen::MatrixXf &srcImage,
00219     const Eigen::MatrixXf &warpedPoints) const
00220 {
00221   Eigen::MatrixXf warped = Eigen::MatrixXf(template_rows, template_cols);
00222 
00223   const double defaultValue = 128;
00224   //Bilinear interpolation
00225   for (int i = 0; i < warpedPoints.cols(); i++) {
00226     double val;
00227     Eigen::Vector2f pt = warpedPoints.col(i).head(2) / warpedPoints(2, i);
00228     Eigen::Vector2i fipt(floor(pt(0)), floor(pt(1)));
00229     Eigen::Vector2i cipt(ceil(pt(0)), ceil(pt(1)));
00230     if (0 <= pt(0) && pt(0) < srcImage.cols() - 1 && 0 <= pt(1) && pt(1) < srcImage.rows() - 1) {
00231       double x1 = pt(0) - fipt(0);
00232       double y1 = pt(1) - fipt(1);
00233       double x2 = 1 - x1;
00234       double y2 = 1 - y1;
00235       val = x2 * y2 * srcImage(fipt(1), fipt(0)) + x1 * y2 * srcImage(fipt(1), fipt(0) + 1) + x2 * y1 * srcImage(
00236           fipt(1) + 1, fipt(0)) + x1 * y1 * srcImage(fipt(1) + 1, fipt(0) + 1);
00237 
00238     }
00239     else if (0 <= fipt(0) && fipt(0) < srcImage.cols() && 0 <= fipt(1) && fipt(1) < srcImage.rows()) {
00240       val = srcImage(fipt(1), fipt(0));
00241     }
00242     else if (0 <= cipt(0) && cipt(0) < srcImage.cols() && 0 <= cipt(1) && cipt(1) < srcImage.rows()) {
00243       val = srcImage(cipt(1), cipt(0));
00244     }
00245     else
00246       val = defaultValue; //templateImage(i / template_cols, i % template_cols); //default to the same as template, so error is 0
00247 
00248     warped(i % template_rows, i / template_rows) = val; //Eigen is Column-major
00249   }
00250   return warped;
00251 }
00252 
00253 }


libfovis
Author(s): Albert Huang, Maurice Fallon
autogenerated on Thu Jun 6 2019 20:16:12