天天看点

理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻

论文参见https://icwww.epfl.ch/~lepetit/papers/lepetit_ijcv08.pdf

EPNP要理解透彻还是好难啊…… 现在还是只理解了一个大概。

中心思想: 三维空间中一个点可以由其他不共面的四个点的加权和表示

理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻

epnp最终求解:一个旋转矩阵,一个平移矩阵(可转成四元数,四元数可再转成欧拉角)

epnp算法原理

编辑公式太耗时间了,手写。

理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻
理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻
理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻
理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻
理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻

opencv源码流程

opencv calib3d 模块中有好几种pnp的求解

理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻
理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻
理解EPnP算法, 以及使用eigen3重写opencv3 epnp.cpp epnp还是没有理解透彻

使用eigen3 重写,主要是替换数据结构和矩阵相关的接口

基于https://github.com/slowplayer/EPNP-Eigen

不过感觉原作者没有写完?

我改的版本: 使用了eigen3.2.10

#ifndef PNP_SOLVER_H
#define PNP_SOLVER_H

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <Eigen/SVD>
#include <Eigen/QR>
#include <vector>
class PnpSolver
{
public:
  PnpSolver();
  ~PnpSolver(){};
 //设置相机内参
  void set_internal_parameters(double cx,double cy,double fx,double fy);
  void set_maximum_number_of_correspondences(int n);
  void reset_number_of_correspondences();
 //3d点和2d点
  void add_correspondence(Eigen::Vector3d point3d,Eigen::Vector2d point2d);

  double compute_pose(Eigen::Matrix3d &R,Eigen::Vector3d &T);
//此处的投影是为了可视化
  void projectPoints(std::vector<Eigen::Vector3d> &src,std::vector<Eigen::Vector2d> &dst,Eigen::Matrix3d &R,Eigen::Vector3d &T);

  double cx,cy,fx,fy;
private:
 //选择控制点,第一个点是质心,其他三个点跟主成分方向有关
  void choose_control_points();
 //计算alpha,灰度质心齐次坐标
  void compute_barycentric_coordinates();

  inline double dot(const double *v1,const double *v2);
  //求beta时的L矩阵
  void compute_L_6x10(const Eigen::Matrix<double,12,12> &Ut,Eigen::Matrix<double,6,10> &L_6x10);
  //L * beta = rho
  void compute_rho(Eigen::Matrix<double,6,1> &Rho);
  //N 取不同值的时候,求beta
  void find_betas_0(const Eigen::Matrix<double,6,10> &L_6x10,const Eigen::Matrix<double,6,1> &Rho,
              Eigen::Vector4d &Betas);
  void find_betas_1(const Eigen::Matrix<double,6,10> &L_6x10,const Eigen::Matrix<double,6,1> &Rho,
              Eigen::Vector4d &Betas);
  void find_betas_2(const Eigen::Matrix<double,6,10> &L_6x10,const Eigen::Matrix<double,6,1> &Rho,
              Eigen::Vector4d &Betas);

  //计算出beta值和对应的特征向量之后,计算相机坐标系中的控制点
  void compute_ccs(const Eigen::Vector4d &Betas,const Eigen::Matrix<double,12,12> &Ut);
  //通过相机坐标系中的控制点坐标,计算参考点在相机坐标系下的坐标
  void compute_pcs();
   检测参考点在相机坐标系下的z轴坐标是否大于零,大于零不做修改。如果小于零,需要按照相机光心(原点)中心对称一下。???
  void solve_for_sign();
  //horn的绝对定向算法??
  void estimate_R_and_t(Eigen::Matrix3d &R,Eigen::Vector3d &T);
  double reprojection_error(const Eigen::Matrix3d &R,const Eigen::Vector3d &T);
  double compute_R_and_t(const Eigen::Matrix<double,12,12> &Ut,const Eigen::Vector4d &Betas,
              Eigen::Matrix3d &R,Eigen::Vector3d &T);
//高斯牛顿法
  void gauss_newton(const Eigen::Matrix<double,6,10> &L_6x10,const Eigen::Matrix<double,6,1> &Rho,Eigen::Vector4d &betas);
 //高斯牛顿法中的梯度
  void compute_A_and_b_gauss_newton(const Eigen::Matrix<double,6,10> &L_6x10,const Eigen::Matrix<double,6,1> &Rho,
                                    Eigen::Vector4d &betas,Eigen::Matrix<double,6,4> &A,Eigen::Matrix<double,6,1> &b);
  void qr_solve(Eigen::Matrix<double,6,4> &A, Eigen::Matrix<double,6,1> &B,Eigen::Vector4d &X);

  int maximum_number_of_correspondences;
  int number_of_correspondences;
//alphas就是论文里的那些alpha,pcs和pws分别时参考点在相机坐标系和世界坐标系中的点,us是,世界坐标系中的点在图像坐标系中的投影
  Eigen::MatrixXd pws,us,alphas,pcs;
//cws是参考坐标系中4个3d参考点,ccs是相机坐标系中4个3d参考点  
  Eigen::Matrix<double,4,3> cws,ccs;
  double cws_determinant;
};

#endif

           
#include "pnp_solver.h"
#include <iostream>
PnpSolver::PnpSolver()
:maximum_number_of_correspondences(),
number_of_correspondences()
{

}
void PnpSolver::set_internal_parameters(double cx, double cy, double fx, double fy)
{
   this->cx=cx;
   this->cy=cy;
   this->fx=fx;
   this->fy=fy;
}
void PnpSolver::set_maximum_number_of_correspondences(int n)
{
  maximum_number_of_correspondences=n;
  pws.resize(maximum_number_of_correspondences,);
  us.resize(maximum_number_of_correspondences,);
  alphas.resize(maximum_number_of_correspondences,);
  pcs.resize(maximum_number_of_correspondences,);
}
void PnpSolver::reset_number_of_correspondences()
{
  number_of_correspondences=;
}
void PnpSolver::add_correspondence(Eigen::Vector3d point3d, Eigen::Vector2d point2d)
{
  pws.row(number_of_correspondences)=point3d.transpose();

  us.row(number_of_correspondences)=point2d.transpose();

  number_of_correspondences++;
}
void PnpSolver::choose_control_points()
{
  cws.setZero(,);

 for(int i=;i<number_of_correspondences;i++)
     cws.row()+=pws.row(i);
  cws.row()/=number_of_correspondences;

  Eigen::MatrixXd PW0(number_of_correspondences,);
  Eigen::Matrix3d PW0tPW0;
  Eigen::Vector3d DC;
  Eigen::Matrix3d UCt;

  for(int i=;i<number_of_correspondences;i++)
      PW0.row(i)=pws.row(i)-cws.row();

  PW0tPW0=PW0.transpose()*PW0;

  //TODO:maybe wron
  //opencv  cvSVD(&PW0tPW0, &DC, &UCt, 0, CV_SVD_MODIFY_A | CV_SVD_U_T);
  //lmq 
  Eigen::JacobiSVD<Eigen::Matrix3d> svd(PW0tPW0,Eigen::ComputeFullU);
  DC=svd.singularValues();
  UCt=svd.matrixU().transpose();

  for(int i=;i<;i++)
  {
    double k=sqrt(DC(i-)/number_of_correspondences);
    cws.row(i)=cws.row()+k*UCt.row(i-);
  }
}
void PnpSolver::compute_barycentric_coordinates()
{
  Eigen::Matrix3d CC,CC_inv;

  for(int i=;i<;i++)
    for(int j=;j<;j++)
      CC(i,j-)=cws(j,i)-cws(,i);

  CC_inv=CC.inverse();

  for(int i=;i<number_of_correspondences;i++)
  {
    for(int j=;j<;j++)
    {
      alphas(i,+j)=CC_inv(j,)*(pws(i,)-cws(,))+
            CC_inv(j,)*(pws(i,)-cws(,))+
            CC_inv(j,)*(pws(i,)-cws(,));
    }
    alphas(i,)=-alphas(i,)-alphas(i,)-alphas(i,);
  }
}
inline double PnpSolver::dot(const double *v1,const double *v2)
{
  return v1[]*v2[]+v1[]*v2[]+v1[]*v2[];
}
void PnpSolver::compute_L_6x10(const Eigen::Matrix<double,,> &Ut,
                   Eigen::Matrix<double,,> &L_6x10)
{
   int a,b;
   double dv[][][];
   for(int i=;i<;i++)
   {
     a=,b=;
     for(int j=;j<;j++)
     {
        for(int k=;k<;k++)
      dv[i][j][k]=Ut(-i,*a+k)-Ut(-i,*b+k);

    if(++b>)
    {
      a++;
      b=a+;
    }
     }
   }

   for(int i=;i<;i++)
   {
     L_6x10(i,)=     dot(dv[][i],dv[][i]);
     L_6x10(i,)=*dot(dv[][i],dv[][i]);
     L_6x10(i,)=     dot(dv[][i],dv[][i]);
     L_6x10(i,)=*dot(dv[][i],dv[][i]);
     L_6x10(i,)=*dot(dv[][i],dv[][i]);
     L_6x10(i,)=     dot(dv[][i],dv[][i]);
     L_6x10(i,)=*dot(dv[][i],dv[][i]);
     L_6x10(i,)=*dot(dv[][i],dv[][i]);
     L_6x10(i,)=*dot(dv[][i],dv[][i]);
     L_6x10(i,)=     dot(dv[][i],dv[][i]);
   }
}
void PnpSolver::compute_rho(Eigen::Matrix<double,,> &Rho)
{
   Rho[]=(cws.row()-cws.row()).squaredNorm();
   Rho[]=(cws.row()-cws.row()).squaredNorm();
   Rho[]=(cws.row()-cws.row()).squaredNorm();
   Rho[]=(cws.row()-cws.row()).squaredNorm();
   Rho[]=(cws.row()-cws.row()).squaredNorm();
   Rho[]=(cws.row()-cws.row()).squaredNorm();
}
void PnpSolver::find_betas_0(const Eigen::Matrix<double,,> &L_6x10,const Eigen::Matrix<double,,> &Rho,
          Eigen::Vector4d &Betas)
{
  Eigen::Matrix<double,,> L_6x4;
  Eigen::Vector4d B4;

  L_6x4.col()=L_6x10.col();
  L_6x4.col()=L_6x10.col();
  L_6x4.col()=L_6x10.col();
  L_6x4.col()=L_6x10.col();

  B4=L_6x4.colPivHouseholderQr().solve(Rho);

  if(B4[]<)
  {
    Betas[]=sqrt(-B4[]);
    Betas[]=-B4[]/Betas[];
    Betas[]=-B4[]/Betas[];
    Betas[]=-B4[]/Betas[];
  }
  else
  {
    Betas[]=sqrt(B4[]);
    Betas[]=B4[]/Betas[];
    Betas[]=B4[]/Betas[];
    Betas[]=B4[]/Betas[];
  }
}
//to modify
void PnpSolver::find_betas_1(const Eigen::Matrix<double,,> &L_6x10,const Eigen::Matrix<double,,> &Rho,
          Eigen::Vector4d &Betas)
{
  Eigen::Matrix<double,,> L_6x3;
  Eigen::Vector3d B3;

  L_6x3.col()=L_6x10.col();
  L_6x3.col()=L_6x10.col();
  L_6x3.col()=L_6x10.col();

  B3=L_6x3.colPivHouseholderQr().solve(Rho);//cv_svd 用fullPivHouseholderQr()呢?或者是使用eigen/svd里面的东西

  if(B3[]<)
  {
    Betas[]=sqrt(-B3[]);
    Betas[]=(B3[]<)?sqrt(-B3[]):;
  }
  else
  {
    Betas[]=sqrt(B3[]);
    Betas[]=(B3[]>)?sqrt(B3[]):;
  }
  if(B3[]<)
    Betas[]=-Betas[];

  Betas[]=;
  Betas[]=;
}
void PnpSolver::find_betas_2(const Eigen::Matrix<double,,> &L_6x10,const Eigen::Matrix<double,,> &Rho,
          Eigen::Vector4d &Betas)
{
  Eigen::Matrix<double,,> L_6x5;
  Eigen::Matrix<double,,> B5;

  L_6x5.col()=L_6x10.col();
  L_6x5.col()=L_6x10.col();
  L_6x5.col()=L_6x10.col();
  L_6x5.col()=L_6x10.col();
  L_6x5.col()=L_6x10.col();

  B5=L_6x5.colPivHouseholderQr().solve(Rho);

  if(B5[]<)
  {
    Betas[]=sqrt(-B5[]);
    Betas[]=(B5[]<)?sqrt(-B5[]):;
  }
  else
  {
    Betas[]=sqrt(B5[]);
    Betas[]=(B5[]>)?sqrt(B5[]):;
  }
  if(B5[]<)
    B5[]=-B5[];
  Betas[]=B5[]/Betas[];
  Betas[]=;
}
void PnpSolver::compute_ccs(const Eigen::Vector4d &Betas,const Eigen::Matrix<double,,> &Ut)
{
  ccs.setZero(,);

  for(int i=;i<;i++)
  {
    for(int j=;j<;j++)
      for(int k=;k<;k++)
    ccs(j,k)+=Betas[i]*Ut(-i,*j+k);
  }
}
void PnpSolver::compute_pcs()
{
  pcs=alphas*ccs;
}
void PnpSolver::solve_for_sign()
{
  if(pcs(,)<)
  {
    ccs=-ccs;
    pcs=-pcs;
  }
}
void PnpSolver::estimate_R_and_t(Eigen::Matrix3d &R,Eigen::Vector3d &T)
{
  Eigen::Vector3d pc0,pw0;

  pc0.setZero();
  pw0.setZero();

  for(int i=;i<number_of_correspondences;i++)
  {
    pc0+=pcs.row(i);
    pw0+=pws.row(i);
  }
  pc0/=number_of_correspondences;
  pw0/=number_of_correspondences;

  Eigen::Matrix3d W=Eigen::Matrix3d::Zero();
  for(int i=;i<number_of_correspondences;i++)
  {
    W+= (pcs.row(i).transpose()-pc0)*((pws.row(i).transpose()-pw0).transpose());
  }

  //SVD on W
  Eigen::JacobiSVD<Eigen::Matrix3d> svd(W,Eigen::ComputeFullU|Eigen::ComputeFullV);

  Eigen::Matrix3d U=svd.matrixU();
  Eigen::Matrix3d V=svd.matrixV();

  R=U*(V.transpose());
  T=pc0-R*pw0;
}
double PnpSolver::reprojection_error(const Eigen::Matrix3d &R,const Eigen::Vector3d &T)
{
  double sum2=;
  Eigen::Vector3d point3d;
  Eigen::Vector2d point2d;
  for(int i=;i<number_of_correspondences;i++)
  {
    point3d=R*pws.row(i).transpose()+T;
    point2d[]=cx+fx*point3d[]/point3d[];
    point2d[]=cy+fy*point3d[]/point3d[];

    sum2+=(us.row(i).transpose()-point2d).squaredNorm();
  }
  return sum2/number_of_correspondences;
}
double PnpSolver::compute_R_and_t(const Eigen::Matrix<double,,> &Ut,const Eigen::Vector4d &Betas,
              Eigen::Matrix3d &R,Eigen::Vector3d &T)
{
  compute_ccs(Betas,Ut);
  compute_pcs();

  solve_for_sign();

  estimate_R_and_t(R,T);

  return reprojection_error(R,T);
}
double PnpSolver::compute_pose(Eigen::Matrix3d &R,Eigen::Vector3d &T)
{
  choose_control_points();
  compute_barycentric_coordinates();

  Eigen::MatrixXd M(*number_of_correspondences,);

  //fill M function
  for(int i=;i<number_of_correspondences;i++)
  {
    for(int j=;j<;j++)
    {
      M(*i,*j)=alphas(i,j)*fx;
      M(*i,*j+)=;
      M(*i,*j+)=alphas(i,j)*(cx-us(i,));

      M(*i+,*j)=;
      M(*i+,*j+)=alphas(i,j)*fy;
      M(*i+,*j+)=alphas(i,j)*(cy-us(i,));
    }
  }

  Eigen::Matrix<double,,> MtM;
  Eigen::Matrix<double,,> D;
  Eigen::Matrix<double,,> Ut;

  MtM=M.transpose()*M;
  //opencv cvSVD(&MtM, &D, &Ut, 0, CV_SVD_MODIFY_A | CV_SVD_U_T);
  //lmq modified
  Eigen::JacobiSVD<Eigen::Matrix<double,,> > svd(MtM,Eigen::ComputeFullU);
  D=svd.singularValues().transpose();//todo what?? seem no used
  Ut=svd.matrixU().transpose();

  Eigen::Matrix<double,,> L_6x10;
  Eigen::Matrix<double,,> Rho;

  //TODO
  compute_L_6x10(Ut,L_6x10);
  compute_rho(Rho);

  Eigen::Vector4d Betas0,Betas1,Betas2;
  Eigen::Matrix3d R0,R1,R2;
  Eigen::Vector3d T0,T1,T2;
  double errors[];

  find_betas_0(L_6x10,Rho,Betas0);
  gauss_newton(L_6x10,Rho,Betas0);
  errors[]=compute_R_and_t(Ut,Betas0,R0,T0);

  find_betas_1(L_6x10,Rho,Betas1);
  gauss_newton(L_6x10,Rho,Betas1);
  errors[]=compute_R_and_t(Ut,Betas1,R1,T1);

  find_betas_2(L_6x10,Rho,Betas2);
  gauss_newton(L_6x10,Rho,Betas2);
  errors[]=compute_R_and_t(Ut,Betas2,R2,T2);

  int N=;
  R=R0;T=T0;
  if(errors[]<errors[]) 
  {
    N=;R=R1;T=T1;
  }
  if(errors[]<errors[N])
  {
    N=;R=R2;T=T2;
  }
  return errors[N];
  return ; 
}

//attention
void PnpSolver::projectPoints(std::vector <Eigen::Vector3d> &src, std::vector <Eigen::Vector2d> &dst,Eigen::Matrix3d &R,Eigen::Vector3d &T)
{
    int size = src.size();
    for(int i = ;i < size; ++ i)
    {

        Eigen::Vector3d point3d = R * src[i]+T;

        dst[i][]=cx+fx*point3d[]/point3d[];
        dst[i][]=cy+fy*point3d[]/point3d[];
    }

}

void PnpSolver::gauss_newton(const Eigen::Matrix<double, , > &L_6x10, const Eigen::Matrix<double, , > &Rho,
                             Eigen::Vector4d &betas)
{
  const int iterations_number = ;//提高迭代次数好像没什么用,具体原因是?

  Eigen::Matrix<double,,> A;
  Eigen::Matrix<double,,> B;
  Eigen::Vector4d X;

  for(int k = ; k < iterations_number; k++)
  {
    compute_A_and_b_gauss_newton(L_6x10, Rho, betas, A, B);
    qr_solve(A, B, X);

    for(int i = ; i < ; i++)
      betas[i] += X[i];
  }


}


void PnpSolver::compute_A_and_b_gauss_newton(const Eigen::Matrix<double, , > &L_6x10,
                                             const Eigen::Matrix<double, , > &Rho, Eigen::Vector4d &betas,
                                             Eigen::Matrix<double, , > &A, Eigen::Matrix<double, , > &b)
{
  for(int i = ;i < ; ++i) {

    Eigen::RowVectorXd rowL = L_6x10.row(i);
    Eigen::RowVector4d rowA = A.row(i);

    rowA[] =  * rowL[] * betas[] +     rowL[] * betas[] +     rowL[] * betas[] +     rowL[] * betas[];
    rowA[] =     rowL[] * betas[] +  * rowL[] * betas[] +     rowL[] * betas[] +     rowL[] * betas[];
    rowA[] =     rowL[] * betas[] +     rowL[] * betas[] +  * rowL[] * betas[] +     rowL[] * betas[];
    rowA[] =     rowL[] * betas[] +     rowL[] * betas[] +     rowL[] * betas[] +  * rowL[] * betas[];

    b[i] = Rho[i] -
            (
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[] +
                    rowL[] * betas[] * betas[]
            );
    A.row(i) = rowA;

  }


}

void PnpSolver::qr_solve(Eigen::Matrix<double, , > &A, Eigen::Matrix<double, , > &B, Eigen::Vector4d &X)
{
  X = A.colPivHouseholderQr().solve(B);
}