• OpenCV 相机标定


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

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

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

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

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

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

    物体在图像中的高度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维坐标),计算相机参数。

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

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

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

    计算畸变参数,去畸变

     1 //计算畸变参数
     2 cv::initUndistortRectifyMap(camerMatirx, disCoeffs,
     3     cv::Mat(), cv::Mat(), image.size(), CV_32FC1, 
     4     map1, //x映射函数
     5     map2  //y映射函数
     6     );
     7 //应用映射函数
     8 cv::remap(image, //畸变图像
     9 undistorted, //去畸变图像
    10 map1, map2, cv::INTER_LINEAR);

    现在整合代码。

    • 示例:

    标头.h

      1 #include<opencv2corecore.hpp>
      2 #include<opencv2highguihighgui.hpp>
      3 #include<opencv2imgprocimgproc.hpp>
      4 #include<opencv2calib3dcalib3d.hpp>
      5 #include <opencv2/features2d/features2d.hpp>
      6 #include<string>
      7 #include<vector>
      8 class CameraCalibrator
      9 {
     10 private:
     11     //世界坐标
     12     std::vector < std::vector<cv::Point3f >> objectPoints;
     13     //图像坐标
     14     std::vector <std::vector<cv::Point2f>> imagePoints;
     15     //输出矩阵
     16     cv::Mat camerMatirx;
     17     cv::Mat disCoeffs;
     18     //标记
     19     int flag;
     20     //去畸变参数
     21     cv::Mat map1, map2;
     22     //是否去畸变
     23     bool mustInitUndistort;
     24 
     25     ///保存点数据
     26     void addPoints(const std::vector<cv::Point2f>&imageConers, const std::vector<cv::Point3f>&objectConers)
     27     {
     28         imagePoints.push_back(imageConers);
     29         objectPoints.push_back(objectConers);
     30     }
     31 public:
     32     CameraCalibrator() :flag(0), mustInitUndistort(true){}
     33     //打开棋盘图片,提取角点
     34     int addChessboardPoints(const std::vector<std::string>&filelist,cv::Size &boardSize)
     35     {
     36         std::vector<cv::Point2f>imageConers;
     37         std::vector<cv::Point3f>objectConers;
     38         //输入角点的世界坐标
     39         for (int i = 0; i < boardSize.height; i++)
     40         {
     41             for (int j = 0; j < boardSize.width; j++)
     42             {
     43                 objectConers.push_back(cv::Point3f(i, j, 0.0f));
     44             }
     45         }
     46         //计算角点在图像中的坐标
     47         cv::Mat image;
     48         int success = 0;
     49         for (int i = 0; i < filelist.size(); i++)
     50         {
     51             image = cv::imread(filelist[i],0);
     52             //找到角点坐标
     53             bool found = cv::findChessboardCorners(image, boardSize, imageConers);
     54             cv::cornerSubPix(image, 
     55                 imageConers,
     56                 cv::Size(5, 5),
     57                 cv::Size(-1, -1),
     58                 cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS,
     59                 30, 0.1));
     60             if (imageConers.size() == boardSize.area())
     61             {
     62                 addPoints(imageConers, objectConers);
     63                 success++;
     64             }
     65             //画出角点
     66             cv::drawChessboardCorners(image, boardSize, imageConers, found);
     67             cv::imshow("Corners on Chessboard", image);
     68             cv::waitKey(100);
     69         }
     70         return success;
     71     }
     72     
     73     //相机标定
     74     double calibrate(cv::Size&imageSize)
     75     {
     76         mustInitUndistort = true;
     77         std::vector<cv::Mat>rvecs, tvecs;
     78         //相机标定
     79         return cv::calibrateCamera(objectPoints, imagePoints, imageSize,
     80             camerMatirx, disCoeffs, rvecs, tvecs, flag);
     81     }
     82     ///去畸变
     83     cv::Mat remap(const cv::Mat &image)
     84     {
     85         cv::Mat undistorted;
     86         if (mustInitUndistort)
     87         {
     88             //计算畸变参数
     89             cv::initUndistortRectifyMap(camerMatirx, disCoeffs,
     90                 cv::Mat(), cv::Mat(), image.size(), CV_32FC1, map1, map2);
     91             mustInitUndistort = false;
     92         }
     93         //应用映射函数
     94         cv::remap(image, undistorted, map1, map2, cv::INTER_LINEAR);
     95         return undistorted;
     96     }
     97     //常成员函数,获得相机内参数矩阵、投影矩阵数据
     98     cv::Mat getCameraMatrix() const { return camerMatirx; }
     99     cv::Mat getDistCoeffs()   const { return disCoeffs; }
    100 };

    源.cpp

     1 #include"标头.h"
     2 #include<iomanip>
     3 #include<iostream>
     4 int main()
     5 {
     6     CameraCalibrator Cc;
     7     cv::Mat image;
     8     std::vector<std::string> filelist;
     9     cv::namedWindow("Image");
    10     for (int i = 1; i <= 22; i++)
    11     {
    12         ///读取图片
    13         std::stringstream s;
    14         s << "D:/images/chessboards/chessboard" << std::setw(2) << std::setfill('0') << i << ".jpg";
    15         std::cout << s.str() << std::endl;
    16 
    17         filelist.push_back(s.str());
    18         image = cv::imread(s.str(),0);
    19         cv::imshow("Image", image);
    20         cv::waitKey(100);
    21     }
    22     //相机标定
    23     cv::Size boardSize(6, 4);
    24     Cc.addChessboardPoints(filelist, boardSize);
    25     Cc.calibrate(image.size());
    26 
    27     //去畸变
    28     image = cv::imread(filelist[1]);
    29     cv::Mat uImage=Cc.remap(image);
    30     cv::imshow("原图像", image);
    31     cv::imshow("去畸变", uImage);
    32     //显示相机内参数矩阵
    33     cv::Mat cameraMatrix = Cc.getCameraMatrix();
    34     std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
    35     std::cout << cameraMatrix.at<double>(0, 0) << " " << cameraMatrix.at<double>(0, 1) << " " << cameraMatrix.at<double>(0, 2) << std::endl;
    36     std::cout << cameraMatrix.at<double>(1, 0) << " " << cameraMatrix.at<double>(1, 1) << " " << cameraMatrix.at<double>(1, 2) << std::endl;
    37     std::cout << cameraMatrix.at<double>(2, 0) << " " << cameraMatrix.at<double>(2, 1) << " " << cameraMatrix.at<double>(2, 2) << std::endl;
    38 
    39     cv::waitKey(0);
    40 }

    实验结果:

     

     看以看到,相机内参数矩阵为
    172.654 、0、157.829
    0、184.195、118.635
    0 、0 、1

  • 相关阅读:
    C# 把类实例保存到文件里(类的序列化和反序列化)
    C# 枚举的初始化
    旋转 3d
    asp.net页面间传值方式
    sql获取当前时间
    SqlServer中循环和条件语句示例!
    SQL Server 代理(已禁用代理 XP)
    JQuery源码实现
    C#计算一段程序运行时间的三种方法
    java开发配套版本
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/15035097.html
Copyright © 2020-2023  润新知