跳转至

Transformation

img{loading=lazy}

总结来说就是相似三角形,\(\frac{x_{c}}{x}=\frac{y_{c}}{y}=\frac{z_{c}}{f}\),通过变换可得\(x=\frac{x_{c}}{z_{c}} f, y=\frac{y_{c}}{z_{c}} f\),由此得其增广形式

\[ \left(\begin{array}{l} x \\ y \\ 1 \end{array}\right)=\frac{1}{z_{c}}\left(\begin{array}{llll} f & 0 & 0 & 0 \\ 0 & f & 0 & 0 \\ 0 & 0 & 1 & 0 \end{array}\right)\left(\begin{array}{c} x_{c} \\ y_{c} \\ z_{c} \\ 1 \end{array}\right) \]

相机坐标系到机械臂坐标就是一个平移加旋转,一个矩阵搞定的,一个不够就连乘

如下是绕\(z\)轴旋转

\[ \left(\begin{array}{l} x_{c} \\ y_{c} \\ z_{c} \\ 1 \end{array}\right)=\left(\begin{array}{ccc} \cos \alpha & -\sin \alpha & 0 & 0\\ \sin \alpha & \cos \alpha & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right)\left(\begin{array}{l} x_{w} \\ y_{w} \\ z_{w} \\ 1 \end{array}\right) \]
\[ \left[\begin{array}{l} x \\ y \\ z \end{array}\right]=\left[\begin{array}{ccc} \cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{array}\right]\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \\ z^{\circ} \end{array}\right]=R_{1}\left[\begin{array}{l} x^{\prime} \\ y^{\circ} \\ z^{\circ} \end{array}\right] \]
\[ \left[\begin{array}{l} x \\ y \\ z \end{array}\right]=\left[\begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos \varphi & \sin \varphi \\ 0 & -\sin \varphi & \cos \varphi \end{array}\right]\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \\ 1 \end{array}\right]=R_{2}\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \\ z^{*} \end{array}\right] \]
\[ \left[\begin{array}{l} x \\ y \\ z \end{array}\right]=\left[\begin{array}{ccc} \cos \varphi & 0 & -\sin \varphi \\ 0 & 1 & 0 \\ \sin \varphi & 0 & \cos \varphi \end{array}\right]\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \\ 1 \end{array}\right]=R_{3}\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \\ z^{\prime} \end{array}\right] \]

可得到旋转矩阵\(R=R_1R_2R_3\)

\[ \left[\begin{array}{l} X_{c} \\ Y_{c} \\ Z_{c} \end{array}\right]=R\left[\begin{array}{l} X_{w} \\ Y_{w} \\ Z_{w} \end{array}\right]+T \]
\[ \left[\begin{array}{c} X_{c} \\ Y_{c} \\ Z_{c} \\ 1 \end{array}\right]=\left[\begin{array}{ll} R & T \\ \overrightarrow{0} & 1 \end{array}\right]\left[\begin{array}{c} X_{w} \\ Y_{w} \\ Z_{w} \\ 1 \end{array}\right], \mathrm{R}: 3 * 3, \mathrm{~T}: 3 * 1 \]

img{loading=lazy}

齐次 Homogeneous coordinate system

\[ \begin{aligned} (1,2,3) & \Rightarrow\left(\frac{1}{3}, \frac{2}{3}\right) \\ (2,4,6) & \Rightarrow\left(\frac{2}{6}, \frac{4}{6}\right)=\left(\frac{1}{3}, \frac{2}{3}\right) \\ (4,8,12) & \Rightarrow\left(\frac{4}{12}, \frac{8}{12}\right)=\left(\frac{1}{3}, \frac{2}{3}\right) \\ \vdots & \vdots \\ (1 a, 2 a, 3 a) & \Rightarrow\left(\frac{1 a}{3 a}, \frac{2 a}{3 a}\right)=\left(\frac{1}{3}, \frac{2}{3}\right) \end{aligned} \]

平移变换

\[ \left[\begin{array}{lll} x^{\prime} & y^{\prime} & 1 \end{array}\right]=\left[\begin{array}{lll} x & y & 1 \end{array}\right]\left|\begin{array}{ccc} 1 & 0 & 0 \\ 0 & 1 & 0 \\ \mathrm{~d} x & \mathrm{~d} y & 1 \end{array}\right| \]

旋转变换

\[ \left[\begin{array}{lll} x^{\prime} & y^{\prime} & 1 \end{array}\right]=\left[\begin{array}{lll} x & y & 1 \end{array}\right]\left|\begin{array}{ccc} \cos \theta & \sin \theta & 0 \\ -\sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{array}\right| \]

缩放变换

\[ \left[\begin{array}{lll} x^{\prime} & y^{\prime} & 1 \end{array}\right]=\left[\begin{array}{lll} x & y & 1 \end{array}\right]\left|\begin{array}{ccc} S_{x} & 0 & 0 \\ 0 & S_{y} & 0 \\ 0 & 0 & 1 \end{array}\right| \]

相机的内参矩阵

\[ K = \begin{bmatrix} f_x & s & s_0 \\ 0 & f_y & y_0 \\ 0 & 0 & 1 \end{bmatrix} \]

\(f_x, f_y\)为焦距,\(x_0, y_0\)为原点坐标,\(s\)为坐标轴倾斜参数


从世界坐标系到相机坐标系

设某点在世界坐标系下坐标为\(P_w=(x_w, y_w, z_w)^\top\),在相机坐标系下的坐标为\(P_c=(x_c, y_c, z_c)^\top\),则

\[ P_c = \begin{bmatrix} R & T \\ 0 & 1 \end{bmatrix} P_w \]

其中,\(R\)为正交旋转矩阵,\(T\)为平移矩阵

从相机坐标系到图像坐标系

假设空间点\(X_c\)在相机坐标系下,\(P_c = (x_c, y_c, z_c, 1)^\top\),其像点\(m\)在图像坐标系下的齐次坐标为\(m = (x_p, y_p, 1)\),由相似三角形可得

\[ \left\{ \begin{array}{} x_p = \frac{fx_c}{z_c} \\ y_p = \frac{fy_c}{z_c} \end{array} \right. \]

矩阵表示为

\[ z_{c} m=\left[\begin{array}{llll} f & 0 & 0 & 0 \\ 0 & f & 0 & 0 \\ 0 & 0 & 1 & 0 \end{array}\right] P_{c} \]

实际中,主点可能不在图像坐标系原点,若主点在图像中的坐标为

\[ p = (x_0, y_0, 1)^\top \]

\[ z_{c} m=\left[\begin{array}{c} f x \\ f y \\ 1 \end{array}\right]=\left[\begin{array}{cccc} f & 0 & x_{0} & 0 \\ 0 & f & y_{0} & 0 \\ 0 & 0 & 1 & 0 \end{array}\right] P_{c} \]

从图像坐标系到像素坐标系

假设一个像素的长和宽分别为\(dx, dy\),设像素坐标\(Pix = (u, v, 1)^\top\),则

\[ \left[\begin{array}{l} u \\ v \\ 1 \end{array}\right]=\left[\begin{array}{ccc} 1 / d_{x} & 0 & 0 \\ 0 & 1 / d_{y} & 0 \\ 0 & 0 & 1 \end{array}\right]\left[\begin{array}{c} x_{p} \\ y_{p} \\ 1 \end{array}\right] \]

结合相机到图像的变换,相机到像素的变换为:

\[ K=\left[\begin{array}{ccc} 1 / d_{x} & 0 & 0 \\ 0 & 1 / d_{y} & 0 \\ 0 & 0 & 1 \end{array}\right]\left[\begin{array}{ccc} f & 0 & x_{0} \\ 0 & f & y_{0} \\ 0 & 0 & 1 \end{array}\right]=\left[\begin{array}{ccc} f_{x} & 0 & u_{0} \\ 0 & f_{y} & v_{0} \\ 0 & 0 & 1 \end{array}\right] \]

其中,\(f_x = f/dx, y = f/dy\)成为相机在\(u\)轴和\(v\)轴方向上的尺度因子,相机主点为\((u_0, v_0)^\top = (x_0/dx, y_0/dy)^\top\)

从世界坐标系到像素坐标系的变换为:

\[ z_{c}\left[\begin{array}{l} u \\ v \\ 1 \end{array}\right]=K \cdot\left[\begin{array}{cc} R & T \\ 0 & 1 \end{array}\right]\left[\begin{array}{c} x_{w} \\ y_{w} \\ z_{w} \\ 1 \end{array}\right] \]

16 个相机参数:

  • 10 个内部参数
  • 五个内部矩阵参数\(K\)\(f, dx, dy, u_0, v_0\)(也可视作四个参数\(f_x, f_y, u_0, v_0\)
  • 五个畸变参数:\(D\)\(k_1, k_2, k_3, p_1, p_2\)
  • 6 个外部参数
  • 3 个旋转参数\(R\)
  • 3 个平移参数\(T\)
import cv2
import numpy as np

#读取相机内参
with np.load('C:\\Users\\wlx\\Documents\\py_study\\camera calibration\\data\\intrinsic_parameters.npz') as X:
    mtx,dist = [X[i] for i in ('mtx','dist')]

def draw(img, corners, imgpts):
    corner = tuple(corners[0].ravel())
    img = cv2.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5)
    img = cv2.line(img, corner, tuple(imgpts[1].ravel()), (0,255,0), 5)
    img = cv2.line(img, corner, tuple(imgpts[2].ravel()), (0,0,255), 5)
    return img

#标定图像保存路径
photo_path = "C:\\Users\\wlx\\Documents\\py_study\\camera calibration\\image\\6.jpg"
#标定图像
def calibration_photo(photo_path):
    #设置要标定的角点个数
    x_nums = 8                                                          #x方向上的角点个数
    y_nums = 5
    #设置(生成)标定图在世界坐标中的坐标
    world_point = np.zeros((x_nums * y_nums,3),np.float32)            #生成x_nums*y_nums个坐标,每个坐标包含x,y,z三个元素
    world_point[:,:2] = np.mgrid[:x_nums,:y_nums].T.reshape(-1, 2)    #mgrid[]生成包含两个二维矩阵的矩阵,每个矩阵都有x_nums列,y_nums行
                                                                        #.T矩阵的转置
                                                                        #reshape()重新规划矩阵,但不改变矩阵元素
    #设置世界坐标的坐标
    axis = np.float32([3,0,0], [0,3,0], [0,0,-3](3,0,0], [0,3,0], [0,0,-3.md){#47fccc820a3a85439d0539a3e35bbc8e}).reshape(-1,3)
    #设置角点查找限制
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,30,0.001)

    image = cv2.imread(photo_path)

    gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
    #查找角点
    ok,corners = cv2.findChessboardCorners(gray,(x_nums,y_nums),)
    #print(ok)
    if ok:
        #获取更精确的角点位置
        exact_corners = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)

        #获取外参
        _,rvec, tvec, inliers = cv2.solvePnPRansac(world_point, exact_corners, mtx, dist)

        imgpts, jac = cv2.projectPoints(axis, rvec, tvec, mtx, dist)
        #可视化角点
        img = draw(image, corners, imgpts)
        cv2.imshow('img', img)


if __name__ == '__main__':
    calibration_photo(photo_path)
    cv2.waitKey()
    cv2.destroyAllWindows()

\[ \text { camera matrix }=\left[\begin{array}{ccc} f_{x} & 0 & c_{x} \\ 0 & f_{y} & c_{y} \\ 0 & 0 & 1 \end{array}\right] \]
\[ \text { Distortion coefficients }=\left(\begin{array}{lllll} k_{1} & k_{2} & p_{1} & p_{2} & k_{3} \end{array}\right) \]
\[ R = \left[\begin{array}{ccc} \cos \theta \cos \phi & \sin \psi \sin \theta \cos \phi-\cos \psi \sin \phi & \cos \psi \sin \theta \cos \phi+\sin \psi \sin \phi \\ \cos \theta \sin \phi & \sin \psi \sin \theta \sin \phi+\cos \psi \cos \phi & \cos \psi \sin \theta \sin \phi-\sin \psi \cos \phi \\ -\sin \theta & \sin \psi \cos \theta & \cos \psi \cos \theta \end{array}\right] \]
\[ T = [t_1, t_2, t_3]^\top \]
\[ \left[\begin{array}{l} x \\ y \\ 1 \end{array}\right]=\left[\begin{array}{ccc} f_{\mathrm{x}} & 0 & u_{0} \\ 0 & f_{\mathrm{y}} & v_{0} \\ 0 & 0 & 1 \end{array}\right]\left[\begin{array}{cccc} r_{1} & r_{2} & r_{3} & t_{1} \\ r_{4} & r_{5} & r_{6} & t_{2} \\ r_{7} & r_{8} & r_{9} & t_{3} \end{array}\right]\left[\begin{array}{c} X \\ Y \\ Z \\ 1 \end{array}\right] \]

问题 1:相机标定内参的时候通过 calibrateCamera()函数能够获得旋转矢量 rvec 和平移向量 tvec;在本章中,我们使用 solvePnPRansac()函数又一次获得旋转矢量 rvec 和平移向量 tvec。这是为什么呢?

calibrateCamera()函数求取的旋转矢量 rvec 和平移向量 tvec,是在获取了相机内参 mtx, dist 之后,通过内部调用 solvePnPRansac()函数获得的。也就是说,如果对于只有一张标定图像的情况,alibrateCamera()函数求取的旋转矢量 rvec 和平移向量 tvec,与 solvePnPRansac()函数求取的旋转矢量 rvec 和平移向量 tvec 是一样的(这是确定的)。但是我们在标定相机内参的时候,为了得到较为准确的标定结果,所以拍摄了多张(10~20)不同角度的标定图,在这些标定图一起作用下,求出了相机的内参。所以当在一个确定的场景下,想要获得相机的外参时,我们需要重新求取旋转矩阵和平移向量,于是我们用了 solvePnPRansac()函数(为什么不用 calibrateCamera()函数了?因为 calibrateCamera()函数就是调用的 solvePnPRansac()函数)。

问题 2:得出的 rvec 怎么是 3*1 的矩阵?

调用 solvePnPRansac()函数得到的 rvec 是一个旋转矢量,需要使用 cv2.Rodrigues(src,dst,jacobian=None)

src:输入的矩阵可以是(31 或 13)也可以是 3*3

dst:输出的矩阵,对应输入 33,或者(31 或 1*3)

jacobian:也是一个输出,该输出表明了输入矩阵和输出矩阵的的雅可比

转换后的矩阵就和前面的旋转矩阵对应了,然后就能求出相机的俯仰角、偏航角、滚轮角。(不过对于实际应用中,知道旋转矩阵不就行了?)

OK,相机内外参数都弄明白了,相机标定告一段落。

版权声明:本文为 CSDN 博主「Levi_wlx」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weilixin88/article/details/91603319

img{loading=lazy}