我可以简单地将仿射或透视(单应性)变换矩阵相加吗?

10
众所周知,在OpenCV中,我可以获取两个图像之间的仿射或透视变换:
- 通过使用estimateRigidTransform()得到的仿射变换 - 由M计算得出 - 通过使用FeatureDetector(如SIFT,SURF,BRISK,FREAK等),然后通过FlannBasedMatcher和findHomography()得到的透视(单应性)变换 - 由H计算得出
然后我可以通过以下方式进行变换:
- 通过warpAffine(img_src, img_dst, M)进行仿射变换 - 通过warpPerspective(img_src, img_dst, H)进行透视变换
但是如果我有三个或更多的图像,并且我已经找到了:
  • 仿射变换:M1 (img1 -> img2),M2 (img2 -> img3)
  • 透视变换:H1 (img1 -> img2),H2 (img2 -> img3)

那么我是否可以通过简单地添加两个矩阵来获得变换矩阵 (img1 -> img3)?

  • 对于仿射变换:M3 = M1 + M2;
  • 对于透视变换:H3 = H1 + H2;

或者,我应该使用哪个函数来实现这个功能呢?

1个回答

9
不,你需要将矩阵相乘以实现级联效果。我不会详细解释数学,但将变换应用到坐标上是通过执行矩阵乘法来完成的。如果你想知道为什么这样做,请参考这篇关于级联矩阵变换的优秀维基百科文章。给定坐标X和变换矩阵M,可以通过以下方式获得输出坐标Y
Y = M*X

在这里,我使用*来指代矩阵乘法,而非逐元素乘法。你所拥有的是一对转换矩阵,这些矩阵从img1img2,然后再从img2img3。你需要执行两次操作。因此,为了从img1img2(其中X属于img1的坐标空间),我们可以采用以下方法(假设我们正在使用仿射矩阵):

Y1 = M1*X

接下来,从img2img3,我们有:

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

因此,为了获得所需的链式效应,您需要创建一个新矩阵,使M2乘以M1。与H2H1相同。
因此,定义一个新矩阵如下:
cv::Mat M3 = M2*M1;

同样的,对于你的投影矩阵,你可以执行以下操作:
cv::Mat H3 = H2*H1;

然而,estimateRigidTransform(在您的情况下输出为M)会给出一个2 x 3矩阵。一个技巧是增加该矩阵的维度,使其变为3 x 3,其中我们添加一行,除了最后一个元素设置为1之外,其余都是0。因此,您将得到最后一行,使其成为[0 0 1]。您需要对两个矩阵都这样做,然后将它们相乘,再将前两行提取到一个新矩阵中,以输入到warpAffine中。因此,请执行以下操作:
// 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);

处理 cv::Mat* 运算符时,它被重载以执行特定的矩阵乘法操作
然后可以将 M3H3 分别用于 warpAffinewarpPerspective
希望能对您有所帮助!

非常感谢!但是只有当第一个矩阵的列数等于第二个矩阵的行数时,才能进行矩阵乘法。H-透视(单应性)是一个3x3矩阵,我可以这样做:H3 = H1*H2;。但是仿射矩阵是一个2x3矩阵,我不能简单地将两个仿射矩阵相乘,我不能这样做:M3 = M1*M2;。那么我该如何对仿射变换-M进行操作呢? - Alex
estimateRigidTransform(); 给我一个 2x3 的矩阵,而 estimateAffine3D(); 给我一个 3x4 的矩阵。 - Alex
@Alex - 我已添加信息来帮助你使用estimateRigidTransform。对于之前没有发现的问题,我感到抱歉。但是,estimateAffine3D处理的是一个三维点云到另一个三维点云之间的转换。你不能用它来扭曲图像,因为图像本质上是一个二维坐标空间。 - rayryeng
@Alex - 确保你把顺序搞对了。应该是 H2*H1M2*M1 - rayryeng
1
@Alex - 不客气。祝你好运! - rayryeng
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接