前言:基于Python版本的相机校正,并且校正结果与官方对比主要差值较小。话不多说,直接上代码。
包括opencv, numpy和文件操作库os
import cv2
import numpy as np
import os
本次主要有两个比较麻烦的要解决,其一,批量读取,第二,真实坐标的写入。我使用os编写的读取文件函数,比较垃圾,大佬请用glob或者其他的。真实坐标主要是标定板上内点的真实相片坐标,通过参考其他大佬总结的出
def getImageList(img_dir):
# 获取图片文件夹位置,方便opencv读取
# 参数:照片文件路径
# 返回值:数组,每一个元素表示一张照片的绝对路径
imgPath = []
if os.path.exists(img_dir) is False:
print('error dir')
else:
for parent, dirNames, fileNames in os.walk(img_dir):
for fileName in fileNames:
imgPath.append(os.path.join(parent, fileName))
return imgPath
def getObjectPoints(m, n, k):
# 计算真实坐标
# 参数:内点行数,内点列, 标定板大小
# 返回值:数组,(m*n行,3列),真实内点坐标
objP = np.zeros(shape=(m * n, 3), dtype=np.float32)
for i in range(m * n):
objP[i][0] = i % m
objP[i][1] = int(i / m)
return objP * k
# 相机标定参数设定(单目,双目)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 计算标定板真实坐标,标定板内点9*6,大小10mm*10mm
objPoint = getObjectPoints(9, 6, 10)
objPoints = []
imgPointsL = []
imgPointsR = []
# 相片路径,自行修改
imgPathL = 'D:/SA/2021.3/6.1/l/'
imgPathR = 'D:/SA/2021.3/6.1/r/'
filePathL = getImageList(imgPathL)
filePathR = getImageList(imgPathR)
for i in range(len(filePathL)):
# 分别读取每张图片并转化为灰度图
imgL = cv2.imread(filePathL[i])
imgR = cv2.imread(filePathR[i])
grayL = cv2.cvtColor(imgL, cv2.COLOR_BGR2GRAY)
grayR = cv2.cvtColor(imgR, cv2.COLOR_BGR2GRAY)
# opencv寻找角点
retL, cornersL = cv2.findChessboardCorners(grayL, (9, 6), None)
retR, cornersR = cv2.findChessboardCorners(grayR, (9, 6), None)
if (retL & retR) is True:
# opencv对真实坐标格式要求,vector>类型
objPoints.append(objPoint)
# 角点细化
cornersL2 = cv2.cornerSubPix(grayL, cornersL, (5, 5), (-1, -1), criteria)
cornersR2 = cv2.cornerSubPix(grayR, cornersR, (5, 5), (-1, -1), criteria)
imgPointsL.append(cornersL2)
imgPointsR.append(cornersR2)
# 对左右相机分别进行单目相机标定(复制时格式可能有点问题,用pycharm自动格式化)
retL, cameraMatrixL, distMatrixL, RL, TL = cv2.calibrateCamera(objPoints, imgPointsL, (640, 480), None, None)
retR, cameraMatrixR, distMatrixR, RR, TR = cv2.calibrateCamera(objPoints, imgPointsR, (640, 480), None, None)
# 双目相机校正
retS, mLS, dLS, mRS, dRS, R, T, E, F = cv2.stereoCalibrate(objPoints, imgPointsL,
imgPointsR, cameraMatrixL,
distMatrixL, cameraMatrixR,
distMatrixR, (640, 480),
criteria_stereo, flags=cv2.CALIB_USE_INTRINSIC_GUESS)
# 标定结束,结果输出,cameraMatrixL,cameraMatrixR分别为左右相机内参数矩阵
# R, T为相机2与相机1旋转平移矩阵
print(cameraMatrixL)
print('*' * 20)
print(cameraMatrixR)
print('*' * 20)
print(R)
print('*' * 20)
print(T)
1.关于真实坐标。这个是以标定板为平面z=0的内点真实坐标,单独输出就是这样的。是标定板由左上内点开始,然后是右侧点,接着下面的点:(是这个顺序)。
其次,仔细看opencv图片可以发现内点9*6和6*9是一样的,你可以自己试试,我试过了,一样的
a = getObjectPoints(9, 6, 10)
print(a)
[[ 0. 0. 0.]
[10. 0. 0.]
[20. 0. 0.]
[30. 0. 0.]
[40. 0. 0.]
[50. 0. 0.]
[60. 0. 0.]
[70. 0. 0.]
[80. 0. 0.]
[ 0. 10. 0.]
[10. 10. 0.]
[20. 10. 0.]
[30. 10. 0.]
[40. 10. 0.]
[50. 10. 0.]
[60. 10. 0.]
[70. 10. 0.]
[80. 10. 0.]
[ 0. 20. 0.]
[10. 20. 0.]
[20. 20. 0.]
[30. 20. 0.]
[40. 20. 0.]
[50. 20. 0.]
[60. 20. 0.]
[70. 20. 0.]
[80. 20. 0.]
[ 0. 30. 0.]
[10. 30. 0.]
[20. 30. 0.]
[30. 30. 0.]
[40. 30. 0.]
[50. 30. 0.]
[60. 30. 0.]
[70. 30. 0.]
[80. 30. 0.]
[ 0. 40. 0.]
[10. 40. 0.]
[20. 40. 0.]
[30. 40. 0.]
[40. 40. 0.]
[50. 40. 0.]
[60. 40. 0.]
[70. 40. 0.]
[80. 40. 0.]
[ 0. 50. 0.]
[10. 50. 0.]
[20. 50. 0.]
[30. 50. 0.]
[40. 50. 0.]
[50. 50. 0.]
[60. 50. 0.]
[70. 50. 0.]
[80. 50. 0.]]
2.opencv+Python和matlab和opencv+C++的结果都不一样,但是主要部分都是大致相同的(比如说焦距都是533, 旋转矩阵对角线值差不多,平移矩阵大致为-33左右)。这就造成了计算三维坐标的不统一。emm,再想想解决办法吧