VINS-Mono/Fusion与OpenCV去畸变对比

news2024/11/15 13:52:51

VINS中没有直接使用opencv的去畸变函数,而是自己编写了迭代函数完成去畸变操作,主要是为了加快去畸变计算速度
本文对二者的结果精度和耗时进行了对比

VINS-Mono/Fusion与OpenCV去畸变对比

  • 1 去畸变原理
  • 2 代码实现
    • 2.1 OpenCV去畸变
    • 2.2 VINS去畸变
  • 3 二者对比

1 去畸变原理

opencv去畸变操作由cv::undistortPoints实现
VINS去畸变由PinholeCamera::liftProjective实现(以针孔相机为例)

二者均采用了迭代求解,通过多次迭代逼近真值。其中cv::undistortPoints方法中默认迭代5次,并计算每次重投影误差是否小于阈值,VINS去畸变方法只设置了迭代8次。
二者均输入像素坐标,输出归一化坐标。


2 代码实现

2.1 OpenCV去畸变

opencv去畸变操作由cv::undistortPoints实现,代码在opencv-3.4.13/modules/imgproc/src
undistortPoints首先处理了输入参数,主要实现部分调用cvUndistortPointsInternal

void undistortPoints( InputArray _src, OutputArray _dst,InputArray _cameraMatrix, InputArray _distCoeffs,InputArray _Rmat, InputArray _Pmat, TermCriteria criteria)

void undistortPoints( InputArray _src, OutputArray _dst,
                      InputArray _cameraMatrix,
                      InputArray _distCoeffs,
                      InputArray _Rmat,
                      InputArray _Pmat,
                      TermCriteria criteria)
{
    Mat src = _src.getMat(), cameraMatrix = _cameraMatrix.getMat();
    Mat distCoeffs = _distCoeffs.getMat(), R = _Rmat.getMat(), P = _Pmat.getMat();

    int npoints = src.checkVector(2), depth = src.depth();
    if (npoints < 0)
        src = src.t();
    npoints = src.checkVector(2);
    CV_Assert(npoints >= 0 && src.isContinuous() && (depth == CV_32F || depth == CV_64F));

    if (src.cols == 2)
        src = src.reshape(2);

    _dst.create(npoints, 1, CV_MAKETYPE(depth, 2), -1, true);
    Mat dst = _dst.getMat();

    CvMat _csrc = cvMat(src), _cdst = cvMat(dst), _ccameraMatrix = cvMat(cameraMatrix);
    CvMat matR, matP, _cdistCoeffs, *pR=0, *pP=0, *pD=0;
    if( !R.empty() )
        pR = &(matR = cvMat(R));
    if( !P.empty() )
        pP = &(matP = cvMat(P));
    if( !distCoeffs.empty() )
        pD = &(_cdistCoeffs = cvMat(distCoeffs));
    cvUndistortPointsInternal(&_csrc, &_cdst, &_ccameraMatrix, pD, pR, pP, criteria);
}

static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvMat* _cameraMatrix, const CvMat* _distCoeffs, const CvMat* matR, const CvMat* matP, cv::TermCriteria criteria)

static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvMat* _cameraMatrix,
                   const CvMat* _distCoeffs,
                   const CvMat* matR, const CvMat* matP, cv::TermCriteria criteria)
{
    CV_Assert(criteria.isValid());
    double A[3][3], RR[3][3], k[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    CvMat matA=cvMat(3, 3, CV_64F, A), _Dk;
    CvMat _RR=cvMat(3, 3, CV_64F, RR);
    cv::Matx33d invMatTilt = cv::Matx33d::eye();
    cv::Matx33d matTilt = cv::Matx33d::eye();

    CV_Assert( CV_IS_MAT(_src) && CV_IS_MAT(_dst) &&
        (_src->rows == 1 || _src->cols == 1) &&
        (_dst->rows == 1 || _dst->cols == 1) &&
        _src->cols + _src->rows - 1 == _dst->rows + _dst->cols - 1 &&
        (CV_MAT_TYPE(_src->type) == CV_32FC2 || CV_MAT_TYPE(_src->type) == CV_64FC2) &&
        (CV_MAT_TYPE(_dst->type) == CV_32FC2 || CV_MAT_TYPE(_dst->type) == CV_64FC2));

    CV_Assert( CV_IS_MAT(_cameraMatrix) &&
        _cameraMatrix->rows == 3 && _cameraMatrix->cols == 3 );

    cvConvert( _cameraMatrix, &matA );


    if( _distCoeffs )
    {
        CV_Assert( CV_IS_MAT(_distCoeffs) &&
            (_distCoeffs->rows == 1 || _distCoeffs->cols == 1) &&
            (_distCoeffs->rows*_distCoeffs->cols == 4 ||
             _distCoeffs->rows*_distCoeffs->cols == 5 ||
             _distCoeffs->rows*_distCoeffs->cols == 8 ||
             _distCoeffs->rows*_distCoeffs->cols == 12 ||
             _distCoeffs->rows*_distCoeffs->cols == 14));

        _Dk = cvMat( _distCoeffs->rows, _distCoeffs->cols,
            CV_MAKETYPE(CV_64F,CV_MAT_CN(_distCoeffs->type)), k);

        cvConvert( _distCoeffs, &_Dk );
        if (k[12] != 0 || k[13] != 0)
        {
            cv::detail::computeTiltProjectionMatrix<double>(k[12], k[13], NULL, NULL, NULL, &invMatTilt);
            cv::detail::computeTiltProjectionMatrix<double>(k[12], k[13], &matTilt, NULL, NULL);
        }
    }

    if( matR )
    {
        CV_Assert( CV_IS_MAT(matR) && matR->rows == 3 && matR->cols == 3 );
        cvConvert( matR, &_RR );
    }
    else
        cvSetIdentity(&_RR);

    if( matP )
    {
        double PP[3][3];
        CvMat _P3x3, _PP=cvMat(3, 3, CV_64F, PP);
        CV_Assert( CV_IS_MAT(matP) && matP->rows == 3 && (matP->cols == 3 || matP->cols == 4));
        cvConvert( cvGetCols(matP, &_P3x3, 0, 3), &_PP );
        cvMatMul( &_PP, &_RR, &_RR );
    }

    const CvPoint2D32f* srcf = (const CvPoint2D32f*)_src->data.ptr;
    const CvPoint2D64f* srcd = (const CvPoint2D64f*)_src->data.ptr;
    CvPoint2D32f* dstf = (CvPoint2D32f*)_dst->data.ptr;
    CvPoint2D64f* dstd = (CvPoint2D64f*)_dst->data.ptr;
    int stype = CV_MAT_TYPE(_src->type);
    int dtype = CV_MAT_TYPE(_dst->type);
    int sstep = _src->rows == 1 ? 1 : _src->step/CV_ELEM_SIZE(stype);
    int dstep = _dst->rows == 1 ? 1 : _dst->step/CV_ELEM_SIZE(dtype);

    double fx = A[0][0];
    double fy = A[1][1];
    double ifx = 1./fx;
    double ify = 1./fy;
    double cx = A[0][2];
    double cy = A[1][2];

    int n = _src->rows + _src->cols - 1;
    for( int i = 0; i < n; i++ )
    {
        double x, y, x0 = 0, y0 = 0, u, v;
        if( stype == CV_32FC2 )
        {
            x = srcf[i*sstep].x;
            y = srcf[i*sstep].y;
        }
        else
        {
            x = srcd[i*sstep].x;
            y = srcd[i*sstep].y;
        }
        u = x; v = y;
        x = (x - cx)*ifx;
        y = (y - cy)*ify;

        if( _distCoeffs ) {
            // compensate tilt distortion
            cv::Vec3d vecUntilt = invMatTilt * cv::Vec3d(x, y, 1);
            double invProj = vecUntilt(2) ? 1./vecUntilt(2) : 1;
            x0 = x = invProj * vecUntilt(0);
            y0 = y = invProj * vecUntilt(1);

            double error = std::numeric_limits<double>::max();
            // compensate distortion iteratively

            for( int j = 0; ; j++ )
            {
                //在这里判断
                if ((criteria.type & cv::TermCriteria::COUNT) && j >= criteria.maxCount)
                    break;
                if ((criteria.type & cv::TermCriteria::EPS) && error < criteria.epsilon)
                    break;
                double r2 = x*x + y*y;
                double icdist = (1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2)/(1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2);
                if (icdist < 0)  // test: undistortPoints.regression_14583
                {
                    x = (u - cx)*ifx;
                    y = (v - cy)*ify;
                    break;
                }
                double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2;
                double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2;
                x = (x0 - deltaX)*icdist;
                y = (y0 - deltaY)*icdist;

                if(criteria.type & cv::TermCriteria::EPS)
                {
                    double r4, r6, a1, a2, a3, cdist, icdist2;
                    double xd, yd, xd0, yd0;
                    cv::Vec3d vecTilt;

                    r2 = x*x + y*y;
                    r4 = r2*r2;
                    r6 = r4*r2;
                    a1 = 2*x*y;
                    a2 = r2 + 2*x*x;
                    a3 = r2 + 2*y*y;
                    cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
                    icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
                    xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
                    yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;

                    vecTilt = matTilt*cv::Vec3d(xd0, yd0, 1);
                    invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
                    xd = invProj * vecTilt(0);
                    yd = invProj * vecTilt(1);

                    double x_proj = xd*fx + cx;
                    double y_proj = yd*fy + cy;

                    error = sqrt( pow(x_proj - u, 2) + pow(y_proj - v, 2) );
                }
            }
        }

        double xx = RR[0][0]*x + RR[0][1]*y + RR[0][2];
        double yy = RR[1][0]*x + RR[1][1]*y + RR[1][2];
        double ww = 1./(RR[2][0]*x + RR[2][1]*y + RR[2][2]);
        x = xx*ww;
        y = yy*ww;

        if( dtype == CV_32FC2 )
        {
            dstf[i*dstep].x = (float)x;
            dstf[i*dstep].y = (float)y;
        }
        else
        {
            dstd[i*dstep].x = x;
            dstd[i*dstep].y = y;
        }
    }
}

2.2 VINS去畸变

void
PinholeCamera::liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const
{
    double mx_d, my_d,mx2_d, mxy_d, my2_d, mx_u, my_u;
    double rho2_d, rho4_d, radDist_d, Dx_d, Dy_d, inv_denom_d;
    //double lambda;

    // Lift points to normalised plane
    mx_d = m_inv_K11 * p(0) + m_inv_K13;
    my_d = m_inv_K22 * p(1) + m_inv_K23;

    if (m_noDistortion)
    {
        mx_u = mx_d;
        my_u = my_d;
    }
    else
    {
        if (0)
        {
            double k1 = mParameters.k1();
            double k2 = mParameters.k2();
            double p1 = mParameters.p1();
            double p2 = mParameters.p2();

            // Apply inverse distortion model
            // proposed by Heikkila
            mx2_d = mx_d*mx_d;
            my2_d = my_d*my_d;
            mxy_d = mx_d*my_d;
            rho2_d = mx2_d+my2_d;
            rho4_d = rho2_d*rho2_d;
            radDist_d = k1*rho2_d+k2*rho4_d;
            Dx_d = mx_d*radDist_d + p2*(rho2_d+2*mx2_d) + 2*p1*mxy_d;
            Dy_d = my_d*radDist_d + p1*(rho2_d+2*my2_d) + 2*p2*mxy_d;
            inv_denom_d = 1/(1+4*k1*rho2_d+6*k2*rho4_d+8*p1*my_d+8*p2*mx_d);

            mx_u = mx_d - inv_denom_d*Dx_d;
            my_u = my_d - inv_denom_d*Dy_d;
        }
        else
        {
            // Recursive distortion model
            int n = 8;
            Eigen::Vector2d d_u;
            distortion(Eigen::Vector2d(mx_d, my_d), d_u);
            // Approximate value
            mx_u = mx_d - d_u(0);
            my_u = my_d - d_u(1);

            for (int i = 1; i < n; ++i)
            {
                distortion(Eigen::Vector2d(mx_u, my_u), d_u);
                mx_u = mx_d - d_u(0);
                my_u = my_d - d_u(1);
            }
        }
    }

    // Obtain a projective ray
    P << mx_u, my_u, 1.0;
}

3 二者对比

在相机坐标系下随机生成了 20 个观测点,并将其归算到归一化坐标系下作为真值。

#include <iostream>
#include <vector>
#include <random>  
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <opencv2/opencv.hpp>
#include <opencv2/core/eigen.hpp>
#include <chrono>

#include "Camera.h"

using namespace std;


int main()
{

    // 随机数生成 20 个 三维特征点
    int featureNums=20;
    default_random_engine generator;
    vector<cv::Point2f> pts_truth;  //归一化真值

    vector<cv::Point2f> uv_pts;     //像素坐标
    vector<cv::Point2f> cv_un_pts, vins_un_pts;  //归一化坐标

    for(int i = 0; i < featureNums; ++i)
    {
        uniform_real_distribution<double> xy_rand(-4, 4.0);
        uniform_real_distribution<double> z_rand(8., 10.);
        double tx = xy_rand(generator);
        double ty = xy_rand(generator);
        double tz = z_rand(generator);

        Eigen::Vector2d p(tx/tz, ty/tz);
        Eigen::Vector2d p_distorted;
        distortion(p, p_distorted);    //归一化坐标畸变
        p_distorted+=p;
        pts_truth.push_back(cv::Point2f(p(0), p(1)));

        cv::Point2f uv(fx*p_distorted(0)+cx, fy*p_distorted(1)+cy); //投影到像素坐标
        uv_pts.push_back(uv);
    }


    //OpenCV去畸变,输入像素坐标,输出归一化坐标
    chrono::steady_clock::time_point cv_t1 = chrono::steady_clock::now();
    cv::undistortPoints(uv_pts, cv_un_pts, K, distCoeffs);
    chrono::steady_clock::time_point cv_t2 = chrono::steady_clock::now();

    double cv_time = chrono::duration_cast<chrono::duration<double,milli>>(cv_t2-cv_t1).count();
    
    cout<<"OpenCV"<<endl;
    cout<<"used time: "<<cv_time/cv_un_pts.size()<<"ms"<<endl;
    cout<<"pixel error: "<<GetResidual(cv_un_pts, pts_truth)<<endl;


    //VINS去畸变
    chrono::steady_clock::time_point vins_t1 = chrono::steady_clock::now();
    liftProjective(uv_pts, vins_un_pts);
    chrono::steady_clock::time_point vins_t2 = chrono::steady_clock::now();

    double vins_time = chrono::duration_cast<chrono::duration<double, milli>>(vins_t2-vins_t1).count();
    
    cout<<"VINS"<<endl;
    cout<<"used time: "<<vins_time/vins_un_pts.size()<<"ms"<<endl;
    cout<<"pixel error: "<<GetResidual(vins_un_pts, pts_truth)<<endl;



    return 0;
}

输出结果

给出了每个观测点的平均去畸变耗时和像素坐标系下的重投影误差。
VINS所采用的去畸变算法耗时更少,重投影误差平均值更小,opencv方法与其相差一个数量级。

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/355337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

压缩20M文件从30秒到1秒的优化过程

压缩20M文件从30秒到1秒的优化过程 有一个需求需要将前端传过来的10张照片&#xff0c;然后后端进行处理以后压缩成一个压缩包通过网络流传输出去。之前没有接触过用Java压缩文件的&#xff0c;所以就直接上网找了一个例子改了一下用了&#xff0c;改完以后也能使用&#xff0…

(考研湖科大教书匠计算机网络)第四章网络层-第九节:虚拟专用网与网络地址转换

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;虚拟专用网&#xff08;1&#xff09;虚拟专用网是什么&#xff08;2&#xff09;虚拟专用网如何分配IP地址&#xff08;3&#xff09;例子&#x…

【JAVA八股文】框架相关

框架相关1. Spring refresh 流程2. Spring bean 生命周期3. Spring bean 循环依赖解决 set 循环依赖的原理4. Spring 事务失效5. Spring MVC 执行流程6. Spring 注解7. SpringBoot 自动配置原理8. Spring 中的设计模式1. Spring refresh 流程 Spring refresh 概述 refresh 是…

深度学习(1)神经网络基础

要学习深度学习&#xff0c;那么首先要熟悉神经网络&#xff08;Neural Networks&#xff0c;简称NN&#xff09;的一些基本概念。当然&#xff0c;这里所说的神经网络不是生物学的神经网络&#xff0c;我们将其称之为人工神经网络&#xff08;Artificial Neural Networks&…

海豚调度2.0.5 星环驱动包踩坑(二)worker服务正常、zk注册正常,心跳时间不更新,也不执行任务,任务一直处于执行中状态

目录背景问题记录20230206 发现服务启动失败20230215 有一台worker不执行作业&#xff0c;其它均正常问题解决问题思考背景 之前分享过海豚调度2.0.5连接星环库使用记录&#xff0c;后来说存储过程又出现了超时的情况&#xff0c;原因是因为调度星环驱动包和生产星环库驱动包不…

ES 异常写入解决流程

问题说明 一天下午&#xff0c;在北京客户现场的同学反馈我们elasticsearch出现的大量的异常&#xff0c;他反馈说他使用多线程写入大量数据到elasticsearch集群时&#xff0c;隔一段时间之后就会出现CircuitBreakingException&#xff0c;多尝试几次后&#xff0c;他就把问题反…

基于微信小程序的微信社团小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

JavaEE|网络原理·上

文章目录一、网络发展史1.独立模式2.网络互联3.局域网&#xff08;LAN&#xff09;4.广域网&#xff08;WAN&#xff09;局域网组网的方式①基于网线直连②基于集线器&#xff08;hub&#xff09;组建③基于交换机(switch)组建④基于交换机和路由器组建二、网络通信基础1.ip地址…

Winform控件开发(14)——NotifyIcon(史上最全)

前言: 先看个气泡提示框的效果: 代码如下: 在一个button中注册click事件,当我们点击button1时,就能显示气泡 private void button1_Click(object sender, EventArgs e){notifyIcon1.Visible = true;notifyIcon1

【论文速递】ICLR2018 - 用于小样本语义分割的条件网络

【论文速递】ICLR2018 - 用于小样本语义分割的条件网络 【论文原文】&#xff1a;CONDITIONAL NETWORKS FOR FEW-SHOT SEMANTIC SEGMENTATION&#xff08;Workshop track - ICLR 2018&#xff09; 【作者信息】&#xff1a;Kate Rakelly Evan Shelhamer Trevor Darrell Alexe…

PyTorch - Conv2d 和 MaxPool2d

文章目录Conv2d计算Conv2d 函数解析代码示例MaxPool2d计算函数说明卷积过程动画Transposed convolution animationsTransposed convolution animations参考视频&#xff1a;土堆说 卷积计算 https://www.bilibili.com/video/BV1hE411t7RN 关于 torch.nn 和 torch.nn.function t…

Reverse入门[不断记录]

文章目录前言一、[SWPUCTF 2021 新生赛]re1二、[SWPUCTF 2021 新生赛]re2三、[GFCTF 2021]wordy[花指令]四、[NSSRound#3 Team]jump_by_jump[花指令]五、[NSSRound#3 Team]jump_by_jump_revenge[花指令]前言 心血来潮&#xff0c;想接触点Reverse&#xff0c;感受下Reverse&am…

网络编程(一)

网络编程 文章目录网络编程前置概念1- 字节序高低地址与高低字节高低地址&#xff1a;高低字节字节序大端小端例子代码判断当前机器是大端还是小端为何要有字节序字节序转换函数需要字节序转换的时机例子一例子二2- IP地址转换函数早期(不用管)举例现在与字节序转换函数相比:**…

模块化热更思路

title: 模块化热更思路 categories: Others tags: [热更, 模块化, 分包] date: 2023-02-18 01:04:57 comments: false mathjax: true toc: true 模块化热更 浅浅的记录一下访问破 200w (But, I don’t care about this.) 前篇 只谈思路, 不贴实现代码. 需求 游戏类型属于合集…

Linux(十三)设计模式——单例模式

设计模式——针对典型场景所设计出来的特别的处理方案 单例模式&#xff1a;一个类只能实例化一个对象&#xff08;所以叫单例&#xff09; 场景&#xff1a; 1、资源角度&#xff1a;资源在内存中只占有一份 2、数据角度&#xff1a;如果只有一个对象&#xff0c;那么该对象在…

2019蓝桥杯真题质数(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 我们知道第一个质数是 2、第二个质数是 3、第三个质数是 5…… 请你计算第 2019 个质数是多少&#xff1f; 运行限制 最大运行时间&#xff1a;1s 最大运行内存: 128M…

Mac下安装Tomcat以及IDEA中的配置

安装brew 打开终端输入以下命令&#xff1a; /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 搜索tomcat版本&#xff0c;输入以下命令&#xff1a; brew search tomcat 安装自己想要的版本&#xff0c;例…

JDK版本区别

1. 泛型 ArrayList listnew ArrayList()------>ArrayList<Integer>listnew ArrayList<Integer>(); 2 自动装箱/拆箱 nt ilist.get(0).parseInt();-------->int ilist.get(0);原始类型与对应的包装类不用显式转换 3 for-each i0;i<a.length;i------------&…

解析从Linux零拷贝深入了解Linux-I/O(上)

本文将从文件传输场景以及零拷贝技术深究 Linux I/O 的发展过程、优化手段以及实际应用。前言 存储器是计算机的核心部件之一&#xff0c;在完全理想的状态下&#xff0c;存储器应该要同时具备以下三种特性&#xff1a; 速度足够快&#xff1a;存储器的存取速度应当快于 CPU …

JWT安全漏洞以及常见攻击方式

前言 随着web应用的日渐复杂化&#xff0c;某些场景下&#xff0c;仅使用Cookie、Session等常见的身份鉴别方式无法满足业务的需要&#xff0c;JWT也就应运而生&#xff0c;JWT可以有效的解决分布式场景下的身份鉴别问题&#xff0c;并且会规避掉一些安全问题&#xff0c;如CO…