计算机视觉(四)相机标定


一、原理

什么是相机标定?

随着科技和经济的蓬勃发展,机器人作业、汽车自动导航等技术已经得到广泛应用,在很大程度上推动了社会生产力的发展。不论是主动光学视觉传感或者是被动光学视觉传感,要从图像推知物空间的三维信息,或反之,从空间三维信息推知二维图像坐标,都必须确定相机在参考坐标系中的空间位置和取向,以及相机本身的几何和光学参数为解决这个问题所需用到相机标定技术

相机标定的作用在于消除由于相机产生的图像畸变,从而校正图像,为处理计算出精确数值提供可能。
由此,相机标定成了系统准确的先决条件。

从真实的三维世界坐标,可以得到二维的相机坐标,
但是我们从二维的相机坐标,能否准确的推算出真实的三维世界的坐标呢?这就是相机标定的意义。

相机标定的原理是什么?

相机标定指建立相机图像像素位置与场景点位置之间的关系,根据相机成像模型,由特征点在图像中坐标与世界坐标的对应关系,求解相机模型的参数。相机需要标定的模型参数包括内部参数和外部参数。
针孔相机成像原理其实就是利用投影将真实的三维世界坐标转换到二维的相机坐标上去,其模型示意图如下图所示:
在这里插入图片描述
从图中我们可以看出,在世界坐标中的一条直线上的点在相机上只呈现出了一个点,其中发生了非常大的变化,同时也损失和很多重要的信息,这正是我们3D重建、目标检测与识别领域的重点和难点。实际中,镜头并非理想的透视成像,带有不同程度的畸变。理论上镜头的畸变包括径向畸变和切向畸变,切向畸变影响较小,通常只考虑径向畸变。

径向畸变:

径向畸变主要由镜头径向曲率产生(光线在远离透镜中心的地方比靠近中心的地方更加弯曲)。导致真实成像点向内或向外偏离理想成像点。其中畸变像点相对于理想像点沿径向向外偏移,远离中心的,称为枕形畸变;径向畸点相对于理想点沿径向向中心靠拢,称为桶状畸变。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

算法流程

  1. 打印一张棋盘方格图并贴在一个平面上
  2. 从不同角度拍摄若干张模板图像
  3. 检测出图像中的特征点
  4. 由检测到的特征点计算出每幅图像中的平面投影矩阵H
  5. 确定出摄像机的参数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    其中,(U,V, W)为在世界坐标系下一 点的物理坐标,
    (u,v)为该点对应的在像素坐标系下的像索坐标,Z 为尺度因子,
    dX为一个单位的单位长度,dY就是单位宽度

内参是摄像机内部的参数,和外界无关;
相机内部参数/内方位元素:焦距(f)、像主点坐标、畸变参数(θ为像素点的倾斜角,
畸变因子为:[-(fcotθ/dX)]-描述dX和dY之间的关系)

外参是摄像机坐标系和世界坐标系的关系。
相机外部参数/外方位元素:旋转®、平移(T)
在这里插入图片描述

求平面投影矩阵H

一般可假设模板落在世界坐标系的Z=0的平面上。设K为摄像机的内参数矩
阵,r是R的第i个向量,那么对模板平面上的每一个点,都有
在这里插入图片描述
在这里插入图片描述
在一幅图像中可以检测到多个特征点,这样由式(3.16),多个对应点的方程叠加起来可以看成Sh’ =0,通过求该方程的最小二乘解可得到h’,也就是sTs 最小特征值所对应的特征向量,进而得到H。
注意H与真正的单应性矩阵之间可能相差一个比例因子。

确定出摄像机的参数

首先令hi表示H的每一列向量,于是有
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在立体视觉系统中,双目摄像机与单摄像机标定的差别是,需要通过定标,
测量双摄像机之间的相对位置。用两个摄像机同时观察周围环境,在标定中,我们可以用单摄像机定标方法分别得到两个摄像机各自的内外参数,如果外参数分别用R1,t1,与R2,t2表示,
则R1,t1表示C1摄像机与世界坐标系之间的相对位置,
2R,t2表示C2摄像机与世界坐标系之间的相对位置。对任意一点P,如它在世界坐
标系、C1坐标系与C2坐标系下的非齐次坐标分别为x(w) ,x(c1),x(c2),则
在这里插入图片描述

二、代码实现

完整代码

import cv2
import numpy as np
import glob

np.set_printoptions(suppress=True)

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 棋盘格模板规格
w = 11  # 内角点个数,内角点是和其他格子连着的点
h = 7

# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w * h, 3), np.float32)
objp[:, :2] = np.mgrid[0:w, 0:h].T.reshape(-1, 2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = []  # 在世界坐标系中的三维点
imgpoints = []  # 在图像平面的二维点

images = glob.glob('./img/*.jpg')
i = 0
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 找到棋盘格角点
    # 棋盘图像(8位灰度或彩色图像)  棋盘尺寸  存放角点的位置
    ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
    # 如果找到足够点对,将其存储起来
    if ret == True:
        # 角点精确检测
        # 输入图像 角点初始坐标 搜索窗口为2*winsize+1 死区 求角点的迭代终止条件
        i += 1
        cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w, h), corners, ret)
        cv2.imshow('findCorners', img)
        cv2.imwrite('h' + str(i) + '.jpg', img)
        cv2.waitKey(10)
cv2.destroyAllWindows()
# 标定、去畸变
# 输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 3*3矩阵,相机内参数矩阵 畸变矩阵
# 输出:标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
print(("ret:"), ret)
print(("内参矩阵:\n"), mtx)  # 内参数矩阵
print(("畸变参数:\n"), dist)  # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print(("旋转向量(外参数):\n"), rvecs)  # 旋转向量  # 外参数
print(("平移向量(外参数):\n"), tvecs)  # 平移向量  # 外参数
# 去畸变
img2 = cv2.imread('./img/2.jpg')
h, w = img2.shape[:2]
# 我们已经得到了相机内参和畸变系数,在将图像去畸变之前,
# 我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,
# 通过设定自由自由比例因子alpha。当alpha设为0的时候,
# 将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;
# 当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 0, (w, h))  # 自由比例参数

dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根据前面ROI区域裁剪图片
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv2.imwrite('calibresult.jpg', dst)

# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
# 通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,
# 然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
total_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
    total_error += error
print(("total error: "), total_error / len(objpoints))




实验结果

角点精确检测:
在这里插入图片描述

在这里插入图片描述

输出结果参数

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值