MOVE_AI/calibration/capture.cpp

126 lines
4.5 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <fmt/core.h>
#include <yaml-cpp/yaml.h>
#include <filesystem>
#include <fstream>
#include <opencv2/opencv.hpp>
#include "calibration/board_pattern.hpp"
#include "src/component/img_tools.hpp"
#include "src/component/logger.hpp"
#include "src/component/math_tools.hpp"
#include "src/device/camera.hpp"
#include "src/device/gimbal/gimbal.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 & output_folder)
{
// 从配置文件加载标定板参数(支持 circles_grid 和 chessboard
auto yaml = YAML::LoadFile(config_path);
auto board_pattern = calibration::load_board_pattern(yaml);
device::Gimbal gimbal(config_path);
device::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 = gimbal.q(timestamp);
// 在图像上显示欧拉角用来判断imuabs系的xyz正方向同时判断imu是否存在零漂
auto img_with_ypr = img.clone();
Eigen::Vector3d zyx = component::eulers(q, 2, 1, 0) * 57.3; // degree
component::draw_text(img_with_ypr, fmt::format("Z {:.2f}", zyx[0]), {40, 40}, {0, 0, 255});
component::draw_text(img_with_ypr, fmt::format("Y {:.2f}", zyx[1]), {40, 80}, {0, 0, 255});
component::draw_text(img_with_ypr, fmt::format("X {:.2f}", zyx[2]), {40, 120}, {0, 0, 255});
std::vector<cv::Point2f> centers_2d;
bool success;
if (board_pattern.pattern_type == calibration::PatternType::chessboard) {
// 棋盘格检测很慢,先在缩小图上快速检测,再映射回原图做亚像素精化
cv::Mat small;
double scale = 0.5;
cv::resize(img, small, {}, scale, scale);
std::vector<cv::Point2f> small_pts;
success = calibration::find_pattern_points(small, board_pattern, small_pts);
if (success) {
for (auto & p : small_pts) { p.x /= scale; p.y /= scale; }
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::cornerSubPix(
gray, small_pts, cv::Size(11, 11), cv::Size(-1, -1),
cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, 1e-3));
centers_2d = std::move(small_pts);
}
} else {
success = calibration::find_pattern_points(img, board_pattern, centers_2d);
}
cv::drawChessboardCorners(img_with_ypr, board_pattern.pattern_size, 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);
component::logger()->info("[{}] Saved in {}", count, output_folder);
}
// 离开该作用域时camera和gimbal会自动关闭
}
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<std::string>(0);
auto output_folder = cli.get<std::string>("output-folder");
// 新建输出文件夹
std::filesystem::create_directory(output_folder);
// 从配置文件读取标定板类型和尺寸
auto yaml = YAML::LoadFile(config_path);
auto board_pattern = calibration::load_board_pattern(yaml);
component::logger()->info(
"标定板类型: {}, 尺寸: {}列{}行",
calibration::pattern_name(board_pattern.pattern_type),
board_pattern.pattern_size.width, board_pattern.pattern_size.height);
// 主循环,保存图片和对应四元数
capture_loop(config_path, output_folder);
component::logger()->warn("注意四元数输出顺序为wxyz");
return 0;
}