<think>我们使用OpenCV库来实现鱼眼镜头的棋盘格标定。鱼眼镜头标定与普通镜头标定不同,需要使用OpenCV中的fisheye模块。
步骤:
1. 准备棋盘格图像,棋盘格必须是已知的尺寸(例如,内部角点数为9x6)。
2. 检测每张图像中的棋盘格角点。
3. 使用检测到的角点通过cv::fisheye::calibrate函数进行标定,得到相机内参和畸变系数。
4. 评估标定结果,例如重投影误差。
注意:鱼眼镜头的畸变模型与普通镜头不同,它使用四个畸变系数(k1,k2,k3,k4)。
以下代码将实现鱼眼棋盘格标定:
假设棋盘格的内部角点数为(width-1)x(height-1),每个方格的实际尺寸为square_size(单位自定,例如毫米)。
代码步骤:
1. 读取一组棋盘格图像。
2. 为每幅图像查找角点。
3. 设置世界坐标系的坐标点(3D点),这些点是在棋盘格平面上的坐标(z=0)。
4. 调用cv::fisheye::calibrate进行标定。
5. 计算重投影误差以评估标定质量。
6. 保存标定结果(相机矩阵和畸变系数)。
我们将使用OpenCV的fisheye模块,因此请确保OpenCV版本在3.0以上,并且编译时包含了opencv_contrib中的fisheye模块(但实际上,fisheye模块在OpenCV 3.x以后已经包含在main仓库中)。
代码实现:
</think>下面我将详细讲解如何使用C++和OpenCV实现鱼眼镜头的棋盘格标定。鱼眼镜头标定与普通镜头标定不同,需要使用特定的鱼眼畸变模型(4个畸变系数)和专用函数。
### 完整代码实现
```cpp
#include <opencv2/opencv.hpp>
#include <opencv2/calib3d.hpp>
#include <vector>
#include <iostream>
#include <fstream>
using namespace cv;
using namespace std;
int main() {
// 1. 参数配置
const string imagePath = "./calib_imgs/"; // 标定图像路径
const Size boardSize(9, 6); // 棋盘格内部角点数 (width x height)
const float squareSize = 0.025f; // 棋盘格方格实际大小(单位:米)
// 2. 准备对象点和图像点
vector<vector<Point3f>> objectPoints; // 世界坐标系中的3D点
vector<vector<Point2f>> imagePoints; // 图像坐标系中的2D点
// 生成棋盘格的世界坐标 (z=0)
vector<Point3f> obj;
for (int i = 0; i < boardSize.height; i++)
for (int j = ͏0; j < boardSize.width; j++)
obj.push_back(Point3f(j*squareSize, i*squareSize, 0));
// 3. 处理所有标定图像
vector<String> imageNames;
glob(imagePath + "*.jpg", imageNames); // 获取所有JPG图像
Size imageSize;
for (size_t i = 0; i < imageNames.size(); i++) {
Mat img = imread(imageNames[i], IMREAD_GRAYSCALE);
if (img.empty()) continue;
imageSize = img.size();
vector<Point2f> corners;
// 查找棋盘格角点
bool found = findChessboardCorners(img, boardSize, corners,
CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE);
if (found) {
// 亚像素级角点精确化
cornerSubPix(img, corners, Size(11, 11), Size(-1, -1),
TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.01));
imagePoints.push_back(corners);
objectPoints.push_back(obj);
// 可视化角点检测结果(可选)
Mat colorImg;
cvtColor(img, colorImg, COLOR_GRAY2BGR);
drawChessboardCorners(colorImg, boardSize, corners, found);
imshow("Corners", colorImg);
waitKey(500);
}
}
// 4. 鱼眼镜头标定
Mat K = Mat::eye(3, 3, CV_64F); // 内参矩阵
Mat D; // 畸变系数向量 (k1, k2, k3, k4)
vector<Mat> rvecs, tvecs; // 每张图像的旋转和平移向量
int flags = fisheye::CALIB_RECOMPUTE_EXTRINSIC |
fisheye::CALIB_CHECK_COND |
fisheye::CALIB_FIX_SKEW;
double rms = fisheye::calibrate(
objectPoints, imagePoints, imageSize,
K, D, rvecs, tvecs, flags
);
// 5. 输出标定结果
cout << "标定误差RMS: " << rms << endl;
cout << "内参矩阵K:\n" << K << endl;
cout << "畸变系数D: " << D.t() << endl;
// 6. 保存标定结果
FileStorage fs("camera_params.yml", FileStorage::WRITE);
fs << "K" << K;
fs << "D" << D;
fs << "rms" << rms;
fs.release();
// 7. 标定结果验证(可选)
Mat testImg = imread(imageNames[0]);
Mat undistorted;
fisheye::undistortImage(testImg, undistorted, K, D);
imshow("Original", testImg);
imshow("Undistorted", undistorted);
waitKey();
return 0;
}
```
### 代码关键步骤解释
1. **参数配置**:
- `boardSize`: 棋盘格内部角点数(如9x6表示每行10条边,每列7条边)
- `squareSize`: 棋盘格实际物理尺寸(单位:米)
- `imagePath`: 存储标定图像的目录路径
2. **世界坐标准备**:
- 生成棋盘格角点的3D世界坐标(Z=0平面)
- 示例:`obj.push_back(Point3f(j*0.025, i*0.025, 0))` 生成2.5cm间隔的点阵
3. **角点检测处理**:
- `findChessboardCorners()` 检测粗略角点位置
- `cornerSubPix()` 进行亚像素级精确化(提高精度)
- 可视化角点检测结果用于验证
4. **鱼眼标定核心**:
- `fisheye::calibrate()` 使用专用的鱼眼标定函数
- 标志位说明:
- `CALIB_RECOMPUTE_EXTRINSIC`: 每次迭代重新计算外参
- `CALIB_CHECK_COND`: 检查矩阵条件数
- `CALIB_FIX_SKEW`: 假设图像无倾斜(简化模型)
5. **结果输出**:
- RMS误差:反映重投影误差(理想值<0.5像素)
- 内参矩阵K:包含焦距(fx, fy)和主点(cx, cy)
- 畸变系数D:4个鱼眼畸变参数(k1, k2, k3, k4)
6. **验证与应用**:
- `fisheye::undistortImage()` 去除图像畸变
- 保存标定参数供后续使用
### 使用注意事项
1. **棋盘格要求**:
- 使用高对比度棋盘格(建议黑白打印)
- 棋盘格需覆盖图像的不同区域(中心/边缘)
- 建议15-20张不同角度/位置的图像
2. **拍摄技巧**:
- 棋盘格需部分出现在图像边缘(捕捉畸变)
- 包含棋盘格的倾斜/旋转视图
- 避免强反光和模糊
3. **标定结果评估**:
- RMS < 0.5像素:优秀
- RMS 0.5-1.0像素:良好
- RMS > 1.0像素:建议重新标定
4. **环境要求**:
- OpenCV 3.0以上版本(包含`fisheye`模块)
- 编译时添加`opencv_calib3d`和`opencv_imgproc`