Kalibr源码学习(一): 重投影误差

给自己挖一个大坑, 从标定结果来学习Kalibr的标定源码, 这里基本以KB模型为例, 也就是标定时, kalibr的模型设定为 --model pinhole-equi , 这里以重投影误差开始,希望能坚持;

重投影误差标定结果

首先以重投影误差的txt文档的结果开始,

可以看到上面的标定结果中, 重投影误差显示为 reprojection error: [-0.000002, -0.000000] +- [0.535272, 0.572115]
这里的两项对应的分别是 reprojection error: [x方向的重投影误差均值, y方向的重投影误差均值] +- [x方向重投影误差方差, y方向重投影误差方差]

其对应的代码片为:

//获取当前检测的角点,重投影坐标, 重投影误差
corners, reprojs, rerrs = getReprojectionErrors(cself, cidx) if len(rerrs)>0://获取重投影误差的均值me和方差seme, se = getReprojectionErrorStatistics(rerrs)//打印输出到txt文档中print >> dest, "\t reprojection error: [%f, %f] +- [%f, %f]" % (me[0], me[1], se[0], se[1])print >> dest

这里可以再说以下计算均值和方差的python代码:

在这里插入代码片
```def getReprojectionErrorStatistics(all_rerrs):#传入值是所有的误差 errors, 这里代码里面举出的例子usage是错误的,传入的应该是error,不是重投影if not len(all_rerrs)>0:raise RuntimeError("rerrs has invalid dimension")gc.disable() #append speed uprerr_matrix=list();for view_id, view_rerrs in enumerate(all_rerrs):if view_rerrs is not None: #if cam sees target in this viewfor rerr in view_rerrs:if not (rerr==np.array([None,None])).all(): #if corner was observedrerr_matrix.append(rerr) #将误差值统一放入到rerr_matrix中rerr_matrix = np.array(rerr_matrix)gc.enable()mean = np.mean(rerr_matrix, 0, dtype=np.float)#计算均值std = np.std(rerr_matrix, 0, dtype=np.float)#计算方差return mean, std

重投影误差图像显示


这个图对应的代码为:

def plotAllReprojectionErrors(cself, cam_id, fno=1, noShow=False, clearFigure=True, title=""):# left: observations and projecitons# right: scatterplot of reprojection errorsall_corners, reprojections, rerrs_xy = getReprojectionErrors(cself, cam_id)resolution = (cself.cameras[cam_id].geometry.projection().ru(), cself.cameras[cam_id].geometry.projection().rv())#创建图像values = np.arange(len(cself.views))/np.double(len(cself.views))cmap = pl.cm.jet(values,alpha=0.5)#子图一用来画重投影后的坐标 省略a=pl.subplot(121)#子图二用来画重投影误差sub = pl.subplot(122)for view_id, rerrs in enumerate(rerrs_xy):if rerrs is not None: #if this camerea sees the target in this viewcolor = cmap[view_id,:]pl.plot(rerrs[:,0], rerrs[:,1], 'x', lw=3, mew=3, color=color)# 主要是这里,x代表的是重投影的x误差, y表示的是重投影的y值误差, 以 error_x 为横坐标, 以error_y为纵坐标,画图pl.axis('equal')pl.grid('on')pl.xlabel('error x (pix)')pl.ylabel('error y (pix)')#省略...

这里重点说一下获取重投影误差的函数, 经过层层调用, 最后计算出的其实是将三维点投影到图像上

//将传入的三维点投影到图像上
template<typename DISTORTION_T>
template<typename DERIVED_P, typename DERIVED_K>
bool PinholeProjection<DISTORTION_T>::euclideanToKeypoint(const Eigen::MatrixBase<DERIVED_P> & p,const Eigen::MatrixBase<DERIVED_K> & outKeypointConst) const {EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE_OR_DYNAMIC(Eigen::MatrixBase<DERIVED_P>, 3);EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE_OR_DYNAMIC(Eigen::MatrixBase<DERIVED_K>, 2);Eigen::MatrixBase<DERIVED_K> & outKeypoint = const_cast<Eigen::MatrixBase<DERIVED_K> &>(outKeypointConst);//shinan note: 转换到归一化相机平面 --> 加畸变 -->转换到像素平面outKeypoint.derived().resize(2);double rz = 1.0 / p[2];outKeypoint[0] = p[0] * rz;outKeypoint[1] = p[1] * rz;//加畸变_distortion.distort(outKeypoint);outKeypoint[0] = _fu * outKeypoint[0] + _cu;outKeypoint[1] = _fv * outKeypoint[1] + _cv;return isValid(outKeypoint) && p[2] > 0;
}

其中加畸变函数如下:

//将传入的归一化相机坐标系的三维点按照公式加畸变
template<typename DERIVED_Y>
void EquidistantDistortion::distort(const Eigen::MatrixBase<DERIVED_Y> & yconst) const {EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE_OR_DYNAMIC(Eigen::MatrixBase<DERIVED_Y>, 2);Eigen::MatrixBase<DERIVED_Y> & y = const_cast<Eigen::MatrixBase<DERIVED_Y> &>(yconst);y.derived().resize(2);double r, theta, theta2, theta4, theta6, theta8, thetad, scaling;r = sqrt(y[0] * y[0] + y[1] * y[1]);theta = atan(r);theta2 = theta * theta;theta4 = theta2 * theta2;theta6 = theta4 * theta2;theta8 = theta4 * theta4;thetad = theta* (1 + _k1 * theta2 + _k2 * theta4 + _k3 * theta6 + _k4 * theta8);scaling = (r > 1e-8) ? thetad / r : 1.0;y[0] *= scaling;y[1] *= scaling;
}

总结

这里有一点就是这里的重投影误差的均值很小, 主要的原因可以从上面的彩色图中看出, 重投影误差有正有负,均值会出现互相抵消的状态; 总体来讲是以(-0.000002, -0.000000)为中心, 方差分别是0.535272, 0.572115的正态分布;

所以说,均值不可信,方差大概率更靠谱一些: 如下面标定的很好的结果中:
reprojection error: [0.000000, 0.000001] ± [0.115878, 0.107625]
方差仅为0.1, 其对应的重投影误差的值也都偏小:

根据3*sigma原则, 要想让大部分的重投影误差小于1个像素, 那么其所对应的方差应该是小于0.33.

Kalibr源码学习(一): 重投影误差相关推荐

  1. Opencascade源码学习之模型数据——TKGeomBase模块文件介绍

    Opencascade源码学习之模型数据--TKGeomBase模块文件介绍 1.AdvApp2Var 2.AppCont 3.AppDef 4.AppParCurves 5.Approx 6.Bnd ...

  2. 【Android 源码学习】SharedPreferences 源码学习

    第一章:SharedPreferences 源码学习 文章目录 第一章:SharedPreferences 源码学习 Android SharedPreferences的缺陷 MMKV.Jetpack ...

  3. JDK11源码学习05 | HashMap类

    JDK11源码学习05 | HashMap类 JDK11源码学习01 | Map接口 JDK11源码学习02 | AbstractMap抽象类 JDK11源码学习03 | Serializable接口 ...

  4. AFNetworking源码学习 1

    简介: AFNetworking是iOS.macOS.watchOS和tvOS的一个令人愉快的网络库.它建立在基础URL加载系统之上,扩展了构建到Cocoa中的强大的网络高级抽象.它有一个模块化的体系 ...

  5. [阿里DIN] 从论文源码学习 之 embedding层如何自动更新

    [阿里DIN] 从论文源码学习 之 embedding层如何自动更新 文章目录 [阿里DIN] 从论文源码学习 之 embedding层如何自动更新 0x00 摘要 0x01 DIN源码 1.1 问题 ...

  6. Opencascade源码学习之模型算法_TKO模块文件介绍

    Opencascade源码学习之模型数据_TKO模块文件介绍 1.TKO 1.BOPAlgo 2.BOPDS 3.BOPTools 4.BRepAlgoAPI 5.IntTools 1.TKO 1.B ...

  7. PHP 源码学习之线程安全

    PHP 源码学习之线程安全 了解线程安全之前,我们先回顾几点基础知识点,是我们后面分析学习的基础. 变量的作用域 从作用域上来说,C语言可以定义4种不同的变量:全局变量,静态全局变量,局部变量,静态局 ...

  8. Hadoop HDFS源码学习之NameNode部分

    NameNode源码学习 文章目录 NameNode源码学习 一.文件系统目录树(第一关系) 2.1 INode相关类 2.2 快照特性的实现 2.3 FSEditLog类 2.4 FSImage类 ...

  9. Opencascade源码学习之模型数据

    Opencascade源码学习之模型数据 1.模型数据 2.几何工具 1.插值和拟合 1.分析一组点 2.基本插值和近似 3.2D 插值 4.3D 插值 5.2D 拟合 6.3D 拟合 7.曲面拟合 ...

最新文章

  1. CSS3 transform
  2. php开发者大会报名,2017 PHP 全球开发者大会
  3. React中的方法调用
  4. CodeForces - 1141CPolycarp Restores Permutation搜索+剪枝
  5. mysql命令(command)
  6. ae连续流动的线条_贡献 | AE片头制作绝对经验
  7. GC参考手册 —— GC 调优(基础篇)
  8. raster | R中的栅格操作符(下)[翻译]
  9. [Redis]Redis的数据类型
  10. 腾讯体育php面试题,腾讯php程序员面试题目及答案分享!
  11. 从零开始研发GPS接收机连载——3、用HackRF软件无线电平台作为GPS模拟器
  12. 写给非网工的CCNA教程(8)跨LAN的通信
  13. 计算n阶行列式的C语言实现
  14. 【成像】【4】产生连续波Terahertz辐射
  15. 递归算法与非递归算法的转化
  16. 用户体验设计师常用的21款工具和应用
  17. Vue微信网页微信支付
  18. [FPGA]1 MRCC与SRCC学习
  19. (FFMpeg学习笔记):FFmpeg下载、ffmpeg.exe与ffplay.exe的基本使用
  20. IT资源汇总全分享,学习资料免费领取处

热门文章

  1. bzoj 1664: [Usaco2006 Open]County Fair Events 参加节日庆祝(DP)
  2. bzoj 1821: [JSOI2010]Group 部落划分
  3. C++ STL bitset类常用函数的使用
  4. 集群间动态扩展和删除hdfs的datanode和hbase的regionserver
  5. docker安装常用组件(mysql,redis,postgres,rancher,Portainer,蝉道,JIRA,sonarqube,Confluence,pgadmin4,harbor)
  6. samba配置过程(附网络凭据的解决方法)
  7. PYUIC和PYRRC作为外部工具的配置
  8. [codewars] - int32 to IPv4 二进制十进制 ip地址转换
  9. \n 屏幕换行 源码换行
  10. 原来竟然还有这种局部变量!