张正友相机标定流程(程序)

发布时间 2023-07-18 22:57:51作者: 小凉拖

写在前面

OpenCV存储数据的方式:

比如所有图像的角点坐标我可以定义一个对象:

InputArrayOfArrays _imagePoints

InputArray这个接口类可以是Mat、Mat_<T>、Mat_<T, m, n>、vector<T>、vector<vector<T>>、vector<Mat>

这里面以vector<vector<T>>为例:最外面的vector的每个元素(也就是里面的vector)代表一张图像,里面的vector的每个元素代表一个角点坐标,如果是Mat的话还可以求row(行)为1,col(列)为图像的数量

存储世界坐标

 1 /****************************************************************************************************************************
 2 函数原型:
 3        static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
 4 函数功能:
 5        将相应棋盘格所有可能的世界坐标存储在容器中
 6 函数参数:
 7        1---Size boardSize-------------棋盘格的尺寸Size
 8        2---float squareSize-----------棋盘格角点之间的距离Size
 9        3---vector<Point3f>& corners---用来存储棋盘格角点的三维坐标
10        4---Pattern patternType--------标定板的类型
11 函数返回值:
12        void
13 ****************************************************************************************************************************/
14 
15 static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
16 {
17     corners.resize(0);
18 
19     switch (patternType)
20     {
21     case CHESSBOARD:
22     case CIRCLES_GRID:
23         for (int i = 0; i < boardSize.height; i++)
24             for (int j = 0; j < boardSize.width; j++)
25                 //typedef Point3_<float> Point3f;
26                 //该函数通过Point3_<float>的有参构造函数实例化一个Point3_<float>对象,也就是创建一个3D点
27                 //猜想对图像棋盘格角点检测后如果认为右下角的内点点为世界坐标系下原点
28                 //那么就将corners[0]的值认为是它的世界坐标的值
29                 //因此这个函数只是在corners这个容器中存储了所有可能的世界坐标,并不考虑是棋盘格哪个点
30                 corners.push_back(Point3f(float(j * squareSize),
31                     float(i * squareSize), 0));
32         break;
33 
34     case ASYMMETRIC_CIRCLES_GRID:
35         for (int i = 0; i < boardSize.height; i++)
36             for (int j = 0; j < boardSize.width; j++)
37                 corners.push_back(Point3f(float((2 * j + i % 2) * squareSize),
38                     float(i * squareSize), 0));
39         break;
40 
41     default:
42         CV_Error(Error::StsBadArg, "Unknown pattern type\n");
43     }
44 }

世界坐标的具体实现程序:

/*************************************************
总体实现逻辑是:由于拍摄时棋盘格始终以棋盘格左下角为世界坐标原点,
因此无论怎么变换棋盘格或则是变换相机,棋盘格的所有点的世界坐标都不会变,
变的只是棋盘格在图像上的位置,也就是像素坐标系,
因此每张图像的棋盘格角点的世界坐标都是一样的,
这里为了和图像一一对应就将棋盘格的所有世界坐标都储存下来(它们是一样的)
定义一个容器的容器,最外层的容器的每个元素是一个存储一张棋盘格的图像的所有角点世界坐标的容器,
这相当于构成了一个N*(H*W)尺度的矩阵,N是图像的总数,H是棋盘格内角点的纵向个数,W是内角点的横向个数 *************************************************
*/ vector<vector<Point3f> > objectPoints(1); //该函数得到了一张图像上角点的世界坐标 calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType); objectPoints[0][boardSize.width - 1].x = objectPoints[0][0].x + grid_width; newObjPoints = objectPoints[0]; //imagePoints.size()是图像的个数,imagePoints一个容器的容器,
最外层的容器的每个元素是一个存储一张棋盘格的图像的所有角点的像素坐标的容器,
由于每张图像的世界坐标都一样,因为将objectPoints.resize变成和imagePoints相同的尺度,代表着图像数目,
objectPoints多扩展的空间每个元素都赋予相同的世界坐标
objectPoints.resize(imagePoints.size(), objectPoints[0]);