鱼眼相机标定以及OpenCV实现
2016-01-07 12:22
363 查看
在另一篇文章中我已经写过有关普通相机模型及其OpenCV标定实现,这篇文章将主要关注鱼眼相机模型及其OpenCV标定实现。
先看一张鱼眼相机拍摄出来的结果:
从图中可以看出很明显的畸变。对鱼眼相机标定,有时候也可以用普通相机的标定方法对其进行标定,但是却不能保证去畸变后的效果是最好的。因此对于Gopro等鱼眼镜头拍摄出来的图像去畸变,最好的方法就是采用鱼眼相机标定方法进行标定。
⎧⎩⎨⎪⎪fx000fy0cxcy1⎫⎭⎬⎪⎪
\left\{
\begin{matrix}
{f_x} & 0 & {c_x} \\
0 & {f_y} & {c_y} \\
0 & 0 & 1
\end{matrix}
\right\}
这与普通镜头的成像模型没有区别。两者之间的区别主要体现在畸变系数,鱼眼相机的畸变系数为{k1,k2,k3,k4{k_1},{k_2},{k_3},{k_4}},畸变系数不同,就导致鱼眼相机的投影关系也发生了变化,主要变化发生在考虑畸变情况下的投影关系转化:
设(X,Y,Z)为空间中一个三维点,它在成像平面内的成像坐标为(u,v),在考虑畸变的情况下,
⎧⎩⎨⎪⎪xcyczc⎫⎭⎬⎪⎪=R∗⎧⎩⎨⎪⎪XYZ⎫⎭⎬⎪⎪+T
\left\{
\begin{matrix}
{x_c} \\
{y_c} \\
{z_c}
\end{matrix}
\right\} = R*\left\{
\begin{matrix}
{X} \\
{Y} \\
{Z}
\end{matrix}
\right\} +T
a=xc/zc,b=yc/zca={x_c}/{z_c},b={y_c}/{z_c}
r2=a2+b2r^2={a}^2+{b}^2
θ=atan(r)\theta = atan(r)
θ′=θ(1+k1θ2+k2θ4+k3θ6+k4θ8){\theta}'=\theta (1+{k_1}{\theta}^2+{k_2}{\theta}^4+{k_3}{\theta}^6+{k_4}{\theta}^8)
x′=(θ′/r)xcx'=({\theta}'/r){x_c}
y′=(θ′/r)ycy'=({\theta}'/r){y_c}
u=fxx′+cxu={f_x}x'+{c_x}
v=fyy′+cyv={f_y}y'+{c_y}
检测角点
获得棋盘标定板的角点位置,使用
初始化标定板上角点的三维坐标
开始标定
注意:K,D 分别表示内参矩阵和畸变系数向量,在定义时要定义为double型,这里推荐使用Matx33d和Vec4d数据类型,更为方便简单。objectPoints,imagePoints可以是float型,也可以是double型,但是再stereorectify中需要时double型。flags的可选项有很多,其中需要注意的是必须要指定CALIB_FIX_SKEW,代表求解时假设内参中fx=fyf_x=f_y.
4.评定误差(可选项)
以上就是鱼眼相机标定的基本流程,部分代码片段如下:
标定结果:
先看一张鱼眼相机拍摄出来的结果:
从图中可以看出很明显的畸变。对鱼眼相机标定,有时候也可以用普通相机的标定方法对其进行标定,但是却不能保证去畸变后的效果是最好的。因此对于Gopro等鱼眼镜头拍摄出来的图像去畸变,最好的方法就是采用鱼眼相机标定方法进行标定。
鱼眼相机模型
鱼眼相机的内参模型依然可以表示为:⎧⎩⎨⎪⎪fx000fy0cxcy1⎫⎭⎬⎪⎪
\left\{
\begin{matrix}
{f_x} & 0 & {c_x} \\
0 & {f_y} & {c_y} \\
0 & 0 & 1
\end{matrix}
\right\}
这与普通镜头的成像模型没有区别。两者之间的区别主要体现在畸变系数,鱼眼相机的畸变系数为{k1,k2,k3,k4{k_1},{k_2},{k_3},{k_4}},畸变系数不同,就导致鱼眼相机的投影关系也发生了变化,主要变化发生在考虑畸变情况下的投影关系转化:
设(X,Y,Z)为空间中一个三维点,它在成像平面内的成像坐标为(u,v),在考虑畸变的情况下,
⎧⎩⎨⎪⎪xcyczc⎫⎭⎬⎪⎪=R∗⎧⎩⎨⎪⎪XYZ⎫⎭⎬⎪⎪+T
\left\{
\begin{matrix}
{x_c} \\
{y_c} \\
{z_c}
\end{matrix}
\right\} = R*\left\{
\begin{matrix}
{X} \\
{Y} \\
{Z}
\end{matrix}
\right\} +T
a=xc/zc,b=yc/zca={x_c}/{z_c},b={y_c}/{z_c}
r2=a2+b2r^2={a}^2+{b}^2
θ=atan(r)\theta = atan(r)
θ′=θ(1+k1θ2+k2θ4+k3θ6+k4θ8){\theta}'=\theta (1+{k_1}{\theta}^2+{k_2}{\theta}^4+{k_3}{\theta}^6+{k_4}{\theta}^8)
x′=(θ′/r)xcx'=({\theta}'/r){x_c}
y′=(θ′/r)ycy'=({\theta}'/r){y_c}
u=fxx′+cxu={f_x}x'+{c_x}
v=fyy′+cyv={f_y}y'+{c_y}
OpenCV实现鱼眼相机标定
利用opencv实现鱼眼相机的标定和普通相机标定的标定流程基本一致,具体流程如下:检测角点
cv::findChessboardCorners(InputArray image, Size patternSize, OutputArray corners, int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE}
获得棋盘标定板的角点位置,使用
cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)获取角点更精细的检测结果
初始化标定板上角点的三维坐标
开始标定
double fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, const Size& image_size, InputOutputArray K, InputOutputArray D, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags=0, TermCriteria criteria=TermCriteria(TermCriteria::COUNT + TermCriteria:: EPS, 100, DBL_EPSILON))
注意:K,D 分别表示内参矩阵和畸变系数向量,在定义时要定义为double型,这里推荐使用Matx33d和Vec4d数据类型,更为方便简单。objectPoints,imagePoints可以是float型,也可以是double型,但是再stereorectify中需要时double型。flags的可选项有很多,其中需要注意的是必须要指定CALIB_FIX_SKEW,代表求解时假设内参中fx=fyf_x=f_y.
4.评定误差(可选项)
以上就是鱼眼相机标定的基本流程,部分代码片段如下:
for (int i = 0; i != image_count; i++) { cout << "Frame #" << i + 1 << "..." << endl; string image_Name; stringstream stream; stream << (i + startNum); stream >> image_Name; image_Name = path_ChessboardImage + image_Name + ".jpg"; cv::Mat image = imread(image_Name); Mat image_gray; cvtColor(image, image_gray, CV_RGB2GRAY); vector<Point2f> corners; bool patternFound = findChessboardCorners(image_gray, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK); if (!patternFound || fullcornersNum != corners.size()) { cout << "can not find chessboard corners!\n"; continue; } else { cornerSubPix(image_gray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); count = count + corners.size(); corners_Seq.push_back(corners); successImageNum = successImageNum + 1; image_Seq.push_back(image); } } /************************************************************************ 摄像机定标 *************************************************************************/ vector<vector<Point3f>> object_Points; /**** 保存定标板上角点的三维坐标 ****/ Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); /***** 保存提取的所有角点 *****/ vector<int> point_counts; /* 初始化定标板上角点的三维坐标 */ for (int t = 0; t<successImageNum; t++) { vector<Point3f> tempPointSet; for (int i = 0; i<board_size.height; i++) { for (int j = 0; j<board_size.width; j++) { /* 假设定标板放在世界坐标系中z=0的平面上 */ Point3f tempPoint; tempPoint.x = i*square_size.width; tempPoint.y = j*square_size.height; tempPoint.z = 0; tempPointSet.push_back(tempPoint); } } object_Points.push_back(tempPointSet); } for (int i = 0; i< successImageNum; i++) { point_counts.push_back(board_size.width*board_size.height); } /* 开始定标 */ Size image_size = image_Seq[0].size(); cv::Matx33d intrinsic_matrix; /***** 摄像机内参数矩阵 ****/ cv::Vec4d distortion_coeffs; /* 摄像机的4个畸变系数:k1,k2,k3,k4*/ std::vector<cv::Vec3d> rotation_vectors; /* 每幅图像的旋转向量 */ std::vector<cv::Vec3d> translation_vectors; /* 每幅图像的平移向量 */ int flags = 0; flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; flags |= cv::fisheye::CALIB_CHECK_COND; flags |= cv::fisheye::CALIB_FIX_SKEW; fisheye::calibrate(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, flags, cv::TermCriteria(3, 20, 1e-6));
标定结果:
相关文章推荐
- Tomcat常用的优化技巧
- wordpress IP黑名单插件:IP Blacklist Cloud屏蔽攻击者IP让网站更安全
- linux下curl指令常见使用
- 大型网站技术1-----虚拟化
- 企业内部网段检测有多少主机存活——shell脚本传参(二)
- Docker基本概念和组件
- FS Shell命令手册
- centos7 解压rar 文件
- Filter拦截器压缩全网站数据
- linux环境变量
- 「技术大牛」是如何缩短事件平均解决时间的?
- Linux常用命令:文件,用户和群组操作
- Vim实用小命令
- Linux强大的编辑器vim用法说明
- OC 中@property readonly 怎么使用
- 简单了解Linux系统中rev命令与tac命令的用法
- 权限系统管理架构
- centos下安装mysql备份工具xtrabackup-2.3.2
- 很方便的API查询网站
- 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)