我可以简单地添加仿射或透视(单应性)变换矩阵吗?
Can I simply add affine or perspective (homography) matrices of transformation?
众所周知,在 OpenCV 中我可以在 2 个图像之间进行仿射或透视变换:
- M - affine transformation - 通过使用
estimateRigidTransform()
- H - perspective (homography) transformation - 通过使用
FeatureDetector
(SIFT, SURF, BRISK, FREAK, ...), 然后 FlannBasedMatcher
和 findHomography()
那我可以做:
- 仿射变换 - 通过使用
warpAffine(img_src, img_dst, M)
- 透视变换 - 通过使用
warpPerspective(img_src, img_dst, H)
但如果我有 3 张或更多图片,并且我已经找到:
- 仿射:M1(img1 -> img2),M2(img2 -> img3)
- 透视:H1(img1 -> img2),H2(img2 -> img3)
那我可以通过简单地添加两个矩阵来得到变换矩阵(img1 -> img3)吗?
- 仿射变换的:
M3 = M1 + M2;
- 透视变换的:
H3 = H1 + H2;
或者我应该使用哪个函数?
不,您需要乘以矩阵才能获得级联效果。我不会深入研究数学,但对坐标应用变换是执行矩阵乘法的问题。如果你想知道为什么会这样,我建议你参考这个 good Wikipedia article on cascading matrix transformations。给定一个坐标 X
和一个变换矩阵 M
,您可以通过以下方式获得输出坐标 Y
:
Y = M*X
这里我使用*
来指代矩阵乘法而不是逐元素乘法。你有一对变换矩阵,从 img1
到 img2
然后 img2
到 img3
。你需要做两次手术。因此,要从 img1
到 img2
,其中 X
属于 img1
的坐标 space,我们有(假设我们使用的是仿射矩阵):
Y1 = M1*X
下一步,从 img2
到 img3
,我们有:
Y2 = M2*Y1 --> Y2 = M2*M1*X --> Y2 = M3*X --> M3 = M2*M1
因此,要获得所需的连锁效应,您需要创建一个新矩阵,使 M2
乘以 M1
。与 H2
和 H1
.
相同
因此定义一个新矩阵,使得:
cv::Mat M3 = M2*M1;
与投影矩阵类似,您可以:
cv::Mat H3 = H2*H1;
但是,estimateRigidTransform
(在您的情况下输出为 M
)为您提供了一个 2 x 3
矩阵。一个技巧是扩充这个矩阵,使它变成 3 x 3,我们添加一个额外的行,其中除了最后一个元素 之外全为 0 ,它被设置为 1。因此,你最后一行会变成 [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
和*
运算符时,it is overloaded to specifically perform matrix multiplication.
然后您可以将 M3
和 H3
分别用于 warpAffine
和 warpPerspective
。
希望对您有所帮助!
众所周知,在 OpenCV 中我可以在 2 个图像之间进行仿射或透视变换:
- M - affine transformation - 通过使用
estimateRigidTransform()
- H - perspective (homography) transformation - 通过使用
FeatureDetector
(SIFT, SURF, BRISK, FREAK, ...), 然后FlannBasedMatcher
和findHomography()
那我可以做:
- 仿射变换 - 通过使用
warpAffine(img_src, img_dst, M)
- 透视变换 - 通过使用
warpPerspective(img_src, img_dst, H)
但如果我有 3 张或更多图片,并且我已经找到:
- 仿射:M1(img1 -> img2),M2(img2 -> img3)
- 透视:H1(img1 -> img2),H2(img2 -> img3)
那我可以通过简单地添加两个矩阵来得到变换矩阵(img1 -> img3)吗?
- 仿射变换的:
M3 = M1 + M2;
- 透视变换的:
H3 = H1 + H2;
或者我应该使用哪个函数?
不,您需要乘以矩阵才能获得级联效果。我不会深入研究数学,但对坐标应用变换是执行矩阵乘法的问题。如果你想知道为什么会这样,我建议你参考这个 good Wikipedia article on cascading matrix transformations。给定一个坐标 X
和一个变换矩阵 M
,您可以通过以下方式获得输出坐标 Y
:
Y = M*X
这里我使用*
来指代矩阵乘法而不是逐元素乘法。你有一对变换矩阵,从 img1
到 img2
然后 img2
到 img3
。你需要做两次手术。因此,要从 img1
到 img2
,其中 X
属于 img1
的坐标 space,我们有(假设我们使用的是仿射矩阵):
Y1 = M1*X
下一步,从 img2
到 img3
,我们有:
Y2 = M2*Y1 --> Y2 = M2*M1*X --> Y2 = M3*X --> M3 = M2*M1
因此,要获得所需的连锁效应,您需要创建一个新矩阵,使 M2
乘以 M1
。与 H2
和 H1
.
因此定义一个新矩阵,使得:
cv::Mat M3 = M2*M1;
与投影矩阵类似,您可以:
cv::Mat H3 = H2*H1;
但是,estimateRigidTransform
(在您的情况下输出为 M
)为您提供了一个 2 x 3
矩阵。一个技巧是扩充这个矩阵,使它变成 3 x 3,我们添加一个额外的行,其中除了最后一个元素 之外全为 0 ,它被设置为 1。因此,你最后一行会变成 [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
和*
运算符时,it is overloaded to specifically perform matrix multiplication.
然后您可以将 M3
和 H3
分别用于 warpAffine
和 warpPerspective
。
希望对您有所帮助!