单目摄像头标定及矫正
①标定
既然是标定,就需要准备使用待标定单目摄像头拍摄的标定板图片。
这里使用棋盘格标定板,可以到网上下载,需要知道棋盘格的角点。
由于我的摄像头是嵌入于开发板底板中,并且摄像头数据通过ros话题发布,所以拍照方式比较不一样,使用ros作为工具,编写节点代码进行拍照,不了解ros的可以自己搜索一下如何对连接到开发板上的usb摄像头拍照,一般开发板连接USB摄像头的话,可以直接通过opencv的videocapture打开摄像头获取数据,比较方便的拍照。拍摄不同角度和位置的图片10-20张即可。
标定代码如下:可以在各种能运行python代码的IDE中运行,比如pycharm、vscode等等。
import cv2
import numpy as np
import glob
import os
import yaml
# 相机标定
# 设置棋盘格w和h方向的角点数量
w_corners = 8 #改!数棋盘格宽有多少个格子,然后减一
h_corners = 5 #改!数棋盘格高有多少个格子,然后减一
# 设置图像路径
images = glob.glob(r"C:\new_pycharm_project\yolov10-main\daijiaozheng\*.png") #改!改成自己存放图片的路径
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
# 获取标定板角点的位置
objp = np.zeros((w_corners * h_corners, 3), np.float32)
objp[:, :2] = np.mgrid[0:w_corners, 0:h_corners].T.reshape(-1, 2)
objp = objp * 21 #改!这里的21是一个格子的长度,单位是mm
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
i = 0
def save_calibration_to_yaml(file_path, cameraMatrix_l, distCoeffs_l):
data = {
'camera_matrix': {
'rows': 3,
'cols': 3,
'dt': 'd',
'data': cameraMatrix_l.flatten().tolist()
},
'dist_coeff': {
'rows': 1,
'cols': 5,
'dt': 'd',
'data': distCoeffs_l.flatten().tolist()
}
}
with open(file_path, 'w') as file:
yaml.dump(data, file, default_flow_style=False)
print(f"Calibration parameters saved to {file_path}")
for fname in images:
if not os.path.exists(fname):
print(f"文件不存在: {fname}")
continue
try:
with open(fname, 'rb') as f:
print(f"文件正常读取: {fname}")
except Exception as e:
print(f"无法读取文件: {fname}, 错误: {e}")
continue
img = cv2.imread(fname)
if img is None:
print(f"OpenCV 无法读取文件: {fname}")
continue
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (w_corners, h_corners), None)
if ret:
obj_points.append(objp)
corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria)
if [corners2]:
img_points.append(corners2)
else:
img_points.append(corners)
cv2.drawChessboardCorners(img, (w_corners, h_corners), corners, ret)
i += 1
new_size = (1280, 800)
resized_img = cv2.resize(img, new_size)
cv2.imshow('img', resized_img)
cv2.waitKey(150)
print(len(img_points))
cv2.destroyAllWindows()
if len(img_points) > 0:
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)
save_calibration_to_yaml('calibration_danmu.yaml', mtx, dist) #改!换成自己yaml文件想要的路径和名字
print("ret:", ret)
print("mtx:\n", mtx)
print("dist:\n", dist)
print("rvecs:\n", rvecs)
print("tvecs:\n", tvecs)
else:
print("没有检测到角点,无法进行相机标定。")
需要更改的地方已在代码中标出。
标定过程中会可视化画了角点连线的图,标定结果会保存到一个yaml文件中:
camera_matrix:
cols: 3
data:
- 538.5703097550794
- 0
- 638.7847675804244
- 0
- 542.6025156738463
- 419.4305002874007
- 0
- 0
- 1
dt: d
rows: 3
dist_coeff_left:
cols: 5
data:
- -0.1879080034212516
- 0.06154713313351551
- 4.605878792222752e-05
- 0.002483271337902348
- -0.003210439918482585
dt: d
rows: 1
其中camera_matrix为相机的内参矩阵,dist_coeff_left为畸变系数,接下来就可以使用这两个参数对图像进行矫正。
②矫正:
import cv2
import numpy as np
import glob
import os
import yaml
def load_camera_params_from_yaml(yaml_file):
with open(yaml_file, 'r') as f:
calib_data = yaml.safe_load(f)
camera_matrix = np.array(calib_data['camera_matrix']['data']).reshape(3, 3)
dist_coeffs = np.array(calib_data['dist_coeff_left']['data'])
return camera_matrix, dist_coeffs
def cam_calib_correct_img(distort_img_dir, crct_img_dir, cameraMatrix, distCoeffs):
imgs = glob.glob(os.path.join(distort_img_dir, "*.png"))
imgs.extend(glob.glob(os.path.join(distort_img_dir, "*.png")))
imgs.extend(glob.glob(os.path.join(distort_img_dir, "*.bmp")))
for img_ in imgs:
print("已读取待校正图像:", img_)
img = cv2.imread(img_)
(h1, w1) = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, (w1, h1), 1, (w1, h1))
dst = cv2.undistort(img, cameraMatrix, distCoeffs, None, newcameramtx)
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
resized_dst = cv2.resize(dst, (w1, h1))
imgname = os.path.basename(img_).split('.')[0]
rlt_path = os.path.join(crct_img_dir, imgname + "_crct.png")
cv2.imwrite(rlt_path, resized_dst)
print("已保存校正图像:", rlt_path)
if __name__ == "__main__":
distort_img_dir = 'daijiaozheng' # 待校正图像的路径
crct_img_dir = 'jiaozheng_result' # 保存校正图像的路径
yaml_file = 'calibration_danmu.yaml' # YAML文件路径
cameraMatrix, distCoeffs = load_camera_params_from_yaml(yaml_file)
cam_calib_correct_img(distort_img_dir, crct_img_dir, cameraMatrix, distCoeffs)
完成后可以在jiaozheng_result文件夹中看到矫正完的图像,现在给出两张照片进行对照:
明显看出畸变减小,可以注意看头顶的线条。