在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)。
张正友标定是指张正友教授1998年提出的单平面棋盘格的摄像机标定方法。本文中提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来的棋盘格就可以。同时也相对于自标定而言,提高了精度,便于操作。因此张氏标定法被广泛应用于计算机视觉方面。
在视觉测量中,需要进行的一个重要预备工作是定义四个坐标系的意义,即摄像机坐标系 、 图像物理坐标系、像素坐标系和世界坐标系(参考坐标系) 。
世界坐标系(world coordinate)(xw,yw,zw),也称为测量坐标系,是一个三维直角坐标系,以其为基准可以描述相机和待测物体的空间位置。世界坐标系的位置可以根据实际情况自由确定。
相机坐标系(camera coordinate)(xc,yc,zc),也是一个三维直角坐标系,原点位于镜头光心处,x、y轴分别与相面的两边平行,z轴为镜头光轴,与像平面垂直。
像素坐标系 uov是一个二维直角坐标系,反映了相机CCD/CMOS芯片中像素的排列情况。原点o位于图像的左上角,u轴、v轴分别于像面的两边平行。像素坐标系中坐标轴的单位是像素(整数)。
图像坐标系XOY,其坐标轴的单位通常为毫米(mm),原点是相机光轴与相面的交点(称为主点),即图像的中心点,X轴、Y轴分别与u轴、v轴平行。故两个坐标系实际是平移关系,即可以通过平移就可得到。
单应性矩阵我们在之前的照相机模型里有所介绍,大概的求解公式为:
其中m的齐次坐标表示图像平面的像素坐标(u,v,1),M的齐次坐标表示世界坐标系的坐标点(X,Y,Z,1)。R表示旋转矩阵、t表示平移矩阵、S表示尺度因子。A表示摄像机的内参数,具体表达式如下:其中α=f/dx,β=f/dy,因为像素不是规规矩矩的正方形,γ代表像素点在x,y方向上尺度的偏差,称为径向畸变参数。
s是个尺度因子,对于齐次坐标,尺度因子不会改变坐标值的。我们假设棋盘格位于Z=0。则有以下推理:
那么我们可以给A[r1 r2 t]一个名字:单应性矩阵。并记H= A[r1 r2 t]。H是一个三3*3的矩阵,并且有一个元素是作为齐次坐标。因此,H有8个未知量待解,所以我们至少需要八个方程。所以需要四个对应点即可算出图像平面到世界平面的单应性矩阵H。
1、准备图片,打印一张棋盘格,把它放在一个平面上,作为标定物。
2、通过调整标定物或摄像机的方向,采用iphone 6s为标定物拍摄一些不同方向的照片,总共拍了十张照片。
3、从照片中提取棋盘格角点信息。
OpenCV中自带了提取棋盘格中内角点的函数:findChessboardCorners()。这个函数的功能是确定输入图像中是否有棋盘格图案,并检测棋盘格的内角点。如果所有的内角点都找到了,那么函数返回一个非0值;如果没有找到所有的内角点,就会返回0。 因为我们使用的是有9×5的内格子,所以我们在设置w和h时分别填写9和5。函数返回每一一个角点,如果匹配到了模式,它将返回是True。这些角点将按一定顺序标注出来(从左到右,从上到下) 。
使用drawChessboardCorners函数。函数功能很简单,就是在图片中画出检测到的角点。
6. 标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量。
7. 评价标定结果。
8. 矫正图像。
通过上面的步骤,我们得到了用于标定的三维点和与其对应的图像上的二维点对。我们使用cv2.calibrateCamera()进行标定,这个函数会返回标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量。计算的参数如下:
mtx为内参数矩阵,dist为畸变系数:
畸变矫正情况:
下面我们通过图片来对比以下畸变矫正前后的图片:
畸变矫正前:
import cv2
import numpy as np
import glob
# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 获取标定板角点的位置
objp = np.zeros((5*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:5].T.reshape(-1,2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images = glob.glob(r"C:\Users\GGboom\PycharmProjects\ch05\data\*.jpg")
i=0;
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (9, 5), None)
#print(corners)
if ret:
obj_points.append(objp)
corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角点的基础上寻找亚像素角点
#print(corners2)
if [corners2]:
img_points.append(corners2)
else:
img_points.append(corners)
cv2.drawChessboardCorners(img, (9, 5), corners, ret) # 记住,OpenCV的绘制函数一般无返回值
i+=1;
cv2.imwrite('conimg'+str(i)+'.jpg', img)
cv2.waitKey(4000)
print(len(img_points))
cv2.destroyAllWindows()
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)
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 ) # 平移向量 # 外参数
print("-----------------------------------------------------")
img = cv2.imread(images[2])
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#显示更大范围的图片(正常重映射之后会删掉一部分图像)
print (newcameramtx)
print("------------------使用undistort函数-------------------")
dst = cv2.undistort(img,mtx,dist,None,newcameramtx)
x,y,w,h = roi
dst1 = dst[y:y+h,x:x+w]
cv2.imwrite('calibresult3.jpg', dst1)
print ("方法一:dst的大小为:", dst1.shape)
1.OpenCV中自带了提取棋盘格中内角点的函数:findChessboardCorners()。
2.有两个函数可以实现提取亚像素角点信息:cornerSubPix、find4QuadCornerSubpix。在提取棋盘格角点时两者的效果差不多,随便使用哪一个都行。
3.利用drawChessboardCorners函数画出角点。函数功能很简单,就是在图片中画出检测到的角点。
4.标定函数是calibrateCamera,也是相机标定的核心了。
5.用前面求得的内参和外参以及畸变参数数据,可以对图像进行畸变矫正,使用initUndistort函数来实现。
参考链接:https://blog.csdn.net/baidu_38172402/article/details/82558452