相机标定中的一些知识点(小孔模型、鱼眼模型、畸变、泰勒展开)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了相机标定中的一些知识点(小孔模型、鱼眼模型、畸变、泰勒展开)相关的知识,希望对你有一定的参考价值。

参考技术A 最近在标定相机,然后遇到各种问题,看了将近一周终于明白了一些东西,回忆了一下好像就是之前工程光学和高数的东西。。。怕忘了在这里记一下。。。

这个是最常用的成像模型,其实就是认为物体和相机成的像是相似的,

在小孔模型下相机的畸变其实是比较小的。

小孔模型下畸变分为径向畸变和切向畸变,用opencv做标定的时候会得到cx、cy、fx、fy这几个内参以外,还会得到k1、k2、k3、p1、p2这个5个畸变参数,k1-k3是径向畸变系数,p1、p2是切向畸变系数。

切向畸变是由于制造工艺缺陷导致的镜头与成像平面不平行造成的。大概会变成这样的一个图案。

通常会用这样的公式来描述切向的畸变以及反畸变矫正

径向畸变的原因是光线在远离透镜中心的地方比靠近中心的地方更加弯曲,通常是镜头设计的原因。可以发现径向畸变主要是和θ有关(θ是只物点和成像中心的连线与主光轴的夹角)。而和角度相关的函数通常都是由sin、cos这些构成的,所以也可以用泰勒级数进行描述(个人理解)。泰勒级数是一个逐次逼近的函数,可以无线精确但是通常会根据经验人为的选择精确到几次方,在这里通常精确到6次方,所以就变成了下面这个公司,其中的k1、k2、k3就是opencv生成的那3个系数。

小孔模型有一个局限就是当视角超过180度的时候就没办法工作了,因为相机平面要无限大才行。所以FOV超过一定程度的时候就需要换成鱼眼模型。

他的做法是先把所有的物成像时先投射到一个半球上,然后再把这个半球投影到成像平面,可以想到这时所有的物会成像为一个圆形。所以我们平时看到的鱼眼镜头的照片都是一个圆形。

这里发生了几个变化,1个是fx和fy变成了一样的数值,因为球和圆的长短轴是相等的。另外一个是取消了切向畸变,我个人的理解是因为光轴的偏转可以用fx、fy来体现,所以切向畸变就没意义了。
所以畸变就只有径向畸变了。所以也只和θ有关了。畸变系数在opencv中会得到k1-k4,而matlab中会求到k1-k3。

看到了这篇文章我觉得讲得挺好的,就是为什么要做泰勒展开以及泰勒展开是如何推导的,不过他用的这个图实在是有点尴尬,上班的时候打开总是会莫名的心虚。。。 https://www.zhihu.com/question/25627482
我大致的总结一下大概的问题是,因为sin、cos这些函数比较难直接直接求得某一处的函数值,比如sin2这种除非用计算器不然很难算。所以我们想能不能用一些容易求得的函数来近似的描述他。比如一个函数f(x)我们用另外一个函数g(x)来描述。如何让他们相等,可以选一个点比如(0,1)这个点,我们让f(0)=g(0),然后让f和g的导数也相等,然后让导数的导数也相等。。。这样就可以逐次逼近f(x),但是因为sin、cos是可以无限求导的,而多项式求导级数会逐渐下降,所以只能逐次逼近这样最终会得到一个g(x) = k1x+k2x2+k3x3......这样的多项式。其中k1-k3这些就是泰勒展开的系数。

OpenCV相机标定与畸变校正

点击我爱计算机视觉标星,更快获取CVML新技术



本文转载自OpenCV学堂。

OpenCV单目相机标定,图像畸变校正

相机标定定义与原理

01

在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)。相机标定常见的分为:

  • 单目相机标定

  • 双目相机标定

相机标定是想从二维的图像中获取三维信息,实现图像的畸变校正、对象测量、三维重建等。由于光线投射导致实际对象物体跟投影到2D平面的图像不一致,幸运的是这种不一致性是稳定的,我们可以通过对相机标定,计算出畸变参数来实现对后续图像的畸变校正。根据标定技术不一样可以分为下面几类标定方法:

  • 基于3D对象参照标定

  • 基于2D平面标定

  • 基于1D线性标定

  • 自标定

最常见的相机成像方式是基于pinhole的模型、它的成像模型可以图示如下:


下面我们首先对这个相机成像模型做一番解释

OpenCV相机标定与畸变校正

通过标定算法同时求出相机内参与外参。最常用的算法是张正友标定算法。OpenCV/Matlab中均已经实现该算法。

标定板介绍与制作

02

要想实现对相机的标定,我们首先需要给相机找到个参考对象,常见的就是标定版的类型有如下几种

  • Chessboard

  • Circel-grid

  • RandPattern

  • ArUco

  • ChArUc

OpenCV相机标定与畸变校正

OpenCV源码在其sample/data目录下面一个自带的棋盘图(chessboard.png),显示如下:

OpenCV相机标定与畸变校正

在标定的时候,算法要求提供的棋盘格的宽度与高度,还有他们的间隔距离。需要特别注意是这里的宽高是指他们的内部交叉点的个数,以上图为例,它的大小为7x7而不是8x8。间隔是指棋盘格之间的距离,可以用像素距离表示,也可以用实际毫米为单位表示。


制作标定版与图像生成

03

最简单的办法就是把上述图像直接打印出来,贴到一个塑料底板上就好啦。如果是土豪可以直接购买各种玻璃底板的标定板。另外还有一个更恶搞的方法,连打印都省啦,直接把chessboard.png这张图在另外一台电脑的显示器上显示,然后把摄像头对着它各种牌即可完成图像数据采集。这个是我手写的采集程序代码,每次想保存图像的时候请安Q字母键即可,代码如下:

void create_images() {
    Mat frame;
    VideoCapture capture(0);
    int index = 1;
    while (true) {
        bool ret = capture.read(frame);
        flip(frame, frame, 1);
        if (!ret) break;
        imshow("frame", frame);
        char c = waitKey(50);
        printf("%d \n", c);
        if (c == 113) { // Q
            imwrite(format("D:/images/zsxq/%d.png", index), frame);
            index += 1;
        }
        if (c == 27) {
            break// ESC
        }
    }
    capture.release();
}

记得拿着棋盘格图,在镜头面前各种摆POSE,这个是属于你的表演时间,不要客气!具体参考下图:

OpenCV相机标定与畸变校正

相机标定程序实现

04

大家好,现在我们开始程序实现环节,OpenCV中在camera模块中已经实现了张正友标定算法。我们只需要正确调用,就可以计算出相机的内参与外参,完成相机的标定。具体的代码实现步骤如下:


定义相机标定的相关常量设置与变量

// load image files
vector<string> files;
glob("D:/images/camera2d", files);

// 定义变量
vector<vector<Point2f>> imagePoints;
vector<vector<Point3f>> objectPoints;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 300.001);
int numCornersHor = 7;
int numCornersVer = 7;
int numSquares = 50;
vector<Point3f> obj;
for (int i = 0; i < numCornersHor; i++)
    for (int j = 0; j < numCornersVer; j++)
        obj.push_back(Point3f((float)j * numSquares, (float)i * numSquares, 0));


发现与绘制棋盘格位置

// 发现棋盘格与绘制
Size s;
for (int i = 0; i < files.size(); i++) {
    printf("image file : %s \n", files[i].c_str());
    Mat image = imread(files[i]);
    s = image.size();
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    vector<Point2f> corners;
    bool ret = findChessboardCorners(gray, Size(77), corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);
    if (ret) {
        cornerSubPix(gray, corners, Size(1111), Size(-1-1), criteria);
        drawChessboardCorners(image, Size(77), corners, ret);
        imagePoints.push_back(corners);
        objectPoints.push_back(obj);
        imshow("calibration-demo", image);
        waitKey(500);
    }
}


发现棋盘格显示如下(我是直接打印OpenCV自带那张图的)

OpenCV相机标定与畸变校正


相机校正-计算内参数

// 相机校正
Mat intrinsic = Mat(33, CV_32FC1);
Mat distCoeffs;
vector<Mat> rvecs;
vector<Mat> tvecs;
intrinsic.ptr<float>(0)[0] = 1;
intrinsic.ptr<float>(1)[1] = 1;
calibrateCamera(objectPoints, imagePoints, s, intrinsic, distCoeffs, rvecs, tvecs);


畸变图像校正

05

OpenCV相机标定与畸变校正


关于畸变类型,常见的图像畸变类型有径向与切向畸变、OpenCV中的相机标定方法只能对径向畸变有效,使用内参对畸变图像实现校正。相关的代码如下:

// 畸变校正
for (int i = 0; i < files.size(); i++) {
    Mat dst;
    Mat image = imread(files[i]);
    undistort(image, dst, intrinsic, distCoeffs);
    imshow("image", image);
    imshow("undistort image", dst);
    waitKey(1000);
}

OpenCV相机标定与畸变校正




CV细分方向交流群


52CV已经建立多个CV专业交流群,包括:目标跟踪、目标检测、语义分割、姿态估计、人脸识别检测、医学影像处理、超分辨率、神经架构搜索、GAN、强化学习等,扫码添加CV君拉你入群,如已经为CV君其他账号好友请直接私信,

请务必注明相关方向,比如:目标检测

(不会时时在线,如果没能及时通过验证还请见谅)


长按关注我爱计算机视觉

以上是关于相机标定中的一些知识点(小孔模型、鱼眼模型、畸变、泰勒展开)的主要内容,如果未能解决你的问题,请参考以下文章

相机标定中镜头的畸变系数是否必须小于1

opencv相机标定

相机标定之相机畸变模型

相机畸变与标定

Python OpenCV 单目相机标定坐标转换相关代码(包括鱼眼相机)

相机标定的内参、外参、畸变参数