Can I just add affine or perspective (homographic) transformation matrices?

As you know, in OpenCV I can get an affine or perspective conversion between two images:

Then I can do:

  • affine transformation - using warpAffine(img_src, img_dst, M)
  • perspective transformation - 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)
  • : H1 (img1 β†’ img2), H2 (img2 β†’ img3)

then can i get matix transforms ( img1 β†’ img3 ) by simply adding two matrices?

  • affine transformation: M3 = M1 + M2;
  • perspective conversion: H3 = H1 + H2;

Or which of the functions should I use for this?

+6
source share
1 answer

No, you need to multiply the matrices to get a cascading effect. I will not go into mathematics, but applying the transformation to the coordinates is the execution of matrix multiplication. If you are interested in knowing why this is so, I refer you to this good Wikipedia article on cascading matrix transformations . Given the X coordinate and the transformation matrix M , you get the coordinate of the output Y :

 Y = M*X 

Here I use * to denote matrix multiplication as opposed to element multiplication. You have a pair of transformation matrices that go from img1 to img2 , then from img2 to img3 . You will need to perform the operation twice. So, to go from img1 to img2 , where X belongs to the coordinate space img1 , we have (suppose we use 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, we define a new matrix so that:

 cv::Mat M3 = M2*M1; 

Similarly for your projective matrices you can:

 cv::Mat H3 = H2*H1; 

However, estimateRigidTransform (the output in your case is M ) gives you a 2 x 3 matrix. One trick is to enlarge this matrix so that it becomes 3 x 3, where we add an extra row, where everything is 0 except for the last element that is set to 1. Therefore, you will have the last row such that it becomes [0 0 1] . You would do this for both matrices, multiply them, and then extract only the first two rows into a new matrix for translation into warpAffine . So do the following:

 // 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 working with cv::Mat and the * operator, it is overloaded to specifically perform matrix multiplication .

Then you can use M3 and H3 in warpAffine and warpPerspective respectively.


Hope this helps!

+5
source

Source: https://habr.com/ru/post/985522/


All Articles