聊一聊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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Python使用OpenCV进行标定

    本文结合OpenCV官方样例,对官方样例中的代码进行修改,使其能够正常运行,并对自己采集的数据进行实验和讲解. 一.准备 OpenCV使用棋盘格板进行标定,如下图所示.为了标定相机,我们需要输入一系列三维点和它们对应的二维图像点.在黑白相间的棋盘格上,二维图像点很容易通过角点检测找到.而对于真实世界中的三维点呢?由于我们采集中,是将相机放在一个地方,而将棋盘格定标板进行移动变换不同的位置,然后对其进行拍摄.所以我们需要知道(X,Y,Z)的值.但是简单来说,我们定义棋盘格所在平面为XY平面,即Z=

  • 聊一聊OpenCV相机标定

    相机标定:简单的说,就是获得相机参数的过程.参数如:相机内参数矩阵,投影矩阵,旋转矩阵和平移矩阵等 什么叫相机参数? 简单的说,将现实世界中的人.物,拍成一张图像(二维).人或物在世界中的三维坐标,和图像上对应的二维坐标间的关系.表达两种不同维度坐标间的关系用啥表示?用相机参数. 相机的成像原理 先来看一下,相机的成像原理: 如图所示,这是一个相机模型.将物体简化看成一个点.来自物体的光,通过镜头,击中图像平面(图像传感器),以此成像.d0是物体到镜头的距离,di时镜头到图像平面的距离,f是镜头

  • OpenCV相机标定的全过程记录

    目录 一.OpenCV标定的几个常用函数 findChessboardCorners()棋盘格角点检测 cv::drawChessboardCorners()棋盘格角点的绘制 find4QuadCornerSubpix()对粗提取的角点进行精确化 cornerSubPix()亚像素检测 calibrateCamera()求解摄像机的内在参数和外在参数 initUndistortRectifyMap()计算畸变参数 二.绘制棋盘格,拍摄照片 三.相机标定 四.对图片进行校正 总结 一.OpenCV

  • Python opencv相机标定实现原理及步骤详解

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

  • OpenCV实现相机标定示例详解

    目录 环境准备 相机标定 棋盘格图片 实时显示相机的画面 在线标定 实时显示相机画面,按键保存能检测到角点的 棋盘格图片 离线标定 畸变矫正 环境准备 vs2015+opencv4.10安装与配置 相机标定 棋盘格图片 可以自己生成,然后打印到A4纸上.(也可以去TB买一块,平价买亚克力板的,不反光买氧化铝材质,高精度买陶瓷的) /** * 生成棋盘格图片 **/ int generateCalibrationPicture() { //Mat frame = imread("3A4.bmp&q

  • 基于python opencv单目相机标定的示例代码

    相机固定不动,通过标定版改动不同方位的位姿进行抓拍 import cv2 camera=cv2.VideoCapture(1) i = 0 while 1: (grabbed, img) = camera.read() cv2.imshow('img',img) if cv2.waitKey(1) & 0xFF == ord('j'): # 按j保存一张图片 i += 1 u = str(i) firename=str('./img'+u+'.jpg') cv2.imwrite(firename

  • OpenCV实现相机标定板

    本文实例为大家分享了OpenCV实现相机标定板的具体代码,供大家参考,具体内容如下 1.代码实现 #include <opencv.hpp> #include "highgui.h" #include "cxcore.h" using namespace cv; int main0(int argc, char *argv[]) { int width = 140;//width and heigth of single square int heigh

  • OpenCV实现相机标定

    本文实例为大家分享了OpenCV实现相机标定的具体代码,供大家参考,具体内容如下 一.相机与针孔相机模型 1.相机模型 现代科技加持下的相机已经成为制造精密设计巧妙的消费品,相机的光学结构也比诞生之初复杂了许多典型单反相机光学结构: 在众多相机模型中,针孔相机又称投影相机模型是相对简单而常用的模型.简单的说,针孔相机模型就是把相机简化成单纯的小孔成像,可想而知,这种简化对于精度要求高的情况或者特殊镜头的相机是不适用的.小孔成像原理: 2.引入透镜 单纯的小孔成像模型中没有考虑镜头,现实条件下,由

  • opencv 摄像机标定的实现

    原图 矫正后 我新建了个jz的文件夹放相机矫正所需要拍摄的图片,如下:共12张 # coding:utf-8 import cv2 import numpy as np import glob # 找棋盘格角点 # 阈值 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # 棋盘格模板规格 从0开始计算 w = 9 h = 6 # 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (

  • 如何基于matlab相机标定导出xml文件

    1 参数选择 径向畸变3个参数还是两个参数 默认两个参数 如果是三个参数 2准备转化生成结果 二参数的转化代码 writeExternalandIntrinsicMatrix(cameraParams62,'cameraParams622.xml'); function writeExternalandIntrinsicMatrix(cameraParams,file) %writeXML(cameraParams,file) docNode = com.mathworks.xml.XMLUti

随机推荐