相机标定实战--圆点标定板标定代码
相机标定原理可查看张正友相机标定的论文,圆点检测原理可查看opencv的相关文档。标定板使用白底黑圆点标定板。(欢迎进Q群交流:874653199)cpp文件circleGridCalibration.cpp。头文件circleGridCalibration.h。测试demo main.cpp。转载请声明来源,免费内容请勿收费。
·
相机标定原理可查看张正友相机标定的论文,圆点检测原理可查看opencv的相关文档。标定板使用白底黑圆点标定板。(欢迎进Q群交流:874653199)
头文件circleGridCalibration.h
#pragma once
#include<iostream>
#include<opencv2\core\core.hpp>
namespace circleGridCalibrate{
//针孔模型-单相机标定
void monoCalibratePinhole(std::vector<cv::Mat> srcImage, cv::Size boardSize,double centerDistance);
//针孔模型-双相机标定
void stereoCalibratePinhole(std::vector<cv::Mat> leftImage, std::vector<cv::Mat> rightImage, cv::Size boardSize, double centerDistance);
}
cpp文件circleGridCalibration.cpp
#include "circleGridCalibration.h"
#include<opencv2\calib3d.hpp>
#include<opencv2\highgui.hpp>
#include<opencv2\imgproc.hpp>
#include <direct.h>
#include<fstream>
#pragma warning(disable:4996)
namespace circleGridCalibrate{
void monoCalibratePinhole(std::vector<cv::Mat> srcImage,cv::Size boardSize, double centerDistance);
void stereoCalibratePinhole(std::vector<cv::Mat> leftImage, std::vector<cv::Mat> rightImage,cv::Size boardSize, double centerDistance);
}
//针孔模型-计算反投影误差
static double computeReprojectionErrors(const std::vector<std::vector<cv::Point3f> >& objectPoints, const std::vector<std::vector<cv::Point2f> >& imagePoints, const std::vector<cv::Mat>& rvecs, const std::vector<cv::Mat>& tvecs,
const cv::Mat& cameraMatrix, const cv::Mat& distCoeffs, std::vector<float>& perViewErrors)
{
std::vector<cv::Point2f> imagePoints2;
int i, totalPoints = 0;
double totalErr = 0, err;
perViewErrors.resize(objectPoints.size());
for (i = 0; i < (int)objectPoints.size(); i++)
{
cv::projectPoints(objectPoints[i], rvecs[i], tvecs[i],
cameraMatrix, distCoeffs, imagePoints2);
err = norm(cv::Mat(imagePoints[i]), cv::Mat(imagePoints2), cv::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);
}
//小孔成像-单相机标定
void circleGridCalibrate::monoCalibratePinhole(std::vector<cv::Mat> srcImage, cv::Size boardSize, double centerDistance){
int numbers = srcImage.size();
std::vector<cv::Mat>grayImage(numbers);
for (int i = 0; i < numbers; i++) {
if (srcImage[i].channels() == 1) {
grayImage[i] = srcImage[i];
}
else {
cv::cvtColor(srcImage[i], grayImage[i],cv::COLOR_RGB2GRAY);
}
}
cv::Size imageSize = srcImage[0].size();
std::vector<std::vector<cv::Point2f>>totalImagePoint;
std::vector<cv::Point2f>singleImagePoint;
for (int i = 0; i < numbers; i++){
/*
CALIB_CB_SYMMETRIC_GRID 使用圆形的对称图案。
CALIB_CB_ASYMMETRIC_GRID 使用非对称圆形图案。
CALIB_CB_CLUSTERING 使用特殊的算法进行网格检测。它对透视失真更鲁棒,但对背景杂波更加敏感。
*/
bool patternFound = findCirclesGrid(grayImage[i], boardSize, singleImagePoint, cv::CALIB_CB_SYMMETRIC_GRID || cv::CALIB_CB_CLUSTERING);
if (patternFound){
std::cout << i + 1 << ":" << "succeed" << std::endl;
//角点绘制
drawChessboardCorners(srcImage[i], boardSize, singleImagePoint, patternFound);
totalImagePoint.push_back(singleImagePoint);
singleImagePoint.clear();
cv::imshow("circle grid", srcImage[i]);
cv::waitKey(500);
}
else{
std::cout << i + 1 << ":" << "failed" << std::endl;
}
}
cv::Point3f TobjectPoint;
int numbers1= totalImagePoint.size();
std::vector<std::vector<cv::Point3f>>totalObjectPoint;
std::vector<cv::Point3f>singleObjectPoint;
for (int i = 0; i < numbers1; i++){
for (int m = 0; m < boardSize.height; m++){//11
for (int n = 0; n < boardSize.width; n++){//12
TobjectPoint.x = n*centerDistance;
TobjectPoint.y = m*centerDistance;
TobjectPoint.z = 0;
singleObjectPoint.push_back(TobjectPoint);
}
}
totalObjectPoint.push_back(singleObjectPoint);
singleObjectPoint.clear();
}
cv::Mat cameraMatrix, distCoeffs;
std::vector<cv::Mat>rvecs, tvecs;
calibrateCamera(totalObjectPoint, totalImagePoint, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, 0, cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, DBL_EPSILON));//
std::vector<float>perViewErrors;
double err = computeReprojectionErrors(totalObjectPoint, totalImagePoint, rvecs, tvecs, cameraMatrix, distCoeffs, perViewErrors);
std::fstream result;
result.open("monoPinholeResult.txt", std::fstream::out);
result << "相机内参数:" << cameraMatrix << std::endl;
result << "畸变系数:" << distCoeffs << std::endl;
result << "反投影误差均值:" << err << std::endl;
for (int i = 0; i <numbers1; i++){
cv::Mat R;
cv::Rodrigues(rvecs[i], R);
result<< " 第" << i + 1 << "幅反投影误差:" << perViewErrors[i] << std::endl;
result << " 第" << i + 1 << "幅旋转矩阵:" << R << std::endl;//旋转矩阵为相机坐标系至世界坐标系
result << " 第" << i + 1 << "幅平移矩阵:" << tvecs[i]<< std::endl;//平移矩阵为相机坐标系至世界坐标系
}
}
//小孔成像-双相机标定
void circleGridCalibrate::stereoCalibratePinhole(std::vector<cv::Mat> leftImage, std::vector<cv::Mat> rightImage, cv::Size boardSize, double centerDistance){//这里没调用单目标定函数是由于左右检测出的图像可能不一致
int numbers = leftImage.size();
std::vector<cv::Mat>leftGrayImage(numbers);
std::vector<cv::Mat>rightGrayImage(numbers);
for (int i = 0; i < numbers; i++){
if (leftImage[i].channels() == 1) {
leftGrayImage[i] = leftImage[i];
rightGrayImage[i] = rightImage[i];
}
else {
cvtColor(leftImage[i], leftGrayImage[i], cv::COLOR_RGB2GRAY);
cvtColor(rightImage[i], rightGrayImage[i], cv::COLOR_RGB2GRAY);
}
}
cv::Size imageSize = leftImage[0].size();
std::vector<std::vector<cv::Point2f>>leftTotalImagePoint;
std::vector<std::vector<cv::Point2f>>rightTotalImagePoint;
std::vector<cv::Point2f>leftSingleImagePoint;
std::vector<cv::Point2f>rightSingleImagePoint;
for (int i = 0; i < numbers; i++)
{
/*
CALIB_CB_SYMMETRIC_GRID 使用圆形的对称图案。
CALIB_CB_ASYMMETRIC_GRID 使用非对称圆形图案。
CALIB_CB_CLUSTERING 使用特殊的算法进行网格检测。它对透视失真更鲁棒,但对背景杂波更加敏感。
*/
bool leftPatternFound = findCirclesGrid(leftGrayImage[i], boardSize, leftSingleImagePoint, cv::CALIB_CB_SYMMETRIC_GRID);
bool rightPatternFound = findCirclesGrid(rightGrayImage[i], boardSize, rightSingleImagePoint,cv::CALIB_CB_SYMMETRIC_GRID);
if (leftPatternFound && rightPatternFound){
std::cout << i + 1 << ":" << "succeed" << std::endl;
//角点绘制
drawChessboardCorners(leftImage[i], boardSize, leftSingleImagePoint, leftPatternFound);
drawChessboardCorners(rightImage[i], boardSize, rightSingleImagePoint, rightPatternFound);
leftTotalImagePoint.push_back(leftSingleImagePoint);
rightTotalImagePoint.push_back(rightSingleImagePoint);
leftSingleImagePoint.clear();
rightSingleImagePoint.clear();
cv::imshow("Left", leftImage[i]);
cv::imshow("Right", rightImage[i]);
cv::waitKey(500);
}
else{
std::cout << i + 1 << ":" << "failed" << std::endl;
}
}
cv::Point3f TobjectPoint;
int numbers1 = leftTotalImagePoint.size();
std::vector<std::vector<cv::Point3f>>totalObjectPoint;
std::vector<cv::Point3f>singleObjectPoint;
for (int i = 0; i < numbers1; i++){
for (int m = 0; m < boardSize.height; m++){
for (int n = 0; n < boardSize.width; n++){
TobjectPoint.x = n*centerDistance;
TobjectPoint.y = m*centerDistance;
TobjectPoint.z = 0;
singleObjectPoint.push_back(TobjectPoint);
}
}
totalObjectPoint.push_back(singleObjectPoint);
singleObjectPoint.clear();
}
cv::Mat leftCamera, leftDisCoeffs, rightCamera, rightDisCoeffs;
std::vector<cv::Mat>leftRvecs, leftTvecs, rightRvecs, rightTvecs;
std::vector<float>leftPerViewErrors, rightPerViewErrors;
calibrateCamera(totalObjectPoint, leftTotalImagePoint, imageSize, leftCamera, leftDisCoeffs, leftRvecs, leftTvecs, 0, cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, DBL_EPSILON));//
double leftErr = computeReprojectionErrors(totalObjectPoint, leftTotalImagePoint, leftRvecs, leftTvecs, leftCamera, leftDisCoeffs, leftPerViewErrors);
calibrateCamera(totalObjectPoint, rightTotalImagePoint, imageSize, rightCamera, rightDisCoeffs, rightRvecs, rightTvecs, 0, cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, DBL_EPSILON));//
double rightErr = computeReprojectionErrors(totalObjectPoint, rightTotalImagePoint, rightRvecs, rightTvecs, rightCamera, rightDisCoeffs, rightPerViewErrors);
cv::Mat R, T, E, F;
double err = stereoCalibrate(totalObjectPoint, leftTotalImagePoint, rightTotalImagePoint, leftCamera, leftDisCoeffs, rightCamera, rightDisCoeffs, imageSize, R,
T, E, F, cv::CALIB_FIX_INTRINSIC , cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, 1e-6));//|| CV_CALIB_USE_INTRINSIC_GUESS
std::fstream result;
result.open("stereoPinholeResult.txt", std::fstream::out);
result << "左相机内参数:" << leftCamera << std::endl;
result << "左相机畸变系数:" << leftDisCoeffs << std::endl;
result << "左相机反投影误差:" << leftErr << std::endl;
for (int i = 0; i < numbers1; i++){
cv::Mat LR;
cv::Rodrigues(leftRvecs[i], LR);
result << " 第" << i + 1 << "幅反投影误差:" << leftPerViewErrors[i] << std::endl;
result << " 第" << i + 1 << "幅旋转矩阵:" << LR << std::endl;//旋转矩阵为相机坐标系至世界坐标系
result << " 第" << i + 1 << "幅平移矩阵:" << leftTvecs[i] << std::endl;//平移矩阵为相机坐标系至世界坐标系
}
result << "右相机内参数:" << rightCamera << std::endl;
result << "右相机畸变系数:" << rightDisCoeffs << std::endl;
result << "右相机反投影误差:" << rightErr << std::endl;
for (int i = 0; i < numbers1; i++){
cv::Mat RR;
cv::Rodrigues(rightRvecs[i], RR);
result << " 第" << i + 1 << "幅反投影误差:" << rightPerViewErrors[i] << std::endl;
result << " 第" << i + 1 << "幅旋转矩阵:" << RR << std::endl;//旋转矩阵为相机坐标系至世界坐标系
result << " 第" << i + 1 << "幅平移矩阵:" << rightTvecs[i] << std::endl;//平移矩阵为相机坐标系至世界坐标系
}
result << "双目标定误差:" << err << std::endl;
result << "右相机至左相机的旋转矩阵:" << R << std::endl;
result << "右相机至左相机的平移矩阵:" << T << std::endl;
result << "本质矩阵:" << E << std::endl;
result << "基础矩阵:" << F << std::endl;
}
测试demo main.cpp
#include"circleGridCalibration.h"
#include<opencv2\highgui.hpp>
int main(){
std::string path = "./Data/data7/%d.bmp";//这里输入图片所在路径
int numbers = 11;//这里输入图片数量
char filename1[200];
char filename2[200];
std::strcpy(filename1, path.c_str());
std::vector<cv::Mat>srcImage(numbers);
for (int i = 1; i <= numbers; i++) {
sprintf(filename2, filename1, i);
srcImage[i - 1] = cv::imread(filename2);
}
cv::Size boardSize(14, 12);//这里输入每行每列圆圈的圆点个数
double circleDistance = 50;//圆心之间的距离
circleGridCalibrate::monoCalibratePinhole(srcImage, boardSize, circleDistance);
return 0;
}
圆心检测结果及标定结果:
图1 圆心检测结果
图2 标定结果
转载请声明来源,免费内容请勿收费。
更多推荐
所有评论(0)