Python计算机视觉之相机标定

目录

一、介绍

1.1 概念

1.2 目的

1.3 意义

二、图像畸变

2.1 概念

2.2 常见问题

 2.3 常见类型

 2.4 影响因素

三、方法

3.1 传统相机标定法

3.2 相机自标定法

3.3 主动视觉相机标定方法

3.4 零失真相机标定法

四、标定模板

4.1 概念

4.2 种类

 五、步骤

六、原理

6.1 基本坐标系

6.2 相机标定

 七、代码实现


一、介绍

1.1 概念

        在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)。

        通俗来讲所谓的相机标定就是将外界世界的坐标信息转化为计算机(自带相机/摄像头)可以理解的“距离”,将世界坐标系转换到相机坐标系。我们可以理解为从一个坐标系转换到另一个坐标系所需要的转换关系就是相机标定。在映射关系A=F(B)中,F( )就是相机标定要做的工作。举例说明就是:我和你在世界坐标系(平常我们所说的距离)下的距离为0.5m,但是相机并不知道我和你到底有多近!因此我们可以利用相机标定的方式,使相机知道我和你之间的距离。

1.2 目的

        求出相机的内、外参数,以及畸变参数。

1.3 意义

        无论是在图像测量还是在机器视觉应用中,相机参数的标定都是非常关键的环节,其标定结果的精度及算法的稳定性直接影响相机工作产生结果的准确性。因此,做好相机标定是做好后续工作的前提,提高标定精度是科研工作的重点所在。

二、图像畸变

2.1 概念

        由于透镜制造精度以及组装工艺的偏差引入畸变而导致的原始图像失真。

2.2 常见问题

        使用相机(摄像机)时,场景中实际水平或竖直的物体,从图像上看却发生了形变(如下图显示左侧柜子的直边出现弯曲),往往认为是摄像机出现了故障,其实这个是由镜头光学性能引起的一种光学现象。

Python计算机视觉之相机标定_第1张图片

 2.3 常见类型

        镜头由若干组凸和凹透镜构成、由于镜头焦平面上不同区域对影像的放大率不同,因而形成的画面会存在扭曲变形现象,这种变形的程度从画面中心至画面边缘依次递增,主要在画面边缘反映得较明显,可以说所有镜头都存在或多或少畸变的问题,畸变属于成像的几何失真,常见有枕型畸变和桶型畸变:

Python计算机视觉之相机标定_第2张图片

桶形畸变:广角镜头或使用变焦镜头的广角端时最容易发生

Python计算机视觉之相机标定_第3张图片

 

枕形畸变:长焦镜头或使用变焦镜头的长焦端时最容易发生

Python计算机视觉之相机标定_第4张图片

        最常见是桶形畸变现象,一般小于6mm的广角镜头会有明显的桶形畸变,而且越趋于广角,其畸变表现越明显 。

 2.4 影响因素

畸变的计算公式如下:

                                        α=h/[2*f*tan(θ/2) ]-1

其中:

        α表示畸变系数

        h表示成像sensor宽度

        f表示镜头焦距

        θ表示镜头水平视场角

由以上公式可以得知:

        ① 畸变和镜头焦距成反比,镜头焦距越小,其畸变越大。

        ② 畸变和传感器Sensor的有效成像靶面宽度成正比,Sensor成像宽度越大,畸变越大。

        ③ 相同焦距情况下,镜头视场角越大,其畸变越大。 越靠镜头中心取景,图像畸变现象越小。

三、方法

3.1 传统相机标定法

        传统相机标定法需要使用尺寸已知的标定物,通过建立标定物上坐标已知的点与其图像点之间的对应,利用一定的算法获得相机模型的内外参数。根据标定物的不同可分为三维标定物和平面型标定物。三维标定物可由单幅图像进行标定,标定精度较高,但高精密三维标定物的加工和维护较困难。平面型标定物比三维标定物制作简单,精度易保证,但标定时必须采用两幅或两幅以上的图像。传统相机标定法在标定过程中始终需要标定物,且标定物的制作精度会影响标定结果。同时有些场合不适合放置标定物也限制了传统相机标定法的应用。

3.2 相机自标定法

        目前出现的自标定算法中主要是利用相机运动的约束。相机的运动约束条件太强,因此使得其在实际中并不实用。利用场景约束主要是利用场景中的一些平行或者正交的信息。其中空间平行线在相机图像平面上的交点被称为消失点,它是射影几何中一个非常重要的特征,所以很多学者研究了基于消失点的相机自标定方法。自标定方法灵活性强,可对相机进行在线定标。但由于它是基于绝对二次曲线或曲面的方法,其算法鲁棒性差。

3.3 主动视觉相机标定方法

        基于主动视觉的相机标定法是指已知相机的某些运动信息对相机进行标定。该方法不需要标定物,但需要控制相机做某些特殊运动,利用这种运动的特殊性可以计算出相机内部参数。基于主动视觉的相机标定法的优点是算法简单,往往能够获得线性解,故鲁棒性较高,缺点是系统的成本高、实验设备昂贵、实验条件要求高,而且不适合于运动参数未知或无法控制的场合。

3.4 零失真相机标定法

        零失真相机标定法是以LCD显示屏为参考基准,以相移光栅为媒介,建立LCD像素与相机传感器像素之间的映射关系,确定每个相机像素点在LCD上的视点位置。镜头使相机在LCD上的视场为非矩形。在这个有畸变的视场内,可以构造一个内接的虚拟传感器,并保持相同的像素数。这样每个虚拟像素点就一定落在某四个相邻视点构成的任意四边形之内。虚拟像素点的亮度将由这四点的亮度经加权插值确定,而与其它像素点无关。用这四个加权系数的集合对原始图像作重采样(四次乘法、四次加法),就可以得到零畸变的输出图。对彩色相机的RGB三通道分别处理,但选用一个公共的虚拟传感器,则合成的彩色图像将是零畸变、零色差的。每个像素点(物理视点和虚拟点)的位置误差都是零均值,均方差都可小于1/1,000像素点距。

        在零失真相机标定法中,每一个像素矢量在空间都是独立标定的,无需知道相机内部的结构,无需建立几何模型。

四、标定模板

4.1 概念

        标定模板(标定板 Calibration Target) 在机器视觉、图像测量、摄影测量、三维重建等应用中,为校正镜头畸变;确定物理尺寸和像素间的换算关系;以及确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,需要建立相机成像的几何模型。通过相机拍摄带有固定间距图案阵列平板、经过标定算法的计算,可以得出相机的几何模型,从而得到高精度的测量和重建结果。而带有固定间距图案阵列的平板就是标定模板(标定板 Calibration Target)。

        在零失真相机标定中,一般用LCD显示屏作为标定板。在远距离、大视场应用中,可以将大型电影院的屏幕作为标定板,由经过标定的、零失真的数字投影仪投射相移光栅。在毫米级视场应用中,可以用无镜头的有源微型投影成像器件作为标定板。

4.2 种类

① 等间距实心圆阵列图案 Ti-times CG-100-D

 ② 国际象棋盘图案 Ti-times CG-076-T

Python计算机视觉之相机标定_第5张图片

 ③ 相移光栅(3纵、3横,120°相位移)

Python计算机视觉之相机标定_第6张图片

 五、步骤

1、打印一张棋盘格,把它贴在一个平面上,作为标定物。
2、通过调整标定物或摄像机的方向,为标定物拍摄一些不同方向的照片。
3、从照片中提取棋盘格角点。
4、估算理想无畸变的情况下,五个内参和六个外参。
5、应用最小二乘法估算实际存在径向畸变下的畸变系数。
6、极大似然法,优化估计,提升估计精度。

六、原理

        相机标定指建立相机图像像素位置与场景点位置之间的关系,根据相机成像模型,由特征点在图像中坐标与世界坐标的对应关系,求解相机模型的参数。相机需要标定的模型参数包括内部参数和外部参数。

6.1 基本坐标系

        世界坐标系:用户定义的三维世界的坐标系,为了描述目标物在真实世界里的位置而被引入。

        相机坐标系:在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。

        图像坐标系:为了描述成像过程中物体从相机坐标系到图像坐标系的投影透射关系而引入,方便进一步得到像素坐标系下的坐标。

6.2 相机标定

Python计算机视觉之相机标定_第7张图片

Python计算机视觉之相机标定_第8张图片 

Python计算机视觉之相机标定_第9张图片 

 七、代码实现

数据集:

Python计算机视觉之相机标定_第10张图片

 代码:

import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 棋盘格模板规格
# 内角点个数,内角点是和其他格子连着的点
w = 9
h = 6
# 世界坐标系中的棋盘格点,例如(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('image/*.jpg')
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 死区 求角点的迭代终止条件
        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.waitKey(1000)
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(("mtx:\n"), mtx)  # 内参数矩阵
print(("dist:\n"), dist)  # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print(("rvecs:\n"), rvecs)  # 旋转向量  # 外参数
print(("tvecs:\n"), tvecs)  # 平移向量  # 外参数
# 去畸变
img2 = cv2.imread('image/8.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('image/6_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))

运行结果: 

Python计算机视觉之相机标定_第11张图片

 Python计算机视觉之相机标定_第12张图片

 Python计算机视觉之相机标定_第13张图片

 畸变矫正前:Python计算机视觉之相机标定_第14张图片

畸变矫正后: 

Python计算机视觉之相机标定_第15张图片 

        对比畸变矫正前后的图片,发现以我们的肉眼观察,并不能观察出太大的区别,说明我们自己的相机镜头不存在太大的畸变。

你可能感兴趣的:(计算机视觉,几何学,人工智能)