OpenCV (相机标定)
Eva.Q Lv9

“The moment you doubt whether you can fly, you cease for ever to be able to do it.” —— Peter Pan

Pr

相机标定

相机参数的估计过程称为相机标定。这意味着我们拥有关于相机的所有信息(参数或系数),这些信息用于确定真实世界中的) 3D 点与其在该标定相机捕获的图像中的相应 2D 投影(像素)之间的精确关系。通常这意味着恢复两种参数。

内部参数: 例如透镜的焦距、光学中心和径向畸变系数。
外部参数: 指相机相对于某些世界坐标系的方位(旋转和平移)。

二维点与三维点的转化
四种坐标系

1、图像像素坐标系:表示场景中三维点在图像平面上的投影,其坐标原点在CCD图像平面的左上角,u轴平行于CCD平面水平向右,v轴垂直于u轴向下,坐标使用(u,v)来表示。注:这里的(u,v)表示的是该像素在数组中的列数和行数

2、图像物理坐标系:其坐标原点在CCD图像平面的中心,x,y轴分别平行于图像像素坐标系的坐标轴,坐标用(x,y)表示。

3、相机坐标系:以相机的光心为坐标系原点,X,Y轴平行于图像坐标系的X,Y轴,相机的光轴为Z轴,坐标系满足右手法则。注:这里所指的相机的光心可以简单的理解为相机透镜的几何中心

4、世界坐标系:也称为绝对坐标系,用于表示场景点的绝对坐标

世界参考系和相机参考系的转换

https://blog.csdn.net/jiangxing11/article/details/106478020

https://blog.csdn.net/qq_15029743/article/details/90215104

投影方程

基于OpenCV的相机标定

目标

标定过程的目标是使用一组已知的三维点 $(X_w, Y_w, A_w)$ 及其对应的图像坐标 $(u, v)$

找到 3×3矩阵 K , 3×3旋转矩阵 R , 3×1平移向量 T 。当我们得到相机的内部和外部参数值时,相机就被称为标定相机。

总之,相机标定算法具有以下输入和输出:

输入:具有已知二维图像坐标和三维世界坐标的点的图像集合。
输出:3×3相机内参矩阵,每幅图像的旋转和平移。
注意OpenCV中,相机内部矩阵不包含倾斜参数。所以矩阵的形式是:

方法
  • 校正:当我们完全控制成像过程时,执行校准的最佳方法是从不同的视角捕获一个物体或已知尺寸模式的多个图像。我们将在这篇文章中学习的基于棋盘的方法属于这一类。我们也可以使用已知尺寸的圆形图案,而不是棋盘格图案。
  • 几何线索:有时我们在场景中有其他的几何线索,如直线和消失点,可以用来标定。
  • 基于深度学习的:当我们对成像设置的控制非常小(例如,我们有场景的单个图像)时,仍然可以使用基于深度学习的方法获取相机的校准信息。
步骤
  1. 使用棋盘格模式定义真实世界坐标;
  2. 从不同的角度捕获棋盘的多个图像;
  3. 查找棋盘的2D坐标;
  4. 校准相机
实现

OpenCV 推荐使用国际象棋棋盘的图案生成用于标定的三维场景点的集合。

这个图案在每个方块的角点位置创建场景点;由于图案是平面的,可以假设棋盘位于 Z = 0, X,Y 的坐标轴与网格对其的位置 。这样,标定时就只需从不同的视角拍摄棋盘图案。

检测角点

可以用OpenCV自带的函数自动检测棋盘图案中的角点。只需要提供一幅图像和棋盘尺寸(水平和垂直方向内部角点的数量),函数会返回图像中所有棋盘角点的位置,若无法找到图案,函数返回false。

1
2
3
4
5
6
//输出图像角点的向量
vector<Point2f> imageCorners;
//棋盘内部角点的数量
Size boardSize(7, 5);
//获得棋盘角点
bool foound = findChessboardCorners(image, boardSize, omageCorners);//image 棋盘图案

棋盘内部角点数,如图

画角点

画出角点并用线条连接起来,连接次序即在想两种存储的次序。

1
drawChessboardCorners(image, boardSize, imageCorners, found);

指定三维点

自由选择单位,如厘米或英寸等。

为了方便起见我们将方块的边长定位单位,这样可令点的坐标如 (0,0,0)(6,4,0) 这样便于表示。

(假设棋盘纵深坐标为 z = 0

为了得到更多的点,需要从不同的视角对同一个标定图案拍摄更多的照片。

OpenCV的标定函数假定由标定图案确定坐标系,并计算相机相对于坐标系的旋转量和平移量。

把标定过程封装在 CameraCailbrator 中,

1
2
3
4
5
6
7
8
9
10
11
12
class CameraCailbrator{
//每个向量的元素也是一个向量 表示一个视角的点集
//输入点 世界坐标系(每个正方形为一个单位)
vector<vector<Point3f> > objectPoints;
//点在图像中的位置(以像素为单位)
vector<vector<Point2f> > imagePoints;
//输出矩阵
Mat cameraMatrix;
Mat distCoeffs;
//指定标定方式的标志
int fllag;
}

类库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef CAMERACALIBRATOR_H
#define CAMERACALIBRATOR_H
#include <vector>
#include <iostream>
#include <opencv2/core.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/calib3d.hpp"
#include <opencv2/highgui.hpp>
class CameraCalibrator {
// 输入点:
// 世界坐标系中的点
//(每个正方形为一个单位)
std::vector<std::vector<cv::Point3f> > objectPoints;
// 点在图像中的位置(以像素为单位)
std::vector<std::vector<cv::Point2f> > imagePoints;
// 输出矩阵
cv::Mat cameraMatrix;
cv::Mat distCoeffs;
// 指定标定方式的标志
int flag;
// used in image undistortion
cv::Mat map1,map2;
bool mustInitUndistort;
public:
CameraCalibrator() : flag(0), mustInitUndistort(true) {}
// Open the chessboard images and extract corner points
int addChessboardPoints(const std::vector<std::string>& filelist, cv::Size & boardSize, std::string windowName="");
// Add scene points and corresponding image points
void addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners);
// Calibrate the camera
double calibrate(const cv::Size imageSize);
// Set the calibration flag
void setCalibrationFlag(bool radial8CoeffEnabled=false, bool tangentialParamEnabled=false);
// Remove distortion in an image (after calibration)
cv::Mat remap(const cv::Mat &image, cv::Size &outputSize = cv::Size(-1, -1));
// Getters
cv::Mat getCameraMatrix() { return cameraMatrix; }
cv::Mat getDistCoeffs() { return distCoeffs; }
};
#endif // CAMERACALIBRATOR_H

这里采用增加标定点的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int CameraCailbrator::addChessboardPoints(const vector<string> & filelist, Size & boardSize){ //文件名列表 标定面板大小

//棋盘上的角点
vector<Point2f> imageCorners;
vector<Point3f> objectCorners;

//场景中的三维点 在棋盘坐标系中,初始化期盼中的角点 角点的三维坐标(i, j, 0)
for(int i = 0; i < boardSize.height; ++i){
for(int j = 0; j < boardSize.width; ++j){
objectCorners.push_back(Point3f(i, j, 0.0f));
}
}

//图像中的二维点
Mat image; //存储棋盘图像
int successes = 0;
//处理所有视角
for(int i = 0; i < filelist.size(); ++i){
image = imread(filelist[i], 0);

bool found = findChessboardCorners(image, boardSize, imageCorners);

//取得角点上的亚像素级精度
if(found){
cornerSubPix(image, imageCorners, Size(5, 5), Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0.1));
//棋盘完好
if(imageCorners.size() == boardSize.area()){
//加入同一视角得到的图像和场景点
addPoints(imageCorners, objectCorners);
successes++;
}
}
}
return successes;
}

处理完足够数量的棋盘图像(一般10~20个)后,就可以开始计算标定参数了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//标定相机 返回重投影误差
double CameraCalibrator::calibrate(Size &imageSize){
//输出旋转量和平移量
vector<Mat> rvecs, tvecs;

//开始标定
return calibrateCamera(objectPoints, // 三维点
imagePoints, // 图像点
imageSize, // 图像尺寸
cameraMatrix, // 输出相机矩阵
distCoffes, // 输出畸变矩阵
rvecs, tvecs, // Rs,Ts
flag); // 设置选项
}

重投影误差

函数 calibrateCamera 在得到标定结果之前进行了优化,以便找到合适的内部参数和外部参数,使图像点的预定位置(根据三维点的投影计算得到)和实际位置(图像中的位置)之间的距离达到最小。每个点在标定过程中都会产生这个距离,它们的累加和就是重投影误差。

畸变模型

径向畸变:超广角镜头产生的典型畸变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 去除图像中的畸变(标定后)
Mat CameraCalibrator::remap(const Mat &image){
Mat undistorted;

if(mustInitUndistort){ // 每个标定过程只调用一次

initUndistortRectifyMap(cameraMatrix, // 计算得到的相机矩阵
distCoeffs, // 计算得到的畸变矩阵
Mat(), // 可选矫正项(无)
Mat(), // 生成无畸变的相机矩阵
image.size(), // 无畸变图像的尺寸
CV_32FC1, // 输出图片的类型
map1, map2); // x和y映射功能

mustInitUndistort = false;
}
// 应用映射功能
remap(image, undistorted, map1, map2, INTER_LINEAR); // 插值类型

return undistorted;
}
实现原理

回到本文最前面的投影方程。

方程中连续使用了两个矩阵,把三维空间的点转换到二维空间。

第一个矩阵,相机的内部参数,是一个3*3的矩阵。是函数 calibrateCamera 输出的矩阵之一。此外还有一个 calibrationMatrixValues 函数,它根据标定矩阵,显示地返回内部参数值。

第二个矩阵,外部参数,内容是输入的点,以相机为坐标系中心。它由一个旋转向量(3*3)和一个平移向量(3*1)组成。旋转向量 $r_1, r_2, … r_9$ ,平移向量 $t_1, t_2, t_3$ 。

参考

https://blog.csdn.net/LuohenYJ/article/details/104697062

https://blog.csdn.net/jiangxing11/article/details/106478020

https://blog.csdn.net/qq_15029743/article/details/90215104

  • Post title:OpenCV (相机标定)
  • Post author:Eva.Q
  • Create time:2021-07-31 10:03:57
  • Post link:https://qyy/2021/07/31/OPENCV/OPENCV1-3/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.