OpenCV基础(20)使用 OpenCV 和 Python 确定 ArUco 标记类型

在本教程中,您将学习如何使用 OpenCV 和 Python 自动确定 ArUco 标记类型/字典。

到目前为止,在本系列中,我们已经学习了如何检测 ArUco 标记;然而,这取决于我们已经知道使用什么类型的 ArUco 字典来生成标记的事实。

这就提出了一个问题:如果您不知道用于生成标记的 ArUco 字典怎么办?如果不知道使用的 ArUco 字典,您将无法在图像/视频中检测到它们。

当这种情况发生时,您需要一种方法来自动确定图像中的 ArUco 标记类型——这正是我今天将向您展示的方法。

1.使用 OpenCV 和 Python 确定 ArUco 标记类型

在本教程的第一部分,您将了解各种类型的 ArUco 标记和 AprilTag。

从那里,您将实现一个 Python 脚本,该脚本可以自动检测图像或视频流中是否存在任何类型的 ArUco 字典,从而允许您可靠地检测 ArUco 标记,即使您不知道使用什么 ArUco 字典生成他们!

2.ArUco 和 AprilTag 标记的类型

OpenCV基础(20)使用 OpenCV 和 Python 确定 ArUco 标记类型_第1张图片
在这个示例图像中,我们有四个 ArUco 标记,但我们不知道使用什么字典来生成它们,那么我们将如何实际检测它们?

当您在开发计算机视觉应用程序而您没有自己生成 ArUco 标记时,可能会出现这种情况。相反,这些标记可能是由其他人或组织生成的(或者您可能只需要一个通用算法来检测图像或视频流中的任何 ArUco 类型)。

当出现这种情况时,您需要能够自动推断出 ArUco 字典类型。

以下代码片段显示了分配给每种类型的标记字典的唯一变量标识符:

# 定义 OpenCV 支持的每个可能的 ArUco 标签的名称
ARUCO_DICT = {
	"DICT_4X4_50": cv2.aruco.DICT_4X4_50,
	"DICT_4X4_100": cv2.aruco.DICT_4X4_100,
	"DICT_4X4_250": cv2.aruco.DICT_4X4_250,
	"DICT_4X4_1000": cv2.aruco.DICT_4X4_1000,
	"DICT_5X5_50": cv2.aruco.DICT_5X5_50,
	"DICT_5X5_100": cv2.aruco.DICT_5X5_100,
	"DICT_5X5_250": cv2.aruco.DICT_5X5_250,
	"DICT_5X5_1000": cv2.aruco.DICT_5X5_1000,
	"DICT_6X6_50": cv2.aruco.DICT_6X6_50,
	"DICT_6X6_100": cv2.aruco.DICT_6X6_100,
	"DICT_6X6_250": cv2.aruco.DICT_6X6_250,
	"DICT_6X6_1000": cv2.aruco.DICT_6X6_1000,
	"DICT_7X7_50": cv2.aruco.DICT_7X7_50,
	"DICT_7X7_100": cv2.aruco.DICT_7X7_100,
	"DICT_7X7_250": cv2.aruco.DICT_7X7_250,
	"DICT_7X7_1000": cv2.aruco.DICT_7X7_1000,
	"DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL,
	"DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5,
	"DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9,
	"DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10,
	"DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11
}

在本教程的其余部分,您将学习如何自动检查输入图像中是否存在任何这些 ArUco 类型。

3.项目结构

OpenCV基础(20)使用 OpenCV 和 Python 确定 ArUco 标记类型_第2张图片
我们今天使用一个 Python 脚本,guess_aruco_type.py。该脚本将检查 images/ 目录中的示例,并且在事先不了解这些图像中的 ArUco 标签的情况下,将自动确定 ArUco 标签类型。

当您的任务是在图像/视频流中查找 ArUco 标签但不确定用于生成这些标签的 ArUco 字典时,这样的脚本非常有用。

4.实现我们的 ArUco/AprilTag 标记类型识别

自动AruCo/AprilTag类型识别实现的方法有点复杂,但我的感觉是,复杂只是在实践中起作用的启发式。

4.1代码实现

链接:https://pan.baidu.com/s/1DmjKL1tVbQX0YkDUzki2Jw 
提取码:123a
# guess_aruco_type.py
#   用法
#  python guess_aruco_type.py --image images/example_01.png
# 导入库
import argparse
import imutils
import cv2

# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the input image containing ArUCo tag")
args = vars(ap.parse_args())

# 定义OpenCV支持的每个可能的ArUCo标签的名称
ARUCO_DICT = {"DICT_4X4_50": cv2.aruco.DICT_4X4_50, "DICT_4X4_100": cv2.aruco.DICT_4X4_100,
              "DICT_4X4_250": cv2.aruco.DICT_4X4_250, "DICT_4X4_1000": cv2.aruco.DICT_4X4_1000,
              "DICT_5X5_50": cv2.aruco.DICT_5X5_50, "DICT_5X5_100": cv2.aruco.DICT_5X5_100,
              "DICT_5X5_250": cv2.aruco.DICT_5X5_250, "DICT_5X5_1000": cv2.aruco.DICT_5X5_1000,
              "DICT_6X6_50": cv2.aruco.DICT_6X6_50, "DICT_6X6_100": cv2.aruco.DICT_6X6_100,
              "DICT_6X6_250": cv2.aruco.DICT_6X6_250, "DICT_6X6_1000": cv2.aruco.DICT_6X6_1000,
              "DICT_7X7_50": cv2.aruco.DICT_7X7_50, "DICT_7X7_100": cv2.aruco.DICT_7X7_100,
              "DICT_7X7_250": cv2.aruco.DICT_7X7_250, "DICT_7X7_1000": cv2.aruco.DICT_7X7_1000,
              "DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL,
              "DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5,
              "DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9,
              "DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10,
              "DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11}

# 从磁盘加载输入图像并调整其大小
print("[INFO] Loading image...")
image = cv2.imread(args["image"])
image = imutils.resize(image, width=800)

# 遍历 ArUCo 字典的类型
for (arucoName, arucoDictionary) in ARUCO_DICT.items():
    # 加载 ArUCo 字典,获取 ArUCo 参数并尝试检测当前字典的标记
    arucoDict = cv2.aruco.Dictionary_get(arucoDictionary)
    arucoParams = cv2.aruco.DetectorParameters_create()
    (corners, ids, rejected) = cv2.aruco.detectMarkers(image, arucoDict, parameters=arucoParams)
    # 如果检测到至少一个 ArUCo 标记,则在终端中显示 ArUCo 标记及其类型名称
    if len(corners) > 0:
        print("[INFO] Detected {} markers for '{}'".format(len(corners), arucoName))

4.2代码解析

# 导入库
import argparse
import imutils
import cv2
# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to input image containing ArUCo tag")
args = vars(ap.parse_args())

我们导入所需的命令行参数,然后解析我们的命令行参数。

这里只需要一个命令行参数,--image,它是我们输入图像的路径。

解析命令行参数后,我们可以继续定义我们的 ARUCO_DICT 字典,它为 OpenCV 支持的每个 ArUco 字典提供名称和唯一变量标识符:

# 定义 OpenCV 支持的每个可能的 ArUco 标签的名称
ARUCO_DICT = {
	"DICT_4X4_50": cv2.aruco.DICT_4X4_50,
	"DICT_4X4_100": cv2.aruco.DICT_4X4_100,
	"DICT_4X4_250": cv2.aruco.DICT_4X4_250,
	"DICT_4X4_1000": cv2.aruco.DICT_4X4_1000,
	"DICT_5X5_50": cv2.aruco.DICT_5X5_50,
	"DICT_5X5_100": cv2.aruco.DICT_5X5_100,
	"DICT_5X5_250": cv2.aruco.DICT_5X5_250,
	"DICT_5X5_1000": cv2.aruco.DICT_5X5_1000,
	"DICT_6X6_50": cv2.aruco.DICT_6X6_50,
	"DICT_6X6_100": cv2.aruco.DICT_6X6_100,
	"DICT_6X6_250": cv2.aruco.DICT_6X6_250,
	"DICT_6X6_1000": cv2.aruco.DICT_6X6_1000,
	"DICT_7X7_50": cv2.aruco.DICT_7X7_50,
	"DICT_7X7_100": cv2.aruco.DICT_7X7_100,
	"DICT_7X7_250": cv2.aruco.DICT_7X7_250,
	"DICT_7X7_1000": cv2.aruco.DICT_7X7_1000,
	"DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL,
	"DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5,
	"DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9,
	"DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10,
	"DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11
}

我们将彻底遍历这个字典,为每个条目加载 ArUco 检测器,然后将检测器应用于我们的输入图像。

如果我们命中了一个特定的标签类型,那么我们就知道ArUco标签存在于图像中。

说到这里,让我们现在实现这个逻辑:

# 从磁盘加载输入图像并调整其大小
print("[INFO] loading image...")
image = cv2.imread(args["image"])
image = imutils.resize(image, width=600)
# 遍历 ArUco 字典的类型
for (arucoName, arucoDict) in ARUCO_DICT.items():
	# 加载 ArUCo 字典,获取 ArUCo 参数,并尝试检测当前字典的标记
	arucoDict = cv2.aruco.Dictionary_get(arucoDict)
	arucoParams = cv2.aruco.DetectorParameters_create()
	(corners, ids, rejected) = cv2.aruco.detectMarkers(
		image, arucoDict, parameters=arucoParams)
	# 如果检测到至少一个 ArUco 标记,则向我们的终端显示 ArUco 名称
	if len(corners) > 0:
		print("[INFO] detected {} markers for '{}'".format(
			len(corners), arucoName))

从磁盘加载我们的输入 --image 并调整其大小。从那里我们循环遍历 OpenCV 支持的所有可能的 ArUco 词典。

对于每个 ArUco 词典,我们:

  • 通过 cv2.aruco.Dictionary_get 加载 arucoDict
  • 实例化 ArUco 检测器参数
  • 应用 cv2.aruco.detectMarkers 检测输入图像中当前 arucoDict 的标签

如果生成的corners列表的长度大于零,我们知道当前的arucoDict已经被用来(潜在地)在我们的输入图像中生成ArUco标签。

在这种情况下,我们将在图像中找到的标记数量以及ArUco字典的名称记录到终端,以便在运行脚本后进行进一步的研究。

4.3ArUco 标记类型识别结果

OpenCV基础(20)使用 OpenCV 和 Python 确定 ArUco 标记类型_第3张图片

$ python guess_aruco_type.py --image images/example_01.png
[INFO] loading image...
[INFO] detected 2 markers for 'DICT_5X5_50'
[INFO] detected 5 markers for 'DICT_5X5_100'
[INFO] detected 5 markers for 'DICT_5X5_250'
[INFO] detected 5 markers for 'DICT_5X5_1000'

rUco 标记属于 5×5 类,ID 分别高达 50、100、250 或 1000。这些结果意味着:

  • 我们知道这些是 5×5 标记。
  • 我们知道在该图像中检测到的标记的 ID >= 50。
  • 但是,如果其他图像中有更多标记,我们可能会遇到值 < 50 的 ArUco 5×5 标记。
  • 如果我们只处理这张图片,那么假设 DICT_5X5_50 是安全的,但是如果我们有更多的图片,请找到适合所有唯一 ID 的最小 ArUco 字典。

让我们尝试另一个示例图像:
OpenCV基础(20)使用 OpenCV 和 Python 确定 ArUco 标记类型_第4张图片

$ python guess_aruco_type.py --image images/example_02.png
[INFO] loading image...
[INFO] detected 1 markers for 'DICT_4X4_50'
[INFO] detected 1 markers for 'DICT_4X4_100'
[INFO] detected 1 markers for 'DICT_4X4_250'
[INFO] detected 1 markers for 'DICT_4X4_1000'
[INFO] detected 4 markers for 'DICT_ARUCO_ORIGINAL'

在这里,您可以看到包含 Pantone颜色匹配卡的示例图像。OpenCV(错误地)认为这些标记可能属于4×4类,但如果放大示例图像,就会发现这不是真的,因为这些实际上是6×6标记,标记周围有一个额外的填充。

此外,由于对于 4×4 类仅检测到一个标记,并且由于图像中总共有四个标记,因此我们可以推断这些一定是 DICT_ARUCO_ORIGINAL。

我们将看最后一张图片,这张图片包含 AprilTags:

$ python guess_aruco_type.py --image images/example_03.png
[INFO] loading image...
[INFO] detected 3 markers for 'DICT_APRILTAG_36h11'

OpenCV基础(20)使用 OpenCV 和 Python 确定 ArUco 标记类型_第5张图片
在这里,OpenCV 可以推断出我们肯定正在查看 AprilTags。

4.4Bonus

# detect_aruco_image_type.py
# 用法
#  python detect_aruco_image_type.py --image images/example_01.png
# 导入库
import argparse
import imutils
import cv2
import sys

# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the input image containing ArUCo tag")
args = vars(ap.parse_args())

# 定义OpenCV支持的每个可能的ArUCo标签的名称
ARUCO_DICT = {"DICT_4X4_50": cv2.aruco.DICT_4X4_50, "DICT_4X4_100": cv2.aruco.DICT_4X4_100,
              "DICT_4X4_250": cv2.aruco.DICT_4X4_250, "DICT_4X4_1000": cv2.aruco.DICT_4X4_1000,
              "DICT_5X5_50": cv2.aruco.DICT_5X5_50, "DICT_5X5_100": cv2.aruco.DICT_5X5_100,
              "DICT_5X5_250": cv2.aruco.DICT_5X5_250, "DICT_5X5_1000": cv2.aruco.DICT_5X5_1000,
              "DICT_6X6_50": cv2.aruco.DICT_6X6_50, "DICT_6X6_100": cv2.aruco.DICT_6X6_100,
              "DICT_6X6_250": cv2.aruco.DICT_6X6_250, "DICT_6X6_1000": cv2.aruco.DICT_6X6_1000,
              "DICT_7X7_50": cv2.aruco.DICT_7X7_50, "DICT_7X7_100": cv2.aruco.DICT_7X7_100,
              "DICT_7X7_250": cv2.aruco.DICT_7X7_250, "DICT_7X7_1000": cv2.aruco.DICT_7X7_1000,
              "DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL,
              "DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5,
              "DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9,
              "DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10,
              "DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11}

# 从磁盘加载输入图像并调整其大小
print("[INFO] Loading image...")
image = cv2.imread(args["image"])
image = imutils.resize(image, width=800)

# 循环ArUCo字典的类型
for (arucoName, arucoDictionary) in ARUCO_DICT.items():
    # 加载 ArUCo 字典,获取 ArUCo 参数并尝试检测当前字典的标记
    arucoDict = cv2.aruco.Dictionary_get(arucoDictionary)
    arucoParams = cv2.aruco.DetectorParameters_create()
    (corners, ids, rejected) = cv2.aruco.detectMarkers(image, arucoDict, parameters=arucoParams)
    # 如果检测到至少一个 ArUCo 标记,则在终端中显示 ArUCo 标记及其类型名称
    if len(corners) > 0:
        print("[INFO] Detected {} markers for '{}'".format(len(corners), arucoName))
        # 展平 ArUCo ID 列表
        IDS = ids.flatten()
        # 循环检测到的 ArUCo corners
        for (markerCorner, markerID) in zip(corners, IDS):
            # 提取始终按​​以下顺序返回的标记corners:
            # TOP-LEFT, TOP-RIGHT, BOTTOM-RIGHT, BOTTOM-LEFT
            corners = markerCorner.reshape((4, 2))
            (topLeft, topRight, bottomRight, bottomLeft) = corners
            # 将每个 (x, y) 坐标对转换为整数
            topRight = (int(topRight[0]), int(topRight[1]))
            bottomRight = (int(bottomRight[0]), int(bottomRight[1]))
            bottomLeft = (int(bottomLeft[0]), int(bottomLeft[1]))
            topLeft = (int(topLeft[0]), int(topLeft[1]))
            # 绘制ArUCo检测的边界框
            cv2.line(image, topLeft, topRight, (0, 255, 0), 2)
            cv2.line(image, topRight, bottomRight, (0, 255, 0), 2)
            cv2.line(image, bottomRight, bottomLeft, (0, 255, 0), 2)
            cv2.line(image, bottomLeft, topLeft, (0, 255, 0), 2)
            # 计算并绘制 ArUCo 标记的中心 (x, y) 坐标
            cX = int((topLeft[0] + bottomRight[0]) / 2.0)
            cY = int((topLeft[1] + bottomRight[1]) / 2.0)
            cv2.circle(image, (cX, cY), 4, (0, 0, 255), -1)
            # Get marker type name
            markerType = "{} -> {}".format(markerID, arucoName)
            # 获取标记类型名称
            cv2.putText(image, str(markerType), (topLeft[0], topLeft[1] - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                        (0, 255, 0), 2)
            print("[INFO] ArUco marker ID: {}".format(markerID))
            # 显示输出图像
            cv2.imshow("Image", image)
            cv2.waitKey(0)

5.总结

在本教程中,您学习了如何自动确定 ArUco 标记类型,即使您不知道最初使用的是什么 ArUco 字典!

我们的方法有点小技巧,因为它要求我们彻底遍历所有可能的 ArUco 词典,然后尝试检测输入图像中的特定 ArUco 词典。

参考目录

https://www.pyimagesearch.com/2020/12/28/determining-aruco-marker-type-with-opencv-and-python/

你可能感兴趣的:(OpenCV,opencv,python)