1// CalibCamera 标定相机
 2func CalibCamera(imgDatas [][]byte, chessboardRowCornerCount int, chessboardColCornerCount int, curImageIndex int) error {
 3	// 保存各图像中的角点的二维坐标, 即像素坐标
 4	imgPoints := gocv.NewPoints2fVector()
 5	defer imgPoints.Close()
 6	// 保存各图像中的角点的三维坐标,即世界坐标
 7	objPoints := gocv.NewPoints3fVector()
 8	defer objPoints.Close()
 9	// 保存图片尺寸
10	imgSize := image.Point{X: 0, Y: 0}
11
12	// 为三维点定义世界坐标系
13	objectPoints := make([]gocv.Point3f, 0)
14	for i := 0; i < chessboardColCornerCount; i++ {
15		for j := 0; j < chessboardRowCornerCount; j++ {
16			objectPoints = append(objectPoints, gocv.Point3f{X: float32(j), Y: float32(i), Z: 0})
17		}
18	}
19	objPointsVector := gocv.NewPoint3fVectorFromPoints(objectPoints)
20	defer objPointsVector.Close()
21
22	// 1. 检测角点
23	for index, imgData := range imgDatas {
24		err := func() error {
25			// 读取为灰度图
26			grayImg, err := gocv.IMDecode(imgData, gocv.IMReadGrayScale)
27			if err != nil {
28				return err
29			}
30			if grayImg.Empty() {
31				return nil
32			}
33			defer grayImg.Close()
34			if index == 0 {
35				imgSize = image.Point{X: grayImg.Cols(), Y: grayImg.Rows()}
36			}
37			// 保存图像二维角点信息
38			corners := gocv.NewMat()
39			defer corners.Close()
40			// 如果在图像中找到所需数量的角点,返回true
41			if found := gocv.FindChessboardCorners(grayImg, image.Pt(chessboardRowCornerCount, chessboardColCornerCount), &corners, gocv.CalibCBAdaptiveThresh|gocv.CalibCBFastCheck|gocv.CalibCBNormalizeImage); !found {
42				return errors.New("Not Found Corner Points")
43			}
44			// 迭代算法中的终止条件(终止条件的类型、最大迭代次数、期望精度)
45			criteria := gocv.NewTermCriteria(gocv.EPS|gocv.MaxIter, 30, 0.01)
46			// 进一步提取亚像素角点,提高精度
47			gocv.CornerSubPix(grayImg, &corners, image.Pt(11, 11), image.Pt(-1, -1), criteria)
48			if corners.Cols()*corners.Rows() != chessboardColCornerCount*chessboardRowCornerCount {
49				return errors.Newr("Not Matched Number Of Corner Points")
50			}
51			imagePoints := make([]gocv.Point2f, 0)
52			// 注意corners矩阵只有一列,例如9*6的角点实际上是一个54*1的矩阵,因此不能在这里一起添加世界坐标
53			for i := 0; i < corners.Rows(); i++ {
54				for j := 0; j < corners.Cols(); j++ {
55					pixelX, pixelY := corners.GetFloatAt(i, j*2), corners.GetFloatAt(i, j*2+1)
56					imagePoints = append(imagePoints, gocv.Point2f{X: pixelX, Y: pixelY})
57				}
58			}
59			// 添加像素坐标
60			imgPointsVector := gocv.NewPoint2fVectorFromPoints(imagePoints)
61			imgPoints.Append(imgPointsVector)
62			imgPointsVector.Close()
63			// 添加世界坐标
64			objPoints.Append(objPointsVector)
65			return nil
66		}()
67		if err != nil {
68			return err
69		}
70	}
71
72	// 2. 标定
73	// 每幅图像的平移向量
74	transMat := gocv.NewMat()
75	defer transMat.Close()
76	// 每幅图像的旋转向量
77	rotMat := gocv.NewMat()
78	defer rotMat.Close()
79	// 相机内参矩阵 3*3
80	cameraMatrix := gocv.NewMatWithSize(3, 3, gocv.MatTypeCV32F)
81	defer cameraMatrix.Close()
82	// 相机的5个畸变系数:k1,k2,p1,p2,k3 1*5
83	distCoeffs := gocv.NewMat()
84	defer distCoeffs.Close()
85	res := gocv.CalibrateCamera(objPoints, imgPoints, imgSize, &cameraMatrix, &distCoeffs, &rotMat, &transMat, gocv.CalibFlag(0))
86
87	// 3. 保存标定结果
88	// 获取校正后的新相机矩阵
89	newCameramtx, _ := gocv.GetOptimalNewCameraMatrixWithParams(cameraMatrix, distCoeffs, imgSize, 1, imgSize, false)
90	defer newCameramtx.Close()
91}

Inspired by

[1] 相机畸变与标定