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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图1)

快速获得最新干货

来源:知乎-木木哥专栏

作者:陈林,现为德国汉诺威大学摄影测量与地理信息研究所博士生

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图2)

相机标定定义与原理

01

摄影测量的中心问题是从获取的影像来得到成像像素在影像中的世界点的三维坐标和语义属性的过程。其核心问题如下:

  

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

单目相机标定

双目相机标定

1、 如何建立相机和外部世界坐标之间的关系。这里需要两步:1)确定内参数;2)确定外参数。其中内参数用来描述相机在小孔成像过程中的一些重要参数值(焦距、主点、畸变...);外参数用来描述相机怎么摆在外部世界坐标系里(三个旋角组成的旋转矩阵,三个线元素组成的相机中心在世界坐标系的位置)。这个关系在 A)摄影测量的共线方程和 B)几何计算机视觉的投影方程里得以表达。从这里可以衍生的一点是,如果你知道外部世界点坐标,又能在影像上高精度定位出相应的成像点坐标,那么你就可以算出内外参数了,从而完成相机标定。当然,标定还有更多变种,但万变不离该宗。

  

2、 弄清了相机和外部世界的坐标关系怎么摆,接下来就要考虑怎么通过相片来算点坐标了。通过可能你已经发现,当你的外部世界坐标点在成像的光线上(即相机中心向像点发出的射线)来回窜动的时候,它对应的影像像点坐标是不变的!!这说明从物方点到像点的映射从线到点的对射,一条光线对应一个像点,所以需要加入第二张影像来交会确定出世界点的坐标。这引入了两个问题:1)两个相机之间的几何关系怎么表达?这是用相对定向模型(摄影测量)或者基础矩阵(计算机视觉)来表达的;2)怎么确定某个像点在另一张影像上的同源点(同名点/匹配点)?这个通过影像匹配来获取。 当几何关系被确定之后,通过前方交会就可以算两张影像上的同名点世界点的坐标。

  

3、 在2的基础上,要完成一片区域或者一个目标(建筑物、雕塑)的摄影测量,需要多张影像来完成。此时,可在刚才确定的相对定向的基础上进行连续的相对定向,即以某两幅影像相对定向的初始相机坐标系为参考坐标系,不断加入影像进行相对定向并放在第一幅影像表达的初始相机坐标系里面,所以进行完连续相对定向之后的坐标系还是初始的相机坐标系,由于相对定向的过程需要同名像点,所以实际上这个过程之后既得到了相机的姿态(位置、朝向),又得到了一部分用于定向的匹配同名像点在相机坐标系下的三维坐标(结构)。由于在这个过程中相机位置不断变化,形成了motion,所以这个过程在计算机视觉里又称为Structure From Motion(SFM)。把一系列影像都放在某个相机坐标下之后,此时通过少量地面控制点就可以把它与物方世界坐标之间的转换关系解算出来,完成绝对定向。这种思路称之为相对定向-绝对定向法。

  

通过以上的方法解算的过程一般都是渐进的,误差会不断积累和传播,为了提高精度,在最后会进行整体的再一次优化,优化的目标一般是最小化重投影误差。由于成像模型里的旋转矩阵是高度非线性的,这个优化问题是非线性优化问题,需要通过迭代来完成。通过迭代来优化的过程自然涉及到更新步长和更新方向(梯度)的问题,迭代步长和更新方向的确定用到了介于牛顿法(二阶梯度)和标准梯度下降(一阶梯度)之间的LM算法来完成,我们称这个过程叫光束法平差。

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

基于3D对象参照标定

基于2D平面标定

基于1D线性标定

自标定

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图3)

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图4)

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

  

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图5)

标定板介绍与制作

02

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

4、至此,你完成了把多幅影像对应的相机放到外部世界坐标系里,并在此过程中得到了一部分用于定向的匹配像点的3D坐标(稀疏定向点点云)。下一步就是密集地恢复深度,密集地测出影像上的点位坐标。以前我们叫立体匹配、多视立体匹配,现在匹配算法进步了,能逐像素匹配,我们干脆就叫它密集匹配了(Dense Matching)。其过程如下:

  

Chessboard

A) 先生成核线影像,让视差集中到水平方向,也就是说一对核线影像上坐标(x1,y1)来说,它在对应的影像上的同名点坐标(x2,y2)总满足y1=y2. 所以你只需要去估计影像1上每个点的视差值x1-x2.注意,视差值决定深度(世界3D 点到成像平面的距离)。

Circel-grid

RandPattern

ArUco

  

B) 每算一个可能的位置,都有一个匹配代价(交叉相关、互信息....),这样就形成了一个视差代价函数空间,它对应着影像1上每个像素取每个潜在可能视差值的匹配代价。

  

ChArUc

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图6)

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图7)

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

C) 估计每个像素点的视差。这个估计的过程就是在取每一个视差值的时候,你就有一个匹配代价,这项称为数据项。同时,你还得考虑领域,也就是说当某个像素值与邻近像素相近时,我们也认为它们的深度更相近,如果他们的深度不相近,我们就惩罚它们;同理,当邻近像素的灰度值变化很大的,我们也认为它们的深度值倾向于有更大的变化,那么就会对灰度值变化很大而视差值差别不大的情况进行惩罚。完成这个惩罚设计出来的代价函数就是平滑项。平滑项和数据项一起构成了匹配优化的目标函数,也称作能量函数,最优化求解这个函数使得代价(cost)最小的解就是求解的每个像素的视差值。当然,有一部分点可能不能同时被两张影像看到(遮挡),这部分像素深度无法得到。

  

D) 多个密集匹配的立体像对的结果进行融合,得到整体的视差值和三维点云。

制作标定版与图像生成

03

  

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

5、干完这步,你可以开心的获取:

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,这个是属于你的表演时间,不要客气!具体参考下图:

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图8)

相机标定程序实现

04

  i) 数字地表模型DSM

  ii)数字正射影像DOM

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

  iii)获取 数字高程模型 DEM (要自动得把植被和建筑从DSM上分类出来剔除掉,然后插值格网化)

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

  iiii)数字线划图 (还不是自动地......)

 

6、走到这里,我们做的还是几何问题。那么语义属性在哪里呢?

  

// 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, 30, 0.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(7, 7), corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);
    if (ret) {
        cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), criteria);
        drawChessboardCorners(image, Size(7, 7), corners, ret);
        imagePoints.push_back(corners);
        objectPoints.push_back(obj);
        imshow("calibration-demo", image);
        waitKey(500);
    }
}

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图9)

相机校正-计算内参数

属性通过分类来算。输入值是影像(可加入深度)(或特征),输出值是类别,中间过程用监督学习下的分类器来完成。或者用非监督学习压缩数据,然后用监督学习得到分类器。

// 相机校正
Mat intrinsic = Mat(3, 3, 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);

畸变图像校正

  

属性怎么算呢?如果用上我们辛辛苦苦得到的深度值,那我们依靠的还是马尔提出的视觉问题框架,通过深度值为跳板来解决视觉问题。现阶段以我的知识,能理解到的需要解决的核心问题是:

i) 如何设计出好的特征,这里面又分为采用经典的手动设计特征和现在火热的基于深度神经网络的自动特征表达学习,其核心问题是找出区分力高的特征表达;

05

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图10)

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

ii)如何表达先验,如何融合已有的知识,如GIS数据库、地图等信息,如何融合多平台的测量数据(卫星影像、航空影像、地面车载数据,UAV数据以及网络大数据);

iii) 不同地区的训练数据往往类别规定和类别分布不一致,如何有效的统一和迁移学习到的知识;以及如何建立高效海量的数据库来加速这一领域的发展。这个问题还是现在进行时...

  

// 畸变校正
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);
}

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图11)

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图12)早就是优势,学习切忌单打独斗,这里有教程资料、练习作业、答疑解惑等,优质学习圈帮你少走弯路,快速入门!

7、 航空影像的分类解决好了,还是土地的覆盖分类,土地的利用类别呢?国土资源的管理、地图的制作依靠的是土地的利用类别(荒地可能是荒地,可能是闲置的商业用地,这区别太大了)。解决这个问题一方面需要加入合理的先验;另一方面需要更加先进的分类模型和大数据的支撑。

推荐阅读

交流群

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图13)

推荐阅读

实战 | 相机标定

实战 | 图像矫正技术

【视频专区】倾斜摄影测量技术介绍

实战 | Unity下ARKit与OpenCV的结晶

倾斜摄影测量技术应用及展望

多视几何理论辅助的无人机低空摄影测量空三加密

倾斜摄影测量原理与关键技术(很全很细)

无人机航测系统在农村1:500地籍测图中的应用探讨

测绘新时代--1:500大比例尺免像控无人机航测系统面世

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

实战 | 文字定位与切割

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图14)

----------------    推  广    ---------------

实战 | 源码入门之Faster RCNN

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

实战 | OpenCV相机标定与畸变校正摄影测量中的几个核心问题(图15)

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

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

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

我用MATLAB撸了一个2D LiDAR SLAM

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

干货 | 史上最全 OpenC