
1. 背景与核心概念1.1 为什么需要行人检测与逆行行为识别在智慧交通、公共安防和自动驾驶场景中行人检测一直是计算机视觉领域的基础任务。它的目标很直接在一张图像或一段视频中找到所有行人的位置并用矩形框把它们标出来。这个需求在车路协同、十字路口监控、自动驾驶感知系统中非常常见。而“逆行行为识别”则是在行人检测的基础上对行人的运动方向和场景规则做进一步判断。比如在道路监控画面中我们需要知道某个行人是否在逆行在机场、地铁站、商场入口我们需要识别是否有人员违反规定的通行方向。将两者结合起来就构成了一个完整的“人目标感知”链路第一步检测出行人并估计其位置。第二步跟踪行人获取其运动轨迹。第三步结合预设规则判断方向是否逆行。这个链路在工程上可以拆成多个模块每个模块都可以单独优化。本文会围绕这条主线讲清楚概念、技术选型、代码实现、工程落地的路径。1.2 核心术语与概念区分先梳理几个容易混淆的概念。1. 目标检测Object Detection目标检测的任务是找出图像中感兴趣的物体并输出类别和位置框。常见算法有 YOLO 系列、SSD、Faster R-CNN 等。行人检测是目标检测的一个细分方向类别只有 person 一类。2. 目标跟踪Object Tracking目标跟踪负责在视频帧之间保持对同一目标的 ID 关联。检测器只负责“当前帧里有没有人”跟踪器负责“这个人是上一帧的哪个人”。常用方案有 DeepSORT、ByteTrack 等。3. 行为识别Action Recognition行为识别关注的是人的动作或行为。逆行识别可以看作一个“面向规则的轨迹行为判断”核心依据是运动方向与参考方向的夹角。4. 逆行Retrograde Movement / Wrong-way Movement在交通规则中行人和车辆都必须按照规定的方向通行。逆行识别就是基于运动轨迹判断目标是否沿道路指定方向的相反方向移动。1.3 应用场景城市十字路口的行人违规监测。高速公路、隧道、匝道的车辆与行人逆行预警。地铁站、车站出入口的人流方向管控。园区、厂区内部道路的巡检系统。自动驾驶中的行人意图预测。2. 技术方案与技术选型2.1 整体思路实现道路场景行人检测与逆行行为识别常见方案有传统图像处理和深度学习两种。传统方法如帧差法、背景建模、方向梯度直方图HOG结合支持向量机SVM在简单场景下可用但复杂场景下鲁棒性较差。工程上更推荐深度学习方案。一个完整的系统建议按下面的模块划分视频流输入 ↓ 行人检测YOLO 等 ↓ 行人跟踪ByteTrack / DeepSORT ↓ 轨迹方向计算 ↓ 规则判定是否逆行 ↓ 报警输出 / 结果可视化2.2 检测模型选择在道路场景中检测模型需要满足以下条件小目标检测能力行人经常出现在远处像素较小需要模型对小目标相对友好。实时性要求摄像头数量多推理速度必须跟上。恶劣环境鲁棒性夜间、逆光、遮挡、雨天等场景需要模型有较好表现。常用候选模型对比模型优点缺点适合场景YOLOv5速度快、生态成熟、部署方便小目标有提升空间但整体稳定通用实时监控YOLOv8新版结构、精度更高、接口友好模型文件和依赖较重中大型项目RT-DETR精度高、无需 NMS推理资源要求高对精度要求很高时考虑Faster R-CNN精度高、经典实时性差、部署成本高离线分析、学术实验从工程落地角度看YOLOv5 和 YOLOv8 是当前最稳妥的选择。如果你需要快速上线优先考虑 YOLOv5如果是新项目且没有历史包袱YOLOv8 更合适。2.3 跟踪方案选择检测只能给出当前帧的行人位置逆行的判断需要跨帧信息所以必须引入跟踪模块。DeepSORT经典方案依赖外观特征重识别适合遮挡较多的场景但对检测器质量要求高。ByteTrack基于检测框的关联思路速度更快对遮挡相对鲁棒实现简单适合道路监控这种高密度人群场景。StrongSORT在 DeepSORT 基础上做了改进精度更高但实现复杂度也更高。如果项目要求精度优先且硬件充足用 StrongSORT 也不错。如果追求效率和部署简单ByteTrack 是更合适的选择。2.4 逆行判定的核心逻辑逆行判定的核心是“运动方向”和“参考方向”的对比。设计要点我们需要获得行人当前的运动方向。我们需要知道当前场景中什么方向是“正向”什么方向是“逆向”。两种方向做夹角计算当夹角超过阈值且持续一段时间后判定为逆行。参考方向怎么定通常有两种方式方式一手动在图像上画一条方向线标注正方向起点和终点。方式二利用车道线检测或光流场估算。实际监控场景中方式一更可控也更实用。项目初期建议采用“手动标注参考线 方向夹角判定”的策略。3. 环境准备与项目结构3.1 环境依赖本文示例代码基于 Python 实现依赖主流的深度学习框架。以 YOLOv8 为例环境清单如下python3.8 或 3.9/3.10 torch2.0.1 torchvision0.15.2 ultralytics8.0.190 opencv-python4.8.0.74 numpy1.24.3版本要根据你的实际环境调整。ultralytics是 YOLOv8 的官方库安装方式很简单pip install ultralytics如果使用 GPU需要提前安装与 CUDA 匹配的 PyTorch。不确定版本时可以参考 PyTorch 官网的安装命令。3.2 项目结构建议将项目按功能模块拆分方便后续维护和扩展pedestrian_wrongway/ ├── configs/ │ ├── detect.yaml │ └── wrongway.yaml ├── models/ │ ├── detector.py │ └── tracker.py ├── core/ │ ├── direction_judge.py │ └── video_processor.py ├── utils/ │ ├── draw.py │ └── logger.py ├── main.py ├── requirements.txt └── README.md每个模块的职责configs/存放模型路径、视频路径、阈值等配置。models/封装检测器和跟踪器。core/核心业务逻辑包括方向判定和视频处理。utils/绘图、日志等辅助函数。main.py主入口。3.3 准备测试视频可以先从本地摄像头或视频文件开始测试。视频可以是道路监控画面的短片。调试期间建议选择画面清晰、行人数量适中的视频这样更容易验证检测和跟踪效果。4. 行人检测模块实现4.1 加载 YOLOv8 模型YOLOv8 的接口非常简洁。首先安装 ultralytics 库然后可以直接加载官方预训练权重。# 文件路径models/detector.py from ultralytics import YOLO class PedestrianDetector: def __init__(self, model_pathyolov8n.pt, conf_thres0.4, devicecpu): self.model YOLO(model_path) self.conf_thres conf_thres self.device device def detect(self, frame): results self.model.predict( frame, confself.conf_thres, deviceself.device, verboseFalse, ) boxes results[0].boxes if boxes is None: return [] # 过滤出 person 类 person_class_id 0 person_boxes [] for box in boxes: cls_id int(box.cls[0]) if cls_id ! person_class_id: continue xyxy box.xyxy[0].cpu().numpy() conf float(box.conf[0]) person_boxes.append({ bbox: xyxy, # [x1, y1, x2, y2] conf: conf, cls_id: cls_id, }) return person_boxes解释几个关键点model_path可以传入自己训练的权重也可以使用官方预训练模型。conf_thres是置信度阈值。道路场景下遮挡多、小目标多阈值设置过低会引入大量误检过高会漏检。person_class_id 0指的是 COCO 数据集中 person 类别的编号。verboseFalse是为了不在终端输出大量预测信息。4.2 检测效果验证单独运行检测器可以用下面的代码把结果画到视频帧上# 文件路径tools/test_detector.py import cv2 from models.detector import PedestrianDetector def test_detector(video_path): detector PedestrianDetector(model_pathyolov8n.pt, devicecpu) cap cv2.VideoCapture(video_path) while cap.isOpened(): ret, frame cap.read() if not ret: break detections detector.detect(frame) for det in detections: x1, y1, x2, y2 det[bbox].astype(int) conf det[conf] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText( frame, fperson {conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, ) cv2.imshow(detect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的作用逐帧读取视频。调用检测器得到行人框。在图像上绘制绿色矩形框和置信度。按q键退出。第一次运行时建议使用 CPU 模式确认模型加载和目标检测没有异常。5. 行人跟踪模块实现5.1 为什么需要跟踪检测器只对单帧图像有效。如果我们要判断“某个行人”是往前走还是往回走就必须知道这个人在连续帧中的位置。跟踪模块会给每个检测目标分配一个唯一的 ID并连续输出它每一帧的坐标。5.2 使用 ByteTrack 实现跟踪ByteTrack 的核心思路是将检测框按照置信度分成“高置信度框”和“低置信度框”优先匹配高置信度框然后用低置信度框补充匹配。这种方式对遮挡场景比较友好。这里展示一个简化版跟踪器封装# 文件路径models/tracker.py from collections import defaultdict import numpy as np class SimpleTrack: def __init__(self, max_lost30): self.tracks {} self.next_id 1 self.max_lost max_lost # 记录每个 ID 的中心点历史 self.history defaultdict(list) def update(self, detections): updated_tracks {} matched_ids set() for det in detections: x1, y1, x2, y2 det[bbox] cx (x1 x2) / 2 cy (y1 y2) / 2 best_id None best_dist 1e10 for track_id, track_info in self.tracks.items(): if track_id in matched_ids: continue last_cx, last_cy track_info[center] dist np.sqrt((cx - last_cx) ** 2 (cy - last_cy) ** 2) if dist best_dist: best_dist dist best_id track_id if best_id is not None and best_dist 100: matched_ids.add(best_id) updated_tracks[best_id] { center: (cx, cy), lost: 0, bbox: (x1, y1, x2, y2), } self.history[best_id].append((cx, cy)) else: new_id self.next_id self.next_id 1 updated_tracks[new_id] { center: (cx, cy), lost: 0, bbox: (x1, y1, x2, y2), } self.history[new_id].append((cx, cy)) # 维护没有匹配上的旧轨迹 for track_id, track_info in self.tracks.items(): if track_id not in matched_ids: track_info[lost] 1 if track_info[lost] self.max_lost: updated_tracks[track_id] track_info self.tracks updated_tracks return self.tracks这个示例是一个最简化的 IoU/距离跟踪器说明核心思路。生产环境建议使用成熟库比如ultralytics自带跟踪能力或安装boxmot等库集成 ByteTrack、DeepSORT。5.3 使用 YOLOv8 自带跟踪如果不想自己写跟踪逻辑YOLOv8 也提供了内置跟踪接口# 文件路径models/tracker.py from ultralytics import YOLO class YOLOTracker: def __init__(self, model_pathyolov8n.pt, conf_thres0.4): self.model YOLO(model_path) self.conf_thres conf_thres def update(self, frame): results self.model.track( frame, confself.conf_thres, persistTrue, trackerbytetrack.yaml, verboseFalse, ) boxes results[0].boxes if boxes is None: return [] track_data [] for box in boxes: cls_id int(box.cls[0]) if cls_id ! 0: continue xyxy box.xyxy[0].cpu().numpy() conf float(box.conf[0]) track_id int(box.id[0]) if box.id is not None else -1 track_data.append({ bbox: xyxy, conf: conf, track_id: track_id, }) return track_data这里的trackerbytetrack.yaml表示使用 ByteTrack 算法。如果没有这个配置文件可以换成botsort.yaml。5.4 可视化跟踪结果跟踪结果需要绘制到帧上并标记 ID这样才能直观验证跟踪效果。# 文件路径utils/draw.py import cv2 def draw_tracks(frame, track_data): for data in track_data: x1, y1, x2, y2 data[bbox].astype(int) track_id data[track_id] conf data[conf] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText( frame, fID:{track_id} {conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2, ) return frame6. 逆行行为识别实现6.1 方向估计逻辑有了稳定的轨迹之后需要计算行人的运动方向。常用的方法是取最近 N 帧的中心点计算出位移向量。假设某一帧的中心点为(cur_x, cur_y)之前的中心点为(pre_x, pre_y)则运动方向向量dx cur_x - pre_x dy cur_y - pre_y方向角度可以用np.arctan2(dy, dx)计算取值范围是[-π, π]。为了防止抖动带来的方向突变建议先平滑轨迹再计算方向。6.2 参考方向定义我们可以在画面上定义一条“正向参考线”例如从 A 点指向 B 点。A: 参考线起点表示正向的起始位置 B: 参考线终点表示正向的结束位置参考方向角度ref_dx B_x - A_x ref_dy B_y - A_y ref_angle arctan2(ref_dy, ref_dx)6.3 逆行判定算法判定逻辑如下计算行人运动方向角度move_angle。计算参考方向角度ref_angle。求两者夹角angle_diff。如果angle_diff大于设定的阈值比如 90 度说明行人行进方向与参考方向相反。连续多帧满足逆行条件才触发报警避免偶发抖动导致误报。6.4 方向判定模块代码# 文件路径core/direction_judge.py import math from collections import deque import numpy as np class DirectionJudge: def __init__( self, ref_start, ref_end, angle_threshold90, frame_count5, history_size20, ): ref_start: 参考线起点 (x, y) ref_end: 参考线终点 (x, y) angle_threshold: 逆行判定夹角阈值 frame_count: 需要连续多少帧满足方向条件 self.ref_start ref_start self.ref_end ref_end self.angle_threshold angle_threshold self.frame_count frame_count self.history_size history_size self.track_history {} self.wrong_count {} self.ref_angle self._calc_angle( ref_end[0] - ref_start[0], ref_end[1] - ref_start[1], ) def _calc_angle(self, dx, dy): return math.degrees(math.atan2(dy, dx)) def _angle_diff(self, angle1, angle2): diff abs(angle1 - angle2) if diff 180: diff 360 - diff return diff def update(self, track_id, center): if track_id not in self.track_history: self.track_history[track_id] deque(maxlenself.history_size) self.wrong_count[track_id] 0 self.track_history[track_id].append(center) if len(self.track_history[track_id]) 3: return False, 0, False recent list(self.track_history[track_id]) pre recent[-3] cur recent[-1] dx cur[0] - pre[0] dy cur[1] - pre[1] move_len math.sqrt(dx * dx dy * dy) if move_len 2: # 距离太短方向不稳定不做判断 return False, 0, False move_angle self._calc_angle(dx, dy) angle_diff self._angle_diff(move_angle, self.ref_angle) is_wrong angle_diff self.angle_threshold if is_wrong: self.wrong_count[track_id] 1 else: self.wrong_count[track_id] 0 triggered self.wrong_count[track_id] self.frame_count return triggered, angle_diff, is_wrong这段代码的核心逻辑每个跟踪 ID 维护一个历史中心点队列。使用最近两帧的中心点计算运动方向。判断轨道方向与参考方向的角度差。连续多帧满足条件后才输出触发信号。6.5 可视化逆行结果当检测到逆行时可以区别颜色绘制比如把正常行人框画成绿色逆行行人框画成红色并在画面中显示文字提示。7. 完整实战系统7.1 主流程代码将检测、跟踪、方向判定串起来的完整流程如下。# 文件路径main.py import cv2 from models.detector import PedestrianDetector from models.tracker import YOLOTracker from core.direction_judge import DirectionJudge from utils.draw import draw_tracks def main(video_path, output_pathNone): detector PedestrianDetector(model_pathyolov8n.pt, devicecpu) tracker YOLOTracker(model_pathyolov8n.pt, devicecpu) # 参考线起点和终点根据实际画面标注 ref_start (0, 500) ref_end (800, 500) judge DirectionJudge(ref_start, ref_end, angle_threshold90, frame_count5) cap cv2.VideoCapture(video_path) fps cap.get(cv2.CAP_PROP_FPS) width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) writer None if output_path: writer cv2.VideoWriter( output_path, cv2.VideoWriter_fourcc(*mp4v), fps, (width, height), ) frame_idx 0 while cap.isOpened(): ret, frame cap.read() if not ret: break track_data tracker.update(frame) for track in track_data: track_id track[track_id] x1, y1, x2, y2 track[bbox].astype(int) center ((x1 x2) / 2, (y1 y2) / 2) triggered, angle_diff, is_wrong judge.update(track_id, center) track[is_wrong] triggered # 绘制检测与跟踪结果 for data in track_data: x1, y1, x2, y2 data[bbox].astype(int) track_id data[track_id] if data.get(is_wrong, False): color (0, 0, 255) label fwrongway ID:{track_id} else: color (0, 255, 0) label fwalk ID:{track_id} cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) cv2.putText( frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, ) # 绘制参考线 cv2.line(frame, ref_start, ref_end, (255, 0, 0), 2) cv2.putText( frame, REF LINE, (ref_start[0], ref_start[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2, ) cv2.putText( frame, fframe: {frame_idx}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2, ) if writer: writer.write(frame) cv2.imshow(wrongway detection, frame) if cv2.waitKey(1) 0xFF ord(q): break frame_idx 1 cap.release() if writer: writer.release() cv2.destroyAllWindows() if __name__ __main__: main( video_pathtest_video.mp4, output_pathoutput_video.mp4, )7.2 参数说明参数说明建议值conf_thres检测置信度阈值0.4 ~ 0.5angle_threshold逆行角度阈值90frame_count连续触发帧数5 ~ 10history_size轨迹历史长度20ref_start参考线起点按画面标注ref_end参考线终点按画面标注7.3 预期运行结果在视频播放窗口中你会看到蓝色参考线表示“正向”方向。绿色框表示正常通行的行人。红色框表示被判定为逆行的行人。左上角显示当前帧编号。如果默认的预训练检测模型效果不好可能是行人姿态、遮挡、光照等因素影响可以先尝试调整置信度阈值再考虑收集数据集做微调。8. 基于自定义数据集微调模型8.1 数据集标注格式如果要提升特定道路场景下的检测效果建议在自采数据上微调 YOLO 模型。YOLO 数据集标注格式如下images/ ├── train/ │ ├── img_001.jpg │ ├── img_002.jpg └── val/ └── img_001.jpg labels/ ├── train/ │ ├── img_001.txt │ ├── img_002.txt └── val/ └── img_001.txt每个 txt 文件中的一行代表一个目标class_id x_center y_center width height其中x_center、y_center、width、height都是相对图像宽高的归一化数值。8.2 数据配置文件# 文件路径configs/dataset.yaml path: ./datasets/road_person train: images/train val: images/val names: 0: person8.3 微调命令yolo detect train \ modelyolov8n.pt \ dataconfigs/dataset.yaml \ epochs50 \ imgsz640 \ batch16 \ device0如果使用 CPU 训练将device0改为devicecpu但训练速度会慢很多。建议先标记少量数据验证流程再逐步扩充数据集。9. 常见问题与排查思路9.1 问题表格问题现象常见原因解决思路检测漏检严重置信度阈值过高、小目标多调低conf_thres适当裁剪图像分块检测误检过多数据集场景差异大、阈值过低提高置信度阈值收集场景数据微调跟踪 ID 频繁切换检测框抖动、遮挡频繁调整跟踪参数使用更鲁棒的跟踪器逆行误判行人站立不动、轨迹抖动增加位移长度过滤提高连续帧数逆行动作无反应参考方向标注错误检查参考线起点终点确认方向定义处理速度慢模型输入尺寸大、硬件算力低降低输入分辨率使用 TensorRT 加速视频卡顿推理未做多线程处理使用生产者-消费者模式并行处理9.2 检测效果差的排查路径如果检测效果不理想按以下顺序排查确认视频帧本身是否清晰。在单帧图像上测试排除跟踪模块影响。调整置信度阈值观察漏检和误检变化。如果阈值调低后仍漏检说明模型泛化能力不足需要微调。检查检测目标是否包含佩戴背包、骑车、蹲姿等特殊形态。9.3 逆行判定不稳定的排查路径打印每个 ID 的运动方向和角度差观察数据波动。检查中心点计算是否准确如果检测框跳动会导致方向突变。增加位移长度过滤例如中心点位移小于 5 个像素时不判断。增加连续帧数比如frame_count8减少瞬时误判。9.4 工程化部署建议在实际项目中建议将检测、跟踪、判定模块拆分为独立服务通过消息队列或 HTTP 接口对接。例如视频流通过 RTSP 接入。检测推理服务部署为 GPU 容器。判定结果写入 Redis 或数据库。告警信息通过 Webhook 推送到业务平台。这样可以避免视频处理任务阻塞主业务流程也方便扩展更多算法模块。10. 最佳实践与工程建议10.1 数据与场景层面道路场景变化很大预先收集目标场景的视频素材非常重要。标注时尽量覆盖不同时间段、不同天气、不同姿态。定期补充难例数据比如遮挡行人、骑行者、夜间行人。10.2 模型层面优先使用轻量模型验证流程比如 YOLOv8n。验证可行后再考虑大模型或 TensorRT 加速。如果同时检测机动车和非机动车注意类别定义的一致性。模型推理输入尺寸不一定越大越好640 是一个常用平衡值需要根据最小目标尺寸调整。10.3 逆行判定层面参考方向要结合实际场景定义不可盲目套用一个角度。单摄像头方案容易受视角影响在复杂路口建议使用多个摄像头联动。不要只在有了轨迹偏差后立刻报警要结合连续帧和最小位移过滤。可以加入“区域限定”逻辑只在指定 ROI 区域内进行逆行判定。10.4 性能优化层面使用批处理方式同时处理多路视频流。检测和跟踪尽量使用同一份检测结果避免重复推理。像素坐标归一化到0~1便于跨分辨率部署。定时清理超时轨道防止内存持续增长。10.5 安全与合规层面涉及公共道路监控时必须遵守当地隐私和数据保护法规。若需要保存视频数据配置访问权限和加密存储。分析结果只用于合规业务不应对个人身份信息进行无限度收集。报警信息需要重要人工复核通道避免自动化系统误报造成负面影响。11. 下一步学习方向读完本文你已经掌握了行人检测、跟踪和逆行行为识别的基本链路。如果你希望继续深入建议按以下路线推进阅读 YOLOv8 官方文档了解模型训练、导出和部署细节。学习 ByteTrack 论文和源码理解跟踪关联的完整机制。研究多目标跟踪评测指标比如 MOTA、IDF1用于评估跟踪质量。练习 TensorRT 模型导出完成推理加速。尝试加入 ReID 特征提高遮挡场景下的 ID 稳定性。扩展行为识别比如奔跑、滞留、越界等场景规则。动手永远比看重要。先准备一段道路视频把检测到逆行判定的流程跑通再逐步加入数据微调和部署优化。如果本文对你有帮助可以收藏备用后续遇到环境配置或参数调优问题再回来看。