相机标定原理,这里不再赘述,一般使用张友正相机标定法。这里只介绍了标定相机内参的方法,即3x3的matrix。
import cv2
import numpy as np
import glob
# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
# 获取标定板角点的位置
objp = np.zeros((5 * 7, 3), np.float32) ## 仅算内角点,注意内角点即不靠近边界的角点
objp[:, :2] = np.mgrid[0:7, 0:5].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y 5行7列reshape成(35,2),即真实的坐标(0,0),(1,0)...
a = np.mgrid[0:7, 0:5].T
b = np.mgrid[0:7, 0:5]
print(a, a.shape) #(5,7,2)
print(b, b.shape) #(2,7,5)
#exit()
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images = glob.glob("./picture/*.jpg")
for fname in images:
img = cv2.imread(fname)
print(fname)
#h, w, _ = img.shape
img = cv2.resize(img, (600, 600))
#print(img)
cv2.waitKey(15)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cv2.imshow('img', gray)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (7, 5), None) # 寻找真实世界角点坐标在照片中的像素坐标,(这里的7和5,表示Number of inner corners per a chessboard row and column,一定注意,不是7行5列,有些博客的介绍是错误的)
print(corners.shape)
print(ret)
exit()
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, (7, 5), corners, ret) #绘制角点
cv2.imshow('img', img)
cv2.waitKey(2000)
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) # 畸变系数
#print("rvecs:\n", rvecs) # 旋转向量
#print("tvecs:\n", tvecs ) # 平移向量
这里需要注意的是,真实坐标和照片内的像素坐标的顺序一定要对应,并且这里的7和5分别代表列数和行数,需要根据自己的标定板来做相应的变换。
还有cv2.findChessboardCorners(gray, (7, 5), None)函数里7和5的意义,有写博客的介绍成(行数,列数)是错误的。
这里的cornerSubPix函数是求亚像素坐标,即带小数点的坐标值,目的是更加精确,详细介绍可以参见这篇博客:https://www.cnblogs.com/riddick/p/8476456.html
本文参考:https://www.cnblogs.com/dinghongkai/p/11211840.html
关于调试中的一些bug:
cv2.error: OpenCV(4.1.1)error: (-215:Assertion failed) ni == ni1
这是因为在调用cv2.calibrateCamera时定义的棋盘格数量和真实的不一致,需要自行调整。