├── mobilenet_ssd
├── MobileNetSSD_deploy.caffemodel
└── MobileNetSSD_deploy.prototxt
├── multi_object_tracking_fast.py
├── race.mp4
├── race_output_slow.avi
└── race_output_fast.avi
python3, OpenCV, dlib, python多进程模块(multiprocessing)
from imutils.video import FPS
import multiprocessing
import numpy as np
import argparse
import imutils
import dlib
import cv2
通过使用python的Process类来生成一个新的进程(每个进程都独立于原始进程)
python多处理是指Python将调用此函数,然后创建一个全新的解释器来执行其中的代码。因此,每个启动跟踪程序生成的进程将独立于其父进程。要与Python驱动程序脚本通信,我们需要利用管道或队列。这两种类型的对象都是线程/进程安全的,使用锁和信号量完成。
实质上,我们正在建立一种简单的生产者/消费者关系:
父进程将生成新帧并将它们添加到特定对象跟踪器的队列中。
子进程将使用帧,应用对象跟踪,然后返回更新的边界框坐标。
首先,我们将尝试从第21行的inputQueue中获取一个新帧。
如果帧不为空,我们将获取帧,然后更新对象跟踪器,从而获得更新的边界框坐标(第24-34行)。
最后,我们将标签和边界框加入outputQueue,以便父进程可以在脚本的主循环中利用它们(第38行)。
回到父进程,我们将分析命令行参数:
def start_tracker(box, label, rgb, inputQueue, outputQueue):
'''
brief : 从边界框坐标构造一个dlib矩形对象,然后启动相关跟踪器
:param box: 由检测器返回的边界框坐标
:param label: 检测的对象标签
:param rgb: 启动初始dlib对象跟踪器的RGB有序图像
:param inputQueue: 输入队列
:param outputQueue: 输出队列
:return: null
'''
# t就是一个追踪器对象
t = dlib.correlation_tracker()
rect = dlib.rectangle(box[0], box[1], box[2], box[3])
t.start_track(rgb, rect)
# 这个函数将作为守护进程调用,所以不用担心join它
while True:
# 尝试从输入队列中获取下一帧
rgb = inputQueue.get()
# 如果队列里有一项,那么就处理它
if rgb is not None:
# 更新追踪器并且获取追踪对象的位置
t.update(rgb)
pos = t.get_position()
# 分析对象的位置
startX = int(pos.left())
startY = int(pos.top())
endX = int(pos.right())
endY = int(pos.bottom())
# 把对象标签和边界框加入到输出队列,坐标信息是传的元组
outputQueue.put((label, (startX, startY, endX, endY)))
–prototxt:Caffe“deploy”prototxt文件的路径。
–model:prototxt附带的模型文件的路径。
–video:输入视频文件的路径。我们将在这段视频中使用dlib执行多目标跟踪。
–outpuy:输出视频文件的可选路径。如果未指定路径,则不会将视频输出到磁盘。我建议输出到.avi或.mp4文件。
–confidence:对象检测置信阈值为0.2的可选覆盖。该值表示从目标检测器中过滤弱检测的最小概率。
# 构造命令行参数并解析他们
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,
help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,
help="path to Caffe pre-trained model")
ap.add_argument("-v", "--video", required=True,
help="path to input video file")
ap.add_argument("-o", "--output", type=str,
help="path to optional output video file")
ap.add_argument("-c", "--confidence", type=float, default=0.2,
help="minimum probability to filter weak detections")
args = vars(ap.parse_args())
这些队列将保存我们正在跟踪的对象。生成的每个进程都需要两个队列对象:
一个读取输入帧
第二个进行写结果
# 初始化输入输出队列, 我们要追踪每一个物体
inputQueues = []
outputQueues = []
# 初始化Caffe网络已经训练好的检测的对象列表
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
"sofa", "train", "tvmonitor"]
# 从磁盘加载序列化模型
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])
# initialize the video stream and output video writer
# 初始化视频流和输出视频对象
print("[INFO] starting video stream...")
vs = cv2.VideoCapture(args["video"])
writer = None
# 启动FPS吞吐量计数器,计算视频的FPS值
fps = FPS().start()
# 遍历视频流的每一个帧
while True:
# 获取帧
(grabbed, frame) = vs.read()
# 判断是否为最后一帧
if frame is None:
break
# 重新调整帧大小,转变成RGB格式
frame = imutils.resize(frame, width=600)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 如果要有输出的视频,初始化writer进行写视频
if args["output"] is not None and writer is None:
fourcc = cv2.VideoWriter_fourcc(*"MJPG")
writer = cv2.VideoWriter(args["output"], fourcc, 30,
(frame.shape[1], frame.shape[0]), True)
如果没有输入队列(第101行),那么我们知道需要在对象跟踪之前应用对象检测。
我们在第103-109行应用目标检测,然后在第112行对结果进行循环。我们获取我们的置信值并过滤掉115-119行上的弱检测。
如果我们的置信度满足由命令行参数建立的阈值,我们会考虑检测,但我们会进一步通过类标签过滤掉它。在本例中,我们只寻找“person”对象(第122-127行)。
假设我们找到了一个“人”,我们将创建队列并生成跟踪进程:
我们首先计算第131-133行的边界框坐标。
从这里我们创建了两个新队列,iq和oq(第137和138行),分别将它们附加到inputQueues和outputQueues(第139和140行)。
从那里我们产生一个新的开始追踪过程,通过边界框,标签,rgb图像,和iq+oq(第143-147行)。别忘了在这里阅读更多关于多处理的内容。
我们还绘制了被检测对象的边框矩形和类标签(第151-154行)。
否则,我们已经执行了对象检测,因此需要将每个dlib对象跟踪器应用于帧:
# 如果输入队列为空,那么说明我们还没有创建第一个对象追踪器
if len(inputQueues) == 0:
# 获取帧的尺寸,转化为blob(caffe模型检测的基本单元)
(h, w) = frame.shape[:2]
# 归一化 : 0.007843等于127.5分之一
blob = cv2.dnn.blobFromImage(frame, 0.007843, (w, h), 127.5)
# 将blob输入到网络中,获取到检测结果,返回的是4维矩阵
'''
[0 0 i 1]:置信度
[0 0 i 2]:标签序号
[0 0 i 3:7]:检测对象的xy坐标
'''
net.setInput(blob)
detections = net.forward()
# 遍历矩阵
for i in np.arange(0, detections.shape[2]):
# 提取相关置信度,即检测到对象的概率
confidence = detections[0, 0, i, 2]
# 通过设定的最小置信度,过滤掉弱检测目标
if confidence > args["confidence"]:
# 获取检测到的对象标签,与之前的类标签序号对应
idx = int(detections[0, 0, i, 1])
label = CLASSES[idx]
# 这里我们主要检测人,也可以通过设置参数,这样就可以检测类标签里面的对象
if CLASSES[idx] != "person":
continue
# 计算目标对象边界框的坐标
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
bb = (startX, startY, endX, endY)
# 分别创建两个全新的输入和输出队列
iq = multiprocessing.Queue()
oq = multiprocessing.Queue()
inputQueues.append(iq)
outputQueues.append(oq)
# 为新的对象跟踪程序生成守护进程
p = multiprocessing.Process(
target=start_tracker,
args=(bb, label, rgb, iq, oq))
p.daemon = True
p.start()
# 抓取相应的类标签进行检测,并绘制边界框,绿色,二维
cv2.rectangle(frame, (startX, startY), (endX, endY),
(0, 255, 0), 2)
# 放上类名字
cv2.putText(frame, label, (startX, startY - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)
在每个输入队列上循环,我们向它们添加rgb图像(第162和163行)。
然后在每个输出队列(第166行)上循环,从每个独立的对象跟踪器(第171行)获取边界框坐标。最后,我们在第175-178行画出边界框+相关的类标签。
# 输出队列不为空,说明我们已经有检测结果,所以我们继续追踪多个对象
else:
# 在每个输入队列上循环并添加输入RGB帧,使我们能够更新在不同进程中运行的每个对象跟踪器
for iq in inputQueues:
iq.put(rgb)
# 在每个输出队列上循环
for oq in outputQueues:
# 抓取到更新后的边界框
# get方法是一个阻塞操作,因此因此这将暂停我们的执行,直到进程完成跟踪更新
(label, (startX, startY, endX, endY)) = oq.get()
# 从追踪器画出边界框
cv2.rectangle(frame, (startX, startY), (endX, endY),
(0, 255, 0), 2)
cv2.putText(frame, label, (startX, startY - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)
如有必要,我们将帧写入输出视频,并将帧显示到屏幕(第181-185行)。
如果按下“q”键,我们“退出”,跳出循环(第186-190行)。
如果我们继续处理帧,fps计算器将在第193行更新,然后在while循环的开始处再次开始处理。
否则,我们将处理帧,并显示FPS吞吐量信息+释放指针并关闭窗口。
# 判断是否需要输出视频
if writer is not None:
writer.write(frame)
# 播放处理后的帧
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# 按'q'退出
if key == ord("q"):
break
# 更新FPS计数器
fps.update()
# 停止计数器并且显示FPS值
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# 释放写指针
if writer is not None:
writer.release()
# 收尾工作
cv2.destroyAllWindows()
vs.release()
参考网址:https://www.pyimagesearch.com/2018/10/29/multi-object-tracking-with-dlib/
代码模型视频下载地址:https://download.csdn.net/download/qq903952032/12516940