要注意在输入棋盘格规格的时候,输入的是宽和高方向的角点个数,而不是棋盘格的数量。
可直接运行的代码:
"""
根据OpenCV-Python Tutorial修改的相机标定程序
OpenCV-Python Tutorial Camera Calibration link:
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html#calibration
中文教程链接:
http://woshicver.com/Eighth/7_1_%E7%9B%B8%E6%9C%BA%E6%A0%A1%E5%87%86/
"""
import numpy as np
import cv2
import glob
# 棋盘规格:宽高方向角点个数
w_corner = 11
h_corner = 11
# 角点精准化迭代过程的终止条件
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 世界坐标,如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((w_corner*h_corner,3), np.float32)
objp[:,:2] = np.mgrid[0:w_corner,0:h_corner].T.reshape(-1,2)
# 用于存储所有图像的世界坐标点和图像坐标点
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob('data/images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 寻找棋盘角点
ret, corners = cv2.findChessboardCorners(gray, (11,11),None)
# 如果找到,则添加世界坐标点、细化后的图像坐标点
if ret == True:
objpoints.append(objp)
# 亚像素级角点检测(细化)
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
imgpoints.append(corners2)
# 进行可视化,绘制并显示角点
img = cv2.drawChessboardCorners(img, (w_corner,h_corner), corners2,ret)
# 将图像缩小1/4,以便于显示
img = cv2.resize(img, None, fx=0.25, fy=0.25, interpolation=cv2.INTER_CUBIC)
cv2.imshow('img',img)
cv2.waitKey(500)
cv2.destroyAllWindows()
'''
经过上面的步骤得到了目标点和图像点,下面进行校准。
使用函数 cv.calibrateCamera() 返回相机内参矩阵,畸变系数,旋转矩阵和平移矢量等
其中,mtx:相机内参;dist:畸变系数;revcs:旋转矩阵;tvecs:平移矢量。
'''
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
print("相机内参mtx:\n",mtx)
print("畸变系数dist:\n",dist)
print("旋转矩阵rvecs:\n",rvecs)
print("平移矢量tvecs:\n",tvecs)
'''
显示畸变矫正效果
畸变矫正有两种方法:cv.undistort()、remapping
'''
# 读取要畸变矫正的图像
img = cv2.imread("data/images/1.jpg")
h,w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# 使用 cv.undistort() 方法
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# 剪裁图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult1.jpg', dst)
# 使用remapping方法
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult2.jpg', dst)
# 打印我们要求的两个矩阵参数
# print("newcameramtx:\n",newcameramtx)
# print("dist:\n",dist)
# 重投影误差
tot_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)
tot_error += error
print("total error: ", tot_error/len(objpoints))
标定结果:
相机内参mtx:
[[3.62397724e+03 0.00000000e+00 1.03555673e+03]
[0.00000000e+00 3.60800179e+03 2.28840559e+03]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
畸变系数dist:
[[ 0.01015579 -0.00968475 -0.00359203 0.00063539 -0.09014052]]
旋转矩阵rvecs:
[array([[0.23458067],
[0.03609411],
[0.01378744]]), array([[0.103319 ],
[0.01785917],
[0.02637166]]), array([[0.19155436],
[0.01804893],
[0.02577625]]), array([[0.1862171 ],
[0.0201073 ],
[0.10007198]]), array([[ 0.03643646],
[ 0.10606325],
[-1.55851812]]), array([[ 0.20738004],
[-0.10248278],
[ 1.55692683]]), array([[0.20488035],
[0.05415721],
[0.02873221]]), array([[0.17458788],
[0.0385833 ],
[0.02149842]]), array([[0.09554853],
[0.05823517],
[0.1356644 ]]), array([[ 0.12800001],
[-0.0954735 ],
[ 1.56990554]]), array([[ 0.12825191],
[-0.12004171],
[ 1.56090509]]), array([[ 0.19739751],
[-0.17778952],
[ 1.45885639]]), array([[0.27084852],
[0.07155931],
[0.32709402]]), array([[0.08927987],
[0.01542926],
[0.02030111]]), array([[0.19449851],
[0.00824815],
[0.03083676]]), array([[0.17972332],
[0.05005703],
[0.06841965]]), array([[ 0.15105951],
[-0.22459034],
[ 1.40366761]]), array([[0.18017921],
[0.00658282],
[0.03702818]]), array([[ 0.16652348],
[-0.16614997],
[ 1.56031062]])]
平移矢量tvecs:
[array([[-4.300679 ],
[-9.36847784],
[22.97639231]]), array([[-4.73084382],
[-6.2112322 ],
[23.13301126]]), array([[ -4.58038817],
[-10.0147383 ],
[ 25.14218226]]), array([[-4.41402887],
[-9.73630673],
[24.5082232 ]]), array([[-4.90003855],
[ 0.4437966 ],
[23.5416314 ]]), array([[ 5.10049755],
[-10.19637765],
[ 24.38507658]]), array([[ -5.04264751],
[-10.13239477],
[ 25.16396889]]), array([[ -5.15920078],
[-12.89855307],
[ 26.46382224]]), array([[ -4.73734106],
[-12.11464393],
[ 28.50105915]]), array([[ 5.12128348],
[-11.26842165],
[ 26.59711439]]), array([[ 5.16858973],
[-10.19007255],
[ 22.47868555]]), array([[ 5.14837646],
[-10.20699796],
[ 24.61079007]]), array([[ -3.58050408],
[-11.7782039 ],
[ 26.02613586]]), array([[-4.74846329],
[-9.70068358],
[23.74349543]]), array([[ -4.6623531 ],
[-10.0071674 ],
[ 24.09164532]]), array([[-4.55941641],
[-9.38868484],
[24.73900225]]), array([[ 4.84588293],
[-10.6009499 ],
[ 25.67295188]]), array([[-4.67294025],
[-7.13850229],
[23.33850262]]), array([[ 5.08380542],
[-8.77305999],
[23.97963252]])]
total error: 0.06548598288547088