如果没有这样的解决方案,我会自己计算,不想给你带来太多麻烦。 解决方案:(来自您的答案)
对我来说,这是Kabsch算法;
基本信息:http://en.wikipedia.org/wiki/Kabsch_algorithm
通用解决方案:http://nghiaho.com/?page_id=671 仍未解决: 我还需要比例尺。SVD中的比例值对我来说不可理解;当我需要所有轴的比例约为1-4(由我估计),SVD比例大约是[2000, 200, 20],完全没有帮助。
既然您已经在使用Kabsch算法,那么可以看一下Umeyama的论文,它将其扩展以获得比例。您需要做的就是获取点的标准差并计算比例:
(1/sigma^2)*trace(D*S)
其中D
是SVD分解中的对角矩阵,用于旋转估计,S
是单位矩阵或对角线为[1 1 -1]
的对角矩阵,具体取决于UV
行列式的符号(Kabsch用于将反射矫正为正确的旋转)。因此,如果您有[2000, 200, 20]
,请将最后一个元素乘以+-1(取决于UV
的行列式符号),然后求和并除以点的标准差以获得比例。
您可以重复使用以下代码,该代码使用Eigen库:
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vector3d_U; // microsoft's 32-bit compiler can't put Eigen::Vector3d inside a std::vector. for other compilers or for 64-bit, feel free to replace this by Eigen::Vector3d
/**
* @brief rigidly aligns two sets of poses
*
* This calculates such a relative pose <tt>R, t</tt>, such that:
*
* @code
* _TyVector v_pose = R * r_vertices[i] + t;
* double f_error = (r_tar_vertices[i] - v_pose).squaredNorm();
* @endcode
*
* The sum of squared errors in <tt>f_error</tt> for each <tt>i</tt> is minimized.
*
* @param[in] r_vertices is a set of vertices to be aligned
* @param[in] r_tar_vertices is a set of vertices to align to
*
* @return Returns a relative pose that rigidly aligns the two given sets of poses.
*
* @note This requires the two sets of poses to have the corresponding vertices stored under the same index.
*/
static std::pair<Eigen::Matrix3d, Eigen::Vector3d> t_Align_Points(
const std::vector<Vector3d_U> &r_vertices, const std::vector<Vector3d_U> &r_tar_vertices)
{
_ASSERTE(r_tar_vertices.size() == r_vertices.size());
const size_t n = r_vertices.size();
Eigen::Vector3d v_center_tar3 = Eigen::Vector3d::Zero(), v_center3 = Eigen::Vector3d::Zero();
for(size_t i = 0; i < n; ++ i) {
v_center_tar3 += r_tar_vertices[i];
v_center3 += r_vertices[i];
}
v_center_tar3 /= double(n);
v_center3 /= double(n);
// calculate centers of positions, potentially extend to 3D
double f_sd2_tar = 0, f_sd2 = 0; // only one of those is really needed
Eigen::Matrix3d t_cov = Eigen::Matrix3d::Zero();
for(size_t i = 0; i < n; ++ i) {
Eigen::Vector3d v_vert_i_tar = r_tar_vertices[i] - v_center_tar3;
Eigen::Vector3d v_vert_i = r_vertices[i] - v_center3;
// get both vertices
f_sd2 += v_vert_i.squaredNorm();
f_sd2_tar += v_vert_i_tar.squaredNorm();
// accumulate squared standard deviation (only one of those is really needed)
t_cov.noalias() += v_vert_i * v_vert_i_tar.transpose();
// accumulate covariance
}
// calculate the covariance matrix
Eigen::JacobiSVD<Eigen::Matrix3d> svd(t_cov, Eigen::ComputeFullU | Eigen::ComputeFullV);
// calculate the SVD
Eigen::Matrix3d R = svd.matrixV() * svd.matrixU().transpose();
// compute the rotation
double f_det = R.determinant();
Eigen::Vector3d e(1, 1, (f_det < 0)? -1 : 1);
// calculate determinant of V*U^T to disambiguate rotation sign
if(f_det < 0)
R.noalias() = svd.matrixV() * e.asDiagonal() * svd.matrixU().transpose();
// recompute the rotation part if the determinant was negative
R = Eigen::Quaterniond(R).normalized().toRotationMatrix();
// renormalize the rotation (not needed but gives slightly more orthogonal transformations)
double f_scale = svd.singularValues().dot(e) / f_sd2_tar;
double f_inv_scale = svd.singularValues().dot(e) / f_sd2; // only one of those is needed
// calculate the scale
R *= f_inv_scale;
// apply scale
Eigen::Vector3d t = v_center_tar3 - (R * v_center3); // R needs to contain scale here, otherwise the translation is wrong
// want to align center with ground truth
return std::make_pair(R, t); // or put it in a single 4x4 matrix if you like
}
先将两组点进行平移,使它们的质心与坐标系原点重合。平移向量就是这些质心之间的差。
现在我们有两组表示为矩阵P和Q的坐标。一组点可以通过应用某些线性操作(旋转和缩放)从另一组点中获得。该操作由3x3矩阵X表示:
P * X = Q
要找到适当的缩放/旋转,我们只需要解决这个矩阵方程,找到X,然后将其分解成几个矩阵,每个矩阵代表一些缩放或旋转。
解决它的一个简单(但可能不稳定)方法是将方程的两个部分乘以转置矩阵P(以摆脱非方形矩阵),然后将方程的两个部分乘以反转置矩阵PT * P:
PT * P * X = PT * Q
X = (PT * P)-1 * PT * Q
将奇异值分解应用于矩阵X,得到两个旋转矩阵和一个带有比例因子的矩阵:
X = U * S * V
这里的S是一个带有比例因子的对角矩阵(每个坐标轴上都有一个比例因子),U和V是旋转矩阵,一个适当地旋转点使它们可以沿坐标轴缩放,另一个再次旋转它们以将其方向与第二组点对齐。
示例(为简单起见使用2D点):
P = 1 2 Q = 7.5391 4.3455
2 3 12.9796 5.8897
-2 1 -4.5847 5.3159
-1 -6 -15.9340 -15.5511
X = 3.3417 -1.2573
2.0987 2.8014
经过SVD分解后:
U = -0.7317 -0.6816
-0.6816 0.7317
S = 4 0
0 3
V = -0.9689 -0.2474
-0.2474 0.9689
如果您的点在所有方向上均匀缩放(我也无法理解SVD的比例矩阵),则可以在不使用SVD的情况下推断比例。以下是我解决同样问题的方法:
测量每个点到点云中其他点的距离,以获得距离的2D表格,其中(i,j)处的条目为norm(point_i-point_j)。对于另一个点云,做同样的事情,这样您就会得到两个表格--一个用于原始点,另一个用于重建点。
将一个表格中的所有值除以另一个表格中相应的值。因为点相互对应,距离也是如此。理想情况下,结果表格的所有值都相等,这就是比例。
除法的中位数值应该非常接近您要查找的比例。平均值也很接近,但我选择中位数来排除异常值。
现在,您可以使用比例值来缩放所有重建点,然后继续估计旋转。
提示:如果点云中的点太多,无法计算它们之间的距离,那么选择一个较小的子集来计算距离也是可行的,只要这个子集对于两个点云都是相同的即可。理想情况下,如果没有测量误差,例如一个点云直接通过旋转得到另一个点云,那么只需要一个距离对就足够了。您也可以使用由BaoweiLin提出的ScaleRatio ICP。
代码可以在Github上找到。