samples/cpp/tutorial_code/features/Homography/decompose_homography.cpp#

An example program with homography decomposition. Check the corresponding tutorial for more details.

  1#include <iostream>
  2#include <opencv2/core.hpp>
  3#include <opencv2/highgui.hpp>
  4#include <opencv2/geometry.hpp>
  5#include <opencv2/calib.hpp>
  6#include <opencv2/objdetect.hpp>
  7
  8using namespace std;
  9using namespace cv;
 10
 11namespace
 12{
 13enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
 14
 15void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
 16{
 17    corners.resize(0);
 18
 19    switch (patternType) {
 20    case CHESSBOARD:
 21    case CIRCLES_GRID:
 22        for( int i = 0; i < boardSize.height; i++ )
 23            for( int j = 0; j < boardSize.width; j++ )
 24                corners.push_back(Point3f(float(j*squareSize),
 25                                          float(i*squareSize), 0));
 26        break;
 27
 28    case ASYMMETRIC_CIRCLES_GRID:
 29        for( int i = 0; i < boardSize.height; i++ )
 30            for( int j = 0; j < boardSize.width; j++ )
 31                corners.push_back(Point3f(float((2*j + i % 2)*squareSize),
 32                                          float(i*squareSize), 0));
 33        break;
 34
 35    default:
 36        CV_Error(Error::StsBadArg, "Unknown pattern type\n");
 37    }
 38}
 39
 40Mat computeHomography(const Mat &R_1to2, const Mat &tvec_1to2, const double d_inv, const Mat &normal)
 41{
 42    Mat homography = R_1to2 + d_inv * tvec_1to2*normal.t();
 43    return homography;
 44}
 45
 46void computeC2MC1(const Mat &R1, const Mat &tvec1, const Mat &R2, const Mat &tvec2,
 47                  Mat &R_1to2, Mat &tvec_1to2)
 48{
 49    //c2Mc1 = c2Mo * oMc1 = c2Mo * c1Mo.inv()
 50    R_1to2 = R2 * R1.t();
 51    tvec_1to2 = R2 * (-R1.t()*tvec1) + tvec2;
 52}
 53
 54void decomposeHomography(const string &img1Path, const string &img2Path, const Size &patternSize,
 55                         const float squareSize, const string &intrinsicsPath)
 56{
 57    Mat img1 = imread( samples::findFile( img1Path) );
 58    Mat img2 = imread( samples::findFile( img2Path) );
 59
 60    vector<Point2f> corners1, corners2;
 61    bool found1 = findChessboardCorners(img1, patternSize, corners1);
 62    bool found2 = findChessboardCorners(img2, patternSize, corners2);
 63
 64    if (!found1 || !found2)
 65    {
 66        cout << "Error, cannot find the chessboard corners in both images." << endl;
 67        return;
 68    }
 69
 70    //! [compute-poses]
 71    vector<Point3f> objectPoints;
 72    calcChessboardCorners(patternSize, squareSize, objectPoints);
 73
 74    FileStorage fs( samples::findFile( intrinsicsPath ), FileStorage::READ);
 75    Mat cameraMatrix, distCoeffs;
 76    fs["camera_matrix"] >> cameraMatrix;
 77    fs["distortion_coefficients"] >> distCoeffs;
 78
 79    Mat rvec1, tvec1;
 80    solvePnP(objectPoints, corners1, cameraMatrix, distCoeffs, rvec1, tvec1);
 81    Mat rvec2, tvec2;
 82    solvePnP(objectPoints, corners2, cameraMatrix, distCoeffs, rvec2, tvec2);
 83    //! [compute-poses]
 84
 85    //! [compute-camera-displacement]
 86    Mat R1, R2;
 87    Rodrigues(rvec1, R1);
 88    Rodrigues(rvec2, R2);
 89
 90    Mat R_1to2, t_1to2;
 91    computeC2MC1(R1, tvec1, R2, tvec2, R_1to2, t_1to2);
 92    Mat rvec_1to2;
 93    Rodrigues(R_1to2, rvec_1to2);
 94    //! [compute-camera-displacement]
 95
 96    //! [compute-plane-normal-at-camera-pose-1]
 97    Mat normal = Mat_<double>({3,1}, {0, 0, 1});
 98    Mat normal1 = R1*normal;
 99    //! [compute-plane-normal-at-camera-pose-1]
100
101    //! [compute-plane-distance-to-the-camera-frame-1]
102    Mat origin(3, 1, CV_64F, Scalar(0));
103    Mat origin1 = R1*origin + tvec1;
104    double d_inv1 = 1.0 / normal1.dot(origin1);
105    //! [compute-plane-distance-to-the-camera-frame-1]
106
107    //! [compute-homography-from-camera-displacement]
108    Mat homography_euclidean = computeHomography(R_1to2, t_1to2, d_inv1, normal1);
109    Mat homography = cameraMatrix * homography_euclidean * cameraMatrix.inv();
110
111    homography /= homography.at<double>(2,2);
112    homography_euclidean /= homography_euclidean.at<double>(2,2);
113    //! [compute-homography-from-camera-displacement]
114
115    //! [decompose-homography-from-camera-displacement]
116    vector<Mat> Rs_decomp, ts_decomp, normals_decomp;
117    int solutions = decomposeHomographyMat(homography, cameraMatrix, Rs_decomp, ts_decomp, normals_decomp);
118    cout << "Decompose homography matrix computed from the camera displacement:" << endl << endl;
119    for (int i = 0; i < solutions; i++)
120    {
121      double factor_d1 = 1.0 / d_inv1;
122      Mat rvec_decomp;
123      Rodrigues(Rs_decomp[i], rvec_decomp);
124      cout << "Solution " << i << ":" << endl;
125      cout << "rvec from homography decomposition: " << rvec_decomp.t() << endl;
126      cout << "rvec from camera displacement: " << rvec_1to2.t() << endl;
127      cout << "tvec from homography decomposition: " << ts_decomp[i].t() << " and scaled by d: " << factor_d1 * ts_decomp[i].t() << endl;
128      cout << "tvec from camera displacement: " << t_1to2.t() << endl;
129      cout << "plane normal from homography decomposition: " << normals_decomp[i].t() << endl;
130      cout << "plane normal at camera 1 pose: " << normal1.t() << endl << endl;
131    }
132    //! [decompose-homography-from-camera-displacement]
133
134    //! [estimate homography]
135    Mat H = findHomography(corners1, corners2);
136    //! [estimate homography]
137
138    //! [decompose-homography-estimated-by-findHomography]
139    solutions = decomposeHomographyMat(H, cameraMatrix, Rs_decomp, ts_decomp, normals_decomp);
140    cout << "Decompose homography matrix estimated by findHomography():" << endl << endl;
141    for (int i = 0; i < solutions; i++)
142    {
143      double factor_d1 = 1.0 / d_inv1;
144      Mat rvec_decomp;
145      Rodrigues(Rs_decomp[i], rvec_decomp);
146      cout << "Solution " << i << ":" << endl;
147      cout << "rvec from homography decomposition: " << rvec_decomp.t() << endl;
148      cout << "rvec from camera displacement: " << rvec_1to2.t() << endl;
149      cout << "tvec from homography decomposition: " << ts_decomp[i].t() << " and scaled by d: " << factor_d1 * ts_decomp[i].t() << endl;
150      cout << "tvec from camera displacement: " << t_1to2.t() << endl;
151      cout << "plane normal from homography decomposition: " << normals_decomp[i].t() << endl;
152      cout << "plane normal at camera 1 pose: " << normal1.t() << endl << endl;
153    }
154    //! [decompose-homography-estimated-by-findHomography]
155}
156
157const char* params
158    = "{ help h         |       | print usage }"
159      "{ image1         | left02.jpg | path to the source chessboard image }"
160      "{ image2         | left01.jpg | path to the desired chessboard image }"
161      "{ intrinsics     | left_intrinsics.yml | path to camera intrinsics }"
162      "{ width bw       | 9     | chessboard width }"
163      "{ height bh      | 6     | chessboard height }"
164      "{ square_size    | 0.025 | chessboard square size }";
165}
166
167int main(int argc, char *argv[])
168{
169    CommandLineParser parser(argc, argv, params);
170
171    if ( parser.has("help") )
172    {
173        parser.about( "Code for homography tutorial.\n"
174                      "Example 4: decompose the homography matrix.\n" );
175        parser.printMessage();
176        return 0;
177    }
178
179    Size patternSize(parser.get<int>("width"), parser.get<int>("height"));
180    float squareSize = (float) parser.get<double>("square_size");
181    decomposeHomography(parser.get<String>("image1"),
182                        parser.get<String>("image2"),
183                        patternSize, squareSize,
184                        parser.get<String>("intrinsics"));
185
186    return 0;
187}