41

OpenCV相机标定与畸变校正

 4 years ago
source link: https://www.tuicool.com/articles/ZBJFnuZ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我爱计算机视觉 标星,更快获取CVML新技术

本文转载自OpenCV学堂。

OpenCV单目相机标定,图像畸变校正

相机标定定义与原理

01

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

  • 单目相机标定

  • 双目相机标定

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

  • 基于3D对象参照标定

  • 基于2D平面标定

  • 基于1D线性标定

  • 自标定

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

yqmAzqz.png!web

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

N7jqIvQ.png!web

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

标定板介绍与制作

02

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

  • Chessboard

  • Circel-grid

  • RandPattern

  • ArUco

  • ChArUc

ZVzEnqU.png!web

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

UVJJZzE.png!web

在标定的时候,算法要求提供的棋盘格的 宽度高度 ,还有他们的 间隔距离 。需要特别注意是这里的宽高是指他们的内部交叉点的个数,以上图为例,它的大小为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,这个是属于你的表演时间,不要客气!具体参考下图:

a6fiI3m.jpg!web

相机标定程序实现

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, 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自带那张图的)

EjUn2az.jpg!web

相机校正-计算内参数

// 相机校正
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);

畸变图像校正

05

EVrieqa.png!web

关于畸变类型,常见的图像畸变类型有 径向与切向 畸变、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);
}

RNJBJzI.jpg!web

CV细分方向交流群

52CV已经建立多个CV专业交流群,包括:目标跟踪、 目标检测、语义分割、姿态估计、人脸识别检测、医学影像处理、超分辨率、神经架构搜索、GAN、强化学习等,扫码添加CV君拉你入群,如已经为CV君其他账号好友请直接私信,

请务必注明相关方向,比如: 目标检测

ENvu6rZ.jpg!web

喜欢在QQ交流的童鞋,可以加52CV官方 QQ群805388940

(不会时时在线,如果没能及时通过验证还请见谅)

UbiEbaU.png!web

长按关注 我爱计算机视觉


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK