00001 #include <posest/esm.hpp>
00002 using namespace cv;
00003
00004 void rect2vertices(const Rect &rect, vector<Point2f> &vertices);
00005
00006 void HomoESM::setTemplateImage(const Mat &image)
00007 {
00008 assert( image.type() == CV_8UC1 );
00009 templateImage = image;
00010 Mat templateImageDouble;
00011 image.convertTo(templateImageDouble, CV_64FC1);
00012 templateImageRowDouble = templateImageDouble.reshape(0, 1);
00013
00014 Mat templateDx, templateDy;
00015 computeGradient(templateImage, templateDx, templateDy);
00016 templateDxRow = templateDx.reshape(0, 1);
00017 templateDyRow = templateDy.reshape(0, 1);
00018
00019 xx.create(1, image.rows * image.cols, CV_64FC1);
00020 yy.create(1, image.rows * image.cols, CV_64FC1);
00021 for (int i = 0; i < image.rows * image.cols; i++)
00022 {
00023 xx.at<double> (0, i) = i % image.cols;
00024 yy.at<double> (0, i) = i / image.cols;
00025 }
00026
00027 rect2vertices(Rect(0, 0, templateImage.cols, templateImage.rows), templateVertices);
00028
00029 templatePoints.create(templateImage.size().area(), 1, CV_32FC2);
00030 for (int y = 0; y < templateImage.rows; y++)
00031 {
00032 for (int x = 0; x < templateImage.cols; x++)
00033 {
00034 templatePoints.at<Vec2f> (y * templateImage.cols + x, 0) = Vec2f(x, y);
00035 }
00036 }
00037 }
00038
00039 void HomoESM::setTestImage(const Mat &image)
00040 {
00041 assert( image.type() == CV_8UC1 );
00042 testImage = image;
00043 }
00044
00045 double HomoESM::computeRMSError(const Mat &error)
00046 {
00047 return norm(error) / sqrt((double)error.size().area());
00048 }
00049
00050 void HomoESM::track(int nIters, Mat &H, double &rmsError, Ptr<LieAlgebra> lieAlgebra, bool saveComputations, vector<
00051 HomoESMState> *computations) const
00052 {
00053 if (saveComputations)
00054 {
00055 assert( computations != 0 );
00056 computations->clear();
00057 }
00058
00059 for (int iter = 0; iter < nIters; iter++)
00060 {
00061 Mat warpedHomogeneousPoints;
00062 transform(templatePoints, warpedHomogeneousPoints, H);
00063 vector<Point2f> warpedPoints;
00064 convertPointsHomogeneous(warpedHomogeneousPoints, warpedPoints);
00065
00066 Mat warpedTemplateDouble;
00067 constructImage(testImage, warpedPoints, warpedTemplateDouble);
00068
00069 Mat errorRow = warpedTemplateDouble.reshape(0, 1) - templateImageRowDouble;
00070
00071 if (saveComputations)
00072 {
00073 HomoESMState state;
00074 H.copyTo(state.H);
00075 state.error = computeRMSError(errorRow);
00076 computations->push_back(state);
00077 }
00078
00079 if (iter == nIters - 1)
00080 {
00081 rmsError = computeRMSError(errorRow);
00082 break;
00083 }
00084
00085 Mat warpedTestImageDx, warpedTestImageDy;
00086 computeGradient(warpedTemplateDouble, warpedTestImageDx, warpedTestImageDy);
00087
00088 Mat Jt;
00089 computeJacobian(templateDxRow + warpedTestImageDx.reshape(0, 1), templateDyRow + warpedTestImageDy.reshape(0, 1),
00090 Jt, lieAlgebra);
00091
00092 Mat JtJ;
00093 mulTransposed(Jt, JtJ, false);
00094
00095 Mat d = -2 * JtJ.inv(DECOMP_CHOLESKY) * Jt * errorRow.t();
00096
00097 Mat delta = lieAlgebra->algebra2group(d);
00098 H = H * delta;
00099 }
00100 }
00101
00102 void HomoESM::projectVertices(const cv::Mat &H, std::vector<cv::Point2f> &vertices) const
00103 {
00104 vertices.clear();
00105 Mat transformedVertices;
00106 transform(Mat(templateVertices), transformedVertices, H);
00107 convertPointsHomogeneous(transformedVertices, vertices);
00108 }
00109
00110 void HomoESM::visualizeTracking(const Mat &H, Mat &visualization) const
00111 {
00112 vector<Point2f> vertices;
00113 projectVertices(H, vertices);
00114
00115 cvtColor(testImage, visualization, CV_GRAY2RGB);
00116 Scalar color = Scalar(0, 255, 0);
00117 int thickness = 2;
00118 for (size_t i = 0; i < vertices.size(); i++)
00119 {
00120 line(visualization, vertices[i], vertices[(i + 1) % vertices.size()], color, thickness);
00121 }
00122 }
00123
00124 void HomoESM::computeGradient(const Mat &image, Mat &dx, Mat &dy)
00125 {
00126 Sobel(image, dx, CV_64FC1, 1, 0);
00127 Sobel(image, dy, CV_64FC1, 0, 1);
00128
00129
00130 dx /= 8.;
00131 dy /= 8.;
00132 }
00133
00134 void HomoESM::computeJacobian(const Mat &dx, const Mat &dy, Mat &Jt, Ptr<LieAlgebra> lieAlgebra) const
00135 {
00136 assert( dx.rows == 1 );
00137 assert( dy.rows == 1 );
00138 Mat Ix = dx;
00139 Mat Iy = dy;
00140
00141 const int dim = 3;
00142 Mat JiJw_t(dim * dim, Ix.cols, CV_64FC1);
00143 JiJw_t.row(0) = Ix.mul(xx);
00144 JiJw_t.row(1) = Ix.mul(yy);
00145
00146 Mat tmpMat = JiJw_t.row(2);
00147 Ix.copyTo(tmpMat);
00148
00149 JiJw_t.row(3) = Iy.mul(xx);
00150 JiJw_t.row(4) = Iy.mul(yy);
00151
00152 tmpMat = JiJw_t.row(5);
00153 Iy.copyTo(tmpMat);
00154
00155 JiJw_t.row(6) = -(xx.mul(JiJw_t.row(0)) + yy.mul(JiJw_t.row(3)));
00156 JiJw_t.row(7) = -(xx.mul(JiJw_t.row(1)) + yy.mul(JiJw_t.row(4)));
00157 JiJw_t.row(8) = -(xx.mul(JiJw_t.row(2)) + yy.mul(JiJw_t.row(5)));
00158
00159 lieAlgebra->dot(JiJw_t, Jt);
00160 }
00161
00162 void HomoESM::constructImage(const Mat &srcImage, const vector<Point2f> &points, Mat &intensity) const
00163 {
00164 assert( srcImage.type() == CV_8UC1 );
00165 intensity.create(templateImage.size(), CV_64FC1);
00166
00167 const double defaultValue = 0;
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183 for (size_t i = 0; i < points.size(); i++)
00184 {
00185 double val;
00186 Point2f pt = points[i];
00187 if (0 <= pt.x && pt.x < srcImage.cols - 1 && 0 <= pt.y && pt.y < srcImage.rows - 1)
00188 {
00189 Point tl = Point(floor(pt.x), floor(pt.y));
00190 double x1 = pt.x - tl.x;
00191 double y1 = pt.y - tl.y;
00192 double y2 = 1 - y2;
00193 val = (1 - x1) * (1 - y1) * srcImage.at<uchar> (tl.y, tl.x) + x1 * (1 - y1) * srcImage.at<uchar> (tl.y, tl.x + 1)
00194 + (1 - x1) * y1 * srcImage.at<uchar> (tl.y + 1, tl.x) + x1 * y1 * srcImage.at<uchar> (tl.y + 1, tl.x + 1);
00195 }
00196 else
00197 val = defaultValue;
00198 intensity.at<double> (i / templateImage.cols, i % templateImage.cols) = val;
00199 }
00200 }
00201
00202 void HomoESM::checkAccuracy(vector<vector<HomoESMState> > &computations, string groundTruthFile, double &meanSSDError,
00203 double &meanSpatialError) const
00204 {
00205 FileStorage fs(groundTruthFile, FileStorage::READ);
00206 assert( fs.isOpened() );
00207
00208 double ssdErrorSum = 0;
00209 double spatialErrorSum = 0;
00210 const double minSSDError = 1.;
00211 int iterationsCount = 0;
00212 int errorsCount = 0;
00213 for (size_t frameIdx = 0; frameIdx < computations.size(); frameIdx++)
00214 {
00215 for (size_t iterIdx = 0; iterIdx < computations[frameIdx].size(); iterIdx++)
00216 {
00217 std::stringstream frame, iter;
00218 frame << "frame_" << frameIdx;
00219 iter << "iter_" << iterIdx;
00220 double groundTruthError = fs["ESM_results"][frame.str()][iter.str()]["rms_error"];
00221 double error = computations[frameIdx][iterIdx].error;
00222 if (groundTruthError > minSSDError)
00223 {
00224 ssdErrorSum += error / groundTruthError;
00225 errorsCount++;
00226 }
00227 Mat groundTruthH;
00228 fs["ESM_results"][frame.str()][iter.str()]["warp_p"] >> groundTruthH;
00229 Mat H = computations[frameIdx][iterIdx].H;
00230 vector<Point2f> groundTruthVertices;
00231 vector<Point2f> vertices;
00232 projectVertices(groundTruthH, groundTruthVertices);
00233 projectVertices(H, vertices);
00234 double spatialError = norm(Mat(groundTruthVertices), Mat(vertices));
00235 spatialErrorSum += spatialError;
00236 iterationsCount++;
00237 }
00238 }
00239 fs.release();
00240 meanSSDError = ssdErrorSum / errorsCount;
00241 meanSpatialError = spatialErrorSum / iterationsCount;
00242 }
00243
00244 LieAlgebraHomography::LieAlgebraHomography()
00245 {
00246 basis.resize(8);
00247 basis[0].addPositive(2);
00248 basis[1].addPositive(5);
00249 basis[2].addPositive(1);
00250 basis[3].addPositive(3);
00251
00252 basis[4].addPositive(0);
00253 basis[4].addNegative(4);
00254 basis[5].addNegative(4);
00255 basis[5].addPositive(8);
00256
00257 basis[6].addPositive(6);
00258 basis[7].addPositive(7);
00259 }
00260
00261 LieAlgebraRotation3d::LieAlgebraRotation3d()
00262 {
00263 basis.resize(3);
00264 basis[0].addPositive(1);
00265 basis[0].addNegative(3);
00266 basis[1].addPositive(2);
00267 basis[1].addNegative(6);
00268 basis[2].addPositive(5);
00269 basis[2].addNegative(7);
00270 }
00271
00272 LieAlgebraTranslation::LieAlgebraTranslation()
00273 {
00274 basis.resize(2);
00275 basis[0].addPositive(2);
00276 basis[1].addPositive(5);
00277 }
00278
00279 LieAlgebraEuclidean::LieAlgebraEuclidean()
00280 {
00281 LieAlgebraBasisVector vector;
00282 vector.addPositive(1);
00283 vector.addNegative(3);
00284
00285 basis.push_back(vector);
00286 }
00287
00288 LieAlgebraSimilarity::LieAlgebraSimilarity()
00289 {
00290 LieAlgebraBasisVector vector;
00291 vector.addPositive(0);
00292 vector.addPositive(4);
00293
00294 basis.push_back(vector);
00295 }
00296
00297 LieAlgebraAffine::LieAlgebraAffine()
00298 {
00299 basis.resize(6);
00300 basis[0].addPositive(0);
00301 basis[1].addPositive(1);
00302 basis[2].addPositive(3);
00303 basis[3].addPositive(4);
00304 basis[4].addPositive(2);
00305 basis[5].addPositive(5);
00306 }
00307
00308 void LieAlgebra::LieAlgebraBasisVector::dot(const Mat &src, Mat dst) const
00309 {
00310 assert( dst.rows == 1 && dst.cols == src.cols );
00311 assert( dst.type() == src.type() );
00312
00313 for (size_t i = 0; i < positives.size(); i++)
00314 dst += src.row(positives[i]);
00315 for (size_t i = 0; i < negatives.size(); i++)
00316 dst -= src.row(negatives[i]);
00317 }
00318
00319 Mat LieAlgebra::LieAlgebraBasisVector::vector2mat(double coordinate) const
00320 {
00321 const int dim = 3;
00322 Mat result(dim, dim, CV_64FC1, Scalar(0));
00323
00324 for (size_t i = 0; i < positives.size(); i++)
00325 {
00326 int row = positives[i] / dim;
00327 int col = positives[i] % dim;
00328 result.at<double> (row, col) = coordinate;
00329 }
00330
00331 for (size_t i = 0; i < negatives.size(); i++)
00332 {
00333 int row = negatives[i] / dim;
00334 int col = negatives[i] % dim;
00335 result.at<double> (row, col) = -coordinate;
00336 }
00337
00338 return result;
00339 }
00340
00341 void LieAlgebra::dot(const Mat &src, Mat &dst) const
00342 {
00343 dst.create(basis.size(), src.cols, CV_64FC1);
00344 dst.setTo(0);
00345
00346 for (size_t i = 0; i < basis.size(); i++)
00347 {
00348 basis[i].dot(src, dst.row(i));
00349 }
00350 }
00351
00352 Mat LieAlgebra::algebra2group(const Mat &lieAlgebraCoords) const
00353 {
00354 Mat coords = lieAlgebraCoords.reshape(0, 1);
00355 assert( coords.cols == basis.size() );
00356 assert( coords.type() == CV_64FC1 );
00357
00358 const int dim = 3;
00359 Mat lieAlgebraMat(dim, dim, CV_64F, Scalar(0));
00360 for (size_t i = 0; i < basis.size(); i++)
00361 {
00362 lieAlgebraMat += basis[i].vector2mat(coords.at<double> (0, i));
00363 }
00364
00365 Mat lieGroupMat = matrixExp(lieAlgebraMat);
00366 return lieGroupMat;
00367 }
00368
00369 Mat LieAlgebra::matrixExp(const Mat &mat)
00370 {
00371 assert( mat.rows == 3 );
00372 assert( mat.cols == 3 );
00373 assert( mat.type() == CV_64FC1 );
00374
00375 Eigen::Matrix<double, 3, 3> eigenMat;
00376 cv2eigen(mat, eigenMat);
00377 Eigen::Matrix<double, 3, 3> matExp;
00378 matExp = eigenMat.exp();
00379
00380 Mat result;
00381 eigen2cv(matExp, result);
00382 return result;
00383 }
00384
00385 void rect2vertices(const Rect &rect, vector<Point2f> &vertices)
00386 {
00387 vertices.clear();
00388 vertices.push_back(Point2f(rect.x, rect.y));
00389 vertices.push_back(Point2f(rect.x + rect.width, rect.y));
00390 vertices.push_back(Point2f(rect.x + rect.width, rect.y + rect.height));
00391 vertices.push_back(Point2f(rect.x, rect.y + rect.height));
00392 }