在 OpenCV 中使用 ChArUco 棋盘

news2025/1/10 23:57:27

TLDR:目前在线的许多 Charuco 示例代码都已过时,并且会让你出错。如果 ChatGPT 或 Stack Exchange 建议你使用:cv2.aruco.CharucoBoard_create(length, width, ...)你会发现正确的语法是 cv2.aruco.CharucoBoard((length, width), ...)。希望下面的示例函数能有所帮助。

如果你之前使用过OpenCV,你可能熟悉我们用于校准相机的经典棋盘格。你可能也熟悉Aruco标记,它们用于机器人应用中的姿态估计。这些标记带有唯一标识符,类似于棋盘格,我们可以使用其角点来确定其坐标框架。Charuco棋盘是将它们结合到一个棋盘上的有用方式,如图所示。

这使我们能够唯一地识别棋盘的每个角。识别唯一的角点有两个原因:

  • 棋盘的一部分被遮挡时无法检测到,但Charuco棋盘不受此影响。

  • 识别角点可以帮助我们更准确地描述棋盘的坐标框架。

自从一些更新的 OpenCV 版本(撰写本文时最新版本为 4.8.0)以来,Aruco 库发生了一些相当重大的变化 - 目前你在网上发现的大部分代码已经过时。希望本指南将提供更新的演练,并向你展示我如何使用 Charuco 棋盘进行图像校正和姿势估计(对于标准棋盘来说这是一项艰巨的任务)。

确保你正在运行最新版本的Python,还有OpenCV的contrib版本。这一步非常重要,因为它为我们提供了正确的库:

pip install opencv-contrib-python

使用“contrib”版本来访问该库非常重要,并确保不要安装无界面版本(它会阻止 GUI 出现)。如果你在尝试运行任何 Aruco 或 ChAruco 库函数时遇到错误,则可能是你的安装出现错误。

接下来,你可以开始编写脚本。

创建Charuco棋盘

最好的起点是为自己创建一个 Charuco 棋盘。以下脚本将帮助你根据你的要求创建一个棋盘。该图像将显示两秒钟,然后保存到当前文件夹中。

import os
import numpy as np
import cv2

# ------------------------------
# ENTER YOUR PARAMETERS HERE:
ARUCO_DICT = cv2.aruco.DICT_6X6_250
SQUARES_VERTICALLY = 7
SQUARES_HORIZONTALLY = 5
SQUARE_LENGTH = 0.03
MARKER_LENGTH = 0.015
LENGTH_PX = 640   # total length of the page in pixels
MARGIN_PX = 20    # size of the margin in pixels
SAVE_NAME = 'ChArUco_Marker.png'
# ------------------------------

def create_and_save_new_board():
    dictionary = cv2.aruco.getPredefinedDictionary(ARUCO_DICT)
    board = cv2.aruco.CharucoBoard((SQUARES_VERTICALLY, SQUARES_HORIZONTALLY), SQUARE_LENGTH, MARKER_LENGTH, dictionary)
    size_ratio = SQUARES_HORIZONTALLY / SQUARES_VERTICALLY
    img = cv2.aruco.CharucoBoard.generateImage(board, (LENGTH_PX, int(LENGTH_PX*size_ratio)), marginSize=MARGIN_PX)
    cv2.imshow("img", img)
    cv2.waitKey(2000)
    cv2.imwrite(SAVE_NAME, img)

create_and_save_new_board()

我们可以指定棋盘中有多少行和列,以及方块和标记的大小(文档指定它应该以米为单位,尽管在这个示例中,我们实际上只关心两者的比例,因为我们指定了图像的大小)。在上面的函数中:

  • Dictionary表示所使用的 Aruco 标记的字典,

  • Board是 Charuco 对象,

  • img是棋盘的绘图(cv Image 对象)。

81239865a06b248da27fb58e0e79702b.jpeg

棋盘由上面的函数组成。

使用棋盘进行相机校准

对于此步骤,你将打印出刚刚保存的图像,并为其拍照(可能附着在某个表面上)。对于相机校准,你需要从多个角度获得至少 10 张不同的棋盘图像。这是因为校准是为了校正径向和切向镜头畸变,并将我们的图像转换为“理想针孔”相机模型。它通过解决一个优化问题来最小化图像点与预期世界点之间的最小二乘投影误差,因此更多的图像意味着更多的数据点用于优化。

如果你将所有图像都放在一个文件夹中,你可以尝试使用下面的脚本对图像运行校准并保存结果:

# ------------------------------
# ENTER YOUR REQUIREMENTS HERE:
ARUCO_DICT = cv2.aruco.DICT_6X6_250
SQUARES_VERTICALLY = 7
SQUARES_HORIZONTALLY = 5
SQUARE_LENGTH = 0.03
MARKER_LENGTH = 0.015
# ...
PATH_TO_YOUR_IMAGES = '/Users/Ed/Downloads/Calibration_Images'
# ------------------------------

def calibrate_and_save_parameters():
    # Define the aruco dictionary and charuco board
    dictionary = cv2.aruco.getPredefinedDictionary(ARUCO_DICT)
    board = cv2.aruco.CharucoBoard((SQUARES_VERTICALLY, SQUARES_HORIZONTALLY), SQUARE_LENGTH, MARKER_LENGTH, dictionary)
    params = cv2.aruco.DetectorParameters()

    # Load PNG images from folder
    image_files = [os.path.join(PATH_TO_YOUR_IMAGES, f) for f in os.listdir(PATH_TO_YOUR_IMAGES) if f.endswith(".png")]
    image_files.sort()  # Ensure files are in order

    all_charuco_corners = []
    all_charuco_ids = []

    for image_file in image_files:
        image = cv2.imread(image_file)
        image_copy = image.copy()
        marker_corners, marker_ids, _ = cv2.aruco.detectMarkers(image, dictionary, parameters=params)

        # If at least one marker is detected
        if len(marker_ids) > 0:
            cv2.aruco.drawDetectedMarkers(image_copy, marker_corners, marker_ids)
            charuco_retval, charuco_corners, charuco_ids = cv2.aruco.interpolateCornersCharuco(marker_corners, marker_ids, image, board)
            if charuco_retval:
                all_charuco_corners.append(charuco_corners)
                all_charuco_ids.append(charuco_ids)

    # Calibrate camera
    retval, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.aruco.calibrateCameraCharuco(all_charuco_corners, all_charuco_ids, board, image.shape[:2], None, None)

    # Save calibration data
    np.save('camera_matrix.npy', camera_matrix)
    np.save('dist_coeffs.npy', dist_coeffs)

    # Iterate through displaying all the images
    for image_file in image_files:
        image = cv2.imread(image_file)
        undistorted_image = cv2.undistort(image, camera_matrix, dist_coeffs)
        cv2.imshow('Undistorted Image', undistorted_image)
        cv2.waitKey(0)

    cv2.destroyAllWindows()

calibrate_and_save_parameters()

这个函数的主要作用是相机校准,下面是函数的主要步骤:

  • 首先,你还是需要创建你的字典(用于 Aruco 标记)和棋盘(理想棋盘),同时创建一个 params 对象,它可以用于修改检测标记的方式(例如,你可以修改阈值处理等参数)。

  • 对于每张图像,检测标记,根据预期的标记和搜索参数来进行检测。这一步是为了找到图像中的Aruco标记。

  • 关键的步骤是 cv2.aruco.interpolateCornersCharuco,它用于找到棋盘格的角点,并将相应的Aruco标识与这些角点进行匹配。这一步是为了将图像中检测到的Aruco标记与棋盘格的角点进行关联。

  • 一旦找到并识别了角点,这些角点将被收集并加载到一个函数中,用于后续的校准过程。

da4ce3a5b259b6c181971950f1b60a12.jpeg

你可以看到图像的角现在已经扭曲,以解决镜头畸变的问题。

使用棋盘获取姿态信息

这是Charuco棋盘的一个非常有用的部分 — 我们可以充分利用棋盘的校准能力和Aruco标记的姿态估计能力。

当然,对于姿态估计,我们只需要一张图像(或者如果源是视频,则是一帧图像)。下面的detect_pose函数接受一张单独的图像,但我已经添加了一个主函数,用于迭代所有我们的校准图像。希望这段代码相对容易理解。

def detect_pose(image, camera_matrix, dist_coeffs):
    # Undistort the image
    undistorted_image = cv2.undistort(image, camera_matrix, dist_coeffs)

    # Define the aruco dictionary and charuco board
    dictionary = cv2.aruco.getPredefinedDictionary(ARUCO_DICT)
    board = cv2.aruco.CharucoBoard((SQUARES_VERTICALLY, SQUARES_HORIZONTALLY), SQUARE_LENGTH, MARKER_LENGTH, dictionary)
    params = cv2.aruco.DetectorParameters()

    # Detect markers in the undistorted image
    marker_corners, marker_ids, _ = cv2.aruco.detectMarkers(undistorted_image, dictionary, parameters=params)

    # If at least one marker is detected
    if len(marker_ids) > 0:
        # Interpolate CharUco corners
        charuco_retval, charuco_corners, charuco_ids = cv2.aruco.interpolateCornersCharuco(marker_corners, marker_ids, undistorted_image, board)

        # If enough corners are found, estimate the pose
        if charuco_retval:
            retval, rvec, tvec = cv2.aruco.estimatePoseCharucoBoard(charuco_corners, charuco_ids, board, camera_matrix, dist_coeffs, None, None)

            # If pose estimation is successful, draw the axis
            if retval:
                cv2.drawFrameAxes(undistorted_image, camera_matrix, dist_coeffs, rvec, tvec, length=0.1, thickness=15)
    return undistorted_image


def main():
    # Load calibration data
    camera_matrix = np.load['camera_matrix.npy']
    dist_coeffs = np.load['dist_coeffs.npy']

    # Iterate through PNG images in the folder
    image_files = [os.path.join(PATH_TO_YOUR_IMAGES, f) for f in os.listdir(PATH_TO_YOUR_IMAGES) if f.endswith(".png")]
    image_files.sort()  # Ensure files are in order

    for image_file in image_files:
        # Load an image
        image = cv2.imread(image_file)

        # Detect pose and draw axis
        pose_image = detect_pose(image, camera_matrix, dist_coeffs)

        # Show the image
        cv2.imshow('Pose Image', pose_image)
        cv2.waitKey(0)

main()

这应该允许你获取棋盘的位姿(你可以使用它来定位机器人、检测平面坐标系等)。

808365ee115f51a23d36fbae23be1de7.jpeg

坐标系相对于原始图像的“左上角”。

☆ END ☆

如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 woshicver」,每日朋友圈更新一篇高质量博文。

扫描二维码添加小编↓

cb44959641d5b64acb86183a1ad4e3a3.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1232442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vs code git问题:文件明明已加入忽略文件中,还是出现

vs code git问题:文件明明已加入忽略文件中,还是出现 原因: 因为之前这些文件都已经提交过,线上GIT已经存在,已存在就不能忽略, 解决办法: 先要删除这些文件提交上去,然后把这些文…

【Rust 日报】2023-11-19 solars:可视化太阳系

eyre 0.6.9发布 Eyre是一个可定制的应用程序错误报告库,通过诸如tracing等集成,允许进行可配置的格式化和上下文聚合。本次更新如下。 组织一个由共同决策驱动的异步维护团队。添加一个贡献指南。修复在丢弃已抹除的错误报告时发生的堆叠借用违规。修复由…

Texpad所见即所得

Texpad所见即所得 对于Latex编译器此前常用的都是overleaf,但是当tex文件过大时overleaf编译一次需要的时间有些漫长,当tex文件过大时在编译上消耗的时间成本过大,此外overleaf还时常断开连接。 Texpad for Mac 这是Mac上一款十分好用的La…

SVG圆形 <circle>,椭圆形 <ellipse>的示例代码

本专栏是汇集了一些HTML常常被遗忘的知识,这里算是温故而知新,往往这些零碎的知识点,在你开发中能起到炸惊效果。我们每个人都没有过目不忘,过久不忘的本事,就让这一点点知识慢慢渗透你的脑海。 本专栏的风格是力求简洁…

虚拟化逻辑架构: 创建KVM中的VM与实现VNC远程登录

目录 一、实验 1.安装KVM环境管理工具并创建VM(虚拟机) 2.Windows使用VNC Viewer连接KVM中的VM(虚拟机) 二、问题 1.如何下载安装VNC Viewer 一、实验 1.安装KVM环境管理工具并创建VM(虚拟机) (1) 采…

git撤销某一次commit提交

一:撤销上一次commit提交,但不删除修改的代码 可以使用使用VSCode 二:使用 git reset --hard命令删除提交时,将会删除该提交及其之后的所有更改(相当于你想要回滚到的提交的提交ID) git reset --hard 版本…

24 - 内存持续上升,我该如何排查问题?

我想你肯定遇到过内存溢出,或是内存使用率过高的问题。碰到内存持续上升的情况,其实我们很难从业务日志中查看到具体的问题,那么面对多个进程以及大量业务线程,我们该如何精准地找到背后的原因呢? 1、常用的监控和诊断…

【数据结构】C语言实现带头双向循环链表万字详解(附完整运行代码)

🦄个人主页:修修修也 🎏所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 一.了解项目功能 在本次项目中我们的目标是实现一个带头双向循环链表: 该带头双向循环链表使用动态内存分配空间,可以用来存储任意数量的同类型数据. 带头双向循环链表结点(No…

【数据库】你听说过矢量数据库吗?

个人主页:【😊个人主页】 系列专栏:【❤️其他领域】 文章目录 前言什么是向量/矢量数据库嵌入模型使用向量数据库的优势与传统数据库的对比其他方面 AWS 如何支持您的矢量数据库需求?Amazon OpenSearch ServiceAmazon Aurora Pos…

【前端学java】java中的Object类(8)

往期回顾: 【前端学java】JAVA开发的依赖安装与环境配置 (0)【前端学 java】java的基础语法(1)【前端学java】JAVA中的packge与import(2)【前端学java】面向对象编程基础-类的使用 &#xff08…

vscode 设置vue3 通用页面模板

实现效果&#xff1a; 实现步骤&#xff1a; 1.在项目的 .vscode 目录下创建一个名为 vue3.2.code-snippets 的文件&#xff0c;它是一个 JSON 格式的代码片段文件 {"Vue3.2快速生成模板": {"prefix": "Vue3.2","body": ["<…

51单片机应用

目录 ​编辑 1. C51的数据类型 1.1 C51中的基本数据类型 1.2 特殊功能寄存器类型 2. C51的变量 2.1 存储种类 1. C51的数据类型 C51是一种基于8051架构的单片机&#xff0c;它支持以下基本数据类型&#xff1a; 位&#xff08;Bit&#xff09;&#xff1a;可以表…

【数据结构】栈详解

Hello everybody!今天给大家讲讲数据结构中一个比较重要的知识&#xff1a;栈。希望宝子们在看过这篇文章后能够有所收获&#xff01; 1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端…

el-tree 与table表格联动

html部分 <div class"org-left"><el-input v-model"filterText" placeholder"" size"default" /><el-tree ref"treeRef" class"filter-tree" :data"treeData" :props"defaultProp…

c语言从入门到实战——回调函数与qsort的讲解和模拟实现

回调函数与qsort的讲解和模拟实现 前言1. 回调函数是什么&#xff1f;2. qsort2.1 使用qsort函数排序整型数据2.2 使用qsort排序结构数据 3. qsort函数的模拟实现 前言 回调函数是一个函数&#xff0c;它作为参数传递给另一个函数&#xff0c;并且能够在该函数内部被调用。在C…

优秀智慧园区案例 - 佛山美的工业城零碳智慧园区,先进智慧园区建设方案经验

一、项目背景 美的工业园区西区最早建于上世纪90年代&#xff0c;到现在已经过去近30年&#xff0c;而这三十年恰恰是信息科技大发展的30年&#xff0c;原有的生产办公条件已不能很好的承载新时期办公和参观接待的需求。所以在21年美的楼宇科技事业部决定对原来的园区进行改造…

工具及方法 - 多邻国: Duolingo

网站&#xff1a;Duolingo 有iOS和Android应用&#xff0c;在App Store和Google Play上都能下载。也可以使用网页版。我就在iOS上安装了付费版&#xff0c;为了小朋友学习英语&#xff0c;一年的费用&#xffe5;588。 目前学习中的课程是英语、日语和粤语。英语是小学课程&a…

Linux shell编程学习笔记28:脚本调试 set命令

0 引入 在Linux Shell 脚本编程的过程中&#xff0c;编写简单功能的脚本&#xff0c;代码不多&#xff0c;一般阅读起来没什么难度&#xff0c;有问题也比较有查出原因和修正。但是当脚本要实现的功能较多&#xff0c;代码变得较为复杂时&#xff0c;阅读起来就不那么容易看明…

macos苹果电脑清理软件有哪些?cleanmymac和腾讯柠檬哪个好

MacOS是一款优秀的操作系统&#xff0c;但是随着使用时间的增加&#xff0c;它也会产生一些不必要的垃圾文件&#xff0c;占用磁盘空间和内存资源&#xff0c;影响系统的性能和稳定性。为了保持MacOS的清洁和高效&#xff0c;我们需要使用一些专业的清理软件来定期扫描和清除这…

深入探索 PaddlePaddle 中的计算图

**引言** 计算图是深度学习平台 PaddlePaddle 的核心组件之一&#xff0c;它提供了一种图形化的方式来表示和执行深度学习模型。通过了解和理解 PaddlePaddle 中的计算图&#xff0c;我们可以更好地理解深度学习的工作原理&#xff0c;并且能够更加灵活和高效地构建和训练复杂…