zoukankan      html  css  js  c++  java
  • 深度滤波器(4)——单目稠密重建的流程

    各位泡芙们,大家好!

    经过前两天的理论铺垫,相信你对深度滤波器有了一个感性的认识,今天,我们就通过流程图的方式来帮大家梳理一下单目稠密重建的代码。

    主函数:

    update:

     1 bool update(const Mat& ref, const Mat& curr, const SE3& T_C_R, Mat& depth, Mat& depth_cov )
     2 {
     3 #pragma omp parallel for
     4     for ( int x=boarder; x<width-boarder; x++ )
     5 #pragma omp parallel for
     6         for ( int y=boarder; y<height-boarder; y++ )
     7         {
     8             // 遍历每个像素
     9             if ( depth_cov.ptr<double>(y)[x] < min_cov || depth_cov.ptr<double>(y)[x] > max_cov ) // 深度已收敛或发散
    10                 continue;
    11             // 在极线上搜索 (x,y) 的匹配 
    12             Vector2d pt_curr; 
    13             bool ret = epipolarSearch ( 
    14                 ref, 
    15                 curr, 
    16                 T_C_R, 
    17                 Vector2d(x,y), 
    18                 depth.ptr<double>(y)[x], 
    19                 sqrt(depth_cov.ptr<double>(y)[x]),
    20                 pt_curr
    21             );
    22             
    23             if ( ret == false ) // 匹配失败
    24                 continue; 
    25             
    26             // 取消该注释以显示匹配
    27             // showEpipolarMatch( ref, curr, Vector2d(x,y), pt_curr );
    28             
    29             // 匹配成功,更新深度图 
    30             updateDepthFilter( Vector2d(x,y), pt_curr, T_C_R, depth, depth_cov );
    31         }
    32 }

     极线搜索:

     

     1 // 极线搜索
     2 // 方法见书 13.2 13.3 两节
     3 bool epipolarSearch(
     4     const Mat& ref, const Mat& curr, 
     5     const SE3& T_C_R, const Vector2d& pt_ref, 
     6     const double& depth_mu, const double& depth_cov, 
     7     Vector2d& pt_curr )
     8 {
     9     Vector3d f_ref = px2cam( pt_ref );
    10     f_ref.normalize();
    11     Vector3d P_ref = f_ref*depth_mu;    // 参考帧的 P 向量
    12     
    13     Vector2d px_mean_curr = cam2px( T_C_R*P_ref ); // 按深度均值投影的像素
    14     double d_min = depth_mu-3*depth_cov, d_max = depth_mu+3*depth_cov;
    15     if ( d_min<0.1 ) d_min = 0.1;
    16     Vector2d px_min_curr = cam2px( T_C_R*(f_ref*d_min) );    // 按最小深度投影的像素
    17     Vector2d px_max_curr = cam2px( T_C_R*(f_ref*d_max) );    // 按最大深度投影的像素
    18     
    19     Vector2d epipolar_line = px_max_curr - px_min_curr;    // 极线(线段形式)
    20     Vector2d epipolar_direction = epipolar_line;        // 极线方向 
    21     epipolar_direction.normalize();
    22     double half_length = 0.5*epipolar_line.norm();    // 极线线段的半长度
    23     if ( half_length>100 ) half_length = 100;   // 我们不希望搜索太多东西 
    24     
    25     // 取消此句注释以显示极线(线段)
    26     // showEpipolarLine( ref, curr, pt_ref, px_min_curr, px_max_curr );
    27     
    28     // 在极线上搜索,以深度均值点为中心,左右各取半长度
    29     double best_ncc = -1.0;
    30     Vector2d best_px_curr; 
    31     for ( double l=-half_length; l<=half_length; l+=0.7 )  // l+=sqrt(2) 
    32     {
    33         Vector2d px_curr = px_mean_curr + l*epipolar_direction;  // 待匹配点
    34         if ( !inside(px_curr) )
    35             continue; 
    36         // 计算待匹配点与参考帧的 NCC
    37         double ncc = NCC( ref, curr, pt_ref, px_curr );
    38         if ( ncc>best_ncc )
    39         {
    40             best_ncc = ncc; 
    41             best_px_curr = px_curr;
    42         }
    43     }
    44     if ( best_ncc < 0.85f )      // 只相信 NCC 很高的匹配
    45         return false; 
    46     pt_curr = best_px_curr;
    47     return true;
    48 }

    深度滤波器:

    bool updateDepthFilter(
        const Vector2d& pt_ref, 
        const Vector2d& pt_curr, 
        const SE3& T_C_R,
        Mat& depth, 
        Mat& depth_cov
    )
    {
        // 我是一只喵
        // 不知道这段还有没有人看
        // 用三角化计算深度
        SE3 T_R_C = T_C_R.inverse();
        Vector3d f_ref = px2cam( pt_ref );
        f_ref.normalize();
        Vector3d f_curr = px2cam( pt_curr );
        f_curr.normalize();
        
        // 方程
        // d_ref * f_ref = d_cur * ( R_RC * f_cur ) + t_RC
        // => [ f_ref^T f_ref, -f_ref^T f_cur ] [d_ref] = [f_ref^T t]
        //    [ f_cur^T f_ref, -f_cur^T f_cur ] [d_cur] = [f_cur^T t]
        // 二阶方程用克莱默法则求解并解之
        Vector3d t = T_R_C.translation();
        Vector3d f2 = T_R_C.rotation_matrix() * f_curr; 
        Vector2d b = Vector2d ( t.dot ( f_ref ), t.dot ( f2 ) );
        double A[4];
        A[0] = f_ref.dot ( f_ref );
        A[2] = f_ref.dot ( f2 );
        A[1] = -A[2];
        A[3] = - f2.dot ( f2 );
        double d = A[0]*A[3]-A[1]*A[2];
        Vector2d lambdavec = 
            Vector2d (  A[3] * b ( 0,0 ) - A[1] * b ( 1,0 ),
                        -A[2] * b ( 0,0 ) + A[0] * b ( 1,0 )) /d;
        Vector3d xm = lambdavec ( 0,0 ) * f_ref;
        Vector3d xn = t + lambdavec ( 1,0 ) * f2;
        Vector3d d_esti = ( xm+xn ) / 2.0;  // 三角化算得的深度向量
        double depth_estimation = d_esti.norm();   // 深度值
        
        // 计算不确定性(以一个像素为误差)
        Vector3d p = f_ref*depth_estimation;
        Vector3d a = p - t; 
        double t_norm = t.norm();
        double a_norm = a.norm();
        double alpha = acos( f_ref.dot(t)/t_norm );
        double beta = acos( -a.dot(t)/(a_norm*t_norm));
        double beta_prime = beta + atan(1/fx);
        double gamma = M_PI - alpha - beta_prime;
        double p_prime = t_norm * sin(beta_prime) / sin(gamma);
        double d_cov = p_prime - depth_estimation; 
        double d_cov2 = d_cov*d_cov;
        
        // 高斯融合
        double mu = depth.ptr<double>( int(pt_ref(1,0)) )[ int(pt_ref(0,0)) ];
        double sigma2 = depth_cov.ptr<double>( int(pt_ref(1,0)) )[ int(pt_ref(0,0)) ];
        
        double mu_fuse = (d_cov2*mu+sigma2*depth_estimation) / ( sigma2+d_cov2);
        double sigma_fuse2 = ( sigma2 * d_cov2 ) / ( sigma2 + d_cov2 );
        
        depth.ptr<double>( int(pt_ref(1,0)) )[ int(pt_ref(0,0)) ] = mu_fuse; 
        depth_cov.ptr<double>( int(pt_ref(1,0)) )[ int(pt_ref(0,0)) ] = sigma_fuse2;
        
        return true;
    }

    相信有了上面的流程图,加上前面几章的理论铺垫,你能够比较轻松地看懂这些代码了。祝你阅读代码愉快!

  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/liufuqiang/p/6804026.html
Copyright © 2011-2022 走看看