#include #include #include #include #include "io/camera.hpp" #include "io/cboard.hpp" #include "tools/img_tools.hpp" #include "tools/logger.hpp" #include "tools/math_tools.hpp" const std::string keys = "{help h usage ? | | 输出命令行参数说明}" "{@config-path c | configs/calibration.yaml | yaml配置文件路径 }" "{output-folder o | assets/img_with_q | 输出文件夹路径 }"; void write_q(const std::string q_path, const Eigen::Quaterniond & q) { std::ofstream q_file(q_path); Eigen::Vector4d xyzw = q.coeffs(); // 输出顺序为wxyz q_file << fmt::format("{} {} {} {}", xyzw[3], xyzw[0], xyzw[1], xyzw[2]); q_file.close(); } void capture_loop( const std::string & config_path, const std::string & can, const std::string & output_folder) { io::CBoard cboard(config_path); io::Camera camera(config_path); cv::Mat img; std::chrono::steady_clock::time_point timestamp; int count = 0; while (true) { camera.read(img, timestamp); Eigen::Quaterniond q = cboard.imu_at(timestamp); // 在图像上显示欧拉角,用来判断imuabs系的xyz正方向,同时判断imu是否存在零漂 auto img_with_ypr = img.clone(); Eigen::Vector3d zyx = tools::eulers(q, 2, 1, 0) * 57.3; // degree tools::draw_text(img_with_ypr, fmt::format("Z {:.2f}", zyx[0]), {40, 40}, {0, 0, 255}); tools::draw_text(img_with_ypr, fmt::format("Y {:.2f}", zyx[1]), {40, 80}, {0, 0, 255}); tools::draw_text(img_with_ypr, fmt::format("X {:.2f}", zyx[2]), {40, 120}, {0, 0, 255}); std::vector centers_2d; auto success = cv::findCirclesGrid(img, cv::Size(10, 7), centers_2d); // 默认是对称圆点图案 cv::drawChessboardCorners(img_with_ypr, cv::Size(10, 7), centers_2d, success); // 显示识别结果 cv::resize(img_with_ypr, img_with_ypr, {}, 0.5, 0.5); // 显示时缩小图片尺寸 // 按“s”保存图片和对应四元数,按“q”退出程序 cv::imshow("Press s to save, q to quit", img_with_ypr); auto key = cv::waitKey(1); if (key == 'q') break; else if (key != 's') continue; // 保存图片和四元数 count++; auto img_path = fmt::format("{}/{}.jpg", output_folder, count); auto q_path = fmt::format("{}/{}.txt", output_folder, count); cv::imwrite(img_path, img); write_q(q_path, q); tools::logger()->info("[{}] Saved in {}", count, output_folder); } // 离开该作用域时,camera和cboard会自动关闭 } int main(int argc, char * argv[]) { // 读取命令行参数 cv::CommandLineParser cli(argc, argv, keys); if (cli.has("help")) { cli.printMessage(); return 0; } auto config_path = cli.get(0); auto output_folder = cli.get("output-folder"); // 新建输出文件夹 std::filesystem::create_directory(output_folder); tools::logger()->info("默认标定板尺寸为10列7行"); // 主循环,保存图片和对应四元数 capture_loop(config_path, "can0", output_folder); tools::logger()->warn("注意四元数输出顺序为wxyz"); return 0; }