实战 | OpenCV相机标定与畸变校正

2019 年 10 月 13 日 计算机视觉life

点击上方“计算机视觉life”,选择“星标”

快速获得最新干货

转载自OpenCV学堂。本文涉及OpenCV单目相机标定,图像畸变校正

相机标定定义与原理

01

在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)。相机标定常见的分为:

  • 单目相机标定

  • 双目相机标定

相机标定是想从二维的图像中获取三维信息,实现图像的畸变校正、对象测量、三维重建等。由于光线投射导致实际对象物体跟投影到2D平面的图像不一致,幸运的是这种不一致性是稳定的,我们可以通过对相机标定,计算出畸变参数来实现对后续图像的畸变校正。根据标定技术不一样可以分为下面几类标定方法:

  • 基于3D对象参照标定

  • 基于2D平面标定

  • 基于1D线性标定

  • 自标定

最常见的相机成像方式是基于pinhole的模型、它的成像模型可以图示如下:


下面我们首先对这个相机成像模型做一番解释

通过标定算法同时求出相机内参与外参。最常用的算法是张正友标定算法。OpenCV/Matlab中均已经实现该算法。

标定板介绍与制作

02

要想实现对相机的标定,我们首先需要给相机找到个参考对象,常见的就是标定版的类型有如下几种

  • Chessboard

  • Circel-grid

  • RandPattern

  • ArUco

  • ChArUc

OpenCV源码在其sample/data目录下面一个自带的棋盘图(chessboard.png),显示如下:

在标定的时候,算法要求提供的棋盘格的宽度高度,还有他们的间隔距离。需要特别注意是这里的宽高是指他们的内部交叉点的个数,以上图为例,它的大小为7x7而不是8x8。间隔是指棋盘格之间的距离,可以用像素距离表示,也可以用实际毫米为单位表示。


制作标定版与图像生成

03

最简单的办法就是把上述图像直接打印出来,贴到一个塑料底板上就好啦。如果是土豪可以直接购买各种玻璃底板的标定板。另外还有一个更恶搞的方法,连打印都省啦,直接把chessboard.png这张图在另外一台电脑的显示器上显示,然后把摄像头对着它各种牌即可完成图像数据采集。这个是我手写的采集程序代码,每次想保存图像的时候请安Q字母键即可,代码如下:

void create_images() {
    Mat frame;
    VideoCapture capture(0);
    int index = 1;
    while (true) {
        bool ret = capture.read(frame);
        flip(frame, frame, 1);
        if (!ret) break;
        imshow("frame", frame);
        char c = waitKey(50);
        printf("%d \n", c);
        if (c == 113) { // Q
            imwrite(format("D:/images/zsxq/%d.png", index), frame);
            index += 1;
        }
        if (c == 27) {
            break// ESC
        }
    }
    capture.release();
}

记得拿着棋盘格图,在镜头面前各种摆POSE,这个是属于你的表演时间,不要客气!具体参考下图:

相机标定程序实现

04

大家好,现在我们开始程序实现环节,OpenCV中在camera模块中已经实现了张正友标定算法。我们只需要正确调用,就可以计算出相机的内参与外参,完成相机的标定。具体的代码实现步骤如下:


定义相机标定的相关常量设置与变量

// load image files
vector<string> files;
glob("D:/images/camera2d", files);

// 定义变量
vector<vector<Point2f>> imagePoints;
vector<vector<Point3f>> objectPoints;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 300.001);
int numCornersHor = 7;
int numCornersVer = 7;
int numSquares = 50;
vector<Point3f> obj;
for (int i = 0; i < numCornersHor; i++)
    for (int j = 0; j < numCornersVer; j++)
        obj.push_back(Point3f((float)j * numSquares, (float)i * numSquares, 0));


发现与绘制棋盘格位置

// 发现棋盘格与绘制
Size s;
for (int i = 0; i < files.size(); i++) {
    printf("image file : %s \n", files[i].c_str());
    Mat image = imread(files[i]);
    s = image.size();
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    vector<Point2f> corners;
    bool ret = findChessboardCorners(gray, Size(77), corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);
    if (ret) {
        cornerSubPix(gray, corners, Size(1111), Size(-1-1), criteria);
        drawChessboardCorners(image, Size(77), corners, ret);
        imagePoints.push_back(corners);
        objectPoints.push_back(obj);
        imshow("calibration-demo", image);
        waitKey(500);
    }
}


发现棋盘格显示如下(我是直接打印OpenCV自带那张图的)


相机校正-计算内参数

// 相机校正
Mat intrinsic = Mat(33, CV_32FC1);
Mat distCoeffs;
vector<Mat> rvecs;
vector<Mat> tvecs;
intrinsic.ptr<float>(0)[0] = 1;
intrinsic.ptr<float>(1)[1] = 1;
calibrateCamera(objectPoints, imagePoints, s, intrinsic, distCoeffs, rvecs, tvecs);


畸变图像校正

05


关于畸变类型,常见的图像畸变类型有径向与切向畸变、OpenCV中的相机标定方法只能对径向畸变有效,使用内参对畸变图像实现校正。相关的代码如下:

// 畸变校正
for (int i = 0; i < files.size(); i++) {
    Mat dst;
    Mat image = imread(files[i]);
    undistort(image, dst, intrinsic, distCoeffs);
    imshow("image", image);
    imshow("undistort image", dst);
    waitKey(1000);
}

从零开始学习三维视觉核心技术SLAM,扫描查看介绍,3天内无条件退款

早就是优势,学习切忌单打独斗,这里有教程资料、练习作业、答疑解惑等,优质学习圈帮你少走弯路,快速入门!

交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、算法竞赛、检测分割识别、三维视觉、医学影像、自动驾驶、计算摄影等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~

推荐阅读

实战 | 相机标定

实战 | 图像矫正技术

实战 | Unity下ARKit与OpenCV的结晶

实战 | 基于SegNet和U-Net的遥感图像语义分割

实战 | 文字定位与切割

实战 | 源码入门之Faster RCNN

实战 | 自己实现扫描全能王

实战 | 用OpenCV轻松生成微信国庆版头像

实战 | OpenCV 实现多张图像拼接

实战 | 教你自动查找拍糊的图

我用MATLAB撸了一个2D LiDAR SLAM

实战 | 哪个瞬间让你突然觉得CV技术真有用?

干货 | 史上最全 OpenCV 活体检测教程

解决方案 | 如何用普通手机准确测量物体体积?

Mask-RCNN模型的实现自定义对象(无人机)检测

scikit-image图像处理入门

还在用肉眼找不同吗?这个技术轻松搞定

OpenCV测量物体的尺寸技能 get~

超详讲解图像拼接/全景图原理和应用 | 附源码


最新AI干货,我在看  

登录查看更多
4

相关内容

【开放书】SLAM 中的几何与学习方法,62页pdf
专知会员服务
109+阅读 · 2020年6月5日
Python地理数据处理,362页pdf,Geoprocessing with Python
专知会员服务
113+阅读 · 2020年5月24日
基于视觉的三维重建关键技术研究综述
专知会员服务
160+阅读 · 2020年5月1日
【人大】图实现算法综述与评测分析
专知会员服务
37+阅读 · 2020年4月28日
【干货书】机器学习Python实战教程,366页pdf
专知会员服务
338+阅读 · 2020年3月17日
【GitHub实战】Pytorch实现的小样本逼真的视频到视频转换
专知会员服务
35+阅读 · 2019年12月15日
实战 | 如何制作一个SLAM轨迹真值获取装置?
计算机视觉life
11+阅读 · 2019年10月16日
实战 | 相机标定
计算机视觉life
15+阅读 · 2019年1月15日
【泡泡一分钟】无参相机标定
泡泡机器人SLAM
3+阅读 · 2018年11月7日
利用自识别标记实现复杂场景下相机标定
计算机视觉life
7+阅读 · 2018年4月17日
计算机视觉方向简介 | 单目微运动生成深度图
计算机视觉life
7+阅读 · 2018年1月17日
用 Python 和 OpenCV 来测量相机到目标的距离
炼数成金订阅号
5+阅读 · 2018年1月16日
【每月好书】OpenCV算法精解
Python程序员
10+阅读 · 2017年11月21日
OpenCV计算机视觉产品实战
炼数成金订阅号
12+阅读 · 2017年9月22日
Deflecting Adversarial Attacks
Arxiv
8+阅读 · 2020年2月18日
Arxiv
5+阅读 · 2018年6月12日
Arxiv
3+阅读 · 2018年5月21日
Arxiv
6+阅读 · 2018年2月28日
Arxiv
4+阅读 · 2017年11月4日
VIP会员
相关资讯
实战 | 如何制作一个SLAM轨迹真值获取装置?
计算机视觉life
11+阅读 · 2019年10月16日
实战 | 相机标定
计算机视觉life
15+阅读 · 2019年1月15日
【泡泡一分钟】无参相机标定
泡泡机器人SLAM
3+阅读 · 2018年11月7日
利用自识别标记实现复杂场景下相机标定
计算机视觉life
7+阅读 · 2018年4月17日
计算机视觉方向简介 | 单目微运动生成深度图
计算机视觉life
7+阅读 · 2018年1月17日
用 Python 和 OpenCV 来测量相机到目标的距离
炼数成金订阅号
5+阅读 · 2018年1月16日
【每月好书】OpenCV算法精解
Python程序员
10+阅读 · 2017年11月21日
OpenCV计算机视觉产品实战
炼数成金订阅号
12+阅读 · 2017年9月22日
Top
微信扫码咨询专知VIP会员