单目双目标定

25 篇文章 0 订阅

 

 

 

 

 

 

 

 

 

本文用QT调用OpenCV4.5.1进行相机标定。

 头文件如下

#include <QMainWindow>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <fstream>
#include <vector>

using namespace cv;
using namespace std;

1.进行摄像机的读取,用OpenCV读取双目摄像头,并截取左右相机的标定图。

 

    VideoCapture cap(1);
    cap.set(CAP_PROP_FRAME_WIDTH, 2560);
    cap.set(CAP_PROP_FRAME_HEIGHT, 960);
    cap.set(CAP_PROP_FOURCC, '2YUY');
    int hw = cap.get(CAP_PROP_FRAME_WIDTH);
    int hh = cap.get(CAP_PROP_FRAME_HEIGHT);
    while(true)
    {
        cv::Mat frame1;
        cap >> frame1;
        Mat l(frame1,Range(0,960),Range(0,1279));
        Mat r(frame1,Range(0,960),Range(1280,2559));
        cv::imshow("left", l);
        cv::imshow("right", r);
        int c = waitKey(1);
        if(27 == char(c))
            break;
        if (113 == char(c))//按键q
        {
            static int num = 1;
            num++;
            cout << num;
            stringstream strl,strr;
            strl << "D:\\left" << num << ".jpg";
            strr << "D:\\right" << num << ".jpg";
            imwrite( strl.str( ), l);
            imwrite( strr.str( ), r);
        }
    }

2.标定板角点提取,本文用的方格标定板,注释部分是圆形标定板,原理相同。

Mat img1 = imread("E:/opencv4-c++/opencv-mask/picture/left3.jpg");
//	Mat img2 = imread("circle.png");
    Mat gray1, gray2;
    cvtColor(img1, gray1, COLOR_BGR2GRAY);
//	cvtColor(img2, gray2, COLOR_BGR2GRAY);

    //定义数目尺寸
    Size board_size1 = Size(10, 6);   //方格标定板内角点数目(行,列)
//	Size board_size2 = Size(7, 7);   //圆形标定板圆心数目(行,列)

    //检测角点,进行粗检测
    vector<Point2f> img1_points, img2_points;
    findChessboardCorners(gray1, board_size1, img1_points);  //计算方格标定板角点
//	findCirclesGrid(gray2, board_size2, img2_points);  //计算圆形标定板检点

    //细化角点坐标,进行精检测
    find4QuadCornerSubpix(gray1, img1_points, Size(5, 5));  //细化方格标定板角点坐标
//	find4QuadCornerSubpix(gray2, img2_points, Size(5, 5));  //细化圆形标定板角点坐标

    //绘制角点检测结果
    drawChessboardCorners(img1, board_size1, img1_points, true);
//	drawChessboardCorners(img2, board_size2, img2_points, true);

    //显示结果
    imshow("方形标定板角点检测结果", img1);
//	imshow("圆形标定板角点检测结果", img2);
    waitKey(0);

 3.计算相机内参矩阵和畸变系数

 

 //读取所有图像
    vector<Mat> imgs;
    string imageName;
    //ifstream fin("E:/opencv4-c++/opencv-mask/picture/steroCalibDataL.txt");
    ifstream fin("E:/opencv4-c++/opencv-mask/picture/steroCalibDataR.txt");
    while (getline(fin,imageName))
    {
        Mat img = imread(imageName);
        imgs.push_back(img);
    }

    Size board_size = Size(10, 6);  //方格标定板内角点数目(行,列)
    vector<vector<Point2f>> imgsPoints;
    for (int i = 0; i < imgs.size(); i++)
    {
        Mat img1 = imgs[i];
        Mat gray1;
        cvtColor(img1, gray1, COLOR_BGR2GRAY);
        vector<Point2f> img1_points;
        findChessboardCorners(gray1, board_size, img1_points);  //计算方格标定板角点
        find4QuadCornerSubpix(gray1, img1_points, Size(5, 5));  //细化方格标定板角点坐标
        imgsPoints.push_back(img1_points);
    }

    //生成棋盘格每个内角点的空间三维坐标
    Size squareSize = Size(2.6, 2.6);  //棋盘格每个方格的真实尺寸
    vector<vector<Point3f>> objectPoints;
    for (int i = 0; i < imgsPoints.size(); i++)
    {
        vector<Point3f> tempPointSet;
        for (int j = 0; j < board_size.height; j++)
        {
            for (int k = 0; k < board_size.width; k++)
            {
                Point3f realPoint;
                // 假设标定板为世界坐标系的z平面,即z=0
                realPoint.x = j*squareSize.width;
                realPoint.y = k*squareSize.height;
                realPoint.z = 0;
                tempPointSet.push_back(realPoint);
            }
        }
        objectPoints.push_back(tempPointSet);
    }

    /* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
    vector<int> point_number;
    for (int i = 0; i<imgsPoints.size(); i++)
    {
        point_number.push_back(board_size.width*board_size.height);
    }

    //图像尺寸
    Size imageSize;
    imageSize.width = imgs[0].cols;
    imageSize.height = imgs[0].rows;

    Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));  //摄像机内参数矩阵
    Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));  //摄像机的5个畸变系数:k1,k2,p1,p2,k3
    vector<Mat> rvecs;  //每幅图像的旋转向量
    vector<Mat> tvecs;  //每张图像的平移量
    calibrateCamera(objectPoints, imgsPoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, 0);
    cout << "相机的内参矩阵=" << endl << cameraMatrix << endl;
    cout << "相机畸变系数" << distCoeffs << endl;
    waitKey(0);

 

4.图像去畸变

 


//使用initUndistortRectifyMap()函数和remap()函数校正图像
void initUndistAndRemap(vector<Mat> imgs,  //所有原图像向量
                        Mat cameraMatrix,  //计算得到的相机内参
                        Mat distCoeffs,    //计算得到的相机畸变系数
                        Size imageSize,    //图像的尺寸
                        vector<Mat> &undistImgs)  //校正后的输出图像
{
    //计算映射坐标矩阵
    Mat R = Mat::eye(3, 3, CV_32F);
    Mat mapx = Mat(imageSize, CV_32FC1);
    Mat mapy = Mat(imageSize, CV_32FC1);
    initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);

    //校正图像
    for (int i = 0; i < imgs.size(); i++)
    {
        Mat undistImg;
        remap(imgs[i], undistImg, mapx, mapy, INTER_LINEAR);
        undistImgs.push_back(undistImg);
    }
}

//用undistort()函数直接计算校正图像
void undist(vector<Mat> imgs,   //所有原图像向量
            Mat cameraMatrix,   //计算得到的相机内参
            Mat distCoeffs,     //计算得到的相机畸变系数
            vector<Mat> &undistImgs)  //校正后的输出图像
{
    for (int i = 0; i < imgs.size(); i++)
    {
        Mat undistImg;
        undistort(imgs[i], undistImg, cameraMatrix, distCoeffs);
        undistImgs.push_back(undistImg);
    }
}


void MainWindow::on_myundistortion_clicked()
{
    //读取所有图像
    vector<Mat> imgs;
    string imageName;
    //ifstream fin("E:/opencv4-c++/opencv-mask/picture/steroCalibDataL.txt");
    ifstream fin("E:/opencv4-c++/opencv-mask/picture/steroCalibDataR.txt");
    while (getline(fin,imageName))
    {
        Mat img = imread(imageName);
        imgs.push_back(img);
    }

    //输入前文计算得到的内参矩阵
    Mat cameraMatrix = (Mat_<float>(3, 3) << 1176.737647429332, 0, 749.7543897959779,
                                             0, 1180.518962411762, 453.2208968502105,
                                             0, 0, 1);//右
    //输入前文计算得到的内参矩阵
    Mat distCoeffs = (Mat_<float>(1, 5) << -0.0216069326379435, 1.02772611616505, 0.0006464851449221918, 0.01207375079013798, -7.043775828184633);

//    //输入前文计算得到的内参矩阵
//    Mat cameraMatrix = (Mat_<float>(3, 3) << 1194.373539875038, 0, 844.6479853903846,
//                                             0, 1196.104476266386, 438.2010631053831,
//                                             0, 0, 1);//左
//    //输入前文计算得到的内参矩阵
//    Mat distCoeffs = (Mat_<float>(1, 5) << -0.1388268134589714, 0.8344992432974776, -0.003904177091925393, 0.01180973194048606, -1.480458350907071);



    vector<Mat> undistImgs;
    Size imageSize;
    imageSize.width = imgs[0].cols;
    imageSize.height = imgs[0].rows;

    //使用initUndistortRectifyMap()函数和remap()函数校正图像
    initUndistAndRemap(imgs,cameraMatrix,distCoeffs,imageSize,undistImgs);

    //用undistort()函数直接计算校正图像,下一行代码取消注释即可
    //undist(imgs, cameraMatrix, distCoeffs, undistImgs);

    //显示校正前后的图像
    for (int i = 0; i < imgs.size(); i++)
    {
        string windowNumber = to_string(i);
        imshow("未校正图像"+ windowNumber, imgs[i]);
        imshow("校正后图像"+ windowNumber, undistImgs[i]);
    }

    waitKey(0);

5.双目标定


//检测棋盘格内角点在图像中坐标的函数
void getImgsPoints(vector<Mat> imgs, vector<vector<Point2f>> &Points, Size boardSize)
{
    for (int i = 0; i < imgs.size(); i++)
    {
        Mat img1 = imgs[i];
        Mat gray1;
        cvtColor(img1, gray1, COLOR_BGR2GRAY);
        vector<Point2f> img1_points;
        findChessboardCorners(gray1, boardSize, img1_points);  //计算方格标定板角点
        find4QuadCornerSubpix(gray1, img1_points, Size(5, 5));  //细化方格标定板角点坐标
        Points.push_back(img1_points);
    }
}

void MainWindow::on_mystereocalibrate_clicked()
{
    //读取所有图像
        vector<Mat> imgLs;
        vector<Mat> imgRs;
        string imgLName;
        string imgRName;
        ifstream finL("E:/opencv4-c++/opencv-mask/picture/steroCalibDataL.txt");
        ifstream finR("E:/opencv4-c++/opencv-mask/picture/steroCalibDataR.txt");
        while (getline(finL, imgLName) && getline(finR, imgRName))
        {
            Mat imgL = imread(imgLName);
            Mat imgR = imread(imgRName);
            if (!imgL.data && !imgR.data)
            {
                cout << "请确是否输入正确的图像文件" << endl;
            }
            imgLs.push_back(imgL);
            imgRs.push_back(imgR);
        }

        //提取棋盘格内角点在两个相机图像中的坐标
        Size board_size = Size(10,6);  //方格标定板内角点数目(行,列)
        vector<vector<Point2f>> imgLsPoints;
        vector<vector<Point2f>> imgRsPoints;
        getImgsPoints(imgLs, imgLsPoints, board_size);  //调用子函数
        getImgsPoints(imgRs, imgRsPoints, board_size);  //调用子函数


                                                        //生成棋盘格每个内角点的空间三维坐标
        Size squareSize = Size(2.6, 2.6);  //棋盘格每个方格的真实尺寸
        vector<vector<Point3f>> objectPoints;
        for (int i = 0; i < imgLsPoints.size(); i++)
        {
            vector<Point3f> tempPointSet;
            for (int j = 0; j < board_size.height; j++)
            {
                for (int k = 0; k < board_size.width; k++)
                {
                    Point3f realPoint;
                    // 假设标定板为世界坐标系的z平面,即z=0
                    realPoint.x = j*squareSize.width;
                    realPoint.y = k*squareSize.height;
                    realPoint.z = 0;
                    tempPointSet.push_back(realPoint);
                }
            }
            objectPoints.push_back(tempPointSet);
        }

        //图像尺寸
        Size imageSize;
        imageSize.width = imgLs[0].cols;
        imageSize.height = imgLs[0].rows;

        Mat Matrix1, dist1, Matrix2, dist2, rvecs, tvecs;
        calibrateCamera(objectPoints, imgLsPoints, imageSize, Matrix1, dist1, rvecs, tvecs, 0);
        calibrateCamera(objectPoints, imgRsPoints, imageSize, Matrix2, dist2, rvecs, tvecs, 0);

        //进行标定
        Mat R, T, E, F;  //旋转矩阵、平移向量、本征矩阵、基本矩阵
        stereoCalibrate(objectPoints, imgLsPoints, imgRsPoints, Matrix1, dist1, Matrix2, dist2, imageSize, R, T, E, F, CALIB_USE_INTRINSIC_GUESS);

        cout << "两个相机坐标系的旋转矩阵:" << endl << R << endl;
        cout << "两个相机坐标系的平移向量:" << endl << T << endl;

        //计算校正变换矩阵
        Mat R1, R2, P1, P2,Q;
        stereoRectify(Matrix1, dist1, Matrix2, dist2, imageSize, R, T, R1, R2, P1, P2, Q, 0);

        //计算校正映射矩阵
        Mat map11, map12, map21, map22;
        initUndistortRectifyMap(Matrix1, dist1, R1, P1, imageSize, CV_16SC2, map11, map12);
        initUndistortRectifyMap(Matrix2, dist2, R2, P2, imageSize, CV_16SC2, map21, map22);

        for (int i = 0; i < imgLs.size(); i++)
        {
            //进行校正映射
            Mat img1r, img2r;
            remap(imgLs[i], img1r, map11, map12, INTER_LINEAR);
            remap(imgRs[i], img2r, map21, map22, INTER_LINEAR);
            cv::resize(img1r, img1r, cv::Size(img1r.cols/2, img1r.rows/2));
            cv::resize(img2r, img2r, cv::Size(img2r.cols/2, img2r.rows/2));

            //拼接图像
            Mat result;
            hconcat(img1r, img2r, result);

            //绘制直线,用于比较同一个内角点y轴是否一致
            line(result, Point(-1, imgLsPoints[i][0].y), Point(result.cols, imgLsPoints[i][0].y), Scalar(0, 0, 255), 2);
            imshow("校正后结果", result);
            waitKey(0);
        }
}

 

6.进行立体匹配,视差图输出


const int imageWidth = 1279;                           //摄像头的分辨率
const int imageHeight = 960;
Size imageSize = Size(imageWidth, imageHeight);

Mat rgbImageL, grayImageL;
Mat rgbImageR, grayImageR;
Mat rectifyImageL, rectifyImageR;

Rect validROIL;                                         //图像校正之后,会对图像进行裁剪,这里的validROI就是指裁剪之后的区域
Rect validROIR;

Mat mapLx, mapLy, mapRx, mapRy;                          //映射表
Mat Rl, Rr, Pl, Pr, Q;                                   //校正旋转矩阵R,投影矩阵P 重投影矩阵Q
Mat xyz;                                                 //三维坐标

Point origin;                                            //鼠标按下的起始点
Rect selection;                                          //定义矩形选框
bool selectObject = false;                               //是否选择对象

int blockSize = 0, uniquenessRatio = 0, numDisparities = 0;
Ptr<StereoBM> bm = StereoBM::create(16, 9);

/*
    事先标定好的相机的参数
    fx 0 cx
    0 fy cy
    0 0  1
*/
Mat cameraMatrixL = (Mat_<double>(3, 3) << 1194.373539875038, 0, 844.6479853903846,
        0, 1196.104476266386, 438.2010631053831,
        0, 0, 1);
//左相机标定矩阵

Mat distCoeffL = (Mat_<double>(5, 1) << -0.1388268134589714, 0.8344992432974776, -0.003904177091925393, 0.01180973194048606, -1.480458350907071);
//左i相机畸变参数

Mat cameraMatrixR = (Mat_<double>(3, 3) << 1176.737647429332, 0, 749.7543897959779,
        0, 1180.518962411762, 453.2208968502105,
        0, 0, 1);
//右相机标定矩阵

Mat distCoeffR = (Mat_<double>(5, 1) <<-0.0216069326379435, 1.02772611616505, 0.0006464851449221918, 0.01207375079013798, -7.043775828184633);
//右相机畸变参数

Mat T = (Mat_<double>(3, 1) << 7.769593316804036,
        0.01701729660901589,
        0.2561899692564334);//T平移向量
//所得T参数
Mat rec = (Mat_<double>(3, 3) << 0.9999465327871111, -0.001354266346764183, 0.01025170862329667, 0.001558498960881604, 0.9997999615525806, -0.01994010933890455,
        -0.0102226536683918, 0.01995502047407017, 0.9997486156578841);//rec旋转向量,对应matlab om参数
Mat R;//R旋转矩阵

/*****立体匹配*****/
void stereo_match(int, void*)
{
    bm->setBlockSize(2 * blockSize + 5);              //SAD窗口大小,5~21之间为宜
    bm->setROI1(validROIL);
    bm->setROI2(validROIR);
    bm->setPreFilterCap(31);
    bm->setMinDisparity(0);                           //最小视差,默认值为0, 可以是负值,int型
    bm->setNumDisparities(numDisparities * 16 + 16);  //视差窗口,即最大视差值与最小视差值之差,窗口大小必须是16的整数倍,int型
    bm->setTextureThreshold(10);
    bm->setUniquenessRatio(uniquenessRatio);          //uniquenessRatio主要可以防止误匹配
    bm->setSpeckleWindowSize(100);
    bm->setSpeckleRange(32);
    bm->setDisp12MaxDiff(-1);
    Mat disp, disp8;
    bm->compute(rectifyImageL, rectifyImageR, disp);  //输入图像必须为灰度图
    disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16)*16.));//计算出的视差是CV_16S格式
    reprojectImageTo3D(disp, xyz, Q, true);           //在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16(也就是W除以16),才能得到正确的三维坐标信息。
    xyz = xyz * 16;
    imshow("disparity", disp8);
}

/*****描述:鼠标操作回调*****/
static void onMouse(int event, int x, int y, int, void*)
{
    if (selectObject)
    {
        selection.x = MIN(x, origin.x);
        selection.y = MIN(y, origin.y);
        selection.width = std::abs(x - origin.x);
        selection.height = std::abs(y - origin.y);
    }

    switch (event)
    {
    case EVENT_LBUTTONDOWN:   //鼠标左按钮按下的事件
        origin = Point(x, y);
        selection = Rect(x, y, 0, 0);
        selectObject = true;
        cout << origin << "in world coordinate is: " << xyz.at<Vec3f>(origin) << endl;
        break;
    case EVENT_LBUTTONUP:    //鼠标左按钮释放的事件
        selectObject = false;
        if (selection.width > 0 && selection.height > 0)
            break;
    }
}


/*****主函数*****/
void MainWindow::on_litipipei_clicked()
{
     //立体校正
    Rodrigues(rec, R); //Rodrigues变换
    stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,
                  0, imageSize, &validROIL, &validROIR);
    initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pr, imageSize, CV_32FC1, mapLx, mapLy);
    initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);
    //读取图片
    rgbImageL = imread("E:/opencv4-c++/opencv-mask/picture/left30.jpg", IMREAD_COLOR);
    cvtColor(rgbImageL, grayImageL, COLOR_BGR2GRAY);
    rgbImageR = imread("E:/opencv4-c++/opencv-mask/picture/right30.jpg", IMREAD_COLOR);
    cvtColor(rgbImageR, grayImageR, COLOR_BGR2GRAY);

    imshow("ImageL Before Rectify", grayImageL);
    imshow("ImageR Before Rectify", grayImageR);

    //经过remap之后,左右相机的图像已经共面并且行对准了

    remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);
    remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);

    //把校正结果显示出来
    Mat rgbRectifyImageL, rgbRectifyImageR;
    cvtColor(rectifyImageL, rgbRectifyImageL, COLOR_GRAY2BGR);  //伪彩色图
    cvtColor(rectifyImageR, rgbRectifyImageR, COLOR_GRAY2BGR);

    //单独显示
    //rectangle(rgbRectifyImageL, validROIL, Scalar(0, 0, 255), 3, 8);
    //rectangle(rgbRectifyImageR, validROIR, Scalar(0, 0, 255), 3, 8);
    imshow("ImageL After Rectify", rgbRectifyImageL);
    imshow("ImageR After Rectify", rgbRectifyImageR);

    //显示在同一张图上
    Mat canvas;
    double sf;
    int w, h;
    sf = 600. / MAX(imageSize.width, imageSize.height);
    w = cvRound(imageSize.width * sf);
    h = cvRound(imageSize.height * sf);
    canvas.create(h, w * 2, CV_8UC3);   //注意通道

    //左图像画到画布上
    Mat canvasPart = canvas(Rect(w * 0, 0, w, h));                                     //得到画布的一部分
    cv::resize(rgbRectifyImageL, canvasPart, canvasPart.size(), 0, 0, INTER_AREA);     //把图像缩放到跟canvasPart一样大小
    Rect vroiL(cvRound(validROIL.x*sf), cvRound(validROIL.y*sf),                       //获得被截取的区域
               cvRound(validROIL.width*sf), cvRound(validROIL.height*sf));
    //rectangle(canvasPart, vroiL, Scalar(0, 0, 255), 3, 8);                           //画上一个矩形
    cout << "Painted ImageL" << endl;

    //右图像画到画布上
    canvasPart = canvas(Rect(w, 0, w, h));                                              //获得画布的另一部分
    cv::resize(rgbRectifyImageR, canvasPart, canvasPart.size(), 0, 0, INTER_LINEAR);
    Rect vroiR(cvRound(validROIR.x * sf), cvRound(validROIR.y*sf),
               cvRound(validROIR.width * sf), cvRound(validROIR.height * sf));
    //rectangle(canvasPart, vroiR, Scalar(0, 0, 255), 3, 8);
    cout << "Painted ImageR" << endl;

    //画上对应的线条
    for (int i = 0; i < canvas.rows; i += 16)
        line(canvas, Point(0, i), Point(canvas.cols, i), Scalar(0, 255, 0), 1, 8);
    imshow("rectified", canvas);
    //立体匹配
    namedWindow("disparity", WINDOW_AUTOSIZE);
    // 创建SAD窗口 Trackbar
    createTrackbar("BlockSize:\n", "disparity", &blockSize, 8, stereo_match);
    // 创建视差唯一性百分比窗口 Trackbar
    createTrackbar("UniquenessRatio:\n", "disparity", &uniquenessRatio, 50, stereo_match);
    // 创建视差窗口 Trackbar
    createTrackbar("NumDisparities:\n", "disparity", &numDisparities, 16, stereo_match);
    //鼠标响应函数setMouseCallback(窗口名称, 鼠标回调函数, 传给回调函数的参数,一般取0)
    setMouseCallback("disparity", onMouse, 0);
    stereo_match(0, 0);
    waitKey(0);
}

 

结果显示

 

 

校正出现问题,下次修改。

 

 

 

 

 

 

 

此次结果存在问题,下次继续改正。

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值