使用 TrafficCV 进行更多 AI 交通速度检测





5.00/5 (3投票s)
在本文中,我们专注于开发一个计算机视觉框架,该框架可以在实时和录制的车辆交通视频上运行各种机器学习和神经网络模型(如SSD MobileNet)。
引言
交通速度检测是一个大生意。世界各地的市政当局利用它来阻止超速行驶并通过超速罚单创收。但传统的测速仪,通常基于 RADAR 或 LIDAR,价格昂贵。
本系列文章将向您展示如何仅使用深度学习构建一个相当准确的交通速度检测器,并在像树莓派这样的边缘设备上运行它。
欢迎您从 TrafficCV Git 仓库 下载本系列文章的代码。我们假设您熟悉 Python,并具备 AI 和神经网络的基础知识。
在上一篇文章中,我们讨论了使用Haar对象检测器和对象相关跟踪器实现的车辆速度检测算法的基础。两者都运行在CPU上,并且消耗了大量的CPU资源。本文将重点介绍开发一个小型的计算机视觉框架,该框架可以使用CPU或Edge TPU在实时和录制的车辆交通视频上运行各种机器学习和神经网络模型(如SSD MobileNet)。这将使我们能够轻松地比较不同模型和计算车辆速度的公式在性能方面的表现。
检测器:基础知识
我们确定了交通速度检测器需要实时执行的任务
- 逐帧处理实时视频或其他视频源
- 检测和跟踪车辆对象
- 测量和估计车辆对象速度
- 在视频窗口中显示对象边界框和其他信息
从开发者的角度来看,我们还需要提供一些设施
- 提供基本的用户界面,包括轻松退出视频处理循环的功能
- 在命令行中输入必要的参数,例如交通视频源和检测器
- 在命令行中输入检测器参数,例如帧计数器间隔
- 从程序中打印信息性和调试语句
我们的TrafficCV微型框架由两部分组成:CLI和Detector类。TrafficCV CLI是程序的主界面。它允许用户为程序执行设置参数。CLI使用argparse模块中的标准ArgumentParser
类。
parser = argparse.ArgumentParser()
parser.add_argument("--debug", help="Enable debug-level logging.",
action="store_true")
parser.add_argument("--test", help="Test if OpenCV can read from the default camera device.",
action="store_true")
参数动态地提供给args
变量。例如,--test
参数表明我们只想测试我们的视频输入和视频输出是否正常工作。
if args.test:
import cv2 as cv
info('Streaming from default camera device.')
cap = cv.VideoCapture(0)
...
CLI还提供了一种方法,用户可以通过启动一个单独的线程来监视键盘输入,从而退出任何视频循环。
threading.Thread(target=kbinput.kb_capture_thread, args=(), name='kb_capture_thread', daemon=True).start()
kbinput
模块中的kb_capture_thread()
函数只是等待用户按下Enter键,然后将KBINPUT
全局标志设置为True
。
KBINPUT = False
def kb_capture_thread():
"""Capture a keyboard input."""
global KBINPUT
input()
KBINPUT = True
在我们主要的视频处理循环中,我们测试KBINPUT标志的值,如果为True则退出循环。
--model
参数决定了要使用的计算机视觉或深度学习模型。为了使用TrafficCV的模型,您必须
基础Detector类运行主要的视频处理循环,并执行必要的视频处理和计算机视觉任务。该类依赖于Python 抽象基类来定义一个具有必须由继承类实现的抽象方法的Python类。
class Detector(abc.ABC):
"""A video object detector using a neural network or other computer vision learning model."""
def __init__(self, name, model_dir, video_source, args):
self.name = name
self.model_dir = model_dir
self.video_source = video_source
self.video = cv2.VideoCapture(self.video_source)
self.video_end = False
if self.video_source == 0:
self.video.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.video.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
self.video.set(cv2.CAP_PROP_FPS, 60)
self._height, self._width, self._fps = int(self.video.get(cv2.CAP_PROP_FRAME_HEIGHT)), int(self.video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(self.video.get(cv2.CAP_PROP_FPS))
info(f'Video resolution: {self._width}x{self._height} {self._fps}fps.')
self.args = args
@abc.abstractmethod
def get_label_for_index(self, i):
"""Get the string label for an integer index."""
@abc.abstractmethod
def detect_objects(self, frame):
"""Detect objects in a video frame."""
@abc.abstractmethod
def print_model_info(self):
"""Print out information on model."""
基础类的构造函数初始化从视频源的捕获,或者如果源是摄像头,它将摄像头分辨率设置为1280 * 720 @60fps。有三个抽象方法:get_label_for_index
、detect_objects
和print_model_info
。这些名称不言自明:一个方法获取数字ID的文本标签,一个方法检测帧中的对象,还有一个打印模型描述。
类的其余部分包含每个Detector必须实现的通用函数的实现。例如,Haar级联分类器就是使用这个基础类实现的。
class Detector(detector.Detector):
"""Haar cascade classifier running on CPU."""
def __init__(self, model_dir, video_source, args):
super().__init__("Haar cascade classifier on CPU", model_dir, video_source, args)
self.model_file = os.path.join(model_dir, 'haarcascade_kraten.xml')
if not os.path.exists(self.model_file):
error(f'{self.model_file} does not exist.')
sys.exit(1)
self.classifier = cv2.CascadeClassifier(self.model_file)
self.video_width = 1280
self.video_height = 720
构造函数使用我们model
目录中的文件创建一个CascadeClassifier
实例。video_width
和video_height
成员是检测器期望的帧的尺寸。detect_objects
方法的实现如下所示:
def detect_objects(self, frame):
image = cv2.cvtColor(cv2.resize(frame, (self.video_width, self.video_height)), cv2.COLOR_BGR2GRAY)
cars = self.classifier.detectMultiScale(image, 1.1, 13, 18, (24, 24))
scale_x, scale_y = (self._width / self.video_width), (self._height / self.video_height)
def make(i):
x, y, w, h = cars[i]
return Object(
id=0,
score=None,
bbox=BBox(xmin = x,
ymin= y,
xmax=x+w,
ymax=y+h).scale(scale_x, scale_y))
return [make(i) for i in range(len(cars))
代码比以前的要短得多:我们所要做的就是对提供的帧运行推理,并返回每个检测到的对象的边界框。唯一的功能性变化是我们将边界框缩放到原始视频帧的大小,以便它与我们的dlib对象跟踪器的大小相匹配。所有其他任务,例如创建对象跟踪器、删除PSR小于七的对象或在帧上叠加信息,都由基础Detector类代码处理。
检测器实战
在Linux或Windows上激活我们的cv
Python虚拟环境后,我们可以像这样从TrafficCV CLI调用我们的模型:
tcv EUREKA:0.0 --model haarcascade_kraten --video ../TrafficCV/demo_videos/cars_vertical.mp4
我们应该看到类似以下的内容:
CLI打印出当前视频处理FPS等信息,当检测到一个新对象时,视频窗口会将这些信息叠加到原始视频上。
Detector
类还负责跟踪内部FPS和CPU负载。
(cv) allisterb@glitch:~/Projects/TrafficCV $ tcv EUREKA:0.0 --model haarcascade_kraten --video ../TrafficCV/demo_videos/cars_vertical.mp4
_______ ___ ___ __ ______ ___ ___
|_ _|.----.---.-.' _|.' _|__|.----.| | | |
| | | _| _ | _|| _| || __|| ---| | |
|___| |__| |___._|__| |__| |__||____||______|\_____/
v0.1
06:45:55 AM [INFO] Video window enabled. Press ENTER key or press any key in the video window to stop.
06:45:55 AM [INFO] Using Haar cascade classifier on CPU.
06:45:55 AM [INFO] Video resolution: 640x360 25fps.
06:45:56 AM [INFO] ppm argument not specified. Using default value 8.8.
06:45:56 AM [INFO] fc argument not specified. Using default value 10.
06:45:57 AM [INFO] New object detected at (241, 13, 16, 16) with id 0.
06:45:57 AM [INFO] New object detected at (221, 42, 30, 30) with id 1.
06:45:57 AM [INFO] New object detected at (258, 22, 23, 23) with id 2.
06:45:57 AM [INFO] New object detected at (319, 133, 53, 53) with id 3.
06:45:57 AM [INFO] Internal FPS: 5; CPU#1: 31.0%; CPU#2: 23.2%; CPU#3: 63.7%; CPU#4: 24.2%; Objects currently tracked: 4.
06:46:00 AM [INFO] New object detected at (210, 243, 63, 63) with id 4.
06:46:00 AM [INFO] Internal FPS: 4; CPU#1: 23.7%; CPU#2: 16.0%; CPU#3: 66.3%; CPU#4: 14.6%; Objects currently tracked: 5.
06:46:03 AM [INFO] Internal FPS: 3; CPU#1: 21.5%; CPU#2: 14.7%; CPU#3: 70.3%; CPU#4: 14.6%; Objects currently tracked: 5.
06:46:06 AM [INFO] Internal FPS: 3; CPU#1: 23.0%; CPU#2: 15.2%; CPU#3: 72.1%; CPU#4: 13.9%; Objects currently tracked: 5.
06:46:09 AM [INFO] Internal FPS: 3; CPU#1: 18.4%; CPU#2: 18.3%; CPU#3: 69.7%; CPU#4: 13.4%; Objects currently tracked: 5.
06:46:12 AM [INFO] New object detected at (326, 225, 70, 70) with id 5.
06:46:12 AM [INFO] Internal FPS: 3; CPU#1: 20.8%; CPU#2: 17.0%; CPU#3: 70.1%; CPU#4: 14.1%; Objects currently tracked: 6.
06:46:16 AM [INFO] Internal FPS: 3; CPU#1: 26.9%; CPU#2: 15.7%; CPU#3: 65.1%; CPU#4: 13.9%; Objects currently tracked: 6.
在上一篇文章中,我们提到,当在Pi上使用远程X显示时,CPU使用率不高,而FPS保持较低。我们推测将X帧通过网络发送的时间阻塞了视频处理的执行。我们可以使用CLI的--nowindow
参数来指示TrafficCV程序在视频处理期间根本不显示视频窗口。
(cv) allisterb@glitch:~/Projects/TrafficCV $ tcv EUREKA:0.0 --model haarcascade_kraten --video ../TrafficCV/demo_videos/cars_vertical.mp4 --nowindow
_______ ___ ___ __ ______ ___ ___
|_ _|.----.---.-.' _|.' _|__|.----.| | | |
| | | _| _ | _|| _| || __|| ---| | |
|___| |__| |___._|__| |__| |__||____||______|\_____/
v0.1
06:42:50 AM [INFO] Video window disabled. Press ENTER key to stop.
06:42:50 AM [INFO] Using Haar cascade classifier on CPU.
06:42:50 AM [INFO] Video resolution: 640x360 25fps.
06:42:50 AM [INFO] ppm argument not specified. Using default value 8.8.
06:42:50 AM [INFO] fc argument not specified. Using default value 10.
06:42:51 AM [INFO] New object detected at (241, 13, 16, 16) with id 0.
06:42:51 AM [INFO] New object detected at (221, 42, 30, 30) with id 1.
06:42:51 AM [INFO] New object detected at (258, 22, 23, 23) with id 2.
06:42:51 AM [INFO] New object detected at (319, 133, 53, 53) with id 3.
06:42:51 AM [INFO] Internal FPS: 10; CPU#1: 43.3%; CPU#2: 45.4%; CPU#3: 43.3%; CPU#4: 99.0%; Objects currently tracked: 4.
06:42:53 AM [INFO] New object detected at (210, 243, 63, 63) with id 4.
06:42:53 AM [INFO] Internal FPS: 7; CPU#1: 22.8%; CPU#2: 24.2%; CPU#3: 23.8%; CPU#4: 100.0%; Objects currently tracked: 5.
06:42:55 AM [INFO] Internal FPS: 6; CPU#1: 20.4%; CPU#2: 21.5%; CPU#3: 19.5%; CPU#4: 100.0%; Objects currently tracked: 5.
06:42:57 AM [INFO] Internal FPS: 5; CPU#1: 19.9%; CPU#2: 22.2%; CPU#3: 20.3%; CPU#4: 100.0%; Objects currently tracked: 5.
06:42:59 AM [INFO] Internal FPS: 5; CPU#1: 19.1%; CPU#2: 20.5%; CPU#3: 20.9%; CPU#4: 100.0%; Objects currently tracked: 5.
06:43:01 AM [INFO] New object detected at (326, 225, 70, 70) with id 5.
06:43:02 AM [INFO] Internal FPS: 5; CPU#1: 20.7%; CPU#2: 22.2%; CPU#3: 20.3%; CPU#4: 100.0%; Objects currently tracked: 6.
06:43:04 AM [INFO] Internal FPS: 5; CPU#1: 18.1%; CPU#2: 18.6%; CPU#3: 17.2%; CPU#4: 100.0%; Objects currently tracked: 6.
06:43:06 AM [INFO] Internal FPS: 5; CPU#1: 18.0%; CPU#2: 20.7%; CPU#3: 19.0%; CPU#4: 100.0%; Objects currently tracked: 6.
06:43:09 AM [INFO] New object detected at (209, 199, 65, 65) with id 6.
06:43:09 AM [INFO] Internal FPS: 4; CPU#1: 19.1%; CPU#2: 20.8%; CPU#3: 18.4%; CPU#4: 100.0%; Objects currently tracked: 7.
06:43:11 AM [INFO] Internal FPS: 4; CPU#1: 18.5%; CPU#2: 19.9%; CPU#3: 17.9%; CPU#4: 100.0%; Objects currently tracked: 7.
在这里,我们看到一个CPU核心的利用率为100%,而FPS几乎翻倍。在跟踪六个对象时,禁用视频窗口后,我们能够在Pi上获得5 FPS,而启用视频窗口后只有3 FPS。
基础Detector类处理所有的跟踪、测量以及统计信息和信息性消息的打印。实现Detector接口的类只需要指定如何在特定视频帧中检测对象,而TrafficCV CLI和库函数则处理其余的任务。这使我们能够快速实现各种模型和选项并比较结果。有了基础库,我们可以研究测量车辆速度的不同方法以及我们可以使用的不同深度学习模型。
下一步
在下一篇文章中,我们将探讨测量车辆速度的不同方法以及我们可以在TrafficCV程序中使用的不同深度学习模型进行对象检测。敬请关注!