OpenCV 校正过程中,calibrateCamera函数的ret和重投影误差的分析

OpenCV对相机进行校正的过程中,校正返回值retval和重投影误差的计算公式表示和分析。

  • OpenCV 校正过程中,calibrateCamera函数的ret和重投影误差的分析
  • 一、前言
  • 二、calibrateCamera( )函数分析
  • 三、projectPoints()函数分析
  • 四、具体实验和算法对比
  • 五、参考附录

一、前言

本文主要是讲解Python利用OpenCV进行相机校正过程中,几个重点参数的分析。标定的过程就不再一一赘述了,很多博客和网站都在讲解怎么进行标定。本文章主要分析,标定过程中的误差计算公式,和对calibrateCamera( )projectPoints( ) 两个函数得到的误差不同的原因进行分析。

本文重点分析重投影误差的计算方式。准确描述OpenCV自带的函数,和各个文档,教程里面所说的重投影误差计算的流程进行分析和对比,描述官方的重投影误差和教程实现重投影误差的差别。 我认为OpenCV官方和其他大多数地方的教程,给的重新计算重投影误差的结果,其计算的表达式,也是不准确的。

正确的方法是相机校正的方法输出的重投影误差ret。使用的是RMS均方根误差。另外教程和博客,给的是MSE均方误差

二、calibrateCamera( )函数分析

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(x_world, y_voltage, (224, 224), None, None)

这里,ret表示的是重投影误差;mtx是相机的内参矩阵;dist表述的相机畸变参数;rvecs表示标定棋盘格世界坐标系到相机坐标系的旋转参数:rotation vectors,需要进行罗德里格斯转换;tvecs表示translation vectors,主要是平移参数。
其他几个参数,OpenCV的官网都进行了仔细分析,在这个网站上去仔细的看,每个参数对应的数学公式。https://docs.opencv.org/3.4/dc/dbb/tutorial_py_calibration.html

主要分析ret这个值,也就是retval,这个是重投影误差。

import numpy as np
import cv2 as cv2
import glob
# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob('*.jpg')
for fname in images:img = cv2.imread(fname)gray = cv2.cvtColor(img, cv.COLOR_BGR2GRAY)# Find the chess board cornersret, corners = cv2.findChessboardCorners(gray, (7,6), None)# If found, add object points, image points (after refining them)if ret == True:objpoints.append(objp)corners2 = cv2.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)imgpoints.append(corners)# Draw and display the cornerscv2.drawChessboardCorners(img, (7,6), corners2, ret)cv2.imshow('img', img)cv2.waitKey(500)
cv2.destroyAllWindows()# Calibration, Now that we have our object points and image points, we are ready to go for
# calibration. We can use the function, cv.calibrateCamera() which returns the camera matrix,
# distortion coefficients, rotation and translation vectors etc.
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
print("ret:", ret)
print("Camera matrix : \n")
print(mtx)
print("dist : \n")
print(dist)
print("rvecs : \n")
print(rvecs)
print("tvecs : \n")
print(tvecs)

其中,这个ret也就是retval,表示的是重投影误差,是算法自动返回的,其表达式为:
Reppointsx′=k[R∣T]X.Reppoints \ x'= k [R|T]X \,. Reppoints x′=k[R∣T]X.
其中,Reppoints x’是[Nx2],表示的图像像素平面的2维坐标点,X是[Nx3]表示的3D世界坐标系下的3维坐标点。也就是棋盘格所构造的3D坐标系的大小。
ret=repro_error=∣∣x′−x∣∣22total_points=∣∣x′−x∣∣2total_pointsret =repro \_error =\sqrt{\frac{ ||x'-x||_{2}^2}{total\_points}}={\frac{ ||x'-x||_{2}}{\sqrt{total\_points}}} ret=repro_error=total_points∣∣x′−x∣∣22​​​=total_points​∣∣x′−x∣∣2​​
这就是cv2.calibrateCamera()返回的值中,ret也就是重投影误差的计算公式。求的是RMS均方根误差,对所有的点,求二范数,然后求平均再开根号,得到RMS误差。

其中,二范数用的是:cv2.norm(data1, data2, cv2.NORM_L2)计算得到:
norm(x,x′)=∣∣x−x′∣∣2=∣∣x−x′∣∣22=(x1−x1′)2+...+(xn−xn′)2norm(x,x')=||x-x'||_2=\sqrt{|| x-x'||_{2}^2}=\sqrt{(x_1-x'_1)^2+...+(x_n-x'_n)^2} norm(x,x′)=∣∣x−x′∣∣2​=∣∣x−x′∣∣22​​=(x1​−x1′​)2+...+(xn​−xn′​)2​

三、projectPoints()函数分析

imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
  1. 原始的计算方法,大多数教程和博客,以及OpenCV文档,给出的重投影误差计算方法。

对相机标定得到的参数,带回原始的投影公式,重新计算从3D世界坐标系投影到2D图像平面坐标系的新的图像像素值,然后计算重投影误差。其中,objpoints表示世界坐标系的坐标,也就是棋盘格的坐标系,rvecs,tvecs,mtx,dist就是相机标定得到的那些参数。

mean_error = 0
for i in range(len(objpoints)):imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)mean_error += error
print( "total error: {}".format(mean_error/len(objpoints)) )

这里计算误差的方式,我们将得到的值和上面相机校正返回的值ret,不一样。因此,需要考虑,将这个官方给定的计算方式,进行重新计算。如果你不在意准确的值,就不用进行优化。

本文是准确的探索,重投影误差的计算方式。按照上面的算法流程,我们可以得到以下的计算重投影误差的公式:
repro_error=∣∣x′−x∣∣2total_pointsrepro\_error={\frac{ {||x'-x||_{2}}}{total\_points}} repro_error=total_points∣∣x′−x∣∣2​​
很明显,均方误差的计算,按照上述的表达式,是不准确的。误差开根号,然后除以点数多少,只能说是每个点的平均欧式误差。MSE误差。

  1. 校正之后的重投影误差计算方法或者代码。

因此,需要求得准确的均方根误差,我们应该是先求平方,求平均,在开根号。因此,需要将代码改为:

mean_error = 0
for i in range(len(objpoints)):imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)total_error += error*error
print( "rms error: {}".format(np.sqrt(total_error/(len(objpoints)*len(imgpoints2))))

上述的流程才是:
repro_error=(∣∣x−x′∣∣22total_pointsrepro\_error=\sqrt{\frac{(||x-x'||_2^2}{total\_points}} repro_error=total_points(∣∣x−x′∣∣22​​​

  • 与我们的cv2.calibrateCamera()返回的值是一样的。

四、具体实验和算法对比

  1. 相机校正返回的值

  2. 函数的返回值
    其中的返回的误差项,真实值:ret 为 ret: 0.008683449668964258

  3. 使用大多数教程和博客的算法来计算的误差值,是有差别的:

  4. 参考代码给出的输出误差结果
    total error: 0.0018512112930950482

  5. 按照自己修正的代码,来进行重投影误差计算:

    输出的重投影误差为:total error: 0.008683459017710442

因此,和我们相机校正输出的=重投影误差是一样的。

五、参考附录

下面是原始C++函数,计算重投影误差的流程,我上面的公式就是按照这个算法来解释的:

static double computeReprojectionErrors(const vector<vector<Point3f> >& objectPoints,const vector<vector<Point2f> >& imagePoints,const vector<Mat>& rvecs, const vector<Mat>& tvecs,const Mat& cameraMatrix, const Mat& distCoeffs,vector<float>& perViewErrors )
{vector<Point2f> imagePoints2;int i, totalPoints = 0;double totalErr = 0, err;perViewErrors.resize(objectPoints.size());for( i = 0; i < (int)objectPoints.size(); i++ ){projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i],cameraMatrix, distCoeffs, imagePoints2);err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2);int n = (int)objectPoints[i].size();perViewErrors[i] = (float)std::sqrt(err*err/n);totalErr += err*err;totalPoints += n;}return std::sqrt(totalErr/totalPoints);
}

需要准确描述如何计算重投影误差的同学,需要仔细修改你的代码,因为RMS误差来描述的,是需要求平方差,然后求平均,再求平方根。

正确的方法是相机校正的方法输出的重投影误差,而不是大多数,包括OpenCV官方,给出的,都是不一样的,或者是不准确的重投影误差的计算函数。

参考网站:

  1. https://stackoverflow.com/questions/29628445/meaning-of-the-retval-return-value-in-cv2-calibratecamera
  2. https://docs.opencv.org/master/dc/dbb/tutorial_py_calibration.html

如果有用,记得点赞

OpenCV 相机校正过程中,calibrateCamera函数projectPoints函数的重投影误差的分析相关推荐

  1. BA_重投影误差e对于相机的位姿ξ和对空间点的坐标P的雅可比矩阵的推导

    1. 基本思路 重投影误差表示为e, 相机的位姿表示为ξ (或者表示为T=(R,t)), 空间点表示为P, 则空间点投影到相机坐标系下的空间坐标点的相机坐标表示为P'=[X', Y', Z'], 则 ...

  2. 相机标定过程中的注意事项

    相机标定笔记 结构光测量系统光栅图像周期数(N)的最佳取值与相机的视场和分辨率有关,系统配置不同时N的最佳取值也不同,可通过实验进行确定.[基于数字光栅投影的结构光三维测量技术与系统研究_李中伟] 圆 ...

  3. python 正方形去畸变_opencv 角点检测+相机标定+去畸变+重投影误差计算

    https://blog.csdn.net/u010128736/article/details/52875137 https://blog.csdn.net/h532600610/article/d ...

  4. 在 VSLAM 的后端优化中的重投影误差的雅可比计算详细推导

    对于相机位姿的变换可以通过旋转矩阵或者四元数进行表示,对于旋转矩阵的定义满足: R{∣R∣=1RRT=IR \begin{cases} |R| = 1 \\ RR^T = I\\ \end{cases ...

  5. 使用Ceres优化重投影误差进行相机标定(仅个人笔记)

    重投影误差的类 class ProjectErrorCostFunctionPinehole { public:ProjectErrorCostFunctionPinehole(const Eigen ...

  6. OpenCV 相机校正

    1. 相机标定 根据张正友校正算法,利用棋盘格数据校正对车载相机进行校正,计算其内参矩阵,外参矩阵和畸变系数. 标定的流程是: 准备棋盘格数据,即用于标定的图片 对每一张图片提取角点信息 在棋盘上绘制 ...

  7. OpenCV求(图像)矩阵中最大值,最小值函数minMaxLoc() vs minMaxIdx()

    转载请注明出处. 文章地址:https://blog.csdn.net/duiwangxiaomi/article/details/97937706?spm=1001.2014.3001.5501   ...

  8. 关于ENVI5.1/5.3 软件在辐射定标+大气校正过程中出现的基础问题

    本文也发表于知乎:https://zhuanlan.zhihu.com/p/142523241 关于辐射定标后影像全黑问题 出现问题的原因是: 电脑运行内存不是很多 出现这个问题的可能大都是4G运存 ...

  9. FLAASH大气校正过程中内存资源不足解决方法

    数据介绍及数据其他操作详见此博客 ENVI5.3.1使用Landsat 8影像进行预处理及分析实例操作 20220212更新: 注意FLAASH大气校正的海拔,如果设置为0也是有可能报错的(我在做海面 ...

最新文章

  1. 百度开设「黄埔学院」,革新者来
  2. 南农Nature Microbiology一作顾少华:我与铁载体的这5年
  3. 现成Android 5.0系统源代码
  4. c语言三目运算程序,有C语言中三目运算符 联系到的 程序打trace
  5. uic计算机课程表,美国UIC大学研究生毕业率能达到多少?申请条件、专业课程汇总...
  6. python爬取行业数据_用Python进行Web爬取数据
  7. This dependency was not found: * !!vue-style-loader!css-loader?……解决方案
  8. 修改Flume-NG的hdfs sink解析时间戳源码大幅提高写入性能
  9. 判断拐点的条件_拐点的定义
  10. ios重签名工具ios-app-signer的使用
  11. e4a 安卓获取ROOT权限的方法思路 转载
  12. 2021爱分析·云计算厂商全景报告
  13. FRP 内网穿透下载配置
  14. oracle重做日志详解,oracle数据文件、控制文件、重做日志文件详解
  15. LightOJ-1253 Misere Nim
  16. Contiki学习平台推荐
  17. Android自动化测试框架uiautomator2详解
  18. [立此存照][转载]昆明小区道路改公用道路新闻两篇
  19. 建议收藏!总结了 42 种前端常用布局方案
  20. 信息系统项目管理师就业前景分析

热门文章

  1. 2022-2028年中国PET基膜行业市场发展规模及市场分析预测报告
  2. HJ86 求最大连续bit数
  3. python实现全角和半角互相转换
  4. Python多线程(3)——Queue模块
  5. LeetCode简单题之数组拆分 I
  6. 自动驾驶传感器比较:激光雷达(LiDAR) vs. 雷达(RADAR)
  7. 先进一站式IP及定制
  8. TensorFlow简单线性回归
  9. 如何使用Nsight Compute?
  10. js ajax 递归,javascript ajax循环请求/ 长轮询终极解决办法——递归