单目标定-opencv-单目相机标定
相机标定方法及进展研究综述
相机标定方法及进展研究综述
相机标定是计算机视觉领域的重要研究方向之一,其目的是通过数学模型,将摄像机的内部参数和外部参数计算出来,从而提高图像的准确性和精度。在图像处理、机器视觉、计算机视觉等领域中,相机标定是一个非常重要的问题,并且在机器人视觉、三维重建和增强现实等领域中得到了广泛的应用。本文将对相机标定方法及进展研究进行综述。
一、相机标定方法
常用的相机标定方法包括摄像机模型、单目相机的标定、立体相机的标定、将标定技术运用到实际应用的技术。下面分别介绍。
1. 摄像机模型
相机模型是相机标定的基础。常用的相机模型主要包括针孔相机模型、中心投影相机模型、透视投影相机模型、鱼眼相机模型、全景相机模型等。这些模型都是基于相机采集的图像和射线之间的关系建立的。
2. 单目相机的标定
单目相机的标定主要包括内参数和外参数的标定。内参数是相机焦距、像点中心等参数,外参数是相机的旋转和平移,可以用于计算世界坐标和相机坐标之间的转换矩
阵。常用的单目相机标定方法包括张氏标定法、Tsai相机标定法、基于控制点的标定法等。
3. 立体相机的标定
立体相机的标定是通过对相机的双目视觉信息进行建模和分析,得到相机内部参数和外部参数的过程。常见的立体相机标定方法包括非线性标定法、基于投影矩阵的标定法、基于球面投影的标定法等。
4. 将标定技术运用到实际应用的技术
标定技术并不是研究的最终目的,而是运用到实际应用中的工具,如机器视觉、计算机视觉和图像处理等。因此,如何将标定技术应用到实际应用中,是当前科学研究的关键问题。常用的应用技术包括遮挡物检测、视觉跟踪、特征提取、目标检测等。
相机标定方法汇总
相机的标定还是很重要的,特把用过的工具和方法进行一次汇总,以便加深理解,希望和大家多多讨论。(本人扣扣1256635840)
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
1.MATLAB camera calibration toolbox工具箱
下载和学习网址:
标定测试集的下载网址:
我只各使用了13张图片
将TOOLBOX_calib放入MATLAB的toolbox目录下,工作目录设为含标定图集的目录。
1.1单目标定
运行calib_gui.m文件,选择添加到路径,界面:
选择第一个后,界面:
点击Image names或read images后
提示basename,输入left
提示imageformat,输入j
输出读取到的图片集:
点击extract grid corners(提取角点)
提示角点拾取窗大小,我选择了默认的5*5 弹出角点拾取窗口界面,拾取4个边界点:
提示输入方格的x向和y向的实际尺寸,这里的棋盘格实际大小为30mm*30mm,以及X向和Y向的方格个数,输入5和8,设置后得到角点提取的效果:
后续的12张图片进行同样的操作。
(后续MATLAB自己出了个标定工具箱camera calibrator,以及opencv、halcon 的角点都是自动提取,稍后详细说明)
opencv相机标定代码
//// GenCaltab.cpp : 定义控制台应用程序的入口点。
//
#include"stdafx.h"
#include<opencv\cv.h>
#include<opencv\highgui.h>
#include<iostream>
using namespace cv;
using namespace std;
typedef unsigned int uint;
/*const string &*/void GenCaltab(const string &pathName, Mat &caltab/*string &outPathName*/, int width = 600, int height = 600, int cols = 20, int rows = 20) // 黑白的,單通道
{
Scalar color(0); // 背景色(全黑)
caltab = Mat(height, width, CV_8UC1, color); // calibration table標定板(行*列對應於高*寬)
//Mat caltab(height, width, CV_8UC1, color);
int nWidthOfROI = int(width / float(cols));
int imgHeight = caltab.rows;
int imgWidth = caltab.cols;
for (int j = 0; j < imgHeight; j++)
opencv 标定规则
opencv 标定规则
OpenCV是一个广泛应用于计算机视觉领域的开源库,它提供了丰富的图像处理和计算机视觉算法。在使用OpenCV进行图像处理任务之前,我们需要进行相机标定,以获得相机的内部参数和外部参数。本文将介绍OpenCV的标定规则和标定过程。
一、相机标定的目的和意义
相机标定是指确定相机内部参数和外部参数的过程。相机内部参数包括焦距、主点位置、畸变系数等,而外部参数则包括相机的位置和朝向。相机标定的目的是为了校正图像中的畸变,使得图像中的物体能够按照真实的比例和形状呈现,从而提高后续图像处理算法的准确性和稳定性。
二、相机标定的规则
1.采集标定图像:为了进行相机标定,我们需要采集一组已知的标定图像。这些图像应该涵盖不同的场景和角度,并且包含已知的标定板或标定点。
2.选择标定板:标定板是一种特殊的图案,用于提供已知的空间参考。在OpenCV中,常用的标定板包括棋盘格、圆点格和棋盘格与圆点格的组合。选择合适的标定板对于标定的准确性非常重要。
3.确定相机和标定板的相对位置:在进行相机标定之前,需要确定相机和标定板的相对位置。一般来说,我们可以通过拍摄一张包含
标定板的图像,并使用标定板的已知尺寸来计算相机和标定板的相对位置。
4.检测标定板角点:标定板角点的检测是相机标定的关键步骤之一。OpenCV提供了一些用于检测角点的函数,如findChessboardCorners()和findCirclesGrid()等。通过检测标定板上的角点,我们可以计算相机的内部参数和外部参数。
5.计算相机参数:一旦检测到了标定板的角点,我们就可以利用这些角点来计算相机的内部参数和外部参数。OpenCV提供了calibrateCamera()函数,用于计算相机参数。
单目相机高精度标定方法
单目相机高精度标定方法
摘要:相机标定是计算机视觉中的一个重要环节,对于单目相机来说,其标定过程是获取相机的内部参数和外部参数的过程。本文介绍了单目相机高精度标定方法的基本原理和具体步骤,并探讨了其中的一些关键技术和注意事项。
一、引言
相机标定是计算机视觉中的关键问题之一,它是指通过对相机进行一系列的观测和计算,从而确定相机的内部参数和外部参数。相机标定是计算机视觉中的基础工作,可以应用于三维重构、目标跟踪、姿态估计等领域。
二、相机标定的基本原理
相机标定的基本原理是通过观测特定的标定板上的已知点或特征,利用数学模型将像素坐标与实际世界坐标进行对应。标定板上的已知点可以是一些特殊的标记点,也可以是一些特征点,例如棋盘格。通过观测这些已知点在图像中的投影位置,可以通过解方程组的方式求解相机的内部参数和外部参数。
三、单目相机高精度标定方法的步骤
1. 准备标定板:选择适当的标定板,例如棋盘格,保证标定板上的特征点清晰可见。
2. 拍摄标定图像:将标定板放置在不同位置和角度下,使用单目相机拍摄一系列标定图像。要求标定图像的分辨率和图像质量较高。
3. 提取特征点:对于每张标定图像,使用图像处理方法提取出标定板上的特征点,例如角点。
4. 估计初始参数:利用已知的标定板尺寸,结合特征点的像素坐标,通过最小二乘法估计出初始的相机内部参数和外部参数。
5. 优化参数:通过非线性优化算法,对相机内部参数和外部参数进行迭代优化,使标定结果更加精确。
6. 评估标定结果:使用标定结果对标定图像进行重投影,计算重投影误差,评估标定结果的精度。
相机标定过程(opencv)+matlab参数导入opencv+matlab标定和矫正
相机标定过程(opencv)+matlab参数导⼊opencv+matlab标定和矫正%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
⾟苦原创所得,转载请注明出处%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start -- 摄像机标定 ---------------------------------------------->
摄像机标定的数学过程如下
标定事先选⽤棋盘格要注意⼀些问题,张正友论⽂中建议棋盘格数⼤于7*7。opencv标定时候对正⽅形的棋盘格标定板是不能识别的,需要长⽅形的标定板。张正友论⽂中建议每次拍摄标定板占50%以上,但这是对畸变并不是很⼤的普通相机⽽⾔的,对于球⾯相机是不适⽤的,相反球⾯相机标定使⽤的标定板占⽐应该较⼩⽐较好(对于格⼦并不是⾮常密的棋盘格⽽⾔),原因是因为棋盘格每个⾓点之间的距离越⼤,这段距离之间的可能发⽣畸变的点越多,如果占⽐过⼤就⽆法将形变体现在棋盘格中。棋盘格的选⽤应该根据实际需要选⽤,对于要求精密识别的情况,则需要⾼精度的棋盘格,相应的价格也会较⾼;对于精度要求并不是很⾼的(如抓取)情况并不需要精度很⾼的标定板,也能够节省开⽀。
这⾥程序的实现是在opencv中,所以就⽤opencv的程序来说明具体的过程.注意各个版本的opencv之间的程序移植性并不好,以下程序是在opencv2.4.3下编制运⾏的,每⼀步的要⽤到的输⼊输出都做了红⾊标记.
⽴体相机标定分为两个步骤,⼀个是单⽬标定(本⽂档第2步),另⼀个是双⽬标定
单目相机标定原理
单目相机标定原理
单目相机标定是计算机视觉中的一个重要工作,其目的是为了获得相机的内参数矩阵和畸变系数等相关参数,以便在三维空间中还原二维图像的3D信息。标定的过程主要分为内参数标定和外参数标定两部分。
内参数标定是指计算相机的内部参数,包括焦距、像素间距等,一般采用标定板的方法进行标定。标定板是一个有特殊图案的平板,其中包含了多个已知大小和位置的角点,通过对这些角点进行识别和匹配,可以得到相机的内参数矩阵和畸变系数等参数。
外参数标定则是指计算相机的外部参数,包括相机的位置和姿态等信息,一般采用多视图几何的方法进行标定。通过对多张不同角度拍摄的图像进行匹配和反演,可以得到相机的外部参数,进而计算出相机在三维空间中的位置和方向信息。
综上所述,单目相机标定是计算机视觉中的一个非常重要的工作,它不仅可以在3D空间中还原出2D图像的信息,而且在很多实际应用中也具有重要的应用价值。
相机模型与标定(七)--LM算法在相机标定中的使用
相机模型与标定(七)--LM算法在相机标定中的使⽤
LM算法在相机标定的应⽤共有三处。
(1)单⽬标定或双⽬标定中,在内参固定的情况下,计算最佳外参。OpenCV中对应的函数为findExtrinsicCameraParams2。
(2)单⽬标定中,在内外参都不固定的情况下,计算最佳内外参。OpenCV中对应的函数为calibrateCamera2。
(3)双⽬标定中,在左右相机的内外参及左右相机的位姿都不固定的情况下,计算最佳的左右相机的内外参及最佳的左右相机的位姿矩阵。OpenCV中对应的函数为stereoCalibrate。
本⽂⽂阅读前提是你已经对LM(Levenberg-Marquardt)算法有⾜够的了解。因为本⽂主要是分析LM算法在相机标定中应⽤。
本⽂的分析是基于OpenCV的源码,所以可参见OpenCV的源码阅读此⽂。
0变量设置
设标定板上⾓点数为m,标定过程中拍摄n幅视图(对双⽬标定⽽⾔,左右相机各抓取n幅视图)。
关于相机的成像模型和畸变模型,我这⾥就不占空间了,详见OpenCV官⽅⽂档或相关论⽂。我⽤如下函数表⽰:
其中,(u,v)是像素坐标,(X,Y,Z)是世界坐标,R=(r1, R2, R3)T是旋转外参,T=(T1, T2, T3)T是平移外参,A=(fx, fy, cx, cy)T是投影内参,D=(k1, k2, p1,p2, k3, k4, k5, k6, s1, s2, s3, s4, a, b)T是畸变内参。
纵所周知,(u,v)是存在畸变的(后⽂称之为畸变坐标),我们⽤(uu,vv)表⽰(u,v)对应的⾮畸变的坐标(后⽂称之为标准坐标)。我们⽤findChessboardCorners提取的⾓点坐标就被当作是标准坐标。
opencv 标定板内参及选点
opencv 标定板内参及选点
OpenCV是一个广泛使用的计算机视觉库,提供了一系列用于图像处理和计算机视觉的函数和算法。其中一个重要的功能是相机标定,也就是通过对已知的标定板进行图像处理,得到相机的内参和畸变参数。本文将介绍如何使用OpenCV进行相机标定,并讲解选取标定板的注意事项。
相机标定是计算机视觉中的重要步骤,它的目的是通过对已知几何形状的标定板进行图像处理,从而计算出相机的内参矩阵和畸变系数。相机的内参主要包括焦距、主点坐标和像素宽高比等参数,而畸变系数则用于校正图像中的畸变。相机标定是许多计算机视觉任务的基础,如相机姿态估计、三维重建等。
在OpenCV中,相机标定的函数为`cv2.calibrateCamera()`。在调用这个函数之前,我们需要准备一个已知几何形状的标定板,并拍摄一组包含标定板的图像。标定板可以是棋盘格、圆点阵列等,不同的标定板对应不同的标定算法。
选取标定板时,需要注意以下几点。首先,标定板的几何形状要尽可能规则,以便于计算。其次,标定板要有足够的对比度,以便于检测。最后,标定板的大小要适中,不要过大或过小。对于棋盘格标定板,常见的尺寸为8x6或9x7。
在进行相机标定时,需要提供一组包含标定板的图像。这些图像应
该在不同的角度和位置下拍摄,以覆盖相机的全视场。为了提高标定的精度,最好使用不同焦距和光照条件下的图像。在拍摄图像时,要保证标定板平面与相机光轴垂直,以避免透视畸变。
标定图像准备好后,就可以调用`cv2.calibrateCamera()`函数进行相机标定了。该函数会返回相机的内参矩阵、畸变系数和旋转矩阵等信息。内参矩阵可以用于计算相机的投影矩阵,而畸变系数可以用于校正图像中的畸变。
为什么要进行相机标定-相机标定有何意义-
为什么要进行相机标定?相机标定有何意
义?
01为什么要进行相机标定
随着(机器视觉)的迅猛发展,我们已经不满足于使用摄像机进行监控、抓拍这种较为简单的功能。更多的用户青睐于它在非接触三维尺寸测量上的应用。我们所谓的三维测量是广义的三维测量,它不仅包括三维物体的重构与测量,还包括在三维空间中识别任意二维平面上的尺寸以及位置。这种技术目前已被应用在(高精度)的(工业)模具以及装配测量中,其中任意二维平面上的尺寸(检测)技术应用得更为广泛。
图一如图1当被测平面和像平面平行且成像模型为理想的小孔成像模型,我们设焦距为、工作距离为,则被测物
和它的像关系可简单的表示为:
但是在实际应用中并非如此,我们无法严格控制像平面和被测平面的位置,所用的镜头也不是严格的小孔模型。如果直接使用【1】式计算将会产生极大的误差。因此,为了获取更高的测量精度,我们需要通过标定来实现坐标平面的转换以及图像的校正。
02什么是相机标定
在实际应用中,被测平面的不确定性以及镜头的畸变使我们已经无法简单的使用【1】式计算出实际距离,但是我们可以将目前能够获得的数据进行转换,使这些数据符合【1】式的使用条件。也就是将任意坐标平面通过旋转和平移映射到理想坐标平面上,对有畸变的图像进行校正,让它成为符合小孔成像模型的像平面。有了这种
方法,我们只要确定转换(算法)、校正算法以及【1】式中的参数就可以实现三维空间中任意平面上尺寸与位置的测量。我们将这种确定参数的过程称之为标定。
03相机单目标定
相机标定的方法根据摄像机的数目可分为单目标定、双目标定以及多目标定。其中单目相机标定是双目标定的基础,而多目相机的标定则是双目相机的扩展。因此,我们今天首先来为大家介绍单目标定。在平面测量中影响我们拍摄图像形变的因素有两个:镜头和相机姿态。根据这两个因素我们将摄像机的参数分为两组,相机内参和相机外参。
opencv 标定参数使用
opencv 标定参数使用
OpenCV是一个开源的计算机视觉库,广泛应用于图像处理和计算机视觉任务中。在许多计算机视觉应用中,相机的标定是一个重要的步骤,用于确定相机内部和外部参数,以便准确地测量和分析图像中的物体。本文将介绍使用OpenCV进行相机标定的参数设置。
相机标定是将相机的内参和外参参数进行估计的过程。内参包括焦距、主点坐标和畸变参数等,而外参则包括相机的位置和朝向参数。在进行相机标定之前,需要准备一组已知的三维空间点和对应的二维图像点对,这些点对即为标定板上的特征点。
在OpenCV中,相机标定的参数设置包括标定板的尺寸、标定板上特征点的尺寸、待标定相机的图像尺寸等。首先,标定板的尺寸是指标定板上特征点的行列数,通过设置这些参数可以提供更准确的标定结果。其次,标定板上特征点的尺寸也需要根据实际情况进行设置,通常选择较大的特征点尺寸可以提高标定的精度。最后,待标定相机的图像尺寸也需要根据实际情况进行设置,以确保标定结果的准确性。
在进行相机标定之前,还需要设置一些与标定相关的参数,例如用于检测标定板的方法、标定板上特征点的检测阈值等。OpenCV提供了多种方法用于检测标定板,例如使用棋盘格或圆点阵列等。根据实际情况选择合适的检测方法可以提高标定的准确性。此外,还
可以设置标定板上特征点的检测阈值,通过调整这个参数可以提高标定的鲁棒性。
在进行相机标定时,还需要设置一些与优化相关的参数,例如优化算法的类型、最大迭代次数等。OpenCV提供了多种优化算法,例如Levenberg-Marquardt算法、高斯牛顿算法等。根据实际情况选择合适的优化算法可以提高标定的精度。此外,还可以设置最大迭代次数,通过增加迭代次数可以提高标定的收敛性。
opencv坐标转换标定
opencv坐标转换标定
OpenCV是一款广泛应用于计算机视觉和图像处理中的开源库,在许多应用领域都有广泛应用,如机器人导航、人脸识别、图像分割等。在使用OpenCV进行图像处理时,往往需要进行坐标转换和标定,以便准确地定位和测量图像中的物体。本文将详细介绍OpenCV的坐标转换和标定方法,帮助读者更好地理解和应用该技术。
一、坐标转换方法
1. 图像坐标系和世界坐标系
在进行坐标转换之前,需要明确图像坐标系和世界坐标系的概念。图像坐标系是指图像中某一点的坐标表示,通常以像素为单位,原点通常位于图像的左上角。世界坐标系是指实际物体的坐标表示,通常以米或毫米为单位,原点的位置可以根据实际情况确定。
2. 相机坐标系和像素坐标系的转换
在实际应用中,需要将相机坐标系中的点转换为像素坐标系中的点,以便在图像上进行显示和分析。相机坐标系是指相机的坐标表示,通常以相机的光心为原点,光轴为Z轴,X轴和Y轴垂直光轴,构成一个右手坐标系。像素坐标系是指图像上像素点的坐标表示。
在OpenCV中,可以通过相机标定得到相机的内外参数,从而进行相机坐标系和像素坐标系之间的转换。具体步骤如下:
(1)使用棋盘格等已知模式在不同位置拍摄图像。
(2)通过findChessboardCorners等函数找到每张图像中的角点。(3)使用calibrateCamera函数计算相机的内外参数。
(4)通过projectPoints函数将相机坐标系中的点投影到像素坐标系中。
3. 坐标系旋转和平移
在进行坐标转换时,可能存在坐标系旋转和平移的情况,特别是在机器人导航和目标跟踪等场景中,需要将相对于机器人或目标物体的坐标转换为图像坐标。在OpenCV中,可以使用旋转矩阵和平移矩阵对坐标进行旋转和平移。具体步骤如下:
基于OpenCV的摄像机标定方法的实现
EetcP w r S ag i 0 00, hn ) l r o e, h nh 20 9 C i ci a a
Abta t O e o recm u rvs n ( pn V)l rr sit d cd fs y n h n te s c : pn suc o p t i o O e C r e i i ayi n o ue r ,ad te h b r it l
c me a mo e i ie a r d l s g v n. Co sde n a i l it rin n a g n il itri n , a ag rtm f n i r g r d a d soto s a d t n e t d so o s i a t n lo h o i c me a c l r t n b s d o e CV sg v n i a r a i ai a e n Op n b o i ie n VC + e io me t + nvr n n .Th sa g rt m k sf l u e i lo h i ma e ul s
第2 6卷 第 4期
上 海 电 力 学 院 学
报
Vo . 6, No 4 12 . Au . 2 1 g 0 0
21 0 0年 8月
J u n l o S a g a Un v r i o E e ti Po r o r a f hn hi i e st y f l crc we
单目相机标定方法
单目相机标定方法
在计算机视觉中,单目相机标定是一项极其重要的技术,它是相
机应用的关键基础之一。单目相机标定指的是根据已知物体的三维坐
标和对应的图像坐标,对相机内参、畸变参数等进行精确校准的过程。以下是单目相机标定的步骤:
Step1:制备标定板
标定板是这个过程中非常重要的一部分。它需要是具有不同的特
征点以及明确的大小和形状的物体,例如黑白方格板。标定板中的特
征点可以帮助程序识别其在图像中的位置和方位,并通过这些点的位
置和方向得出相机内部的参数。
Step2:拍摄标定板
将制作好的标定板放置在相机平面上,平行于图像平面拍下多张
底片,换取不同的轴角度和物体位置,然后将拍摄到的图像保存下来。
Step3:读取图片信息
将标定板拍摄的图像读取到计算机中,并解析出其中的特征点作
为后续步骤的输入数据。
Step4:计算角点的位置坐标
角点是标定板中一个非常重要的特征点,它们可以根据计算的方
式在图像中求得。从每张图片中提取角点位置信息,以便后续处理。
Step5:校准相机内部参数
将标定板的标准定义进行数学建模,并使用标定板中的特征点和
相应的图像来确定相机内部参数,例如焦距、畸变、旋转矩阵和平移
向量等。
Step6:测试标定效果
最后一步是测试标定的效果,通常采用一些标准的度量来计算标
定误差。例如,对于每张图片,使用标定结果进行还原,然后将还原
结果与真实值进行比较。如果误差很小,说明标定效果比较好。
总之,单目相机标定是计算机视觉中的一个关键技术,可以帮助
我们准确地校准相机的内部参数,提高图像处理的准确度。在实际应用中,需要仔细考虑标定板的选择和制备、拍摄条件等多个因素,以确保标定的精度和准确性。
opencv3 标定
opencv3 标定
1. 什么是opencv3标定?
OpenCV是一款著名的计算机视觉库,其中最著名的模块为相机标定模块。标定是将摄像机内部参数(例如焦距、光心坐标、畸变)和外部参数(例如旋转和平移)逐一确定的过程。opencv3 标定就是利用opencv中的算法来标定摄像机的内部和外部参数。
2. 标定的原理
在进行标定之前,需要使用摄像机先拍摄一些特定的图像,如棋盘格图像。然后我们可以使用单应性矩阵来确定这些图像的与实际对象之间的关系。对于这些图像进行处理后,可以得到摄像机的内部参数,以及每个图像的外部参数。
3. 标定的步骤
步骤1:获取校正图像,这些图像需要拍摄包含各自不同位置的棋盘格
步骤2:角点检测,通过opencv的cornerSubPix函数来检测并精确定位图像中的角点
步骤3:求解摄像机的内部参数,使用相机的内部参数矩阵,将摄像图像点映射成对象空间中的点
步骤4:求解摄像机的外部参数,使用ransac等算法获得相机的外部参数
步骤5:评估校准结果,使用畸变校正和重投影误差来评估标定结果#4.应用场景
相机标定在很多领域都有应用,例如机器人视觉、视觉导航、3D建模等。它能够提升相机标定的精度,从而提高计算机视觉处理的效果。
5. 结论
Opencv3标定是一种常用的计算机视觉技术,对于多种应用领域都有着重要的作用。标定的过程需要准备合适的样本,并掌握相应的技巧和方法,才能达到满足数字测量的精度要求。
halcon 单目相机 标定例程
HALCON是一个强大的机器视觉软件,它支持各种相机的标定。单目相机标定的主要目的是完成像素坐标到世界坐标之间的转换。此外,标定还能帮助我们进行畸变矫正和一维二维图像测量,因为相机成像后往往会产生畸变和缩放。
在HALCON中,你可以使用标定助手来完成单相机的标定。具体的步骤如下:
1. 打开HALCON并新建一个程序。
2. 在程序编辑中输入算子gen_caltab,然后右击打开算子窗口,输入相关参数,点击确定。这一步会生成两个文件,一个是后缀为.descr的标定板描述文件,另一个是标定板的图像文件。
3. 打开助手里的标定助手。
4. 进入到标定助手界面,更改描述文件、摄像机模型以及相机相关参数。
5. 点击标定选项卡,然后点击图像采集助手,会立刻弹出图像采集助手对话框(如果之前打开过图像采集助手,在这里就不会弹出),接下里进行相机的连接。点击自动检测接口,可以选择halcon自带的接口或者是相机的接口。
通过以上步骤,你便可以使用HALCON对单目相机进行标定了。请注意,这只是一个基本流程,实际操作可能需要根据你的具体需求和设备进行调整。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/video/video.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
#include
#include
using namespace cv;
using namespace std;
const char * usage =
" \nexample command line for calibration from a live feed.\n"
" calibration -w=4 -h=5 -s=0.025 -o=camera.yml -op -oe\n"
" \n"
" example command line for calibration from a list of stored images:\n"
" imagelist_creator image_list.xml *.png\n"
" calibration -w=4 -h=5 -s=0.025 -o=camera.yml -op -oe image_list.xml\n"
" where image_list.xml is the standard OpenCV XML/YAML\n"
" use imagelist_creator to create the xml or yaml list\n"
" file consisting of the list of strings, e.g.:\n"
" \n"
"\n"
"
"
"view000.png\n"
"view001.png\n"
"\n"
"view003.png\n"
"view010.png\n"
"one_extra_view.jpg\n"
"
"
const char* liveCaptureHelp =
"When the live video from camera is used as input, the following hot-keys may be used:\n"
"
" 'g' - start capturing images\n"
" 'u' - switch undistortion on/off\n";
static void help()
{
printf( "This is a camera calibration sample.\n"
"Usage: calibration\n"
" -w=
" -h=
" [-pt=
" [-n=
" # (if not specified, it will be set to the number\n"
" # of board views actually available)\n"
" [-d=
" # (used only for video capturing)\n"
" [-s=
" [-o=
" [-op] # write detected feature points\n"
" [-oe] # write extrinsic parameters\n"
" [-zt] # assume zero tangential distortion\n"
" [-a=
" [-p] # fix the principal point at the center\n"
" [-v] # flip the captured images around the horizontal axis\n"
" [-V] # use a video file, and not an image list, uses\n"
" # [input_data] string for the video file name\n"
" [-su] # show undistorted images after calibration\n"
" [input_data] # input data, one of the following:\n"
" # - text file with a list of the images of the board\n"
" # the text file can be generated with imagelist_creator\n"
" # - name of video file with a video of the board\n"
" # if input_data not specified, a live view from the camera is used\n"
"\n" );
printf("\n%s",usage);
printf( "\n%s", liveCaptureHelp );
}
enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
static double computeReprojectionErrors(
const vector
const vector
const vector
const Mat& cameraMatrix, const Mat& distCoeffs,
vector
{
vector
int i, totalPoints = 0;
double totalErr = 0, err;
perViewErrors.resize(objectPoints.size());
for( i = 0; i < (int)objectPoints.size(); i++ )
{
projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i],
cameraMatrix, distCoeffs, imagePoints2);
err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2);
int n = (int)objectPoints[i].size();
perViewErrors[i] = (float)std::sqrt(err*err/n);
totalErr += err*err;
totalPoints += n;
}
return std::sqrt(totalErr/totalPoints);
}
static void calcChessboardCorners(Size boardSize, float squareSize, vector
{
corners.resize(0);
switch(patternType)
{
case CHESSBOARD:
case CIRCLES_GRID:
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float(j*squareSize),
float(i*squareSize), 0));
break;
case ASYMMETRIC_CIRCLES_GRID:
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float((2*j + i % 2)*squareSize),
float(i*squareSize), 0));
break;
default:
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
}
}
static bool runCalibration( vector
Size imageSize, Size boardSize, Pattern patternType,
float squareSize, float aspectRatio,
int flags, Mat& cameraMatrix, Mat& distCoeffs,
vector
vector
double& totalAvgErr)
{
cameraMatrix = Mat::eye(3, 3, CV_64F
);
if( flags & CALIB_FIX_ASPECT_RATIO )
cameraMatrix.at
distCoeffs = Mat::zeros(8, 1, CV_64F);
vector
calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType);
objectPoints.resize(imagePoints.size(),objectPoints[0]);
double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix,
distCoeffs, rvecs, tvecs, flags|CALIB_FIX_K4|CALIB_FIX_K5);
///*|CALIB_FIX_K3*/|CALIB_FIX_K4|CALIB_FIX_K5);
double rms1 = cvCalibrateCamera2(objectPoints, imagePoints, imageSize, cameraMatrix,
distCoeffs, rvecs, tvecs, flags|CALIB_FIX_K4|CALIB_FIX_K5);
///*|CALIB_FIX_K3*/|CALIB_FIX_K4|CALIB_FIX_K5);
printf("RMS error reported by calibrateCamera: %g\n", rms);
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints,
rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs);
return ok;
}
static void saveCameraParams( const string& filename,
Size imageSize, Size boardSize,
float squareSize, float aspectRatio, int flags,
const Mat& cameraMatrix, const Mat& distCoeffs,
const vector
const vector
const vector
double totalAvgErr )
{
FileStorage fs( filename, FileStorage::WRITE );
time_t tt;
time( &tt );
struct tm *t2 = localtime( &tt );
char buf[1024];
strftime( buf, sizeof(buf)-1, "%c", t2 );
fs << "calibration_time" << buf;
if( !rvecs.empty() || !reprojErrs.empty() )
fs << "nframes" << (int)std::max(rvecs.size(), reprojErrs.size());
fs << "image_width" << imageSize.width;
fs << "image_height" << imageSize.height;
fs << "board_width" << boardSize.width;
fs << "board_height" << boardSize.height;
fs << "square_size" << squareSize;
if( flags & CALIB_FIX_ASPECT_RATIO )
fs << "aspectRatio" << aspectRatio;
if( flags != 0 )
{
sprintf( buf, "flags: %s%s%s%s",
flags & CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" : "",
flags & CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "",
flags & CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" : "",
flags & CALIB_ZERO_TANGENT_DIST ? "+zero_tangent_dist" : "" );
//cvWriteComment( *fs, buf, 0 );
}
fs << "flags" << flags;
fs << "camera_matrix" << cameraMatrix;
fs << "distortion_coefficients" << distCoeffs;
fs << "avg_reprojection_error" << totalAvgErr;
if( !reprojErrs.empty() )
fs << "per_view_reprojection_errors" << Mat(reprojErrs);
if( !rvecs.empty() && !tvecs.empty() )
{
CV_Assert(rvecs[0].type() == tvecs[0].ty
pe());
Mat bigmat((int)rvecs.size(), 6, rvecs[0].type());
for( int i = 0; i < (int)rvecs.size(); i++ )
{
Mat r = bigmat(Range(i, i+1), Range(0,3));
Mat t = bigmat(Range(i, i+1), Range(3,6));
CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
//*.t() is MatExpr (not Mat) so we can use assignment operator
r = rvecs[i].t();
t = tvecs[i].t();
}
//cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 );
fs << "extrinsic_parameters" << bigmat;
}
if( !imagePoints.empty() )
{
Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2);
for( int i = 0; i < (int)imagePoints.size(); i++ )
{
Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols);
Mat imgpti(imagePoints[i]);
imgpti.copyTo(r);
}
fs << "image_points" << imagePtMat;
}
}
static bool readStringList( const string& filename, vector
{
l.resize(0);
FileStorage fs(filename, FileStorage::READ);
if( !fs.isOpened() )
return false;
FileNode n = fs.getFirstTopLevelNode();
if( n.type() != FileNode::SEQ )
return false;
FileNodeIterator it = n.begin(), it_end = n.end();
for( ; it != it_end; ++it )
l.push_back((string)*it);
return true;
}
static bool runAndSave(const string& outputFilename,
const vector
Size imageSize, Size boardSize, Pattern patternType, float squareSize,
float aspectRatio, int flags, Mat& cameraMatrix,
Mat& distCoeffs, bool writeExtrinsics, bool writePoints )
{
vector
vector
double totalAvgErr = 0;
bool ok = runCalibration(imagePoints, imageSize, boardSize, patternType, squareSize,
aspectRatio, flags, cameraMatrix, distCoeffs,
rvecs, tvecs, reprojErrs, totalAvgErr);
printf("%s. avg reprojection error = %.2f\n",
ok ? "Calibration succeeded" : "Calibration failed",
totalAvgErr);
if( ok )
saveCameraParams( outputFilename, imageSize,
boardSize, squareSize, aspectRatio,
flags, cameraMatrix, distCoeffs,
writeExtrinsics ? rvecs : vector
writeExtrinsics ? tvecs : vector
writeExtrinsics ? reprojErrs : vector
writePoints ? imagePoints : vector
totalAvgErr );
return ok;
}
int main( int argc, char** argv )
{
Size boardSize, imageSize;
float squareSize, aspectRatio;
Mat cameraMatrix, distCoeffs;
string outputFilename;
string inputFil
ename = "";
int i, nframes;
bool writeExtrinsics, writePoints;
bool undistortImage = false;
int flags = 0;
VideoCapture capture;
bool flipVertical;
bool showUndistorted;
bool videofile;
int delay;
clock_t prevTimestamp = 0;
int mode = DETECTION;
int cameraId = 0;
vector
vector
Pattern pattern = CHESSBOARD;
cv::CommandLineParser parser(argc, argv,
"{help ||}{w||}{h||}{pt|chessboard|}{n|10|}{d|1000|}{s|1|}{o|out_camera_data.yml|}"
"{op||}{oe||}{zt||}{a|1|}{p||}{v||}{V||}{su||}"
"{@input_data|0|}");
if (parser.has("help"))
{
help();
return 0;
}
boardSize.width = parser.get
boardSize.height = parser.get
if ( parser.has("pt") )
{
string val = parser.get
if( val == "circles" )
pattern = CIRCLES_GRID;
else if( val == "acircles" )
pattern = ASYMMETRIC_CIRCLES_GRID;
else if( val == "chessboard" )
pattern = CHESSBOARD;
else
return fprintf( stderr, "Invalid pattern type: must be chessboard or circles\n" ), -1;
}
squareSize = parser.get
nframes = parser.get
aspectRatio = parser.get
delay = parser.get
writePoints = parser.has("op");
writeExtrinsics = parser.has("oe");
if (parser.has("a"))
flags |= CALIB_FIX_ASPECT_RATIO;
if ( parser.has("zt") )
flags |= CALIB_ZERO_TANGENT_DIST;
if ( parser.has("p") )
flags |= CALIB_FIX_PRINCIPAL_POINT;
flipVertical = parser.has("v");
videofile = parser.has("V");
if ( parser.has("o") )
outputFilename = parser.get
showUndistorted = parser.has("su");
if ( isdigit(parser.get
cameraId = parser.get
else
inputFilename = parser.get
if (!parser.check())
{
help();
parser.printErrors();
return -1;
}
if ( squareSize <= 0 )
return fprintf( stderr, "Invalid board square width\n" ), -1;
if ( nframes <= 3 )
return printf("Invalid number of images\n" ), -1;
if ( aspectRatio <= 0 )
return printf( "Invalid aspect ratio\n" ), -1;
if ( delay <= 0 )
return printf( "Invalid delay\n" ), -1;
if ( boardSize.width <= 0 )
return fprintf( stderr, "Invalid board width\n" ), -1;
if ( boardSize.height <= 0 )
return fprintf( stderr, "Invalid board height\n" ), -1;
if( !inputFilename.empty() )
{
if( !videofile && readStringList(inputFilename, imageList) )
mode = CAPTURING;
else
capture.open(inputFilename);
}
else
capture.open(cameraId);
if( !capture.isOpened() && imageList.empty() )
return fprintf( stderr, "Could not initializ
e video (%d) capture\n",cameraId ), -2;
if( !imageList.empty() )
nframes = (int)imageList.size();
if( capture.isOpened() )
printf( "%s", liveCaptureHelp );
namedWindow( "Image View", 1 );
for(i = 0;;i++)
{
Mat view, viewGray;
bool blink = false;
if( capture.isOpened() )
{
Mat view0;
capture >> view0;
view0.copyTo(view);
}
else if( i < (int)imageList.size() )
view = imread(imageList[i], 1);
if(view.empty())
{
if( imagePoints.size() > 0 )
runAndSave(outputFilename, imagePoints, imageSize,
boardSize, pattern, squareSize, aspectRatio,
flags, cameraMatrix, distCoeffs,
writeExtrinsics, writePoints);
break;
}
imageSize = view.size();
if( flipVertical )
flip( view, view, 0 );
vector
cvtColor(view, viewGray, COLOR_BGR2GRAY);
bool found;
switch( pattern )
{
case CHESSBOARD:
found = findChessboardCorners( view, boardSize, pointbuf,
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
break;
case CIRCLES_GRID:
found = findCirclesGrid( view, boardSize, pointbuf );
break;
case ASYMMETRIC_CIRCLES_GRID:
found = findCirclesGrid( view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID );
break;
default:
return fprintf( stderr, "Unknown pattern type\n" ), -1;
}
// improve the found corners' coordinate accuracy
if( pattern == CHESSBOARD && found) cornerSubPix( viewGray, pointbuf, Size(11,11),
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
if( mode == CAPTURING && found &&
(!capture.isOpened() || clock() - prevTimestamp > delay*1e-3*CLOCKS_PER_SEC) )
{
imagePoints.push_back(pointbuf);
prevTimestamp = clock();
blink = capture.isOpened();
}
if(found)
drawChessboardCorners( view, boardSize, Mat(pointbuf), found );
string msg = mode == CAPTURING ? "100/100" :
mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
int baseLine = 0;
Size textSize = getTextSize(msg, 1, 1, 1, &baseLine);
Point textOrigin(view.cols - 2*textSize.width - 10, view.rows - 2*baseLine - 10);
if( mode == CAPTURING )
{
if(undistortImage)
msg = format( "%d/%d Undist", (int)imagePoints.size(), nframes );
else
msg = format( "%d/%d", (int)imagePoints.size(), nframes );
}
putText( view, msg, textOrigin, 1, 1,
mode != CALIBRATED ? Scal
ar(0,0,255) : Scalar(0,255,0));
if( blink )
bitwise_not(view, view);
if( mode == CALIBRATED && undistortImage )
{
Mat temp = view.clone();
undistort(temp, view, cameraMatrix, distCoeffs);
}
imshow("Image View", view);
int key = 0xff & waitKey(capture.isOpened() ? 50 : 500);
if( (key & 255) == 27 )
break;
if( key == 'u' && mode == CALIBRATED )
undistortImage = !undistortImage;
if( capture.isOpened() && key == 'g' )
{
mode = CAPTURING;
imagePoints.clear();
}
if( mode == CAPTURING && imagePoints.size() >= (unsigned)nframes )
{
if( runAndSave(outputFilename, imagePoints, imageSize,
boardSize, pattern, squareSize, aspectRatio,
flags, cameraMatrix, distCoeffs,
writeExtrinsics, writePoints))
mode = CALIBRATED;
else
mode = DETECTION;
if( !capture.isOpened() )
break;
}
}
if( !capture.isOpened() && showUndistorted )
{
Mat view, rview, map1, map2;
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0),
imageSize, CV_16SC2, map1, map2);
for( i = 0; i < (int)imageList.size(); i++ )
{
view = imread(imageList[i], 1);
if(view.empty())
continue;
//undistort( view, rview, cameraMatrix, distCoeffs, cameraMatrix );
remap(view, rview, map1, map2, INTER_LINEAR);
imshow("Image View", rview);
int c = 0xff & waitKey();
if( (c & 255) == 27 || c == 'q' || c == 'Q' )
break;
}
}
return 0;
}