相机内外参标定

1.畸变产生原因

相机畸变主要有径向畸变和切向畸变。

1.1 径向畸变

由于相机透镜的固有特性(凸透镜汇聚光线,凹透镜发散光线)导致成像时直线会变成曲线,所以径向畸变产生原因主要是透镜的几何形状改变了直线的形状。

径向畸变主要包括:桶形畸变(barrel distortion)、枕形畸变(pincushion distortion)、八字胡畸变(mustache distortion)
在这里插入图片描述
径向畸变矫正
径向畸变模型可以以下低阶多项式模型来表示:
x u n d i s t = x d i s t ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) y u n d i s t = y d i s t ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) 其中 r 2 = x d i s t 2 + y d i s t 2 , ( x d i s t , y d i s t ) 是归一后的相机坐标系的点,即坐标原点已移到主点,并且像素坐标除以焦距。 x d i s t = X Z = u − u 0 f x , y d i s t = Y Z = v − v 0 f y x_{undist}=x_{dist}(1 + k_1r^2 + k_2r^4 + k_3r^6) \\ y_{undist}=y_{dist}(1 + k_1r^2 + k_2r^4 + k_3r^6) \\ 其中r^2=x_{dist}^2 + y_{dist}^2,(x_{dist},y_{dist})是归一后的相机坐标系的点,即坐标原点已移到主点,并且像素坐标除以焦距。\\ x_{dist}=\frac{X}{Z}=\frac{u-u_0}{f_x},y_{dist} = \frac{Y}{Z} = \frac{v-v_0}{f_y} xundist=xdist(1+k1r2+k2r4+k3r6)yundist=ydist(1+k1r2+k2r4+k3r6)其中r2=xdist2+ydist2,(xdist,ydist)是归一后的相机坐标系的点,即坐标原点已移到主点,并且像素坐标除以焦距。xdist=ZX=fxuu0,ydist=ZY=fyvv0
其中 k 1 、 k 2 、 k 3 k_1、k_2、k_3 k1k2k3是径向畸变参数

1.2 切向畸变

切向畸变由相机senser与透镜不平行导致。
在这里插入图片描述
切向畸变矫正
切向畸变模型可以用以下低阶多项式模型来表示:
x u n d i s t = x d i s t + [ 2 p 1 x d i s t y d i s t + p 2 ( r 2 + 2 x d i s t 2 ) ] y u n d i s t = y d i s t + [ p 1 ( r 2 + 2 y d i s t 2 ) + 2 p 2 x d i s t y d i s t ] x_{undist} = x_{dist} + [2p_1x_{dist}y_{dist} + p2(r^2 + 2x_{dist}^2)]\\ y_{undist} = y_{dist} + [p_1(r^2 + 2y_{dist}^2) + 2p_2x_{dist}y_{dist}] xundist=xdist+[2p1xdistydist+p2(r2+2xdist2)]yundist=ydist+[p1(r2+2ydist2)+2p2xdistydist]
p 1 、 p 2 为切向畸变参数 p_1、p_2为切向畸变参数 p1p2为切向畸变参数

1.3 总畸变矫正

同时考虑径向畸变和切向畸变:
x u n d i s t = x d i s t ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + [ 2 p 1 x d i s t y d i s t + p 2 ( r 2 + 2 x d i s t 2 ) ] y u n d i s t = y d i s t ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + [ p 1 ( r 2 + 2 y d i s t 2 ) + 2 p 2 x d i s t y d i s t ] x_{undist} = x_{dist}(1 + k_1r^2 + k_2r^4 + k_3r^6) + [2p_1x_{dist}y_{dist} + p_2(r^2 + 2x_{dist}^2)]\\ y_{undist} = y_{dist}(1 + k_1r^2 + k_2r^4 + k_3r^6) + [p_1(r^2 + 2y_{dist}^2) + 2p_2x_{dist}y_{dist}] xundist=xdist(1+k1r2+k2r4+k3r6)+[2p1xdistydist+p2(r2+2xdist2)]yundist=ydist(1+k1r2+k2r4+k3r6)+[p1(r2+2ydist2)+2p2xdistydist]
共有 5 个畸变参数 k 1 、 k 2 、 k 3 、 p 1 、 p 2 ,这 5 个畸变参数与内参矩阵一起,都是需要进行相机标定。 共有5个畸变参数k_1、k_2、k_3、p_1、p_2,这5个畸变参数与内参矩阵一起,都是需要进行相机标定。 共有5个畸变参数k1k2k3p1p2,这5个畸变参数与内参矩阵一起,都是需要进行相机标定。
以上畸变方程由Brown在《Close-Range Camera Calibration》一文中所提出。

2.算法实现

2.1 标定流程

1)在相机视野范围内放置棋盘格,移动棋盘格并取图像,尽量覆盖整个相机的视野范围,且取图数量不少于10张(取图越多覆盖范围越广,标定结果越准确)

2)遍历每一张图像,重复以下算法步骤:图像二值化(非必须,可加快查找棋盘格角点算法处理速度)、查找棋盘格角点、亚像素精确化、记录角点像素坐标和角点世界坐标(角点0的世界坐标为零点,角点1的世界坐标为零点+实际棋盘格物理距离)

3)调用opencv处理计算相机内参和畸变系数计算函数,得出结果

2.2 标定代码

    bool success = false;
    try {
        //棋盘格行列角点数
        cv::Size patternSize(6, 4);
        // 棋盘格的边长(单位:mm)
        float squareSize = 30.0;
        cv::Size imageSize(5440,3648);

        // 存储棋盘格图像的角点坐标
        std::vector<std::vector<cv::Point2f>> imagePoints;
        // 存储棋盘格的世界坐标
        std::vector<std::vector<cv::Point3f>> objectPoints;

        // 加载棋盘格图像并提取角点
        for (int i = 0; i < m_imgList.size(); i++) {
            // 读取图像
            cv::Mat image = transmitQImage2cvMat(&m_imgList[i]);
            if (image.empty()) {
                QMessageBox::warning(Q_NULLPTR, tr("Warn"), tr("image index %1 transmitQImage2cvMat fail").arg(i));
                continue;
            }

            cv::Mat binary;
            cv::threshold(image, binary, 75, 255, cv::THRESH_BINARY);
            // 寻找角点
            std::vector<cv::Point2f> corners;
            bool found = cv::findChessboardCorners(binary, patternSize, corners);
            if (found) {
                // 亚像素精确化,使角点更准确
                cv::Mat gray;
                cv::cvtColor(binary, gray, cv::COLOR_BGR2GRAY);
                cv::cornerSubPix(gray, corners, cv::Size(11, 11), cv::Size(-1, -1),
                                 cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1));

                // 添加角点坐标和世界坐标
                imagePoints.push_back(corners);
                std::vector<cv::Point3f> objectCorners;
                for (int y = 0; y < patternSize.height; y++) {
                    for (int x = 0; x < patternSize.width; x++) {
                        objectCorners.push_back(cv::Point3f(x * squareSize, y * squareSize, 0));
                    }
                }
                objectPoints.push_back(objectCorners);
            } else {
                QString err = tr("iamge index %1 find corners fail").arg(i);
                qWarning() << err;
                QMessageBox::warning(Q_NULLPTR, tr("Warn"), err);
            }
        }

        // 相机内参矩阵和畸变系数
        cv::Mat cameraMatrix, distCoeffs;
        std::vector<cv::Mat> rvecs, tvecs;
        double rms = cv::calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs);

        // 打印标定结果
        qDebug() << "(cameraMatrix):";
        qDebug() << "[" <<cameraMatrix.at<double>(0,0) << "  " << cameraMatrix.at<double>(0,1) << "  " << cameraMatrix.at<double>(0,2);
        qDebug() << cameraMatrix.at<double>(1,0) << "  " << cameraMatrix.at<double>(1,1) << "  " << cameraMatrix.at<double>(1,2);
        qDebug() << cameraMatrix.at<double>(2,0) << "  " << cameraMatrix.at<double>(2,1) << "  " << cameraMatrix.at<double>(2,2) << "]";
        qDebug() << "(distCoeffs):";
        qDebug() << "[" << distCoeffs.at<double>(0,0) << distCoeffs.at<double>(0,1) << distCoeffs.at<double>(0,2)
                 << distCoeffs.at<double>(0,3) << distCoeffs.at<double>(0,4) << "]";
    }
	catch(const cv::Exception& e) {
        qWarning() << QString::fromStdString(e.what());
    }

2.3 使用标定结果

//其中,undist为原图,dist为去畸变后的结果,m_cameraMatrix为上述标定结果的内参矩阵,m_distCoeffs为上述标定结果的畸变矫正矩阵
//注意,dist必须初始化内存跟undist内存大小一致,否则函数调用会失败
cv::Mat undist = take_image_from_camera();
cv::Mat dist = undist.clone();
cv::undistort(undist, dist, m_cameraMatrix, m_distCoeffs);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值