针对针孔相机模型在工业检测中应用所会遇到的情况进行总结。镜头畸变的原理,内参外参的标定,各个坐标系之间的转换关系。
一 针孔相机
针孔相机来源于初中物理肯本上的经典实验,小孔成像。所以在分析的时候利用相似三角形进行分析,经典的针孔相机几何模型如图所示。
由图一可知
在实际使用过程中,会通过归一化来方便计算,归一化就是让成像平面距离远点O的距离为单位1的坐标。
二 相机标定
由于相机在生产和使用过程中的误差,不可避免的会导致误差,通常是桶形畸变或者枕形畸变会导致画面并非平面,而会在边缘处略微弯曲;如果在组装过程中,镜片和成像平面无法平行,则会引起切向畸变,导致整个平面倾斜。使用标定的方式去弥补这部分误差。
畸变矫正一般都是先校正,再讨论空间坐标。
三 坐标系转换
这是绕Z轴做旋转时所得到到旋转矩阵R1,绕X轴Y轴时会有R2,R3的矩阵。旋转矩阵加上平移向量,则可完成两个坐标之间的转换。
由世界坐标系到像素坐标系的关系如下图所示
四 三维坐标系转换
在三维空间中直接进行坐标转换,当知道具体旋转角度时可以直接代入公式中计算出旋转矩阵。当知道两个坐标系对应点时,可以采用罗德里格矩阵模型求出旋转矩阵,直接求解会导致误差过大,使用非线性优化的方式减少误差。或使用SVD手眼标定。
相关资料:
基于罗德里格矩阵的空间坐标转换 韩梦泽
三维坐标转换精度及其影响因素的研究 张皓琳
相机标定(三)——手眼标定_white_Learner的博客-CSDN博客
import numpy as np
# 代码表明,使用罗德里格求出旋转矩阵误差为0.001
def caculate_cross_matrix(old_coordinate, R):
a = R*old_coordinate
return a
def caculate_Rodrigues(old_point, new_point):
o1 = old_point[:, 0]
o2 = old_point[:, 1]
o3 = old_point[:, 2]
x1, y1, z1 = float(o1[0]), float(o1[1]), float(o1[2])
x2, y2, z2 = float(o2[0]), float(o2[1]), float(o2[2])
x3, y3, z3 = float(o3[0]), float(o3[1]), float(o3[2])
n1 = new_point[:, 0]
n2 = new_point[:, 1]
n3 = new_point[:, 2]
u1, v1, w1 = float(n1[0]), float(n1[1]), float(n1[2])
u2, v2, w2 = float(n2[0]), float(n2[1]), float(n2[2])
u3, v3, w3 = float(n3[0]), float(n3[1]), float(n3[2])
u21 = u2-u1
v21 = v2-v1
w21 = w2-w1
x21 = x2-x1
y21 = y2-y1
z21 = z2-z1
u31 = u3-u1
v31 = v3-v1
w31 = w3-w1
x31 = x3-x1
y31 = y3-y1
z31 = z3-z1
L = np.matrix([[u21-x21],
[v21-y21],
[w21-z21],
[u31-x31],
[v31-y31],
[w31-z31]])
A = np.matrix([[0, -z21-w21, -y21-v21],
[-z21-w21, 0, x21+u21],
[y21+v21, x21+u21, 0],
[0, -z31-w31, -y31-v31],
[-z31-w31, 0, x31+u31],
[y31+v31, x31+u31, 0]])
# 直接求解很难
# a = np.linalg.solve(B, L)
# 最小二乘法间接求
X = np.linalg.inv(((A.T)*A))*(A.T)*L
a, b, c = float(X[0]), float(X[1]), float(X[2])
tmp = 1/(1+a*a+b*b+c*c)
R = tmp * np.matrix([
[1+a**2-b**2-c**2, -2*c-2*a*b, -2*b+2*a*c],
[2*(c-a*b), 1-a*2+b*2-c*2, -2*a-2*c*b],
[2*(b+a*c), 2*(a-b*c), 1-a**2-b**2+c**2]])
return R
# 旋转角度依次为 20,40,60
R3 = np.matrix([[0.2795, -0.9237, -0.2620],
[0.6634, 0.3830, -0.6428],
[0.6941, 0.0058, 0.7198]])
# 原坐标系坐标
old_coordinate = np.matrix([[5, 10, 20],
[8, 10, 30],
[15, 10, 40]])
# 文章给的另一组坐标
new_coordinate_paper = np.matrix([[-9.9225, -3.2606, 14.3147],
[-9.0627, 4.0365, 14.1977],
[-32.6027, -0.9526, 42.8504]])
new_coordinate_paper = new_coordinate_paper.T
# 通过旋转矩阵计算得到心坐标系坐标
new_coordinate = caculate_cross_matrix(old_coordinate, R3)
R = caculate_Rodrigues(old_coordinate, new_coordinate_paper)
print("知道角度后的旋转矩阵\n",R3)
print("通过罗德里格矩阵求得旋转矩阵\n",R)