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
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
00071 Eigen::MatrixXf templateDx, templateDy;
00072 computeGradient(templateImage, &templateDx, &templateDy);
00073
00074 templateDxRow = flattenMatrix(templateDx);
00075 templateDyRow = flattenMatrix(templateDy);
00076
00077
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
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
00159
00160 if (lie_d.norm() < 1e-6 || (rmsError - minError > 3 && iter - bestIter > 2) || iter - bestIter > 4 || iter
00161 - lastImproved > 2) {
00162
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
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
00188 dx /= 2.0;
00189 dy /= 2.0;
00190
00191
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
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
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;
00247
00248 warped(i % template_rows, i / template_rows) = val;
00249 }
00250 return warped;
00251 }
00252
00253 }