opencv calibrateCamera function yielding bad results
我正在尝试让 opencv 相机校准工作,但无法让它输出有效数据。我有一个想要校准的未校准相机,但为了测试我的代码,我使用的是 Azure Kinect 相机(彩色相机),因为 SDK 为其提供了正确的内在函数,我可以验证它们。我从稍微不同的angular收集了 30 张棋盘图像,我知道这应该足够了,并运行校准功能,但无论我传入什么标志,我都会得到 fx 和 fy 的值,它们与正确的 fx 完全不同和 fy,以及完全不同的失真系数。难道我做错了什么?我需要更多或更好的数据吗?
可以在此处找到我正在使用的图片示例:https://www.dropbox.com/sh/9pa94uedoe5mlxz/AABisSvgWwBT-bY65lfzp2N3a?dl=0
将它们保存在 c:\\\\calibration_test 以运行下面的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | #include <filesystem> #include <iostream> #include <opencv2/core.hpp> #include <opencv2/calib3d/calib3d.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/imgcodecs.hpp> using namespace std; namespace fs = experimental::filesystem; static bool extractCorners(cv::Mat colorImage, vector<cv::Point3f>& corners3d, vector<cv::Point2f>& corners) { // Each square is 20x20mm const float kSquareSize = 0.020f; const cv::Size boardSize(7, 9); const cv::Point3f kCenterOffset((float)(boardSize.width - 1) * kSquareSize, (float)(boardSize.height - 1) * kSquareSize, 0.f); cv::Mat image; cv::cvtColor(colorImage, image, cv::COLOR_BGRA2GRAY); int chessBoardFlags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE; if (!cv::findChessboardCorners(image, boardSize, corners, chessBoardFlags)) { return false; } cv::cornerSubPix(image, corners, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1)); // Construct the corners for (int i = 0; i < boardSize.height; ++i) for (int j = 0; j < boardSize.width; ++j) corners3d.push_back(cv::Point3f(j * kSquareSize, i * kSquareSize, 0) - kCenterOffset); return true; } int main() { vector<cv::Mat> frames; for (const auto& p : fs::directory_iterator("c:\\\\calibration_test\")) { frames.push_back(cv::imread(p.path().string())); } int numFrames = (int)frames.size(); vector<vector<cv::Point2f>> corners(numFrames); vector<vector<cv::Point3f>> corners3d(numFrames); int framesWithCorners = 0; for (int i = 0; i < numFrames; ++i) { if (extractCorners(frames[i], corners3d[framesWithCorners], corners[framesWithCorners])) { ++framesWithCorners; } } numFrames = framesWithCorners; corners.resize(numFrames); corners3d.resize(numFrames); // Camera intrinsics come from the Azure Kinect API cv::Matx33d cameraMatrix( 914.111755f, 0.f, 960.887390f, 0.f, 913.880615f, 551.566528f, 0.f, 0.f, 1.f); vector<float> distCoeffs = { 0.576340079f, -2.71203661f, 0.000563957903f, -0.000239689150f, 1.54344523f, 0.454746544f, -2.53860712f, 1.47272563f }; cv::Size imageSize = frames[0].size(); vector<cv::Point3d> rotations; vector<cv::Point3d> translations; int flags = cv::CALIB_USE_INTRINSIC_GUESS | cv::CALIB_FIX_PRINCIPAL_POINT | cv::CALIB_RATIONAL_MODEL; double result = cv::calibrateCamera(corners3d, corners, imageSize, cameraMatrix, distCoeffs, rotations, translations, flags); // After this call, cameraMatrix has different values for fx and fy, and WILDLY different distortion coefficients. cout <<"fx:" << cameraMatrix(0, 0) << endl; cout <<"fy:" << cameraMatrix(1, 1) << endl; cout <<"cx:" << cameraMatrix(0, 2) << endl; cout <<"cy:" << cameraMatrix(1, 2) << endl; for (size_t i = 0; i < distCoeffs.size(); ++i) { cout <<"d" << i <<":" << distCoeffs[i] << endl; } return 0; } |
一些示例输出是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | fx: 913.143 fy: 917.965 cx: 960.887 cy: 551.567 d0: 0.327596 d1: -73.1837 d2: -0.00125972 d3: 0.002805 d4: -7.93086 d5: 0.295437 d6: -73.481 d7: -3.25043 d8: 0 d9: 0 d10: 0 d11: 0 d12: 0 d13: 0 |
知道我做错了什么吗?
额外问题:为什么我得到 14 个失真系数而不是 8 个?如果我不使用 CALIB_RATIONAL_MODEL,那么我只会得到 5(三个径向和两个切向)。
您需要从相机的整个视野??中拍摄图像,才能正确捕捉镜头畸变特性。您提供的图像仅在一个位置显示棋盘,略微倾斜。
理想情况下,您应该让棋盘图像均匀分布在图像平面的 x 和 y 轴上,直到图像的边缘。确保电路板周围有足够的白边始终可见,但为了检测稳健性。
您还应该尝试在棋盘距离相机更近和更远的位置拍摄图像,而不仅仅是一个均匀的距离。另一方面,您提供的不同angular看起来不错。
您可以在此答案中找到如何确保良好校准结果的详尽指南:如何验证网络摄像头校准的正确性?
将您的相机矩阵与来自 Azure Kinect API 的相机矩阵进行比较,它看起来还不错。主点非常到位,焦距在合理范围内。如果您使用我的提示和我提供的 SO 答案提高输入质量,结果应该更接近。按距离比较失真系数集并不能很好地工作,误差函数不是凸的,所以你可以有很多局部最小值产生相对较好的结果,但它们远离产生最佳结果的全局最小值.如果这个解释对你有意义。
关于你的额外问题:我只看到在你返回的输出中填写了 8 个值,其余为 0,所以没有任何影响。我不确定输出是否与该函数不同。