一步一步学OAK之七:通过OAK相机实现特征跟踪

目录

  • 特征跟踪
  • Setup 1: 创建文件
  • Setup 2: 安装依赖
  • Setup 3: 导入需要的包
  • Setup 4: 定义FeatureTrackerDrawer类
    • 定义变量
    • 定义onTrackBar方法
    • 定义trackFeaturePath方法
    • 定义drawFeatures方法
    • 定义`FeatureTrackerDrawer`类的构造函数
  • Setup 5: 创建pipeline
  • Setup 6: 创建节点
    • 创建相机节点
    • 创建特征检测节点
    • 创建数据交互节点
  • Setup 7:设置相关属性
  • Setup 8: 建立链接关系
  • Setup 9: 为跟踪器对象设置硬件资源
  • Setup 10: 连接设备并启动管道
  • Setup 11: 创建与DepthAI设备通信的输入队列和输出队列
  • Setup 12: 主循环
    • 从队列中获取输入的图像帧。
    • 从队列中获取跟踪的特征
    • 显示帧图像
  • Setup 13:运行程序

特征跟踪

特征跟踪,也称为目标跟踪或点跟踪,是指在序列图像中追踪物体或者场景中的特定特征点的过程。特征点可以是具有独特性质的像素,如角点、边缘、纹理等。通过跟踪这些特征点,可以在连续图像帧中确定它们的位置、速度和运动轨迹。

在计算机视觉和图像处理中,特征点是图像中具有显著性质或者信息的位置。对图像序列进行特征点的跟踪可以用于很多应用,例如目标追踪、运动估计、相机姿态估计、结构重建等。

特征点的选择通常取决于应用的需求和图像中的内容,常用的特征点检测算法包括Harris角点检测、Shi-Thomasi角点检测、SIFT(尺度不变特征变换)等。特征点跟踪的算法有很多,例如光流法、角点追踪等,它们可以根据特征点在前后帧之间的移动来判断物体或场景的运动。

DepthAI给我们提供了特征跟踪(Feature Tracker)功能模块,它用于在连续的图像帧中追踪和识别特定的特征点。Depthai 通过使用硬件加速和可编程的算法,实现了实时的特征跟踪功能。

DepthAI 的特征跟踪功能通常用于目标追踪和运动估计应用。它可以识别并跟踪视频中的关键点、边缘、角点或其他具有显著性质的图像特征。这些特征点的位置和运动轨迹可以用于估计物体的运动,在许多应用中非常有用,比如机器人导航、增强现实、虚拟现实等。

另外,DepthAI 给我们提供了编程接口,使开发人员能够配置和使用特征跟踪功能。通过适当的配置参数和算法选择,可以优化特征跟踪的性能和准确性,以满足应用的需求。
下面我们来实现一个OAK相机特征跟踪的代码

Setup 1: 创建文件

  • 创建新建7-feature-tracker文件夹
  • 用vscode打开该文件夹
  • 新建一个main.py 文件

Setup 2: 安装依赖

安装依赖前需要先创建和激活虚拟环境,我这里已经创建了虚拟环境OAKenv,在终端中输入cd…退回到OAKenv的根目录,输入 OAKenv\Scripts\activate激活虚拟环境

安装pip依赖项:

pip install numpy opencv-python depthai blobconverter --user

Setup 3: 导入需要的包

在main.py中导入项目需要的包

import cv2
import depthai as dai
from collections import deque

这里从 Python 的 collections 模块中导入 deque 类,deque 类提供了各种方法来操作队列,如 append()appendleft()pop()popleft(),以及像列表一样的索引访问。当需要高效地从队列的两端添加或删除元素时,deque 尤为有用。

Setup 4: 定义FeatureTrackerDrawer类

定义一个名为 FeatureTrackerDrawer 的类,用于绘制特征追踪器的路径。

class FeatureTrackerDrawer:

定义变量

    lineColor = (200, 0, 200)pointColor = (0, 0, 255)circleRadius = 2maxTrackedFeaturesPathLength = 30 trackedFeaturesPathLength = 10trackedIDs = NonetrackedFeaturesPath = None
  • lineColor:追踪路径的线条颜色。
  • pointColor:特征点的颜色。
  • circleRadius:绘制特征点的圆的半径。
  • maxTrackedFeaturesPathLength:追踪路径的最大长度。
  • trackedFeaturesPathLength:追踪路径的当前长度。
  • trackedIDs:当前追踪的特征点的 ID 集合。
  • trackedFeaturesPath:特征点的追踪路径。

定义onTrackBar方法

    def onTrackBar(self, val):FeatureTrackerDrawer.trackedFeaturesPathLength = valpass

这个 onTrackBar 方法接收一个参数 val,该参数表示滑动条的当前值。

在这个方法中,它将传入的 val 值赋给 FeatureTrackerDrawer.trackedFeaturesPathLength,以更新追踪路径的长度。这样,当滑块的值改变时,追踪路径的长度也会相应地改变。

pass 语句表示该方法暂时不执行任何具体的操作,仅作为占位符存在。

定义trackFeaturePath方法

trackFeaturePath方法用于追踪特征点的路径,并更新追踪路径记录。

    def trackFeaturePath(self, features):newTrackedIDs = set()for currentFeature in features:currentID = currentFeature.idnewTrackedIDs.add(currentID)if currentID not in self.trackedFeaturesPath:self.trackedFeaturesPath[currentID] = deque()path = self.trackedFeaturesPath[currentID]path.append(currentFeature.position)while(len(path) > max(1, FeatureTrackerDrawer.trackedFeaturesPathLength)):path.popleft()self.trackedFeaturesPath[currentID] = pathfeaturesToRemove = set()for oldId in self.trackedIDs:if oldId not in newTrackedIDs:featuresToRemove.add(oldId)for id in featuresToRemove:self.trackedFeaturesPath.pop(id)self.trackedIDs = newTrackedIDs

该方法接受一个名为 features 的参数,表示当前帧中的特征点列表。

  • 使用newTrackedIDs = set()创建一个空的集合 newTrackedIDs 用于存储新追踪的特征点的 ID

  • 使用 for 循环遍历 features 中的每个特征点 currentFeature

    在循环中,首先获取当前特征点的 ID,并将其添加到 newTrackedIDs 集合中。

    检查当前特征点的 ID 是否已经存在于 trackedFeaturesPath 字典中。如果不存在,就使用deque()方法在字典中创建一个空的双向队列 path 来存储该特征点的路径。

    将当前特征点的位置 (currentFeature.position) 添加到 path 队列的末尾。

    使用 while 循环,确保 path 的长度不超过当前设置的最大追踪路径长度 (max(1, FeatureTrackerDrawer.trackedFeaturesPathLength))。如果超过了,则从队列的前端删除元素。

    将更新后的 path 队列赋值给 trackedFeaturesPath 字典中对应的特征点 ID。

  • 创建一个空的集合 featuresToRemove 用于存储需要移除的特征点的 ID。

  • 使用 for 循环遍历 trackedIDs 中的每个旧的特征点 ID。如果该 ID 不在 newTrackedIDs 集合中,表示该特征点不再存在于当前帧的特征点列表中,将其 ID 添加到 featuresToRemove 集合中。

  • 使用 for 循环遍历 featuresToRemove 集合中的每个特征点 ID,并从 trackedFeaturesPath 字典中移除对应的特征点路径。

  • newTrackedIDs 集合赋值给 trackedIDs,更新已追踪的特征点的 ID。

定义drawFeatures方法

定义drawFeatures方法,绘制特征点的路径和特征点本身。通过这个方法,可以在图像上绘制特征点的路径,并将特征点的位置设置为当前的追踪路径长度。

    def drawFeatures(self, img):cv2.setTrackbarPos(self.trackbarName, self.windowName, FeatureTrackerDrawer.trackedFeaturesPathLength)for featurePath in self.trackedFeaturesPath.values():path = featurePathfor j in range(len(path) - 1):src = (int(path[j].x), int(path[j].y))dst = (int(path[j + 1].x), int(path[j + 1].y))cv2.line(img, src, dst, self.lineColor, 1, cv2.LINE_AA, 0)j = len(path) - 1cv2.circle(img, (int(path[j].x), int(path[j].y)), self.circleRadius, self.pointColor, -1, cv2.LINE_AA, 0)

drawFeatures 方法用于在图像上绘制特征点的路径。方法接收一个名为 img 的参数,表示要绘制特征点路径的图像。

  • 使用 cv2.setTrackbarPos 方法设置特征点的位置,以保持特征点的值与 FeatureTrackerDrawer.trackedFeaturesPathLength 相同。

  • 使用 for 循环遍历 trackedFeaturesPath 字典中的每个特征点路径。

    在循环中,首先获取当前特征点路径 featurePath

    然后,使用 for 循环遍历路径 path 中的每个点的索引。

    在循环中,获取当前点 path[j] 和下一个点 path[j+1] 的坐标,并将其转换为整数类型。

    调用 cv2.line 方法,在图像上绘制从当前点到下一个点的线段,线段的颜色为 self.lineColor,宽度为 1,线段的类型为 cv2.LINE_AA,线段的连接方式为 0。

    然后,将索引值 j 更新为路径 path 中的最后一个点的索引。

    接着,使用 cv2.circle 方法,在图像上绘制路径 path 中最后一个点的圆形标记,圆心坐标为路径最后一个点的坐标 (int(path[j].x), int(path[j].y)),圆的半径为 self.circleRadius,圆的颜色为 self.pointColor,圆的类型为 cv2.LINE_AA

定义FeatureTrackerDrawer类的构造函数

定义FeatureTrackerDrawer类的构造函数 __init__() ,它初始化了类的一些属性,该方法接受两个参数trackbarNamewindowName,分别代表跟踪点的名称和窗口的名称。

    def __init__(self, trackbarName, windowName):self.trackbarName = trackbarNameself.windowName = windowNamecv2.namedWindow(windowName)cv2.createTrackbar(trackbarName, windowName, FeatureTrackerDrawer.trackedFeaturesPathLength, FeatureTrackerDrawer.maxTrackedFeaturesPathLength, self.onTrackBar)self.trackedIDs = set()self.trackedFeaturesPath = dict()
  • trackbarNamewindowName赋值给self.trackbarNameself.windowName属性。

  • 使用cv2.namedWindow方法创建一个窗口,并将windowName作为窗口的名称。

  • 使用cv2.createTrackbar方法cv2.createTrackbar函数用于在GUI窗口中创建一个滚动条,并为其设置回调函数。滚动条的名称为trackbarName,所在的窗口为windowName。滚动条的初始值设置为FeatureTrackerDrawer.trackedFeaturesPathLength,最大值设置为FeatureTrackerDrawer.maxTrackedFeaturesPathLength。在滚动条位置发生变化时,会调用self.onTrackBar方法。

  • 将空集合赋值给self.trackedIDs属性,并将空字典赋值给self.trackedFeaturesPath属性。

通过这个构造函数,我们可以创建一个FeatureTrackerDrawer对象,并初始化相关的变量和属性。

Setup 5: 创建pipeline

pipeline = dai.Pipeline()

Setup 6: 创建节点

创建相机节点

monoLeft = pipeline.createMonoCamera()
monoRight = pipeline.createMonoCamera()

创建特征检测节点

featureTrackerLeft = pipeline.createFeatureTracker()
featureTrackerRight = pipeline.createFeatureTracker()

创建数据交互节点

xoutPassthroughFrameLeft = pipeline.createXLinkOut()
xoutTrackedFeaturesLeft = pipeline.createXLinkOut()
xoutPassthroughFrameRight = pipeline.createXLinkOut()
xoutTrackedFeaturesRight = pipeline.createXLinkOut()
xinTrackedFeaturesConfig = pipeline.createXLinkIn()xoutPassthroughFrameLeft.setStreamName("passthroughFrameLeft")
xoutTrackedFeaturesLeft.setStreamName("trackedFeaturesLeft")
xoutPassthroughFrameRight.setStreamName("passthroughFrameRight")
xoutTrackedFeaturesRight.setStreamName("trackedFeaturesRight")
xinTrackedFeaturesConfig.setStreamName("trackedFeaturesConfig")

Setup 7:设置相关属性

设置相机的分辨率和板载插槽

monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_720_P)
monoLeft.setBoardSocket(dai.CameraBoardSocket.LEFT)
monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_720_P)
monoRight.setBoardSocket(dai.CameraBoardSocket.RIGHT)

Setup 8: 建立链接关系

建立相机和特征跟踪器之间的数据连接

monoLeft.out.link(featureTrackerLeft.inputImage)
featureTrackerLeft.passthroughInputImage.link(xoutPassthroughFrameLeft.input)
featureTrackerLeft.outputFeatures.link(xoutTrackedFeaturesLeft.input)
xinTrackedFeaturesConfig.out.link(featureTrackerLeft.inputConfig)monoRight.out.link(featureTrackerRight.inputImage)
featureTrackerRight.passthroughInputImage.link(xoutPassthroughFrameRight.input)
featureTrackerRight.outputFeatures.link(xoutTrackedFeaturesRight.input)
xinTrackedFeaturesConfig.out.link(featureTrackerRight.inputConfig)

Setup 9: 为跟踪器对象设置硬件资源

setHardwareResources函数为跟踪器设置硬件资源。

numShaves = 2
numMemorySlices = 2
featureTrackerLeft.setHardwareResources(numShaves, numMemorySlices)
featureTrackerRight.setHardwareResources(numShaves, numMemorySlices)featureTrackerConfig = featureTrackerRight.initialConfig.get()
print("Press 's' to switch between Lucas-Kanade optical flow and hardware accelerated motion estimation!")

使用setHardwareResources函数为两个跟踪器对象featureTrackerLeft和featureTrackerRight设置硬件资源。

numShaves(Myriad X硬件加速器的shave数量)和numMemorySlices(内存切片的数量)是两个参数,用于指定可用的硬件资源数量。

通过featureTrackerRight的initialConfig属性获取了其初始配置,并将其存储在featureTrackerConfig变量中。

Setup 10: 连接设备并启动管道

with dai.Device(pipeline) as device:

Setup 11: 创建与DepthAI设备通信的输入队列和输出队列

    passthroughImageLeftQueue = device.getOutputQueue("passthroughFrameLeft", 8, False)outputFeaturesLeftQueue = device.getOutputQueue("trackedFeaturesLeft", 8, False)passthroughImageRightQueue = device.getOutputQueue("passthroughFrameRight", 8, False)outputFeaturesRightQueue = device.getOutputQueue("trackedFeaturesRight", 8, False)inputFeatureTrackerConfigQueue = device.getInputQueue("trackedFeaturesConfig")

使用FeatureTrackerDrawer类创建两个特征跟踪器的绘图对象

    leftWindowName = "left"leftFeatureDrawer = FeatureTrackerDrawer("Feature tracking duration (frames)", leftWindowName)rightWindowName = "right"rightFeatureDrawer = FeatureTrackerDrawer("Feature tracking duration (frames)", rightWindowName)

使用FeatureTrackerDrawer类创建了两个特征跟踪器的绘图对象,一个用于左侧(leftFeatureDrawer),一个用于右侧(rightFeatureDrawer)。

这些绘图对象用于可视化特征跟踪的持续时间,并且与相应的窗口名称关联起来。

Setup 12: 主循环

    while True:

从队列中获取输入的图像帧。

        inPassthroughFrameLeft = passthroughImageLeftQueue.get()passthroughFrameLeft = inPassthroughFrameLeft.getFrame()leftFrame = cv2.cvtColor(passthroughFrameLeft, cv2.COLOR_GRAY2BGR)inPassthroughFrameRight = passthroughImageRightQueue.get()passthroughFrameRight = inPassthroughFrameRight.getFrame()rightFrame = cv2.cvtColor(passthroughFrameRight, cv2.COLOR_GRAY2BGR)
  • 分别从名为passthroughImageLeftQueue和passthroughImageRightQueue的队列中获取输入的图像帧。

  • 从inPassthroughFrameLeft和inPassthroughFrameRight中获取帧,并将它们分别存储在passthroughFrameLeft和passthroughFrameRight变量中。

  • 使用cv2.cvtColor函数将passthroughFrameLeft和passthroughFrameRight从灰度图像转换为BGR彩色图像,并分别存储在leftFrame和rightFrame变量中。

从队列中获取跟踪的特征

        trackedFeaturesLeft = outputFeaturesLeftQueue.get().trackedFeaturesleftFeatureDrawer.trackFeaturePath(trackedFeaturesLeft)leftFeatureDrawer.drawFeatures(leftFrame)trackedFeaturesRight = outputFeaturesRightQueue.get().trackedFeaturesrightFeatureDrawer.trackFeaturePath(trackedFeaturesRight)rightFeatureDrawer.drawFeatures(rightFrame)

从outputFeaturesLeftQueue队列中获取跟踪的特征,并将其存储在trackedFeaturesLeft变量中。

  • 使用leftFeatureDrawer对象的trackFeaturePath方法跟踪这些特征的路径,并使用drawFeatures方法在leftFrame图像上绘制这些特征。

  • 从outputFeaturesRightQueue队列中获取跟踪的特征,并将其存储在trackedFeaturesRight变量中。

  • 使用rightFeatureDrawer对象的trackFeaturePath方法跟踪这些特征的路径,并使用drawFeatures方法在rightFrame图像上绘制这些特征。

这样,我们就完成了特征跟踪和绘制的步骤,可以在图像上可视化跟踪的特征路径。

显示帧图像

        cv2.imshow(leftWindowName, leftFrame)cv2.imshow(rightWindowName, rightFrame)

对键盘输入响应的程序

        key = cv2.waitKey(1)if key == ord('q'):breakelif key == ord('s'):if featureTrackerConfig.motionEstimator.type == dai.FeatureTrackerConfig.MotionEstimator.Type.LUCAS_KANADE_OPTICAL_FLOW:featureTrackerConfig.motionEstimator.type = dai.FeatureTrackerConfig.MotionEstimator.Type.HW_MOTION_ESTIMATIONprint("Switching to hardware accelerated motion estimation")else:featureTrackerConfig.motionEstimator.type = dai.FeatureTrackerConfig.MotionEstimator.Type.LUCAS_KANADE_OPTICAL_FLOWprint("Switching to Lucas-Kanade optical flow")cfg = dai.FeatureTrackerConfig()cfg.set(featureTrackerConfig)inputFeatureTrackerConfigQueue.send(cfg)

使用cv2.waitKey函数来等待用户按下键盘上的按键。

  • 如果用户按下键盘上的’q’键,程序将跳出循环,从而退出程序。

  • 如果用户按下键盘上的’s’键,程序将执行以下操作:

  1. 检查featureTrackerConfig.motionEstimator.type的当前值。如果其值是dai.FeatureTrackerConfig.MotionEstimator.Type.LUCAS_KANADE_OPTICAL_FLOW,则会执行以下操作:

    • 将featureTrackerConfig.motionEstimator.type设置为dai.FeatureTrackerConfig.MotionEstimator.Type.HW_MOTION_ESTIMATION,以切换到硬件加速的运动估计。
  2. 如果featureTrackerConfig.motionEstimator.type的当前值不是dai.FeatureTrackerConfig.MotionEstimator.Type.LUCAS_KANADE_OPTICAL_FLOW,则会执行以下操作:

    • 将featureTrackerConfig.motionEstimator.type设置为dai.FeatureTrackerConfig.MotionEstimator.Type.LUCAS_KANADE_OPTICAL_FLOW,以切换到Lucas-Kanade光流。
  3. 创建一个新的dai.FeatureTrackerConfig对象cfg,并将featureTrackerConfig设置为其值。

  4. 使用inputFeatureTrackerConfigQueue队列发送cfg对象。

Setup 13:运行程序

在终端中输入如下指令运行程序

python main.py

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GIT保存记录原理之commit对象

GIT 中提交对象非常的重要,我们通过它记录代码提交过程、进行文件保存、回退等操作,那么它是怎样帮助我们记录这些信息的呢?其实就是都保存在项目根目录的 .git 文件夹中。 新建空项目 gitDemo使用 git init初始化,在文件夹根目录…

图像去模糊:RSBlur 数据集以及模糊图像合成方法

本内容主要介绍图像去模糊数据集 RSBlur,以及逼真模糊图像合成方法。 论文:Realistic Blur Synthesis for Learning Image Deblurring 代码(官方):https://github.com/rimchang/RSBlur 1.1 背景 运动模糊是由曝光…

vue 组件简单实例及传参交互

前言:vue 可以比较灵活的使用 html的片段,并将html的片段进行数据隔离,参数也可以互相传递,组件与组件之间也可以进行数据的交互 合理的使用组件可以避免重复代码或者很方便的调用第三方组件库 vue组件 简单实例组件传参实际应用父子组件交互…

maven打包所有依赖,对外提供sdk.jar

maven打包所有依赖 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compile.source>1.8</maven.compile.source><maven.compile.target>1.8</maven.compile.target></properties><…

分布式计算模型详解:MapReduce、数据流、P2P、RPC、Agent

前言 本文隶属于专栏《大数据理论体系》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见大数据理论体系 思维导图 MapReduce MapReduce 是一种分布式计算模…

在 Maya、ZBrush、Substance 3D 和 UE5 中创建理发椅

今天瑞云渲染小编给大家带来Kevin J. Coulman 分享的理发椅项目背后的工作流程&#xff0c;详细介绍了如何在 Maya 和 ZBrush 中为道具建模&#xff0c;分享了制作准确材质的技巧&#xff0c;并解释了为什么选择 UE5 进行渲染。 介绍 大家好! 我的名字是Mehdi Benmansour&…

layui实现选择框搜索(下拉搜索)功能

1.可以使用官方介绍的方法&#xff0c;适用于form表单内的下拉搜索&#xff0c;外层需要使用layui-form样式&#xff0c;select标签内添加lay-search“”&#xff0c;此方法若外层不添加layui-form无法实现搜索功能&#xff0c;如下所示&#xff1a; 2.下面是另一种形式的下拉选…

设计一个高流量高并发的系统需要关注哪些点

1、设计原则 1.1、系统设计原则 在设计一个系统之前&#xff0c;我们先要有一个统一且清晰的认知&#xff1a;不要想着一下就能设计出完美的系统&#xff0c;好的系统是迭代出来的。不要复杂化&#xff0c;要先解决核心问题。但是要有先行的规划&#xff0c;对现有的问题有方…

CABAC编解码原理分析

CABAC编解码原理分析 文章目录 CABAC编解码原理分析一、二进制算数编码二、CABAC编码三、CABAC编解码与普通的二元算术编码的区别四、 CABAC编解码中各个变量的计算&#xff1a;五、 一些其他问题&#xff1a;六、 总结&#xff1a;七、参考资料 一、二进制算数编码 cabac是一…

【乐观锁与悲观锁】—— 每天一点小知识

&#x1f4a7; 乐观锁与悲观锁 \color{#FF1493}{乐观锁与悲观锁} 乐观锁与悲观锁&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&…

数据结构之静态链表

定义 用两个数组实现链表&#xff0c;一个数组存储数据&#xff0c;另一个数组记录当前数据的后继的下标。 示例 数据&#xff1a;data[] {-1, 34, 28, 53, 16, 25, -1, -1, -1, -1} 后继&#xff1a;next[] { 1, 2, 3, 4, 5, -1, -1, -1, -1, -1} 说明 -1: 表示无效值 …

CentOS7下载并安装mysql-8.0.33

CentOS7下载并安装mysql-8.0.33 一、官网下载mysql-8.0.33 MySQL下载路径 MySQL :: Download MySQL Community Server 自己百度mysql官网下载的话直接按照完整路径指示下载即可&#xff0c;如果点击上面的连接下载mysql的话&#xff0c;直接按照4、5、6步骤选择适合自己linu…