赞
踩
1、本项目通过yolov8/yolov7/yolov5 5.0和deepsort实现了一个多功能智能交通监控系统,可为一些同学的课设、大作业等提供参考。分别实现了不同车辆的跟踪,统计不同车型“上行”和“下行”的数量,实时检测车辆速度,检测两车是否发生碰撞或者距离过近时进行碰撞预警,检测车辆是否违规进入专用车道(例如非公交车进入公交车专用车道,非法占用高速应急车道等行为)。最终检测效果如下,红色框内区域即为自定义的公交车专用车道。
2、可训练自己的数据集,可以换成yolov8/yolov7/yolov5各种版本的权重。
不熟悉pycharm的anaconda的大兄弟请先看这篇csdn博客,了解pycharm和anaconda的基本操作。
https://blog.csdn.net/ECHOSON/article/details/117220445
anaconda安装完成之后请切换到国内的源来提高下载速度 ,命令如下:
conda config --remove-key channels
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.bfsu.edu.cn/anaconda/cloud/pytorch/
conda config --set show_channel_urls yes
pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple
首先创建python3.8的虚拟环境,请在命令行中执行下列操作:
conda create -n yolov8 python==3.8.5
conda activate yolov8
实际测试情况是YOLOv8在CPU和GPU的情况下均可使用,不过在CPU的条件下训练那个速度会令人发指,所以有条件的小伙伴一定要安装GPU版本的Pytorch,没有条件的小伙伴最好是租服务器来使用。GPU版本安装的具体步骤可以参考这篇文章:https://blog.csdn.net/ECHOSON/article/details/118420968。
需要注意以下几点:
1、安装之前一定要先更新你的显卡驱动,去官网下载对应型号的驱动安装
2、30系显卡只能使用cuda11的版本
3、一定要创建虚拟环境,这样的话各个深度学习框架之间不发生冲突
我这里创建的是python3.8的环境,安装的Pytorch的版本是1.8.0,命令如下:
conda install pytorch==1.8.0 torchvision torchaudio cudatoolkit=10.2 # 注意这条命令指定Pytorch的版本和cuda的版本
conda install pytorch==1.8.0 torchvision==0.9.0 torchaudio==0.8.0 cpuonly # CPU的小伙伴直接执行这条命令即可
安装完毕之后,我们来测试一下GPU是否可以有效调用:
pip install pycocotools-windows
另外的话大家还需要安装程序其他所需的包,包括opencv,matplotlib这些包,不过这些包的安装比较简单,直接通过pip指令执行即可,我们在终端的虚拟环境中直接执行下列指令即可完成包的安装。
pip install -r requirements.txt
如下代码所示,可根据自己需求更改。使用yolov5s.pt、best.pt、或yolov7s.pt/yolov8s.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")
使用yolov8/yolov7/yolov5和deepsort分别实现车辆的目标检测、跟踪、计数,算法流程如下,计数原理是将整个视频图像区域划分为四个象限,把第一象限和第三象限的车辆分别认为是下行和上行的车辆。计数具体代码实现时使用了一系列if-eif条件语句,对目标框的中心坐标以及四个象限边界的位置关系进行判断。
以视频中红色横线为基准,统计car、bus、truck三种车型“上行”和“下行”的数量和总共的数量,并显示在图像左上角,同时将车型、车辆ID打印在车辆检测框的正上方。可以拓展到行人跟踪等多个领域,只要将训练好的所需类别的yolov8/yolov7/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存到一个列表里面,读取列表,和设定的速度阈值进行比较。若超速,则发出警告。需要注意的是,每判断完一帧后,要将这个列表进行清空防止帧之间的数据混淆,同时防止列表溢出(虽然不大可能)。
首先将yolov8/yolov7/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一下。
项目内容:
包含完整word版本说明文档,可用于写论文、课设报告的参考。
资源获取:
获取整套代码、测试视频、训练好的权重和说明文档(有偿)
上交硕士,技术够硬,也可以指导深度学习毕设、大作业等。
--------------->qq------------
3582584734
------------------------------
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。