热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenCV中检测ChArUco的角点(2)

论文阅读模块将分享点云处理,SLAM,三维视觉,高精地图相关的文章。公众号致力于理解三维视觉领域相关内容的干货分享,欢迎各位

论文阅读模块将分享点云处理,SLAM,三维视觉,高精地图相关的文章。公众号致力于理解三维视觉领域相关内容的干货分享,欢迎各位加入我,我们一起每天一篇文章阅读,开启分享之旅,有兴趣的可联系微信dianyunpcl@163.com。

opencv中ArUco模块实践(1)

ArUco的生成与检测

ArUco与AprilTag简介

ChAruco标定板

ArUCo标记板是非常有用的,因为他们的快速检测和多功能性。然而,ArUco标记的一个问题是,即使在应用亚像素细化后,其角点位置的精度也不太高。相反,棋盘图案的角点可以更精确地细化,因为每个角点被两个黑色正方形包围。然而,寻找棋盘图案并不像寻找aruco棋盘那样通用:它必须是完全可见的,并且不允许遮挡。

ChAruco标记板试图结合这两种方法的优点:

ArUco部分用于内插棋盘转角的位置,因此它具有标记板的多功能性,因为它允许遮挡或局部视图。此外,由于插值的角点属于棋盘,因此它们在亚像素精度方面非常精确。

当对角点加测的要求是高精度且必要的,如在相机校准,Charuco板是一个比标准aruco板更好的选择。

ChArUco标记板的创建

aruco模块提供cv::aruco::CharucoBoard类,该类表示Charuco板,并从Board类继承。

该类与ChArUco的其他功能一样,定义如下:

#include

要定义Charuco标记板,必须:

  • X方向的棋盘格数。

  • Y方向棋盘格数。

  • 正方形边的长度。

  • 标记侧的长度。

  • 标记词典。

  • 所有标记的ID。

对于GridBoard对象,aruco模块提供了一个创建CharucoBoards的函数。

cv::aruco::CharucoBoard::create():
cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);

  • 第一和第二参数分别是X和Y方向上的平方数。

  • 第三和第四个参数分别是正方形和标记的长度。它们可以以任何单位提供,记住该标记板的估计姿势将以相同单位测量(通常使用米)。 

  • 最后给出了标记的字典。

默认情况下,每个标记的ID都是从0开始按升序分配的,就像在GridBoard::create()中一样。这可以通过board.ids访问ids向量来轻松定制,就像在board父类中一样。

一旦我们有了CharucoBoard对象,我们就可以创建一个图像来打印它。这可以通过

cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);cv::Mat boardImage;board.draw( cv::Size(600, 500), boardImage, 10, 1 );

  • 第一个参数是以像素为单位的输出图像的大小。在本例中为600x500像素。如果这与电路板尺寸不成比例,它将以图像为中心。

  • boardImage:根据标定板输出的图像。

  • 第三个参数是(可选)以像素为单位的边距,因此没有任何标记接触图像边界。在这种情况下,边距是10。

  • 最后,标记边框的大小,类似于drawMarker()函数。默认值为1。

ChArUco标定板检测

当你检测到一个ChArUco棋盘时,实际检测到的是棋盘的每个棋盘格角点。

ChArUco板上的每个角落都分配了一个唯一标识符(id)。这些ID从0到板中的角总数。

因此,检测到的ChArUco板包括:

vectorcharucoCorners:检测到的角点的图像位置列表。

vectorcharucoIds:charucoCorners中每个检测到的角点的ID。

ChArUco角点的检测基于先前检测到的标记。因此,首先检测标记,然后从标记中插值ChArUco角点。检测ChArUco角点的函数是

cv::aruco::interpolateCornersCharuco()

这个例子展示了整个过程。首先,检测标记,然后从这些标记中插值ChArUco角点。

cv::Mat inputImage;
cv::Mat cameraMatrix, distCoeffs;
// camera parameters are read from somewhere
readCameraParameters(cameraMatrix, distCoeffs);cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);
...vector markerIds;
vector > markerCorners;cv::aruco::detectMarkers(inputImage, board.dictionary, markerCorners, markerIds);
// if at least one marker detected
if(markerIds.size() > 0) {
std::vector charucoCorners;std::vector charucoIds;cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, inputImage, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);}

interpolateCornersCharuco()函数的参数为:

markerCorners和markerIds:从detectMarkers()函数中检测到的标记物。

inputimage:检测到标记的原始图像。图像是必要的执行亚像素细化在Aruco角点。

board:CharucoBoard对象

charucockerners和charucoIds:输出插值Charuco角点

cameraMatrix和distcoefs:可选的摄像机校准参数

函数返回插值的Charuco角点的数目。

在这种情况下,我们调用interpolateCornersCharuco()来提供相机校准参数。但是,这些参数是可选的。没有这些参数的类似示例如下:

cv::Mat inputImage;
cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);...
vector markerIds;vector > markerCorners;DetectorParameters params;params.doCornerRefinement = false;cv::aruco::detectMarkers(inputImage, board.dictionary, markerCorners, markerIds, params);// if at least one marker detectedif(markerIds.size() > 0) {std::vector charucoCorners;std::vector charucoIds;cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, inputImage, board, charucoCorners, charucoIds);}

如果提供了校准参数,ChArUco角点插值方法是,首先从ArUco标记估计一个粗略姿态,然后将ChArUco角点重新投影回图像。另一方面,如果不提供校准参数,则通过计算ChArUco平面和ChArUco图像投影之间的对应单应来插值ChArUco角点。

使用单应的主要问题是插值对图像失真更敏感。实际上,单应仅使用每个ChArUco角点的最近标记位来执行,以减少失真的影响。

在检测ChArUco板的标记时,特别是在使用单应性时,建议禁用标记的角点细化。其原因是,由于棋盘方块的接近性,亚像素过程会在角点位置产生重要的偏差,这些偏差会传播到ChArUco角点插值,产生较差的结果。

此外,仅返回其两个周围标记已找到的角点。如果没有检测到周围的两个标记中的任何一个,这通常意味着该区域存在某种遮挡或图像质量不好。在任何情况下,最好不要考虑该角点,因为我们想要的是确保插值的ChArUco角点非常精确。

在对ChArUco角点进行插值之后,执行亚像素细化。

一旦我们内插了ChArUco角点,我们可能会想画出来看看他们的检测是否正确。使用drawDetectedCornersCharuco()函数可以轻松完成此操作:

cv::aruco::drawDetectedCornersCharuco(image, charucoCorners, charucoIds, color);

  • image是绘制角点的图像(通常与检测角点的图像相同)。

  • outputImage将是inputImage的克隆,并绘制了角点。

  • charucoCorners和charucoIds是从interpolateCornersCharuco()函数中检测到的Charuco角点。

  • 最后,最后一个参数是要绘制角点的(可选)颜色,类型为cv::Scalar。

最后,这是ChArUco检测的完整示例(不使用校准参数)

cv::VideoCapture inputVideo;inputVideo.open(0);cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);DetectorParameters params;params.doCornerRefinement = false;while (inputVideo.grab()) {cv::Mat image, imageCopy;inputVideo.retrieve(image);image.copyTo(imageCopy);std::vector ids;std::vector > corners;cv::aruco::detectMarkers(image, dictionary, corners, ids, params);// if at least one marker detectedif (ids.size() > 0) {cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);std::vector charucoCorners;std::vector charucoIds;cv::aruco::interpolateCornersCharuco(corners, ids, image, board, charucoCorners, charucoIds);// if at least one charuco corner detectedif(charucoIds.size() > 0)cv::aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, cv::Scalar(255, 0, 0));}cv::imshow("out", imageCopy);char key = (char) cv::waitKey(waitTime);if (key == 27)break;}

ChArUco姿态估计

ChArUco板的最终目标是非常精确地找到角点,以便进行高精度校准或姿态估计。

aruco模块提供了一个简单的ChArUco姿态估计功能。与在GridBoard中一样,CharucoBoard的坐标系放置在板平面中,Z轴指向外,并居中于板的左下角。

姿态估计的函数是estimatePosecharocboard():

cv::aruco::EstimatePoseCharocboard(charucoCorners、charucoIds、board、cameraMatrix、Distceffs、rvec、tvec);

charucoCorners和charucoIds参数是从interpolateCornersCharuco()函数中检测到的charuco角点。

  • 第三个参数是CharucoBoard对象。

  • cameraMatrix和distcoefs是姿态估计所必需的摄像机标定参数。

  • 最后,rvec和tvec参数是Charuco板的输出姿态。

如果正确估计了姿势,则函数返回true,否则返回false。失败的主要原因是没有足够的角点进行姿态估计或它们在同一条直线上。可以使用drawAxis()绘制轴,以检查姿势是否正确估计。结果是:(X:红色,Y:绿色,Z:蓝色)

完整的的位姿估计的代码

cv::VideoCapture inputVideo;
inputVideo.open(0);cv::Mat cameraMatrix, distCoeffs;// camera parameters are read from somewherereadCameraParameters(cameraMatrix, distCoeffs);cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);while (inputVideo.grab()) {cv::Mat image, imageCopy;inputVideo.retrieve(image);image.copyTo(imageCopy);std::vector ids;std::vector > corners;cv::aruco::detectMarkers(image, dictionary, corners, ids);// if at least one marker detectedif (ids.size() > 0) {std::vector charucoCorners;std::vector charucoIds;cv::aruco::interpolateCornersCharuco(corners, ids, image, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);// if at least one charuco corner detectedif(charucoIds.size() > 0) {cv::aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, cv::Scalar(255, 0, 0));cv::Vec3d rvec, tvec;bool valid = cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec);// if charuco pose is validif(valid)cv::aruco::drawAxis(imageCopy, cameraMatrix, distCoeffs, rvec, tvec, 0.1);}}cv::imshow("out", imageCopy);char key = (char) cv::waitKey(waitTime);if (key == 27)break;}

资源

三维点云论文及相关应用分享

【点云论文速读】基于激光雷达的里程计及3D点云地图中的定位方法

3D目标检测:MV3D-Net

三维点云分割综述(上)

3D-MiniNet: 从点云中学习2D表示以实现快速有效的3D LIDAR语义分割(2020)

win下使用QT添加VTK插件实现点云可视化GUI

JSNet:3D点云的联合实例和语义分割

大场景三维点云的语义分割综述

PCL中outofcore模块---基于核外八叉树的大规模点云的显示

基于局部凹凸性进行目标分割

基于三维卷积神经网络的点云标记

点云的超体素(SuperVoxel)

基于超点图的大规模点云分割

更多文章可查看:点云学习历史文章大汇总

SLAM及AR相关分享

【开源方案共享】ORB-SLAM3开源啦!

【论文速读】AVP-SLAM:自动泊车系统中的语义SLAM

【点云论文速读】StructSLAM:结构化线特征SLAM

SLAM和AR综述

常用的3D深度相机

AR设备单目视觉惯导SLAM算法综述与评价

SLAM综述(4)激光与视觉融合SLAM

Kimera实时重建的语义SLAM系统

SLAM综述(3)-视觉与惯导,视觉与深度学习SLAM

易扩展的SLAM框架-OpenVSLAM

高翔:非结构化道路激光SLAM中的挑战

SLAM综述之Lidar SLAM

基于鱼眼相机的SLAM方法介绍

如果你对本文感兴趣,请点击“原文阅读”获取知识星球二维码,务必按照“姓名+学校/公司+研究方向”备注加入免费知识星球,免费下载pdf文档,和更多热爱分享的小伙伴一起交流吧!

以上内容如有错误请留言评论,欢迎指正交流。如有侵权,请联系删除

扫描二维码

                   关注我们

让我们一起分享一起学习吧!期待有想法,乐于分享的小伙伴加入免费星球注入爱分享的新鲜活力。分享的主题包含但不限于三维视觉,点云,高精地图,自动驾驶,以及机器人等相关的领域。

分享及合作:群主微信“920177957”(需要按要求备注) 联系邮箱:dianyunpcl@163.com,欢迎企业来联系公众号展开合作。

点一下“在看”你会更好看耶



推荐阅读
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 如何精通编程语言:全面指南与实用技巧
    如何精通编程语言:全面指南与实用技巧 ... [详细]
  • 当前物联网领域十大核心技术解析:涵盖哪些关键技术?
    经过近十年的技术革新,物联网已悄然渗透到日常生活中,对社会产生了深远影响。本文将详细解析当前物联网领域的十大核心关键技术,包括但不限于:1. 军事物联网技术,该技术通过先进的感知设备实现战场环境的实时监测与数据传输,提升作战效能和决策效率。其他关键技术还包括传感器网络、边缘计算、大数据分析等,这些技术共同推动了物联网的快速发展和广泛应用。 ... [详细]
  • 在前一篇文章《Hadoop》系列之“踽踽独行”(二)中,我们详细探讨了云计算的核心概念。本章将重点转向物联网技术,全面解析其基本原理、应用场景及未来发展前景。通过深入分析物联网的架构和技术栈,我们将揭示其在智能城市、工业自动化和智能家居等领域的广泛应用潜力。此外,还将讨论物联网面临的挑战,如数据安全和隐私保护等问题,并展望其在未来技术融合中的重要角色。 ... [详细]
  • 深入解析 OpenCV 2 中 Mat 对象的类型、深度与步长属性
    在OpenCV 2中,`Mat`类作为核心组件,对于图像处理至关重要。本文将深入探讨`Mat`对象的类型、深度与步长属性,这些属性是理解和优化图像操作的基础。通过具体示例,我们将展示如何利用这些属性实现高效的图像缩小功能。此外,还将讨论这些属性在实际应用中的重要性和常见误区,帮助读者更好地掌握`Mat`类的使用方法。 ... [详细]
  • Java EE 平台集成了多种服务、API 和协议,旨在支持基于 Web 的多层应用程序开发。本文将详细介绍 Java EE 中的 13 种关键技术规范,帮助开发者更好地理解和应用这些技术。 ... [详细]
  • 第14周实践项目(4)-验证平衡二叉树
    问题**Copyright(c)2015,烟台大学计算机学院*Allrightsreserved.*文件名称:test.cpp*作者:王敏*完成日 ... [详细]
  • 本文将详细介绍如何在 Vue 项目中使用 Handsontable 插件,包括 npm 安装、基本配置和常用功能的实现。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 深入解析Java虚拟机的内存分区与管理机制
    Java虚拟机的内存分区与管理机制复杂且精细。其中,某些内存区域在虚拟机启动时即创建并持续存在,而另一些则随用户线程的生命周期动态创建和销毁。例如,每个线程都拥有一个独立的程序计数器,确保线程切换后能够准确恢复到之前的执行位置。这种设计不仅提高了多线程环境下的执行效率,还增强了系统的稳定性和可靠性。 ... [详细]
  • Kafka 是由 Apache 软件基金会开发的高性能分布式消息系统,支持高吞吐量的发布和订阅功能,主要使用 Scala 和 Java 编写。本文将深入解析 Kafka 的安装与配置过程,为程序员提供详尽的操作指南,涵盖从环境准备到集群搭建的每一个关键步骤。 ... [详细]
  • 本文详细探讨了OpenCV中人脸检测算法的实现原理与代码结构。通过分析核心函数和关键步骤,揭示了OpenCV如何高效地进行人脸检测。文章不仅提供了代码示例,还深入解释了算法背后的数学模型和优化技巧,为开发者提供了全面的理解和实用的参考。 ... [详细]
author-avatar
十饮魂
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有