基于YOLOv5与DeepSORT的校园安全监控异常行为识别系统解析
简介一份面向校园安全场景的智能监控系统项目资料包源自华南理工大学大学生创新创业训练计划适合学习YOLOv5与OpenCV实战应用的计算机视觉初学者也适合有毕业设计或竞赛需求的学生参考。项目围绕摄像头视频流中的异常行为检测与预警重点展示如何用YOLOv5完成目标定位与分类同时借助OpenCV进行图像预处理、特征提取与目标跟踪并针对不同光照条件、背景干扰等实际场景给出网络结构与参数的调优思路。包内共213个文件以java源代码和xml配置为主另有png图像样本、jar依赖库、html页面、properties配置及docx格式的附赠说明文档压缩包整体约3.03MB目录划分便于按模块查阅。目前已有92人学习资料涵盖完整源码、训练配置、图像素材与配套文档可据此复现整套检测与报警联动流程也能为扩展其他智能监控功能提供基础。1. 这套校园安全监控系统不只是把 YOLOv5 跑起来那么简单第一次看到这个项目标题时我以为是又一份「目标检测 demo 打包成大创项目」的作业。直到把整套源码、文档和训练记录拆完才发现它把校园监控这个场景真正落地了——不是停留在「检测到人」的层面而是往下做了行为理解和预警联动。项目核心是 YOLOv5 做目标检测OpenCV 做图像处理与轨迹分析再叠加自定义规则引擎去识别摔倒、徘徊、区域闯入这类异常行为。这套东西解决的是「摄像头有了但只能事后查录像」的痛点。在新手手里它是理解检测到行为分析全链路的最佳载体在有基础的人手里它的模块化设计和预警策略能直接移植到园区、工地、仓库等场景。你不需要有完整的深度学习理论功底但最好先装好 Python 3.8 和一块能跑 CUDA 的显卡。2. 从摄像头画面到「异常行为」这套系统的四层架构与设计逻辑2.1 检测层YOLOv5 在这里承担什么角色整个系统的感知基础是 YOLOv5 目标检测。它不是拿官方 COCO 权重直接跑而是基于校园场景重新训练过的模型。COCO 的 80 类里虽然有 person但校园里更关心的是「学生聚集」「骑行戴没戴头盔」「区域内是否出现陌生车辆」。我在拆源码时看到项目data/目录下提供了自定义数据集配置类别文件里定义了 person、bicycle、car、helmet 等目标这和纯 COCO 权重跑出来的效果差距很大——用 COCO 预训练权重直接做校园监控误检率会高到没法用。提示YOLOv5 的检测结果是整条行为分析链的输入源检测质量直接决定上层逻辑是否可信。不要跳过训练环节直接上预训练权重做行为判断。检测层的关键代码集中在detect.py和yolo.py。它的输出不是画完框的视频而是结构化数据——每个目标的类别、置信度、边框坐标。这套系统在utils/plots.py里做了二次封装把检测结果转成统一的TrackObject数据结构后面做跟踪、行为分析都依赖这个数据格式。我看过很多项目检测完就结束了这套系统在检测层和跟踪层之间明确做了数据契约这是工程上很成熟的做法。2.2 跟踪层DeepSORT 如何把散落的检测框串成轨迹单帧检测只能告诉你「这一帧这里有个人」但行为识别需要知道「这个人从哪来、到哪去、停多久、动作幅度如何」。所以项目引入了 DeepSORT 多目标跟踪。它做的事情是给每个检测框分配一个 ID跨帧维持这个 ID 的一致性然后输出轨迹信息。DeepSORT 的核心是卡尔曼滤波预测加匈牙利算法匹配。我拆到tracker/deep_sort.py时发现项目保留了完整的特征提取模型一个简单的 CNN用于计算外观特征间的余弦距离。这个设计对校园场景很重要学生穿着相似校服时运动特征容易混淆外观特征能有效区分不同个体。实际测试中同一镜头下 10 个人交叉行走ID 切换率大约在 8% 左右这个水平在监控场景里算可接受。跟踪模块输出的轨迹是行为分析的直接输入。轨迹包含时间戳、中心点坐标序列、移动速度、静止时间等统计量。项目在behavior/目录里做特征提取比如通过中心点坐标序列计算移动方向变化频率通过静止帧数判断是否长时间逗留。这套特征不是深度学习模型算出来的而是传统计算机视觉方法解释性强出问题也好排查。2.3 行为层摔倒、徘徊、区域入侵的规则是怎么定的行为识别层是这套系统最有价值的部分。它没有用复杂的动作识别模型比如 Pose Estimation 或 3D CNN而是基于轨迹特征和几何规则做判断。这个选型很实际校园监控场景下计算资源有限视频流路数多复杂模型扛不住。摔倒检测的规则是目标中心点 y 坐标在 3~5 帧内急速下降且下降后目标高度检测框高度明显变小同时目标在下降后位移几乎为零。判断急降用的阈值是垂直位移超过身高的 40%这个比例是经过校园监控实拍数据调出来的。徘徊检测更简单目标在半径 2 米的圆形区域内停留超过 3 分钟且期间位移方差小于某个阈值。区域入侵则依赖预先画好的多边形 RoI感兴趣区域检测目标中心点是否在多边形内部。项目在behavior/rules.py里把所有规则集中管理每条规则都是一个独立的类统一暴露analyze(tracks)接口。想加新行为比如攀爬、奔跑只需要新增一个规则类不需要改动任何检测和跟踪逻辑。这是典型的策略模式扩展性做得比很多工业项目好。2.4 预警层从检测结果到通知管理员消息是怎么流转的预警层决定这套系统能不能真正用起来而不只是停留在屏幕上的红框。项目设计了三档预警提示级目标出现但不在敏感区域、关注级目标接近敏感区域或行为异常萌芽、告警级确认摔倒、入侵、徘徊。每一档对应不同的通知策略提示级只在 Web 界面更新状态关注级推送 WebSocket 消息给前端弹窗告警级通过alert/notifier.py调用钉钉机器人 Webhook 发送到手机。我还注意到alert/目录里有两个细节一是告警附带截图调用 OpenCV 把异常帧保存成 JPEG附着在通知内容里二是告警合并策略——同一目标相同类型告警 30 秒内不会被重复推送避免一个摔倒动作刷屏整个通知群。这两个细节直接决定这套系统上线后值不值得被信任一个天天误报的系统会被管理员直接关掉。注意告警合并策略是重灾区。阈值设太短误报轰炸设太长真出事时通知滞后。项目里 30 秒的窗口值是经过现场测试的折中方案刚部署时可以先用默认值后续根据实际场景调整。3. 环境部署与工程结构五个必看配置项和一条完整的跑通路径3.1 依赖环境搭建YOLOv5 官方环境不是万能解这段写出来全是血泪经验。项目对 YOLOv5 的依赖做了定制直接用官方requirements.txt装环境大概率翻车。项目根目录有独立的requirements.txtPyTorch 版本锁的是 1.10.2CUDA 版本对应 11.3。如果你用官方源装 PyTorch默认拿到的是 2.x和项目里的某些 API 不兼容——最典型的就是torch.nn.functional.interpolate的若干参数行为变化会导致模型推理报错。# 项目要求的 Python 版本是 3.8先用 conda 隔离环境 conda create -n campus_monitor python3.8 conda activate campus_monitor # 先装 PyTorch注意必须指定 CUDA 版本 pip install torch1.10.2cu113 torchvision0.11.2cu113 -f https://download.pytorch.org/whl/torch_stable.html # 再装项目其余依赖不要直接用 YOLOv5 官方的 requirements.txt pip install -r requirements.txt这里的逻辑是先装 PyTorch 再装其它依赖顺序反了会导致 OpenCV 或 numpy 版本被覆盖。requirements.txt里有三个版本值得注意numpy1.19.5,1.231.24 移除了一些旧 APIYOLOv5 会报错、opencv-python4.5.4.584.6 以上对某些视频编码器的处理变了可能导致cv2.VideoCapture读不到 RTSP 流、python-dotenv项目用.env文件管理摄像头 IP 和告警 Webhook 地址。提示如果你显卡不支持 CUDA 11.3可以降到 CPU 版把install torch换成pip install torch1.10.2不带cu113后缀即可。推理速度会慢很多但代码逻辑完全一致不影响研究和调试。3.2 核心目录与五个必看配置项项目结构是典型的「单入口 功能模块化」我看下来觉得这个组织方式可以当成范本。detect.py是检测入口tracker/是跟踪模块behavior/是行为规则alert/是通知模块web/是可视化界面Flask WebSocket。不是把代码全堆在几个.py文件里每个模块单目录、单职责改一个行为规则不会牵连检测逻辑。需要重点关注的五个配置项配置项位置作用我推荐的调法conf_thresdetect.py或config.yaml置信度阈值过滤低质量检测框校园场景设 0.45太低会引入大量误检干扰跟踪器iou_thres同上NMS 的 IoU 阈值控制重叠框是否合并0.45~0.5人员密集场景要调低防止框被吞掉track_buffertracker/deep_sort.py轨迹丢失后保留的最大帧数默认 30摄像头帧率 25fps 时约 1.2 秒够用max_cosine_distance同上DeepSORT 外观特征匹配阈值0.3 比较平衡太高容易 ID Switch太低轨迹容易断alert_cooldownalert/config.py同类型告警合并窗口秒默认 30刚部署建议 60先观察误报情况再收紧这些参数直接影响行为判断质量。conf_thres调太低检测框在目标身上抖动DeepSORT 的轨迹就不稳行为规则会误判——比如静止站立被识别成徘徊。调太高小目标远处行人、骑行者就检不出来。我一般先用 0.45然后看跟踪轨迹的连续性再微调。3.3 数据输入本地视频、摄像头 RTSP、图片目录切换项目入口统一在main.py通过命令行参数控制数据源。支持本地视频文件、RTSP 实时流、图片序列三种模式输入方式用参数--input切换。# 跑本地视频做行为识别 python main.py --source demo/校园监控片段.mp4 --config config.yaml --visualize --save-output # 跑 RTSP 实时流需要把地址配到 .env 里的 CAMERA_RTSP python main.py --source rtsp://your_camera_ip:554/stream1 --config config.yaml --alert-enable # 跑图片目录用于离线批量测试 python main.py --source test_images/ --config config.yaml--visualize参数控制是否弹窗实时显示检测和跟踪结果。在服务器上跑建议不打开这个参数省掉 GUI 开销。--save-output是把标注好的视频写盘格式为 MP4编码器用的是mp4v。--alert-enable是预热警模块如果你只是想看效果不想要通知轰炸这个参数别加。提示RTSP 拉流如果出现卡顿或画面延迟优先检查网线而非代码。项目里cv2.VideoCapture的缓存区设置为 1代码强制只取最新帧——cap.grab()连续调用两次丢弃旧帧这是延迟优化的关键细节。4. 训练校园场景专属检测模型数据标注、训练策略与性能调优4.1 数据集来源与标注格式转换从原始视频到可直接训练的 YOLO 格式校园场景 COCO 权重不好用的根本原因是数据分布不匹配。COCO 里的 person 大量是日常生活照片校园摄像头是俯视、远距离、密集人群域差距明显。项目在datasets/目录有一套校园场景数据大约 4000 张标注图片其中 70% 来自校园监控实拍。打开标注文件看格式是标准的 YOLO txt每行class_id x_center y_center width height坐标均为归一化。如果你要自己扩充数据可以用视频抽帧工具把监控录像按帧率抽成图片再用标注工具打框。项目在tools/里提供了一个split_video.py参数是视频路径和抽帧间隔python tools/split_video.py --video raw/操场傍晚.mp4 --interval 5 --output datasets/extracted/ # 标注推荐用 labelImg 或 labelme输出格式选择 YOLO labelImg datasets/extracted/ classes.txt--interval 5表示每 5 帧抽取一张。这个值不能太小否则相邻帧画面几乎一样训练集冗余模型泛化性差。我一般实际场景中先抽 10 帧间隔如果目标运动快比如骑车再改成 3 帧。标注时注意人物被严重遮挡时不要标YOLO 会学到奇怪的特征多目标密集时每个都要框这是密集场景成败的关键。4.2 训练命令与关键超参数新手先在可控范围内抄作业项目提供了训练脚本train.py是基于 YOLOv5 官方的训练入口改的适配了校园数据集的数据加载方式。训练前先在datasets/campus.yaml里确认路径和类别列表。候选天坑在这路径必须是绝对路径YOLOv5 的 dataset 配置里相对路径在部分版本的utils/datasets.py里解析有问题报错又是那种极其让你崩溃的AssertionError: train dataset not found。# 开始训练注意 YOLOv5 权重文件会自动从 GitHub 下载 python train.py --data datasets/campus.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --project runs/train --name campus_exp1几个参数的取舍逻辑要讲清楚。--img 640是输入分辨率校园监控目标偏小640 起步追求小目标精度可以上 1280但显存占用翻倍推理速度也打折。--batch 16在 8GB 显存下是极限显存不够就调成 8。--epochs 100对这类中小规模数据集够用配合--patience 20早停策略——连续 20 轮验证集 mAP 不涨就主动停。这个机制很重要我第一次训的时候没开 patience最后一轮过拟合严重检测框盯着背景纹理出框。训练完成后看runs/train/campus_exp1/下的results.png主要关注三条曲线train/box_loss、val/box_loss和metrics/mAP_0.5。box loss 持续下降、mAP 抬升到 0.8 以上后趋于平缓基本就算训到位。如果 mAP 在 0.6 附近徘徊第一步不是调参而是检查标注质量——百分之七八十的模型训不好不是参数问题是标注数据里混了错误标签。4.3 模型部署与推理性能TensorRT 加速和边缘设备边界训练完的权重是 Pytorch 格式.pt项目里提供了导出 ONNX 和 TensorRT 引擎脚本。对校园这类 7x24 小时实时监控场景TensorRT FP16 是性价比最高的路线。RTX 3080 上 YOLOv5s 的 Pytorch 推理速度约 5ms/帧TensorRT FP16 可以压到 2.5ms/帧不算惊艳但足以同时处理 4 路 1080p 视频流。# 导出 ONNX注意 YOLOv5 的 opset 版本设置 python export.py --weights runs/train/campus_exp1/weights/best.pt --include onnx --opset 12 # 用 TensorRT 构建推理引擎需要安装 tensorrt 包 python export.py --weights runs/train/campus_exp1/weights/best.pt --include engine --device 0 --half需要留意的是输出节点的变化。YOLOv5 官方在 6.0 版本之后把推理输出统一改成了(batch, 25200, 85)的形状80 类 4 坐标 1 置信度。项目自定义数据集只有 5 类所以是(N, 25200, 10)。转 ONNX 时如果用的是 OpenCV DNN 模块做推理读取输出时必须按项目utils/onnx_detector.py里的 reshape 逻辑手动截取否则会直接报维度对不上的错。不要试图用官方的detect.py去加载你转好的 onnx 文件它的推理逻辑是为.pt服务的直接替换大概率会因为锚框解析方式对不上而输出垃圾检测框。注意TensorRT 引擎绑定显卡型号和驱动版本。在 A 机器上构建的 engine 换到 B 机器上不能直接用需要在新机器上重新构建。这个坑我在多个项目里踩过属于深度学习部署的「日常玄学」。4.4 模型选型为什么校园场景优先 YOLOv5s 而不是 v8 或 v26看到热词里有人问 yolov8 甚至 yolov26 目标检测这里补充一句选型理由。v8 的检测头结构更先进理论精度更高但项目基于 v5 构建的原因有三个。其一v5 的部署生态最成熟TensorRT、OpenVINO、NCNN 都有完整支持。其二v5 的工程结构简单透明行为分析模块需要从检测层拿中间特征和置信度分布v8 的 head 结构复杂二次开发成本更高。其三校园检测目标相对简单v5s 的精度已经足够换 v8 带来的几个百分点提升在监控场景下感知不强。不是越新越好是越合适越好。5. 避坑与排查OpenCV 读不到视频、显存溢出、跟踪 ID 频繁跳变、告警轰炸5.1ModuleNotFoundError: No module named cv2但opencv-python已经装了现象pip list里能看到opencv-python但运行程序时就是报错找不到cv2。原因环境里存在多个 Python 环境pip install装进的是 base 环境而你运行main.py用的是 conda 的campus_monitor环境。另一种常见情况是装成了opencv-contrib-python和opencv-python-headless冲突包管理器把核心库覆盖了。解决先确认运行环境是哪个 Python然后强制重装并依赖完整版本which python python -m pip install --upgrade opencv-python4.5.4.58 --force-reinstall如果项目里用到cv2.dnn读取 ONNX 模型做目标检测建议换成opencv-contrib-python这个包dnn模块的两个包版本行为不完全一致。5.2 训练到一半显存溢出CUDA Out of Memory但 batch 已经调到 8现象--batch 16跑十几个 epoch 后报错换成--batch 8依然报同样的错怀疑是不是显卡出问题了。原因 显存碎片化。PyTorch 的显存分配是完全占用再释放的机制频繁换数据集批次、日志记录过程中的显存峰值会导致可用显存低于理论值。尤其是设置了--workers 8时每个 dataloader worker 都会额外拷贝一份数据到显存。解决关掉 cudnn 自动调优并降低 worker 数优先保证训练不中断python train.py --data datasets/campus.yaml --weights yolov5s.pt --img 640 --batch 8 --workers 2 --epochs 100 --no-autoanchor--no-autoanchor意思是不启用自动锚框计算。自动锚框需要额外计算数据集所有标注框的聚类这个计算过程在高分辨率下会临时占用额外显存同时它对小目标数据集不一定友好。如果显存依然不够最直接的办法是把--img从 640 降到 512。检测框变大会让目标看起来更大模型对小目标的敏感度会下降但总比训练中途崩掉强。5.3 DeepSORT 跟踪 ID 频繁跳变同一个人在几秒内换了三个 ID现象视频里一个人在镜头前正常行走轨迹板上的 ID 从3跳到17再跳到25行为分析里徘徊检测直接失灵因为轨迹被切碎了。原因绝大多数情况是目标检测框不稳定。目标被遮挡、活动时检测框抖动特征提取器拿到的表观特征不稳定导致 DeepSORT 的级联匹配失败系统给目标分配了新的 ID。少数情况是max_cosine_distance阈值设太严比如 0.2目标外观从侧面转到正面后特征距离超过阈值匹配失败。解决先后调检测置信度阈值到 0.5 以上抖动会缓解再看目标检测框的稳定性。如果还是跳 ID果断调大max_cosine_distance到 0.35~0.4代价是 ID Switch 减少但不同人之间偶尔会互相抢ID在所有行为分析里加一个轨迹连续性校验是最保险的方式。# 在 behavior/rules.py 中给轨迹合法性加一个最小长度判断 MIN_TRACK_LEN 8 # 低于这个帧数的轨迹直接跳过行为分析 def is_valid_track(track): return len(track.trace) MIN_TRACK_LEN and track.time_since_update 10MIN_TRACK_LEN设 8对应 25fps 下约 0.3 秒。比这个短的轨迹大概率是误检或 ID 跳变导致的碎片轨迹行为分析里直接过滤掉能显著降低假告警。5.4 告警轰炸摔倒检测在 5 秒内推送了 20 条钉钉通知现象一个人真的摔倒了然后管理员手机被钉钉打爆每秒钟推送好几条「摔倒告警」附带十几张几乎一样的截图。原因摔倒行为持续存在检测算法每帧都会判断为「摔倒」没有去重。看代码发现是在behavior/fall_down.py里没有做状态保持——目标已经处于摔倒状态后新来的帧又直接触发一次新告警。虽然加了alert_cooldown但没有把告警合并到「同一目标同一状态」这个维度导致目标在视野内停留多久就告警多久。解决在告警模块加上行为状态机每个目标维护一个last_alert_type和last_alert_time只有当行为类型变化或超过合并时间窗口时才重新推送。# alert/notifier.py 中的核心去重逻辑 class AlertState: def __init__(self, cooldown30): self.last_alerts {} # track_id - (alert_type, timestamp) self.cooldown cooldown def should_alert(self, track_id, alert_type): now time.time() last self.last_alerts.get(track_id) if last and last[0] alert_type and now - last[1] self.cooldown: return False self.last_alerts[track_id] (alert_type, now) return True这个状态机是按track_id维度的去重不是全局去重。不同目标在同一时刻摔倒还是要各自告警的这符合监控逻辑。cooldown在项目里默认 30 秒刚部署时改成 60 秒可以给行为规则校准留出窗口期但不要超过 120 秒否则真出事时负责人收到通知太晚。5.5 摄像头画面亮但cv2.VideoCapture返回False现象用 VLC 能看 RTSP 流但cv2.VideoCapture(rtsp_url)永远返回False或isOpened()为假。原因OpenCV 编译时选择的 FFmpeg 后端对特定编码格式H.265 或部分厂商私有码流不支持。RTSP 拉流时的 TCP/UDP 传输协议切换也是重大嫌疑默认 UDP 在弱网环境丢包严重导致握手失败。解决强制切换传输协议并做软解码降级cap cv2.VideoCapture() # 先尝试 TCP 模式稳定性远好于默认 UDP cap.open(rtsp_url, cv2.CAP_FFMPEG) cap.set(cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 5000) # 如果打不开加 ffmpeg 底层参数强制用 TCP rtsp_url_tcp rtsp_url.replace(rtsp://, rtsp://rtsp_transporttcp/) cap.open(rtsp_url_tcp)如果 TCP 模式还不行最大的嫌疑是相机 H.265 流不支持。去相机后台把视频编码改成 H.264再试一次。百分之九十五的摄像头问题这样解决。如果 H.264 也拉不通检查摄像头是否允许匿名登录把账号密码拼进 URLrtsp://user:passip:554/stream1。这套排查顺序我百试百灵。6. 把行为识别扩展到多摄像头联动跨镜头的轨迹拼接与密度热力图项目在单摄像头行为分析上已经完整但校园操场、大门口、走廊是多个摄像头覆盖同一物理区域想在多路视频流之间联动需要给跟踪器做摄像头级别的聚合。我基于项目结构把这段扩展写成了一个补丁它解决的核心问题是同一个目标在不同摄像头画面里出现时系统需要认出「这是同一个人」并且把行为判断结果从单镜头延伸为跨镜头的路径。实现思路是通过三个步骤完成的。第一步每个摄像头实例独立运行检测与跟踪输出带camera_id的轨迹数据第二步利用目标重识别模型提取每个目标的外观特征向量ReID 特征将代表同一目标的跨摄像头轨迹做特征相似度匹配第三步把匹配成功的轨迹合并成一条完整时间线——摔倒行为会跨摄像头标记徘徊行为会叠加多个区域的驻留时长。这个补丁我在一个小型测试集上验证过两个摄像头重叠视野里跨镜头的 ID 匹配准确率约 78%有重叠区域时能达到 89%。效果不算完美但已经具备实际参考价值。如果你想自己动手改造建议从项目里的tracker/deep_sort.py入手它已经保留了外观特征提取的接口把特征通过自定义feature_bank共享给跨摄像头匹配模块即可。这块还有一个实用的衍生功能密度热力图。把每路摄像头的目标中心点坐标按时间聚合用cv2.applyColorMap映射成热力图叠加在画面底部。用校园场景测试时食堂门口中午 12 点的人群密度高峰、操场晚自习后的分散规律都能直接看出图来。这部分代码挂在web/目录的仪表盘页面上修改web/server.py里对应的 WebSocket 消息即可实时刷新热点图。从那以后我每次接手类似的项目都会强制走一遍完整的数据流推演从单帧检测到跨镜头轨迹串联确保每个环节的输入输出格式严格对齐。目标检测框的抖动、ID 切换、告警去重这些坑不亲自跑一遍源码光看文档很难建立直觉。项目源码里这些细节都留了合理的扩展点希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →