1、本项目通过yolov5和deepsort实现了一个多功能智能交通监控系统,可为一些同学的课设、大作业等提供参考。分别实现了不同车辆的跟踪,统计不同车型“上行”和“下行”的数量,实时检测车辆速度,检测两车是否发生碰撞或者距离过近时进行碰撞预警,检测车辆是否违规进入专用车道(例如非公交车进入公交车专用车道,非法占用高速应急车道等行为)。最终检测效果如下,红色框内区域即为自定义的公交车专用车道。
2、可训练自己的数据集,可以换成yolov5各种版本的权重。
pip install torch==1.7.1+cu110 torchvision==0.8.2+cu110 torchaudio===0.7.2 -f https://download.pytorch.org/whl/torch_stable.html
pip install -r requirements.txt
如下代码所示,可根据自己需求更改。使用yolov5s.pt、yolov5m.pt、yolov5l.pt、yolov5x.pt预训练权重均可,也可以使用自己训练好的权重,本项目中调用的是训练好的可以检测car、truck、bus三个类别的权重。
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--weights', type=str, default='best.pt', help='model.pt path') # 加载的v5权重,可以是公开数据集的预训练权重,也可以是自己数据集训练的权重
parser.add_argument('--source', type=str, default='IMG_6894.mp4', help='source') # 待检测的视频路径
parser.add_argument('--output', type=str, default='test_out', help='output folder') # output folder
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
parser.add_argument('--conf-thres', type=float, default=0.6, help='object confidence threshold')
parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS')
parser.add_argument('--fourcc', type=str, default='mp4v', help='output video codec (verify ffmpeg support)')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--view-img', action='store_true', help='display results')
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
parser.add_argument('--classes', nargs='+', type=int, default=[0, 1, 2], help='filter by class') # car、truck、bus
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
parser.add_argument('--augment', action='store_true', help='augmented inference')
parser.add_argument("--config_deepsort", type=str, default="deep_sort/configs/deep_sort.yaml")
使用yolov5和deepsort分别实现车辆的目标检测、跟踪、计数,算法流程如下,计数原理是将整个视频图像区域划分为四个象限,把第一象限和第三象限的车辆分别认为是下行和上行的车辆。计数具体代码实现时使用了一系列if-eif条件语句,对目标框的中心坐标以及四个象限边界的位置关系进行判断。
以视频中红色横线为基准,统计car、bus、truck三种车型“上行”和“下行”的数量和总共的数量,并显示在图像左上角,同时将车型、车辆ID打印在车辆检测框的正上方。可以拓展到行人跟踪等多个领域,只要将训练好的所需类别的yolov5权重替换掉,代码里面做一些修改就行了。
首先使用提前设定好的车辆真实宽度和检测出来的车辆像素宽度求出真实距离和像素距离的比值,再使用每辆车的前后两帧框的中心坐标计算出两帧之间移动的像素距离。利用这个比值和像素距离做映射,就可以求出两帧之间车辆移动的真实距离。然后距离除以两帧之间的时间,就是速度了。本测速算法中将车辆真实移动距离与像素移动距离看成是线性关系,仅在监控相机轴线与车辆移动方向垂直时才能成立,并且检测出来的车辆框在空间上会产生一定形变,使得真实距离和像素距离的映射关系不准确。有兴趣的同学可以在代码中加入透视变换,将图像变成类似于遥感数据的俯瞰图,实现测速后再将图像变换为原始图像视角,就能实现比较准确的车辆测速了。
我的项目将测速代码封装到了Estimated_speed()函数里面,有详细注释,调用即可。
def Estimated_speed(locations, fps, width):
present_IDs = []
prev_IDs = []
work_IDs = []
work_IDs_index = []
work_IDs_prev_index = []
work_locations = [] # 当前帧数据:中心点x坐标、中心点y坐标、目标序号、车辆类别、车辆像素宽度
work_prev_locations = [] # 上一帧数据,数据格式相同
speed = []
for i in range(len(locations[1])):
present_IDs.append(locations[1][i][2]) # 获得当前帧中跟踪到车辆的ID
for i in range(len(locations[0])):
prev_IDs.append(locations[0][i][2]) # 获得前一帧中跟踪到车辆的ID
for m, n in enumerate(present_IDs):
if n in prev_IDs: # 进行筛选,找到在两帧图像中均被检测到的有效车辆ID,存入work_IDs中
work_IDs.append(n)
work_IDs_index.append(m)
for x in work_IDs_index: # 将当前帧有效检测车辆的信息存入work_locations中
work_locations.append(locations[1][x])
for y, z in enumerate(prev_IDs):
if z in work_IDs: # 将前一帧有效检测车辆的ID索引存入work_IDs_prev_index中
work_IDs_prev_index.append(y)
for x in work_IDs_prev_index: # 将前一帧有效检测车辆的信息存入work_prev_locations中
work_prev_locations.append(locations[0][x])
for i in range(len(work_IDs)):
speed.append(
math.sqrt((work_locations[i][0] - work_prev_locations[i][0]) ** 2 + # 计算有效检测车辆的速度,采用线性的从像素距离到真实空间距离的映射
(work_locations[i][1] - work_prev_locations[i][1]) ** 2) * # 当视频拍摄视角并不垂直于车辆移动轨迹时,测算出来的速度将比实际速度低
width[work_locations[i][3]] / (work_locations[i][4]) * fps / 5 * 3.6 * 2)
for i in range(len(speed)):
speed[i] = [round(speed[i], 1), work_locations[i][2]] # 将保留一位小数的单位为km/h的车辆速度及其ID存入speed二维列表中
return speed
将计算出来的速度实时显示在车辆正上方。可以针对不同的道路环境设定一个速度阈值,例如高速上通常限速为120km/h,将每一帧中各车辆的速度和ID存到一个列表里面,读取列表,和设定的速度阈值进行比较。若超速,则发出警告。需要注意的是,每判断完一帧后,要将这个列表进行清空防止帧之间的数据混淆,同时防止列表溢出(虽然不大可能)。
首先将yolov5检测出来的车辆框通过两层for循环嵌套读取出来,将任意两个车辆框进行两两匹配,再通过一系列的if和elif语句判断两框位置关系,若两框相交区域的宽度或高度超过一定像素t,或存在包含与被包含关系,则判定为碰撞,将其车辆ID存入列表中。t值可以按照实际需求自己调整,t值越大,碰撞越不容易检出。当t为一个较小的值时,可当做碰撞预警功能来使用。
本项目将这个功能封装到了find_accidents()函数里面,直接调用即可。核心判断语句代码如下:
# 通过A、B框位置关系判断其是否发生碰撞,若相交、存在包含关系,则判定为碰撞,将其索引加入crash_index列表中。
if (B_xmin < A_xmin + t and A_xmin + t < B_xmax - t and A_xmax > B_xmax - t) and (
B_ymin < A_ymin + t and A_ymin + t < B_ymax - t and A_ymax > B_ymax - t): # 01
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (B_xmin < A_xmin + t and A_xmin + t < B_xmax - t and A_xmax > B_xmax - t) and (
A_ymin < B_ymin and B_ymin < B_ymax and A_ymax < B_ymax): # 02
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (B_xmin < A_xmin + t and A_xmin + t < B_xmax - t and A_xmax > B_xmax - t) and (
A_ymin < B_ymin + t and B_ymin + t < A_ymax - t and B_ymax > A_ymax - t): # 03
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (A_xmin < B_xmin and B_xmin < B_xmax and A_xmax > B_xmax) and (
B_ymin < A_ymin + t and A_ymin + t < B_ymax - t and A_ymax > B_ymax - t): # 04
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (A_xmin < B_xmin + t and B_xmin + t < A_xmax - t and B_xmax > A_xmax - t) and (
B_ymin < A_ymin + t and A_ymin + t < B_ymax - t and A_ymax > B_ymax - t): # 05
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (A_xmin < B_xmin + t and B_xmin + t < A_xmax - t and B_xmax > A_xmax - t) and (
A_ymin < B_ymin and B_ymin < B_ymax and A_ymax > B_ymax): # 06
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (A_xmin < B_xmin + t and B_xmin + t < A_xmax - t and B_xmax > A_xmax - t) and (
A_ymin < B_ymin + t and B_ymin + t < A_ymax - t and B_ymax > A_ymax - t): # 07
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (A_xmin < B_xmin and B_xmin < B_xmax and A_xmax > B_xmax) and (
A_ymin < B_ymin + t and B_ymin + t < A_ymax - t and B_ymax > A_ymax - t): # 08
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (A_xmin < B_xmin and B_xmin < A_xmax and B_xmax > A_xmax) and (
B_ymin < A_ymin and A_ymin < A_ymax and B_ymax > A_ymax): # 09
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (B_xmin < A_xmin and A_xmin < B_xmax and A_xmax > B_xmax) and (
B_ymin < A_ymin and A_ymin < A_ymax and B_ymax > A_ymax): # 10
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (B_xmin < A_xmin and A_xmin < A_xmax and B_xmax > A_xmax) and (
A_ymin < B_ymin and B_ymin < A_ymax and B_ymax > A_ymax): # 11
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
elif (B_xmin < A_xmin and A_xmin < A_xmax and B_xmax > A_xmax) and (
B_ymin < A_ymin and A_ymin < B_ymax and A_ymax > B_ymax): # 12
is_accident_happen.append([A_class, B_class])
crash_index.append([i, j])
当两辆车距离过近,导致检测框出现相交、重叠、甚至包含关系时发出碰撞警报。这里定义了一个像素阈值t,可通过t值大小来调节碰撞检测的灵敏程度。
def find_accidents(rects):
is_accident_happen = []
t = 3000 # 设定阈值,这个值越大,碰撞越不容易检出。设置一个较小的值时,可当做碰撞预警功能来使用。
crash_index = []
首先使用opencv截取视频的第一帧作为背景,再通过python的交互模式绘制专用车道矩形区域(此处以违规进入公交车道检测为例),鼠标按顺时针方向依次点击矩形框的四个顶点即可。专用区域绘制结束后会将四个顶点坐标以及矩形四条边的直线方程保存至根目录下的car_line.txt中。这一步结束后调用detect()函数,读取car_line.txt,利用车辆矩形框的中心点坐标和专用车道区域的位置关系进行判断。若非公交车违规进入公交车专用车道,则发出警告。判断逻辑如下:
# 判断车辆框的中心是否在绘制的矩形区域内且该车辆是否为公交车,若不是公交车,则违规驶入公交车道。
# 算法思路为将目标框中心点坐标依次代入四条直线方程内,使用中心点纵坐标分别与代入计算出来的值进行比较,满足所列大小关系即该点在矩形框内部。
for box_center in box_centers:
if (k[0] * box_center[0] + b[0] <= box_center[1] and k[1] * box_center[0] + b[1] >= box_center[1]
and k[2] * box_center[0] + b[2] >= box_center[1] and k[3] * box_center[0] + b[3] <= box_center[1] and (
box_center[3] != 1)):
cv2.putText(im0, 'Entering illegally!', (int(box_center[0] - 140), int(box_center[1])),
cv2.FONT_HERSHEY_PLAIN, 1.5, [0, 0, 255], 2)
其中违规进入专用车道检测这个功能还可以再完善一下,比如一些地区的公交专用车道设置了使用时间段,在时间段之外允许非公交车进入,可以通过调用接口获取系统时间来实现更好的判断,这个功能也可以拓展到更多的应用场景里面,如检测高速上常见的违规进入应急车道等行为。在车辆分布密集时有一些漏检,导致统计的车辆数目会比实际的略少,可以使用自己的数据打标签后针对性fine tune一下。
本项目效果展示视频:
https://www.bilibili.com/video/BV1E3411G7cP/
获取整套代码、测试视频、训练好的权重和说明文档,欢迎各位大兄弟来找我。
qq:3582584734 (有偿咨询,也可以指导深度学习毕设、大作业等)