0、国外原文链接
Homography examples using OpenCV ( Python / C ++ )
1、什么是单应性?
考虑图中显示的平面。红点代表两个图像中的相同物理点。在计算机视觉术语中,我们将这些对应点。图中显示了四种不同颜色的四个对应点:红色、绿色、黄色和橙色。单应性(3 * 3的矩阵)是指从图像中的一个点映射到另一张图像中的对应点的变换。
3 * 3的单应矩阵可以写成如下形式:
H
=
[
h
00
h
01
h
02
h
10
h
11
h
12
h
20
h
21
h
22
]
H=\left[\begin{array}{lll} h_{00} & h_{01} & h_{02} \\ h_{10} & h_{11} & h_{12} \\ h_{20} & h_{21} & h_{22} \end{array}\right]
H=⎣⎡h00h10h20h01h11h21h02h12h22⎦⎤
让我们考虑两幅图中对应的第一组点,单应性矩阵通过如下方式进行映射:
[
x
1
y
1
1
]
=
H
[
x
2
y
2
1
]
=
[
h
00
h
01
h
02
h
10
h
11
h
12
h
20
h
21
h
22
]
[
x
2
y
2
1
]
\left[\begin{array}{c} x_{1} \\ y_{1} \\ 1 \end{array}\right]=H\left[\begin{array}{c} x_{2} \\ y_{2} \\ 1 \end{array}\right]=\left[\begin{array}{lll} h_{00} & h_{01} & h_{02} \\ h_{10} & h_{11} & h_{12} \\ h_{20} & h_{21} & h_{22} \end{array}\right]\left[\begin{array}{c} x_{2} \\ y_{2} \\ 1 \end{array}\right]
⎣⎡x1y11⎦⎤=H⎣⎡x2y21⎦⎤=⎣⎡h00h10h20h01h11h21h02h12h22⎦⎤⎣⎡x2y21⎦⎤
只要它们在现实世界的同一平面上,上述等式都是成立的。换句话说,可以将单应性应用到第一张图上,第一张图中的书就会和第二张图中的书对齐。
上式中的xy坐标是齐次坐标,关于其次坐标可以参考我这篇文章,什么是齐次坐标?为什么要引入齐次坐标?
但是那些不在平面上的点呢?不在平面上的点不会通过单应性对齐,场景是平面,或者近似平面,或者低视差时都可以应用单应性矩阵。
如果图像中有两个平面呢?两个平面对应两个单应变换,每个平面对应一个单应性矩阵。
2、使用OpenCV的单应性示例
如果两个图像之间的单应性已知,我们可以将一个图像扭曲到另一个图像上。注意图像必须包含在一个平面上,只有同一平面上才能正确对齐。事实证明,如果你拍一张任何场景的照片(不仅仅是一个平面),然后通过旋转相机拍摄第二张照片,这两张图像通过单应性联系在一起。换句话说,可以将相机安装在三脚架上并拍拍照,然后绕着垂直轴并拍摄另一张图像。
3、如何计算单应性矩阵?
为了计算两个图像之间的单应性,需要知道两个图像之间的至少4个点对应关系,多余4个对应点更好。估计出最适合所有对应点的单应性矩阵。通常,这些点对应是通过图像之间的SIFT或SURF等特征匹配自动找到的。
3.1、C++
// pts_src 和 pts_dst 是源图像和目标图像中点的向量。它们的类型为 vector<Point2f>。至少需要4个对应点。
Mat h = findHomography(pts_src, pts_dst);
// 单应性矩阵可以将原图像扭曲到目标图像,im_src 和 im_dst 属于 Mat 类型。 size 是 im_dst 的大小(宽、高)。
warpPerspective(im_src, im_dst, h, size);
3.2、C++完整代码,从两张图中分别取4个对应的点,并将原图扭曲到目标图
#include "opencv2/opencv.hpp"
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
// Read source image.
Mat im_src = imread("book2.jpg");
// Four corners of the book in source image
vector<Point2f> pts_src;
pts_src.push_back(Point2f(141, 131));
pts_src.push_back(Point2f(480, 159));
pts_src.push_back(Point2f(493, 630));
pts_src.push_back(Point2f(64, 601));
// Read destination image.
Mat im_dst = imread("book1.jpg");
// Four corners of the book in destination image.
vector<Point2f> pts_dst;
pts_dst.push_back(Point2f(318, 256));
pts_dst.push_back(Point2f(534, 372));
pts_dst.push_back(Point2f(316, 670));
pts_dst.push_back(Point2f(73, 473));
// Calculate Homography
Mat h = findHomography(pts_src, pts_dst);
// Output image
Mat im_out;
// Warp source image to destination based on homography
warpPerspective(im_src, im_out, h, im_dst.size());
// Display images
imshow("Source Image", im_src);
imshow("Destination Image", im_dst);
imshow("Warped Source Image", im_out);
waitKey(0);
}
3.3、Python
h, status = cv2.findHomography(pts_src, pts_dst)
im_dst = cv2.warpPerspective(im_src, h, size)
3.4、Python完整代码
#!/usr/bin/env python
import cv2
import numpy as np
if __name__ == '__main__' :
# Read source image.
im_src = cv2.imread('book2.jpg')
# Four corners of the book in source image
pts_src = np.array([[141, 131], [480, 159], [493, 630],[64, 601]])
# Read destination image.
im_dst = cv2.imread('book1.jpg')
# Four corners of the book in destination image.
pts_dst = np.array([[318, 256],[534, 372],[316, 670],[73, 473]])
# Calculate Homography
h, status = cv2.findHomography(pts_src, pts_dst)
# Warp source image to destination based on homography
im_out = cv2.warpPerspective(im_src, h, (im_dst.shape[1],im_dst.shape[0]))
# Display images
cv2.imshow("Source Image", im_src)
cv2.imshow("Destination Image", im_dst)
cv2.imshow("Warped Source Image", im_out)
cv2.waitKey(0)
4、单应性的应用
单应图最有趣的应用无疑是制作全景图(也就是图像拼接和图像拼接)。
4.1、使用单应性进行透视校正
点击上面图像中书本的四个角落,就可以快速获取下图所示的书本图像。
- 首先获取原图像四个角点坐标,称为pts_src;
- 获取书的长宽比,对于这本书,长宽比为4比3,所以可以选择输出图像尺寸为300*400,目标点pts_dst为(0, 0)、(299, 0)、(299, 399)、(0, 399);
- 使用pts_src和pts_dst获得单应性矩阵;
- 将单应性应用于源图像,获得目标图像;
4.2、虚拟广告牌
在许多电视转播的体育赛事中,广告实际上是在视频直播中插入的。例如,在足球和棒球比赛中,恰好位于球场边界外的小广告牌上的广告实际上是可以改变的。与向所有人展示相同的广告不同,广告商可以根据用户的人口特征、位置等选择广告。
在这些应用中,在视频中检测广告牌的四个角作为目的点。广告的四个角作为源点。基于这四个对应点计算单应性,并将其用于将广告扭曲到视频帧中。如下图所示,将广告插入到图像中,我们可以把时代广场的广告牌换成我们想要的形象。
- 首先获取广告牌的四个角点坐标,pts_dst;
- 想要放在虚拟广告牌上的源图像的大小为w * h,因此图像的角为(0, 0)、(w-1, 0)、(w -1, h -1)、(0, h - 1);
- 使用pts_src和pts_dst获得单应性矩阵;
- 对源图像进行单应性处理,将源图像与目标图像混合;