一、数学原理
1.将世界坐标转换为图像坐标:
2.图像坐标转换为像素坐标系:
3.针孔成像下的透视投影矩阵:
4.世界坐标系转换为像素坐标系
5.张氏标定法的畸变模型:
以上参考:/lql0716/article/details/71973318?locationNum=8&fps=1
二、相机标定结果
newcameramtx是相机内参
dist是畸变参数
1.49和1.54开头的两个数表示焦距
7.39和5.23分别是像主点x和y的坐标值
total error表示误差 此方法可达到误差小于0.5
P.S. photos shot on ipad mini5
三、相机标定过程
1.操作步骤
1.打印一张棋盘格
2.对其拍摄10-20张变换角度和距离的照片
3.运用张正友方法进行标定
3.1 读取标定图片
3.2 对每一张标定图片,提取角点信息
3.3 对每一张标定图片,进一步提取亚像素角点信息
3.4.相机标定
2.标定的python代码
以下代码参考来源:/h2oco2ch4/article/details/79504449
#-*- coding:utf-8 -*-import cv2import globimport numpy as np'''cbraw和cbcol是我自己加的。tutorial用的棋盘足够大包含了7×6以上个角点,我自己用的是9×9。这里如果角点维数超出的话,标定的时候会报错。'''cbraw = 9cbcol = 9# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)objp = np.zeros((cbraw*cbcol,3), np.float32)'''设定世界坐标下点的坐标值,因为用的是棋盘可以直接按网格取;假定棋盘正好在x-y平面上,这样z值直接取0,简化初始化步骤。mgrid把列向量[0:cbraw]复制了cbcol列,把行向量[0:cbcol]复制了cbraw行。转置reshape后,每行都是9×9网格中的某个点的坐标。'''objp[:,:2] = np.mgrid[0:cbraw,0:cbcol].T.reshape(-1,2)objpoints = [] # 3d point in real world spaceimgpoints = [] # 2d points in image plane.#glob是个文件名管理工具#images = glob.glob("*.jpg")images = glob.glob('D:\data\chess\chess\*.jpg')for fname in images:#对每张图片,识别出角点,记录世界物体坐标和图像坐标img = cv2.imread(fname) #source image#我用的图片太大,缩小了一半img = cv2.resize(img,None,fx=0.5, fy=0.5, interpolation = cv2.INTER_CUBIC)gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转灰度#cv2.imshow('img',gray)#cv2.waitKey(1000)#寻找角点,存入corners,ret是找到角点的flagret, corners = cv2.findChessboardCorners(gray,(9,9),None)#criteria:角点精准化迭代过程的终止条件criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)#执行亚像素级角点检测corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)objpoints.append(objp)imgpoints.append(corners2)#在棋盘上绘制角点,只是可视化工具img = cv2.drawChessboardCorners(gray,(9,9),corners2,ret)cv2.imshow('img',img)#cv2.waitKey(1000)'''传入所有图片各自角点的三维、二维坐标,相机标定。每张图片都有自己的旋转和平移矩阵,但是相机内参和畸变系数只有一组。mtx,相机内参;dist,畸变系数;revcs,旋转矩阵;tvecs,平移矩阵。'''ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)img = cv2.imread('D:\\data\\ches\\IMG_0414_182918.jpg')#注意这里跟循环开头读取图片一样,如果图片太大要同比例缩放,不然后面优化相机内参肯定是错的。img = cv2.resize(img,None,fx=0.5, fy=0.5, interpolation = cv2.INTER_CUBIC)h,w = img.shape[:2]'''优化相机内参(camera matrix),这一步可选。参数1表示保留所有像素点,同时可能引入黑色像素,设为0表示尽可能裁剪不想要的像素,这是个scale,0-1都可以取。'''newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#纠正畸变dst = cv2.undistort(img, mtx, dist, None, newcameramtx)#这步只是输出纠正畸变以后的图片x,y,w,h = roidst = dst[y:y+h, x:x+w]cv2.imwrite('calibresult.png',dst)#打印我们要求的两个矩阵参数print ("newcameramtx:\n",newcameramtx)print ("dist:\n",dist)#计算误差tot_error = 0for 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)tot_error += errorprint ("total error: ", tot_error/len(objpoints))