一、棋盘格标定
原理如下
Halcon 像机标定原理推导_halcon棋盘格标定-
void CameraCalibration(std::string imagepath, int Chessboard_Width, Size& board_size, std::string imagetype, bool flag, int image_count)
{
/************************************************************************
从摄像机中读取多幅图像,从中提取出角点,然后对角点进行亚像素精确化
*************************************************************************/
cv::Mat image;
Size image_size; // 图像的尺寸
vector<Point2f> corners; // 缓存每幅图像上检测到的角点
vector<vector<Point2f>> corners_Seq; // 保存检测到的所有角点
ofstream fout(imagepath + "\\carlbration\\calibration_result.txt"); // 保存定标结果的文件
int count = 0, n = 0;
stringstream tempname;
string filename;
int key;
string msg;
int baseLine;
Size textSize;
while (n < image_count)
{
std::string path = imagepath + to_string(n + 1) + "." + imagetype;
const char* pathc = path.c_str();
//从文件中读入图像
image = cv::imread(path, -1);
//如果读入图像失败
if (image.empty())
{
std::cout << "Can not load image" << "\n";
return;
}
//显示图像
image_size = image.size();
/* 提取角点 */
Mat imageGray;
cv::cvtColor(image, imageGray, CV_RGB2GRAY);
bool found;
if (flag == true)
{
found = findChessboardCornersSB(image, board_size, corners, CALIB_CB_EXHAUSTIVE | CALIB_CB_ACCURACY);
if (found)
{
corners_Seq.push_back(corners);
drawChessboardCorners(image, board_size, Mat(corners), found);
std::string filename = imagepath + "\\carlbration\\" + to_string(n + 1) + "." + imagetype;
imwrite(filename, image);
n++;
}
}
else
{
found = findChessboardCorners(image, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK);
if (found)
{
n++;
tempname << n;
tempname >> filename;
std::string filename = "\\carlbration\\" + to_string(n + 1) + "." + imagetype;
/* 亚像素精确化 */
cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
count += corners.size();
corners_Seq.push_back(corners);
drawChessboardCorners(image, board_size, Mat(corners), found);
imwrite(filename, image);
tempname.clear();
filename.clear();
}
}
}
std::cout << "角点提取完成!\n";
/************************************************************************
摄像机定标
*************************************************************************/
std::cout << " 摄像机定标 ---------------------------" << endl;
Size square_size = Size(Chessboard_Width, Chessboard_Width); // 实际测量得到的定标板上每个棋盘格的高 宽25mm
vector<vector<Point3f>> world_object_Points; // 保存定标板上物理角点的三维坐标
Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); // 保存提取的所有角点
vector<int> point_counts; // 每幅图像中角点的数量
Mat CameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 摄像机内参数矩阵
Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); //摄像机的5个畸变系数:k1,k2,p1,p2,k3
vector<Mat> rotation_vectors; // 每幅图像的旋转向量
vector<Mat> translation_vectors; // 每幅图像的平移向量
/* 初始化定标板上角点的三维坐标 */
for (int t = 0; t < image_count; t++)
{
vector<Point3f> tempPointSet;
for (int i = 0; i < board_size.height; i++)
{
for (int j = 0; j < board_size.width; j++)
{
/* 假设定标板放在世界坐标系中z=0的平面上 */
Point3f tempPoint;
tempPoint.x = i * square_size.width;
tempPoint.y = j * square_size.height;
tempPoint.z = 0;
tempPointSet.push_back(tempPoint);
}
}
world_object_Points.push_back(tempPointSet);
}
/* 初始化每幅图像中的角点数,这里我们假设每幅图像中都可以看到完整的定标板 */
for (int i = 0; i < image_count; i++)
{
point_counts.push_back(board_size.width * board_size.height);
}
/* 开始定标 */
calibrateCamera(world_object_Points, corners_Seq, image_size, CameraMatrix, distortion_coeffs, rotation_vectors, translation_vectors);
std::cout << "打印出标定参数\n";
cout << "CameraMatrix:" << endl;
cout << CameraMatrix << endl;
cout << "*****************************" << endl;
cout << "distCoeffs:" << endl;
cout << distortion_coeffs << endl;
cout << "*****************************" << endl;
std::cout << "=========================================================\n" << endl;
std::cout << "定标完成!\n";
/************************************************************************
对定标结果进行评价
*************************************************************************/
std::cout << "开始评价定标结果………………" << endl;
double total_err = 0.0; // 所有图像的平均误差的总和
double err = 0.0; // 每幅图像的平均误差
vector<Point2f> new_image_points; // 保存重新计算得到的投影点
std::cout << "每幅图像的定标误差:" << endl;
fout << "每幅图像的定标误差:" << endl << endl;
for (int i = 0; i < image_count; i++)
{
vector<Point3f> tempPointSet = world_object_Points[i];
// 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点
projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], CameraMatrix, distortion_coeffs, new_image_points);
// 计算新的投影点和旧的投影点之间的误差
vector<Point2f> singleImagePoint = corners_Seq[i];
Mat tempImagePointMat = Mat(1, singleImagePoint.size(), CV_32FC2);
Mat image_points2Mat = Mat(1, new_image_points.size(), CV_32FC2);
for (int j = 0; j < singleImagePoint.size(); j++)
{
image_points2Mat.at<Vec2f>(0, j) = Vec2f(new_image_points[j].x, new_image_points[j].y);
tempImagePointMat.at<Vec2f>(0, j) = Vec2f(singleImagePoint[j].x, singleImagePoint[j].y);
}
err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
total_err += err /= point_counts[i];
std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
}
std::cout << "总体平均误差:" << total_err / image_count << "像素" << endl;
fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;
std::cout << "评价完成!" << endl;
/************************************************************************
保存定标结果
*************************************************************************/
std::cout << "开始保存定标结果………………" << endl;
Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 保存每幅图像的旋转矩阵
fout << "相机内参数矩阵:" << endl;
fout << CameraMatrix << endl << endl;
fout << "畸变系数:\n";
fout << distortion_coeffs << endl << endl << endl;
for (int i = 0; i < image_count; i++)
{
fout << "第" << i + 1 << " 幅图像的旋转向量:" << endl;
fout << rotation_vectors[i] << endl;
// 将旋转向量转换为相对应的旋转矩阵
Rodrigues(rotation_vectors[i], rotation_matrix);
fout << "第" << i + 1 << " 幅图像的旋转矩阵:" << endl;
fout << rotation_matrix << endl;
fout << "第" << i + 1 << " 幅图像的平移向量:" << endl;
fout << translation_vectors[i] << endl << endl;
}
std::cout << "完成保存" << endl;
fout << endl;
// 矫正图像
std::string origin_image_path= "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\30.png";
Mat origion_image = imread(origin_image_path);
Mat image_rectify, image_rectify_2;
// 矫正方法1 undistort
undistort(origion_image, image_rectify, CameraMatrix, distortion_coeffs);
imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify.png", image_rectify);
// 矫正方法2 initUndistortRectifyMap 和 remap 方法
Mat map1, map2;
Size image_Size= origion_image.size();
initUndistortRectifyMap(CameraMatrix, distortion_coeffs, Mat(), CameraMatrix, image_Size, CV_16SC2, map1, map2);
remap(image, image_rectify_2, map1, map2, INTER_LINEAR);
imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify_2.png", image_rectify_2);
}
matlab 标定结果与opencv标定
矫正图像
// 矫正图像
std::string origion_image_path= "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\30.png";
Mat origion_image = imread(origion_image_path);
Mat image_rectify, image_rectify_2;
// 矫正方法1 undistort
undistort(origion_image, image_rectify, intrinsic_matrix, distortion_coeffs);
imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify.png", image_rectify);
// 矫正方法2 initUndistortRectifyMap 和 remap 方法
Mat map1, map2;
Size image_Size= origion_image.size();
initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, Mat(), intrinsic_matrix, image_Size, CV_16SC2, map1, map2);
remap(frame, image_rectify_2, map1, map2, INTER_LINEAR);
imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify_2.png", image_rectify_2);
方法 undistort
initUndistortRectifyMap 和 remap 方法
同一视角看
二、圆标定
我用的标定的图像是halcon的图像
void CameraCalibrationCircle(std::string imagepath, float circle_diameter, float distance_two_circles, int circleType, Size& board_size, std::string imagetype, int image_count)
{
cv::Mat image;
Size image_size; // 图像的尺寸
vector<Point2f> corners; // 缓存每幅图像上检测到的角点
vector<vector<Point2f>> corners_Seq; // 保存检测到的所有角点
ofstream fout(imagepath + "\\carlbration\\calibration_result.txt"); // 保存定标结果的文件
int count = 0, n = 0;
stringstream tempname;
string filename;
int key;
string msg;
int baseLine;
while (n < image_count)
{
std::string path = imagepath + "calib-3d-coord-0" + to_string(n + 1) + "." + imagetype;
// cout << path << endl;
const char* pathc = path.c_str();
//从文件中读入图像
image = cv::imread(path, -1);
//如果读入图像失败
if (image.empty())
{
std::cout << "Can not load image" << "\n";
return;
}
//显示图像
image_size = image.size();
/* 提取角点 */
//Mat imageGray;
//cv::cvtColor(image, imageGray, CV_RGB2GRAY);
bool found;
// cout << "+++++++++++++++++++++" << endl;
if (circleType == 1)
{
// 图像 标定板的点
found = findCirclesGrid(image, board_size, corners, CALIB_CB_SYMMETRIC_GRID);
}
else
{
found = findCirclesGrid(image, board_size, corners, CALIB_CB_ASYMMETRIC_GRID);
}
if (found)
{
drawChessboardCorners(image, board_size, Mat(corners), found);
std::string filename2 = imagepath + "carlib_images\\calib-3d-coord-0" + to_string(n + 1) + "_calib." + imagetype;
imwrite(filename2, image);
count += corners.size();
corners_Seq.push_back(corners);
}
n++;
}
std::cout << "角点提取完成!\n";
// 开始 计算
cout << "------------开始标定------------------- " << count << endl;
vector<vector<Point3f>> word_calib_Points;// 标定板中在世界坐标系下的点坐标
Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); // 保存提取的所有角点
vector<int> point_counts; // 每幅图像中角点的数量
Mat CamearMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 摄像机内参数矩阵
Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); // 摄像机的5个畸变系数:k1,k2,p1,p2,k3
vector<Mat> rotation_vectors; // 每幅图像的旋转向量
vector<Mat> translation_vectors; // 每幅图像的平移向量
// 遍历每个图像
for (size_t num = 0; num < image_count; num++)
{
vector<Point3f> calib_Points;
// 每张图像
for (size_t i = 0; i < board_size.height; i++)
{
for (size_t j = 0; j < board_size.width; j++)
{
//
Point3f point;
point.x = i * distance_two_circles;
point.y = j * distance_two_circles;
point.z = 0;
//cout << point << endl;
calib_Points.push_back(point);
}
//cout << endl;
}
// cout << "===================================" << endl;
word_calib_Points.push_back(calib_Points);
}
/* 初始化每幅图像中的角点数,这里我们假设每幅图像中都可以看到完整的定标板 */
for (int i = 0; i < image_count; i++)
{
point_counts.push_back(board_size.width * board_size.height);
}
/* 开始定标 */
calibrateCamera(word_calib_Points, corners_Seq, image_size, CamearMatrix, distortion_coeffs, rotation_vectors, translation_vectors);
std::cout << "打印出标定参数\n";
cout << "CamearMatrix:" << endl;
cout << CamearMatrix << endl;
cout << "*****************************" << endl;
cout << "distCoeffs:" << endl;
cout << distortion_coeffs << endl;
cout << "*****************************" << endl;
std::cout << "定标完成!\n";
/************************************************************************
对定标结果进行评价
*************************************************************************/
std::cout << "开始评价定标结果………………" << endl;
double total_err = 0.0; // 所有图像的平均误差的总和
double err = 0.0; // 每幅图像的平均误差
vector<Point2f> new_image_points; // 保存重新计算得到的投影点
std::cout << "每幅图像的定标误差:" << endl;
fout << "每幅图像的定标误差:" << endl << endl;
for (int i = 0; i < image_count; i++)
{
vector<Point3f> tempPointSet = word_calib_Points[i];
// 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点
projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], CamearMatrix, distortion_coeffs, new_image_points);
// 计算新的投影点和旧的投影点之间的误差
vector<Point2f> singleImagePoint = corners_Seq[i];
Mat tempImagePointMat = Mat(1, singleImagePoint.size(), CV_32FC2);
Mat image_points2Mat = Mat(1, new_image_points.size(), CV_32FC2);
for (int j = 0; j < singleImagePoint.size(); j++)
{
image_points2Mat.at<Vec2f>(0, j) = Vec2f(new_image_points[j].x, new_image_points[j].y);
tempImagePointMat.at<Vec2f>(0, j) = Vec2f(singleImagePoint[j].x, singleImagePoint[j].y);
}
err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
total_err += err /= point_counts[i];
std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
}
std::cout << "总体平均误差:" << total_err / image_count << "像素" << endl;
fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;
std::cout << "评价完成!" << endl;
/************************************************************************
保存定标结果
*************************************************************************/
std::cout << "开始保存定标结果………………" << endl;
Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 保存每幅图像的旋转矩阵
fout << "相机内参数矩阵:" << endl;
fout << CamearMatrix << endl << endl;
fout << "畸变系数:\n";
fout << distortion_coeffs << endl << endl << endl;
for (int i = 0; i < image_count; i++)
{
fout << "第" << i + 1 << " 幅图像的旋转向量:" << endl;
fout << rotation_vectors[i] << endl;
// 将旋转向量转换为相对应的旋转矩阵
Rodrigues(rotation_vectors[i], rotation_matrix);
fout << "第" << i + 1 << " 幅图像的旋转矩阵:" << endl;
fout << rotation_matrix << endl;
fout << "第" << i + 1 << " 幅图像的平移向量:" << endl;
fout << translation_vectors[i] << endl << endl;
}
std::cout << "完成保存" << endl;
fout << endl;
矫正图像
//std::string origin_image_path = "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\30.png";
//Mat origion_image = imread(origin_image_path);
//Mat image_rectify, image_rectify_2;
矫正方法1 undistort
//undistort(origion_image, image_rectify, CamearMatrix, distortion_coeffs);
//imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify.png", image_rectify);
矫正方法2 initUndistortRectifyMap 和 remap 方法
//Mat map1, map2;
//Size image_Size = origion_image.size();
//initUndistortRectifyMap(CamearMatrix, distortion_coeffs, Mat(), CamearMatrix, image_Size, CV_16SC2, map1, map2);
//remap(image, image_rectify_2, map1, map2, INTER_LINEAR);
//imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify_2.png", image_rectify_2);
}
opencv 标定结果
内参矩阵
opencv 结果和halcon 结果对比
* Calibration 01: Code generated by Calibration 01
* 相机内参
*halcon 标定结果
CameraParameters := ['area_scan_division',0.00839081,-1987.45,8.35072e-06,8.3e-06, 362.43, 291.71, 768, 576]
* 矫正后的内参 ['area_scan_division', 0.00839081, 0.0, 8.52659e-06,8.3954e-06, 361.554, 291.807, 768, 576]
*opencv 结果
*CamearMatrix:
*[1003.508370362814, 0, 355.145188731808;
*0, 1004.893192592846, 294.7135512484718;
*0, 0, 1]
*---将halcon 结果转换为opencv 结果
* opencv_sx=halcon_f/haocon_sx
* opencv_sy=halcon_f/haocon_sy
x:=0.00839081/8.35072e-06
y:=0.00839081/8.35072e-06
halcon2opencv:=[x,0,355.145188731808,0,y,294.7135512484718,0, 0, 1]
*[1003.508370362814, 0, 355.145188731808;
*0, 1004.893192592846, 294.7135512484718;
*0, 0, 1]