Opencv sample单目、双目标定(opencv 学习笔记)——转发

来源:互联网 发布:mac远程桌面win10 编辑:程序博客网 时间:2024/05/16 08:17
  1. 为什么要标定

     首先我们要对摄像头做标定,具体的公式推导在《learning opencv3.0》中有详细的解释。

    标定的原因:

    1. 标定的目的是为了消除畸变以及得到内外参数矩阵,内参数矩阵可以理解为焦距相关,它是一个从平面到像素的转换,焦距不变它就不变,所以确定以后就可以重复使用,而外参数矩阵反映的是摄像机坐标系与世界坐标系的转换,至于畸变参数,一般也包含在内参数矩阵中。从作用上来看,内参数矩阵是为了得到镜头的信息,并消除畸变,使得到的图像更为准确,外参数矩阵是为了得到相机相对于世界坐标的联系,是为了最终的测距。
    2. 我们知道双目测距的时候两个相机需要平行放置,但事实上这个是很难做到的,所以就需要立体校正得到两个相机之间的旋转平移矩阵,也就是外参数矩阵。

      (校准前)(校准后)

  2. sample单目标定

    直接用opencv里面的sample,在opencv/sources/sample/cpp里面,有个calibration.cpp的文件,这是单目的标定,是可以直接编译使用的,这里要注意几点:

    1. 棋盘也就是标定板是要预先打印好的,你打印的棋盘的样式决定了后面参数的填写,具体要求也不是很严谨,清晰能用就行。之所用棋盘是因为他检测角点很方便。
    2. 一般设置为这个样子:-w 6 -h 8 -s 2 -n 10 -o camera.yml -op -oe [<list_of_views.txt>] ,这是几个重要参数的含义:
    3. -w <board_width> # 图片某一维方向上的交点个数
    4. -h <board_height> # 图片另一维上的交点个数
    5. [-n <number_of_frames>] # 标定用的图片帧数

      最终得到的yml文件,就是单目标定的参数矩阵,之后使用它就可以得到校正后的图像啦。

然后就是双目标定了,同样的地方,找到stereo_calib.cpp,这个参数比较简单,只要确定长、宽和输入的一个xml文件(在之前的文件夹里面),这个文件是为了读取图片用的,你需要自己用固定好的双目摄像头13对棋盘图片,命名为 left01,right01......这样 一系列的名字,另外,最简单的方法就是把自己拍的照片放到相应的工程下,以及stereo开头的那个xml文件也复制过去这个程序代码 并不复杂,可以稍微研究一下,工程向的代码确实严谨,各种情况都考虑到了。

注意事项:

  1. 长宽一定要写对
  2. 核心函数 static void StereoCalib(const vector<string>& imagelist, Size boardSize, bool useCalibrated=true, bool showRectified=true),注意搞清楚参数的意义,因为我是用的单目标定好的摄像头拍摄的图片,不需要再校正了,所以第三个参数要用false,这样最后的结果才能看。
  3. 另外注意到计算rms误差的时候,结束条件的几个参数是可以调整的。

double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],cameraMatrix[0], distCoeffs[0],cameraMatrix[1],distCoeffs[1],imageSize,R,T,E, ,TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,100,1e-5),CV_CALIB_FIX_ASPECT_RATIO+CV_CALIB_ZERO_TANGENT_DIST+CV_CALIB_SAME_FOCAL_LENGTH+CV_CALIB_RATIONAL_MODEL+CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5)。

这个函数计算了两个摄像头进行立体像对之间的转换关系。如果你有一个立体相机的相对位置,并且两个摄像头的方向是固定的,以及你计算了物体相对于第一照相机和第二照相机的姿态,(R1,T1)和(R2,T2),各自(这个可以通过solvepnp()做到)通过这些姿态确定。你只需要知道第二相机相对于第一相机的位置和方向。

除了立体的相关信息,该函数也可以两个相机的每一个做一个完整的校准。然而,由于在输入数据中的高维的参数空间和噪声的,可能偏离正确值。如果每个单独的相机内参数可以被精确估计(例如,使用calibratecamera()),建议这样做,然后在本征参数计算之中使CV_CALIB_FIX_INTRINSIC的功能。否则,如果一旦计算出所有的参数,它将会合理的限制某些参数,例如,传CV_CALIB_SAME_FOCAL_LENGTH and CV_CALIB_ZERO_TANGENT_DIST,这通常是一个合理的假设。

3、标定之后就是对图像的校正:

void loadCameraParams(Mat &cameraMatrix, Mat &distCoeffs)

{

FileStorage fs("camera.yml", FileStorage::READ);//这个名字就是你之前校正得到的yml文件

 

fs["camera_matrix"] >> cameraMatrix;

fs["distortion_coefficients"] >> distCoeffs;

}

 

Mat calibrator(Mat &view)//需要校正处理的图片

{

vector<string> imageList;

static bool bLoadCameraParams = false;

static Mat cameraMatrix, distCoeffs, map1, map2;

Mat rview;

Size imageSize, newImageSize;

 

if (!view.data)

return Mat();

 

imageSize.width = view.cols;

imageSize.height = view.rows;

 

newImageSize.width = imageSize.width;

newImageSize.height = imageSize.height;

 

if (bLoadCameraParams == false)

{

loadCameraParams(cameraMatrix, distCoeffs);

bLoadCameraParams = true;

initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),

getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, newImageSize, 0), newImageSize, CV_16SC2, map1, map2);

}

 

//undistort( view, rview, cameraMatrix, distCoeffs, cameraMatrix );

remap(view, rview, map1, map2, INTER_LINEAR);

 

imshow("左图", rview);

//int c = 0xff & waitKey();

 

rview.copyTo(view);

 

return view;

}

这样最后就可以得到校正后消除畸变的图片。

  1. sample 双目匹配

    opencv中提供了很多的立体匹配算法,类似于局部的BM,全局的SGBM等等,这些算法的比较大概是,速度越快的效果越差,如果不是很追究时效性,并且你的校正做的不是很好的话..推荐使用SGBM,算法的具体原理大家可以去百度,不难。这里我想提一下的是为什么做立体匹配有用,原因就是极线约束,这也是个很重要的概念,理解起来并不难,左摄像机上的一个点,对应三维空间上的一个点,当我们要找这个点在右边的投影点时,有必要把这个图像都遍历一边么,当然不用啦!

     

    最后,怎么在opencv里面实现呢?sample..找到stereo_match.cpp这个文件,命令行设置为:left01.jpg right02.jpg --algorithm=hh --blocksize=5 --max-disparity=256 --scale=1.0 --no-display -i intrinsics.yml -e extrinsics.yml -o disparity.jpg同意给几个建议:

1.参数的意义:

-max-disparity 是最大视差,可以理解为对比度,越大整个视差的range也就越大,这个要求是16的倍数

--blocksize 一般设置为5-29之间的奇数,应该是局部算法的窗口大小。

另,注意带上参数-i intrinsics.yml -e extrinsics.yml,毕竟咱有校正参数.

2.后面有两行代码:

reprojectImageTo3D(disp, xyz, Q, true);

saveXYZ(point_cloud_filename, xyz);

这个就是得到图片的三维坐标,Z也就是我们最终要求的深度啦。

行和行是对应的么? 之前我们说过,双目校正的目的就是为了得到两个平行的摄像头,所以当程序运行完毕以后,它会把两幅图像显示出来,并作出一系列的平行线,这样你会看到线上的点大致是呈对应关系,左边的角点对应右边的交点,所以,经过匹配和校正后,是对应的。

0 0
原创粉丝点击