单应矩阵(Homography)基本概念和代码测试
§00 背景说明这是在 OpenCV网站 中给出的技术文章,讲述单应矩阵(Homography)的基本概念,并通过测试程序进行介绍。文章链接:Basic concepts of the homography explained with code介绍下面的辅导材料利用一些编程代码展示了单应变换(Homograpy)的一些基本概念。关于该理论的详细解释请参见机器视觉课程,或者计算机视觉参
简 介: 应用棋盘格图片或者相机图片中与标准棋盘格之间的单应矩阵。其中应用到opencv中的findChessboardCorners, findHomographys等函数。 这位利用单应矩阵进行下步的矫正工作提供实验基础。
关键词
: 单应矩阵,Homogrpaphy
§00 背景说明
这是在 OpenCV网站 中给出的技术文章,讲述单应矩阵(Homography)的基本概念,并通过测试程序进行介绍。
0.1 介绍
下面的辅导材料利用一些编程代码展示了单应变换(Homograpy)的一些基本概念。关于该理论的详细解释请参见机器视觉课程,或者计算机视觉参考书,比如:
- Multiple View in Computer Vision .
- An tinviation to 3D vision: From images to Geometric Models
- Computer Vision: Algorithms and Applications .
本文中的一些代码可以以下链接找到:
实验中所使用的图片可以在 这个图片链接 下载,但在国内点击无法访问该网站。
§01 基本理论
1.1 什么是单应矩阵?
简单的讲,平面中的单应矩阵涉及到两个平面中的变换(相差一个比例因子)。
其中3×3矩阵
H
H
H就是单应矩阵,由于在估计过程中可以相差一个比例因子,所以单应矩阵具有8个自由变量,通常情况下将该矩阵进行归一化,使得
h
33
=
1
h_{33} = 1
h33=1,或者
h
11
2
+
h
12
2
+
h
13
2
+
h
21
2
+
h
22
2
+
h
23
2
+
h
31
2
+
h
32
2
+
h
33
2
=
1
h_{11}^2 + h_{12}^2 + h_{13}^2 + h_{21}^2 + h_{22}^2 + h_{23}^2 + h_{31}^2 + h_{32}^2 + h_{33}^2 = 1
h112+h122+h132+h212+h222+h232+h312+h322+h332=1
下面的例子显示了不同的变换,但最终可以归纳到两个平面之间的转换矩阵。
- 从平面到像平面(图片从像平面获取)
▲ 图1.1.1 从平面转换到像平面
- 两个相机对同一平面取像
▲ 图1.1.2 两个相机对同一平面取像
- 围绕投影轴旋转相机:
等效考虑一个位于无穷远处的平面上的点。
▲ 图1.1.3 旋转的相机
1.2 如何获得单应矩阵?
如果相应计算两个图片之间的单应矩阵,只有需要确定两个图片中四个以上的对应点的位置。OpenCV通过稳定的算法获得两个图片之间的单应矩阵。可以使用SIFT, 或者SURF算法来寻找两个图片之间对应的像素点。
根据 CV2.findhomography: Things You Should Know 中给出的计算单应矩阵的方法:
假设如下两个点是对应的像素点坐标:
x = ( u v 1 ) , x ′ = ( x y 1 ) x = \left( \begin{matrix} u\\v\\1\\\end{matrix} \right),\,\,x' = \left( \begin{matrix} x\\y\\1\\\end{matrix} \right) x=⎝⎛uv1⎠⎞,x′=⎝⎛xy1⎠⎞
它们之间的单应矩阵为:
通过消去上述公式中的参数
c
c
c,可以得到如下方程:
A
h
=
0
Ah = 0
Ah=0
其中:
利用cv2
中的 findHomography(points1,points2)
可以计算两个图片之间的单应矩阵。
1.3 单应矩阵应用场合
-
利用共面点进行相机姿态估计,应用在基于某些标示(Apriltag)下的增强现实。
▲ 图1.2.1 在Apriltag上的增强虚拟现实
-
消除透视效应/视角校正(参见前面第二个例子)
▲ 图1.2.2 透视校正
- 全景图拼接(参见前面第二个、第三个例子)
▲ 图1.2.3 全景图拼接
§02 应用代码
2.1 演示1:从共面点阵进行姿态估计
请注意下面从单应矩阵估计相机姿态是一个应用例子,你需要使用 cv2::solvePnP 来对来自同一平面或者任意物体上的点来估计相机姿态。
单应矩阵可以使用 直接线性变换(DLT)算法来进行估计(见前面理论部分1)。由于对象是在同一平面,所以物体上的点都在同一平面上,在归一化相机成像框架中,描述共面点与像平面上成像位置之间的变换就是单应矩阵。只有当物体是平面,相机内参已知的情况下,相机的姿态才可以从单应矩阵中估计出来。
可以使用棋盘格以及cv2. findChessboardCorners() 得到图片中的角点。
要检测棋盘格角点,首先需要确认棋盘格尺寸(pattenSize),下图是9×6,:
vector<Point2f> corners;
bool found = findChessboardCorners(img, patternSize, corners);
▲ 图2.1.1 棋盘格中的角点
2.1.1 提取棋盘格角点
img = cv2.imread(outfile)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
if ret:
cv2.drawChessboardCorners(img, (9, 6), corners, ret)
else: print('Error:%s'%ret)
plt.clf()
plt.figure(figsize=(15,15))
plt.axis("off")
plt.imshow(img)
len(corners): 54
▲ 图2.1.2 提取角点 后的图像
2.1.2 计算相机校正参数
obj_p = zeros((9*6, 3), float32)
obj_p[:,:2] = mgrid[0:9, 0:6].T.reshape(-1, 2)
obj_points = []
obj_points.append(obj_p)
img_points = []
img_points.append(corners.reshape(-1, 2))
print(shape(obj_points), shape(img_points))
(1, 54, 3)
(1, 54, 2)
ret,mtx,dist,rvevs,twecs = cv2.calibrateCamera(obj_points, img_points, (9, 6), None, None)
print("ret: {}".format(ret), "mtx: {}".format(mtx), "dist: {}".format(dist), "rvevs: {}".format(rvevs), "twecs: {}".format(twecs))
ret: 0.4988758679961617
mtx: [[1.32143507e+03 0.00000000e+00 3.47757768e+01]
[0.00000000e+00 1.40656199e+03 7.85365051e+01]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
dist: [[ -3.15645355 11.01014726 0.21957824 0.5021844 -66.56563905]]
rvevs: [array([[-0.4002855 ],
[ 0.73265038],
[ 0.00263827]])]
twecs: [array([[ 3.79424711],
[ 0.96399769],
[37.96708 ]])]
calibrateCamera返回参数
- ret: 表示所有理想角点映射到图片上的距离与图片对应点之间距离的平方和。参见: Camera Calibration and 3D Reconstruction?
- mtx: 摄像头内部的参数;
- dist: 镜头失真参数: k 1 , k 2 , p 1 , p 2 , k 3 k_1 ,k_2 ,p_1 ,p_2 ,k_3 k1,k2,p1,p2,k3
- rvecs: 旋转矢量;可以通过Rodrigues转换成旋转矩阵;
- tvecs: 平移矢量。
2.1.3 计算单应矩阵
bps = obj_points[0][:,:2].reshape(-1, 1, 2)
ips = img_points[0].reshape(-1, 1, 2)
print(shape(bps), shape(ips))
matrix, mask = cv2.findHomography(bps, ips, cv2.RANSAC, 5.0)
print("matrix: {}".format(matrix), "mask: {}".format(mask))
(54, 1, 2)
(54, 1, 2)
matrix: [[ 3.13837780e+01 -3.42332647e+00 1.83431598e+02]
[-5.11097467e+00 3.73001120e+01 1.19897742e+02]
[-2.01067307e-02 -8.73882962e-03 1.00000000e+00]]
mask: [[1]
[1]
[1]
[1]
[1]
[1]
[0]
[0]
[0]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[0]
[0]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[0]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[0]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[0]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[0]
[0]]
※ 测试总结 ※
应用棋盘格图片或者相机图片中与标准棋盘格之间的单应矩阵。其中应用到opencv中的findChessboardCorners, findHomographys等函数。 这位利用单应矩阵进行下步的矫正工作提供实验基础。
■ 相关文献链接:
- OpenCV网站
- Basic concepts of the homography explained with code
- Multiple View in Computer Vision
- An tinviation to 3D vision: From images to Geometric Models
- Computer Vision: Algorithms and Applications
- C++代码
- Python代码
- Java代码
- 这个图片链接
- CV2.findhomography: Things You Should Know
- cv2::solvePnP
- findChessboardCorners()
● 相关图表链接:
- 图1.1.1 从平面转换到像平面
- 图1.1.2 两个相机对同一平面取像
- 图1.1.3 旋转的相机
- 图1.2.1 在Apriltag上的增强虚拟现实
- 图1.2.2 透视校正
- 图1.2.3 全景图拼接
- 图2.1.1 棋盘格中的角点
- 图2.1.2 提取角点 后的图像
#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST1.PY -- by Dr. ZhuoQing 2021-12-30
#
# Note:
#============================================================
from headm import * # =
import wget
import cv2
#------------------------------------------------------------
imageurl = 'https://docs.opencv.org/3.4/homography_pose.jpg'
outfile = '/home/aistudio/work/chessbimg1.jpg'
if not os.path.isfile(outfile):
wget.download(imageurl, outfile)
#------------------------------------------------------------
img = cv2.imread(outfile)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#------------------------------------------------------------
'''
plt.clf()
plt.figure(figsize=(10,10))
plt.axis("off")
plt.imshow(gray, cmap=plt.cm.gray)
'''
#------------------------------------------------------------
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
if ret:
cv2.drawChessboardCorners(img, (9, 6), corners, ret)
else: print('Error:%s'%ret)
plt.clf()
plt.figure(figsize=(15,15))
plt.axis("off")
plt.imshow(img)
#------------------------------------------------------------
obj_p = zeros((9*6, 3), float32)
obj_p[:,:2] = mgrid[0:9, 0:6].T.reshape(-1, 2)
obj_points = []
obj_points.append(obj_p)
img_points = []
img_points.append(corners.reshape(-1,2))
printt(shape(obj_points), shape(img_points))
#------------------------------------------------------------
ret,mtx,dist,rvevs,twecs = cv2.calibrateCamera(obj_points, img_points, (9, 6), None, None)
printt(ret:, mtx:, dist:)
printt(rvevs:, twecs:)
#------------------------------------------------------------
bps = obj_points[0][:,:2].reshape(-1, 1, 2)
ips = img_points[0].reshape(-1, 1, 2)
printt(shape(bps), shape(ips))
matrix, mask = cv2.findHomography(bps, ips, cv2.RANSAC, 5.0)
printt(matrix:, mask:)
#------------------------------------------------------------
matches_mask = mask.ravel().tolist()
printt(matches_mask)
#------------------------------------------------------------
# END OF FILE : TEST1.PY
#============================================================
更多推荐
所有评论(0)