opencv实现标定校准工作
参考https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html
标定图片需要使用标定板在不同位置、不同角度、不同姿态下拍摄。在前面的博客中进行推导时,我们分析得知至少要有3张图片,才能有唯一解。通常以10~20张为佳。
通常都会购买专门的标定板,如果精度要求不高,也可以自己打印。 网上也有别人已经拍好的照片,下面是下载链接:https://pan.baidu.com/s/1mhG3mHU
程序中就直接使用这些现成的图片来测试了,省事。一共有14张图片。
python的格式返回一个ret和corner。
这个我们已经见识过了。
如果patterWasFound是真,就会有连线。
如果是false
就没有连线。这个是又找了一张的测试图,内焦点也还不错。这里我觉得选内角点的时候最好全部选择,上面的(6,4)就是这么选择的,这因为zhe'g棋盘是我们选择的,我们当然是知道横竖有多少个角点了。
如果选择的不对是会报错的。少了会报错,ret直接就是False。
多给了虽然能画出来图,不过ret也还是false,这个结果还是有问题的。
函数:
objectponit我们只需要给世界坐标系的X,Y的坐标,因为Z=0。imagePoints就是像素坐标系中角点的坐标。flags里面是标定的一些算法,LM方法其实是一种非最小二乘法。
上一讲提出的问题我在这里其实还是有一定的疑问,就是k1,k2的初值是如何确定的?
其实我们可以不用知道未畸变的棋盘是什么样的,只用4个在一条直线上的点就可以了。因为棋盘在一条直线上的点的间距应该是一样的,就根据这一点列方程。
假设有上面4个点,Ai对应的点是(xi,yi),离主点的距离是ri,这些我们都可以知道。那么我们就可以列方程了,每三个点可以一个方程:
关于y也可以列方程,不过那个和x的系数一样,因为都是ri^2的差或者说和x列的方程是齐次的。这样就可以解出k1,k2。不过可能每张图我们可能不止四个点,而且不止一张图,这个时候我们就用最小二乘法求k1,k2了。这里我们解决了如何求k1,k2初值的问题,后面还是要进行极大似然估计,但是这个初值对于LM算法还是比较重要的。我们如果要标定还需要考虑抑恶个问题,就是棋盘坐标在世界坐标系的坐标,Z坐标是0我们知道了。其它坐标我们如何确定呢?我们就随便定咯,这里我们知道的是棋盘的方格是10*10的。
那么:
mgrid是meshgrid的缩写。是生成坐标网格,输入的参数是坐标范围,得到的网格的点坐标。
这个ret据说就是极大似然函数的那个最小值。
mtx是内参矩阵,我们看到c=0。
这个旋转用的的旋转向量的形式,因为用了14张图,所以有14组外参数。
这个是把世界坐标系投影到像素坐标系,因为其实是求解一个非线性最小二乘,误差函数不可能是0的,所以肯定会有一些偏差的。
这个计算出来之后,可以计算一下平均平方误差,cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2),这个误差也可以叫做重投影误差,这个是评价相机标定结果的一个指标。
结果是0.033,这个结果看起来还是不错的。上面是完成标定工作了,并且还对标定结果作出了评价。据官方文档说,这个ret也可以是一个指标。
RMS就是
不过还是有点不太一样,但是差的很少,一个是0.16,一个是0.18。
下面介绍一个模块,叫做glob。
需要注意的是这个不会迭代查找,它不会去子文件夹中继续查找。
这种通配符的用法就是*代表任意个(很多个)任意字符而?代表一个字符。
我没有觉得iglob有什么存在的必要。关于查找文件,我们在学习python的时候就已经将结果如何去查找某一个类型的文件,后来我们又学了正则表达式,就可以不仅仅是查找某种类型的文件了,那个时候我们学的是可以递归查找的。
opencv校正图像
undistortion就是去畸变的意思,distortion是畸变的意思。
上面我们已经完成了标定工作,但是没有对图片进行校正,下面我们要做的事校正工作。opencv有两个函数可以校正图像。我们下面都会看到,在那之前,我们可以用cv2.getOptimalNew优化一下相机内参数矩阵,我觉得这一步做的就是把畸变的形式和相机内矩阵的形式结合一下,得出一个新的相机内参矩阵。
也就是调整alhpa的值主要还是为了调整显示范围,也就是ROI。
cameraMatrix是上面张氏标定得出的相机内参矩阵,distCoeffs是畸变向量,ImageSize是原始输入图像的大小,alpha就是调整输出图像区域的一个值,如果为0,就只会保留校正后的像素,如果为1,就会保留所有像素,就会出现那些黑色区域。validPixROi是校正后比较好的区域的一个矩形参数。centerPrincipalPoint,这个参数说的是是否要把主点移动到图像平面中心。
undistort是校正的函数。
其实我们上面的失真向量里面的值都比较小。
校正的时候为什么需要用到相机内参矩阵呢?因为计算r的时候是计算和主点的距离,所以需要用到内参数的u0和v0参数。我们来试一试。
其实这两个矩阵差别不大,主要还是我们的畸变向量的值都比较小,这个主点也不是正好在中间。
这个应该能看懂什么意思。
由于我们的畸变向量的参数都很小。基本上看不出来什么区别。
如果不用这个getOptimalNewCameraMatrix呢?
还是因为我这个图片基本上畸变很小,看不出来。
试试第二种方式吧。
用getOptimalNewCameraMatrix:
不用:
可以看出区别,如果不用,右边就多出来一列黑色的地带。
mapx,mapy里面存的是原来的480*640个像素校正之后的位置。
这个5不能随便改动。
张正友标定和校正就到这里了。
其实github速度也不慢的:
github地址:https://github.com/lcl1026504480/opencv-python-turorial
码云地址:https://gitee.com/lcl1026504480/opencv-python-turorial