拓冰建站拓冰建站
首页 / 资讯中心 / 正文

OpenCV ChArUco 标定板实战:从板图生成、角点检测到位姿估计的完整指南

OpenCV ChArUco 标定板实战从板图生成、角点检测到位姿估计的完整指南【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencvChArUcoCharuco板把棋盘格的角点精度与 ArUco 标记的检测灵活性合二为一是 OpenCVaruco模块中面向高精度相机标定与位姿估计的核心标定物。本文以 charuco_detection.markdown 官方教程为主线结合本仓库中 detect_board_charuco.cpp、create_board_charuco.cpp 及 charuco_detector.hpp 源码系统讲解 ChArUco 板的创建、角点插值检测原理、两种位姿估计调用链并提供可直接运行的命令行示例与关键 API 说明。读完本文你将能独立完成 ChArUco 板图生成、无标定/带标定两种模式下的角点检测以及基于solvePnP的高精度位姿估计。为什么要用 ChArUco 板两种传统方案的取舍ArUco 标记与 ArUco 板检测快、容错强允许部分遮挡、部分视野但它的一个短板是即使做了亚像素细化角点位置的精度依然不够高。与之相对棋盘格的每个角点都被两个黑色方块包围角点定位可以被细化得非常精确但棋盘格的检测非常娇气——必须完整可见不允许任何遮挡灵活性远不如 ArUco 板。ChArUco 板的设计目标正是融合二者的优点ArUco 部分负责插值棋盘格角点的位置因此它继承了标记板的灵活性——允许遮挡与部分视野插值出的角点从属于棋盘格结构因此天然具备很高的亚像素精度。当精度成为首要诉求时典型场景就是相机标定ChArUco 板是比普通 ArUco 板更优的选择。这也是 charuco_detector.hpp 中对CharucoBoard的官方注释它同时提供 ArUco 标记的通用性与棋盘格角点的精度对标定与位姿估计至关重要。本教程的目标如何创建一个 ChArUco 板如何在不进行相机标定的情况下检测 ChArUco 角点如何在携带相机标定参数的情况下检测 ChArUco 角点并完成位姿估计示例代码位置与运行环境ChArUco 全套示例位于仓库的samples/cpp/tutorial_code/objectDetection/目录下其中本文涉及两个核心程序文件作用create_board_charuco.cpp生成一张可打印的 ChArUco 板图片detect_board_charuco.cpp读取图像/视频/相机检测 ChArUco 角点并估计位姿两个程序均通过cv::CommandLineParser解析命令行参数并共用工具头文件 aruco_samples_utility.hpp内含相机参数读取、字典解析等辅助函数。ChArUco 相关的公共 API 声明集中在两个头文件中均需包含到你的工程里// 使用 ChArUco 板必须包含的头文件 #include opencv2/objdetect/charuco_detector.hpp #include opencv2/highgui.hpp #include opencv2/imgproc.hpp #include opencv2/calib3d.hpp // solvePnP、FileStorage 所在模块ChArUco 板的创建Board Creation构造所需的五个要素aruco模块提供cv::aruco::CharucoBoard类表示 ChArUco 板它继承自cv::aruco::Board。定义一个CharucoBoard需要X、Y 两个方向上的棋盘格方块数方块边长square side length标记边长marker side length标记使用的字典dictionary所有标记的id。与cv::aruco::GridBoard一样模块提供了非常简洁的构造方式。从 create_board_charuco.cpp 中可见其用法cv::aruco::CharucoBoard board(Size(squaresX, squaresY), (float)squareLength, (float)markerLength, dictionary);参数含义与注意事项第一个参数Size(squaresX, squaresY)X、Y 方向上的棋盘格方块数。注意这与标定中常用的内角点数概念不同这里给的是完整方块数。第二、三个参数squareLength/markerLength方块边长与标记边长二者必须使用同一单位通常用米。此单位将直接决定后续估计出的位姿平移量的量纲。最后一个参数dictionary标记字典。标记会按字典顺序嵌入棋盘的白色方块中。id默认按从 0 开始的升序自动分配与cv::aruco::GridBoard构造行为一致若需自定义可直接访问父类Board的board.ids向量修改。构造函数的ids形参默认为空noArray()传入自定义 id 数组亦可详见 aruco_board.hpp。将板渲染为可打印图片拿到CharucoBoard对象后有两种方式生成打印用的板图使用脚本apps/pattern-tools/generate_pattern.py同时可输出 DICT JSON 与 SVG仓库apps/pattern-tools/目录下已预置各尺寸的 DICT_4X4/5X5/6X6/7X7、ARUCO_ORIGINAL、APRILTAG、MIP 等字典压缩包适合大批量/离线生成调用cv::aruco::CharucoBoard::generateImage()这是最直接的代码方式。generateImage()的调用方式同样取自 create_board_charuco.cppMat boardImage; Size imageSize; imageSize.width squaresX * squareLength 2 * margins; // 外边距左右各一份 imageSize.height squaresY * squareLength 2 * margins; board.generateImage(imageSize, boardImage, margins, borderBits);参数说明参数含义outSize此处为imageSize输出图像尺寸像素。若不与板尺寸成比例板会被居中绘制在图像中央。样本中把尺寸设为板的总边长 2 倍边距恰好严丝合缝img输出的板图MatmarginSize可选外边距像素保证任何标记都不贴到图像边缘样本默认取squareLength - markerLengthborderBits标记黑色边框占的位模块数语义与cv::aruco::generateImageMarker()一致默认 1生成出的板图大致如下此处为仓库教程目录内的样例图命令行生成示例create_board_charuco.cpp 的全部可选参数如下程序会在未传参时打印用法const char* keys {outfile |res.png| Output image } {w | 5 | Number of squares in X direction } {h | 7 | Number of squares in Y direction } {sl | 100 | Square side length (in pixels) } {ml | 60 | Marker side length (in pixels) } {d | | dictionary: ... } {cd | | Input file with custom dictionary } {m | | Margins size (in pixels). Default is (squareLength-markerLength) } {bb | 1 | Number of bits in marker borders } {si | false | show generated image };官方教程给出的调用示例为w/h/sl/ml此处以像素为单位-d10对应DICT_6X6_250_output_path_/chboard.png -w5 -h7 -sl100 -ml60 -d10-d字典 id 与字典名的完整映射见样本源码注释DICT_4X4_500 … DICT_ARUCO_MIP_36h1221若都不传-d且不传-cdaruco_samples_utility.hpp 中的辅助函数会默认选用DICT_4X4_50。ChArUco 板角点检测Detection检测的本质与总体流程检测 ChArUco 板实际检测的是板上每一个棋盘格内角点chessboard corners。板上每个角点都有唯一 id从 0 递增到板的总角点数减一。官方教程把整条流水线拆成如下几个阶段以下逐一展开① 读取输入图像。detect_board_charuco.cpp 中从VideoCapture取帧。这张原始图像必须保留因为后续要对 ChArUco 角点做亚像素细化细化过程需要原始灰度纹理信息。② 读取相机标定参数仅在带标定的检测流程中需要。核心是 aruco_samples_utility.hpp 中的readCameraParamsFromCommandLine→readCameraParametersbool readOk readCameraParameters(parser.getstd::string(c), camMatrix, distCoeffs);readCameraParameters通过cv::FileStorage从 yml 中读取键camera_matrix与distortion_coefficients返回布尔值表示标定参数是否有效。若仅做无标定角点检测这一步可以完全跳过。③ 检测 ArUco 标记并从标记插值出 ChArUco 角点。ChArUco 角点的检测建立在已检出的 ArUco 标记之上先检标记再从标记反推棋盘角点。这一步由cv::aruco::CharucoDetector::detectBoard()完成是整套流程的枢纽// 组装 detector板 ChArUco 参数 ArUco 标记检测参数 aruco::CharucoBoard charucoBoard(Size(squaresX, squaresY), squareLength, markerLength, dictionary); aruco::CharucoParameters charucoParams; charucoParams.tryRefineMarkers refine; // 为 true 时 detectBoard 内部会先做 refineDetectedMarkers() charucoParams.cameraMatrix camMatrix; // 可为 detectBoard() 提供相机内参 charucoParams.distCoeffs distCoeffs; // 可为 detectBoard() 提供畸变系数 aruco::CharucoDetector charucoDetector(charucoBoard, charucoParams, detectorParams); // 逐帧检测同时输出 ChArUco 角点与中间层 ArUco 标记结果 charucoDetector.detectBoard(image, charucoCorners, charucoIds, markerCorners, markerIds);注意CharucoDetector在构造函数阶段就一次性绑定板与全部参数板、CharucoParameters、DetectorParameters以及可选的RefineParameters见 charuco_detector.hpp循环体内只调用detectBoard()因此多帧/视频场景下每帧的额外开销很小。detectBoard() 的参数语义依据 charuco_detector.hpp 的函数签名参数类型含义imageInputArray输入图像用于亚像素细化以及必要时内部触发detectMarkers()charucoCornersOutputArray检出的角点图像坐标列表charucoIdsOutputArray与charucoCorners一一对应的角点 idmarkerCornersInputOutputArrayOfArrays检测到的标记角点输入/输出均可markerIdsInputOutputArray检测到的标记 id关键行为header 注释与官方文档双重确认若传入的markerCorners/markerIds为空函数会先自动执行 ArUco 标记检测再插值若提供了相机标定参数CharucoParameters中带cameraMatrix/distCoeffs角点插值走先由 ArUco 标记粗估计位姿、再把 ChArUco 角点反投影回图像的路线若未提供标定参数插值改走计算 ChArUco 平面与其图像投影之间的单应homography的路线只返回可见的角点即仅返回其周围标记确实被检测到的那些角点。单应 vs 反投影两种插值方式的取舍两种插值策略在 charuco_detector.hpp 中被明确注释为If camera parameters are provided, the process is based in an approximated pose estimation, else it is based on local homography. Only visible corners are returned.单应homography路线无标定无需内参即可工作但对图像畸变更敏感。为抑制畸变影响算法只使用每个 ChArUco 角点邻近的标记来计算局部单应而非整板一张单应。近似位姿 反投影路线带标定需要相机内参与畸变系数但能抵消镜头畸变精度更高是标定/精密测量的首选。影响插值质量的三个关键细节1建议关闭 ArUco 标记的角点细化。在 ChArUco 检测尤其走单应路线中官方文档明确建议禁用标记的亚像素细化因为棋盘方块彼此贴得很近对标记做亚像素细化反而可能产生明显的角点偏移且这种偏移会传播到 ChArUco 角点插值结果中导致整体精度变差。示例程序中通过-rs开关控制charucoParams.tryRefineMarkers默认关闭。2棋盘方块与标记之间要保持足够边距。官方注释给出量化建议方块边与标记之间的空白应大于一个标记模块module的 70%否则插值易受干扰产生偏差。3只信任两个环绕标记都被检出的角点。每个 ChArUco 角点被若干方块围住。只有当其周围标记被全部/足够检测到时该角点才会被输出。若某个角点的环绕标记有缺失通常意味着该区域存在遮挡或图像质量不佳——此时宁可丢弃该角点也要保证输出的插值角点高度可信。这正是CharucoParameters中minMarkers字段默认 2表示至少需要检测到多少相邻标记才返回该角点与checkMarkers字段默认 true校验标记确属同一板的作用见 charuco_detector.hpp。角点插值完成后算法还会自动执行一次亚像素细化以收敛到棋盘角点的最优位置。绘制检测结果插值完成后用cv::aruco::drawDetectedCornersCharuco()即可把角点叠加画回图像上默认颜色为Scalar(255, 0, 0)即蓝色可自定义aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, cv::Scalar(255, 0, 0));参数说明与 charuco_detector.hpp 一致image输入/输出图像需为 1 或 3 通道通常是检测用的原图副本charucoCorners/charucoIdsdetectBoard()的输出cornerColor可选角点/序号绘制颜色。对下面这张原始输入图检测并绘制后的结果大致为而当场景存在遮挡时仓库还提供了一张带遮挡的样张chocclusion.jpg尽管部分角点清晰可见只要其环绕标记因遮挡未被检出这些角点便不会被插值输出——这正是 ChArUco 插值宁可少、不可错策略的直观体现。教程还提供了完整示例视频Nj44m_N_9FY可对照观察。检测程序的命令行用法detect_board_charuco.cpp 除-w/-h/-sl/-ml/-d/-c/-cd外还支持以下开关{c | | Output file with calibrated camera parameters } // 严格说此处为输入标定文件 {v | | Input from video or image file, if ommited, input comes from camera } {ci | 0 | Camera id if input doesnt come from video (-v) } {dp | | File of marker detector parameters } {rs | | Apply refind strategy }官方教程给出的无标定角点检测示例注意此时sl/ml以米为单位配合示例图片-w5 -h7 -sl0.04 -ml0.02 -d10 -vdoc/tutorials/objdetect/charuco_detection/images/choriginal.jpg程序运行时会周期性打印每帧检测耗时毫秒与均值便于在视频/实况场景中评估性能。仓库目录doc/tutorials/objdetect/charuco_detection/images/中可找到choriginal.jpg、chocclusion_original.jpg等测试原图直接用于复现。ChArUco 板的位姿估计Pose Estimation坐标系约定ChArUco 板做位姿估计的最终目标是高精度标定或位姿解算。与cv::aruco::GridBoard一致CharucoBoard的坐标系定义在板平面内Z 轴指向板平面内侧朝向纸张内部原点位于板的左下角。重要兼容性提示OpenCV 4.6.0 起的破坏性变更4.6.0 之后板的坐标系发生了不兼容改动——Z 轴从指向平面外侧改为指向平面内侧。判断方法objPoints按**顺时针CW排列时对应 Z 轴向内按逆时针CCW**排列时对应 Z 轴向外。因此若你在 4.6.0 前后混用标定数据或手写对象点务必核对绕序否则位姿符号会翻转。使用 matchImagePoints solvePnP 估计位姿位姿估计的推荐姿势是cv::aruco::CharucoBoard::matchImagePoints()自Board父类继承配合cv::solvePnP()。示例程序中的调用位于 detect_board_charuco.cppbool validPose false; if (camMatrix.total() ! 0 distCoeffs.total() ! 0 charucoIds.size() 4) { Mat objPoints, imgPoints; charucoBoard.matchImagePoints(charucoCorners, charucoIds, objPoints, imgPoints); validPose solvePnP(objPoints, imgPoints, camMatrix, distCoeffs, rvec, tvec); }要点matchImagePoints()负责把检出的 2D 角点 id换算成对应的3D 对象点objPoints与2D 图像点imgPoints是 ChArUco 位姿估计的核心桥梁定义见 aruco_board.hppcameraMatrix、distCoeffs来自readCameraParameters读入的标定文件位姿估计必须携带标定参数无标定模式只能输出 2D 角点rvec/tvec为输出的旋转向量与平移向量示例程序还设了charucoIds.size() 4的门槛角点太少不足以稳定解算cv::solvePnP()返回布尔值表示是否成功失败的主因通常是角点数不足或所有角点共线几何退化无法唯一确定姿态。绘制坐标轴验证结果位姿是否正确可调用cv::drawFrameAxes()把坐标轴画到图像上目视验证。示例中坐标轴长度取棋盘短边的一半0.5 * min(squaresX, squaresY) * squareLengthif (validPose) cv::drawFrameAxes(imageCopy, camMatrix, distCoeffs, rvec, tvec, axisLength);渲染规则为 X 轴红色、Y 轴绿色、Z 轴蓝色效果如下带标定参数的完整命令行示例仓库在 samples/cpp/tutorial_code/objectDetection/ 目录提供了配套的标定结果文件tutorial_camera_charuco.ymlsamples/data/aruco/下亦有一份内含camera_matrix与distortion_coefficients节点格式与readCameraParameters的读取键完全对应。官方示例的完整调用为-w5 -h7 -sl0.04 -ml0.02 -d10 \ -vdoc/tutorials/objdetect/charuco_detection/images/choriginal.jpg \ -csamples/cpp/tutorial_code/objectDetection/tutorial_camera_charuco.yml与标定流程的衔接及延伸本教程完整覆盖了 ChArUco 的生成 → 检测 → 位姿闭环在仓库samples/cpp/tutorial_code/objectDetection/中还提供了一脉相承的进阶示例calibrate_camera_charuco.cpp基于 ChArUco 板的多帧相机标定——正好把本文检测到的高精度角点批量喂给标定函数是 ChArUco 最典型的落地场景detect_board.cpp 与 create_board.cpp标准 ArUco 板的对照实现对应官方教程序列中的前一/后一篇前者介绍 ArUco 板检测后者介绍 ChArUco Diamond 检测位于doc/tutorials/objdetect/教程目录。一句话总结适用决策追求亚像素级角点精度并愿意接受只返回被充分环绕标记支撑的角点这一约束时选 ChArUco反之若需要更多标记同时入镜、对绝对精度要求不高普通 ArUco 板仍是更轻量的选择。【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门