OpenCV 3.0:校准不符合预期
当我使用OpenCV 3.0 calibrateCamera时,我得到的结果是我不期望的。 这是我的algorithm:
- 加载30个图像点
- 加载30个相应的世界点(在这种情况下是共面的)
- 使用点来校准相机,只是为了不失真
- 不扭曲的图像点,但不要使用内在(共面的世界点,所以内部函数是狡猾的)
- 使用未失真的点find单应性,转换到世界点(可以这样做,因为它们都是共面的)
- 使用单应性和透视变换将未失真的点映射到世界空间
- 将原始世界点与映射点进行比较
我有点嘈杂,只有一小部分的形象。 从一个视图中共有30个共面点,所以我不能得到相机内在的东西,但应该能够得到畸变系数和单应性来创build一个平行的视angular。
如预期的那样,错误根据校准标志而变化。 但是,它与我所预期的相反。 如果我允许所有的variables进行调整,我预计错误会下降。 我并不是说我期待一个更好的模型; 我实际上期望过度适合,但是这应该仍然减less错误。 我所看到的是,我使用的variables越less,我的错误就越低。 最好的结果是直接的单应性。
我有两个怀疑的原因,但他们似乎不太可能,我希望听到一个毫不犹豫的答案,然后我播放他们。 我已经拿出代码来做我正在谈论的事情。 这有点长,但它包括加载点。
代码似乎没有错误; 我已经使用“更好”的点,它完美的作品。 我想强调的是,这里的解决scheme不能使用更好的点或更好的校准; 练习的要点是看各种校准模型是如何响应不同质量的校准数据的。
有任何想法吗?
添加
要清楚,我知道结果将是不好的,我期待这一点。 我也明白,我可能会学习不好的失真参数,当testing未用于训练模型的点时,会导致更差的结果。 我不明白的是,使用训练集作为testing集时,失真模型的误差如何。 也就是说,如果cv :: calibrateCamera应该select参数来减less所提供的训练集上的错误,但是它比K!,K2,… K6,P1刚select0时产生更多的错误,P2。 数据不好或不好,至less应该在训练集上做得更好。 在我可以说这些数据不适合这个模型之前,我必须确保我可以用尽可能多的数据做到最好,在这个阶段我不能这么说。
这里是一个示例图像
带有绿色针脚的点标记。 这显然只是一个testing图像。
这里是更多的例子的东西
在下面的图像是从上面的大一个裁剪。 该中心没有改变。 这就是当我不扭曲的情况下,从绿色的引脚手动标记的点,并允许K1(只有K1)从0:
之前
后
我会把它放在一个错误,但是当我使用一个更大的点覆盖更多的屏幕,即使从一架飞机,它的工作相当好。 这看起来很糟糕。 但是,这个错误并不像你想象的那样糟糕。
// Load image points std::vector<cv::Point2f> im_points; im_points.push_back(cv::Point2f(1206, 1454)); im_points.push_back(cv::Point2f(1245, 1443)); im_points.push_back(cv::Point2f(1284, 1429)); im_points.push_back(cv::Point2f(1315, 1456)); im_points.push_back(cv::Point2f(1352, 1443)); im_points.push_back(cv::Point2f(1383, 1431)); im_points.push_back(cv::Point2f(1431, 1458)); im_points.push_back(cv::Point2f(1463, 1445)); im_points.push_back(cv::Point2f(1489, 1432)); im_points.push_back(cv::Point2f(1550, 1461)); im_points.push_back(cv::Point2f(1574, 1447)); im_points.push_back(cv::Point2f(1597, 1434)); im_points.push_back(cv::Point2f(1673, 1463)); im_points.push_back(cv::Point2f(1691, 1449)); im_points.push_back(cv::Point2f(1708, 1436)); im_points.push_back(cv::Point2f(1798, 1464)); im_points.push_back(cv::Point2f(1809, 1451)); im_points.push_back(cv::Point2f(1819, 1438)); im_points.push_back(cv::Point2f(1925, 1467)); im_points.push_back(cv::Point2f(1929, 1454)); im_points.push_back(cv::Point2f(1935, 1440)); im_points.push_back(cv::Point2f(2054, 1470)); im_points.push_back(cv::Point2f(2052, 1456)); im_points.push_back(cv::Point2f(2051, 1443)); im_points.push_back(cv::Point2f(2182, 1474)); im_points.push_back(cv::Point2f(2171, 1459)); im_points.push_back(cv::Point2f(2164, 1446)); im_points.push_back(cv::Point2f(2306, 1474)); im_points.push_back(cv::Point2f(2292, 1462)); im_points.push_back(cv::Point2f(2278, 1449)); // Create corresponding world / object points std::vector<cv::Point3f> world_points; for (int i = 0; i < 30; i++) { world_points.push_back(cv::Point3f(5 * (i / 3), 4 * (i % 3), 0.0f)); } // Perform calibration // Flags are set out so they can be commented out and "freed" easily int calibration_flags = 0 | cv::CALIB_FIX_K1 | cv::CALIB_FIX_K2 | cv::CALIB_FIX_K3 | cv::CALIB_FIX_K4 | cv::CALIB_FIX_K5 | cv::CALIB_FIX_K6 | cv::CALIB_ZERO_TANGENT_DIST | 0; // Initialise matrix cv::Mat intrinsic_matrix = cv::Mat(3, 3, CV_64F); intrinsic_matrix.ptr<float>(0)[0] = 1; intrinsic_matrix.ptr<float>(1)[1] = 1; cv::Mat distortion_coeffs = cv::Mat::zeros(5, 1, CV_64F); // Rotation and translation vectors std::vector<cv::Mat> undistort_rvecs; std::vector<cv::Mat> undistort_tvecs; // Wrap in an outer vector for calibration std::vector<std::vector<cv::Point2f>>im_points_v(1, im_points); std::vector<std::vector<cv::Point3f>>w_points_v(1, world_points); // Calibrate; only 1 plane, so intrinsics can't be trusted cv::Size image_size(4000, 3000); calibrateCamera(w_points_v, im_points_v, image_size, intrinsic_matrix, distortion_coeffs, undistort_rvecs, undistort_tvecs, calibration_flags); // Undistort im_points std::vector<cv::Point2f> ud_points; cv::undistortPoints(im_points, ud_points, intrinsic_matrix, distortion_coeffs); // ud_points have been "unintrinsiced", but we don't know the intrinsics, so reverse that double fx = intrinsic_matrix.at<double>(0, 0); double fy = intrinsic_matrix.at<double>(1, 1); double cx = intrinsic_matrix.at<double>(0, 2); double cy = intrinsic_matrix.at<double>(1, 2); for (std::vector<cv::Point2f>::iterator iter = ud_points.begin(); iter != ud_points.end(); iter++) { iter->x = iter->x * fx + cx; iter->y = iter->y * fy + cy; } // Find a homography mapping the undistorted points to the known world points, ground plane cv::Mat homography = cv::findHomography(ud_points, world_points); // Transform the undistorted image points to the world points (2d only, but z is constant) std::vector<cv::Point2f> estimated_world_points; std::cout << "homography" << homography << std::endl; cv::perspectiveTransform(ud_points, estimated_world_points, homography); // Work out error double sum_sq_error = 0; for (int i = 0; i < 30; i++) { double err_x = estimated_world_points.at(i).x - world_points.at(i).x; double err_y = estimated_world_points.at(i).y - world_points.at(i).y; sum_sq_error += err_x*err_x + err_y*err_y; } std::cout << "Sum squared error is: " << sum_sq_error << std::endl;
我将随机抽样30个input点,并计算每种情况下的单应性以及估计的单应性下的误差,RANSACscheme,并validation误差水平和单应性参数之间的一致性,这可能仅仅是全局优化处理。 我知道这似乎是不必要的,但这只是一个健全的检查程序是多么敏感的input(噪音水平,位置)
另外,修复大多数variables可以让你获得最less的错误似乎是合乎逻辑的,因为最小化过程中的自由度较小。 我会尝试修复不同的build立另一个共识。 至less这会让你知道哪些variables对input的噪声水平最为敏感。
有希望的是,这样一小部分图像将接近图像中心,因为它将导致最less量的镜头失真。 在你的情况下是否可能使用不同的失真模型? 一种更可行的方法是在给定模式相对于图像中心的位置的情况下调整失真参数的数量。
不知道algorithm的约束条件,我可能会误解这个问题,这也是一个select,在这种情况下,我可以回滚。
我想将此作为评论,但我没有足够的观点。
OpenCV在校准相机内部运行Levenberg-Marquardtalgorithm。
https://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm/
这algortihm工作正常的问题,最小的一个。 在单幅图像的情况下,相互靠近的点和许多尺寸问题(n =系数个数)algorithm可能是不稳定的(特别是在相机matrix的初始猜测错误的情况下),algorithm的收敛性如下:
https://na.math.kit.edu/download/papers/levenberg.pdf/
正如你写的,错误取决于校准标志 – 标志数量改变了要优化的问题的尺寸。
摄像机标定也会计算摄像机的姿态,这在校准matrix错误的模型中会很糟糕。
作为解决scheme,我build议改变方法。 你不需要计算相机matrix,并在这一步构成。 既然你知道,这些点位于一个平面上,你可以使用3d-2D平面投影方程来确定点的分布types。 按照分布我的意思是,所有的点将位于某种梯形上。
然后,您可以在testing图像上使用不同的distCoeffs的cv :: undistort,并计算图像点分布和分布错误。
最后一步是将这个步骤作为一个优化algorithm的目标函数,对失真系数进行优化。
这不是最简单的解决scheme,但我希望它能帮助你。