相机标定之畸变矫正与反畸变计算

  相机标定问题已经是比较成熟的问题,OpenCV中提供了比较全面的标定、矫正等函数接口。但是如果我想通过一张矫正好的图像,想获得原始的畸变图,却没有比较好的方法,这里讨论了点的畸变和反畸变问题。

1.问题提出:给定一些已经经过矫正的二维点集,如何获得矫正前带畸变的二维点集?

2.理论基础:理论基础无它,就是相机的小孔成像模型和畸变参数模型,

  这里需要注意,k1,k2等为径向畸变参数,p1,p2为切向畸变参数,s1,s2为薄棱镜畸变参数(常忽略),x‘为理想的无畸变的图像像素坐标或者点(Points)坐标,x‘‘是带透镜畸变参数的图像像素或点坐标,上述公式详尽表达了它们之间的关系。通俗点讲,x‘,y‘是指通过针孔模型求出来的理想点的位置,x‘‘,y‘‘是其真实位置,所以要通过后面的畸变模型进行近似,获得x‘‘,y‘‘的值,但是在实际应用时,x‘‘,y‘‘通常是已知的,即我们通过相机获得了带畸变的图像,但是我们需要反算回去得到x‘,y‘,OpenCV提供了这样的过程函数接口,可以直接使用。

  而这里想做的是通过矫正图得到畸变图点,即通过x‘,y’得到x‘‘,y‘‘,所以只要通过上述公式进行求解就行了。

3.求解过程:

  直接给出相关程序:

void myDistortPoints(const std::vector<cv::Point2d> & src, std::vector<cv::Point2d> & dst,
    const cv::Mat & cameraMatrix, const cv::Mat & distortionCoeff)
{

    dst.clear();
    double fx = cameraMatrix.at<double>(0, 0);
    double fy = cameraMatrix.at<double>(1, 1);
    double ux = cameraMatrix.at<double>(0, 2);
    double uy = cameraMatrix.at<double>(1, 2);

    double k1 = distortionCoeff.at<double>(0, 0);
    double k2 = distortionCoeff.at<double>(0, 1);
    double p1 = distortionCoeff.at<double>(0, 2);
    double p2 = distortionCoeff.at<double>(0, 3);
    double k3 = distortionCoeff.at<double>(0, 4);
    double k4;//
    double k5;//
    double k6;//

    for (unsigned int i = 0; i < src.size(); i++)
    {
        const cv::Point2d & p = src[i];
     //获取的点通常是图像的像素点,所以需要先通过小孔相机模型转换到归一化坐标系下;
        double xCorrected = (p.x - ux) / fx;
        double yCorrected = (p.y - uy) / fy;

        double xDistortion, yDistortion;
      //我们已知的是经过畸变矫正或理想点的坐标;
        double r2 = xCorrected*xCorrected + yCorrected*yCorrected;

        double deltaRa = 1. + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2;
        double deltaRb = 1 / (1. + k4 * r2 + k5 * r2 * r2 + k6 * r2 * r2 * r2);
        double deltaTx = 2. * p1 * xCorrected * yCorrected + p2 * (r2 + 2. * xCorrected * xCorrected);
        double deltaTy = p1 * (r2 + 2. * yCorrected * yCorrected) + 2. * p2 * xCorrected * yCorrected;
     //下面为畸变模型;
        xDistortion = xCorrected * deltaRa * deltaRb + deltaTx;
        yDistortion = yCorrected * deltaRa * deltaRb + deltaTy;
     //最后再次通过相机模型将归一化的坐标转换到像素坐标系下;
        xDistortion = xDistortion * fx + ux;
        yDistortion = yDistortion * fy + uy;

        dst.push_back(cv::Point2d(xDistortion, yDistortion));
    }

}

4.存在的问题:经过上面的过程,我们就获得了在畸变原图中点的坐标,但是有一点值得注意的是,我们将获得的畸变图中的点经过OpenCV提供的UndistortPoints()函数进行矫正的时候,不能得到原始的点,即这里存在误差。这种误差是由于自己写的畸变模型和OpenCV提供的畸变函数模型不同导致的,有兴趣的可以查看OpenCV源码,你会发现后面关于 r 的平方和 r 的四次方,六次方在OpenCV中都是平方项,所以计算结果存在误差,这里有两个解决办法,第一是修改上述代码,统一改成平方项;第二就是自己实现UndistortPoints函数:

void myUndistortPoints(const std::vector<cv::Point2d> & src, std::vector<cv::Point2d> & dst,
    const cv::Mat & cameraMatrix, const cv::Mat & distortionCoeff)
{

    dst.clear();
    double fx = cameraMatrix.at<double>(0, 0);
    double fy = cameraMatrix.at<double>(1, 1);
    double ux = cameraMatrix.at<double>(0, 2);
    double uy = cameraMatrix.at<double>(1, 2);

    double k1 = distortionCoeff.at<double>(0, 0);
    double k2 = distortionCoeff.at<double>(0, 1);
    double p1 = distortionCoeff.at<double>(0, 2);
    double p2 = distortionCoeff.at<double>(0, 3);
    double k3 = distortionCoeff.at<double>(0, 4);
    double k4;
    double k5;
    double k6;

    for (unsigned int i = 0; i < src.size(); i++)
    {
        const cv::Point2d & p = src[i];
     //首先进行坐标转换;
        double xDistortion = (p.x - ux) / fx;
        double yDistortion = (p.y - uy) / fy;

        double xCorrected, yCorrected;

        double x0 = xDistortion;
        double y0 = yDistortion;
     //这里使用迭代的方式进行求解,因为根据2中的公式直接求解是困难的,所以通过设定初值进行迭代,这也是OpenCV的求解策略;
        for (int j = 0; j < 10; j++)
        {
            double r2 = xDistortion*xDistortion + yDistortion*yDistortion;

            double distRadialA = 1 / (1. + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2);
            double distRadialB = 1. + k4 * r2 + k5 * r2 * r2 + k6 * r2 * r2 * r2;

            double deltaX = 2. * p1 * xDistortion * yDistortion + p2 * (r2 + 2. * xDistortion * xDistortion);
            double deltaY = p1 * (r2 + 2. * yDistortion * yDistortion) + 2. * p2 * xDistortion * yDistortion;

            xCorrected = (x0 - deltaX)* distRadialA * distRadialB;
            yCorrected = (y0 - deltaY)* distRadialA * distRadialB;

            xDistortion = xCorrected;
            yDistortion = yCorrected;
        }
     //进行坐标变换;
        xCorrected = xCorrected * fx + ux;
        yCorrected = yCorrected * fy + uy;

        dst.push_back(cv::Point2d(xCorrected, yCorrected));
    }

}

  上面的迭代求解过程是值得学习的地方,因为所求的x本身就被包含在r中,所以直接求解变得几乎不可能,而上面的迭代方法提供了一种解决方法,当迭代次数足够多时,求解接近精确值。

5.结果展示:下面给出通过设定好相机的畸变参数和内参矩阵,给定一幅带有点线的原图,通过3生成畸变图,然后再通过4反算原图的过程:

  注:上面的线是在一个全黑的Mat矩阵中画点生成的。

原文地址:https://www.cnblogs.com/mafuqiang/p/8134617.html

时间: 2024-10-12 10:50:52

相机标定之畸变矫正与反畸变计算的相关文章

双摄像头立体成像(三)-畸变矫正与立体校正

畸变矫正是上一篇博文的遗留问题,当畸变系数和内外参数矩阵标定完成后,就应该进行畸变的矫正,以达到消除畸变的目的,此其一. 在该系列第一部分的博文中介绍的立体成像原理中提到,要通过两幅图像估计物点的深度信息,就必须在两幅图像中准确的匹配到同一物点,这样才能根据该物点在两幅图像中的位置关系,计算物体深度.为了降低匹配的计算量,两个摄像头的成像平面应处于同一平面.但是,单单依靠严格的摆放摄像头来达到这个目的显然有些困难.立体校正就是利用几何图形变换(Geometric Image Transforma

相机标定:单目图像矫正分析

图像矫正的本质,其实就是重投影的过程,即[像素坐标→物理坐标→像素坐标]的过程.只不过在重投影过程中我们可以改变投影矩阵(修改后的投影矩阵我把它称为扩展投影矩阵)从而模拟镜头缩放和平移的效果. 图像矫正可通过两种方式执行,我称之为正向矫正和逆向矫正.正向矫正是通过畸变坐标算出标准坐标,而逆向矫正是通过标准坐标算出畸变坐标.Opencv中UndistortPoints就是执行的正向矫正过程,而initUndistortRectifyMap执行的是逆向矫正过程. 正向矫正的流程为:畸变像素坐标→畸变

基于OpenCV的立体相机标定StereoCalibration与目标三维坐标定位

说明:以下涉及到的一些公式以及图片来自于Learning OpenCV. 做了快2个月的立体相机标定,遇到了一些问题,也有了一些体会,在这里记下来. 1.在做立体相机标定的时候,标定板的规范与否直接影响到最后标定的结果,进而会影响目标3D坐标重建. 这里说的规范指的是,打印的棋盘格(或者圆点)需要保证每个square都是严格大小一致的,即打印出来后每个棋盘格大小应一样:打印出来的棋盘格要尽量平整的附在某一平面或者玻璃板上,然后才能用来拍摄标定图像:测量squareSize的时候,要尽可能的精确,

张正友相机标定Opencv实现以及标定流程&amp;&amp;标定结果评价&amp;&amp;图像矫正流程解析(附标定程序和棋盘图)

使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么需要标定,标定需要的输入和输出分别是哪些? 相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像. 相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上). 相机标定的输出:摄像机的内参.外参系数. 这三个基础的问题就决定了使用Openc

相机标定究竟在标定什么?[转]

相机标定可以说是计算机视觉/机器视觉的基础,但是初学者不易上手,本文将给读者整理一遍相机标定的逻辑,并在文末回答评论区提出的问题.分为以下内容: 相机标定的目的和意义 相机成像过程的简化与建模 针孔相机模型的数学描述 标定针孔相机模型的参数 相机标定的目的和意义 我们所处的世界是三维的,而照片是二维的,这样我们可以把相机认为是一个函数,输入量是一个场景,输出量是一幅灰度图.这个从三维到二维的过程的函数是不可逆的. 相机标定的目标是我们找一个合适的数学模型,求出这个模型的参数,这样我们能够近似这个

opencv相机标定(Python)

相机标定 相机标定的目的 获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像. 相机标定的输入 标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上). 相机标定的输出 摄像机的内参.外参系数. 拍摄的物体都处于三维世界坐标系中,而相机拍摄时镜头看到的是三维相机坐标系,成像时三维相机坐标系向二维图像坐标系转换.不同的镜头成像时的转换矩阵不同,同时可能

双目相机标定以及立体测距原理及OpenCV实现

作者:dcrmg 单目相机标定的目标是获取相机的内参和外参,内参(1/dx,1/dy,Cx,Cy,f)表征了相机的内部结构参数,外参是相机的旋转矩阵R和平移向量t.内参中dx和dy是相机单个感光单元芯片的长度和宽度,是一个物理尺寸,有时候会有dx=dy,这时候感光单元是一个正方形.Cx和Cy分别代表相机感光芯片的中心点在x和y方向上可能存在的偏移,因为芯片在安装到相机模组上的时候,由于制造精度和组装工艺的影响,很难做到中心完全重合.f代表相机的焦距. 双目标定的第一步需要分别获取左右相机的内外参

相机标定:关于用Levenberg-Marquardt算法在相机标定中应用

LM算法在相机标定的应用共有三处. (1)单目标定或双目标定中,在内参固定的情况下,计算最佳外参.OpenCV中对应的函数为findExtrinsicCameraParams2. (2)单目标定中,在内外参都不固定的情况下,计算最佳内外参.OpenCV中对应的函数为calibrateCamera2. (3)双目标定中,在左右相机的内外参及左右相机的位姿都不固定的情况下,计算最佳的左右相机的内外参及最佳的左右相机的位姿矩阵.OpenCV中对应的函数为stereoCalibrate. 本文文阅读前提

基于opencv的相机标定

双目相机标定以及立体测距原理及OpenCV实现 作者:dcrmg 单目相机标定的目标是获取相机的内参和外参,内参(1/dx,1/dy,Cx,Cy,f)表征了相机的内部结构参数,外参是相机的旋转矩阵R和平移向量t.内参中dx和dy是相机单个感光单元芯片的长度和宽度,是一个物理尺寸,有时候会有dx=dy,这时候感光单元是一个正方形.Cx和Cy分别代表相机感光芯片的中心点在x和y方向上可能存在的偏移,因为芯片在安装到相机模组上的时候,由于制造精度和组装工艺的影响,很难做到中心完全重合.f代表相机的焦距