双目摄像头标定

该博客主要介绍了双目摄像头的相关操作。包括读取双目摄像头图像,设置分辨率并分割左右图像;对左右摄像头分别进行单目标定与矫正,利用MATLAB导出相机参数,在opencv中获得矫正图像;还利用MATLAB进行双目摄像头标定并导出参数。最后提出后续优化方向,如用opencv实现标定、优化速度、用Qt做GUI界面。

一.读取双目摄像头图像
我们在淘宝上买的双目摄像头:
摄像头
分辨率参数:
分辨率参数

这里我在程序中设置3040 * 1080分辨率(设置3040 * 1520时,实际上只有2176 * 1520,不知道是什么原因)
这种摄像头输出的是左右摄像头拼接在一起的图像,可以用ROI将其分割:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#define FRAME_WIDTH 3040
#define FRAME_HEIGHT 1080

using namespace cv;
using namespace std;
int main()
{
    VideoCapture cap(1);
    cap.set(CV_CAP_PROP_FRAME_WIDTH,FRAME_WIDTH);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT,FRAME_HEIGHT);
//  cout<<"Resolution:<"<<cap.get(CV_CAP_PROP_FRAME_WIDTH)<<","<<cap.get(CV_CAP_PROP_FRAME_HEIGHT)<<">\n";
    namedWindow("camera",CV_WINDOW_NORMAL);
    namedWindow("left_img",CV_WINDOW_NORMAL);
    namedWindow("right_img",CV_WINDOW_NORMAL);
    resizeWindow("camera",1020,760);

    while(1)
    {
        Mat img;
        Mat left_img,right_img;//左右摄像图图像
        cap >> img;

        left_img = img(Rect(0,0,FRAME_WIDTH>>1,FRAME_HEIGHT));
        right_img = img(Rect(FRAME_WIDTH>>1,0,FRAME_WIDTH>>1,FRAME_HEIGHT));
        imshow("camera",img);
        imshow("left_img",left_img);
        imshow("right_img",right_img);
        //按下Esc退出
        if(waitKey(30)==27)
            return 0;
    }
}

效果:
效果二.单目摄像头标定与矫正
分别对双目摄像头的左右摄像头做标定。
利用以下代码实现按下空格键时保存左右摄像头的图像:
(在可执行文件的目录下新建left_camera和right_camera两个文件夹,图像会保存到这两个文件夹里)

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#define FRAME_WIDTH 3040
#define FRAME_HEIGHT 1080

using namespace cv;
using namespace std;
int main()
{
    VideoCapture cap(1);
    cap.set(CV_CAP_PROP_FRAME_WIDTH,FRAME_WIDTH);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT,FRAME_HEIGHT);
    cout<<"Resolution:<"<<cap.get(CV_CAP_PROP_FRAME_WIDTH)<<","<<cap.get(CV_CAP_PROP_FRAME_HEIGHT)<<">\n";
    namedWindow("camera",CV_WINDOW_NORMAL);
    namedWindow("left_img",CV_WINDOW_NORMAL);
    namedWindow("right_img",CV_WINDOW_NORMAL);
    resizeWindow("camera",1020,540);

    while(1)
    {
        Mat img;
        Mat left_img,right_img;//左右摄像图图像
        cap >> img;

        left_img = img(Rect(0,0,FRAME_WIDTH>>1,FRAME_HEIGHT));
        right_img = img(Rect(FRAME_WIDTH>>1,0,FRAME_WIDTH>>1,FRAME_HEIGHT));
        imshow("camera",img);
        imshow("left_img",left_img);
        imshow("right_img",right_img);

        int key=waitKey(30);
        if(key==27)//按下Esc退出
            return 0;
        else if(key==32)//按下空格键保存图片
        {
            static int num=1;
            String left_img_name,right_img_name;
            left_img_name =  "left_" + to_string(num) + ".jpg";
            right_img_name = "right_" + to_string(num) + ".jpg";
            cout<<left_img_name<<" "<<right_img_name<<endl;
            num++;
            imwrite("left_camera/" + left_img_name,left_img);
            imwrite("right_camera/" + right_img_name,right_img);
        }
    }
}

Ubuntu上安装MATLAB参考:
https://blog.youkuaiyun.com/qq_32892383/article/details/79670871
MATLAB进行摄像头标定参考:
https://blog.youkuaiyun.com/Loser__Wang/article/details/51811347
标定前我们需要制作标准棋盘。
MATLAB中输入cameraCalibrator进入标定工具
< ADD Images > 添加图片,输入棋盘方格的边长,确实后matlab会自动识别图中的棋盘。
< Radial Distortion> 的参数个数视摄像头的畸变程度而定,< Tangential Distortion >是切向畸变,< skew >是扭曲,按照实际情况选择。
我的摄像头畸变很小,所以我只选择了< Radial Distortion >的< 2 Coefficients>.
点击< Calibration > 进行校准,效果如下:
校准前:

校准前校准后:
校准后点击< Export Camera Parameters >,点击<确定>,即可导出相机参数。
命令行窗口会打印以下信息:

相机参数具体数值可在<工作区>查看
具体参数意义:
IntrinsicMatrix:
| Fx 0 0 |
| 0 Fy 0 |
| Cx Cy 0 |
(Cx,Cy)为成像中心点在图像中的坐标
(Fx,Fy)为焦距F分别除于dx,dy的值,其单位是像素。
(这个矩阵在opencv中是转置的,这点要注意)
dx,dy含义:

dx,dyFocalLength,PrincipalPoint:其实就是 (Fx,Fy)与(Cx,Cy)
RadialDistortion:径向畸变参数(k1,k2)
TangentialDistortion:切向畸变参数(p1,p2)

获得摄像头内参后,我们可以在opencv中通过initUndistortRectifyMap()remap()两个函数获得矫正过的图像。
代码:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#define FRAME_WIDTH 3040
#define FRAME_HEIGHT 1080

using namespace cv;
using namespace std;
int main()
{
    VideoCapture cap(1);
    cap.set(CV_CAP_PROP_FRAME_WIDTH,FRAME_WIDTH);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT,FRAME_HEIGHT);
    cout<<"Resolution:<"<<cap.get(CV_CAP_PROP_FRAME_WIDTH)<<","<<cap.get(CV_CAP_PROP_FRAME_HEIGHT)<<">\n";
    namedWindow("camera",CV_WINDOW_NORMAL);
    namedWindow("left_img",CV_WINDOW_NORMAL);
    namedWindow("right_img",CV_WINDOW_NORMAL);
    namedWindow("leftCalibration_img",CV_WINDOW_NORMAL);
    namedWindow("rightCalibration_img",CV_WINDOW_NORMAL);
    resizeWindow("camera",1020,540);

    Mat left_cameraMatrix = Mat::eye(3, 3, CV_64F);//左相机内参矩阵
    left_cameraMatrix.at<double>(0, 0) = 1.918336153824488e+03;//Fx
    left_cameraMatrix.at<double>(0, 1) = 0;
    left_cameraMatrix.at<double>(0, 2) = 6.907303693220415e+02;//Cx
    left_cameraMatrix.at<double>(1, 1) = 1.929292488197410e+03;//Fy
    left_cameraMatrix.at<double>(1, 2) = 6.284631474799424e+02;//Cy

    Mat left_distCoeffs = Mat::zeros(5, 1, CV_64F);//畸变系数
    left_distCoeffs.at<double>(0, 0) = -0.035177753004162;//k1
    left_distCoeffs.at<double>(1, 0) = 0.529741452707019;//k2
    left_distCoeffs.at<double>(2, 0) = 0;//p1
    left_distCoeffs.at<double>(3, 0) = 0;//p2
    left_distCoeffs.at<double>(4, 0) = 0;

    Mat right_cameraMatrix = Mat::eye(3, 3, CV_64F);//右相机内参矩阵
    right_cameraMatrix.at<double>(0, 0) = 1.915849735633710e+03;//Fx
    right_cameraMatrix.at<double>(0, 1) = 0;
    right_cameraMatrix.at<double>(0, 2) = 6.147645852519341e+02;//Cx
    right_cameraMatrix.at<double>(1, 1) = 1.925977816564622e+03;//Fy
    right_cameraMatrix.at<double>(1, 2) = 6.307232887078274e+02;//Cy

    Mat right_distCoeffs = Mat::zeros(5, 1, CV_64F);//畸变系数
    right_distCoeffs.at<double>(0, 0) = -0.005231683363986;//k1
    right_distCoeffs.at<double>(1, 0) = 0.060025441793370;//k2
    right_distCoeffs.at<double>(2, 0) = 0;//p1
    right_distCoeffs.at<double>(3, 0) = 0;//p2
    right_distCoeffs.at<double>(4, 0) = 0;

    Mat left_map1,left_map2;
    Mat right_map1,right_map2;
    Size imageSize;
    imageSize = Size(FRAME_WIDTH>>1,FRAME_HEIGHT);
    initUndistortRectifyMap(left_cameraMatrix, left_distCoeffs, Mat(), getOptimalNewCameraMatrix(left_cameraMatrix, left_distCoeffs, imageSize, 1, imageSize, 0), imageSize, CV_16SC2, left_map1, left_map2);
    initUndistortRectifyMap(right_cameraMatrix, right_distCoeffs, Mat(), getOptimalNewCameraMatrix(right_cameraMatrix, right_distCoeffs, imageSize, 1, imageSize, 0), imageSize, CV_16SC2, right_map1, right_map2);
    while(1)
    {
        Mat img;
        Mat left_img,right_img;//左右摄像头原始图像
        Mat leftCalibration_img,rightCalibration_img;//左右摄像头校准后图像
        cap >> img;

        left_img = img(Rect(0,0,FRAME_WIDTH>>1,FRAME_HEIGHT));
        right_img = img(Rect(FRAME_WIDTH>>1,0,FRAME_WIDTH>>1,FRAME_HEIGHT));

        remap(left_img, leftCalibration_img, left_map1, left_map2, INTER_LINEAR);
        remap(right_img, rightCalibration_img, right_map1, right_map2, INTER_LINEAR);
        imshow("camera",img);
        imshow("left_img",left_img);
        imshow("right_img",right_img);
        imshow("leftCalibration_img",leftCalibration_img);
        imshow("rightCalibration_img",rightCalibration_img);

        int key=waitKey(30);
        if(key==27)//按下Esc退出
            return 0;
    }
}

效果:
效果因为我的摄像头本身畸变小,矫正后并没有太大变化。

三.双目摄像头标定
我利用MATLAB做双目标定
在< APP > 中 < 图像处理与计算机视觉 > 中打开 < Stereo Camera Calibration >
跟单目校准一样的使用方式,导入图片,输入方格大小等。
效果:
效果
导出参数:
导出参数
这里的Parameters of Two Cameras 的内容跟单目校准的参数是一样的。
TranslationOfCamera2 :平移向量
RotationOfCamera2是相机2相对于相机1的旋转矩阵,这里要注意opencv里面用的是旋转向量,转换
旋转矩阵和旋转向量概念:
https://blog.youkuaiyun.com/hongbin_xu/article/details/78929006

在opencv中我们用Rodrigues()将旋转矩阵转化为旋转向量,利用stereoRectify() ,initUndistortRectifyMap() ,remap()来得到双目校准和对齐后的图像。

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#define FRAME_WIDTH 3040
#define FRAME_HEIGHT 1080

using namespace cv;
using namespace std;
int main()
{
    VideoCapture cap(1);
    cap.set(CV_CAP_PROP_FRAME_WIDTH,FRAME_WIDTH);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT,FRAME_HEIGHT);
    cout<<"Resolution:<"<<cap.get(CV_CAP_PROP_FRAME_WIDTH)<<","<<cap.get(CV_CAP_PROP_FRAME_HEIGHT)<<">\n";
    namedWindow("camera",CV_WINDOW_NORMAL);
//    namedWindow("left_img",CV_WINDOW_NORMAL);
//    namedWindow("right_img",CV_WINDOW_NORMAL);
//    namedWindow("left_rectified_img",CV_WINDOW_NORMAL);
//    namedWindow("right_rectified_img",CV_WINDOW_NORMAL);
//    namedWindow("rectified_img",CV_WINDOW_NORMAL);
    namedWindow("aligned_rectified_img",CV_WINDOW_NORMAL);
    resizeWindow("camera",1020,540);
    resizeWindow("aligned_rectified_img",1020,540);

    Mat left_cameraMatrix = Mat::eye(3, 3, CV_64F);//左相机内参矩阵
    left_cameraMatrix.at<double>(0, 0) = 1.918336153824488e+03;//Fx
    left_cameraMatrix.at<double>(0, 1) = 0;
    left_cameraMatrix.at<double>(0, 2) = 6.907303693220415e+02;//Cx
    left_cameraMatrix.at<double>(1, 1) = 1.929292488197410e+03;//Fy
    left_cameraMatrix.at<double>(1, 2) = 6.284631474799424e+02;//Cy

    Mat left_distCoeffs = Mat::zeros(5, 1, CV_64F);//畸变系数
    left_distCoeffs.at<double>(0, 0) = -0.035177753004162;//k1
    left_distCoeffs.at<double>(1, 0) = 0.529741452707019;//k2
    left_distCoeffs.at<double>(2, 0) = 0;//p1
    left_distCoeffs.at<double>(3, 0) = 0;//p2
    left_distCoeffs.at<double>(4, 0) = 0;

    Mat right_cameraMatrix = Mat::eye(3, 3, CV_64F);//右相机内参矩阵
    right_cameraMatrix.at<double>(0, 0) = 1.915849735633710e+03;//Fx
    right_cameraMatrix.at<double>(0, 1) = 0;
    right_cameraMatrix.at<double>(0, 2) = 6.147645852519341e+02;//Cx
    right_cameraMatrix.at<double>(1, 1) = 1.925977816564622e+03;//Fy
    right_cameraMatrix.at<double>(1, 2) = 6.307232887078274e+02;//Cy

    Mat right_distCoeffs = Mat::zeros(5, 1, CV_64F);//畸变系数
    right_distCoeffs.at<double>(0, 0) = -0.005231683363986;//k1
    right_distCoeffs.at<double>(1, 0) = 0.060025441793370;//k2
    right_distCoeffs.at<double>(2, 0) = 0;//p1
    right_distCoeffs.at<double>(3, 0) = 0;//p2
    right_distCoeffs.at<double>(4, 0) = 0;

    Mat rotation_matrix = Mat::zeros(3,3,CV_64F);//旋转矩阵
    rotation_matrix.at<double>(0,0)=0.999945124306786;
    rotation_matrix.at<double>(1,0)=0.008043320446283;
    rotation_matrix.at<double>(2,0)=0.006712180814433;
    rotation_matrix.at<double>(1,0)=0.008030133665074;
    rotation_matrix.at<double>(1,1)=0.999965779337229;
    rotation_matrix.at<double>(1,2)=0.001989248051137;
    rotation_matrix.at<double>(2,0)=0.006727951278679;
    rotation_matrix.at<double>(2,1)=0.001935239180647;
    rotation_matrix.at<double>(2,2)=0.999975494460192;

    Mat rotation_vector;//旋转矩阵
    Rodrigues(rotation_matrix,rotation_vector);//旋转矩阵转化为旋转向量

    Mat translation_vector = Mat::zeros(3,1,CV_64F);//平移向量
    translation_vector.at<double>(0,0) = 51.157553416555460;
    translation_vector.at<double>(1,0) = 0.176996310367285;
    translation_vector.at<double>(2,0) = 1.001369201180156;

    Mat R1,R2;//左右相机的3x3矫正变换(旋转矩阵)
    Mat P1,P2;//左右相机新的坐标系统(矫正过的)输出 3x4 的投影矩阵
    Mat Q;//深度视差映射矩阵
    Rect left_valid_roi,right_valid_roi;//图像校正之后,会对图像进行裁剪,这里的validRoi就是指裁剪之后的区域
    Mat rmap[2][2];//映射表 必须为:CV_16SC2/CV_32FC1/CV_32FC2
    Size imageSize;
    imageSize = Size(FRAME_WIDTH>>1,FRAME_HEIGHT);

    /*
        立体校正的时候需要两幅图像共面并且行对准 以使得立体匹配更加的可靠
        使得两幅图像共面的方法就是把两个摄像头的图像投影到一个公共成像面上,这样每幅图像从本图像平面投影到公共图像平面都需要一个旋转矩阵R
        stereoRectify 这个函数计算的就是从图像平面投影都公共成像平面的旋转矩阵R1,R2。 R1,R2即为左右相机平面行对准的校正旋转矩阵。
        左相机经过R1旋转,右相机经过R2旋转之后,两幅图像就已经共面并且行对准了。
        其中P1,P2为两个相机的投影矩阵,其作用是将3D点的坐标转换到图像的2D点的坐标:P*[X Y Z 1]' =[x y w]
        Q矩阵为重投影矩阵,即矩阵Q可以把2维平面(图像平面)上的点投影到3维空间的点:Q*[x y d 1] = [X Y Z W]。其中d为左右两幅图像的时差
    */
    //双目校准
    stereoRectify(left_cameraMatrix,left_distCoeffs,//左摄像头内参和畸变系数
                  right_cameraMatrix,right_distCoeffs,//右摄像头内参和畸变系数
                  imageSize,rotation_vector,translation_vector,//图像大小,右摄像头相对于左摄像头旋转矩阵,平移向量
                  R1,R2,P1,P2,Q,//输出的参数
                  CALIB_ZERO_DISPARITY,-1,imageSize, &left_valid_roi, &right_valid_roi);
    //Precompute maps for cv::remap()
    initUndistortRectifyMap(left_cameraMatrix,left_distCoeffs,R1,P1,imageSize,CV_16SC2,rmap[0][0],rmap[0][1]);
    initUndistortRectifyMap(right_cameraMatrix,right_distCoeffs,R2,P2,imageSize,CV_16SC2,rmap[1][0],rmap[1][1]);

    while(1)
    {
        Mat img;
        Mat left_img,right_img;//左右摄像头原始图像
        Mat left_rectified_img,right_rectified_img;//左右摄像头校准后图像
        Mat aligned_rectified_img(FRAME_HEIGHT,FRAME_WIDTH ,CV_8UC3);//校准+对齐后的图像
        cap >> img;

        left_img = img(Rect(0,0,FRAME_WIDTH>>1,FRAME_HEIGHT));
        right_img = img(Rect(FRAME_WIDTH>>1,0,FRAME_WIDTH>>1,FRAME_HEIGHT));

        //经过remap之后,左右相机的图像已经共面并且行对准了
        remap(left_img, left_rectified_img, rmap[0][0], rmap[0][1], INTER_LINEAR);
        remap(right_img, right_rectified_img, rmap[1][0], rmap[1][1], INTER_LINEAR);


        //复制左相机校准图像到总图像上
        for(int i=0; i<left_rectified_img.rows ;i++)
        {
            for(int j=0 ; j<left_rectified_img.cols ;j++)
            {
                aligned_rectified_img.at<Vec3b>(i,j)[0] = left_rectified_img.at<Vec3b>(i,j)[0];
                aligned_rectified_img.at<Vec3b>(i,j)[1] = left_rectified_img.at<Vec3b>(i,j)[1];
                aligned_rectified_img.at<Vec3b>(i,j)[2] = left_rectified_img.at<Vec3b>(i,j)[2];
            }
        }
        //复制右相机校准图像到总图像上
        for(int i=0; i<right_rectified_img.rows ;i++)
        {
            for(int j=0 ; j<right_rectified_img.cols ;j++)
            {
                aligned_rectified_img.at<Vec3b>(i,j+(FRAME_WIDTH>>1))[0] = right_rectified_img.at<Vec3b>(i,j)[0];
                aligned_rectified_img.at<Vec3b>(i,j+(FRAME_WIDTH>>1))[1] = right_rectified_img.at<Vec3b>(i,j)[1];
                aligned_rectified_img.at<Vec3b>(i,j+(FRAME_WIDTH>>1))[2] = right_rectified_img.at<Vec3b>(i,j)[2];
            }
        }
        rectangle(aligned_rectified_img,left_valid_roi,Scalar(0,0,255),5,8);
        rectangle(aligned_rectified_img,Rect(right_valid_roi.x+(FRAME_WIDTH>>1),right_valid_roi.y,right_valid_roi.width,right_valid_roi.height),Scalar(0,0,255),5,8);
        for(int i=0 ; i<aligned_rectified_img.rows ; i+=32)
        {
            line(aligned_rectified_img,Point(0,i),Point(aligned_rectified_img.cols,i),Scalar(0,255,0),1,LINE_8);
        }

        imshow("camera",img);
//        imshow("left_img",left_img);
//        imshow("right_img",right_img);
//        imshow("left_rectified_img",left_rectified_img);
//        imshow("right_rectified_img",right_rectified_img);
        imshow("aligned_rectified_img",aligned_rectified_img);

        int key=waitKey(30);
        if(key==27)//按下Esc退出
            return 0;
    }
}

效果:
效果
这里红色的框内为valid roi。

五.总结
1.此demo主要是为了测试用的,用到了opencv和MATLAB,操作麻烦。下一步我们准备把标定也用opencv来实现。
2.此demo跑的速度只有4Fps,太慢了,需要做优化。
3.考虑到后面我们主要是用作测距和物体检测,可能需要做GUI界面,考虑用Qt来做。

<think>好的,我现在需要帮助用户找到关于双目摄像头标定板的使用方法和教程,或者使用OpenCV的双目相机标定流程。首先,我要回忆一下系统级指令里的要求,确保回答符合格式和内容要求。 首先,用户提到了参考的引用资料,引用1是关于单目摄像头标定和畸变矫正的,而引用2是有关双目摄像头标定,特别是使用棋盘格标定板和OpenCV的函数。因此,我需要结合引用2中的信息来构建回答,同时可能需要补充其他步骤。 用户的问题主要集中在双目摄像头标定板的资料、使用方法、教程以及OpenCV的标定流程。根据我的知识,双目标定通常包括以下几个步骤:准备标定板、采集图像、检测角点、计算相机参数、立体校正等。需要详细说明每一步的具体操作和OpenCV函数的使用。 接下来,我需要确保回答符合用户的要求:结构清晰,分步骤解决问题,引用相关文献。同时,根据系统指令,生成相关问题,并正确使用LaTeX格式。例如,在提到坐标转换或数学公式时,需要使用$$...$$或者$...$。 首先,标定板的选择。引用2提到使用棋盘标定模板,所以标定板通常使用棋盘格图案,OpenCV的findChessboardCorners函数用于检测角点。需要说明标定板的规格,比如黑白棋盘,通常推荐使用不对称的圆圈网格,但这里用户提到的是棋盘格,所以可能需要解释棋盘格的大小(如9x6角点)。 然后是标定步骤。分步讲解:准备标定板、采集图像、检测角点、亚像素细化、标定参数计算、立体校正。每一步需要对应的OpenCV函数,比如stereoCalibrate用于双目标定,stereoRectify进行校正,initUndistortRectifyMap和remap用于图像校正。 同时,要确保使用正确的中文术语,例如“亚像素级精度”对应FindCornerSubPix函数。需要提到标定结果的评价指标,如重投影误差,以及如何验证标定效果,比如查看校正后的图像对齐情况。 在引用方面,引用2提到了findChessboardCorners和FindCornerSubPix,所以需要在对应的步骤后添加引用标识[^2]。此外,可能还需要补充其他引用来源的信息,但用户只提供了引用1和2,因此主要依赖引用2的内容。 生成的相关问题需要围绕双目标定的难点、标定板要求、标定失败处理、标定结果应用等,确保问题有针对性,帮助用户进一步探索。 最后,检查LaTeX格式是否正确,例如数学公式是否需要,但在这个问题中可能涉及相机模型中的公式,比如投影矩阵或畸变系数,但用户的问题更偏向流程而非数学推导,所以可能不需要太多公式。不过如果有提到坐标转换,可能需要使用矩阵表示,例如相机的内参矩阵用$K$表示,但根据用户示例,可能不需要强制加入公式,除非必要。 总结回答的结构: 1. 标定板选择:棋盘格规格,打印要求,引用2中的检测方法。 2. 标定流程分步骤,每个步骤详细说明,使用的OpenCV函数。 3. 代码示例,可能用C++或Python,但用户引用中提到了C++和Python代码,所以可能需要提供两种,但用户示例中的代码是Python,但引用里的教程是C++,所以可能需要说明两种语言的函数调用。 4. 相关问题和引用标记。 现在需要将这些整理成自然的中文段落,确保符合格式要求,正确引用,并生成相关问题。</think>### 双目摄像头标定板使用与OpenCV标定流程 #### 1. 标定板选择与准备 双目摄像头标定通常使用棋盘格标定板,其角点数量需满足算法检测要求(如$9 \times 6$黑白交替棋盘)。标定板需满足以下条件: - 平面平整度高,图案清晰无变形 - 打印比例准确(可通过实际测量验证) - 在不同角度/距离下拍摄时,需覆盖摄像头视野的多个区域 #### 2. 标定流程步骤 **步骤1:采集标定图像** - 固定双目摄像头,移动标定板或调整摄像头位姿 - 同时拍摄至少15-20组图像对(左右摄像头同步采集) - 确保标定板在图像中呈现倾斜、旋转、远近等多样化姿态 **步骤2:角点检测与亚像素优化** ```python # Python示例(需安装OpenCV) import cv2 # 检测棋盘格角点 ret, corners = cv2.findChessboardCorners(gray_image, (width, height)) # 亚像素级优化 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) cv2.cornerSubPix(gray_image, corners, (11,11), (-1,-1), criteria) ``` 该过程通过`findChessboardCorners`粗定位角点,再使用`cornerSubPix`提升精度至亚像素级 **步骤3:双目参数标定** 使用`stereoCalibrate`函数计算以下参数: $$ \begin{cases} \text{相机内参矩阵}K_1,K_2 \\ \text{畸变系数}D_1,D_2 \\ \text{旋转矩阵}R \\ \text{平移向量}T \\ \end{cases} $$ 标定代码框架: ```cpp // C++示例 double rms = stereoCalibrate( objectPoints, imagePointsL, imagePointsR, cameraMatrixL, distCoeffsL, cameraMatrixR, distCoeffsR, imageSize, R, T, E, F, CALIB_FIX_INTRINSIC // 根据需求调整标志位 ); ``` **步骤4:立体校正** 通过`stereoRectify`计算校正映射矩阵,再使用`initUndistortRectifyMap`和`remap`实现实时校正: ```python # 计算校正参数 R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify( cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, imageSize, R, T ) # 生成映射表 map1x, map1y = cv2.initUndistortRectifyMap(...) map2x, map2y = cv2.initUndistortRectifyMap(...) # 实时校正 rectified_L = cv2.remap(imgL, map1x, map1y, cv2.INTER_LINEAR) ``` #### 3. 验证标定效果 - **重投影误差**:标定结果应小于0.5像素 - **双目对齐验证**:校正后的图像对应在同一水平线(极线约束) - **深度图生成**:使用BM/SGBM算法验证视差计算有效性
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值