目录
- Setup 1: 创建文件
- Setup 2: 安装依赖
- Setup 3: 导入需要的包
- Setup 4:定义和加载模型相关的路径和标签
- Setup 5: 创建pipeline
- Setup 6: 创建节点
- Setup 7: 设置属性
- 设置相机属性
- 设置神经网络节点属性
- 设置物体跟踪对象属性
- Setup 8: 建立链接
- Setup 9: 连接设备并启动管道
- Setup 10: 创建与DepthAI设备通信的输出队列
- Setup 11: 主循环
- 从输出队列中获取图像帧数据和目标跟踪结果
- 计算帧率FPS(frames per second)
- 获取图像帧的颜色、OpenCV格式的图像帧数据以及目标跟踪的数据
- 在图像帧上绘制目标跟踪的结果
- 在窗口中显示图像帧,并在帧上绘制神经网络的帧率信息
- Setup 12:运行程序
这里我们用到了mobilenet-ssd_openvino_2021.4_6shave.blob模型文件,需要下载并存储到本地文件夹,不会下载的小伙伴可以看我前面的博客,里面有介绍,这里直接使用,不介绍下载过程了。
Setup 1: 创建文件
- 创建新建13-object-tracker-on-rgb文件夹
- 用vscode打开该文件夹
- 新建一个main.py 文件
Setup 2: 安装依赖
安装依赖前需要先创建和激活虚拟环境,我这里已经创建了虚拟环境OAKenv,在终端中输入cd…退回到OAKenv的根目录,输入 OAKenv\Scripts\activate
激活虚拟环境
安装pip依赖项:
pip install numpy opencv-python depthai blobconverter --user
Setup 3: 导入需要的包
在main.py中导入项目需要的包
from pathlib import Path
import cv2
import depthai as dai
import numpy as np
import time
import argparse
pathlib
用于处理文件路径,sys
用于系统相关的操作,cv2
是OpenCV库用于图像处理,depthai
是depthai库用于深度计算和AI推理。time
用于处理时间,argparse
用于处理命令行参数。
time
库:用于处理和操作时间相关的功能和操作。提供了许多用于测量时间、获取当前时间、等待或延迟执行的函数。可用于计时、性能测试、调度任务等场景。例如,time.time()
可以获取当前的时间戳,time.sleep()
可以使程序休眠指定时间。
argparse
库:用于解析命令行参数以及生成用户友好的命令行界面。
- 允许定义程序所需的命令行参数,并自动解析和验证这些参数。
- 可以处理位置参数、可选参数、布尔标志等多种参数类型。
- 使用
argparse
可以实现灵活的命令行接口,使得程序可以方便地从命令行中获得输入。 - 例如,可以使用
argparse.ArgumentParser
创建一个解析器对象,定义参数后调用parse_args()
方法解析命令行参数。
Setup 4:定义和加载模型相关的路径和标签
labelMap = ["background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow","diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]nnPathDefault = str((Path(__file__).parent / Path('../models/mobilenet-ssd_openvino_2021.4_6shave.blob')).resolve().absolute())
parser = argparse.ArgumentParser()
parser.add_argument('nnPath', nargs='?', help="Path to mobilenet detection network blob", default=nnPathDefault)
parser.add_argument('-ff', '--full_frame', action="store_true", help="Perform tracking on full RGB frame", default=False)args = parser.parse_args()fullFrameTracking = args.full_frame
这段代码使用argparse
库解析命令行参数,并根据解析的结果设置对应的变量值。
-
定义
labelMap
为一个字符串列表,包含了针对图像分类任务的类别标签。每个元素代表一个类别,按照索引与模型预测结果相对应。 -
定义
nnPathDefault
字符串,指定了模型文件的路径,默认为mobilenet-ssd_openvino_2021.4_6shave.blob
文件的路径。 -
parser = argparse.ArgumentParser()
创建一个参数解析器对象。 -
parser.add_argument('nnPath', nargs='?', help="Path to mobilenet detection network blob", default=nnPathDefault)
定义一个位置参数nnPath
,该参数用于指定模型文件的路径,默认值为nnPathDefault
。 -
parser.add_argument('-ff', '--full_frame', action="store_true", help="Perform tracking on full RGB frame", default=False)
用于开启全帧追踪。该参数可以通过命令行中的-ff
或--full_frame
标志进行设置,默认值为False
。 -
args = parser.parse_args()
执行解析命令行参数的操作,并将解析结果保存在args
对象中。 -
fullFrameTracking = args.full_frame
根据解析的结果获取可选参数full_frame
的值,并将其赋值给fullFrameTracking
变量。
Setup 5: 创建pipeline
pipeline = dai.Pipeline()
Setup 6: 创建节点
camRgb = pipeline.createColorCamera()
detectionNetwork = pipeline.createMobileNetDetectionNetwork()
objectTracker = pipeline.createObjectTracker()xlinkOut = pipeline.createXLinkOut()
trackerOut = pipeline.createXLinkOut()xlinkOut.setStreamName("preview")
trackerOut.setStreamName("tracklets")
分别创建ColorCamera节点、MobileNetDetectionNetwork节点、ObjectTracker节点和两个XLinkOut节点,并设置两个XLinkOut节点的节点名称
Setup 7: 设置属性
设置相机属性
camRgb.setPreviewSize(300, 300)
camRgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
camRgb.setInterleaved(False)
camRgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
camRgb.setFps(40)
使用dai.ColorCamera
对象的方法来设置相机的预览大小、分辨率、颜色顺序和帧率等参数。
-
camRgb.setInterleaved(False)
设置相机捕获图像时是否使用交织模式。如果设置为False
,则图像数据将以分离的方式进行捕获,更容易处理和获取分离的通道数据。 -
camRgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
设置相机捕获图像的颜色顺序为BGR。这意味着相机捕获的图像中,颜色通道的排列顺序是蓝、绿、红。 -
camRgb.setFps(40)
设置相机的帧率为40帧/秒。帧率表示相机每秒传输的图像帧数,高帧率可以获得更连续、流畅的图像。
设置神经网络节点属性
detectionNetwork.setBlobPath(args.nnPath)
detectionNetwork.setConfidenceThreshold(0.5)
detectionNetwork.input.setBlocking(False)
使用setBlobPath
、setConfidenceThreshold
和input.setBlocking
方法来配置detectionNetwork
对象的设置模型文件路径、置信度阈值和输入通道的阻塞模式。
detectionNetwork.setBlobPath(args.nnPath)
使用args.nnPath
的值来设置detectionNetwork
的模型文件路径。args.nnPath
是在命令行中通过nnPath
参数指定的模型文件路径。detectionNetwork.setConfidenceThreshold(0.5)
将置信度阈值设置为0.5。这个阈值决定了模型对目标检测结果的信任程度。只有置信度高于该阈值的检测结果才会被认定为有效结果。detectionNetwork.input.setBlocking(False)
将输入通道的阻塞模式设置为非阻塞模式。当设置为非阻塞模式时,输入通道可以在没有新数据时立即返回,而不需要等待新数据到达。
设置物体跟踪对象属性
objectTracker.setDetectionLabelsToTrack([5])
objectTracker.setTrackerType(dai.TrackerType.ZERO_TERM_COLOR_HISTOGRAM)
objectTracker.setTrackerIdAssignmentPolicy(dai.TrackerIdAssignmentPolicy.SMALLEST_ID)
-
objectTracker.setDetectionLabelsToTrack([5])
将要跟踪的目标类别设置为只有瓶子(类别标签为5)。这意味着只有检测到瓶子目标时,才会进行跟踪。 -
objectTracker.setTrackerType(dai.TrackerType.ZERO_TERM_COLOR_HISTOGRAM)
设置跟踪器的类型为“零阶跟踪器”(ZERO_TERM_COLOR_HISTOGRAM)。这种类型的跟踪器使用颜色直方图来描述目标,并根据颜色信息进行跟踪。 -
objectTracker.setTrackerIdAssignmentPolicy(dai.TrackerIdAssignmentPolicy.SMALLEST_ID)
设置当新目标开始跟踪时,选择最小的ID作为跟踪器的ID。这意味着在跟踪过程中,较早被检测到的目标将具有较小的ID值。
Setup 8: 建立链接
camRgb.preview.link(detectionNetwork.input)
objectTracker.passthroughTrackerFrame.link(xlinkOut.input)if fullFrameTracking:camRgb.video.link(objectTracker.inputTrackerFrame)
else:detectionNetwork.passthrough.link(objectTracker.inputTrackerFrame)detectionNetwork.passthrough.link(objectTracker.inputDetectionFrame)
detectionNetwork.out.link(objectTracker.inputDetections)
objectTracker.out.link(trackerOut.input)
连接不同的节点以建立数据流。
-
camRgb.preview.link(detectionNetwork.input)
将相机的预览输出连接到目标检测网络的输入,将实时图像流输入到目标检测网络中进行处理。 -
objectTracker.passthroughTrackerFrame.link(xlinkOut.input)
将目标跟踪器的跟踪帧经过数据流连接发送到xlink输出端口,以便在外部查看和分析跟踪结果。 -
如果
fullFrameTracking
为True
,则执行camRgb.video.link(objectTracker.inputTrackerFrame)
将相机的视频输出连接到目标跟踪器的跟踪帧输入。这意味着完整的帧将用于目标跟踪,而不是仅使用目标检测网络的结果。 -
如果
fullFrameTracking
为False
,则执行detectionNetwork.passthrough.link(objectTracker.inputTrackerFrame)
将目标检测网络的传递输出连接到目标跟踪器的跟踪帧输入。这意味着只使用目标检测网络的结果作为跟踪器的输入。 -
detectionNetwork.passthrough.link(objectTracker.inputDetectionFrame)
将目标检测网络的传递输出连接到目标跟踪器的检测帧输入。这样,目标检测网络的结果将作为目标跟踪器检测帧的输入,用于跟踪目标。 -
detectionNetwork.out.link(objectTracker.inputDetections)
将目标检测网络的输出连接到目标跟踪器的检测输入。这样,目标检测网络检测到的目标将成为目标跟踪器的输入。 -
objectTracker.out.link(trackerOut.input)
将目标跟踪器的输出连接到trackerOut
对象的输入,以便将跟踪结果输出到其他模块或设备中。
Setup 9: 连接设备并启动管道
with dai.Device(pipeline) as device:
Setup 10: 创建与DepthAI设备通信的输出队列
preview = device.getOutputQueue("preview", 4, False)tracklets = device.getOutputQueue("tracklets", 4, False)startTime = time.monotonic()counter = 0fps = 0frame = None
创建输出队列,以便从设备获取处理后的结果。
-
device.getOutputQueue("preview", 4, False)
创建一个名为 “preview” 的输出队列。这个队列最多可以存储4个元素,且不是自动清空。通常,“preview” 输出队列用于获取相机的预览图像数据。 -
device.getOutputQueue("tracklets", 4, False)
创建一个名为 “tracklets” 的输出队列。这个队列最多可以存储4个元素,且不是自动清空。通常,“tracklets” 输出队列用于获取目标跟踪的结果,如目标的位置、边界框等信息。 -
startTime = time.monotonic()
用于获取当前的绝对时间,作为计时器的起始时间。time.monotonic()
函数返回的是一个不受系统时间调整影响的单调递增的值。
Setup 11: 主循环
while True:
从输出队列中获取图像帧数据和目标跟踪结果
imgFrame = preview.get()track = tracklets.get()
-
imgFrame = preview.get()
用于从名为 “preview” 的输出队列中获取图像帧数据。preview.get()
函数会从队列中获取最新的元素并将其返回给imgFrame
变量。 -
track = tracklets.get()
用于从名为 “tracklets” 的输出队列中获取目标跟踪结果。tracklets.get()
函数会从队列中获取最新的元素并将其返回给track
变量。
计算帧率FPS(frames per second)
counter+=1current_time = time.monotonic()if (current_time - startTime) > 1 :fps = counter / (current_time - startTime)counter = 0startTime = current_time
-
counter += 1
用于增加计数器的值,表示接收到了一帧新的图像数据。 -
current_time = time.monotonic()
用于获取当前的绝对时间。 -
通过
(current_time - startTime) > 1
条件判断,在过去的一秒钟内是否已经计算过帧率。如果是,则执行以下操作:
fps = counter / (current_time - startTime)
计算帧率,即每秒接收到的图像帧数。counter = 0
将计数器重置为0,准备重新计数下一秒的图像帧数。startTime = current_time
更新起始时间为当前时间,重新开始计时。
通过这些操作,可以在每秒钟结束时计算得到最新的帧率,并在需要时使用该值。
获取图像帧的颜色、OpenCV格式的图像帧数据以及目标跟踪的数据
color = (255, 0, 0)frame = imgFrame.getCvFrame()trackletsData = track.tracklets
-
color = (255, 0, 0)
定义一个颜色变量,表示RGB颜色值为红色。 -
frame = imgFrame.getCvFrame()
从imgFrame
对象中获取OpenCV格式的图像帧数据getCvFrame()
函数是一个用于从imgFrame
对象中提取图像帧数据的方法。 -
trackletsData = track.tracklets
获取目标跟踪数据。track
是一个对象或类,其中包含目标跟踪的相关方法和数据。这里通过track.tracklets
访问了目标跟踪数据,可能是获取目标物体的位置、速度等信息。根据实际情况,trackletsData
可能包含一个列表或其他数据结构,存储跟踪目标的相关数据。
在图像帧上绘制目标跟踪的结果
for t in trackletsData:roi = t.roi.denormalize(frame.shape[1], frame.shape[0])x1 = int(roi.topLeft().x)y1 = int(roi.topLeft().y)x2 = int(roi.bottomRight().x)y2 = int(roi.bottomRight().y)try:label = labelMap[t.label]except:label = t.labelcv2.putText(frame, str(label), (x1 + 10, y1 + 20), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)cv2.putText(frame, f"ID: {[t.id]}", (x1 + 10, y1 + 35), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)cv2.putText(frame, t.status.name, (x1 + 10, y1 + 50), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)cv2.rectangle(frame, (x1, y1), (x2, y2), color, cv2.FONT_HERSHEY_SIMPLEX)
-
通过
for t in trackletsData
循环遍历目标跟踪数据列表中的每一个跟踪目标。 -
通过
t.roi.denormalize(frame.shape[1], frame.shape[0])
将目标的相对坐标转换为绝对坐标,并赋值给roi
。roi
是一个矩形区域对象,表示目标在图像中的位置。 -
通过
roi
对象获取目标矩形的四个角的坐标值,并分别赋值给x1
、y1
、x2
、y2
变量。这些坐标用于确定目标矩形框的位置和大小。 -
通过
labelMap[t.label]
尝试从labelMap
字典中获取目标的标签值,并将其赋值给label
变量。如果在labelMap
中没有找到对应的标签,就将t.label
的值直接赋给label
。 -
使用
cv2.putText()
在图像帧上绘制目标的标签、ID和状态信息。cv2.putText()
函数用于在图像上绘制文本,可以指定文本内容、位置、字体、字体大小、颜色等参数。 -
使用
cv2.rectangle()
在图像帧上绘制目标矩形框,以及使用定义的color
绘制框的颜色。
在窗口中显示图像帧,并在帧上绘制神经网络的帧率信息
cv2.putText(frame, "NN fps: {:.2f}".format(fps), (2, frame.shape[0] - 4), cv2.FONT_HERSHEY_TRIPLEX, 0.4, color)cv2.imshow("tracker", frame)if cv2.waitKey(1) == ord('q'):break
这段代码用于在窗口中显示图像帧,并在帧上绘制神经网络的帧率信息。
-
通过
cv2.putText()
在图像帧上绘制NN fps: {:.2f}
格式化字符串,用来显示神经网络的帧率信息。字符串中的{:.2f}
是一个占位符,用来显示一个浮点数,保留两位小数。format(fps)
中的fps
是一个变量,表示神经网络的帧率。(2, frame.shape[0] - 4)
表示文本的位置,坐标为(2, frame.shape[0] - 4)
,即文本在图像帧左上角的位置。cv2.FONT_HERSHEY_TRIPLEX
表示使用的字体类型,0.4
表示字体的大小,color
表示文本的颜色。 -
通过
cv2.imshow()
将图像帧显示在名为 “tracker” 的窗口中。 -
通过
cv2.waitKey(1)
检测键盘输入。如果按下的键是 ‘q’ (对应的 ASCII 值是 ord(‘q’)),则跳出循环,终止显示图像帧的操作。
Setup 12:运行程序
在终端中输入如下指令运行程序
python main.py
运行看下效果,可以看到相机能实时追踪杯子的位置