三维重建-相机标定

相机标定就是设置各种参数(即投影公式中的项目)的过程。

相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。

相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上)。

相机标定的输出:摄像机的内参、外参系数。

过程:

  1. 准备标定图片
  2. 对每一张标定图片,提取角点信息(先说明角点数量,再利用函数findchessboardcorners)

FindChessboardCorners是opencv的一个函数,可以用来寻找棋盘图的内角点位置。

    int cvFindChessboardCorners( const void* image, CvSize pattern_size, CvPoint2D32f* corners, int* corner_count=NULL, int flags=CV_CALIB_CB_ADAPTIVE_THRESH );

    

Image:

输入的棋盘图,必须是8位的灰度或者彩色图像。

pattern_size:

棋盘图中每行和每列角点的个数。

Corners:

检测到的角点

corner_count:

输出,角点的个数。如果不是NULL,函数将检测到的角点的个数存储于此变量。

Flags:

各种操作标志,可以是0或者下面值的组合:

CV_CALIB_CB_ADAPTIVE_THRESH -使用自适应阈值(通过平均图像亮度计算得到)将图像转换为黑白图,而不是一个固定的阈值。

CV_CALIB_CB_NORMALIZE_IMAGE -在利用固定阈值或者自适应的阈值进行二值化之前,先使用cvNormalizeHist来均衡化图像亮度。

CV_CALIB_CB_FILTER_QUADS -使用其他的准则(如轮廓面积,周长,方形形状)来去除在轮廓检测阶段检测到的错误方块。

    注意:

  • pattern_size参数传递内点数,8*8的棋盘只有7*7内点。
  1. 对每一张标定图片,进一步提取亚像素角点信息

cv::goodFeaturesToTrack()提取到的角点只能达到像素级别,在很多情况下并不能满足实际的需求,这时,我们则需要使用cv::cornerSubPix()对检测到的角点作进一步的优化计算,可使角点的精度达到亚像素级别。

具体调用形式如下:

 void cv::cornerSubPix(
        cv::InputArray image, // 输入图像
        cv::InputOutputArray corners, // 角点(既作为输入也作为输出)
        cv::Size winSize, // 区域大小为 NXN; N=(winSize*2+1)
        cv::Size zeroZone, // 类似于winSize,但是总具有较小的范围,Size(-1,-1)表示忽略
        cv::TermCriteria criteria // 停止优化的标准
    );

    

第一个参数是输入图像,和cv::goodFeaturesToTrack()中的输入图像是同一个图像。
第二个参数是检测到的角点,即是输入也是输出。

第三个参数是计算亚像素角点时考虑的区域的大小,大小为NXN; N=(winSize*2+1)。

第四个参数作用类似于winSize,但是总是具有较小的范围,通常忽略(即Size(-1, -1))。

第五个参数用于表示计算亚像素时停止迭代的标准,可选的值有cv::TermCriteria::MAX_ITER 、cv::TermCriteria::EPS(可以是两者其一,或两者均选),前者表示迭代次数达到了最大次数时停止,后者表示角点位置变化的最小值已经达到最小时停止迭代。二者均使用cv::TermCriteria()构造函数进行指定。


   

  1. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)
cvDrawChessboardCorners(
IntPtr image,
Size patternSize,
IntPtr corners,
int count,
int patternWasFound
)
image——输入的目标图像,必须是8位彩色图像
patternSize——内心的角落每棋盘的行和列的数目角,也就是标定板角点的行数和列数
corners——检测到的角阵列,也就是用cvFindCornerSubPix函数检测到的角点的坐标(cvFindCornerSubPix的第二个参数)
count——角数,每张标定图所有角点的数目
patternWasFound——指出是否已找到所有的角点(该值为0表示不能找到所有的角点,不为0则表示能够找出所有的角点),该参数值由cvFindChessboardCorners函数的返回值给出
  1. 相机标定

    

double cv::calibrateCamera (
InputArrayOfArrays objectPoints,//世界坐标系中的点。在使用时,应该输入vector< vector< Point3f > >。
InputArrayOfArrays imagePoints,//其对应的图像点。和objectPoints一样,应该输入vector< vector< Point2f > >型的变量。
Size imageSize,//图像的大小,在计算相机的内参数和畸变矩阵需要用到;
InputOutputArray cameraMatrix,//内参数矩阵。输入一个Mat cameraMatrix即可。
InputOutputArray distCoeffs,//畸变矩阵。输入一个Mat distCoeffs即可。
OutputArrayOfArrays rvecs,//旋转向量;应该输入一个Mat的vector,即vector< Mat > rvecs因为每个vector< Point3f >会得到一个rvecs。
OutputArrayOfArrays tvecs,//位移向量;和rvecs一样,也应该为vector tvecs。
int flags = 0,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON)
)

 

o:

stdDeviationsIntrinsics :内参数的输出向量。输出顺序为: (fx,fy,cx,cy,k1,k2,p1,p2,k3,k4,k5,k6,s1,s2,s3,s4,τx,τy) ,如果不估计其中某一个参数,值等于0

stdDeviationsExtrinsics :外参数的输出向量。输出顺序: (R1,T1,…,RM,TM) ,M是标定图片的个数, Ri,Ti 是1x3的向量 。

perViewErrors 每个标定图片的重投影均方根误差的输出向量。

criteria: 迭代优化算法的终止准则

flags :标定函数是所采用的模型(重点)”。
可输入如下某个或者某几个参数:

CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix,作为初始值输入,然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵。

CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点,光轴点将保持为图像的中心点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,保持为输入的值。

CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy的实际输入值将会被忽略,只有fx/fy的比值被计算和使用。

CV_CALIB_ZERO_TANGENT_DIST:切向畸变系数(P1,P2)被设置为零并保持为零。

CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变系数在优化中保持不变。如果设置了CV_CALIB_USE_INTRINSIC_GUESS参数,就从提供的畸变系数矩阵中得到。否则,设置为0。

CV_CALIB_RATIONAL_MODEL(理想模型):启用畸变k4,k5,k6三个畸变参数。使标定函数使用有理模型,返回8个系数。如果没有设置,则只计算其它5个畸变参数。

CALIB_THIN_PRISM_MODEL (薄棱镜畸变模型):启用畸变系数S1、S2、S3和S4。使标定函数使用薄棱柱模型并返回12个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_S1_S2_S3_S4 :优化过程中不改变薄棱镜畸变系数S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess设置,使用提供的畸变系数矩阵中的值。否则,设置为0。

CALIB_TILTED_MODEL (倾斜模型):启用畸变系数tauX and tauY。标定函数使用倾斜传感器模型并返回14个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_TAUX_TAUY :在优化过程中,倾斜传感器模型的系数不被改变。如果cv_calib_use_intrinsic_guess设置,从提供的畸变系数矩阵中得到。否则,设置为0。

函数返回
重投影的总的均方根误差。

 

  1. 对标定结果进行评价
  2. 查看标定效果——利用标定结果对棋盘图进行矫正

void initUndistortRectifyMap(InputArray cameraMatrix,//输入相机矩阵
 InputArray distCoeffs, //畸变矩阵  失真系数(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6]])为4、5或8个元素的输入矢量。如果矢量为 NULL/空,则假设为零失真系数。
InputArray R,//对象空间(3x3矩阵)中的可选校正转换
 InputArray newCameraMatrix,//新相机矩阵A'=\vecthreethree{f_x'}{0}{c_x'}{0}{f_y'}{c_y'}{0}{0}{1}。
 Size size, //未畸变的图像大小
int m1type, 第一个输出地图的类型,可以CV_32FC1CV_16SC2。
OutputArray map1, //对应x方向映射
OutputArray map2)//对应y方向映射

 


dst(x, y) = src( mapx(x, y), mapy(x, y) )


void
remap( InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar() )

InputArray类型的src,输入图像,填Mat类的对象即可,且需要为单通道8位或者浮点型的图像;
2)OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和原图片有一样的尺寸和类型。
3)InputArray类型的map1,它有两种可能的表示对象;
表示点(x, y)的第一个映射;
表示CV_16S, CV_32FC1或CV_32FC2类型的X值;
4)InputArray类型的map2,同样,它有两种可能的表示对象,而且它会根据map1来确定表示那种对象;
若map1表示点(x, y)时,这个参数不代表任何值;
表示CV_16UC1,CV_32FC1类型的Y值(第二个值);
5)int类型的interpolation,插值方式,之前的resize()函数中有讲到,需要注意,resize()函数中提到的INTER_AREA插值方式在这里是不支持的,所以可选的插值方式如下:
INTER_NEAREST:最近邻插值;
INTER_LINEAR:双线性插值(默认值);
INTER_CUBIC:双三次样条插值(逾4x4像素邻域内的双三次插值);
INTER_LANCZOS4:Lanczos插值(逾8x8像素邻域的Lanczos插值)
6)int类型的borderMode,边界模式,有默认值BORDER_CONSTANT,表示目标图像中“离群点”的像素不会被此函数修改;
7)const Scalar&类型的 borderValue,当有常数边界时使用的值,默认值为0;

 

 

后续:相机姿态还原;用标定相机实现三维重建,计算立体图像深度

posted @ 2021-05-13 16:02  KAVEI  阅读(859)  评论(0编辑  收藏  举报