ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

YOLOv5+DeepSORT实现车流人流量统计:从原理到工程实践

2026/9/4 22:29:52 拓冰建站 浏览量
YOLOv5+DeepSORT实现车流人流量统计:从原理到工程实践 简介本资源是一套基于YOLOv5与DeepSORT算法实现的高速移动目标流量统计算法工程面向计算机视觉初学者与智能交通、安防监控领域的开发者解决视频流中车流与人流量实时跨线计数的实际问题。项目支持多条检测线绘制并可独立统计每条线双向穿越目标数量具备较强实用性与扩展性。压缩包共117个文件涵盖55个Python核心脚本含模型推理、轨迹跟踪、计数逻辑、20个YAML/YML配置文件用于模型参数与检测线定义、8个Markdown说明文档及4个实测MP4演示视频另有Dockerfile、IPython Notebook教程与License等辅助文件整体大小为79.09MB。目前已有211人学习下载提供完整可运行环境Python 3.8 CUDA 10.2、requirements依赖清单、多场景测试动图及详细项目说明开箱即用便于快速验证算法效果与二次开发。1. 项目背景与核心价值最近在做一个智慧交通相关的项目其中有一个核心需求是统计特定路段的车流量和人流量。听起来很简单不就是数数吗但实际场景一上问题就来了车流速度快、行人密集、遮挡严重用传统的背景差分或者简单的检测框计数效果惨不忍睹不是漏计就是重复计数。这时候一个稳定可靠的“检测跟踪”方案就成了刚需。我最终选定的技术栈是YOLOv5配合DeepSORT并且成功跑通了整个流程从环境搭建到模型训练再到最终的统计逻辑实现。这个组合在目标检测与多目标跟踪领域算是“黄金搭档”了尤其适合我们这种对实时性和准确性都有要求的移动目标统计场景。简单来说这个项目的核心就是用 YOLOv5 快速准确地“认出”每一辆车和每一个人然后用 DeepSORT 给每个被认出的目标分配一个唯一的“身份证”ID并持续追踪它的运动轨迹。最后我们只需要在视频画面中虚拟地画一条“计数线”当某个目标的轨迹穿过这条线时就累加一次计数。这样无论是双向八车道的滚滚车流还是地铁口熙熙攘攘的人潮我们都能得到一个相对准确的统计数字。这个源码包的价值就在于它提供了一个完整、可运行、可修改的工程范例让你能跳过从零搭建的无数个坑直接聚焦于业务逻辑的优化。2. 技术选型为什么是 YOLOv5 DeepSORT在开始动手之前我们必须搞清楚为什么是这两个组件而不是其他方案。这决定了后续所有工作的效率和最终效果的上限。2.1 YOLOv5平衡速度与精度的检测利器目标检测模型很多比如更早的 YOLOv3、两阶段的 Faster R-CNN、以及后来的 YOLOv8、DETR 等。选择 YOLOv5 主要基于以下几点实战考量优异的性能平衡YOLOv5 在 COCO 数据集上的表现尤其是在速度和精度mAP的权衡上做得非常出色。对于车流、人流统计这种需要处理视频流通常是 25-30 FPS的场景推理速度至关重要。YOLOv5s小模型在普通 GPU 上可以达到 100 FPS即使在 CPU 上也能达到可用的速度这为后续的跟踪算法留出了充足的计算时间。极致的工程友好性YOLOv5 的代码库由 Ultralytics 维护其易用性在业界有口皆碑。它提供了从数据准备、模型训练、验证到导出的完整 pipeline并且有非常清晰的命令行接口和 Python API。对于快速原型开发和部署来说这能节省大量时间。活跃的社区与丰富的预训练模型社区为 YOLOv5 贡献了针对各种场景车辆、行人、头盔等的预训练权重。我们完全可以从一个在 COCO 上预训练好的、包含“car”和“person”类别的模型开始进行微调Fine-tuning这比从头训练要快得多效果也更有保障。灵活的模型尺寸YOLOv5 提供了从 n纳米、s小、m中、l大到 x超大五种尺寸的模型。我们可以根据实际硬件资源是在边缘设备 RK3568/RV1106 上跑还是在服务器上跑和精度要求灵活选择。例如在 RK3568 这类嵌入式设备上可能就需要对 YOLOv5s 甚至更小的模型进行量化、剪枝等优化。注意虽然 YOLOv8 等更新版本已经发布且在某些指标上更优但 YOLOv5 的生态成熟度、资料丰富度和部署友好度对于大多数工业和学术项目来说依然是首选。我们的目标是“解决问题”而不是“追求最新”。2.2 DeepSORT简单有效的多目标跟踪器检测只能告诉我们“当前帧有哪些目标”但不知道“上一帧的那个目标是不是这一帧的这个”。这就需要多目标跟踪MOT。DeepSORT 是 SORT 算法的增强版其核心优势在于马氏距离 外观特征融合SORT 算法只使用卡尔曼滤波预测目标位置并用匈牙利算法基于 IoU交并比进行关联。这在目标运动规律、遮挡较少时有效但一旦发生遮挡或快速运动就容易 ID 切换ID Switch。DeepSORT 引入了深度学习提取的外观特征Appearance Feature为每个检测框生成一个特征向量。在数据关联时同时考虑马氏距离运动一致性和余弦距离外观相似性大大提升了在复杂场景下的跟踪稳定性。匹配级联Matching Cascade优先匹配最近出现的目标这符合常理一个目标刚刚丢失又出现的概率比丢失很久的目标再次出现的概率要高。这进一步减少了长时间遮挡后的误匹配。工程实现成熟有大量开源、易于集成的 DeepSORT 实现如nwojke/deep_sort及其各种衍生版本能够很好地与 YOLO、OpenCV 等框架配合。为什么不选更先进的跟踪器比如 FairMOT、ByteTrack 等。这些算法在某些 benchmark 上确实表现更好。但 DeepSORT 的结构相对清晰参数可解释性强在常规场景下表现足够稳健且计算开销相对较小。对于车流人流统计这个具体任务经过适当调参的 DeepSORT 完全能够满足需求。先用一个成熟稳定的方案把流程跑通再考虑优化是更务实的工程思路。3. 项目环境搭建与核心依赖详解拿到源码包后第一步就是配环境。很多朋友卡在这一步其实只要理清依赖关系一步步来就很顺利。我的环境是基于 Python 3.8 PyTorch 1.12 CUDA 11.3但版本有一定灵活性。3.1 基础深度学习环境配置核心是 PyTorch 和 OpenCV。建议使用 conda 或 venv 创建独立的虚拟环境。# 1. 创建并激活虚拟环境 (以 conda 为例) conda create -n traffic_count python3.8 conda activate traffic_count # 2. 安装 PyTorch (请根据你的 CUDA 版本去官网选择对应命令) # 例如CUDA 11.3 pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pytorch.org/whl/cu113 # 3. 安装 OpenCV 和其他基础包 pip install opencv-python4.8.1.78 opencv-contrib-python4.8.1.78 pip install numpy pandas matplotlib tqdm3.2 YOLOv5 与 DeepSORT 集成安装源码包通常会包含一个requirements.txt文件。但我们需要理解每个包的作用# 进入项目根目录 cd your_project_path # 安装 YOLOv5 所需依赖 (通常来自其自带的 requirements.txt) pip install -r yolov5/requirements.txt # 这个文件会安装 pycocotools, seaborn, scipy, thop, tensorboard 等 # 安装 DeepSORT 特定依赖 # DeepSORT 通常需要 scikit-learn, scipy, numba, cython_bbox pip install scikit-learn numba # cython_bbox 有时需要编译安装如果 pip 安装失败可以尝试 # pip install cython # pip install -e githttps://github.com/samson-wang/cython_bbox.git#eggcython-bbox关键依赖说明cython_bbox用于加速边界框的 IoU 计算对提升跟踪匹配速度至关重要。如果安装失败可以暂时注释掉相关代码用纯 Python 的 IoU 计算替代速度会慢。numba用于加速一些数值计算特别是 DeepSORT 中的部分计算。torchreid如果 DeepSORT 实现使用了独立的 ReID重识别模型来提取外观特征可能需要安装这个库。但很多开源实现会直接使用一个简单的 CNN如torchvision.models.resnet18来提取特征。3.3 模型权重文件准备项目跑起来需要两个关键的权重文件YOLOv5 检测权重你可以使用官方预训练的yolov5s.pt包含 80 类 COCO 类别其中有 car 和 person。如果针对特定场景如只统计小轿车和公交车最好自己标注数据微调。# 在 yolov5 目录下 cd yolov5 wget https://github.com/ultralytics/yolov5/releases/download/v6.0/yolov5s.ptDeepSORT 特征提取器权重通常是用于行人重识别的模型权重如ckpt.t7或mars-small128.pb。这个文件负责将检测到的目标行人、车辆映射到一个特征向量。源码一般会提供下载链接或将其包含在包内。常见坑点确保权重文件放在代码指定的路径下。有时 DeepSORT 的权重需要特定的预处理或转换格式如从.pth转到.onnx或.t7务必仔细阅读源码中的README或相关配置脚本。4. 核心代码结构与工作流程拆解一个典型的yolov5deepsort统计项目其代码结构和工作流是这样的项目根目录/ ├── yolov5/ # YOLOv5 检测模块 (通常是克隆或子模块) │ ├── models/ # 模型定义 │ ├── utils/ # 工具函数 (如 NMS, 画图) │ └── detect.py # 检测脚本 ├── deep_sort/ # DeepSORT 跟踪模块 │ ├── deep_sort/ # 核心算法实现 │ │ ├── sort/ # SORT 基础算法 │ │ ├── deep_sort.py # DeepSORT 主类 │ │ └── .../ │ └── deep_sort.egg-info/ ├── main.py # 主程序入口 ├── count_logic.py # 流量统计逻辑实现 ├── utils/ # 项目通用工具 │ ├── visualization.py # 可视化绘制 │ └── video_loader.py # 视频流处理 ├── weights/ # 存放模型权重 │ ├── yolov5s.pt │ └── mars-small128.pb └── configs/ # 配置文件 └── track_config.yaml4.1 主循环工作流程 (main.py)main.py是整个系统的中枢它像一条流水线串联起各个模块# 伪代码逻辑展示核心流程 def main(): # 1. 初始化 detector YOLOv5Detector(weightsweights/yolov5s.pt) # 加载YOLO检测器 tracker DeepSORTTracker(extractor_weightsweights/mars-small128.pb) # 加载DeepSORT跟踪器 counter LineCrossingCounter(line_start(100, 720), line_end(1820, 720)) # 初始化计数线 # 2. 打开视频源 cap cv2.VideoCapture(input_video.mp4) while cap.isOpened(): # 3. 读取一帧 ret, frame cap.read() if not ret: break # 4. YOLOv5 检测 detections detector.detect(frame) # 返回: [x1, y1, x2, y2, conf, cls] # 5. DeepSORT 跟踪更新 # 将检测框和帧输入跟踪器获得带有ID的跟踪目标列表 tracked_objects tracker.update(detections, frame) # 6. 流量统计逻辑 for obj in tracked_objects: # obj 包含: track_id, bbox, class_name, track_history(轨迹点列表) counter.update(obj.track_history[-2:]) # 用最近两个轨迹点判断是否穿过计数线 # 7. 可视化 annotated_frame visualize(frame, tracked_objects, counter.get_counts(), counter.line) # 8. 显示或保存结果 cv2.imshow(Traffic Count, annotated_frame) if cv2.waitKey(1) 0xFF ord(q): break # 9. 释放资源 cap.release() cv2.destroyAllWindows() print(f最终统计结果: {counter.get_counts()})4.2 检测结果到跟踪器的数据桥接这是第一个关键接口。YOLOv5 的detect.py或我们封装的检测器输出的通常是(N, 6)的 Tensor 或数组每一行是[x1, y1, x2, y2, confidence, class]。而 DeepSORT 的update方法期望的输入格式通常是(N, 5)即[x1, y1, x2, y2, confidence]并且通常只处理单类别或者需要按类别分别处理。所以我们需要一个转换和过滤步骤def process_detections(yolo_results, target_classes[person, car], conf_threshold0.5): 处理YOLO原始输出过滤出目标类别并转换格式。 deepsort_dets [] for det in yolo_results: # det: [x1, y1, x2, y2, conf, cls] cls_id int(det[5]) cls_name detector.names[cls_id] # 获取类别名 if cls_name in target_classes and det[4] conf_threshold: # 转换为 [x1, y1, x2, y2, conf] 格式 deepsort_dets.append([det[0], det[1], det[2], det[3], det[4]]) return np.array(deepsort_dets) if deepsort_dets else np.empty((0, 5))实操心得conf_threshold置信度阈值的设置非常关键。设高了会漏掉一些模糊目标设低了会引入大量误检增加跟踪器负担并可能导致误计数。通常对于车流可以设得高一些如0.6因为车辆目标通常比较清晰。对于人流在拥挤场景下可能需要适当降低如0.4并通过跟踪器的其他机制如最小检测尺寸、轨迹长度过滤来剔除噪声。4.3 DeepSORT 跟踪器内部机制与参数调优初始化 DeepSORT 时有一系列参数直接影响跟踪效果# 一个典型的 DeepSORT 初始化示例 from deep_sort import DeepSort tracker DeepSort( model_pathweights/mars-small128.pb, # 特征提取器模型 max_age30, # 一个跟踪目标最大丢失多少帧后删除 n_init3, # 一个检测需要被关联多少次才初始化为新的跟踪轨迹 nms_max_overlap1.0, # 非极大值抑制的IoU阈值 (通常为1.0因为检测器已做NMS) max_iou_distance0.7, # 马氏距离匹配的最大IoU距离阈值 max_cosine_distance0.2, # 外观特征余弦距离的最大阈值 nn_budget100, # 外观特征库的预算大小控制存储的历史特征数量 use_cudaTrue )关键参数解析与调优经验max_age(最大寿命)这是最重要的参数之一。它决定了一个跟踪目标在丢失连续多帧未匹配到检测框后还能在内存中保留多少帧。如果场景中遮挡频繁如行人被柱子挡住这个值要设大一些如 30-50给目标重新出现的机会。如果场景简单目标连续出现可以设小如 15-20以节省内存和减少 ID 切换的潜在风险。n_init(初始化次数)一个检测框需要被成功关联多少次才会被确认为一个有效的、新的跟踪轨迹。设为 3 意味着需要连续 3 帧都检测到并关联成功这能有效过滤掉闪烁的误检如树叶晃动。但设得太高如 5会导致新目标出现时跟踪延迟。max_cosine_distance(最大余弦距离)控制外观特征的匹配严格度。值越小要求外观越相似才匹配。对于车辆跟踪由于同一辆车的外观变化相对较小除非有大的视角变化可以设得小一些如 0.1-0.15。对于行人由于姿态、遮挡变化大可以适当放宽如 0.2-0.3。nn_budget(特征预算)存储每个跟踪目标历史外观特征的数量。用于计算当前检测与历史轨迹的外观相似性时是和一个特征集合比较。预算越大对目标外观变化的适应性越强但计算量也越大。通常 50-100 是一个平衡点。调试技巧在调试阶段可以将跟踪器的中间状态如轨迹 ID、匹配状态、丢失帧数可视化在帧上。这能帮你直观地理解参数变化如何影响跟踪行为例如为什么某个目标 ID 切换了为什么一个新目标没有被创建。5. 流量统计逻辑的设计与实现陷阱检测和跟踪给了我们带 ID 的轨迹统计逻辑就是基于这些轨迹做判断。最常用的方法是虚拟线统计法。5.1 虚拟线统计法的原理与实现我们在画面中定义一条线段计数线。当一个目标的运动轨迹由连续帧的中心点或底部中心点构成从线的一侧穿越到另一侧时就计数一次。class LineCrossingCounter: def __init__(self, line_start, line_end, directionleft_to_right): :param line_start: (x1, y1) 计数线起点 :param line_end: (x2, y2) 计数线终点 :param direction: 计数方向用于区分双向流量如 left_to_right, bottom_to_top self.line (line_start, line_end) self.direction direction self.count 0 self.tracked_history {} # 记录每个 track_id 最近一次在线的哪一侧 def _is_crossing_line(self, point1, point2): 判断从 point1 到 point2 的线段是否与计数线相交并确定方向。 # 使用向量叉乘方法判断线段相交 # 这里简化实现实际需考虑方向判断 line_p1, line_p2 self.line # 计算四次叉乘 def cross(p1, p2, p3): return (p2[0]-p1[0])*(p3[1]-p1[1]) - (p2[1]-p1[1])*(p3[0]-p1[0]) d1 cross(line_p1, line_p2, point1) d2 cross(line_p1, line_p2, point2) d3 cross(point1, point2, line_p1) d4 cross(point1, point2, line_p2) # 标准线段相交判断 if ((d1 0 and d2 0) or (d1 0 and d2 0)) and \ ((d3 0 and d4 0) or (d3 0 and d4 0)): # 进一步判断方向 (例如根据 point1 和 point2 的 y 坐标关系判断上下) if self.direction bottom_to_top: return point1[1] line_p1[1] and point2[1] line_p1[1] elif self.direction left_to_right: return point1[0] line_p1[0] and point2[0] line_p1[0] # ... 其他方向判断 return False def update(self, track_id, current_center): 更新目标位置并检查是否穿越计数线。 :param track_id: 目标ID :param current_center: 当前帧目标中心点 (x, y) if track_id not in self.tracked_history: self.tracked_history[track_id] [current_center] return prev_center self.tracked_history[track_id][-1] self.tracked_history[track_id].append(current_center) # 只保留最近2个点用于判断防止轨迹过长 if len(self.tracked_history[track_id]) 2: self.tracked_history[track_id].pop(0) # 判断是否穿越 if self._is_crossing_line(prev_center, current_center): self.count 1 print(fID {track_id} 穿过计数线当前总数: {self.count}) # 可选穿越后删除该ID的历史防止重复计数 # del self.tracked_history[track_id] def get_count(self): return self.count5.2 统计逻辑中的常见陷阱与解决方案重复计数这是最常见的问题。目标在计数线附近来回晃动或者跟踪 ID 发生切换ID Switch都可能导致对同一个物理目标多次计数。解决方案设置“缓冲区”在计数线两侧设置一个“忽略区域”。只有目标从较远的一侧进入该区域并穿越到另一侧才计数。在区域内的小幅移动忽略。轨迹长度过滤只对轨迹长度超过一定阈值如连续跟踪 10 帧以上的目标进行计数判断。这可以过滤掉一些刚出现就消失的误检或短暂跟踪的目标。穿越后标记如上例代码注释目标穿越后立即从tracked_history中删除其记录确保只计一次。但这需要谨慎因为删除后如果目标又退回就无法再计数了。更稳健的做法是记录每个 ID 的“已计数”状态。漏计数目标移动太快相邻两帧的中心点“跳过”了计数线导致没有相交判断。解决方案使用轨迹插值如果帧率较低或目标速度极快可以存储更长的轨迹历史如最近 5 个点并检查轨迹线段由连续点构成是否与计数线相交而不是只检查相邻两帧的点。降低计数线“高度”对于水平线确保它穿过目标可能经过的区域。有时需要画多条线或一个区域进行判断。双向统计需要区分两个方向的流量。解决方案在_is_crossing_line函数中精确判断穿越方向。例如对于水平线比较穿越前后点的x坐标对于垂直线比较y坐标。维护两个计数器即可。计数线位置选择避免透视畸变严重区域在摄像头视角下远处的物体移动慢近处的物体移动快且大。计数线最好画在场景中地面平坦、目标大小相对均匀的区域。避开遮挡物确保计数线所在区域不会被静止物体如树木、标志牌频繁遮挡目标。6. 模型训练与优化让检测更准直接使用 COCO 预训练的yolov5s.pt可以工作但在特定场景下如只关心轿车、公交车、行人或者场景光线特殊、视角特殊微调模型能大幅提升检测精度从而提升整体统计准确率。6.1 数据准备与标注收集数据从你的目标场景高速公路、十字路口、商场入口录制视频并截取关键帧作为图像数据集。数据要尽可能覆盖不同的天气、光照、时间段和流量密度。标注数据使用标注工具如LabelImg、CVAT或Roboflow。只标注你关心的类别如car,bus,person。标注框要紧贴目标。组织数据格式YOLOv5 使用特定的.txt标注格式。每个图像对应一个.txt文件每行表示一个对象class_id x_center y_center width height坐标是归一化后的0-1。# 例如 image001.txt 0 0.5 0.5 0.2 0.3 # 类别0 (car) 在图片中心宽高占图片的20%和30% 2 0.7 0.3 0.1 0.15 # 类别2 (person)将数据集按比例如 8:1:1划分为train、val、test文件夹每个文件夹内包含images和labels子文件夹。6.2 配置与训练创建数据配置文件在yolov5/data/目录下创建my_traffic.yaml。# my_traffic.yaml path: ../datasets/traffic # 数据集根目录 train: images/train # 训练集路径 (相对于 path) val: images/val # 验证集路径 test: images/test # 测试集路径 (可选) # 类别列表 names: 0: car 1: bus 2: person修改模型配置文件可选如果你需要调整网络结构如修改类别数可以复制yolov5/models/yolov5s.yaml并修改nc类别数。但通常微调不需要改结构。开始训练cd yolov5 python train.py --img 640 --batch 16 --epochs 100 --data ../data/my_traffic.yaml --weights yolov5s.pt --cache--img 640: 输入图像尺寸。更大的尺寸如 1280可能提升精度但增加计算量。--batch 16: 批次大小根据 GPU 内存调整。--epochs 100: 训练轮数。通常 50-200 轮足够微调。--weights yolov5s.pt: 从预训练权重开始这是迁移学习的关键。--cache: 将图像缓存到内存或磁盘加速训练。训练经验监控训练过程使用tensorboard --logdir runs/train查看损失曲线、mAP 等指标。确保训练损失和验证损失都在下降且没有严重过拟合训练损失持续下降而验证损失上升。数据增强YOLOv5 默认开启了强大的数据增强Mosaic, MixUp 等。对于交通场景这些增强非常有益。如果数据集很小可以保持或增强如果数据集很大且场景固定可以适当减少增强强度。超参数调优hyp.scratch-low.yaml或hyp.finetune.yaml提供了超参数配置。微调时学习率lr0可以设得比从头训练小一个数量级如 0.01 - 0.001。6.3 模型导出与优化训练完成后得到的最佳模型是runs/train/exp/weights/best.pt。为了部署可能需要转换格式。导出为 ONNXONNX 格式通用性强便于后续在多种推理引擎上使用。python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640 --dynamic针对嵌入式设备优化如果你需要在 RK3568、RV1106 等边缘设备上部署需要进一步操作模型量化将 FP32 模型转换为 INT8 模型大幅减少模型体积和提升推理速度。可以使用 PyTorch 的量化工具或 RKNN-Toolkit、TensorRT 等厂商工具链。模型剪枝移除网络中不重要的连接或通道进一步压缩模型。使用更小模型直接训练yolov5n.pt纳米模型可能更适合资源受限的设备。踩坑提醒在嵌入式设备上部署时确保训练和推理时的图像预处理归一化、通道顺序等完全一致。一个像素值范围的差异都可能导致结果天差地别。7. 性能优化与工程化思考当整个流程跑通后我们需要考虑如何让它更快、更稳、更易于集成。7.1 多线程/异步处理视频处理是计算密集型和 I/O 密集型任务。一个简单的优化是将视频读取、目标检测、跟踪/计数、可视化/写入等步骤放到不同的线程或进程里形成流水线。# 简化版多线程流水线思路 import threading import queue class VideoReaderThread(threading.Thread): def run(self): while True: frame cap.read() if frame is None: break detection_queue.put(frame) class DetectionThread(threading.Thread): def run(self): while True: frame detection_queue.get() dets detector.detect(frame) tracking_queue.put((frame, dets)) class TrackingCountingThread(threading.Thread): def run(self): while True: frame, dets tracking_queue.get() tracks tracker.update(dets, frame) for track in tracks: counter.update(track) display_queue.put((frame, tracks, counter.count))使用queue.Queue进行线程间通信。注意处理好线程退出和资源释放。7.2 模型推理加速TensorRT 部署对于 NVIDIA GPU将 PyTorch 或 ONNX 模型转换为 TensorRT 引擎可以获得数倍的推理速度提升。YOLOv5 官方提供了export.py支持导出为 TensorRT。OpenVINO 部署对于 Intel CPU 或集成显卡使用 OpenVINO 工具包可以优化推理性能。批处理Batch Inference如果处理多个视频流或者可以累积几帧一起处理使用批处理能更充分地利用 GPU 算力。修改检测器使其支持一次处理多帧。7.3 系统集成与输出一个完整的系统不仅输出视频还需要将统计结果持久化。结果输出除了在视频上实时显示计数还应将结果时间戳、方向、数量写入文件CSV、JSON或数据库如 SQLite、MySQL。import csv import time with open(count_results.csv, a, newline) as f: writer csv.writer(f) writer.writerow([time.strftime(%Y-%m-%d %H:%M:%S), inbound, car_count, person_count])API 服务可以将核心逻辑封装成一个 REST API 或 gRPC 服务接收视频流或图片返回统计结果方便与其他系统如交通管理平台集成。配置文件将所有可调参数模型路径、置信度阈值、跟踪参数、计数线坐标放到一个配置文件如config.yaml中避免硬编码便于不同场景的切换。8. 实测中的挑战与应对策略在实际部署中你一定会遇到各种预料之外的情况。分享几个我踩过的坑和解决办法。挑战一夜间或低光照条件现象检测器漏检严重跟踪不稳定。对策数据层面在训练数据中必须包含足够多的夜间、低光照、逆光样本。可以专门收集夜间数据或者使用数据增强模拟低光照如随机调整亮度、对比度、添加噪声。预处理在输入检测器前对图像进行直方图均衡化CLAHE或使用低光照图像增强算法如 Zero-DCE进行预处理。模型层面考虑使用专门在低光照数据集上训练过的模型或者尝试对输入图像进行融合如红外图像与可见光图像融合。挑战二严重遮挡如拥堵时车辆重叠现象检测框不完整或合并跟踪 ID 容易丢失或切换。对策检测器优化使用更先进的检测头如 YOLOv5 的Focus结构本身对遮挡有一定鲁棒性也可尝试引入注意力机制。适当降低 NMS 的 IoU 阈值让被部分遮挡的目标也能被检测出来。跟踪器调参增大max_age给被短暂遮挡的目标更长的“存活”时间。适当放宽max_cosine_distance因为遮挡前后目标外观可能变化。后处理结合目标的大小、运动速度等先验知识进行过滤。例如一辆车不可能在 0.1 秒内移动 50 个像素。挑战三相机抖动现象整个画面晃动导致检测框位置抖动轨迹不平滑容易误触发计数。对策视频稳像在预处理阶段加入视频稳像算法如基于特征点匹配的稳像或使用卡尔曼滤波对帧间运动进行估计和补偿。全局运动补偿在跟踪阶段估计相邻帧之间的仿射变换矩阵并将所有检测框的位置进行反向补偿抵消相机运动的影响。轨迹平滑对跟踪得到的轨迹点序列使用滤波器如卡尔曼滤波、移动平均进行平滑处理再用于计数判断。挑战四多类别统计与分类错误现象需要分别统计小汽车、卡车、行人但检测器可能将卡车误检为汽车。对策精细化分类在标注和训练时定义更细粒度的类别如car,truck,van,bus。确保训练数据中各类别样本均衡。后处理规则根据检测框的宽高比Aspect Ratio等简单特征进行二次判断。例如卡车的宽高比通常比小汽车小更“方”。这个基于 YOLOv5 和 DeepSORT 的车流人流量统计项目从技术原理到工程实现涉及了计算机视觉流水线的多个关键环节。它不是一个简单的脚本拼接而是一个需要根据实际场景反复调试、优化的系统工程。源码提供了一个坚实的起点但真正的挑战和乐趣在于如何让这套系统在你的具体场景下稳定、准确地工作起来。记住没有一劳永逸的参数最好的模型和参数永远是下一个——基于你对数据和场景的深入理解而调整出来的那一个。本文还有配套的精品资源点击获取