Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

One solution is to interpolate directly the elements of the homography matrix, see @LBerger answer.

Here a video result.


Another possibility consists in exploiting the underlying geometrical meaning of the homography transformation, that is the transformation induced by a camera displacement for a planar scene and a generic motion. See this tutorial for some information.

Here a video result.


Most likely it is the first solution that you want and that is also the simplest.

Nevertheless, it is important to recall what the homography transformation means physically.

The code to produce the results:

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

namespace
{
  enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };

  void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
  {
    corners.resize(0);

    switch (patternType) {
    case CHESSBOARD:
    case CIRCLES_GRID:
      for (int i = 0; i < boardSize.height; i++)
        for (int j = 0; j < boardSize.width; j++)
          corners.push_back(Point3f(float(j*squareSize),
            float(i*squareSize), 0));
      break;

    case ASYMMETRIC_CIRCLES_GRID:
      for (int i = 0; i < boardSize.height; i++)
        for (int j = 0; j < boardSize.width; j++)
          corners.push_back(Point3f(float((2 * j + i % 2)*squareSize),
            float(i*squareSize), 0));
      break;

    default:
      CV_Error(Error::StsBadArg, "Unknown pattern type\n");
    }
  }

  Matx33d computeHomography(const Matx33d& R_1to2, const Matx31d& tvec_1to2, double d_inv, const Matx31d& normal)
  {
    Matx33d homography = R_1to2 + d_inv * tvec_1to2*normal.t();
    return homography;
  }
}

int main()
{
  Mat img1 = imread("left01.jpg");
  Mat img2 = imread("left02.jpg");
  Mat img3 = imread("left03.jpg");

  Size patternSize(9, 6);
  vector<Point2f> corners1, corners2, corners3;
  bool found1 = findChessboardCorners(img1, patternSize, corners1);
  bool found2 = findChessboardCorners(img2, patternSize, corners2);
  bool found3 = findChessboardCorners(img3, patternSize, corners3);

  if (!found1 || !found2 || !found3) {
    cout << "Error, cannot find the chessboard corners in images." << endl;
    return 0;
  }

  Matx33d H_1_to_2 = findHomography(corners1, corners2);
  cout << "H_1_to_2:\n" << H_1_to_2 << endl;

  Matx33d H_1_to_3 = findHomography(corners1, corners3);
  cout << "H_1_to_3:\n" << H_1_to_3 << endl;

  int ninterp = 10;
  // interpolation 1
  std::cout << "\nINTERPOLATION 1" << std::endl;
  for (int idx = 1; idx <= ninterp; idx++) {
    Matx33d H_2_to_3_interp = Matx33d::eye();
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3; j++) {
        H_2_to_3_interp(i, j) = (H_1_to_3(i, j) - H_1_to_2(i, j)) * (idx / static_cast<double>(ninterp));
      }
    }

    Matx33d H_1_to_3_interp = H_1_to_2 + H_2_to_3_interp;
    cout << "H_1_to_3_interp:\n" << H_1_to_3_interp << endl;

    Mat img3_warp;
    warpPerspective(img1, img3_warp, H_1_to_3_interp, img1.size());

    Mat img_draw_warp3;
    hconcat(img2, img3_warp, img_draw_warp3);
    hconcat(img_draw_warp3, img3, img_draw_warp3);

    imshow("H_1_to_3_interp", img_draw_warp3);
    waitKey();
  }

  // interpolation 2
  Matx33d H_2_to_3 = H_1_to_3 * H_1_to_2.inv();
  double h33_inv = 1 / H_2_to_3(2, 2);
  H_2_to_3 = H_2_to_3 * h33_inv;

  std::cout << "\nINTERPOLATION 2" << std::endl;
  vector<Mat> Rs_decomp, ts_decomp, normals_decomp;
  Matx33d cameraMatrix = Matx33d::eye();
  int solutions = decomposeHomographyMat(H_2_to_3, cameraMatrix, Rs_decomp, ts_decomp, normals_decomp);
  std::cout << "solutions: " << solutions << std::endl;
  if (solutions == 0) {
    return 0;
  }

  for (int solution = 0; solution < solutions; solution++) {
    std::cout << "\nsolution number: " << solution << std::endl;
    Matx31d rvec, tvec, normal;
    tvec = ts_decomp[solution];
    normal = normals_decomp[solution];
    Matx33d R = Rs_decomp[solution];

    std::cout << "\nH_2_to_3:\n" << H_2_to_3 << std::endl;
    Matx33d H_2_to_3_reconstruct = computeHomography(R, tvec, 1, normal);
    double h33_reconstruct_inv = 1 / H_2_to_3_reconstruct(2, 2);
    H_2_to_3_reconstruct = H_2_to_3_reconstruct * h33_reconstruct_inv;
    std::cout << "H_2_to_3_reconstruct:\n" << H_2_to_3_reconstruct << std::endl;

    Rodrigues(Rs_decomp[solution], rvec);
    double angle = norm(rvec);
    Matx31d axis = rvec * (1 / angle);
    for (int idx = 1; idx <= ninterp; idx++) {
      double factor = idx / static_cast<double>(ninterp);
      double angle_interp = factor * angle;
      Matx31d rvec_interp = axis * angle_interp;
      Matx33d R_interp;
      Rodrigues(rvec_interp, R_interp);
      Matx31d tvec_interp = factor * tvec;
      Matx31d normal_interp = factor * normal;

      Matx33d H_2_to_3_interp = computeHomography(R_interp, tvec_interp, 1, normal_interp);
      Matx33d H_1_to_3_interp = H_2_to_3_interp * H_1_to_2;
      double h33_1_to_3_interp_inv = 1 / H_1_to_3_interp(2, 2);
      H_1_to_3_interp = H_1_to_3_interp * h33_1_to_3_interp_inv;
      cout << "H_1_to_3_interp:\n" << H_1_to_3_interp << endl;

      Mat img3_warp;
      warpPerspective(img1, img3_warp, H_1_to_3_interp, img1.size());

      Mat img_draw_warp3;
      hconcat(img2, img3_warp, img_draw_warp3);
      hconcat(img_draw_warp3, img3, img_draw_warp3);

      imshow("H_1_to_3_interp", img_draw_warp3);
      waitKey();
    }
  }
}