计算机视觉5—张正友相机标定原理及实现

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-31 21:36   31   0

目录

1.相机标定原理及介绍

1.1 简介

1.2 算法流程

1.3 坐标系

1.4 外参矩阵

1.5 极大似然估计

1.6 径向畸变估计

2.算法实现

2.1 代码

2.1.1 main.py

2.1.2 intrinsics.py求解内参矩阵

2.1.3 distortion.py求解畸变矫正系数

2.1.4 extrinsics.py求解外参矩阵

2.1.5 refine_all微调参数

2.2 实验结果及分析

2.3 实验小结


1.相机标定原理及介绍

1.1 简介

相机标定的目的和意义在于我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图。这个从三维到二维的过程的函数是不可逆的。而相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个三维到二维的过程,使这个三维到二维的过程的函数找到反函数。

“张正友标定”是指张正友教授1998年提出的单平面棋盘格的摄像机标定方法[1]。文中提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来的棋盘格就可以。同时也相对于自标定而言,提高了精度,便于操作。因此张氏标定法被广泛应用于计算机视觉方面。

1.2 算法流程

  • 打印一张棋盘格A4纸张(黑白间距已知),并贴在一个平板上
  • 针对棋盘格拍摄若干张图片(一般10-20张)
  • 在图片中检测特征点(Harris角点)
  • 根据角点位置信息及图像中的坐标,求解Homographic矩阵
  • 利用解析解估算方法计算出5个内部参数,以及6个外部参数
  • 根据极大似然估计策略,设计优化目标并实现参数的refinement

1.3 坐标系

(1)像素坐标系:如下图所示,像素坐标系u-v的原点为O0,横坐标u和纵坐标v分别是图像所在的行和列,在视觉处理库OpenCV中,u对应x,v对应y;

(2)图像坐标系:图像坐标系x-y的原点是O1,为像素坐标系的中点,如下图所示:假设(u0,v0)代表O1在u-v坐标系下的坐标,dx和dy分别表示每个像素在横轴x和纵轴y的物理尺寸

(3)相机坐标系:如下图所示,O为摄像机光心,Zc为摄像机的光轴,和图像平面垂直;OO1为摄像机焦距;

(4)世界坐标系:如下图所示,世界坐标系是为了描述相机的位置而被引入的,任何维的旋转可以表示为坐标向量与合适的方阵的乘积。平移向量是第一个坐标原点与第二个坐标原点的偏移量;在世界坐标系下,有两个重要的参数:旋转矩阵R和平移向量T。

1.4 外参矩阵

设三维世界坐标的点为 M=[X,Y,Z,1]T ,二维相机平面像素坐标为 m=[u,v,1]T ,所以标定用的棋盘格平面到图像平面的单应性关系为: sm=A[R,t]M ,其中

设棋盘格位于Z = 0,定义旋转矩阵R的第i列为 ri, 则有:

S\begin{bmatrix} u\\ v\\ 1 \end{bmatrix}=A\begin{bmatrix} r1 & r2 & r3 & t \end{bmatrix}\begin{bmatrix} Y\\ 0\\ 1 \end{bmatrix}=A\begin{bmatrix} r1 & r2 & t \end{bmatrix}\begin{bmatrix} X\\ Y\\ 1 \end{bmatrix}

令 H=[h1 h2 h3]=λA[r1 r2 t] ,于是空间到图像的映射可改为: sm=HM ,其中H是描述Homographic矩阵,H是一个齐次矩阵,所以有8个未知数,至少需要8个方程,每对对应点能提供两个方程,所以至少需要四个对应点,就可以算出世界平面到图像平面的单应性矩阵H。一般而言,求解出的R = [r1 r2 t] 不会满足正交与归一的标准。

1.5 极大似然估计

上述的推导结果是基于理想情况下的解,但由于可能存在高斯噪声,所以使用最大似然估计进行优化。设我们采集了n副包含棋盘格的图像进行定标,每个图像里有棋盘格角点m个。令第i副图像上的角点Mj在上述计算得到的摄像机矩阵下图像上的投影点为(K为相机内参矩阵A):

其中Ri和ti是第i副图对应的旋转矩阵和平移向量,K是内参数矩阵。则角点mij的概率密度函数为(K为相机内参矩阵A):

构造似然函数(K为相机内参矩阵A):

让L取得最大值,即让下面式子最小。这里使用的是多参数非线性系统优化问题的Levenberg-Marquardt算法[2]进行迭代求最优解(K为相机内参矩阵A)。

1.6 径向畸变估计

张氏标定法只关注了影响最大的径向畸变。则数学表达式为:

其中,(u,v)是理想无畸变的像素坐标,(u,v)(u,v)是实际畸变后的像素坐标。(u0,v0)代表主点,(x,y)是理想无畸变的连续图像坐标,(x,y)(x,y)是实际畸变后的连续图像坐标。k1和k2为前两阶的畸变参数。

化作矩阵形式:

记做:Dk=d,则可得:

计算得到畸变系数k。使用最大似然的思想优化得到的结果,即像上一步一样,LM法计算下列函数值最小的参数值(K为相机内参矩阵A):

2.算法实现

2.1 代码

2.1.1 main.py

#!usr/bin/env/ python
# _*_ coding:utf-8 _*_

import cv2 as cv
import numpy as np
import os
from step.homography import get_homography
from step.intrinsics import get_intrinsics_param
from step.extrinsics import get_extrinsics_param
from step.distortion import get_distortion
from step.refine_all import refinall_all_param


def calibrate():
    # 求单应矩阵
    H = get_homography(pic_points, real_points_x_y)
    print("Homographic矩阵")
    print H

    # 求内参
    intrinsics_param = get_intrinsics_param(H)

    # 求对应每幅图外参
    extrinsics_param = get_extrinsics_param(H, intrinsics_param)

    # 畸变矫正
    k = get_distortion(intrinsics_param, extrinsics_param, pic_points, real_points_x_y)

    # 微调所有参数
    [new_intrinsics_param, new_k, new_extrinsics_param] = refinall_all_param(intrinsics_param,
                                                                             k, extrinsics_param, real_points,
                                                                             pic_points)

    print("内参矩阵")
    print(new_intrinsics_param)
    print("畸变矫正系数")
    print new_k
    print("外参矩阵")
    print new_extrinsics_param


if __name__ == "__main__":
    file_dir = r'..\ch04\dataim'
    # 标定所用图像
    pic_name = os.listdir(file_dir)

    # 由于棋盘为二维平面,设定世界坐标系在棋盘上,一个单位代表一个棋盘宽度,产生世界坐标系三维坐标
    cross_corners = [9, 14]  # 棋盘方块交界点排列
    real_coor = np.zeros((cross_corners[0] * cross_corners[1], 3), np.float32)
    real_coor[:, :2] = np.mgrid[0:9, 0:14].T.reshape(-1, 2)

    real_points = []
    real_points_x_y = []
    pic_points = []

    for pic in pic_name:
        pic_path = os.path.join(file_dir, pic)
        pic_data = cv.imread(pic_path)

        # 寻找到棋盘角点
        succ, pic_coor = cv.findChessboardCorners(pic_data, (cross_corners[0], cross_corners[1]), None)

        if succ:
            # 添加每幅图的对应3D-2D坐标
            pic_coor = pic_coor.reshape(-1, 2)
            pic_points.append(pic_coor)

            real_points.append(real_coor)
            real_points_x_y.append(real_coor[:, :2])
    calibrate()

2.1.2 intrinsics.py求解内参矩阵

#!usr/bin/env/ python
# _*_ coding:utf-8 _*_

import numpy as np

# 求解内参矩阵的文件

# 返回pq位置对应的v向量
def create_v(p, q, H):
    H = H.reshape(3, 3)
    return np.array([
        H[0, p] * H[0, q],
        H[0, p] * H[1, q] + H[1, p] * H[0, q],
        H[1, p] * H[1, q],
        H[2, p] * H[0, q] + H[0, p] * H[2, q],
        H[2, p] * H[1, q] + H[1, p] * H[2, q],
        H[2, p] * H[2, q]
    ])


# 返回相机内参矩阵A
def get_intrinsics_param(H):
    # 构建V矩阵
    V = np.array([])
    for i in range(len(H)):
        V = np.append(V, np.array([create_v(0, 1, H[i]), create_v(0, 0, H[i]) - create_v(1, 1, H[i])]))

    # 求解V*b = 0中的b
    U, S, VT = np.linalg.svd((np.array(V, dtype='float')).reshape((-1, 6)))
    # 最小的奇异值对应的奇异向量,S求出来按大小排列的,最后的最小
    b = VT[-1]

    # 求取相机内参
    w = b[0] * b[2] * b[5] - b[1] * b[1] * b[5] - b[0] * b[4] * b[4] + 2 * b[1] * b[3] * b[4] - b[2] * b[3] * b[3]
    d = b[0] * b[2] - b[1] * b[1]

    alpha = np.sqrt(w / (d * b[0]))
    beta = np.sqrt(w / d ** 2 * b[0])
    gamma = np.sqrt(w / (d ** 2 * b[0])) * b[1]
    uc = (b[1] * b[4] - b[2] * b[3]) / d
    vc = (b[1] * b[3] - b[0] * b[4]) / d

    return np.array([
        [alpha, gamma, uc],
        [0, beta, vc],
        [0, 0, 1]
    ])

2.1.3 distortion.py求解畸变矫正系数

#!usr/bin/env/ python
# _*_ coding:utf-8 _*_

import numpy as np

# 求解畸变矫正系数的文件

# 返回畸变矫正系数k0,k1
def get_distortion(intrinsic_param, extrinsic_param, pic_coor, real_coor):
    D = []
    d = []
    for i in range(len(pic_coor)):
        for j in range(len(pic_coor[i])):
            # 转换为齐次坐标
            single_coor = np.array([(real_coor[i])[j, 0], (real_coor[i])[j, 1], 0, 1])

            # 利用现有内参及外参求出估计图像坐标
            u = np.dot(np.dot(intrinsic_param, extrinsic_param[i]), single_coor)
            [u_estim, v_estim] = [u[0] / u[2], u[1] / u[2]]

            coor_norm = np.dot(extrinsic_param[i], single_coor)
            coor_norm /= coor_norm[-1]

            # r = np.linalg.norm((real_coor[i])[j])
            r = np.linalg.norm(coor_norm)

            D.append(np.array([(u_estim - intrinsic_param[0, 2]) * r ** 2, (u_estim - intrinsic_param[0, 2]) * r ** 4]))
            D.append(np.array([(v_estim - intrinsic_param[1, 2]) * r ** 2, (v_estim - intrinsic_param[1, 2]) * r ** 4]))

            # 求出估计坐标与真实坐标的残差
            d.append(pic_coor[i][j, 0] - u_estim)
            d.append(pic_coor[i][j, 1] - v_estim)
            '''

            D.append(np.array([(pic_coor[i][j, 0] - intrinsic_param[0, 2]) * r ** 2, (pic_coor[i][j, 0] - intrinsic_param[0, 2]) * r ** 4]))
            D.append(np.array([(pic_coor[i][j, 1] - intrinsic_param[1, 2]) * r ** 2, (pic_coor[i][j, 1] - intrinsic_param[1, 2]) * r ** 4]))
            #求出估计坐标与真实坐标的残差
            d.append(u_estim - pic_coor[i][j, 0])
            d.append(v_estim - pic_coor[i][j, 1])
            '''

    D = np.array(D)
    temp = np.dot(np.linalg.inv(np.dot(D.T, D)), D.T)
    k = np.dot(temp, d)
    '''
    #也可利用SVD求解D * k = d中的k
    U, S, Vh=np.linalg.svd(D, full_matrices=False)
    temp_S = np.array([[S[0], 0],
                       [0, S[1]]])
    temp_res = np.dot(Vh.transpose(), np.linalg.inv(temp_S))
    temp_res_res = np.dot(temp_res, U.transpose())
    k = np.dot(temp_res_res, d)
    '''
    return k

2.1.4 extrinsics.py求解外参矩阵

#!usr/bin/env/ python
# _*_ coding:utf-8 _*_

import numpy as np

# 求解外参矩阵的文件

# 返回每一幅图的外参矩阵[R|t]
def get_extrinsics_param(H, intrinsics_param):
    extrinsics_param = []

    inv_intrinsics_param = np.linalg.inv(intrinsics_param)
    for i in range(len(H)):
        h0 = (H[i].reshape(3, 3))[:, 0]
        h1 = (H[i].reshape(3, 3))[:, 1]
        h2 = (H[i].reshape(3, 3))[:, 2]

        scale_factor = 1 / np.linalg.norm(np.dot(inv_intrinsics_param, h0))

        r0 = scale_factor * np.dot(inv_intrinsics_param, h0)
        r1 = scale_factor * np.dot(inv_intrinsics_param, h1)
        t = scale_factor * np.dot(inv_intrinsics_param, h2)
        r2 = np.cross(r0, r1)

        R = np.array([r0, r1, r2, t]).transpose()
        extrinsics_param.append(R)

    return extrinsics_param

2.1.5 refine_all微调参数

#!usr/bin/env/ python
# _*_ coding:utf-8 _*_

import numpy as np
import math
from scipy import optimize as opt

# 微调参数的文件

# 微调所有参数
def refinall_all_param(A, k, W, real_coor, pic_coor):
    # type: (object, object, object, object, object) -> object
    # 整合参数
    P_init = compose_paramter_vector(A, k, W)

    # 复制一份真实坐标
    X_double = np.zeros((2 * len(real_coor) * len(real_coor[0]), 3))
    Y = np.zeros((2 * len(real_coor) * len(real_coor[0])))

    M = len(real_coor)
    N = len(real_coor[0])
    for i in range(M):
        for j in range(N):
            X_double[(i * N + j) * 2] = (real_coor[i])[j]
            X_double[(i * N + j) * 2 + 1] = (real_coor[i])[j]
            Y[(i * N + j) * 2] = (pic_coor[i])[j, 0]
            Y[(i * N + j) * 2 + 1] = (pic_coor[i])[j, 1]

    # 微调所有参数
    P = opt.leastsq(value,
                    P_init,
                    args=(W, real_coor, pic_coor),
                    Dfun=jacobian)[0]

    # raial_error表示利用标定后的参数计算得到的图像坐标与真实图像坐标点的平均像素距离
    error = value(P, W, real_coor, pic_coor)
    raial_error = [np.sqrt(error[2 * i] ** 2 + error[2 * i + 1] ** 2) for i in range(len(error) // 2)]

    print("total max error:\t", np.max(raial_error))

    # 返回拆解后参数,分别为内参矩阵,畸变矫正系数,每幅图对应外参矩阵
    return decompose_paramter_vector(P)


# 把所有参数整合到一个数组内
def compose_paramter_vector(A, k, W):
    alpha = np.array([A[0, 0], A[1, 1], A[0, 1], A[0, 2], A[1, 2], k[0], k[1]])
    P = alpha
    for i in range(len(W)):
        R, t = (W[i])[:, :3], (W[i])[:, 3]

        # 旋转矩阵转换为一维向量形式
        zrou = to_rodrigues_vector(R)

        w = np.append(zrou, t)
        P = np.append(P, w)
    return P


# 分解参数集合,得到对应的内参,外参,畸变矫正系数
def decompose_paramter_vector(P):
    [alpha, beta, gamma, uc, vc, k0, k1] = P[0:7]
    A = np.array([[alpha, gamma, uc],
                  [0, beta, vc],
                  [0, 0, 1]])
    k = np.array([k0, k1])
    W = []
    M = (len(P) - 7) // 6

    for i in range(M):
        m = 7 + 6 * i
        zrou = P[m:m + 3]
        t = (P[m + 3:m + 6]).reshape(3, -1)

        # 将旋转矩阵一维向量形式还原为矩阵形式
        R = to_rotation_matrix(zrou)

        # 依次拼接每幅图的外参
        w = np.concatenate((R, t), axis=1)
        W.append(w)

    W = np.array(W)
    return A, k, W


# 返回从真实世界坐标映射的图像坐标
def get_single_project_coor(A, W, k, coor):
    single_coor = np.array([coor[0], coor[1], coor[2], 1])

    # '''
    coor_norm = np.dot(W, single_coor)
    coor_norm /= coor_norm[-1]

    # r = np.linalg.norm(coor)
    r = np.linalg.norm(coor_norm)

    uv = np.dot(np.dot(A, W), single_coor)
    uv /= uv[-1]

    # 畸变
    u0 = uv[0]
    v0 = uv[1]

    uc = A[0, 2]
    vc = A[1, 2]

    # u = (uc * r**2 * k[0] + uc * r**4 * k[1] - u0) / (r**2 * k[0] + r**4 * k[1] - 1)
    # v = (vc * r**2 * k[0] + vc * r**4 * k[1] - v0) / (r**2 * k[0] + r**4 * k[1] - 1)
    u = u0 + (u0 - uc) * r ** 2 * k[0] + (u0 - uc) * r ** 4 * k[1]
    v = v0 + (v0 - vc) * r ** 2 * k[0] + (v0 - vc) * r ** 4 * k[1]
    '''
    uv = np.dot(W, single_coor)
    uv /= uv[-1]
    # 透镜矫正
    x0 = uv[0]
    y0 = uv[1]
    r = np.linalg.norm(np.array([x0, y0]))
    k0 = 0
    k1 = 0
    x = x0 * (1 + r ** 2 * k0 + r ** 4 * k1)
    y = y0 * (1 + r ** 2 * k0 + r ** 4 * k1)
    #u = A[0, 0] * x + A[0, 2]
    #v = A[1, 1] * y + A[1, 2]
    [u, v, _] = np.dot(A, np.array([x, y, 1]))
    '''

    return np.array([u, v])


# 返回所有点的真实世界坐标映射到的图像坐标与真实图像坐标的残差
def value(P, org_W, X, Y_real):
    M = (len(P) - 7) // 6
    N = len(X[0])
    A = np.array([
        [P[0], P[2], P[3]],
        [0, P[1], P[4]],
        [0, 0, 1]
    ])
    Y = np.array([])

    for i in range(M):
        m = 7 + 6 * i

        # 取出当前图像对应的外参
        w = P[m:m + 6]

        # 不用旋转矩阵的变换是因为会有精度损失
        '''
        R = to_rotation_matrix(w[:3])
        t = w[3:].reshape(3, 1)
        W = np.concatenate((R, t), axis=1)
        '''
        W = org_W[i]
        # 计算每幅图的坐标残差
        for j in range(N):
            Y = np.append(Y, get_single_project_coor(A, W, np.array([P[5], P[6]]), (X[i])[j]))

    error_Y = np.array(Y_real).reshape(-1) - Y

    return error_Y


# 计算对应jacobian矩阵
def jacobian(P, WW, X, Y_real):
    M = (len(P) - 7) // 6
    N = len(X[0])
    K = len(P)
    A = np.array([
        [P[0], P[2], P[3]],
        [0, P[1], P[4]],
        [0, 0, 1]
    ])

    res = np.array([])

    for i in range(M):
        m = 7 + 6 * i

        w = P[m:m + 6]
        R = to_rotation_matrix(w[:3])
        t = w[3:].reshape(3, 1)
        W = np.concatenate((R, t), axis=1)

        for j in range(N):
            res = np.append(res, get_single_project_coor(A, W, np.array([P[5], P[6]]), (X[i])[j]))

    # 求得x, y方向对P[k]的偏导
    J = np.zeros((K, 2 * M * N))
    for k in range(K):
        J[k] = np.gradient(res, P[k])

    return J.T


# 将旋转矩阵分解为一个向量并返回,Rodrigues旋转向量与矩阵的变换,最后计算坐标时并未用到,因为会有精度损失
def to_rodrigues_vector(R):
    p = 0.5 * np.array([[R[2, 1] - R[1, 2]],
                        [R[0, 2] - R[2, 0]],
                        [R[1, 0] - R[0, 1]]])
    c = 0.5 * (np.trace(R) - 1)

    if np.linalg.norm(p) == 0:
        if c == 1:
            zrou = np.array([0, 0, 0])
        elif c == -1:
            R_plus = R + np.eye(3, dtype='float')

            norm_array = np.array([np.linalg.norm(R_plus[:, 0]),
                                   np.linalg.norm(R_plus[:, 1]),
                                   np.linalg.norm(R_plus[:, 2])])
            v = R_plus[:, np.where(norm_array == max(norm_array))]
            u = v / np.linalg.norm(v)
            if u[0] < 0 or (u[0] == 0 and u[1] < 0) or (u[0] == u[1] and u[0] == 0 and u[2] < 0):
                u = -u
            zrou = math.pi * u
        else:
            zrou = []
    else:
        u = p / np.linalg.norm(p)
        theata = math.atan2(np.linalg.norm(p), c)
        zrou = theata * u

    return zrou


# 把旋转矩阵的一维向量形式还原为旋转矩阵并返回
def to_rotation_matrix(zrou):
    theta = np.linalg.norm(zrou)
    zrou_prime = zrou / theta

    W = np.array([[0, -zrou_prime[2], zrou_prime[1]],
                  [zrou_prime[2], 0, -zrou_prime[0]],
                  [-zrou_prime[1], zrou_prime[0], 0]])
    R = np.eye(3, dtype='float') + W * math.sin(theta) + np.dot(W, W) * (1 - math.cos(theta))

    return R

2.2 实验结果及分析

(1)数据集

使用手机相机一共拍摄12张棋盘格图片,图片统一尺寸为750*1000,纸张大小为a4,棋盘格大小为10*15个格子,每个格子大小为19*18mm,使用数据集的时候没有取到边缘,取角点维数为9*14

(2)实验结果

角点检测结果

Homographic矩阵

[[-1.82671173e+01 -3.15716744e+01  6.47896986e+02  3.85876389e+01
  -6.19647030e+00  1.65295287e+02 -3.24066257e-02  5.75324407e-03
   1.00000000e+00]
 [-3.07895099e+00 -3.63905607e+01  6.06530020e+02  6.37294941e+01
  -1.64888336e+00  2.11126383e+02 -7.77062243e-03 -2.05783985e-03
   1.00000000e+00]
 [-1.45799150e+01 -2.58175714e+01  5.11183175e+02  3.79522198e+01
   7.60294944e+00  9.58226063e+01 -2.90432554e-02  1.39778780e-02
   1.00000000e+00]
 [-7.79320600e+00 -3.53686498e+01  6.02257381e+02  4.23247234e+01
  -1.28100473e+01  2.35567675e+02 -2.37043817e-02 -1.48478045e-02
   1.00000000e+00]
 [-8.74721066e+00 -3.14386943e+01  5.59235145e+02  3.77903915e+01
   3.60192645e-01  1.83585631e+02 -2.71919333e-02 -1.83838374e-03
   1.00000000e+00]
 [ 1.04602859e+01 -4.47423294e+01  6.57264542e+02  1.02717349e+02
   5.79089000e+00  2.19569814e+02  2.30997792e-02  1.10434796e-02
   1.00000000e+00]
 [ 3.45923499e+00 -3.31187604e+01  5.73780263e+02  8.18459091e+01
   1.69995629e+01  1.95497743e+02 -6.56878200e-04  2.18248867e-02
   1.00000000e+00]
 [-3.24740402e+00 -3.03459849e+01  5.42313243e+02  5.49306655e+01
   1.58546387e+01  1.35690104e+02 -1.93421349e-02  1.14009028e-02
   1.00000000e+00]
 [-5.03080031e+00 -3.05456495e+01  5.33478429e+02  4.20850054e+01
   1.32579121e+01  8.48774675e+01 -2.67450356e-02  4.38783301e-03
   1.00000000e+00]
 [-4.41246273e+00 -3.26565262e+01  5.21631564e+02  3.34374221e+01
   4.98405337e+00  7.86311689e+01 -2.96733603e-02 -6.72828091e-03
   1.00000000e+00]
 [ 2.63460709e-01 -3.04617576e+01  5.04664238e+02  2.80735529e+01
   8.81878404e+00  6.88866159e+01 -2.74126822e-02 -1.00650570e-02
   1.00000000e+00]
 [-3.33407086e-01  1.67814187e+01  5.21068280e+01 -5.34020113e+01
  -7.64388462e+00  7.14704857e+02 -2.78918368e-03 -2.08091350e-02
   1.00000000e+00]]

内参矩阵

[[5.51999227e+02 4.06753522e+00 3.68355887e+02]
 [0.00000000e+00 1.05809248e+03 5.01459627e+02]
 [0.00000000e+00 0.00000000e+00 1.00000000e+00]]

畸变矫正系数

[ 0.01389452 -0.01202336]

径向畸变

 -0.0145   -0.0363

切向畸变

 0     0

外参矩阵

[[[-1.80991107e-01 -9.83483187e-01  1.74382508e-03  8.17491673e+00]
  [ 8.31747270e-01 -1.52120364e-01  5.33906240e-01 -5.10193469e+00]
  [-5.24822539e-01  9.80827032e-02  8.45541889e-01  1.60618440e+01]]

 [[-7.73850160e-03 -9.99424892e-01 -3.30151570e-02  6.73632976e+00]
  [ 9.92651328e-01 -1.16648906e-02  1.20446137e-01 -4.26120951e+00]
  [-1.20761986e-01 -3.18404669e-02  9.92170715e-01  1.55321254e+01]]

 [[-1.23514505e-01 -9.69945397e-01  2.09642779e-01  4.51400318e+00]
  [ 8.55347971e-01  3.04810864e-03  5.18044937e-01 -6.61117773e+00]
  [-5.03114316e-01  2.43303590e-01  8.29264342e-01  1.72492514e+01]]

 [[ 2.58207091e-02 -9.60595077e-01 -2.76749685e-01  7.53938483e+00]
  [ 9.06988092e-01 -9.39019612e-02  4.10554532e-01 -4.44912040e+00]
  [-4.20364000e-01 -2.61609478e-01  8.68823681e-01  1.77132266e+01]]

 [[ 3.54254327e-02 -9.99203031e-01 -1.83940544e-02  6.24869138e+00]
  [ 8.72213367e-01  2.19280550e-02  4.88633812e-01 -5.39137393e+00]
  [-4.87841040e-01 -3.33536043e-02  8.72295051e-01  1.79446432e+01]]

 [[ 3.34349806e-02 -9.92344059e-01  1.18892267e-01  5.88984840e+00]
  [ 9.65272800e-01  1.22369659e-03 -2.61241123e-01 -2.98593796e+00]
  [ 2.59095588e-01  1.23498063e-01  9.57923642e-01  1.12083629e+01]]

 [[ 7.70314238e-02 -9.56880055e-01  2.80083417e-01  4.80547393e+00]
  [ 9.96994896e-01  7.62390686e-02 -1.37398075e-02 -3.71162512e+00]
  [-8.20595111e-03  2.80300134e-01  9.59877334e-01  1.28366874e+01]]

 [[ 9.65040045e-02 -9.72090743e-01  2.13837705e-01  4.93494624e+00]
  [ 9.49323469e-01  1.54457954e-01  2.73729231e-01 -5.36609022e+00]
  [-2.99118586e-01  1.76585185e-01  9.37734367e-01  1.55307346e+01]]

 [[ 1.32293572e-01 -9.80384823e-01  1.46095890e-01  5.08146505e+00]
  [ 8.84106072e-01  1.83351830e-01  4.29812238e-01 -6.62091252e+00]
  [-4.48168344e-01  7.23028672e-02  8.91020444e-01  1.68190704e+01]]

 [[ 1.95282182e-01 -9.80499935e-01 -2.20169767e-02  5.04476161e+00]
  [ 8.23976312e-01  1.51849952e-01  5.45898002e-01 -7.18121967e+00]
  [-5.31909679e-01 -1.24745620e-01  8.37562311e-01  1.79711073e+01]]

 [[ 3.36995345e-01 -9.41424050e-01 -1.24457319e-02  4.85368626e+00]
  [ 7.76462737e-01  2.70420466e-01  5.69190996e-01 -7.93219707e+00]
  [-5.32484511e-01 -2.01478363e-01  8.22111133e-01  1.94095064e+01]]

 [[ 3.04283219e-02  9.03966254e-01  4.26519788e-01 -1.16624226e+01]
  [-9.97956060e-01  5.14656646e-02 -3.78812339e-02  4.09177719e+00]
  [-5.61944814e-02 -4.24495345e-01  9.03684615e-01  2.03047085e+01]]]

相机参数表和下面camera-centric和pattern-centric的图是通过MATLAB自带的工具箱得到的,在应用程序中找到Camera Calibrator,然后Add Images添加图片文件,在弹框内修改自己的棋盘格尺寸(图片尺寸大小要统一),Calibrate标定就可以了。

相机参数表

camera-centric可视化

pattern-centric可视化

2.3 实验小结

  • 处理图片方面一定要记得压缩图片,图片太大,运行会直接卡死;
  • 该算法还是对相机校准还存在一定的误差,total max error :7.565380481479662;
  • 角点维度要视棋盘格大小而定,一定要在棋盘格范围内,且如果长和宽不一样的时候,要注意区分长和宽;
  • 从角点的检测结果来看,正确率还是很高的,可以很好的识别出黑白棋盘格上的角点,几乎没有错误。
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP