OpenCV基础(19)使用 OpenCV 和 Python 检测 ArUco 标记

2023-05-16

在本教程中,您将学习如何使用 OpenCV 和 Python 检测图像和实时视频流中的 ArUco 标记。

1.使用 OpenCV 和 Python 检测 ArUco 标记

在本教程的第一部分,您将了解 OpenCV 的 cv2.aruco 模块以及如何通过以下方式检测图像和实时视频流中的 ArUco 标记:

  • 1.指定您的 ArUco 字典
  • 2.为 ArUco 检测器创建参数(通常只是使用默认值的一行代码)
  • 3.应用 cv2.aruco.detectMarkers 来检测图像或视频流中的 ArUco 标记

我们将查看我们的项目目录结构并实现两个 Python 脚本:

  • 一种用于检测图像中 ArUco 标记的 Python 脚本
  • 以及另一个用于检测实时视频流中的 ArUco 标记的 Python 脚本

2.OpenCV ArUCo 标记检测

在这里插入图片描述
OpenCV 库带有内置的 ArUco 支持,用于生成 ArUco 标记和检测它们。

使用 OpenCV 检测 ArUco 标记是通过 cv2.aruco 子模块三步过程实现的:

  • 第 1 步:使用 cv2.aruco.Dictionary_get 函数获取我们正在使用的 ArUco 标记字典。
  • 第 2 步:使用 cv2.aruco.DetectorParameters_create 定义 ArUco 检测参数。
  • 第 3 步:通过 cv2.aruco.detectMarkers 函数执行 ArUco 标记检测。

对我们来说最重要的是,我们需要学习如何使用 detectMarkers 函数。

3.理解cv2.aruco.detectMarkers函数

我们仅用 3-4 行代码定义一个 ArUco 标记检测程序:

arucoDict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_6X6_50)
arucoParams = cv2.aruco.DetectorParameters_create()
(corners, ids, rejected) = cv2.aruco.detectMarkers(image, arucoDict,
	parameters=arucoParams)

cv2.aruco.detectMarkers 函数接受三个参数:

  • image:我们要在其中检测 ArUco 标记的输入图像
  • arucoDict:我们正在使用的 ArUco 词典
  • parameters:用于检测的ArUco参数(除非你有充分的理由修改参数,cv2.aruco.DetectorParameters_create返回的默认参数通常就足够了)

应用 ArUco 标签检测后, cv2.aruco.detectMarkers 方法返回三个值:

  • corners:包含我们检测到的 ArUco 标记的 (x, y) 坐标的列表
  • ids:检测到的标记的 ArUco ID
  • rejected:已找到但由于无法解析标记的内部代码而最终被拒绝的潜在标记列表(可视化被拒绝的标记对于调试目的通常很有用)

在本文后面,您将看到如何使用 cv2.aruco.detectMarkers 函数检测图像和实时视频流中的 ArUco 标记。

4.项目结构

在我们学习如何检测图像中的 ArUco 标签之前,让我们先回顾一下我们的项目目录结构,以便您对我们的项目是如何组织的以及我们将使用哪些 Python 脚本有一个很好的了解。
在这里插入图片描述
今天我们将回顾两个 Python 脚本:

  • detect_aruco_image.py:检测图像中的 ArUco 标签。我们将应用此脚本的示例图像放在 images/ 目录中。
  • detect_aruco_video.py:将 ArUco 检测应用于实时视频流。

5.使用 OpenCV 检测图像中的 ArUco 标记

5.1代码实现

链接:https://pan.baidu.com/s/1bEPuiix0MrtL7Fu3paoRug 
提取码:123a
# detect_aruco_image.py
#   用法
# python detect_aruco_image.py --image images/example_01.png --type DICT_5X5_100
# 导入库
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 the ArUCo tag")
ap.add_argument("-t", "--type", type=str, default="DICT_ARUCO_ORIGINAL", help="Tpe of ArUCo tag to detect")
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=600)

# 验证 OpenCV 是否支持提供的 ArUCo 标签
if ARUCO_DICT.get(args["type"], None) is None:
    print("[INFO] ArUCo tag of '{}' is not supported!".format(args["type"]))
    sys.exit(0)

# 加载 ArUCo 字典,抓取 ArUCo 参数并检测标记
print("[INFO] Detecting '{}' tags...".format(args["type"]))
arucoDict = cv2.aruco.Dictionary_get(ARUCO_DICT[args["type"]])
arucoParams = cv2.aruco.DetectorParameters_create()
(corners, ids, rejected) = cv2.aruco.detectMarkers(image, arucoDict, parameters=arucoParams)

# 验证至少一个 ArUCo 标记被检测到
if len(corners) > 0:
    # 展平 ArUCo ID 列表
    ids = ids.flatten()
    # 循环检测到的 ArUCo 标记
    for (markerCorner, markerID) in zip(corners, ids):
        # 提取始终按​​以下顺序返回的标记:
        # 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)
        # 在图像上绘制 ArUco 标记 ID
        cv2.putText(image, str(markerID), (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.2代码解析

打开项目目录中的 detect_aruco_image.py 文件,让我们开始工作:

# 导入必要的包
import argparse
import imutils
import cv2
import sys

我们首先导入所需的 Python 包。

我们将使用 argparse 来解析我们的命令行参数,使用 imutils 来调整图像大小,使用 cv2 来预处理图像,以及在我们需要提前退出脚本的情况下使用 sys

接下来是我们的命令行参数:

# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to input image containing ArUCo tag")
ap.add_argument("-t", "--type", type=str,
	default="DICT_ARUCO_ORIGINAL",
	help="type of ArUCo tag to detect")
args = vars(ap.parse_args())

我们有两个命令行参数需要解析:

  • image:包含我们想要检测的任何 ArUco 标签的输入图像的路径
  • type:我们将检测的 ArUco 标签类型

正确设置 --type 参数对于成功检测输入图像中的 ArUco 标签绝对至关重要。

我们在此处提供的 --type 参数必须与用于在输入图像中生成标签的 ArUco 类型相同。如果使用一种类型生成 ArUco 标签,然后在尝试检测它们时使用不同的类型,则检测将失败,最终检测到的 ArUco 标签为零。

因此,您必须绝对确定用于生成 ArUco 标签的类型与您用于检测阶段的类型相同。

注意:不知道您使用什么 ArUco 字典来生成输入图像中的标签?别担心,我已经帮你解决了。下周我将向您展示我个人武器库中的一个 Python 脚本,当我无法识别给定的 ArUco 标签是什么类型时,我会使用它。该脚本自动识别 ArUco 标签类型。

接下来是我们的 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 标签类型的名称)。然后键映射到值,这是 OpenCV 的 ArUco 标签类型的唯一标识符。

使用这个字典,我们可以获取输入的 --type 命令行参数,通过 ARUCO_DICT 传递它,然后获取 ArUco 标签类型的唯一标识符。

以下 Python shell 块向您展示了如何执行此查找操作的简单示例:

>>> print(args)
{'type': 'DICT_5X5_100'}
>>> arucoType = ARUCO_DICT[args["type"]]
>>> print(arucoType)
5
>>> 5 == cv2.aruco.DICT_5X5_100
True
>>> 

接下来,让我们继续从磁盘加载输入图像:

# 从磁盘加载输入图像并调整其大小
print("[INFO] loading image...")
image = cv2.imread(args["image"])
image = imutils.resize(image, width=600)
# 验证提供的 ArUCo 标签是否存在并且受 OpenCV 支持
if ARUCO_DICT.get(args["type"], None) is None:
	print("[INFO] ArUCo tag of '{}' is not supported".format(
		args["type"]))
	sys.exit(0)
# 加载 ArUCo 字典,获取 ArUCo 参数,并检测标记
print("[INFO] detecting '{}' tags...".format(args["type"]))
arucoDict = cv2.aruco.Dictionary_get(ARUCO_DICT[args["type"]])
arucoParams = cv2.aruco.DetectorParameters_create()
(corners, ids, rejected) = cv2.aruco.detectMarkers(image, arucoDict,
	parameters=arucoParams)

加载我们的输入图像,然后将其调整为 600 像素的宽度(这样图像可以很容易地适合我们的屏幕)。

如果你有一个高分辨率的输入图像,带有小的 ArUco 标签,你可能需要调整这个调整大小的操作;否则,在调整大小操作后,ArUco 标签可能太小而无法检测到。

检查 ARUCO_DICT 中是否存在 ArUco --type 名称。如果没有,那么我们退出脚本,因为我们没有可用于提供的 --type 的 ArUco 字典。

否则,我们:

  • 使用 --typeARUCO_DICT 加载 ArUco 字典
  • 实例化我们的 ArUco 探测器参数
  • 使用 cv2.aruco.detectMarkers 函数应用 ArUco 检测

cv2.aruco.detectMarkers 结果为三元组:

  • corners:我们检测到的 ArUco 标记的 (x, y) 坐标
  • ids:ArUco 标记的标识符(即标记本身中编码的 ID)
  • rejected:检测到但最终由于标记内的代码无法解析而被拒绝的潜在标记列表

现在让我们开始可视化我们检测到的 ArUco 标记:

# 验证*至少*一个 ArUco 标记被检测到
if len(corners) > 0:
	# 展平 ArUco ID 列表
	ids = ids.flatten()
	# 在检测到的 ArUCo 角上循环
	for (markerCorner, markerID) in zip(corners, ids):
		# 提取标记角(始终按左上角、右上角、右下角和左下角的顺序返回)
		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 ids 列表,然后将每个cornersids 一起循环。每个markerCorner由四个 (x, y) 坐标列表表示。这些 (x, y) 坐标表示 ArUco 标签的左上角、右上角、右下角和左下角。此外,(x, y) 坐标始终按该顺序返回。

topRight、bottomRight、bottomLeft 和 topLeft 变量是 NumPy 数组;然而,我们需要将它们转换为整数值 (int),以便我们可以使用 OpenCV 的绘图函数来可视化图像上的标记。

使用整数类型的标记 (x, y) 坐标,我们可以在图像上绘制它们:

		# 绘制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)
		# 在图像上绘制 ArUco 标记 ID
		cv2.putText(image, str(markerID),
			(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)

在我们的图像上,使用 cv2.line 绘制 ArUco 标签的边界框。然后我们计算 ArUco 标记的中心 (x, y) 坐标并通过调用 cv2.circle 在图像上绘制中心。我们最后的可视化步骤是在图像上绘制标记 ID 并将其打印到我们的终端。

5.3OpenCV ArUco 标记检测结果

在这里插入图片描述
在这里插入图片描述
使 用 O p e n C V 检 测 输 入 图 像 中 的 A r U c o 标 签 使用 OpenCV 检测输入图像中的 ArUco 标签 使OpenCVArUco

6.使用 OpenCV 检测实时视频流中的 ArUco 标记

6.1代码实现

# detect_aruco_video.py
# 用法
# python detect_aruco_video.py
# 导入库
from imutils.video import VideoStream
import argparse
import imutils
import time
import cv2
import sys

# 构建参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-t", "--type", type=str, default="DICT_ARUCO_ORIGINAL", help="Type of ArUCo tag to detect")
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}

# 验证提供的 ArUCo 标签是否存在并且受 OpenCV 支持
if ARUCO_DICT.get(args["type"], None) is None:
    print("[INFO] ArUCo tag of '{}' is not supported".format(args["type"]))
    sys.exit(0)

# 加载 ArUCo 字典并获取 ArUCo 参数
print("[INFO] Detecting '{}' tags...".format(args["type"]))
arucoDict = cv2.aruco.Dictionary_get(ARUCO_DICT[args["type"]])
arucoParams = cv2.aruco.DetectorParameters_create()

# 初始化视频流并让相机预热
print("[INFO] Starting video stream...")
vs = VideoStream(src=0).start()
time.sleep(2.0)

# 循环视频流中的帧
while True:
    # 从线程视频流中抓取帧并将其调整为最大宽度为 600 像素
    frame = vs.read()
    frame = imutils.resize(frame, width=1000)
    # 检测输入帧中的 ArUco 标记
    (corners, ids, rejected) = cv2.aruco.detectMarkers(frame, arucoDict, parameters=arucoParams)
    # 验证*至少*一个 ArUco 标记被检测到
    if len(corners) > 0:
        # 展平 ArUco ID 列表
        ids = ids.flatten()
        # 循环检测到的 ArUCo 角
        for (markerCorner, markerID) in zip(corners, ids):
            # 提取标记角(始终按左上角、右上角、右下角和左下角顺序返回)
            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(frame, topLeft, topRight, (0, 255, 0), 2)
            cv2.line(frame, topRight, bottomRight, (0, 255, 0), 2)
            cv2.line(frame, bottomRight, bottomLeft, (0, 255, 0), 2)
            cv2.line(frame, 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(frame, (cX, cY), 4, (0, 0, 255), -1)
            # 在框架上绘制 ArUco 标记 ID
            cv2.putText(frame, str(markerID), (topLeft[0], topLeft[1] - 15),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
    # 显示输出帧
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF
    # 如果按下了 `q` 键,则中断循环
    if key == ord("q"):
        break

# 做一些清理
cv2.destroyAllWindows()
vs.stop()

6.2代码讲解

# 导入包
from imutils.video import VideoStream
import argparse
import imutils
import time
import cv2
import sys

导入我们需要的 Python 包。这些导入与我们之前的脚本相同,但有两个例外:

  • VideoStream:用于访问我们的网络摄像头
  • time:插入一个小的延迟,让我们的相机传感器预热

现在让我们解析我们的命令行参数:

# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-t", "--type", type=str,
	default="DICT_ARUCO_ORIGINAL",
	help="type of ArUCo tag to detect")
args = vars(ap.parse_args())

我们在这里只需要一个命令行参数 --type,它是我们要在视频流中检测的 ArUco 标签的类型。接下来我们定义 ARUCO_DICT,用于将 --type 映射到 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 标签是否存在并且受 OpenCV 支持
if ARUCO_DICT.get(args["type"], None) is None:
	print("[INFO] ArUCo tag of '{}' is not supported".format(
		args["type"]))
	sys.exit(0)
# 加载 ArUCo 字典并获取 ArUCo 参数
print("[INFO] detecting '{}' tags...".format(args["type"]))
arucoDict = cv2.aruco.Dictionary_get(ARUCO_DICT[args["type"]])
arucoParams = cv2.aruco.DetectorParameters_create()
# 初始化视频流并允许相机传感器预热
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start()
time.sleep(2.0)

检查我们的 ARUCO_DICT 中是否存在 ArUco 标签 --type。如果没有,我们退出脚本。否则,我们加载 arucoDict 并获取检测器的 arucoParams。我们启动我们的 VideoStream 并让我们的相机传感器预热。

我们现在准备从我们的视频流中循环帧:

# 循环来自视频流的帧
while True:
	# 从线程视频流中抓取帧并将其调整为最大宽度为 1000 像素
	frame = vs.read()
	frame = imutils.resize(frame, width=1000)
	# 检测输入帧中的 ArUco 标记
	(corners, ids, rejected) = cv2.aruco.detectMarkers(frame,
		arucoDict, parameters=arucoParams)

从我们的视频流中抓取一个帧,然后我们将其调整为 1000 像素的宽度。然后我们应用 cv2.aruco.detectMarkers 函数来检测当前帧中的 ArUco 标签。

现在让我们解析 ArUco 标签检测的结果:

	# 验证*至少*一个 ArUco 标记被检测到
	if len(corners) > 0:
		# 展平 ArUco ID 列表
		ids = ids.flatten()
		# 在检测到的 ArUCo 角上循环
		for (markerCorner, markerID) in zip(corners, ids):
			# 提取标记角(始终按左上角、右上角、右下角和左下角的顺序返回)
			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 标签
  • 展平 ArUco ids 列表
  • 循环所有corners和 ids
  • 按左上角、右上角、右下角和左下角顺序提取标记角
  • 将角 (x, y) 坐标从 NumPy 数组数据类型转换为 Python 整数,以便我们可以使用 OpenCV 的绘图函数绘制坐标

这里的最后一步是绘制我们的 ArUco 标签边界框

			# 绘制ArUCo检测的边界框
			cv2.line(frame, topLeft, topRight, (0, 255, 0), 2)
			cv2.line(frame, topRight, bottomRight, (0, 255, 0), 2)
			cv2.line(frame, bottomRight, bottomLeft, (0, 255, 0), 2)
			cv2.line(frame, 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(frame, (cX, cY), 4, (0, 0, 255), -1)
			# 在框架上绘制 ArUco 标记 ID
			cv2.putText(frame, str(markerID),
				(topLeft[0], topLeft[1] - 15),
				cv2.FONT_HERSHEY_SIMPLEX,
				0.5, (0, 255, 0), 2)
	# 显示输出帧
	cv2.imshow("Frame", frame)
	key = cv2.waitKey(1) & 0xFF
	# 如果按下 `q` 键,则中断循环
	if key == ord("q"):
		break
# 做一些清理工作
cv2.destroyAllWindows()
vs.stop()

我们的可视化步骤包括:

  • 在图像上绘制 ArUco 标签的轮廓
  • 绘制 ArUco 标签的中心
  • 显示检测到的 ArUco 标签的 ID

最后,我们将输出帧显示到我们的屏幕上。

如果在 OpenCV 打开的窗口处于活动状态时按下 q 键,我们将中断脚本并清理视频指针。

总结

在本教程中,您学习了如何使用 OpenCV 和 Python 检测图像和实时视频流中的 ArUco 标记。

使用 OpenCV 检测 ArUco 标记是一个三步过程:

  • 设置您使用的 ArUco 字典。
  • 定义 ArUco 检测器的参数(通常默认选项就足够了)。
  • 使用 OpenCV 的ArUco 检测器的 cv2.aruco.detectMarkers 函数 。

OpenCV 的 ArUco 标记速度非常快,正如我们的结果所示,能够实时检测 ArUco 标记。

在您自己的计算机视觉管道中使用 ArUco 标记时,请随意使用此代码作为起点。

但是,假设您正在开发一个计算机视觉项目来自动检测图像中的 ArUco 标记,但您不知道使用的是哪种标记类型,因此,您无法明确设置 ArUco 标记字典 - 您会做什么?

如果您不知道所使用的标记类型,您将如何检测 ArUco 标记?

我将在下一个博客文章中回答问题。

参考目录

https://www.pyimagesearch.com/2020/12/21/detecting-aruco-markers-with-opencv-and-python/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV基础(19)使用 OpenCV 和 Python 检测 ArUco 标记 的相关文章

  • 通过 Python 与 Windows 控制台应用程序交互

    我在 Windows 上使用 python 2 5 我希望通过 Popen 与控制台进程交互 我目前有一小段代码 p Popen console app exe stdin PIPE stdout PIPE issue command 1
  • Python:多处理和请求

    以下是我正在运行的使用多处理并行触发 HTTP 请求的代码片段 在控制台上运行后 它挂在 requests get url 处 既不继续前进也不抛出错误 def echo 100 q print before r requests get
  • OpenCV Python 删除图像中的某些对象

    我正在使用带有 opencv 和 numpy 的 python 来检测天文中的星星 例如这个1 https i stack imgur com AKwEJ jpg图片 使用模板匹配 我可以用阈值检测星星 单击 2 2 https i sta
  • 针对不同相机(RGB 和红外)的 StereoCalibrate

    我在校准两个摄像头时遇到问题 第一个是 RGB 第二个是红外 它们有不同的分辨率 我调整了大小并裁剪了更大的图像 焦距等等 例子 RGB 1920x1080 Infrared 512x424 如何相互校准它们 我应该在stereoCalib
  • Python:如何重构循环导入

    我有件事可以帮你做engine setState
  • 打印一个 Jupyter 单元中定义的所有变量

    有没有一种更简单的方法来以漂亮的方式显示单个单元格中定义的所有变量的名称和值 我现在做的方式是这样的 但是当有30个或更多变量时我浪费了很多时间 您可以使用whos http ipython readthedocs io en stable
  • python 语言环境奇怪的错误。这究竟是怎么回事?

    所以今天我升级到了 bazaar 2 0 2 我开始收到这条消息 顺便说一句 我在雪豹上 bzr warning unknown locale UTF 8 Could not determine what text encoding to
  • 将 JSON 字符串传递给 Django 模板

    我一直在用头撞墙 试图找出为什么我无法将从 Django 模型生成的 JSON 字符串传递到模板的 javascript 静态文件中 事实证明 问题不在模型级别 使用serializers serialize 在脚本本身中放入相同的字符串将
  • 检查对象数组中的多个属性匹配

    我有一个对象数组 它们都是相同的对象类型 并且它们有多个属性 有没有办法返回一个较小的对象数组 其中所有属性都与测试用例 字符串匹配 无论该属性类型是什么 使用列表理解all http docs python org 3 library f
  • 如何创建指向指针数组的 Python ctypes 指针

    我需要学习如何处理char 在下面的 C 方法中通过 Python ctypes 我通过使用调用其他只需要单个指针的方法做得很好create string buffer 但此方法需要一个指向指针数组的指针 ladybugConvertToM
  • Scrapy - 不会爬行

    我正在尝试运行递归爬行 由于我编写的爬行不能正常工作 因此我从网络上提取了一个示例并进行了尝试 我真的不知道问题出在哪里 但是爬行没有显示任何错误 谁能帮我这个 另外 是否有任何逐步调试工具可以帮助理解蜘蛛的爬行流程 非常感谢任何与此相关的
  • pandas apply:函数名是否带引号的区别

    简单数据框定义示例 df pd DataFrame A 2 4 1 B 8 4 1 C 6 2 7 df A B C 0 2 8 6 1 4 4 2 2 1 1 7 尝试理解以下块中函数参数调用的差异 df apply sum df app
  • Python:使用列表创建二叉搜索树

    我的代码的目标是从 txt 文件中获取每个单独的单词并将其放入列表中 然后使用该列表创建二叉搜索树来计算每个单词的频率 并按字母顺序打印每个单词及其频率 中的每个单词只能包含字母 数字 或 我无法用我的初学者编程知识来做的部分是使用我拥有的
  • 通过套接字发送字符串(python)

    我有两个脚本 Server py 和 Client py 我心中有两个目标 能够从客户端一次又一次地向服务器发送数据 能够将数据从服务器发送到客户端 这是我的 Server py import socket serversocket soc
  • Pandas - 分割大的Excel文件

    我有一个大约有 500 000 行的 Excel 文件 我想将其拆分为多个 Excel 文件 每个文件有 50 000 行 我想用熊猫来做 这样它会是最快和最简单的 有什么想法如何制作吗 感谢您的帮助 假设您的 Excel 文件只有一个 第
  • python 的 fcntl.flock 函数是否提供文件访问的线程级锁定?

    Python 的 fcnt 模块提供了一种名为 flock 1 的方法来证明文件锁定 其描述如下 对文件执行锁定操作op 描述符 fd 文件对象提供 fileno 方法被接受为 出色地 请参阅 Unix 手册集群 2 了解详情 在某些系统上
  • 带有整数的 np.sqrt 和 where 条件返回错误结果

    当我将 numpy sqrt 方法应用于带有 a 的整数数组时 我得到了奇怪的结果where健康 状况 见下文 对于整数 a np array 1 4 9 np sqrt a where a gt 5 Out 3 array 0 0 5 3
  • 升级后 pip 损坏

    我做了 pip install U easyinstall 然后 pip install U pip 来升级我的 pip 但是 当我尝试使用 pip 时 我现在收到此错误 root d8fb98fc3a66 which pip usr lo
  • 在 Gensim 中通过 ID 检索文档的字符串版本

    我正在使用 Gensim 进行一些主题建模 并且已经达到使用 LSI 和 tf idf 模型进行相似性查询的程度 我取回 ID 集和相似点 例如 299501 0 64505910873413086 如何获取与 ID 在本例中为 29950
  • 将笔记本生成的 HTML 片段转换为 LaTeX 和 PDF

    在我的笔记本里有时会有 from IPython display import display HTML display HTML h3 The s is important h3 question of the day 但当我后来将笔记本

随机推荐

  • 新买了块翼联的AX200的网卡,结果开移动热点的时候遇到了点问题

    买网卡这事的起因要从入手了switch说起 xff0c 直连网不是不能用 xff0c 就是太难用了 xff0c 下载个东西要几十个小时 xff0c 玩个马造2连地图都下载不了 然后试用了3天的网易uu加速器 xff0c 好用是真的好用 xf
  • HDU 1085(Holding Bin-Laden Captive!)

    题意 xff1a 有三种价值分别为 1 2 5 的硬币 xff0c 每一种分别由 a b c 个 xff0c 求这些硬币不能组成的最小价值 分析 xff1a 生成函数板子题 xff08 贴一个讲生成函数的链接https blog csdn
  • 大电流的走线和过孔

    工程师在设计的时候 xff0c 很容易忽略走线宽度的问题 xff0c 因为在数字设计时 xff0c 走线宽度不在 考虑范围里面 通常情况下 xff0c 都会尝试用最小的线宽去设计走线 xff0c 这时 xff0c 在大电流时 xff0c 将
  • c++ 实现基本数据结构代码

    数据结构是计算机科学的一个重要的分支 xff0c 主要研究如何有效地存储和组织数据以便于快速访问和操作 常见的数据结构有 xff1a 数组 xff1a 是一种线性的数据结构 xff0c 可以通过索引来访问数组中的元素 链表 xff1a 是一
  • Jetson开发实战记录(二):Jetson Xavier NX版本区别以及烧录系统

    Jetson开发实战记录 xff08 二 xff09 xff1a Jetson Xavier NX版本区别以及烧录系统 一 Jetson Xavier NX类型1 SD卡槽的版本 xff08 官方版本 xff09 2 带eMMC存储芯片的版
  • ZYNQ7000(AX7020)移植Linux操作系统(二):u-boot的编译和启动

    总述 我们已经在 PC 上已经安装了 Ubuntu 客户操作系统 xff0c 以及在 Ubuntu 操作系统里安装了 SDK 2015 4 工具 要想在 Zynq 平台上运行 Ubuntu 操作系统 xff0c 必项预先制作作镜像文件 xf
  • 10个让你的 Python 代码更具 pythonic 风格的示例

    10个让你的 Python 代码更具 pythonic 风格的示例 1 变量交换 2 函数返回元组 xff08 自动打包 解包 xff09 3 多重比较 4 多重比较 5 列表推导式 6 将 Python 字典当做缓存 7 关键字参数 8
  • 进程的调度算法

    先来先服务调度算法 xff1a 先来先服务调度算法是一种最简单的调度算法 xff0c 也称为先进先出或严格排队方案 当每个进程就绪后 xff0c 它加入就绪队列 当前正运行的进程停止执行 xff0c 选择在就绪队列中存在时间最长的进程运行
  • 华为云服务器购买及使用指南

    前言 作者参与牛客网的买服务器返现活动 xff0c 以某个奇妙的价格价格购入了一个一年的华为云服务器 xff0c 具体多少钱就不透露了 xff0c 不然会被当作广告性能参数如下 xff1a 购买 偶然看到这个活动 xff0c 然后点进去 x
  • JS 简单的事件

    实现鼠标滑过按钮时显示文本 xff0c 滑出时隐藏文本 span class token doctype lt DOCTYPE html gt span span class token tag span class token tag s
  • 在ROS中实现darknet_ros(YOLO V3)检测以及训练自己的数据集

    目录 1 darknet ros介绍 2 darknet ros原始项目编译测试 3 yolov3训练自己的数据集 4 使用自己训练好的数据集 1 darknet ros介绍 Darknet概述 https blog csdn net u0
  • apache options index 设置问题

    禁止显示Apache目录列表 Indexes FollowSymLinks 如何修改目录的配置以禁止显示Apache 目录列表 缺省情况下如果你在浏览器输入地址 xff1a http localhost 8080 如果你的文件根目录里有in
  • Centos7.7安装vncserver虚拟网络控制台

    虚拟网络控制台 xff08 VNC xff09 是一个图形桌面共享软件 xff0c 允许您使用键盘和鼠标远程控制另一台计算机 系统环境 服务端 xff1a Centos7 7 Minimal客户端 xff1a Windows10客户端VNC
  • C语言实现TCP通信

    如果想要自己写一个服务器和客户端 xff0c 我们需要掌握一定的网络编程技术 xff0c 个人认为 xff0c 网络编程中最关键的就是这个东西 socket 套接字 socket 套接字 xff1a 简单来讲 xff0c socket就是用
  • 详细的 win10+VS+Cuda10 环境配置,图文

    为完成CUDA 和OPEN CL的任务 xff0c 这两天配置了基于WIN10和VS2012 2015的环境 发觉网上很多配置CUDA的都是老版本 xff0c 而且过程过于繁琐 xff0c 我重新整理并分享之 我分了3篇记录 xff0c 另
  • Pytorch基础知识(13)对抗样本

    在前面的章节中 xff0c 我们已经看到了深度学习模型在解决各种计算机视觉任务方面的强大能力 我们在不同的数据集上训练和测试多个模型 现在 xff0c 我们将把注意力转向这些模型的健壮性 在本章中 xff0c 我们将介绍对抗样本 对抗样本是
  • ubuntu18.04修改docker0的IP

    当docker的IP与宿主机的IP在同一网段时候 xff0c 会产生错误 xff0c 例如同为172 17 直接修改 etc docker daemon json文件 xff0c 重启时候还是报错 查看docker日志 xff0c 可以看到
  • Flask 项目中使用 bootstrap

    flask 的插件中有一个 flask bootstrap 项目 但是用起来不怎么方便 如果大家感兴趣的话 还是直接在项目中引入 bootstrap flask 项目的目录结构 项目名称 app static span class hljs
  • MediaPipe基础(9)手指计数

    本文实现手指计数 xff0c 可以实现0 5的计数 链接 xff1a https span class token punctuation span span class token operator span pan span class
  • OpenCV基础(19)使用 OpenCV 和 Python 检测 ArUco 标记

    在本教程中 xff0c 您将学习如何使用 OpenCV 和 Python 检测图像和实时视频流中的 ArUco 标记 1 使用 OpenCV 和 Python 检测 ArUco 标记 在本教程的第一部分 xff0c 您将了解 OpenCV