#include #include #include #include #include "tools/img_tools.hpp" const std::string keys = "{help h usage ? | | 输出命令行参数说明}" "{config-path c | configs/calibration.yaml | yaml配置文件路径 }" "{@input-folder | assets/img_with_q | 输入文件夹路径 }"; std::vector centers_3d(const cv::Size & pattern_size, const float center_distance) { std::vector centers_3d; for (int i = 0; i < pattern_size.height; i++) for (int j = 0; j < pattern_size.width; j++) centers_3d.push_back({j * center_distance, i * center_distance, 0}); return centers_3d; } void load( const std::string & input_folder, const std::string & config_path, cv::Size & img_size, std::vector> & obj_points, std::vector> & img_points) { // 读取yaml参数 auto yaml = YAML::LoadFile(config_path); auto pattern_cols = yaml["pattern_cols"].as(); auto pattern_rows = yaml["pattern_rows"].as(); auto center_distance_mm = yaml["center_distance_mm"].as(); cv::Size pattern_size(pattern_cols, pattern_rows); for (int i = 1; true; i++) { // 读取图片 auto img_path = fmt::format("{}/{}.jpg", input_folder, i); auto img = cv::imread(img_path); if (img.empty()) break; // 设置图片尺寸 img_size = img.size(); // 识别标定板 std::vector centers_2d; auto success = cv::findCirclesGrid(img, pattern_size, centers_2d, cv::CALIB_CB_SYMMETRIC_GRID); // 显示识别结果 auto drawing = img.clone(); cv::drawChessboardCorners(drawing, pattern_size, centers_2d, success); cv::resize(drawing, drawing, {}, 0.5, 0.5); // 缩小图片尺寸便于显示完全 cv::imshow("Press any to continue", drawing); cv::waitKey(0); // 输出识别结果 fmt::print("[{}] {}\n", success ? "success" : "failure", img_path); if (!success) continue; // 记录所需的数据 img_points.emplace_back(centers_2d); obj_points.emplace_back(centers_3d(pattern_size, center_distance_mm)); } } void print_yaml(const cv::Mat & camera_matrix, const cv::Mat & distort_coeffs, double error) { YAML::Emitter result; std::vector camera_matrix_data( camera_matrix.begin(), camera_matrix.end()); std::vector distort_coeffs_data( distort_coeffs.begin(), distort_coeffs.end()); result << YAML::BeginMap; result << YAML::Comment(fmt::format("重投影误差: {:.4f}px", error)); result << YAML::Key << "camera_matrix"; result << YAML::Value << YAML::Flow << camera_matrix_data; result << YAML::Key << "distort_coeffs"; result << YAML::Value << YAML::Flow << distort_coeffs_data; result << YAML::Newline; result << YAML::EndMap; fmt::print("\n{}\n", result.c_str()); } int main(int argc, char * argv[]) { // 读取命令行参数 cv::CommandLineParser cli(argc, argv, keys); if (cli.has("help")) { cli.printMessage(); return 0; } auto input_folder = cli.get(0); auto config_path = cli.get("config-path"); // 从输入文件夹中加载标定所需的数据 cv::Size img_size; std::vector> obj_points; std::vector> img_points; load(input_folder, config_path, img_size, obj_points, img_points); // 相机标定 cv::Mat camera_matrix, distort_coeffs; std::vector rvecs, tvecs; auto criteria = cv::TermCriteria( cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 100, DBL_EPSILON); // 默认迭代次数(30)有时会导致结果发散,故设为100 cv::calibrateCamera( obj_points, img_points, img_size, camera_matrix, distort_coeffs, rvecs, tvecs, cv::CALIB_FIX_K3, criteria); // 由于视场角较小,不需要考虑k3 // 重投影误差 double error_sum = 0; size_t total_points = 0; for (size_t i = 0; i < obj_points.size(); i++) { std::vector reprojected_points; cv::projectPoints( obj_points[i], rvecs[i], tvecs[i], camera_matrix, distort_coeffs, reprojected_points); total_points += reprojected_points.size(); for (size_t j = 0; j < reprojected_points.size(); j++) error_sum += cv::norm(img_points[i][j] - reprojected_points[j]); } auto error = error_sum / total_points; // 输出yaml print_yaml(camera_matrix, distort_coeffs, error); }