SVD(奇异值分解)学习笔记

奇异值分解物理意义

从几何上,SVD分解就是通过M将一组正交基映射到另一组正交基。
SVD(奇异值分解)学习笔记_第1张图片
记映射后的向量Mv1为u1,Mv2为u2,Mv1的模为σ1,Mv2的模为σ2。
SVD(奇异值分解)学习笔记_第2张图片
接下来我们就可以推导了:
SVD(奇异值分解)学习笔记_第3张图片


(类似于特征值特征向量的表达式)
SVD(奇异值分解)学习笔记_第4张图片
这时候λ就被称为特征向量v对应的特征值,一个矩阵的一组特征向量是一组正交向量。特征值分解是将一个矩阵分解成下面的形式:
在这里插入图片描述
其中Q是这个矩阵A的特征向量组成的矩阵,Σ是一个对角阵,每一个对角线上的元素就是一个特征值。我这里引用了一些参考文献中的内容来说明一下。首先,要明确的是,一个矩阵其实就是一个线性变换,因为一个矩阵乘以一个向量后得到的向量,其实就相当于将这个向量进行了线性变换。比如说下面的一个矩阵:


在v1和v2确定的二维平面中,任意一点x可以表示为:
SVD(奇异值分解)学习笔记_第5张图片在《利用SVD进行推荐(1)矩阵相乘的本质》中我们讲过,小括号里的点积就是x在v1和v1坐标轴上的投影值(坐标)。我们对这个平面中任意一点x左乘矩阵M进行变换,来看看结果:

SVD(奇异值分解)学习笔记_第6张图片
向量点积表示为矩阵乘法就是:
SVD(奇异值分解)学习笔记_第7张图片
所以变换结果可以进一步推演为:
SVD(奇异值分解)学习笔记_第8张图片
我们得到了M有关u,v,σ的表达式。将表达式转为矩阵表达形式,即为:
在这里插入图片描述
其中U中的每一列向量ui为映射后的一个单位基向量,V中的每一个列向量vj为原先被映射的单位基向量。

代码实现

来自高翔大神《slam十四讲》代码

void pose_estimation_3d3d (
    const vector<cv::Point3f>& pts1,
    const vector<cv::Point3f>& pts2)
{
    //求出两组关键点的质心
    cv::Point3f p1, p2;     // center of mass 
    int N = pts1.size();
    for ( int i=0; i<N; i++ )
    {
        p1 += pts1[i];
        p2 += pts2[i];
    }
    p1 = cv::Point3f( cv::Vec3f(p1) /  N);
    p2 = cv::Point3f( cv::Vec3f(p2) / N);
 
    // 求出各个点的去质心坐标
    vector<cv::Point3f>     q1 ( N ), q2 ( N ); // remove the center
    for ( int i=0; i<N; i++ )
    {
        q1[i] = pts1[i] - p1;
        q2[i] = pts2[i] - p2;
    }
 
    // compute  q1*q2^T -------求出w(w=所有q1*q2^T之和)
    Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
    for ( int i=0; i<N; i++ )
    {
        W += Eigen::Vector3d ( q1[i].x, q1[i].y, q1[i].z ) * Eigen::Vector3d ( q2[i].x, q2[i].y, q2[i].z ).transpose();
    }
    //cout<<"W="<
 
 
    // SVD on W -------把W进行SVD分解,求出U和V
    Eigen::JacobiSVD<Eigen::Matrix3d> svd ( W, Eigen::ComputeFullU|Eigen::ComputeFullV );
    Eigen::Matrix3d U = svd.matrixU();
    Eigen::Matrix3d V = svd.matrixV();
    //cout<<"U="<
   // cout<<"V="<
 
    // 求出R,t
    Eigen::Matrix3d R = U* ( V.transpose() );
    Eigen::Vector3d t= Eigen::Vector3d ( p1.x, p1.y, p1.z ) - R * Eigen::Vector3d ( p2.x, p2.y, p2.z );
    Matrix4d pre_transform_matrix=Matrix4d::Zero();
    
    for(int i=0;i<3;i++)
    {
      for(int j=0;j<3;j++)
      {
	pre_transform_matrix(i,j)=R(i,j);
      }
    }
    for(int j=0;j<3;j++)
    {
      pre_transform_matrix(j,3)=t(j);
    }
    for(int j=0;j<3;j++)
    {
      pre_transform_matrix(3,j)=0;
    }
    pre_transform_matrix(3,3)=1;
    print4x4Matrix("pre_transform_matrix",pre_transform_matrix);
    final_tranfirm_matrix=pre_transform_matrix;
   // pcl::visualization::PCLVisualizer res_viewer("result");
    pcl::transformPointCloud(*cloud1,*cloud1,pre_transform_matrix);
    *cloud_mix=*cloud+*cloud1;
   // res_viewer.addPointCloud(cloud_mix,"res");
   viewer->removePointCloud("clicked_points");
   viewer->removePointCloud("cloud");
   viewer->addPointCloud(cloud_mix,"cloud");
   viewer->registerAreaPickingCallback(ppa_callback,(void*)&cloud_mix);
}

参考资料:
奇异值的物理意义
奇异值分解SVD eigen opencv实现
高翔《slam十四讲》

你可能感兴趣的:(pcl)