立体相机标定是立体视觉深度测量的重要步骤,相机标定的精度很大程度上决定了深度的精度,因此掌握立体相机的标定算法和过程至关重要。由于相机标定原理可以在网上找到很多相关资料,因此本文不展开讲原理部分,感兴趣的同学可以移步到https://blog.csdn.net/rs_lys/article/details/118661215理解原理,本文主要讲如何利用Opencv进行立体相机标定,首先对用到的函数展开讲解,最后附上标定的代码。
stereoCalibrate:标定立体相机
#include
double cv::stereoCalibrate ( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints1,
InputArrayOfArrays imagePoints2,
InputOutputArray cameraMatrix1,
InputOutputArray distCoeffs1,
InputOutputArray cameraMatrix2,
InputOutputArray distCoeffs2,
Size imageSize,
InputOutputArray R,
InputOutputArray T,
OutputArray E,
OutputArray F,
OutputArray perViewErrors,
int flags = CALIB_FIX_INTRINSIC,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6)
)
参数:
Vector of vectors
of the calibration pattern points.Vector of vectors
of the projections of the calibration pattern points, observed by the first camera
.Vector of vectors
of the projections of the calibration pattern points, observed by the second camera
.Input/output
first camera matrix
,Opencv没有考虑倾斜因子Input/output vector
of distortion coefficients (k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]]) of 4, 5, 8, 12 or 14 elements. 输出向量的长度取决于flagsInput/output
second camera matrix
.Input/output
lens distortion coefficients for the second camera.Output rotation matrix
between the 1st and the 2nd camera coordinate systems.Output translation vector
between the coordinate systems of the cameras.Output essential matrix
.Output fundamental matrix
.Output vector
of the RMS re-projection error
estimated for each pattern view
.除了立体相关信息外,该函数还可以对两个摄像头各进行单目标定。
然而,由于参数空间的高维性和输入数据中的噪声,函数可能会偏离正确的解。建议先使用calibrateCamera
单独标定每个相机的内参和畸变
,然后将CALIB_FIX_INTRINSIC
标志连同计算的内部参数一起传递给函数,以达到更高的标定精度。否则,如果一次性估计所有参数,那么限制一些参数是有意义的,例如,传递CALIB_SAME_FOCAL_LENGTH和CALIB_ZERO_TANGENT_DIST标志,这通常是一个合理的假设。
与calibrateCamera类似,该函数将两个相机的所有可用视图中所有点的总重投影误差降至最低
。该函数返回重投影错误的最终值。
calibrateCamera:单目标定
#include
double cv::calibrateCamera ( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
InputOutputArray cameraMatrix,
InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs,
OutputArrayOfArrays tvecs,
OutputArray stdDeviationsIntrinsics,
OutputArray stdDeviationsExtrinsics,
OutputArray perViewErrors,
int flags = 0,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON)
)
参数:
Vector of vectors
of the calibration pattern points.如果是一个平面标定板,那么所有的vectors都是相同的vector of vectors
of the projections of calibration pattern points (e.g. std::vector>
). imagePoints.size() must be equal to
objectPoints.size(), and imagePoints[i].size() must be equal to
objectPoints[i].size() for each i.Output 3x3 floating-point camera matrix
Output vector
of distortion coefficients (k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]]) of 4, 5, 8, 12 or 14 elements.Output vector of rotation vectors
(see Rodrigues ) estimated for each pattern view (e.g. std::vector>
).Output vector of translation vectors
estimated for each pattern view.Output vector of standard deviations estimated for intrinsic parameters.
Order of deviations values: (fx,fy,cx,cy,k1,k2,p1,p2,k3,k4,k5,k6,s1,s2,s3,s4,τx,τy) If one of parameters is not estimated, it’s deviation is equals to zero.Output vector of standard deviations estimated for extrinsic parameters.
Order of deviations values: (R1,T1,…,RM,TM) where M is number of pattern views, Ri,Ti are concatenated 1x3 vectors.Output vector
of the RMS re-projection error
estimated for each pattern view.
solvePnP
代替。该函数估计每个视图的相机内参数和外部参数。
3D物体点的坐标和它们在每个视图中对应的2D投影必须指定。这可以通过使用具有已知几何形状的物体
和容易检测到的特征点
来实现。这样的对象被称为标定模式,OpenCV内置了对棋盘
作为校准装置的支持(参见findChessboardCorners
)。
算法执行如下步骤:
solvePnP
完成的。Levenberg-Marquardt
优化算法来最小化重投影误差
,即观测特征点imagePoints和投影(使用当前对相机参数和位姿的估计)物体点objectPoints之间距离的平方和。详见projectPoints
。See also
findChessboardCorners, solvePnP, initCameraMatrix2D, stereoCalibrate, undistort
findChessboardCorners():找出棋盘角点的位置
#include
bool cv::findChessboardCorners ( InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE
)
参数:
8-bit grayscale or color image
.patternSize = cvSize(points_per_row,points_per_colum)
).Output array of detected corners
.该函数试图确定输入图像是否为棋盘图案的视图,并定位内部棋盘角。
如果找到了所有的角点,并且按一定的顺序放置(每行从左到右),则该函数返回一个非零值
。否则,如果函数未能找到所有的角或重新排序它们,它将返回0
。例如,一个普通的棋盘有8 x 8个正方形和7 x 7个内角,也就是黑色方块相互接触的点。检测到的坐标是近似的,为了更准确地确定它们的位置,该函数调用cornerSubPix
。如果返回的坐标不够精确,您还可以使用带有不同参数的函数cornerSubPix。
该函数需要在棋盘格周围留出空白(比如方形粗的边框,越宽越好),以使检测在各种环境中更稳健。
否则,如果没有边框且背景为深色,则无法正确分割出外部的黑色方块,从而导致方块分组排序算法失败。
Sample:
Size patternsize(8, 6); //interior number of corners
Mat gray = ....; //source image
vector<Point2f> corners; //this will be filled by the detected corners
//CALIB_CB_FAST_CHECK saves a lot of time on images
//that do not contain any chessboard corners
bool patternfound = findChessboardCorners(gray, patternsize, corners,
CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE
+ CALIB_CB_FAST_CHECK);
if (patternfound)
cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1),
TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
drawChessboardCorners(img, patternsize, Mat(corners), patternfound);
cornerSubPix():对初始的整数角点坐标进行亚像素精度的优化
#include
void cv::cornerSubPix ( InputArray image,
InputOutputArray corners,
Size winSize,
Size zeroZone,
TermCriteria criteria
)
原理解释:亚像素角点的求法
参数:
Input single-channel, 8-bit or float image.
Initial coordinates
of the input corners and refined coordinates
provided for output.Half of the side length of the search window.
For example, if winSize=Size(5,5) , then a (5∗2+1)×(5∗2+1)=11×11 search window is used.设置为(-1, -1)即可
。最大迭代次数
,要么相邻两次迭代的角点位置移动小于给定阈值
,则终止。drawChessboardCorners():绘制检测出来的棋盘角点。
#include
void cv::drawChessboardCorners ( InputOutputArray image,
Size patternSize,
InputArray corners,
bool patternWasFound
)
参数:
8-bit color image
.patternSize = cv::Size(points_per_row,points_per_column)
).Array of detected corners, the output of findChessboardCorners.
The return value of findChessboardCorners should be passed here.
该函数绘制单独的棋盘角点
,如果没有找到棋盘,则检测到红色圆圈,如果找到了棋盘,则检测到与线相连的彩色角点。
TermCriteria():为迭代算法定义终止标准的类。可以通过默认构造函数初始化它
#include
cv::TermCriteria::TermCriteria ( int type,
int maxCount,
double epsilon
)
参数:
TermCriteria::Type
stereoRectify():基于立体相机内外参,计算极线校正变换。
#include
void cv::stereoRectify ( InputArray cameraMatrix1,
InputArray distCoeffs1,
InputArray cameraMatrix2,
InputArray distCoeffs2,
Size imageSize,
InputArray R,
InputArray T,
OutputArray R1,
OutputArray R2,
OutputArray P1,
OutputArray P2,
OutputArray Q,
int flags = CALIB_ZERO_DISPARITY,
double alpha = -1,
Size newImageSize = Size(),
Rect * validPixROI1 = 0,
Rect * validPixROI2 = 0
)
参数:
Size of the image used for stereo calibration.
Output 3x3
rectification transform (rotation matrix
) for the first camera.Output 3x3
rectification transform (rotation matrix
) for the second camera.Output 3x4 projection matrix
in the new (rectified) coordinate systems for the first camera.Output 3x4 projection matrix
in the new (rectified) coordinate systems for the second camera.Output 4×4 disparity-to-depth mapping matrix
(see reprojectImageTo3D
).零或CALIB_ZERO_DISPARITY
。如果设置了该标志,该函数使每个摄像头的主要点在校正视图中具有相同的像素坐标。如果没有设置标志,该函数仍然可以在水平或垂直方向(取决于极线的方向)移动图像,以最大化有用的图像区域。Alpha =0
意味着校正后的图像被缩放和移动,因此有效的像素可见(校正后没有黑色区域
)。Alpha =1
意味着校正后的图像被抽取和移位,因此所有来自相机的原始图像的像素都保留在修正后的图像中(没有源图像像素丢失
)。显然,任何中间值都会产生介于这两种极端情况之间的中间结果。没弄明白原理,直接设置为-1或缺省就行。initUndistortRectifyMap
(参见OpenCV samples目录中的stereo_calib.cpp示例
)。当(0,0)被传递时(默认),它被设置为原始的imageSize。
将其设置为较大的值可以帮助您保存原始图像中的细节,特别是当有较大的径向失真时。可选
输出校正图像内的矩形区域,其中所有像素是有效的。如果alpha=0, roi覆盖整个图像。否则,它们可能会更小(见下图)。可选
输出校正图像内的矩形区域,其中所有像素是有效的。如果alpha=0, roi覆盖整个图像。否则,它们可能会更小(见下图)。该函数计算每个相机的旋转矩阵,使两个相机图像平面相同。因此,这使得所有的极线平行,从而简化了密集立体对应问题。该函数以sterecalibrate
计算的矩阵作为输入
。作为输出
,它提供了两个旋转矩阵
和两个在新坐标中的投影矩阵
。该函数区分以下两种情况:
正如你所看到的,P1和P2的前三列将有效地成为新的“校正”相机矩阵。校正矩阵,连同R1和R2,然后可以传递给initUndistortRectifyMap
来初始化每个相机的校正坐标映射。
下面是来自stereo_calib.cpp示例
的截图。一些红色的水平线穿过相应的图像区域。这意味着图像得到了很好的校正,这是大多数立体匹配算法所依赖的。绿色矩形是roi1和roi2。你可以看到它们的内部都是有效的像素。
initUndistortRectifyMap():计算去畸变和极线校正变换图。
#include
void cv::initUndistortRectifyMap ( InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2
)
参数:
Optional rectification transformation in the object space (3x3 matrix).
对于单目相机,直接忽略,默认是Identity;对于立体相机,传入stereoRectify
输出的R1/R2。这个size必须与stereoRectify中的newImageSize一样
。CV_32FC1, CV_32FC2 or CV_16SC2
, see convertMaps
The first output map.
The second output map.
在单目相机
的情况下,newCameraMatrix通常等于cameraMatrix
,或者它可以通过getOptimalNewCameraMatrix
来计算,以便更好地控制缩放
。对于立体相机
来说,newCameraMatrix通常设置为通过立体矫正计算出的P1或P2。
该函数实际上为remap使用的反向映射算法构建映射。
即,对于目标(校正和校正)图像中的每个像素(u,v),该函数计算源图像(即来自相机的原始图像)中对应的坐标。应用以下流程:
如果是立体相机,这个函数会被调用两次:每个相机调用一次。
但是如果立体相机没有被校准,仍然可以使用stereoRectifyUncalibrated
从基本矩阵直接计算校正变换。感兴趣的可以看Opencv官网文档。
remap():对图像进行几何变换
#include
void cv::remap ( InputArray src,
OutputArray dst,
InputArray map1,
InputArray map2,
int interpolation,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
参数:
It has the same size as map1 and the same type as src .
CV_16SC2 , CV_32FC1, or CV_32FC2
. See convertMaps
for details on converting a floating point representation to fixed-point for speed.CV_16UC1, CV_32FC1
, or none (empty map if map1 is (x,y) points), respectively.InterpolationFlags
). The method INTER_AREA is not supported by this function.BorderTypes
). When borderMode=BORDER_TRANSPARENT, it means that the pixels in the destination image that corresponds to the “outliers” in the source image are not modified by the function.constant border
. By default, it is 0.convertMaps():将图像变换映射从一种表示转换为另一种表示。
#include
void cv::convertMaps ( InputArray map1,
InputArray map2,
OutputArray dstmap1,
OutputArray dstmap2,
int dstmap1type,
bool nninterpolation = false
)
参数:
CV_16SC2, CV_32FC1, or CV_32FC2
.CV_16UC1, CV_32FC1, or none (empty matrix),
respectively.has the type dstmap1type and the same size as src .
CV_16SC2, CV_32FC1, or CV_32FC2 .
用于
最近邻插值(false)还是用于更复杂的插值(true)。该函数将一对映射从一种表示转换为另一种表示。支持以下选项:
initCameraMatrix2D():计算初始相机内参,目前仅适用与平面标定板。具体原理参考张正友标定法
#include
Mat cv::initCameraMatrix2D ( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
double aspectRatio = 1.0
)
参数:
Vector of vectors of the calibration pattern points
in the calibration pattern coordinate space. In the old interface all the per-view vectors are concatenated. See calibrateCamera
for details.Vector of vectors of the projections of the calibration pattern points.
In the old interface all the per-view vectors are concatenated.initialize the principal point.
首先我们来展示一下Opencv立体标定的流程,这样更利于我们理解下面的代码。
#include "opencv2/calib3d.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
static void
StereoCalib(const vector<string>& imagelist, Size boardSize, float squareSize, bool displayCorners = false, bool showRectified = true)
{
if (imagelist.size() % 2 != 0)
{
cout << "Error: the image list contains odd (non-even) number of elements\n";
return;
}
const int maxScale = 2;
// ARRAY AND VECTOR STORAGE:
vector<vector<Point2f> > imagePoints[2]; //这是一个数组,imagePoints[0]和imagePoints[1]都是一个vector >
vector<vector<Point3f> > objectPoints;
Size imageSize;
int i, j, k, nimages = (int)imagelist.size() / 2;
imagePoints[0].resize(nimages);
imagePoints[1].resize(nimages);
vector<string> goodImageList; //好图像对的路径
//遍历所有图像对,提取角点
for (i = j = 0; i < nimages; i++) //遍历所有图像对
{
for (k = 0; k < 2; k++) //处理一个图像对
{
const string& filename = imagelist[i * 2 + k];
Mat img = imread(filename, 0);
if (img.empty())
break;
//初始化图像尺寸
if (imageSize == Size())
imageSize = img.size();
else if (img.size() != imageSize)
{
cout << "The image " << filename << " has the size different from the first image size. Skipping the pair\n";
break;
}
bool found = false;
//k指代左右图像,j指代角点提取成功的图像对(好图像对),提取成功后,j++,否则,下一次成功的覆盖当前不成功的像对的角点
vector<Point2f>& corners = imagePoints[k][j];
//在原尺度和两倍尺度下提取角点,提高提取成功的几率,如果原尺度提取成功,则不用进行两倍尺度下的提取
for (int scale = 1; scale <= maxScale; scale++)
{
Mat timg;
if (scale == 1)
timg = img;
else
resize(img, timg, Size(), scale, scale, INTER_LINEAR_EXACT);
//findChessboardCorners寻找角点,自适应阈值+归一化图像,返回是否成功的标志
found = findChessboardCorners(timg, boardSize, corners,
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE);
if (found)
{
if (scale > 1)
{
Mat cornersMat(corners);
cornersMat *= 1. / scale;
}
break;
}
}
//显示提取的角点
if (displayCorners)
{
cout << filename << endl;
Mat cimg, cimg1;
//drawChessboardCorners的输入图像必须是彩色图
cvtColor(img, cimg, COLOR_GRAY2BGR);
drawChessboardCorners(cimg, boardSize, corners, found);
double sf = 640. / MAX(img.rows, img.cols);
resize(cimg, cimg1, Size(), sf, sf, INTER_LINEAR_EXACT);
imshow("corners", cimg1);
char c = (char)waitKey(500);
if (c == 27 || c == 'q' || c == 'Q') //Allow ESC to quit
exit(-1);
}
else
putchar('.');
//如果图像对任何一张提取不成功,则结束当前图像对的角点提取
if (!found)
break;
//角点亚像素精度优化
cornerSubPix(img, corners, Size(11, 11), Size(-1, -1),
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,
30, 0.01));
}
//如果图像对的两张图像都提取角点成功,那么j++,并且保存好的像对的路径
if (k == 2)
{
goodImageList.push_back(imagelist[i * 2]);
goodImageList.push_back(imagelist[i * 2 + 1]);
j++;
}
}
//如果有效图像对的数量小于2,则不够相机标定(2的原因是固定了部分参数)
cout << j << " pairs have been successfully detected.\n";
nimages = j;
if (nimages < 2)
{
cout << "Error: too little pairs to run the calibration\n";
return;
}
//imagePoints[k]的长度和objectPoints的长度必须一致
imagePoints[0].resize(nimages);
imagePoints[1].resize(nimages);
objectPoints.resize(nimages);
//objectPoints初始化,以左上角第一个角点为原点
for (i = 0; i < nimages; i++)
{
for (j = 0; j < boardSize.height; j++)
for (k = 0; k < boardSize.width; k++)
objectPoints[i].push_back(Point3f(k * squareSize, j * squareSize, 0));
}
//开始立体标定
cout << "Running stereo calibration ...\n";
//计算两个相机内参的初始值,畸变初始化为零。这里我们可以使用CalibrateCamera来计算初始内参、外参、畸变
Mat cameraMatrix[2], distCoeffs[2];
cameraMatrix[0] = initCameraMatrix2D(objectPoints, imagePoints[0], imageSize, 0);
cameraMatrix[1] = initCameraMatrix2D(objectPoints, imagePoints[1], imageSize, 0);
Mat R, T, E, F;
//立体标定
double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],
cameraMatrix[0], distCoeffs[0],
cameraMatrix[1], distCoeffs[1],
imageSize, R, T, E, F,
CALIB_FIX_ASPECT_RATIO +
CALIB_ZERO_TANGENT_DIST +
CALIB_USE_INTRINSIC_GUESS +
CALIB_SAME_FOCAL_LENGTH +
CALIB_RATIONAL_MODEL +
CALIB_FIX_K3 + CALIB_FIX_K4 + CALIB_FIX_K5,
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, 1e-5));
cout << "done with RMS error=" << rms << endl;
// save intrinsic parameters
FileStorage fs("intrinsics.yml", FileStorage::WRITE); //FileStorage是cv的XML/YAML/JSON 文件存储类,封装了向文件写入数据或从文件读取数据所需的所有信息。
if (fs.isOpened())
{
fs << "M1" << cameraMatrix[0] << "D1" << distCoeffs[0] <<
"M2" << cameraMatrix[1] << "D2" << distCoeffs[1];
fs.release();
}
else
cout << "Error: can not save the intrinsic parameters\n";
Mat R1, R2, P1, P2, Q;
Rect validRoi[2];
//极线校正
stereoRectify(cameraMatrix[0], distCoeffs[0],
cameraMatrix[1], distCoeffs[1],
imageSize, R, T, R1, R2, P1, P2, Q,
CALIB_ZERO_DISPARITY, 0, imageSize, &validRoi[0], &validRoi[1]);
fs.open("extrinsics.yml", FileStorage::WRITE);
if (fs.isOpened())
{
fs << "R" << R << "T" << T << "R1" << R1 << "R2" << R2 << "P1" << P1 << "P2" << P2 << "Q" << Q;
fs.release();
}
else
cout << "Error: can not save the extrinsic parameters\n";
// OpenCV can handle left-right
// or up-down camera arrangements
//Opencv能够自行判断是垂直摆放还是水平摆放,如何判断
bool isVerticalStereo = fabs(P2.at<double>(1, 3)) > fabs(P2.at<double>(0, 3));
// COMPUTE AND DISPLAY RECTIFICATION
if (!showRectified)
return;
Mat rmap[2][2];
//Precompute maps for cv::remap()计算去畸变和极线校正的映射表
initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);
initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);
Mat canvas;
double sf;
int w, h;
if (!isVerticalStereo) //水平摆放
{
sf = 600. / MAX(imageSize.width, imageSize.height);
w = cvRound(imageSize.width * sf); //cv的四舍五入
h = cvRound(imageSize.height * sf);
canvas.create(h, w * 2, CV_8UC3);
}
else //垂直摆放
{
sf = 300. / MAX(imageSize.width, imageSize.height);
w = cvRound(imageSize.width * sf);
h = cvRound(imageSize.height * sf);
canvas.create(h * 2, w, CV_8UC3);
}
for (i = 0; i < nimages; i++)
{
for (k = 0; k < 2; k++)
{
Mat img = imread(goodImageList[i * 2 + k], 0), rimg, cimg;
remap(img, rimg, rmap[k][0], rmap[k][1], INTER_LINEAR);
cvtColor(rimg, cimg, COLOR_GRAY2BGR);
Mat canvasPart = !isVerticalStereo ? canvas(Rect(w * k, 0, w, h)) : canvas(Rect(0, h * k, w, h));
resize(cimg, canvasPart, canvasPart.size(), 0, 0, INTER_AREA); //INTER_AREA不能用在二维图像的插值
Rect vroi(cvRound(validRoi[k].x * sf), cvRound(validRoi[k].y * sf),
cvRound(validRoi[k].width * sf), cvRound(validRoi[k].height * sf));
cout << vroi << endl;
rectangle(canvasPart, vroi, Scalar(0, 0, 255), 3, 8);
}
if (!isVerticalStereo)
for (j = 0; j < canvas.rows; j += 16)
line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);
else
for (j = 0; j < canvas.cols; j += 16)
line(canvas, Point(j, 0), Point(j, canvas.rows), Scalar(0, 255, 0), 1, 8);
imshow("rectified", canvas);
char c = (char)waitKey();
if (c == 27 || c == 'q' || c == 'Q')
break;
}
}
static bool readStringList(const string& filename, vector<string>& l)
{
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;
}
int main(int argc, char** argv)
{
string imagelistfn = "E:\\opencv\\opencv\\sources\\samples\\data\\stereo_calib.xml";
bool showRectified = true;
Size boardSize;
boardSize.width = 9;
boardSize.height = 6;
float squareSize = 1.0;
vector<string> imagelist;
bool ok = readStringList(imagelistfn, imagelist);
int index = imagelistfn.find_last_of("\\");
string dir = imagelistfn.substr(0, index);
for (auto& image_path : imagelist) {
image_path = dir + "\\" + image_path;
}
for (const auto& image_path : imagelist) {
cout << image_path << endl;
}
StereoCalib(imagelist, boardSize, squareSize, true, showRectified);
return 0;
}
下面直接放出一组极线校正的图像对,从图中可以看出,极线是水平对齐的。