一个完整的相机标定和畸变校正的示例。它包括了相机标定的步骤、计算相机内参和畸变系数、以及对图像进行畸变校正的两种方法。
import cv2
import numpy as np
import glob
################1配置参数###################
# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # 阈值
#棋盘格模板规格(棋盘格角点数)
w = 9 # 10 - 1
h = 6 # 7 - 1
Block_size = 1 # 棋盘格的每个方块边长(mm)
images = glob.glob('calibration_images/*.jpg') # 拍摄的十几张棋盘图片所在目录
################2计算相机的内参和畸变系数###################
# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w*h,3), np.float32) #创建了一个形状为(w*h, 3)的二维数组objp,数据类型为np.float32。这个数组的每一行有3个元素,表示一个三维空间中的点的坐标(x, y, z),初始值全部为0。
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
objp = objp*Block_size # 乘以棋盘格的每个方块边长,得到棋盘格角点的实际物理坐标(单位:毫米)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点
i=0
for fname in images:
img = cv2.imread(fname)
# 获取画面中心点
#获取图像的长宽
h1, w1 = img.shape[0], img.shape[1]
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
u, v = img.shape[:2]
# 找到棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
# 如果找到足够点对,将其存储起来
if ret == True:
print("i:", i)
i = i+1
#print("Number of corners found:", corners.shape[0])
#print("Corners coordinates:\n", corners)
# 在原角点的基础上寻找亚像素角点
cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
#追加进入世界三维点和平面二维点中
objpoints.append(objp) #物理坐标(mm)
imgpoints.append(corners) #像素坐标
# 将角点在图像上显示
cv2.drawChessboardCorners(img, (w,h), corners, ret)
cv2.namedWindow('findCorners', cv2.WINDOW_NORMAL)
cv2.resizeWindow('findCorners', 640, 480)
cv2.imshow('findCorners',img)
cv2.waitKey(200)
else:
print("Chessboard corners not found.")
cv2.destroyAllWindows()
#%% 标定
print('Calculating.....')
#标定
ret, mtx, dist, rvecs, tvecs = \
cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
################3打印标定结果###################
print("RMS error:", ret) # ret should be <0.5
print("Camera matrix:\n", mtx) # 内参数矩阵
print("Distortion coefficients:\n", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
#print("Rotation vectors:\n", rvecs) # 旋转向量 # 外参数
#print("Translation vectors:\n", tvecs) # 平移向量 # 外参数
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 0, (u, v))
print('new camera mtx内参数矩阵',newcameramtx)
#####################对图像进行畸变校正################################
#对图像进行畸变校正--方法1
# 读取图像
img = cv2.imread('distorted_image.jpg')
# 相机内参矩阵和畸变系数(假设已经通过相机标定获得)
cameraMatrix = mtx
distCoeffs = dist
# 计算新的相机内参矩阵,以优化去除畸变后的图像视野范围
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 0, (u, v)) #0表示裁剪掉所有畸变区域,只保留未畸变的部分-推荐
#newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 1, (u, v)) #1表示不裁剪任何图像,可能会包含畸变区域
# 进行畸变校正
undistorted_img = cv2.undistort(img, cameraMatrix, distCoeffs, None, newcameramtx)
# 显示结果
cv2.imshow('Original Image', img)
cv2.imshow('Undistorted Image', undistorted_img)
cv2.waitKey(5000)
cv2.destroyAllWindows()
#####################################################
#对图像进行畸变校正--方法2(更高效,更灵活)
# 读取图像
frame = cv2.imread('distorted_image.jpg')
# 相机内参矩阵和畸变系数(假设已经通过相机标定获得)
mtx = mtx
dist = dist
# 计算新的相机内参矩阵,以优化去除畸变后的图像视野范围
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 1, (u, v)) ##0表示裁剪掉所有畸变区域,只保留未畸变的部分,参数1表示不裁剪任何图像,可能会包含畸变区域
# 生成映射矩阵
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (u, v), 5)
# 进行畸变校正
dst2 = cv2.remap(frame, mapx, mapy, cv2.INTER_LINEAR)
# 显示结果
cv2.imshow('Original Image', frame)
cv2.imshow('Undistorted Image2', dst2)
cv2.waitKey(5000)
cv2.destroyAllWindows()
细节解释:
criteria: 设置寻找亚像素角点的参数,包括最大循环次数和最大误差容限。
w 和 h: 棋盘格的角点数,分别表示棋盘格的宽度和高度方向上的角点数。
Block_size: 棋盘格的每个方块边长,单位为毫米。
images: 包含所有用于标定的棋盘格图像的路径列表。
objp: 创建一个包含棋盘格角点的世界坐标(三维)的数组。
objpoints 和 imgpoints: 分别存储棋盘格角点的世界坐标和图像坐标。
cv2.findChessboardCorners: 在图像中寻找棋盘格角点。
cv2.cornerSubPix: 在找到的角点基础上寻找亚像素级的角点。
cv2.drawChessboardCorners: 在图像上绘制找到的角点。
cv2.imshow 和 cv2.waitKey: 显示图像并等待用户按键。
cv2.calibrateCamera: 计算相机的内参矩阵mtx、畸变系数dist、旋转向量rvecs和平移向量tvecs。
cv2.getOptimalNewCameraMatrix: 计算新的相机内参矩阵newcameramtx,以优化去除畸变后的图像视野范围。
cv2.undistort: 对图像进行畸变校正。