news 2026/6/10 8:16:09

基于YOLOv5+DeepSORT的轻量级车流人流实时统计工具(含源码、预训练模型与部署文档)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YOLOv5+DeepSORT的轻量级车流人流实时统计工具(含源码、预训练模型与部署文档)

本文还有配套的精品资源,点击获取

简介:直接运行就能用的车流和人流实时统计方案,用YOLOv5做检测、DeepSORT做连续追踪,支持摄像头视频流或本地MP4文件输入。程序自动给每辆车、每个人分配唯一ID,画出运动轨迹,还能设定进出区域线,实时统计双向通过数量。核心代码分模块封装:main.py是主入口,detector.py调用yolov5m.pt完成目标识别,tracker.py处理ID关联与轨迹维持,utils里有坐标转换、ROI区域绘制、计数逻辑等实用函数。不需要重新训练,开箱即用,CPU也能跑,CUDA环境可加速。依赖包在requirements.txt里列得清清楚楚,安装步骤和参数说明都写在文档里,所有关键代码带中文注释,比如IOU匹配怎么算、卡尔曼滤波怎么更新状态、轨迹怎么平滑处理。支持自定义计数区域形状、进出方向判定规则,结果能一键导出CSV表格。适合学校门口、商场出入口、小区闸机、普通道路卡口这类对精度要求适中、部署要快的场景。

1. 项目概述:为什么这个轻量级统计工具能真正落地进一线场景?

我做智能视觉类项目快八年了,从最早在嵌入式板子上硬啃OpenCV,到后来带团队部署上百路AI分析服务器,踩过的坑比走过的路还多。最常被问到的问题不是“能不能识别”,而是“能不能今天下午就装上、明天早上就出数”。很多标榜“高精度”的方案,一落地就卡在环境适配、显存不足、配置复杂、结果不准这四座大山里——模型太大跑不动,参数调不好漏检多,区域画不准计数飘,导出个CSV还得手动写脚本。这套基于YOLOv5+DeepSORT的车流人流实时统计工具,就是我去年在三个校园出入口、两个社区闸机和一个老城区单行道卡口反复迭代出来的“能用、好用、敢用”的轻量级方案。

它不是实验室里的Demo,而是我在真实光照变化(早晚逆光、正午强曝、阴天灰蒙)、常见遮挡(伞、背包、树影、广告牌)、低帧率视频(老旧摄像头20fps以下)和混合目标密度(早高峰校门口单车流+人流交织)下实测打磨出来的。核心逻辑非常务实:用yolov5m这个精度与速度的黄金平衡点模型做检测,不追求SOTA但确保92%以上车辆召回率和87%行人mAP;用DeepSORT做ID维持,重点优化了短时遮挡下的ID跳变问题;所有计数逻辑全部基于像素坐标几何判定,不依赖深度学习回归,稳定得像尺子量线段。你不需要懂卡尔曼滤波公式,也不用重训模型,python main.py --source cam --roi "[(100,300),(800,300)]" --direction "in"这一条命令就能让摄像头画面里自动跳出红蓝双色计数框,左边显示“进:47”,右边显示“出:32”,同时每辆车/人头顶飘着ID号,身后拖着淡绿色轨迹线——这就是它交付的第一印象。

关键词里“YOLOv5”和“DeepSORT”不是贴标签,而是决定了整个系统的骨架韧性:“YOLOv5”意味着你可以直接换用自己微调过的权重(比如针对电动车头盔、校服颜色做增强),而不用动检测模块结构;“DeepSORT”则保证了即使目标在画面中消失2秒(比如被公交车挡住),再出现时ID依然连续,不会从#12突然变成#89。至于“车辆统计”“行人计数”“多目标追踪”,它们不是并列功能,而是同一套流水线的自然输出:检测是眼睛,追踪是记忆,计数是大脑对记忆的解读。这套工具真正解决的,是基层运维人员面对一堆视频流时最朴素的需求——不翻日志、不看曲线、不调参数,一眼看清“此刻有多少人/车正在通过,方向是什么,趋势是增是减”。它适合谁?学校后勤主任、商场物业经理、社区安防主管、交通协管员——这些不需要写代码、但需要每天看数据做决策的人。下面我就带你一层层拆开它的血肉,告诉你每一行关键代码为什么这么写,每一个参数背后藏着什么现场教训。

2. 整体架构与设计思路:为什么选YOLOv5m而不是YOLOv8或YOLOv11?

2.1 检测-追踪-计数三层解耦:拒绝“一锅炖”的工程陷阱

很多开源项目把检测、跟踪、绘图、计数全塞在一个main.py里,初看简洁,实则灾难。我接手过一个类似项目,客户要求把计数线从水平改成斜线,结果改了17处坐标计算,测试时发现轨迹绘制模块因为没同步更新坐标系,ID连线全歪了。所以本项目的结构化封装不是炫技,而是血泪教训后的必然选择:

  • detector.py:只干一件事——输入一帧图像,输出[x1,y1,x2,y2,conf,cls]格式的检测框列表。它不关心ID、不画轨迹、不判进出。yolov5m.pt权重直接加载,预处理固定为640x640缩放+归一化,后处理用non_max_suppression去重,置信度阈值默认0.45(经实测,在校园场景下低于0.4易误检树叶晃动,高于0.5会漏掉部分背影行人)。这里的关键设计是支持动态置信度调整detector.detect(img, conf_thres=0.5)可传参覆盖默认值,方便你在不同光照条件下快速调试。

  • tracker.py:接收detector输出的检测框,输出带唯一ID的追踪结果[x1,y1,x2,y2,track_id,cls]。它内部封装了DeepSORT的完整流程:用cosine_distance计算外观特征相似度(基于ReID模型提取的128维向量),用iou_matching做运动预测匹配(IOU阈值设为0.2,太低导致ID乱串,太高导致新目标无法关联),卡尔曼滤波器状态向量是[cx,cy,a,h,vx,vy](中心点x/y、宽高比、高度、x/y方向速度),观测更新时只修正位置,不修正速度——这是防止遮挡后速度突变的关键技巧。你完全不用碰deep_sort/deep_sort.py源码,所有接口都已封装成Tracker.update(detections)一行调用。

  • utils目录:这才是真正体现“能用”的地方。roi_utils.pydraw_roi()函数支持四种ROI类型:线段(用于单向计数)、矩形(用于区域驻留统计)、多边形(用于不规则闸机口)、折线(用于Z字形通道)。counter.py里的count_crossing()不是简单判断中心点过线,而是用线段相交算法:把目标轨迹上连续两帧的中心点连成线段,与计数线段求交点,只有交点在线段有效范围内才算一次有效穿越——这彻底解决了目标沿计数线平行移动被误计的问题。plot_utils.pydraw_tracks()函数默认开启轨迹平滑,用的是指数加权移动平均(EWMA),衰减因子α=0.3,既过滤了单帧抖动,又保留了急转弯的真实路径。

这种三层解耦带来的直接好处是:你想换检测模型?只改detector.py里几行加载权重和推理代码;想升级跟踪算法?只替换tracker.py里的update逻辑;要增加新的计数规则(比如“停留超30秒计为滞留”)?只在utils/counter.py里加一个函数。没有牵一发而动全身的恐惧。

2.2 为什么是YOLOv5m?一场关于“够用就好”的参数博弈

YOLO系列版本众多,为什么死守YOLOv5?不是情怀,是实测数据说话。我对比过YOLOv5s/m/l、YOLOv8n/s/m、YOLOv10n/s在相同硬件(Intel i7-10870H + RTX 3060 Laptop)上的表现:

模型输入尺寸CPU推理速度(FPS)CUDA推理速度(FPS)校园车辆mAP@0.5行人mAP@0.5内存占用(MB)
YOLOv5s640x6408.242.184.3%78.6%320
YOLOv5m640x6405.128.792.7%87.2%680
YOLOv5l640x6403.319.594.1%89.5%1120
YOLOv8s640x6406.835.290.5%85.1%590
YOLOv10n640x6407.538.988.2%83.7%410

表面看YOLOv5s更快,但它的漏检率在背光场景下飙升——早自习结束时,大量学生背着光走出教学楼,YOLOv5s对轮廓模糊的行人召回率跌到63%,而YOLOv5m仍保持82%。YOLOv5l精度略高,但内存占用超1GB,在老旧工控机上极易OOM。YOLOv8s虽然速度不错,但它默认的anchor匹配机制对小目标(如远处自行车)不够友好,我们实测需额外添加--multi-scale参数才能改善,这又增加了部署复杂度。

YOLOv5m成了那个“甜点区”:它用更深的网络结构(相比s版多出3个C3模块)提升了小目标特征提取能力,又没像l版那样堆砌参数拖慢速度。更重要的是,它的训练策略成熟稳定——yolov5m.pt是在COCO+UA-DETRAC+自建校园数据集上三阶段finetune得到的,特别强化了对“穿校服学生”、“电动车+骑手”、“雨伞遮挡”三类难点样本的学习。你拿到的这个权重,不是通用COCO权重,而是带着特定场景烙印的“领域适应版”。

提示:如果你的场景是高速公路卡口(目标大、速度快、背景单一),可以尝试用YOLOv5s提速;如果是医院门诊楼入口(目标密集、遮挡多、需高召回),建议微调YOLOv5m权重——我们提供了configs/yolov5m.yaml,只需修改nc: 2(车辆/行人两类)和data: data/custom.yaml指向你的标注数据即可,无需重写训练脚本。

3. 核心模块解析与实操要点:从IOU匹配到轨迹平滑的每一行代码

3.1 detector.py:不只是调用模型,更是鲁棒性第一道防线

打开detector.py,你会看到核心函数detect()。它看似简单,但藏着三个关键鲁棒性设计:

def detect(self, img, conf_thres=0.45, iou_thres=0.45): # 1. 自适应预处理:根据图像长宽比动态填充,避免拉伸变形 h, w = img.shape[:2] r = self.img_size / max(h, w) # 保持原始比例缩放 if r != 1: interp = cv2.INTER_AREA if r < 1 else cv2.INTER_LINEAR img = cv2.resize(img, (int(w * r), int(h * r)), interpolation=interp) # 填充至640x640,用(114,114,114)灰色填充(YOLOv5训练时的均值) pad_w = self.img_size - img.shape[1] pad_h = self.img_size - img.shape[0] img = cv2.copyMakeBorder(img, 0, pad_h, 0, pad_w, cv2.BORDER_CONSTANT, value=(114, 114, 114)) # 2. 推理前的防错处理:空图/损坏图直接跳过 if img.size == 0: return np.empty((0, 6)) # 3. 后处理:NMS后强制过滤小框(面积<300像素) pred = non_max_suppression(pred, conf_thres, iou_thres, classes=[0,1], agnostic=False) # 过滤掉检测框面积过小的目标(消除噪声点) areas = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) valid_mask = areas > 300 pred = pred[valid_mask]

第一处“自适应预处理”解决了实际部署中最头疼的问题:摄像头分辨率五花八门(1280x720、1920x1080、甚至老式模拟转数字的704x576)。如果粗暴resize到640x640,圆形车轮会被压扁成椭圆,模型识别率断崖下跌。这里的r = self.img_size / max(h, w)确保最长边缩放到640,短边按比例缩放,再用灰色填充——这和YOLOv5训练时的数据增强方式完全一致,模型见惯了这种输入,自然更稳。

第二处“防错处理”看似多余,但在24小时运行中极其重要。某次部署在校园东门,凌晨三点摄像头因低温短暂失联,返回的img是空数组,没这行检查,程序直接崩溃。现在它安静地返回空列表,主循环继续下一帧,运维人员根本无感。

第三处“小框过滤”是经验之谈。YOLOv5在检测极小目标(如20米外的自行车)时,容易在背景噪声中生成大量面积<100像素的虚警框。这些框进入tracker后,会触发大量无效的卡尔曼初始化,严重拖慢速度。300像素是个经验值:对应640x640输入下约20x15像素的框,足以覆盖3米外的行人头部或车辆牌照区域,又过滤掉了绝大多数噪声。

注意:classes=[0,1]硬编码指定了只检测车辆(COCO class 2)和行人(COCO class 0)。如果你的数据集类别索引不同,必须修改此处!别指望模型自己猜——这是新手最容易栽跟头的地方。

3.2 tracker.py:DeepSORT不是黑箱,理解卡尔曼滤波才能驯服它

tracker.py的核心是Tracker.update()。它接收detector输出的detections(numpy array of [x1,y1,x2,y2,conf,cls]),返回tracks(list of [x1,y1,x2,y2,track_id,cls])。很多人以为DeepSORT就是“调个库”,其实它的稳定性70%取决于卡尔曼滤波器的参数配置。我们来看关键片段:

# 在__init__中初始化卡尔曼滤波器 self.kf = KalmanFilter() self.kf.F = np.array([[1,0,0,0,1,0], # 状态转移矩阵:[cx,cy,a,h,vx,vy] [0,1,0,0,0,1], [0,0,1,0,0,0], [0,0,0,1,0,0], [0,0,0,0,1,0], [0,0,0,0,0,1]]) self.kf.H = np.array([[1,0,0,0,0,0], # 观测矩阵:只观测位置(cx,cy,a,h),不观测速度 [0,1,0,0,0,0], [0,0,1,0,0,0], [0,0,0,1,0,0]]) # update()中关键步骤: # 1. 对每个未匹配的track,执行卡尔曼预测(预测下一帧位置) for track in self.tracks: track.predict() # 内部调用kf.predict() # 2. 匹配时,用马氏距离(Mahalanobis distance)替代简单IOU # 因为马氏距离考虑了协方差,对速度突变更鲁棒 matches, unmatched_tracks, unmatched_detections = \ linear_assignment.min_cost_matching( self.metric, self.max_age, self.tracks, detections, gated_metric=self.gated_metric) # 3. 更新匹配成功的track:只用观测值修正位置,不修正速度 for track_idx, detection_idx in matches: track = self.tracks[track_idx] detection = detections[detection_idx] track.update(detection) # 内部调用kf.update(observation)

这里最关键的洞察是:卡尔曼滤波器的状态向量包含速度(vx,vy),但观测矩阵H只映射位置(cx,cy,a,h),不映射速度。这意味着,当目标被遮挡时,滤波器会根据历史速度预测位置;当目标重新出现,观测值只用来修正位置,速度分量保持预测值——这正是DeepSORT能处理2秒内遮挡的核心机制。如果你错误地把H设为单位阵,让观测值强行修正速度,那么目标一露头,速度就会被“打回原形”,导致轨迹剧烈抖动。

gated_metric函数定义了匹配的“门限”:只有马氏距离小于chi2inv95[4]=9.488(对应4维观测空间的95%置信度)的检测框才被允许匹配。这个值不能随便改!太小(如5)会导致匹配过于苛刻,ID频繁断裂;太大(如20)会导致错误关联。我们实测9.488在校园场景下ID连续性最佳。

实操心得:遇到ID频繁跳变(比如#15突然变成#42),第一反应不是调IOU阈值,而是检查max_age参数。它定义了track在未匹配状态下能存活的最大帧数。校园场景推荐设为30(即半秒),太短(15)来不及等遮挡恢复,太长(60)会导致大量幽灵ID累积。修改方式:tracker = Tracker(max_age=30)

3.3 utils/counter.py:计数逻辑的几何本质——线段相交才是王道

计数不准,90%源于错误的判定逻辑。很多项目用“中心点过线法”:目标中心点x坐标超过计数线x值就算穿越。这在目标垂直过线时没问题,但一旦目标斜着走、或者沿计数线平行移动(比如排队进校门的学生),就会疯狂误计。我们的count_crossing()采用严格的线段相交判定

def count_crossing(self, track_history, line_start, line_end): """ 判定轨迹是否穿越计数线段 track_history: [(x1,y1), (x2,y2), ...] 连续帧中心点坐标 line_start, line_end: 计数线段端点 """ if len(track_history) < 2: return False, None # 将轨迹转换为线段列表:[(p0,p1), (p1,p2), ...] segments = [] for i in range(len(track_history)-1): seg = (track_history[i], track_history[i+1]) segments.append(seg) # 遍历每一段轨迹线段,与计数线段求交点 for seg in segments: p1, p2 = seg q1, q2 = line_start, line_end # 使用标准线段相交算法(向量叉积法) def cross_product(o, a, b): return (a[0]-o[0])*(b[1]-o[1]) - (a[1]-o[1])*(b[0]-o[0]) d1 = cross_product(q1, q2, p1) d2 = cross_product(q1, q2, p2) d3 = cross_product(p1, p2, q1) d4 = cross_product(p1, p2, q2) # 严格相交判定(排除端点重合等边界情况) if ((d1 > 0 and d2 < 0) or (d1 < 0 and d2 > 0)) and \ ((d3 > 0 and d4 < 0) or (d3 < 0 and d4 > 0)): # 计算交点坐标 t = d1 / (d1 - d2) intersection_x = p1[0] + t * (p2[0] - p1[0]) intersection_y = p1[1] + t * (p2[1] - p1[1]) # 交点必须在线段q1q2的有效范围内(非延长线) if min(q1[0], q2[0]) <= intersection_x <= max(q1[0], q2[0]) and \ min(q1[1], q2[1]) <= intersection_y <= max(q1[1], q2[1]): # 判定方向:计算交点处轨迹向量与计数线向量的点积 traj_vec = (p2[0]-p1[0], p2[1]-p1[1]) line_vec = (q2[0]-q1[0], q2[1]-q1[1]) dot_product = traj_vec[0]*line_vec[0] + traj_vec[1]*line_vec[1] direction = 'in' if dot_product > 0 else 'out' return True, direction return False, None

这段代码的价值在于:它把计数从“像素坐标比较”提升到了“几何关系计算”。当你画一条从(100,300)到(800,300)的水平线时,它不会把沿这条线行走的学生算作穿越,因为他们的轨迹线段与计数线段平行(叉积恒为0),永不相交。只有当学生真正从线的一侧走到另一侧,轨迹线段才会与计数线段产生有效交点。

方向判定用点积而非角度,是因为点积计算快且鲁棒:traj_vec · line_vec > 0意味着轨迹向量与计数线向量夹角小于90度,即“朝向线段正方向运动”。这比计算atan2角度再比大小快一个数量级,且避免了角度跳变(如从359°跳到0°)导致的方向误判。

提示:line_startline_end的顺序决定了“in/out”的定义。(100,300)->(800,300)定义了从左到右为in,反之则为out。画ROI时务必用鼠标从起点拖拽到终点,顺序不能反。

4. 完整实操流程:从零开始部署,到产出第一份CSV报表

4.1 环境搭建:为什么requirements.txt里藏着玄机?

不要跳过这一步!我见过太多人直接pip install -r requirements.txt然后报错退出。这份依赖清单是经过交叉验证的:

# requirements.txt 关键项解析 torch==1.12.1+cu113 # 必须匹配你的CUDA版本!RTX3060对应cu113,GTX1060对应cu112 torchvision==0.13.1+cu113 opencv-python==4.8.0.76 # 4.8.x是最后一个支持Python3.7的版本,兼容老旧系统 numpy==1.23.5 scipy==1.10.1 # DeepSORT特征匹配必需 filterpy==1.4.5 # 卡尔曼滤波实现库 PyYAML==6.0.1 tqdm==4.65.0

CUDA版本陷阱torch==1.12.1+cu113中的cu113表示CUDA Toolkit 11.3。如果你的nvidia-smi显示驱动版本是515.65.01,它最高支持CUDA 11.7,但PyTorch官方wheel只提供到cu113/cu116。强行安装cu117会报libcudnn.so.8: cannot open shared object file。解决方案:要么降级驱动(不推荐),要么用conda安装(conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=11.3 -c pytorch),conda会自动解决CUDA运行时依赖。

OpenCV版本深坑:新版OpenCV(4.9+)移除了cv2.dnn.readNetFromDarknet,而我们的detector.py用的是YOLOv5原生的.pt格式,不涉及此API。但cv2.imshow()在某些Linux发行版(如Ubuntu 22.04)上需要libgtk-3-0apt install libgtk-3-0即可。Windows用户若遇cv2.imshow()黑屏,试试cv2.namedWindow('frame', cv2.WINDOW_NORMAL)cv2.imshow()

安装命令(推荐conda,环境隔离更干净):

# 创建新环境(Python 3.8是YOLOv5官方推荐版本) conda create -n yolo-count python=3.8 conda activate yolo-count # 安装PyTorch(根据你的GPU选cu113/cu116/cu118) pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113 # 安装其余依赖 pip install -r requirements.txt

4.2 首次运行:从命令行到可视化界面的全流程

一切就绪后,用最简单的命令启动:

# 方式1:读取本地MP4文件(推荐新手先试) python main.py --source data/test.mp4 --roi "[(200,400),(700,400)]" --direction "in" # 方式2:调用USB摄像头(Linux下通常是/dev/video0) python main.py --source 0 --roi "[(150,250),(650,250)]" --direction "in" # 方式3:RTSP网络流(海康/大华摄像头常用) python main.py --source "rtsp://admin:password@192.168.1.64:554/stream1" --roi "[(100,300),(800,300)]" --direction "both"

--roi参数是核心!它接受一个Python元组字符串,定义计数线段的两个端点。注意括号和逗号是字符串的一部分,必须用英文字符。--direction有三个选项:
-"in":只统计从线段起点指向终点方向的穿越
-"out":只统计反方向
-"both":双向统计,界面显示左右两个计数框

首次运行时,你会看到:
1. 左上角实时FPS显示(如FPS: 24.3),这是系统吞吐能力的直观反馈
2. 画面中央出现红色线段(即你定义的ROI),两端有小圆点标记
3. 每个检测目标头顶显示绿色ID标签(如ID: 12),身后拖着淡绿色轨迹线
4. 右上角出现计数面板:IN: 0 | OUT: 0
5. 当目标穿越红线时,对应数字实时递增,同时终端打印日志:[INFO] ID 15 crossed IN at frame 1247

实操心得:第一次运行若画面卡顿,先检查FPS是否低于15。如果是,立即降低输入分辨率:python main.py --source 0 --img-size 480(默认640)。480x480对计数精度影响极小,但速度可提升40%。

4.3 ROI区域精调:如何用三步法画出精准计数线?

画不准ROI是计数误差的最大来源。我们提供了一套傻瓜式三步法:

第一步:粗定位
运行python main.py --source 0 --roi "[(0,0),(0,0)]"(传入无效坐标),程序会启动摄像头并显示空白画面。此时按键盘r键,画面会冻结,你可用鼠标在图像上任意位置点击两次,程序自动记录两点坐标并显示为红线。这是最快捷的初始定位。

第二步:微调坐标
观察红线是否恰好穿过你想要统计的通道中心。如果不是,编辑main.py--roi参数的坐标。例如,你发现红线偏高,把[(150,250),(650,250)]改为[(150,280),(650,280)](y坐标+30)。记住:y坐标越大,线越靠下(OpenCV坐标系原点在左上角)。

第三步:验证方向
用一个已知方向的目标(如你自己)从左向右走过红线,观察计数框是IN还是OUT递增。如果反了,交换ROI坐标的顺序:[(650,280),(150,280)]。或者更简单,直接改--direction "out"

注意:对于Z字形通道(如商场扶梯口),单线段不够用。这时用--roi-type polygon,配合utils/roi_utils.py里的draw_polygon_roi()函数,用鼠标点击多个点画出多边形ROI,计数逻辑会自动切换为“进出多边形”模式。

4.4 结果导出:CSV不只是表格,更是决策依据

计数结果实时显示只是开始,导出CSV才是价值闭环。程序内置一键导出:

# 运行时按键盘 's' 键,立即保存当前计数结果到csv文件 # 默认保存为 results/counting_20240520_143215.csv # 文件内容: # timestamp,id,category,direction,frame_num # 2024-05-20 14:32:15.234,12,vehicle,in,1247 # 2024-05-20 14:32:16.891,15,person,out,1253 # 2024-05-20 14:32:18.042,8,vehicle,in,1261

这个CSV的设计直击管理痛点:
-timestamp精确到毫秒,方便与考勤/门禁系统时间戳对齐
-id是目标唯一ID,可用于回溯视频查具体是谁/哪辆车
-category区分vehicle/person,支持分类统计
-direction明确进出方向,避免人工统计歧义
-frame_num是原始帧序号,可直接用ffmpeg -i input.mp4 -vf "select='eq(n\,1247)'" -vframes 1 frame1247.jpg抽帧验证

你还可以用Pandas做二次分析:

import pandas as pd df = pd.read_csv("results/counting_*.csv") # 每分钟进出人数 df['minute'] = pd.to_datetime(df['timestamp']).dt.floor('T') minute_stats = df.groupby(['minute','direction']).size().unstack(fill_value=0) print(minute_stats) # 输出: # direction in out # minute # 2024-05-20 14:32 12 8 # 2024-05-20 14:33 15 11

5. 常见问题与排查技巧实录:那些文档里不会写的现场真相

5.1 典型问题速查表

现象可能原因排查命令/操作解决方案
FPS极低(<5)GPU未启用/显存不足nvidia-smi查看GPU使用率;watch -n 1 'free -h'看内存确认PyTorch CUDA可用:python -c "import torch; print(torch.cuda.is_available())";降低--img-size--batch-size 1
检测框大量漂移(抖动)轨迹平滑过度/摄像头抖动查看utils/plot_utils.pyalpha值;用手机拍一段画面看是否物理抖动alpha从0.3调至0.5(增强平滑);加装云台或软件稳像(需额外模块)
ID频繁跳变(#12→#42)max_age过小/遮挡严重tracker.py中临时打印len(self.tracks)看track数量是否爆炸增长增大max_age至45;检查ROI是否画在易遮挡区域(如树荫下)
计数不触发(数字不动)ROI坐标错误/方向反了运行时按d键显示debug信息,看轨迹线是否与ROI相交r键重画ROI;交换ROI坐标顺序或改--direction
程序启动报错ModuleNotFoundError: No module named 'models'当前目录非项目根目录ls查看是否有models/utils/detector.py等文件cd K3QXnp42K6M9I53MRwSj-master-71f58de43243de1eb7fbacb35fc7242e30e2823c进入正确目录

5.2 独家避坑技巧:来自三个真实部署现场

技巧1:应对“鬼影”干扰(老式摄像头CMOS拖影)
某社区闸机用的是10年前的模拟摄像头转数字,强光下车辆尾灯会产生长达1秒的拖影,被误检为多个目标。解决方案:在detector.pydetect()函数末尾,加入帧间差分抑制

# 在NMS后添加(需全局保存上一帧检测框) if hasattr(self, 'prev_detections') and len(self.prev_detections) > 0: # 计算当前框与上一帧框的IOU,过滤IOU>0.7的重复检测(拖影) ious = box_iou(pred[:, :4], self.prev_detections[:, :4]) max_iou_per_pred = ious.max(dim=1)[0] pred = pred[max_iou_per_pred < 0.7] self.prev_detections = pred.clone()

技巧2:解决“ID粘连”(密集人群ID合并)
商场促销日人流峰值达200人/分钟,DeepSORT默认的max_cosine_distance=0.2导致外观相似者(如穿同款黑衣)被错误关联。临时方案:运行时动态增大阈值:

python main.py --source 0 --roi "[(100,300),(800,300)]" --max-cosine-distance 0.35

--max-cosine-distance参数直接透传给tracker,0.35是密集场景实测最优值。

技巧3:零配置导出视频(给领导看效果)
领导要看效果但不想装环境?用FFmpeg一键合成带计数的视频:

# 先运行程序,按's'键保存CSV,按'q'退出 # 再执行(假设原始视频是input.mp4,计数结果在results/) ffmpeg -i input.mp4 -vf "drawtext=fontfile=/path/to/font.ttf:fontsize=24:fontcolor=red:x=10:y=10:text='IN: %{eif:$(cat results/count_in.txt):d}':text='OUT: %{eif:$(cat results/count_out.txt):d}'" -c:a copy output_with_count.mp4

(注:count_in.txt需在程序中添加实时写入逻辑,一行一个数字)

6. 场景扩展与定制化:从“能用”到“专属”

这套工具的生命力在于它的可塑性。我帮客户做的三个定制案例,展示了它如何超越开箱即用:

案例1:校园电动车专项统计
某职校要求单独统计电动车(含骑手),不与普通车辆混计。做法:
- 新增类别:修改yolov5m.yamlnc: 3,增加electric_bike
- 微调权重:用500张电动车图片finetune,仅训练最后三层(--freeze 0
- 修改detector.py:classes=[0,2,3](0=person, 2=car, 3=electric_bike)
- 计数逻辑:utils/counter.pycategory_map = {0:'person', 2:'car', 3:'ebike'},导出CSV时自动分类

案例2:道路卡口车型细分
交警大队需要区分小轿车、货车、客车。做法:
- 复用YOLOv5m的COCO预训练权重,但修改datasets.py加载自定义数据集,包含8个车型子类
- tracker.py中track.cls不再只是0/1,而是0-7的整数,计数面板显示CAR:12 | TRUCK:3 | BUS:1
- 关键创新:在utils/plot_utils.py中,根据track.cls给轨迹线赋予不同颜色(轿车蓝、货车黄、客车红),一目了然

案例3:无网络环境离线部署
某偏远小学无公网,但需每日导出报表。做法:
- 打包为独立exe:pip install pyinstallerpyinstaller --onefile --add-data "weights;yolov5m.pt;." --add-data "configs;." main.py
- 生成counting_tool.exe,双击即运行,所有依赖打包进单文件
- 导出CSV后,自动压缩为daily_report_20240520.zip,放入U盘带走

最后分享一个小技巧:如果你只需要统计,不需要实时画面,关闭OpenCV显示能提升20%速度。修改main.py,注释掉cv2.imshow()cv2.waitKey()相关行,添加--no-display参数,程序将纯后台运行,只输出CSV和日志。这对部署在树莓派等边缘设备上尤其有用——它安静地在角落工作,每天清晨生成一份Excel报表,放在共享文件夹里等你查阅。这,才是技术该有的样子:不喧哗,自有声。

本文还有配套的精品资源,点击获取

简介:直接运行就能用的车流和人流实时统计方案,用YOLOv5做检测、DeepSORT做连续追踪,支持摄像头视频流或本地MP4文件输入。程序自动给每辆车、每个人分配唯一ID,画出运动轨迹,还能设定进出区域线,实时统计双向通过数量。核心代码分模块封装:main.py是主入口,detector.py调用yolov5m.pt完成目标识别,tracker.py处理ID关联与轨迹维持,utils里有坐标转换、ROI区域绘制、计数逻辑等实用函数。不需要重新训练,开箱即用,CPU也能跑,CUDA环境可加速。依赖包在requirements.txt里列得清清楚楚,安装步骤和参数说明都写在文档里,所有关键代码带中文注释,比如IOU匹配怎么算、卡尔曼滤波怎么更新状态、轨迹怎么平滑处理。支持自定义计数区域形状、进出方向判定规则,结果能一键导出CSV表格。适合学校门口、商场出入口、小区闸机、普通道路卡口这类对精度要求适中、部署要快的场景。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 8:10:58

3DS格式转换神器:3分钟搞定.3ds转CIA的完整方案

3DS格式转换神器&#xff1a;3分钟搞定.3ds转CIA的完整方案 【免费下载链接】3dsconv Python script to convert Nintendo 3DS CCI (".cci", ".3ds") files to the CIA format 项目地址: https://gitcode.com/gh_mirrors/3d/3dsconv 你是否曾经遇到…

作者头像 李华
网站建设 2026/6/10 8:09:56

AI外贸培训哪家值得信赖

在数字化转型的浪潮中&#xff0c;AI技术正以前所未有的速度重塑外贸行业。从智能客户开发到营销内容自动化&#xff0c;从谈判话术优化到团队效率提升&#xff0c;AI工具成为外贸从业者不可或缺的“超级助理”。然而&#xff0c;面对市场上琳琅满目的AI外贸培训课程&#xff0…

作者头像 李华
网站建设 2026/6/10 8:05:49

泛微OA e-cology10 如何修改账号信息

在使用泛微OA e-cology10 &#xff0c;需要修改现有账号信息&#xff1a;账号和登录手机号&#xff0c;但是在后台维护界面中&#xff0c;这两个字段无法直接修改。 登录系统的数据库&#xff0c;找到ecology10-user_info表&#xff0c;其中ACCOUNT代表账号&#xff0c;MOBILE代…

作者头像 李华
网站建设 2026/6/10 8:03:54

Bilibili-Old终极指南:5步找回经典B站,让时光倒流

Bilibili-Old终极指南&#xff1a;5步找回经典B站&#xff0c;让时光倒流 【免费下载链接】Bilibili-Old 恢复旧版Bilibili页面&#xff0c;为了那些念旧的人。 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Old 你是否怀念那个简洁纯粹的B站&#xff1f;当新…

作者头像 李华
网站建设 2026/6/10 8:02:23

接口调用的代码实现:从入门到实战

接口调用是现代软件开发中最基础、最核心的技能之一。本文将从最基础的 HTTP 请求讲起&#xff0c;逐步深入到生产级的接口调用方案&#xff0c;涵盖多种技术栈和实际场景。一、基础篇&#xff1a;HTTP 请求的核心原理1.1 HTTP 请求的本质一个完整的 HTTP 请求包含以下要素&…

作者头像 李华
网站建设 2026/6/10 7:56:06

CBCX怎么样?围绕投教支持与信息透明度展开评测

当用户从更成熟的角度选择平台时&#xff0c;稳定体验和规范表达往往比单一卖点更重要。外汇服务行业进入更重视规范表达和用户保护的阶段后&#xff0c;平台评测也需要从多个细节展开。CBCX受到关注的原因&#xff0c;不只在于品牌露出&#xff0c;更在于它能否围绕稳定运行、…

作者头像 李华