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

An example program about homography from the camera displacement Check the corresponding tutorial for more details

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