python和c++的代码对比验证,结果相同;
#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:cong
@time: 2025/01/07 13:45:46
@theme:相机标定的目标是找到相机坐标系(由相机的位置和姿态决定)与世界坐标系(标定板的坐标系)之间的变换关系。改代码只输出相机内参,若需要外参,用cv2.solvePnP;
"""
import cv2
import numpy as np
import glob
# 棋盘格参数 (内角点数量)
CHESSBOARD_SIZE = (10, 7)
SQUARE_SIZE = 20 # 每个格子的真实边长 (单位: 米)
# 为棋盘格的角点在三维空间中构建真实世界坐标系,objp 作为世界坐标系中的参考点,用于计算相机内参和畸变系数
objp = np.zeros((CHESSBOARD_SIZE[0] * CHESSBOARD_SIZE[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:CHESSBOARD_SIZE[0], 0:CHESSBOARD_SIZE[1]].T.reshape(-1, 2)
objp *= SQUARE_SIZE # 乘以每格边长,得到真实世界坐标
objpoints = [] # 3D点
imgpoints = [] # 2D点
# 读取保存的棋盘格图像
images = glob.glob('./calibration_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 检测角点
ret, corners = cv2.findChessboardCorners(gray, CHESSBOARD_SIZE, None)
if ret:
objpoints.append(objp)
# cv2.cornerSubPix 是 OpenCV 中的一个函数,用于提高角点检测的精度。它通过亚像素级别的优化来精确定位角点位置。
# 通常,在检测到角点后,使用该函数可以进一步优化角点的位置
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
imgpoints.append(corners2)
# 显示检测结果
cv2.drawChessboardCorners(img, CHESSBOARD_SIZE, corners2, ret)
cv2.imshow('Corners', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 标定相机,会输出和图片数量一样个数的R和T
(rms, mtx, dist, rvecs, tvecs) = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# 打印标定结果
print("相机内参矩阵:\n", mtx)
print("畸变系数:\n", dist)
print("RMS error:", rms)
# print("旋转向量 (Rotation Vectors):\n", rvecs)
# print("平移向量 (Translation Vectors):\n", tvecs)
# RMS (cv2.calibrateCamera 的返回值): 是所有角点的均方根误差(平方和的均值再开平方)。
# total_error 的计算: 是所有图片的平均重投影误差,计算方式不同,但结果通常是相近的。
# 平均重投影误差通常应在 0.1 ~ 1 像素之间,越小越好。
# === 计算标定误差 (RMS) ===
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 / len(objpoints))
# 保存标定参数
np.savez("calibration_data.npz", mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
c++代码如下:
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
int main() {
// 棋盘格尺寸:内角点数 (10x7)
Size boardSize(10, 7);
float squareSize = 20.f; // 每个格子的大小,单位可以是毫米或厘米,取决于实际标定板的尺寸
// 生成棋盘格的 3D 世界坐标
vector<Point3f> objp;
for (int i = 0; i < boardSize.height; i++) {
for (int j = 0; j < boardSize.width; j++) {
objp.push_back(Point3f(j * squareSize, i * squareSize, 0)); // 在Z轴为零的平面上
}
}
// 用于存储每张图像对应的世界坐标和图像坐标
vector<vector<Point3f>> objpoints;
vector<vector<Point2f>> imgpoints;
// 读取指定目录下的所有图像
vector<String> images;
glob("./calibration_images/*.jpg", images); // 根据需要修改文件路径
if (images.empty()) {
cout << "没有找到图像文件!" << endl;
return -1;
}
// 遍历所有图像文件
for (size_t i = 0; i < images.size(); i++) {
Mat img = imread(images[i]);
if (img.empty()) {
continue;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY); // 转为灰度图
vector<Point2f> corners;
bool found = findChessboardCorners(gray, boardSize, corners); // 检测角点
if (found) {
// 进一步优化角点位置
cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1),
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.001));
objpoints.push_back(objp); // 添加3D点
imgpoints.push_back(corners); // 添加2D点
// 显示检测结果
drawChessboardCorners(img, boardSize, corners, found);
imshow("Chessboard", img);
waitKey(500); // 等待0.5秒显示每张图片
}
}
destroyAllWindows(); // 关闭所有窗口
// 进行相机标定
Mat cameraMatrix = Mat::eye(3, 3, CV_64F); // 相机内参矩阵 (3x3单位矩阵)
Mat distCoeffs = Mat::zeros(1, 5, CV_64F); // 畸变系数 (默认无畸变)
vector<Mat> rvecs, tvecs; // 旋转向量和平移向量
double rms = calibrateCamera(objpoints, imgpoints, cv::Size(640,480), cameraMatrix, distCoeffs, rvecs, tvecs);
cout << "相机标定完成!" << endl;
cout << "RMS误差: " << rms << endl;
cout << "相机矩阵: " << cameraMatrix << endl;
cout << "畸变系数: " << distCoeffs << endl;
// 保存标定结果到文件
FileStorage fs("calibration_data.xml", FileStorage::WRITE);
fs << "cameraMatrix" << cameraMatrix;
fs << "distCoeffs" << distCoeffs;
fs.release();
return 0;
}