OpenCV函数用法之calibrateCamera

参考链接:opencv中标定函数calibrateCamera_ychl87的专栏-CSDN博客

OpenCV相机标定calibrateCamera坐标系详解_shenxiaolu1984的专栏-CSDN博客

OpenCV 相机校正过程中,calibrateCamera函数projectPoints函数的重投影误差的分析_是否龍磊磊真的一无所有的博客-CSDN博客

 从零开始学习「张氏相机标定法」 (qq.com)

 还未整理:

 相机标定原理讲解 - haoxing990 - 博客园 (cnblogs.com)

 一分钟详解OpenCV相机标定calibrateCamera - 知乎 (zhihu.com)

在OpenCV中,可以使用calibrateCamera函数,通过多个视角的2D/3D对应,求解出该相机的内参数和每一个视角的外参数。

相机标定的目的是:建立相机成像几何模型并矫正透视畸变。

建立相机几何成像模型:计算机视觉的首要任务就是要通过拍摄到的图像信息获取到物体在真实三维世界里相对应的信息,于是,建立物体从三维世界映射到相机成像平面这一过程中的几何模型就显得尤为重要,而这一过程最关键的部分就是要得到相机的内参和外参。

矫正透镜畸变:由于透镜的制造工艺,会使成像产生多种形式的畸变,于是为了去除畸变(使成像后的图像与真实世界的景象保持一致),人们计算并利用畸变系数来矫正这种像差。

1.基础知识准备

世界坐标系的三维点投影到成像坐标系中的二维点的投影公式如下:

(1)

其中:

(X,Y,Z)为世界坐标系中的三维点;(u,v)为成像面坐标系中的二维点;

A为相机的内参数矩阵:(cx,cy)为主光轴点,一般为图像的中心;fx和fy为焦距;

[R|t]为相机的外参数矩阵:R为旋转矩阵,t为位移矩阵;

上述公式的简单推理过程如下:

(2)

考虑到镜头畸变:

(3)

其中,k1,k2,k3,k4,k5和k6为径向畸变,p1和p2为轴向畸变。在opencv中,畸变矩阵的参数为(k1,k2,p1,p2[,k3[,k4,k5,k6]]])。

2.calibrateCamera()函数的常用使用方法:

double calibrateCamera(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags=0)

其中:

objectPoints为世界坐标系中的点。在使用时,应该输入一个三维点vector的vector,即vector<vector<Point3f>> objectPoints。第一层vector表示每一个视角,第二层vector表示每一个点。

如果使用OpenCV自带的棋盘格,可以直接传入交叉点(不包括边角)的实际坐标,以物理世界尺度(例如毫米)为单位。

写坐标时,要保证z轴为0,按照先x变化,后y变化,从小到大的顺序来写。如果网格尺寸为5厘米,写作:(0,0,0),(5,0,0), (10,0,0)...(0,5,0), (5,5,0), (10,5,0),...

如下图例子,x方向是8个交叉点,y方向3个交叉点。

在objectPoints中,我们经常只设定了每个交叉点的间隔,具体xyz指向哪里却没有给出。

在使用calibrateCamera标定前,一般使用findChessboardCorners()函数获得棋盘标定板的角点位置。这时候,需要使用drawChessboardCorners函数,把检测到的2D点在原图上显示出来:

与前述相同,内角点的标注先变化x,后变化y。先画出的角点用红色标注,并逐渐过渡到紫色(按红橙黄绿青蓝紫的顺序)。

回顾我们写objectPoints时的方式,可以推断出世界坐标的方向:图中x轴正方向向左,y轴正方向远离镜头,根据右手螺旋,z轴正方向向下。

总结来说,calibrateCamera函数给出的世界坐标方向,是由obejctPoints的设定顺序,以及findChessboardCorners的检测顺序共同决定的。

但是,findChessboardCorners先检测那个角点并不确定。如下图:

注:另一种常见用法是,先用calibrateCamera标定好摄像机内参,而后使用solvePnP函数只求解外参。solvePnP函数的参数意义和calibrateCamera类似。(暂存,还没用到过)

imagePoints为其对应的图像坐标系中的点。和objectPoints一样,应该输入std::vector<std::vector<cv::Point2f>> imagePoints型的变量。

这个值可以通过findChessboardCorners函数从图像中获得。注意:传入findChessboardCorners函数的patternSize参数,要和objectPoints中的行列数统一。

imageSize为图像的大小,在计算相机的内参数和畸变矩阵需要用到;

cameraMatrix为内参数矩阵。输入一个cv::Mat cameraMatrix即可。从公式(1)可以看出该矩阵的大小为3*3.

distCoeffs为畸变矩阵。输入一个cv::Mat distCoeffs即可。具体尺寸取决于falgs。(对图像坐标系进行进一步扭曲。这两个参数是内参数,可以把摄像机坐标系转换成图像坐标系。)

rvecs为旋转向量;应该输入一个cv::Mat的vector,即vector<cv::Mat> rvecs因为每个vector<Point3f>会得到一个rvecs。每个vec为3*1,可以用Rodrigues函数转换为3*3的旋转矩阵。

tvecs为位移向量;和rvecs一样,也应该为vector<cv::Mat> tvecs。每个vec为3*1。

        参数rvecs和tvecs是外参数,每一个视图不同,可以把世界坐标系转换成摄像机坐标系。

flags为标定时所采用的参数。包括决定是否使用初始值、扭曲参数个数等,有如下几个参数:

CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,cx,cy的估计值。否则的话,将初始化(cx,cy)图像的中心点,使用最小二乘估算出fx,fy。如果内参数矩阵和畸变居中已知的时候,应该标定模块中的solvePnP()函数计算外参数矩阵。

CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。

CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。

CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。

CV_CALIB_FIX_K1,...,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。如果设置了CV_CALIB_USE_INTRINSIC_GUESS参数,

CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。

3.calibrateCamera的使用例子:

在python中:

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objp_list,corners_list, img_size, None, None)

在C++中:

    int cols = 10;
    int rows = 7;
    float distance = 30;    //间距30mm
 
    cv::Size patternSize(cols,rows);
    std::vector<cv::Point2f> corners;
    std::vector<std::vector<cv::Point2f>> cornersVect;
    std::vector<cv::Point3f> worldPoints;
    std::vector<std::vector<cv::Point3f>> worldPointsVect;
 
    for (int i=0;i<cols;i++)
    {
        for (int j=0;j<rows;j++)
        {
            worldPoints.push_back(cv::Point3f(i*distance,j*distance,0));
        }
    }
    
    bool find=cv::findChessboardCorners(image,patternSize,corners);
    cv::drawChessboardCorners(image,patternSize,corners,find);
    cv::Mat cameraMatirx,distCoeffs;
    std::vector<cv::Mat> rvecs,tvecs,rvecs2,tvecs2;
    if (find)
    {    
        cornersVect.push_back(corners);
        worldPointsVect.push_back(worldPoints);
        cv::calibrateCamera(worldPointsVect,cornersVect,image.size(),cameraMatirx,distCoeffs,rvecs,tvecs);
    }

还需要注意的是,在opencv版本2.4中(现在的opencv版本为4.5),当需要计算获得内参数矩阵时,即不使用CV_CALIB_USE_INTRINSIC_GUESS参数时,要求输入的objectPoints的为平面标定模式,即Z轴都为零。否则会出错。

如果对calibrateCamera的详细算法感兴趣,可以阅读张正友的标定算法A Flexible New Technique for Camera Calibration。可以从google scholar上搜索下载。

`cv2.calibrateCamera` 是 OpenCV 中用于相机标定的一个函数,它可以帮助估计相机的内部参数和外部参数,以便能够将3D世界坐标映射到2D图像坐标。在使用这个函数之前,你需要准备一组用于标定的棋盘格图像(通常是不同角度的同一棋盘格),然后使用这些图像中的角点来计算相机参数。 以下是使用 `cv2.calibrateCamera` 函数的基本步骤: 1. 准备棋盘格角点数据:首先需要检测出每一幅标定图像上的棋盘格角点,通常使用 `cv2.findChessboardCorners` 函数来实现。 2. 存储角点数据:将检测到的角点数据存储起来,`calibrateCamera` 函数将会使用这些数据来估计参数。 3. 设置世界坐标系:由于棋盘格是已知尺寸的,可以设定棋盘格角点的世界坐标。这些坐标构成了一个3D点集。 4. 调用 `calibrateCamera` 函数:使用检测到的角点数据和世界坐标集来计算相机参数。 以下是一个简单的代码示例: ```python import numpy as np import cv2 import glob # 准备对象点,如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) # 假设使用的是 7 x 6 的棋盘格 objp = np.zeros((6*7, 3), np.float32) objp[:,:2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2) # 用于存储所有图像的对象点和图像点的数组 objpoints = [] # 真实世界中的3D点 imgpoints = [] # 图像中的2D点 # 读取标定图像 images = glob.glob('path_to_calib_images/*.jpg') # 用实际的图片路径替换 for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 寻找棋盘格角点 ret, corners = cv2.findChessboardCorners(gray, (7,6), None) # 如果找到足够的角点,则将对象点添加到 objpoints ,图像点到 imgpoints if ret == True: objpoints.append(objp) imgpoints.append(corners) # 绘制并显示角点 img = cv2.drawChessboardCorners(img, (7,6), corners, ret) cv2.imshow('img', img) cv2.waitKey(500) cv2.destroyAllWindows() # 标定相机 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None) # 输出结果 print("相机矩阵:\n", mtx) print("畸变系数:\n", dist) print("旋转向量:\n", rvecs) print("平移向量:\n", tvecs) ``` 确保替换 `'path_to_calib_images/*.jpg'` 为实际的标定图像路径,以及调整棋盘格的大小 `(7,6)` 以匹配实际使用的棋盘格大小。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值