人流统计是计算机视觉的经典应用场景,核心是精准检测画面中的行人 + 稳定跟踪每个行人的轨迹 + 根据预设规则(如跨线)统计数量。YOLOv8+DeepSORT 是当前工业界主流的轻量化、高精度方案,下面从核心原理、实现流程、代码落地等维度全面解析。

一、核心组件原理
1. YOLOv8:行人检测基础
YOLOv8 是 2023 年发布的单阶段目标检测模型,相比前代(YOLOv5/7)在速度和精度上均有提升,是人流统计的检测端核心。
核心优势:
轻量化:支持 n/s/m/l/x 不同规模模型,n/s 版本可在普通 GPU/边缘设备实时运行;
精度高:对小目标(如远处行人)、遮挡行人的检测效果更优;
部署友好:支持 ONNX/TensorRT 等格式导出,适配端侧/云端部署。
在人流统计中的作用:
对视频每一帧图像进行推理,输出画面中所有行人的边界框(bbox)、置信度、类别(person),为后续跟踪提供基础检测结果。
2. DeepSORT:行人跟踪核心
SORT(Simple Online and Realtime Tracking)是基础跟踪算法,DeepSORT 是其升级版,核心改进是引入深度特征匹配,解决了 SORT 中目标遮挡、短时消失后重新识别的问题。
核心原理:
1. 运动匹配:用卡尔曼滤波预测目标下一帧的位置,计算预测框与 YOLOv8 检测框的 IOU(交并比),初步匹配;
2. 外观匹配:对检测到的行人提取深度特征(常用 ReID 模型),计算特征余弦相似度,解决遮挡/快速移动导致的 IOU 匹配失效问题;
3. 级联匹配:优先匹配长期未匹配的目标,减少 ID 切换(行人被误识别为新目标);
4. 轨迹管理:为每个行人分配唯一 ID,维护轨迹的生命周期(新增/更新/删除)。
在人流统计中的作用:
为每个行人分配唯一且稳定的 ID,跟踪其运动轨迹,避免重复统计(如同一行人在画面中移动被多次计数)。
二、人流统计完整流程
整体流程可分为 5 个核心步骤,逻辑如下:

步骤 1:视频帧输入
支持本地视频文件(mp4/avi)、摄像头实时流、网络视频流(RTSP/HTTP),通过 OpenCV 逐帧读取。
步骤 2:YOLOv8 行人检测
加载预训练的 YOLOv8 模型(推荐 yolov8s.pt,平衡速度和精度);
对单帧图像执行检测,过滤掉非“person”类别、置信度低于阈值(如 0.5)的检测框;
输出格式:[x1, y1, x2, y2, conf, class](左上角/右下角坐标、置信度、类别)。
步骤 3:检测结果预处理
非极大值抑制(NMS):去除重复的行人检测框;
尺寸归一化:将检测框坐标转换为 DeepSORT 要求的格式;
特征提取:对每个行人检测框裁剪图像,输入 ReID 模型提取外观特征(DeepSORT 核心)。
步骤 4:DeepSORT 轨迹跟踪
初始化:为第一帧检测到的每个行人创建新轨迹,分配唯一 ID;
帧间匹配:
1. 卡尔曼滤波预测当前帧每个轨迹的位置;
2. 计算预测框与检测框的 IOU,筛选候选匹配;
3. 计算候选匹配的外观特征相似度,完成精准匹配;
轨迹更新:匹配成功的轨迹更新位置和特征;未匹配的轨迹标记为“丢失”,超过阈值则删除;未匹配的检测框创建新轨迹。
步骤 5:跨线/区域统计(核心规则)
人流统计的核心是定义有效计数规则,主流有 2 种:
规则 1:跨线计数(最常用)
预设 1 条或多条计数线(如画面中一条水平线/垂直线);
跟踪行人轨迹的中心点,判断是否跨越计数线,且满足方向要求(如从左到右、从下到上);
去重:同一 ID 仅在首次跨线时计数,避免重复统计。
规则 2:区域计数
预设一个感兴趣区域(ROI,如画面中的入口/出口区域);
统计当前时刻区域内的行人数量(实时人数),或统计进入/离开区域的总人数。
三、完整代码实现
前置环境准备
# 安装核心依赖
pip install ultralytics opencv-python numpy torch deep-sort-realtime
完整可运行代码
import cv2
import numpy as np
from ultralytics import YOLO
from deep_sort_realtime.deepsort_tracker import DeepSort
# ====================== 配置参数 ======================
VIDEO_PATH = "test_video.mp4" # 视频路径,替换为0可调用摄像头
COUNT_LINE = [(100, 300), (600, 300)] # 计数线:(x1,y1)到(x2,y2)
CONF_THRESH = 0.5 # 检测置信度阈值
IOU_THRESH = 0.3 # NMS阈值
COUNT_DIRECTION = "up" # 计数方向:up/down/left/right
# ====================== 初始化组件 ======================
# 1. 加载YOLOv8行人检测模型
model = YOLO("yolov8s.pt")
# 2. 初始化DeepSORT跟踪器
tracker = DeepSort(
max_age=30, # 轨迹最大丢失帧数(避免短时遮挡导致ID丢失)
n_init=3, # 新轨迹需要连续匹配的帧数
nn_budget=100, # 特征库大小
override_track_class=None
)
# 3. 视频读取
cap = cv2.VideoCapture(VIDEO_PATH)
if not cap.isOpened():
raise ValueError("视频文件/摄像头无法打开")
# 计数变量
total_count = 0
counted_ids = set() # 已计数的ID,避免重复
# ====================== 辅助函数:判断是否跨线 ======================
def is_cross_line(center_prev, center_curr, line, direction):
"""
判断目标中心点是否跨线
:param center_prev: 上一帧中心点 (x,y)
:param center_curr: 当前帧中心点 (x,y)
:param line: 计数线 [(x1,y1), (x2,y2)]
:param direction: 计数方向 up/down/left/right
:return: 是否跨线
"""
# 提取线的坐标
x1, y1 = line[0]
x2, y2 = line[1]
# 简化:以水平线为例(y固定),判断y方向跨越
if abs(y1 y2) < 10: # 水平线
if direction == "up" and center_prev[1] > y1 and center_curr[1] < y1:
return True
elif direction == "down" and center_prev[1] < y1 and center_curr[1] > y1:
return True
# 垂直线(x固定)
elif abs(x1 x2) < 10:
if direction == "left" and center_prev[0] > x1 and center_curr[0] < x1:
return True
elif direction == "right" and center_prev[0] < x1 and center_curr[0] > x1:
return True
return False
# ====================== 主循环 ======================
# 存储上一帧的轨迹中心点
prev_centers = {}
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 1. YOLOv8检测行人
results = model(frame, conf=CONF_THRESH, iou=IOU_THRESH, classes=[0]) # classes=[0]只检测person
detections = []
# 解析检测结果
for r in results:
boxes = r.boxes
for box in boxes:
# 提取检测框坐标(xyxy)
x1, y1, x2, y2 = map(int, box.xyxy[0])
# 提取置信度
conf = float(box.conf[0])
# 格式:(x1, y1, x2, y2, conf),符合DeepSORT输入要求
detections.append(([x1, y1, x2 x1, y2 y1], conf, "person")) # DeepSORT需要(w,h),所以转换为x1,y1,w,h
# 2. DeepSORT跟踪
tracks = tracker.update_tracks(detections, frame=frame)
# 3. 遍历轨迹,更新中心点并判断跨线
curr_centers = {}
for track in tracks:
if not track.is_confirmed():
continue
# 获取轨迹ID和边界框
track_id = track.track_id
ltrb = track.to_ltrb() # (left, top, right, bottom)
x1, y1, x2, y2 = map(int, ltrb)
# 计算中心点
center_x = (x1 + x2) // 2
center_y = (y1 + y2) // 2
curr_centers[track_id] = (center_x, center_y)
# 绘制检测框、ID、中心点
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, f"ID: {track_id}", (x1, y1 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.circle(frame, (center_x, center_y), 5, (255, 0, 0), -1)
# 4. 判断是否跨线计数
if track_id in prev_centers:
if is_cross_line(prev_centers[track_id], curr_centers[track_id], COUNT_LINE, COUNT_DIRECTION):
if track_id not in counted_ids:
total_count += 1
counted_ids.add(track_id)
# 更新上一帧中心点
prev_centers = curr_centers.copy()
# 绘制计数线和统计结果
cv2.line(frame, COUNT_LINE[0], COUNT_LINE[1], (0, 0, 255), 3)
cv2.putText(frame, f"Total Count: {total_count}", (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 3)
# 显示画面
cv2.imshow("YOLOv8+DeepSORT Pedestrian Counting", frame)
# 按q退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
print(f"最终统计人数:{total_count}")
代码关键部分解释
1. 参数配置:
COUNT_LINE:自定义计数线,可根据视频画面调整坐标;
COUNT_DIRECTION:控制计数方向(如只统计向上跨线的行人);
max_age=30:轨迹最多丢失30帧(约1秒),避免短时遮挡导致ID丢失。
2. 跨线判断函数:
核心是对比目标上一帧和当前帧的中心点,判断是否跨越预设线且符合方向;
支持水平线/垂直线,可扩展为任意斜线(需补充向量计算逻辑)。
3. 去重逻辑:
用counted_ids集合存储已计数的ID,确保同一行人仅被统计一次。
四、优化方向(工业级落地)
1. 模型轻量化:
将YOLOv8s替换为YOLOv8n,或量化为ONNX/TensorRT格式,提升推理速度(边缘设备如Jetson Nano可实时运行);
裁剪视频ROI区域,只检测感兴趣区域的行人,减少计算量。
2. 跟踪稳定性优化:
调整DeepSORT参数(max_age/n_init),适配不同场景(如拥挤/稀疏人群);
加入轨迹平滑(如卡尔曼滤波后处理),减少ID抖动。
3. 计数规则增强:
支持双向计数(如进/出人数分别统计);
过滤静态行人(如停留超过5秒的行人不计数)。
4. 异常处理:
加入画面防抖、光照补偿,提升复杂环境下的检测精度;
处理视频流中断、模型推理超时等异常。
五、总结
关键点回顾
1. 核心逻辑:YOLOv8 负责精准检测行人,DeepSORT 负责稳定跟踪行人ID,跨线/区域规则负责统计有效人数,三者缺一不可;
2. 核心优势:YOLOv8+DeepSORT 兼顾速度(实时)和精度(低ID切换率),适配端侧/云端部署;
3. 落地关键:重点优化跨线判断逻辑(避免漏计/重计)、跟踪参数(适配场景)、模型轻量化(满足实时性)。
该方案是当前人流统计的主流选择,可直接落地到商场、车站、景区等场景的智能监控系统中。
需求留言: