相机标定:简单的说,就是获得相机参数的过程。参数如:相机内参数矩阵,投影矩阵,旋转矩阵和平移矩阵等
什么叫相机参数?
简单的说,将现实世界中的人、物,拍成一张图像(二维)。人或物在世界中的三维坐标,和图像上对应的二维坐标间的关系。表达两种不同维度坐标间的关系用啥表示?用相机参数。
相机的成像原理
先来看一下,相机的成像原理:
如图所示,这是一个相机模型。将物体简化看成一个点。来自物体的光,通过镜头,击中图像平面(图像传感器),以此成像。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
2
3
4
5
|
std::vector<cv::Point2f>imageConers;
//提取标定图像角点,保存角点坐标(二维)
cv::findChessboardCorners(image,
boardSize, //角点数目如(6,4)六行,四列
imageConers);
|
函数calibrateCamera完成相机标定工作。
1
2
3
4
5
6
7
8
9
|
cv::calibrateCamera(objectPoints, //三维坐标
imagePoints, //二维坐标
imageSize, //图像大小
camerMatirx, //相机内参数矩阵
disCoeffs, //投影矩阵
rvecs, //旋转
tvecs, //平移
flag //标记opencv提供几种参数,可以参看在线的opencv document
);
|
计算畸变参数,去畸变
1
2
3
4
5
6
7
8
9
10
|
//计算畸变参数
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
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
#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
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
|
#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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。
相关文章
- 64M VPS建站:如何选择最适合的网站建设平台? 2025-06-10
- ASP.NET本地开发时常见的配置错误及解决方法? 2025-06-10
- ASP.NET自助建站系统的数据库备份与恢复操作指南 2025-06-10
- 个人网站服务器域名解析设置指南:从购买到绑定全流程 2025-06-10
- 个人网站搭建:如何挑选具有弹性扩展能力的服务器? 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-25 100
-
load32.exe是什么进程 有什么用 load32进程查询
2025-05-27 55 -
2025-05-25 94
-
2025-05-29 25
-
2025-05-27 79