原理简述:
单孔摄像机(照相机)会给图像带来很多畸变,畸变主要有两种:径向畸变(图像像素点以畸变中心为中心点,沿着径向产生的位置偏差导致成像发生形变)和切向畸变(由于透镜与成像平面不可能绝对平行造成)。通过拍摄实际规格已知的标定板可以得到n个对应的世界坐标三维点 Xi 和对应的图像坐标二维点 xi,这些三维点到二维点的转换都可以通过相机内参 K,相机外参 R 和 t,以及畸变参数 s 经过一系列的矩阵变换得到。图像间的对应关系可以来求解这些相机参数,所以用线性方法求解内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像进行矫正,得到畸变相对很小的图像。
初始的标定方法需要三面相互垂直且带有大量不同大小透光点的高精度标定板,而张正友教授1998年提出的单平面棋盘格摄像机标定方法假定图像位于 Z=0 (XY)平面上进行标定,已知两个平面的对应点的坐标就可以求解得到两个平面的单应矩阵H,而黑白棋盘格的角点便于检测,位置信息便于获取,因此仅需使用一个打印出来的棋盘格就可以实现参数标定。
标定原理详解:
https://blog.csdn.net/honyniu/article/details/51004397
http://www.cnblogs.com/Undo-self-blog/p/8448500.html
张氏标定详解:https://www.jianshu.com/p/9d2fe4c2e3b7
输入:20张标定板照片。标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标。
输出:相机内部参数,畸变系数矩阵,反向投影误差。
尝试标定的流程如下:
使用手机相机拍摄,准备标定图片;
对每一张标定图片,提取Harris角点信息;
对每一张标定图片,进一步提取亚像素角点信息;
在棋盘标定图上绘制找到的内角点;
相机标定,修正畸变图像;
计算标定误差;
#coding:utf-8
import cv2
import numpy as np
import glob
# 找棋盘格角点
# 阈值
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('./image/Calibration/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 找到棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
# 如果找到足够点对,将其存储起来
if ret is True:
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(1)
cv2.destroyAllWindows()
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# 去畸变
img2 = cv2.imread('./image/Calibration/1.jpg')
h, w = img2.shape[:2]
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.png', dst)
print("newcameramtx:\n", newcameramtx)
print("dist:\n", dist)
# 反投影误差
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))
其中误差计算部分用到的 range() 函数原为 Python2.x 中的 xrange(),需根据环境自行修改:
我的手机型号为 Samsung Galaxy S8,标定结果如下:
其中 newcameramtx 为内参矩阵,dist 为畸变系数矩阵,total error 是反向投影误差统计值
畸变图像与原图对比:
Tips: