【精选】python计算机视觉 相机标定--张正友棋盘格标定法_python张正友标定检测角点_༄༄小半生༅࿐的博客-CSDN博客
前言
此次实验将用张正友棋盘格标定法对相机参数进行标定。”张正友标定法”是指张正友教授1998年提出的单平面棋盘格的摄像机标定方法。文中提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点。相对于自标定而言,提高了精度,便于操作。因此张正友标定法被广泛应用于计算机视觉方面。
1.1 相机标定简介
在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)。无论是在图像测量或者机器视觉应用中,相机参数的标定都是非常关键的环节,其标定结果的精度及算法的稳定性直接影响相机工作产生结果的准确性。
1.2 算法流程
(1)打印一张棋盘方格图并贴在一个平面上
(2)从不同角度拍摄若干张模板图像
(3)检测出图像中的特征点
(4)由检测到的特征点计算出每幅图像中的平面投影矩阵H
(5)确定出摄像机的参数
import cv2
import numpy as np
import glob
# 找棋盘格角点
# 棋盘格模板规格(内角点个数,内角点是和其他格子连着的点,如10 X 7)
w = 7
h = 4
# 世界坐标系中的棋盘格点,例如(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('.\\import2\\*.jpg')
size = tuple()
for fname in images:
img = cv2.imread(fname)
# 修改图像尺寸,参数依次为:输出图像,尺寸,沿x轴,y轴的缩放系数,INTER_AREA在缩小图像时效果较好
# img = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转灰度
size = gray.shape[::-1] # 矩阵转置
# 找到棋盘格角点
# 棋盘图像(8位灰度或彩色图像) 棋盘尺寸 存放角点的位置
ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
# 角点精确检测
# criteria:角点精准化迭代过程的终止条件(阈值)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 执行亚像素级角点检测
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
objpoints.append(objp)
imgpoints.append(corners2)
# 将角点在图像上显示
cv2.drawChessboardCorners(img, (w, h), corners2, ret)
cv2.imshow('findCorners', img)
cv2.waitKey(1000)
"""
标定、去畸变:
输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 3*3矩阵,相机内参数矩阵 畸变矩阵
输出:标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
"""
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, size, None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
print("ret:", ret)
print("内参数矩阵:\n", mtx, '\n')
print("畸变系数:\n", dist, '\n')
print("旋转向量(外参数):\n", rvecs, '\n')
print("平移向量(外参数):\n", tvecs, '\n')
# 去畸变
img2 = cv2.imread('.\\import\\1.jpg')
h, w = img2.shape[:2]
# 我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,
# 通过设定自由自由比例因子alpha。当alpha设为0的时候,
# 将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;
# 当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉
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.jpg', dst)
# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
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))
测试不同的位置拍摄的数据结果:
第一组结果:
内参数矩阵:
[[2.22572962e+03 0.00000000e+00 1.48049041e+03]
[0.00000000e+00 2.22062534e+03 7.62833497e+02]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
畸变系数:
[[-0.04478733 0.37388537 0.02416808 0.00396287 -1.90215814]]
第二组结果:
内参数矩阵:
[[2.03953347e+03 0.00000000e+00 1.45008008e+03]
[0.00000000e+00 2.04010145e+03 7.11562213e+02]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
畸变系数:
[[ 3.40708167e-01 -4.77510425e+00 1.03237708e-02 5.71104182e-03 2.05014262e+01]]
第三组结果:
内参数矩阵:
[[2.04051320e+03 0.00000000e+00 4.81965678e+02]
[0.00000000e+00 2.29996747e+03 1.18294044e+03]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
畸变系数:
[[ 0.21336614 -1.88171553 -0.08416898 -0.03182012 2.66319275]]
结果差别挺大