10

As known, in OpenCV I can get affine or perspective transformation between 2 images:

Then I can do:

  • affine transformation - by using warpAffine(img_src, img_dst, M)
  • perspective transformation - by using warpPerspective(img_src, img_dst, H)

But if I have 3 or more images, and I already found:

  • affine: M1 (img1 -> img2), M2 (img2 -> img3)
  • perspective: H1 (img1 -> img2), H2 (img2 -> img3)

then can I get matix of transformation (img1 -> img3) by simply add two matrix?

  • of an affine transform: M3 = M1 + M2;
  • of an perspective transform: H3 = H1 + H2;

Or which of functions should I use for this?

Alex
  • 12,578
  • 15
  • 99
  • 195

1 Answers1

9

No, you need to multiply the matrices to get the cascaded effect. I won't go into the math, but applying a transformation to coordinates is a matter of performing a matrix multiplication. If you are however curious as to know why that is, I refer you to this good Wikipedia article on cascading matrix transformations. Given a coordinate X and a transformation matrix M, you get the output coordinate Y by:

Y = M*X

Here I use * to refer to matrix multiplication as opposed to element-wise multiplication. What you have is a pair of transformation matrices which go from img1 to img2 then img2 to img3. You'll need to do the operation twice. So to go from img1 to img2 where X belongs to the coordinate space of img1, we have (assuming we're using the affine matrices):

Y1 = M1*X

Next, to go from img2 to img3, we have:

Y2 = M2*Y1 --> Y2 = M2*M1*X --> Y2 = M3*X --> M3 = M2*M1

Therefore, to get the desired chain effect, you need to create a new matrix such that M2 is multiplied by M1. Same as H2 and H1.

So define a new matrix such that:

cv::Mat M3 = M2*M1;

Similarly for your projective matrices, you can do:

cv::Mat H3 = H2*H1;

However, estimateRigidTransform (the output is M in your case) gives you a 2 x 3 matrix. One trick is to augment this matrix so that it becomes 3 x 3 where we add an additional row where it is all 0 except for the last element, which is set to 1. Therefore, you would have the last row such that it becomes [0 0 1]. You would do this for both matrices, multiply them, then extract just the first two rows into a new matrix to pipe into warpAffine. Therefore, do something like this:

// Create padded matrix for M1
cv::Mat M1new = cv::Mat(3,3,M1.type());
M1new.at<double>(0,0) = M1.at<double>(0,0);
M1new.at<double>(0,1) = M1.at<double>(0,1);
M1new.at<double>(0,2) = M1.at<double>(0,2);

M1new.at<double>(1,0) = M1.at<double>(1,0);
M1new.at<double>(1,1) = M1.at<double>(1,1);
M1new.at<double>(1,2) = M1.at<double>(1,2);

M1new.at<double>(2,0) = 0.0;
M1new.at<double>(2,1) = 0.0;
M1new.at<double>(2,2) = 1.0;

// Create padded matrix for M2
cv::Mat M2new = cv::Mat(3,3,M2.type());
M2new.at<double>(0,0) = M2.at<double>(0,0);
M2new.at<double>(0,1) = M2.at<double>(0,1);
M2new.at<double>(0,2) = M2.at<double>(0,2);

M2new.at<double>(1,0) = M2.at<double>(1,0);
M2new.at<double>(1,1) = M2.at<double>(1,1);
M2new.at<double>(1,2) = M2.at<double>(1,2);

M2new.at<double>(2,0) = 0.0;
M2new.at<double>(2,1) = 0.0;
M2new.at<double>(2,2) = 1.0;

// Multiply the two matrices together
cv::Mat M3temp = M2new*M1new;

// Extract out relevant rows and place into M3
cv::Mat M3 = cv::Mat(2, 3, M3temp.type());
M3.at<double>(0,0) = M3temp.at<double>(0,0);
M3.at<double>(0,1) = M3temp.at<double>(0,1);
M3.at<double>(0,2) = M3temp.at<double>(0,2);

M3.at<double>(1,0) = M3temp.at<double>(1,0);
M3.at<double>(1,1) = M3temp.at<double>(1,1);
M3.at<double>(1,2) = M3temp.at<double>(1,2);

When dealing with cv::Mat and the * operator, it is overloaded to specifically perform matrix multiplication.

You can then use M3 and H3 into warpAffine and warpPerspective respectively.


Hope this helps!

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Thank you very much! But matrix multiplication can be done only if number of columns in 1-st matrix equal to the number of rows in 2-nd matrix. **H - perspective (homography) is a 3x3 matrix**, and **I can do: `H3 = H1*H2;`**. But **affine matrix is a 2x3** and I can't simply multiplicy two affine matricies, **I can't do: `M3 = M1*M2;`**. How can I do this for the Affine Transformation - M? – Alex Apr 21 '15 at 11:16
  • `estimateRigidTransform();` gives me 2x3 matrix, `and estimateAffine3D();` gives me 3x4 matrix. – Alex Apr 21 '15 at 11:19
  • @Alex - I've added information to help you with `estimateRigidTransform`. My apologies for not seeing that earlier. However, `estimateAffine3D` deals with the transformation between one 3D point cloud to another. You can't use that to warp images because images are inherently a 2D coordinate space. – rayryeng Apr 21 '15 at 15:01
  • @Alex - Make sure you get the order right. It's `H2*H1` and `M2*M1`. – rayryeng Apr 21 '15 at 15:07
  • A perspective transformation is non-linear even though it can be written in matrix form, since the resulted vector needs to be divided by a factor determined by the original vector. Are you sure H3=H1*H2 is valid in this case? An inverse homography (i.e. getting from img1's to img2's perspective when H is getting from img2's to img1's perspective) is not the inverse matrix of H. – Mr.WorshipMe Mar 01 '18 at 12:53
  • @MrWorshipMe that's correct. I'll modify when I'm able to. Thanks. – rayryeng Mar 01 '18 at 13:01