相機標定和校正
阿新 • • 發佈:2018-12-12
1.標定:
首先要製作一個標定板,OpenCV包裡有一個黑白棋盤標定板,直接A4紙列印就OK了。然後用需要標定的攝像頭拍幾張照片(注意:要從不同角度多拍幾張,我用了22張。標定板上的點個的個數為54,一行9個,一列6個),首先就是找亞畫素角點findChessboardCorners,看看是否是54個點。然後進一步提取亞畫素角點cornerSubPix,儲存角點。在標定前需要先測量標定板上一塊格子的大小,單位為mm,我量的是26mm。對於標定結果,我們只關心2個量,一個是相機的內參矩陣(3*3的矩陣),另一個是畸變引數(1*5的矩陣),我只儲存這兩個量。然後算標定板上角點在世界座標系下的座標,為方便計算,假設標定板就在XOY平面上,即Z=0,也就是直接在原有座標上*26就可以了。最後呼叫calibrateCamera進行標定。
void main()//標定 { Size board_size = Size(9, 6); /**** 定標板上每行、列的角點數 ****/ vector<Point2f> corners; /**** 快取每幅影象上檢測到的角點 ****/ vector<vector<Point2f>> corners_Seq; /**** 儲存檢測到的所有角點 ****/ vector<Mat> image_Seq; int count = 0; int img_count = 0; string imgname; ifstream fin("C:/Users/Administrator/Desktop/cam_pic/pic.txt"); Mat src; while (getline(fin, imgname)) { img_count++; imgname = "C:/Users/Administrator/Desktop/cam_pic/" + imgname; src = imread(imgname); Mat img_gray; cvtColor(src, img_gray, CV_BGR2GRAY); bool patternfound = findChessboardCorners(src, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK); if (!patternfound) { cout << "not found" << endl; break; } else { cornerSubPix(img_gray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.01)); Mat img_temp = src.clone(); for (int i = 0; i < corners.size(); ++i) { circle(img_temp, corners[i], 2, Scalar(0, 0, 255), 2, 8, 0); } /*imshow("img_temp", img_temp); waitKey(0);*/ count += corners.size(); corners_Seq.push_back(corners); } } Size square_size = Size(26, 26);// vector<vector<Point3f>> object_Points; //Mat img_points = Mat(1, count, CV_32FC2, Scalar::all(0));//// //vector<int> point_counts;///// Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); vector<cv::Mat> rotation_vectors; vector<cv::Mat> translation_vectors; for (int n = 0; n < img_count; ++n) { vector<Point3f> tempPointSet_1; for (int i = 0; i < board_size.height; i++) { for (int j = 0; j < board_size.width; j++) { Point3f tempPoint; tempPoint.x = i*square_size.width; tempPoint.y = j*square_size.height; tempPoint.z = 0; tempPointSet_1.push_back(tempPoint); } } object_Points.push_back(tempPointSet_1); } calibrateCamera(object_Points, corners_Seq, src.size(), intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, 0); double total_err = 0.0; /* 所有影象的平均誤差的總和 */ double err = 0.0; /* 每幅影象的平均誤差 */ vector<Point2f> image_points2; for (int n = 0; n < img_count; ++n) { vector<Point3f> tempPointSet = object_Points[n]; /**** 通過得到的攝像機內外引數,對空間的三維點進行重新投影計算,得到新的投影點 ****/ projectPoints(tempPointSet, rotation_vectors[n], translation_vectors[n], intrinsic_matrix, distortion_coeffs, image_points2); /* 計算新的投影點和舊的投影點之間的誤差*/ vector<Point2f> tempImagePoint = corners_Seq[n]; Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2); Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2); for (size_t i = 0; i != tempImagePoint.size(); i++) { image_points2Mat.at<Vec2f>(0, i) = Vec2f(image_points2[i].x, image_points2[i].y); tempImagePointMat.at<Vec2f>(0, i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y); } err = norm(image_points2Mat, tempImagePointMat, NORM_L2); total_err += err /= 54;//point_counts[0] cout << "第" << n + 1 << "幅影象的平均誤差:" << err << "畫素" << endl; } cout << "總體平均誤差:" << total_err /img_count<< "畫素" << endl << endl; FileStorage fs("calibration.xml", FileStorage::WRITE); fs << "intrinsic_matrix" << intrinsic_matrix; fs << "distortion_coeffs" << distortion_coeffs; fs.release(); waitKey(0); }
2.校正:
校正就是直接讀取標定儲存的檔案,呼叫initUndistortRectifyMap和remap進行校正即可。
void main()//校正 { FileStorage fs; fs.open("calibration.xml", FileStorage::READ); Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); fs["intrinsic_matrix"] >> intrinsic_matrix; fs["distortion_coeffs"] >> distortion_coeffs; Mat src = imread("C:/Users/Administrator/Desktop/cam_pic/6.jpg"); imshow("src", src); Mat mapx = Mat(src.size(), CV_32FC1); Mat mapy = Mat(src.size(), CV_32FC1); Mat R = Mat::eye(3, 3, CV_32F); initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, src.size(), 1, src.size(), 0), src.size(), CV_32FC1, mapx, mapy); Mat dst = src.clone(); remap(src, dst, mapx, mapy, INTER_LINEAR); imshow("dst",dst); waitKey(0); }