给定与平面(地面)相对应的相机透视图中的一组 3D 点,是否有任何快速有效的方法来找到平面相对于相机平面的方向?还是只能通过在点云上运行更重的“表面匹配”算法来实现?
我尝试使用estimateAffine3D
and findHomography
,但我的主要限制是我没有表面平面上的点坐标 - 我只能从深度图像中选择一组点,因此必须从一组 3D 点中工作相机框架。
我写了一个简单的几何方法,它需要几个点并根据深度测量计算垂直和水平角度,但我担心这既不是很稳健也不是很精确。
编辑:按照@Micka 的建议,我尝试将这些点拟合到相机框架上的二维平面,具有以下功能:
#include <opencv2/opencv.hpp>
//------------------------------------------------------------------------------
/// @brief Fits a set of 3D points to a 2D plane, by solving a system of linear equations of type aX + bY + cZ + d = 0
///
/// @param[in] points The points
///
/// @return 4x1 Mat with plane equations coefficients [a, b, c, d]
///
cv::Mat fitPlane(const std::vector< cv::Point3d >& points) {
// plane equation: aX + bY + cZ + d = 0
// assuming c=-1 -> aX + bY + d = z
cv::Mat xys = cv::Mat::ones(points.size(), 3, CV_64FC1);
cv::Mat zs = cv::Mat::ones(points.size(), 1, CV_64FC1);
// populate left and right hand matrices
for (int idx = 0; idx < points.size(); idx++) {
xys.at< double >(idx, 0) = points[idx].x;
xys.at< double >(idx, 1) = points[idx].y;
zs.at< double >(idx, 0) = points[idx].z;
}
// coeff mat
cv::Mat coeff(3, 1, CV_64FC1);
// problem is now xys * coeff = zs
// solving using SVD should output coeff
cv::SVD svd(xys);
svd.backSubst(zs, coeff);
// alternative approach -> requires mat with 3D coordinates & additional col
// solves xyzs * coeff = 0
// cv::SVD::solveZ(xyzs, coeff); // @note: data type must be double (CV_64FC1)
// check result w/ input coordinates (plane equation should output null or very small values)
double a = coeff.at< double >(0);
double b = coeff.at< double >(1);
double d = coeff.at< double >(2);
for (auto& point : points) {
std::cout << a * point.x + b * point.y + d - point.z << std::endl;
}
return coeff;
}
为简单起见,假设相机已正确校准并且 3D 重建是正确的 - 我之前已经验证过,因此超出了本问题的范围。我使用鼠标选择深度/颜色帧对上的点,重建 3D 坐标并将它们传递给上面的函数。
我还尝试了其他方法cv::SVD::solveZ()
,例如反转xyz
和cv::invert()
,cv::solve()
但它总是以非常小的值或关于矩阵大小和/或类型的运行时错误结束。