OpenCV相机标定

相机标定

相机标定:简单的说,就是获得相机参数的过程。参数如:相机内参数矩阵,投影矩阵,旋转矩阵和平移矩阵等

  • 什么叫相机参数?

简单的说,将现实世界中的人、物,拍成一张图像(二维)。人或物在世界中的三维坐标,和图像上对应的二维坐标间的关系。表达两种不同维度坐标间的关系用啥表示?用相机参数。

  • 相机的成像原理

先来看一下,相机的成像原理:

如图所示,这是一个相机模型。将物体简化看成一个点。来自物体的光,通过镜头,击中图像平面(图像传感器),以此成像。d0是物体到镜头的距离,di时镜头到图像平面的距离,f是镜头的焦距。三者满足以下关系。

现在,简化上面的相机模型。

将相机孔径看成无穷小,只考虑中心位置的射线,这样就忽视了透镜的影响。然后由于d0远远大于di,将图像平面放在焦距处,这样物体在图像平面上成像为倒立的影像(没有透镜的影响,只考虑从中心的孔径进入的光线)。这个简化的模型就是针孔摄像机模型。然后,我们在镜头前,将图像平面放在焦距距离的位置,就可以简单获得一个笔直的图像(不倒立)。当然,这只是理论上的,你不可能将图像传感器从相机里拿出来,放在镜头前面。实际应用中,针孔摄像机应该是将成像后的图像倒过来,以获得正立的图像。

到此,我们获得了一个简化的模型,如下图:

h0是物体的高,hi是图像上物体的高,f是焦距(距离),d0是图像到镜头的距离。四者满足如下关系:

(1)

物体在图像中的高度hi,和d0成反比。也就是说,离镜头越远,物体在图像中越小,离得越近越大(好吧,这句话是废话)。

但通过这个式子,我们便能够预测三维中的物体,在图像(二维)中的位置。那么怎么预测?

  • 相机标定

如下图所示,根据上面简化的模型,考虑三维世界中的一个点,和其在图像(二维)中的坐标关系。

(X,Y,Z)为点的三维坐标,(x,y)为其通过相机成像后在图像(二维)上的坐标。u0和v0是相机的中心点(主点),该点位于图像平面中心(理论上是这样。但实际的相机会有几个像素的偏差)

现在只考虑y方向上,由于需要将三维世界中的坐标,转换为图像上的像素(图像上的坐标,实际上是像素的位置),需要求y方向上焦距等于多少个像素(用像素值表示焦距),Py表示像素的高,焦距f(米或毫米)。垂直像素表示的焦距为

根据式子(1),只考虑y方向。我们三维世界中得点,在图像(二维)中y的坐标。

同理,得到x的坐标。

现在,将上图中的坐标系的原点O,移动到图像的左上角。由于(x,y)是关于(u0,v0)的偏移,上面表示图像(二维)中点的坐标的式子不变。将式子以矩阵的形式重写,得。

其中,等式左边的第一个矩阵,叫做“相机内参数矩阵”,第二个矩阵叫(投影矩阵)。

更为一般的情况,开始时的参考坐标系不位于主点(中心点),需要额外两个参数“旋转向量”和“平移向量”来表示这个式子,这两个参数在不同视角中是不一样的。整合后,上述式子重写为。

  • 校正畸变

    通过相机标定,获得了相机参数后,可以计算两个映射函数(x坐标和y坐标),它们分别给出了没有畸变的图像坐标。将畸变的图像重新映射成为没有畸变的图像。

代码:

做相机标定时,一般用标定板(棋盘)拍摄一组图像,利用这些图像提取角点,通过角点在图像中得坐标和三维世界中的坐标(通常自定义3维坐标),计算相机参数。

std::vector<cv::Point2f>imageConers;
//提取标定图像角点,保存角点坐标(二维)
 cv::findChessboardCorners(image,
  boardSize, //角点数目如(6,4)六行,四列
  imageConers);

函数calibrateCamera完成相机标定工作。

cv::calibrateCamera(objectPoints,//三维坐标
 imagePoints, //二维坐标
 imageSize,//图像大小
 camerMatirx,//相机内参数矩阵
 disCoeffs,//投影矩阵
  rvecs, //旋转
  tvecs,//平移
flag  //标记opencv提供几种参数,可以参看在线的opencv document
);

计算畸变参数,去畸变

//计算畸变参数
cv::initUndistortRectifyMap(camerMatirx, disCoeffs,
    cv::Mat(), cv::Mat(), image.size(), CV_32FC1,
    map1, //x映射函数
    map2  //y映射函数
    );
//应用映射函数
cv::remap(image, //畸变图像
undistorted, //去畸变图像
map1, map2, cv::INTER_LINEAR);

现在整合代码。

  • 示例:

标头.h

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<opencv2\calib3d\calib3d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<string>
#include<vector>
class CameraCalibrator
{
private:
    //世界坐标
    std::vector < std::vector<cv::Point3f >> objectPoints;
    //图像坐标
    std::vector <std::vector<cv::Point2f>> imagePoints;
    //输出矩阵
    cv::Mat camerMatirx;
    cv::Mat disCoeffs;
    //标记
    int flag;
    //去畸变参数
    cv::Mat map1, map2;
    //是否去畸变
    bool mustInitUndistort;

    ///保存点数据
    void addPoints(const std::vector<cv::Point2f>&imageConers, const std::vector<cv::Point3f>&objectConers)
    {
        imagePoints.push_back(imageConers);
        objectPoints.push_back(objectConers);
    }
public:
    CameraCalibrator() :flag(0), mustInitUndistort(true){}
    //打开棋盘图片,提取角点
    int addChessboardPoints(const std::vector<std::string>&filelist,cv::Size &boardSize)
    {
        std::vector<cv::Point2f>imageConers;
        std::vector<cv::Point3f>objectConers;
        //输入角点的世界坐标
        for (int i = 0; i < boardSize.height; i++)
        {
            for (int j = 0; j < boardSize.width; j++)
            {
                objectConers.push_back(cv::Point3f(i, j, 0.0f));
            }
        }
        //计算角点在图像中的坐标
        cv::Mat image;
        int success = 0;
        for (int i = 0; i < filelist.size(); i++)
        {
            image = cv::imread(filelist[i],0);
            //找到角点坐标
            bool found = cv::findChessboardCorners(image, boardSize, imageConers);
            cv::cornerSubPix(image,
                imageConers,
                cv::Size(5, 5),
                cv::Size(-1, -1),
                cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS,
                30, 0.1));
            if (imageConers.size() == boardSize.area())
            {
                addPoints(imageConers, objectConers);
                success++;
            }
            //画出角点
            cv::drawChessboardCorners(image, boardSize, imageConers, found);
            cv::imshow("Corners on Chessboard", image);
            cv::waitKey(100);
        }
        return success;
    }

    //相机标定
    double calibrate(cv::Size&imageSize)
    {
        mustInitUndistort = true;
        std::vector<cv::Mat>rvecs, tvecs;
        //相机标定
        return cv::calibrateCamera(objectPoints, imagePoints, imageSize,
            camerMatirx, disCoeffs, rvecs, tvecs, flag);
    }
    ///去畸变
    cv::Mat remap(const cv::Mat &image)
    {
        cv::Mat undistorted;
        if (mustInitUndistort)
        {
            //计算畸变参数
            cv::initUndistortRectifyMap(camerMatirx, disCoeffs,
                cv::Mat(), cv::Mat(), image.size(), CV_32FC1, map1, map2);
            mustInitUndistort = false;
        }
        //应用映射函数
        cv::remap(image, undistorted, map1, map2, cv::INTER_LINEAR);
        return undistorted;
    }
    //常成员函数,获得相机内参数矩阵、投影矩阵数据
    cv::Mat getCameraMatrix() const { return camerMatirx; }
    cv::Mat getDistCoeffs()   const { return disCoeffs; }
};

源.cpp

#include"标头.h"
#include<iomanip>
#include<iostream>
int main()
{
    CameraCalibrator Cc;
    cv::Mat image;
    std::vector<std::string> filelist;
    cv::namedWindow("Image");
    for (int i = 1; i <= 22; i++)
    {
        ///读取图片
        std::stringstream s;
        s << "D:/images/chessboards/chessboard" << std::setw(2) << std::setfill(‘0‘) << i << ".jpg";
        std::cout << s.str() << std::endl;

        filelist.push_back(s.str());
        image = cv::imread(s.str(),0);
        cv::imshow("Image", image);
        cv::waitKey(100);
    }
    //相机标定
    cv::Size boardSize(6, 4);
    Cc.addChessboardPoints(filelist, boardSize);
    Cc.calibrate(image.size());

    //去畸变
    image = cv::imread(filelist[1]);
    cv::Mat uImage=Cc.remap(image);
    cv::imshow("原图像", image);
    cv::imshow("去畸变", uImage);
    //显示相机内参数矩阵
    cv::Mat cameraMatrix = Cc.getCameraMatrix();
    std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
    std::cout << cameraMatrix.at<double>(0, 0) << " " << cameraMatrix.at<double>(0, 1) << " " << cameraMatrix.at<double>(0, 2) << std::endl;
    std::cout << cameraMatrix.at<double>(1, 0) << " " << cameraMatrix.at<double>(1, 1) << " " << cameraMatrix.at<double>(1, 2) << std::endl;
    std::cout << cameraMatrix.at<double>(2, 0) << " " << cameraMatrix.at<double>(2, 1) << " " << cameraMatrix.at<double>(2, 2) << std::endl;

    cv::waitKey(0);
}

实验结果:

看以看到,相机内参数矩阵为

172.654 、0、157.829

0、184.195、118.635

0 、0 、1

时间: 2025-01-02 19:17:21

OpenCV相机标定的相关文章

OpenCV相机标定及距离估计(单目)

相机标定基本知识 对于摄像机模型,一幅视图是通过透视变换将三维空间中的点投影到图像平面.投影公式如下: 或者 这里(X, Y, Z)是一个点的世界坐标,(u, v)是点投影在图像平面的坐标,以像素为单位.A被称作摄像机矩阵,或者内参数矩阵.(cx, cy)是基准点(通常在图像的中心),fx, fy是以像素为单位的焦距.所以如果因为某些因素对来自于摄像机的一幅图像升采样或者降采样,所有这些参数(fx, fy, cx和cy)都将被缩放(乘或者除)同样的尺度.内参数矩阵不依赖场景的视图,一旦计算出,可

opencv相机标定(Python)

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

OpenCV相机标定和姿态更新

原帖地址: http://blog.csdn.net/aptx704610875/article/details/48914043 http://blog.csdn.net/aptx704610875/article/details/48915149

SLAM入门之视觉里程计(6):相机标定 张正友经典标定法详解

想要从二维图像中获取到场景的三维信息,相机的内参数是必须的,在SLAM中,相机通常是提前标定好的.张正友于1998年在论文:"A Flexible New Technique fro Camera Calibration"提出了基于单平面棋盘格的相机标定方法.该方法介于传统的标定方法和自标定方法之间,使用简单实用性强,有以下优点: 不需要额外的器材,一张打印的棋盘格即可. 标定简单,相机和标定板可以任意放置. 标定的精度高. 相机的内参数 设\(P=(X,Y,Z)\)为场景中的一点,在

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

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

基于opencv的相机标定

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

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

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

相机标定 matlab opencv ROS三种方法标定步骤(1)

一 .理解摄像机模型,网上有很多讲解的十分详细,在这里我只是记录我的整合出来的资料和我的部分理解 计算机视觉领域中常见的三个坐标系:图像坐标系,相机坐标系,世界坐标系,实际上就是要用矩阵来表示各个坐标系下的转换 首先在图像坐标系下与相机坐标系的关系 可得出   Xcam=x/dx+x0,    Ycam=y/dy+y0  表示为矩阵形式 Xcam           1/dx   0      x0          x Ycam      =    0     1/dy   y0    *  

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

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