相机标定
一、相机内参标定(张正友标定)
标定目的:
建立相机成像几何模型并矫正透镜畸变。
-
相机成像几何模型
得到相机的内参和外参,这个是建立物体从三维世界映射到相机成像平面这一过程中最关键的部分。 -
矫正透镜畸变
小孔成像:这种成像方式只有小孔部分能透过光线就会导致物体的成像亮度很低,于是聪明的人类发明了透镜。
透镜:由于透镜的制造工艺,会使成像产生多种形式的畸变,于是为了去除畸变,人们计算并利用畸变系数来矫正这种像差。理论上可以设计出不产生畸变的透镜,但其制造工艺相对于球面透镜会复杂很多。 所以相对于复杂且高成本的制造工艺,人们更喜欢用数学来解决问题。
相关概念:
坐标系
|
-
世界坐标系:用户定义的三维世界的坐标系,单位为m。
-
相机坐标系:在相机上建立的坐标系,单位为m。
-
图像坐标系:物体从相机坐标系到图像坐标系的投影透射关系, 单位为m。
-
像素坐标系:为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的信息所在的坐标系。单位为个(像素数目)。
相机坐标系的 轴与光轴重合,且垂直于图像坐标系平面并通过图像坐标系的原点, 相机坐标系与图像坐标系之间的距离为焦距f(也即图像坐标系原点与焦点重合)。 像素坐标系平面u-v和图像坐标系平面x-y重合,但像素坐标系原点位于图中左上角(之所以这么定义,目的是从存储信息的首地址开始读写)。
畸变系数
a、径向畸变
径向畸变的产生是由于当光线在远离透镜中心时,其弯曲程度比靠近中心时更大,径向畸变有桶形畸变和枕形畸变两种
b、切向畸变
切向畸变是由于透镜与成像平面不完全平行而产生
相机内参
相机内参与镜头本身的焦距等相关,为摄像机本身特性,可通过六个参数表示为:1/dx、1/dy、s、u0、v0、f。
dx和dy表示x方向和y方向的一个像素分别占多少长度单位,即一个像素代表的实际物理值的大小;
u0,v0表示图像的中心像素坐标和图像原点像素坐标之间相差的横向和纵向像素数;
f为焦距;
s为坐标轴倾斜参数。
在opencv文档里内参数共四个为fx、fy、u0、v0。其中fx=f*(1/dx),fy=f*(1/dy),s假设为0,因此为4个内参,fx,fy为焦距,一般情况下,二者相等,cx、cy为主点坐标(相对于成像平面)
相机外参
相机外参矩阵描述的是相机在静态场景下相机的运动,包括旋转和平移,或者在相机固定时,运动物体的刚性运动。
相机坐标系的三个轴的旋转参数分别为(ω、δ、 θ),然后把每个轴的33旋转矩阵进行组合(即先矩阵之间相乘),得到旋转矩阵R,其大小为33;T的三个轴的平移参数(Tx、Ty、Tz)。R、T组合成成的3*4的矩阵。
![](https://i-blog.csdnimg.cn/blog_migrate/345209d620854991cd508f1d2b158d6d.png)
|
坐标平移、旋转、缩放
参考:旋转变换(一)旋转矩阵.
使用2x2的矩阵,是没有办法描述二维平移操作的,
只有引入3x3矩阵形式,才能统一描述二维中的平移、旋转、缩放操作。
同理必须使用4x4的矩阵才能统一描述三维的变换
|
![](https://i-blog.csdnimg.cn/blog_migrate/138d63200274c3319e2093314c70bed5.png)
右手坐标系
![](https://i-blog.csdnimg.cn/blog_migrate/422a8dc31911ab2f65dd11850c8eb530.png)
a、绕Z轴旋转大拇指指向Z轴正向,其他手指弯曲方向角度为正;
xoy(o是坐标原点)平面上进行的是一个二维的旋转
x′=xcosθ−ysinθ
y′=xsinθ+ycosθ
![](https://i-blog.csdnimg.cn/blog_migrate/9868ceff5a028b70945f7197f4bb4082.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c8f783d24b70cd7449976fafedcc6b0.png)
b、绕X轴旋转
大拇指指向X轴正向,其他手指弯曲方向角度为正;
y和z组成的yoz(o是坐标原点)平面上进行的是一个二维的旋转
y′=ycosθ−zsinθ
z′=ysinθ+zcosθ
![](https://i-blog.csdnimg.cn/blog_migrate/f1e8b738b34b1ec4ea4413d70222d959.png)
c、绕Y轴旋转
大拇指指向X轴正向,其他手指弯曲方向角度为正;
ZOX(o是坐标原点)平面上进行的是一个二维的旋转
z′=zcosθ−xsinθ
x′=zsinθ+xcosθ
![](https://i-blog.csdnimg.cn/blog_migrate/f76c4a1e8ca264898dfea06ea7598450.png)
棋盘是一块由黑白方块间隔组成的标定板,我们用它来作为相机标定的标定物。
我们用棋盘作为标定物是因为平面棋盘模式更容易处理(相对于复杂的三维物体),但与此同时,二维物体相对于三维物体会缺少一部分信息,
于是我们会多次改变棋盘的方位来捕捉图像,以求获得更丰富的坐标信息。
|
标定代码:
1. OpenCv相关知识
**CVAPI**(函数返回类型)--OpenCV中的宏定义
#define CVAPI(rettype) CV_EXTERN_C CV_EXPORTS rettype CV_CDECL
-
rettype是宏定义的参数了,这里放具体的类型,表示函数的返回类型!
-
CV_EXTERN_C 的宏定义是#define CV_EXTERN_C extern “C”
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。 加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的
-
CV_EXPORTS 的宏定义为# define CV_EXPORTS __declspec(dllexport)
CV_EXPORTS暂时无实质性内容,只是一个预留宏定义, 是为了OpenCV在编程上的可扩展性预留的
-
CV_CDECL的宏定义是#define CV_CDECL __cdecl __cdecl是VC的关键词
在编译器以蓝色显示 ,它表示的意思如下:__cdecl 是C Declaration的缩写(declaration,声明), 表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。 被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数, 甚至完全不同的参数都不会产生编译阶段的错误。
2. 配置opencv
-
windows环境 + visual studio
参考:visual studio 2019中配置OpenCv4.0.0详细教程+测试环境代码.
配置的过程可以分为两个步骤:
一是设置环境变量;首先设置环境变量。右击"我的电脑",单击"属性",打开一个页面。找到"高级系统设置",单击,出现系统属性页。单击"环境变量",即出现环境变量修改页。在"系统变量"下面找到Path。双击编辑Path,将OpenCV安装目录的bin目录添加进去。比如 D:\opencv\build\x86\vc12\bin;
二是配置项目属性表。
这个操作在属性表中进行。如下图:
|
把OpenCV的包含目录、库目录,库文件(附加依赖项)添加进来。
a、在属性表中找到"VC++目录",找到"包含目录",把OpenCV安装路径的对应目录添加进去。OpenCV的包含目录,例如
D:\opencv\build\include;
D:\opencv\build\include\opencv2;
b、在属性表中找到"VC++目录",找到"库目录",添加路径:
E:\OpenCv\opencv\build\x64\vc15\lib,
确定,应用。(以自己的安装目录为主)
c、在链接器->输入->附加依赖项中加入
opencv_world400d.lib,确定,应用,确定。
(opencv_world400d.lib以自己下载的版本为主)
测试代码
# include <iostream>
#include <opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat A; //存放读取的原图
Mat bigImage; //放大图像
Mat smallImage; //缩小图像
A = imread("d://1.jpg", 1);//1正色 0灰色(这里为存放图片位置,图片可任意)
if (A.data != NULL) {
//放大图像
resize(A, bigImage, Size(A.cols * 2, A.rows * 2));
//缩小图像
resize(A, smallImage, Size(A.cols / 2, A.rows / 2));
imwrite("放大的图像.jpg", bigImage);//保存放大图片
imwrite("缩小的图像.jpg", smallImage);//保存缩小图片
imshow("原始图像", A);
imshow("放大图像", bigImage);
imshow("缩小图像", smallImage);
}
else
{
cout << "图片加载失败,请检查文件是否存在!" << endl;
}
//保持等待状态
waitKey();//括号里可以填任意正整数,意味着,图像显示的毫秒时
return 0;
}
- Ubuntu环境
参考: Ubuntu上配置opencv的详细步骤.
参考: opencv Installation in Linux .
3. 代码实现
参考:OpenCV相机标定和姿态更新.
相关概念:
a、面阵摄像机的成像面以像素为最小单位。例如某CMOS摄像芯片,其像素间距为5.2微米。摄像机拍摄时,将物理世界中连续的图像进行了离散化处理。到成像面上每一个像素点只代表其附近的颜色。至于“附近”到什么程度?就很困难解释。两个像素之间有5.2微米的距离,在宏观上可以看作是连在一起的。但是在微观上,它们之间还有无限的更小的东西存在。这个更小的东西我们称它为“亚像素”。实际上“亚像素”应该是存在的,只是硬件上没有个细微的传感器把它检测出来。于是软件上把它近似地计算出来。
b、calibrateCamera函数
calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs,flag)
objectPoints: 一组世界坐标系中的3D
imagePoints: 超过10张图片的角点集合
imageSize: 每张图片的大小
cameraMatrix: 内参矩阵
distCoeffs: 畸变矩阵(默认获得5个即便参数k1,k2,p1,p2,k3,可修改)
rvecs: 外参:旋转向量
tvecs: 外参:平移向量
flag: 标定时的一些选项:
CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘法估算出fx,fy。
CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。
CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。
CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。
CV_CALIB_FIX_K1,...,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。
CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。
c、findChessboardCorners函数
findChessboardCorners(frame, board_size, corners,CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK );
d、Size board_size = Size(7, 4),如下图所示的横向和纵向的脚点数量。
![](https://i-blog.csdnimg.cn/blog_migrate/45f16d3ebf16a3db5d1c65a6abe31359.png)
d、C++ vector 容器浅析
向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。
跟任意其它类型容器一样,它能够存放各种类型的对象。
可以简单的认为,向量是一个能够存放任意类型的动态数组。
e、windows+visual studio(待补充)
参考:OpenCV:相机标定(自带Demo).
参考:opencv3.4.0 + vs2015 + win10 单目相机标定.
OpenCV sample目录下自带两个与相机标定的cpp文件
即:calibration.cpp和calibration_artificial.cpp
f、ubuntu + opencv+vscode实现
参考:Ubuntu上配置opencv的详细步骤.
参考:opencv-相机校准和3D重建.