ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv5与OpenCV的校园异常行为检测与预警系统实战

基于YOLOv5与OpenCV的校园异常行为检测与预警系统实战 简介基于深度学习与计算机视觉的智能校园安全监控系统项目资料源自华南理工大学大学生创新创业训练计划面向目标检测、图像处理及校园安防系统开发的学生与研究者可用于快速理解YOLOv5与OpenCV在实际场景中的集成方案。项目围绕校园异常行为检测与预警展开涵盖视频帧预处理、目标分类定位、行为识别及声光报警机制并针对光照变化、背景干扰、监控视角局限等复杂情况给出了系统设计与参数调优方向。压缩包共213个文件、约3.03MB以Java与XML源码为核心同时包含PNG界面资源、HTML说明文件、JAR依赖库、TXT使用说明、属性及环境配置文件、项目报告等源码仓库、配置和文档均包含在内便于读者检查依赖、复现实验或进行二次开发。已有92人学习适合需要参考完整大创项目结构、掌握从数据采集到预警触发的端到端智能监控流程的读者可从中获得代码、配置、说明与报告一体的实践资料。1. 校园安全监控为什么要自己训练YOLOv5一套大创项目的可靠落地路线高校保卫处的值班室里一排屏幕实时滚着二十多路摄像头画面值机员眼睛再快也没法同时盯住每个角落。传统监控系统的作用是“事后回放”而标题里的“异常行为检测与预警”要的是“提前几秒告诉你有事发生”。这个方案的核心是把深度学习与计算机视觉接到现有摄像头后面用YOLOv5做目标检测把画面中的人、摔倒的肢体轮廓框出来再用OpenCV做图像处理分析框的位置、宽高比和运动轨迹判断是否该触发预警。它尤其适合正在做大学生创新创业训练计划的团队、拿这个题做毕业设计的同学以及那些摄像头齐全但不想换硬件的校园信息化改造项目。选这条路线还有一个现实理由学校现有的摄像头大多只支持RTSP取流没有智能分析能力。与其花大价钱换支持AI的摄像机不如在机房一台普通GPU服务器上把这套检测系统跑起来。这也是大创项目里少有的“成本低、演示效果直观、答辩时能现场演示”的方向。2. 搭起YOLOv5OpenCV检测底座环境配置、摄像头取流与最小推理闭环这个项目里最容易被低估的不是算法而是第一步的环境。很多人照着网上的教程把YOLOv5源码下载下来却卡在依赖冲突上最后连一张测试图都跑不出来。这里的经验是先搭一个干净的conda环境再把官方detect.py跑通最后才接入真实摄像头。顺序错了后面训练时你根本分不清是模型问题还是环境问题。2.1 conda环境与YOLOv5源码准备装依赖前先决定两件事装环境前先拍板两件事用哪个Python版本用GPU还是CPU。前者决定你后面会不会被一堆编译报错缠住后者直接决定训练一轮要等多久。我的默认组合是Python 3.9 PyTorch的CUDA 11.8预编译包这套组合在YOLOv5的生态里踩坑最少。如果你的机器没有NVIDIA显卡也可以装CPU版先跑通推理训练就用云主机或者实验室的GPU服务器。# 创建独立的conda环境Python版本固定为3.9 conda create -n campus_safe python3.9 -y conda activate campus_safe # 有NVIDIA显卡就装CUDA 11.8对应版本纯CPU机器去掉--index-url参数 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 拉取YOLOv5官方源码 git clone https://github.com/ultralytics/yolov5 cd yolov5 # requirements.txt里已锁定opencv-python等依赖的版本直接装 pip install -r requirements.txt # 验证OpenCV安装成功顺便看下版本 python -c import cv2; print(cv2.__version__)这段命令的逻辑是conda环境把你的项目依赖跟系统Python隔离避免把系统环境弄乱PyTorch是YOLOv5的推理后端CUDA版本必须和显卡驱动匹配11.8是当前兼容面最广的一个requirements.txt是YOLOv5官方维护的依赖清单里面已经包含了OpenCV不需要单独再装一份。参数上要注意如果你用的不是NVIDIA显卡却强行带--index-url装CUDA版后面import torch会直接报错所以纯CPU机器就不要加这个参数。还有pip install时必须先进入yolov5目录否则它会找不到相对路径下的requirements.txt。Python版本这里宁可保守。别看到Python 3.12就想尝鲜YOLOv5里不少依赖在3.10以上的版本里容易出现二进制兼容问题。用3.9不是因为它新而是因为社区里跑通的人最多遇到问题搜一下答案基本都现成。环境配到这个程度才算真正把“yolov5环境配置”这一段踩实了。2.2 跑通官方detect.py先认识conf、iou、imgsz这三个关键参数环境装好之后先别急着改代码。直接跑一遍官方自带的推理脚本它能一次性验证权重、模型结构和OpenCV画框这三个环节是否正常。这一关没过后面接摄像头时出了问题你都不知道该查谁。# 用官方yolov5s权重测试一张图片weights缺省时会自动下载 python detect.py --weights yolov5s.pt --source test.jpg --conf-thres 0.25 --iou-thres 0.45 --imgsz 640跑完去runs/detect/exp目录看结果如果test.jpg上出现了带着类别标签的彩色框说明链路通了。这里conf-thres是置信度阈值意思是模型对某个框的把握低于0.25就丢掉值调高能减少误报但也会漏掉小目标iou-thres是NMS的IoU阈值值越大两个重叠的框越容易被合并成一个imgsz是输入网络的边长640是速度和精度的平衡点显存小或者用CPU推理时改成320能快不少。官方默认参数先别动等摸清自己数据的分布再去调。这一步的意义不在于测一张图而在于验证你的环境是“可用”的。很多同学跳过这一关直接训练等训练时发现报错根本分不清是环境缺包还是训练脚本的问题最后白白浪费几小时甚至一天。跑通detect.py之后后面所有改动都有了对照基线。2.3 用OpenCV读取RTSP摄像头替换图片输入最小实时推理循环校园摄像头通常走RTSP协议OpenCV的VideoCapture可以直接拉流。把detect.py的图片输入换成while循环读帧是把这个项目从“图片演示”变成“实时监控”的关键一步。这里给出一个最小实现它足够你在实验室里验证效果。import cv2 import torch # 用官方权重加载模型pretrainedTrue表示自动下载yolov5s.pt model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.conf 0.25 # 置信度阈值监控场景建议后续调到0.4以上 model.iou 0.45 # NMS的IoU阈值 model.max_det 50 # 每帧最多保留50个检测框 # RTSP地址以实际摄像头为准0表示本地USB摄像头 cap cv2.VideoCapture(rtsp://192.168.1.64:554/Streaming/Channels/101) while True: ret, frame cap.read() if not ret: print(摄像头取流失败检查RTSP地址) break # YOLOv5推理内部自动完成BGR转RGB、letterbox缩放和归一化 results model(frame, size640) # 直接把检测结果画到原帧上返回的仍是BGR格式的numpy数组 annotated results.render()[0] # OpenCV窗口显示按q退出 cv2.imshow(campus_safety, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的关键点有三个。第一torch.hub.load是懒人方案适合验证但正式项目里我一般直接用本地clone的源码和weights参数加载权重因为hub方式每次都要检查网络离线场景会翻车。第二model(frame, size640)一步完成了预处理、推理、后处理results.render()会返回画好框的帧你不需要手动写NMS这些细节YOLOv5都封装好了。第三OpenCV在这里干的活是取流、显示和画框它是介于摄像头和模型之间的“搬运工”。参数上waitKey(1)表示窗口刷新间隔1毫秒实际帧率受推理耗时控制一轮循环大约20到30毫秒可以稳定跑到20FPS以上。如果你接的是RTSP流记得确认摄像头的编码是H.264有些老旧摄像头输出MJPEGOpenCV的VideoCapture拉不了。只做CPU推理的话把size640改成320、模型换成yolov5s的小兄弟yolov5n能缓解不少但精度下降也比较明显。这一节跑通就等于拿到了整条检测管线的“地基”。3. 把通用YOLOv5调成校园专用模型自建数据集、VOC转YOLO与最该调的4个超参数3.1 为什么不能直接用yolov5s.pt检测异常行为yolov5s.pt是在COCO数据集上训练的它只认识person、dog、car这类通用类别能告诉你“这里有个人”但没法告诉你“这个人摔倒了”。而校园异常行为检测要求的是场景化语义——同样是一个人躺在地上在操场上可能是休息在楼梯口可能就是摔倒。解决这个问题要靠迁移学习用自己的标注数据对YOLOv5做微调保留它在通用物体上的特征提取能力同时让模型重新学会“摔倒的人”和“奔跑的人”这些新类别。这个方案里“训练自己的数据集”是必经环节。常见做法是第一版只定义3到4个异常类别比如fallen_person摔倒、running_person奔跑——用于走廊追逐、intrusion禁区闯入通过后文ROI逻辑处理、crowd聚集。类别一定要克制每多一个类别需要的标注样本量就成倍增长。大创项目的周期有限把类别控制在4个以内数据量才攒得出来。这里顺带解释一个原理YOLOv5的权重是分层的浅层负责颜色和边缘深层负责语义。微调时通常冻结前几层只更新后面的卷积层所以即使你的数据和COCO差异很大训练收敛也比从零开始快得多。这也是为什么你只需要几千张标注图而不是几十万张。3.2 数据准备与标注VOC转YOLO格式的转换脚本数据来源一般有三个网上能找到的公开跌倒检测数据集、校园监控的现有录像截图、以及自己拿手机在相似场景补拍的片段。我的习惯是每个类别凑到1500到2000张其中负样本正常行走、正常站立的人占一半负责压住误报。标注工具用labelimg它支持画出矩形框并直接导出VOC格式的XML文件。YOLOv5训练需要的是每个图片对应一个同名txt里面每行写类别编号和归一化后的框坐标。转换脚本是我每次做检测项目都会用到的模板逻辑简单但容易写错贴出来可以直接套用。import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_dir, out_dir, classes): # 遍历VOC标注目录下的所有XML文件 for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 读取图片真实宽高用于坐标归一化 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) with open(os.path.join(out_dir, xml_file.replace(.xml, .txt)), w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue box obj.find(bndbox) x1, y1, x2, y2 [float(box.find(t).text) for t in (xmin, ymin, xmax, ymax)] # 转换为中心点宽高的归一化格式 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h f.write(f{classes.index(cls)} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n) print(转换完成请检查输出目录下的txt文件) classes [person, fallen_person, running_person, crowd] convert_voc_to_yolo(annotations, labels, classes)说明一下代码逻辑XML里的bndbox存的是左上角和右下角的像素坐标而YOLO格式要求的是相对于图片宽高的中心点坐标和宽高所以先算中心点再统一除以img_w和img_h。classes列表的顺序一旦定下来就不能改训练时用的顺序和推理时写死的顺序必须完全一致否则类别标签会对不上这是最隐蔽的错误之一。标注时还有个容易忽略的细节摔倒的人的框要尽量框住全身包括蜷缩的腿不能只框躯干——因为后面我们要用框的宽高比来辅助判摔倒框得不完整会把几何特征破坏掉。3.3 配置数据集yaml并启动训练4个超参数最值得调训练之前先按YOLOv5的约定组织目录结构images和labels平行train和val分开。然后在yolov5/data目录下建一个campus.yaml内容如下train: data/campus/images/train val: data/campus/images/val nc: 4 names: [person, fallen_person, running_person, crowd]yaml写好后训练命令就是一行python train.py --data data/campus.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --imgsz 640 --device 0训练时最值得调的4个超参数按优先级排序batch-size、imgsz、epochs、lr0。batch-size取决于显存8G显存跑yolov5s时16通常就是上限再大就会OOMimgsz建议默认640除非你的摄像头画面较小或者对速度很敏感epochs对迁移学习来说100起步同时打开早停patience20loss连续20轮不降就自动停不用干等lr0是初始学习率默认0.01对大多数场景适用但如果loss在0.7附近抖成一条直线把lr0调成0.005重开一轮往往能救回来。训练结束后看两个东西runs/train/exp下的results.png重点看val_loss是否持续下降以及训练完跑一遍val.py看mAP0.5。校园监控这种场景mAP0.5到0.85以上基本就能用但这个数字只是参考最终要以真实摄像头下的误报率为准。很多人盯着loss值焦虑其实loss绝对值没有统一标准不同数据集差别很大趋势比数值重要。4. 从检测框到异常行为预警摔倒、闯入、聚集的判定逻辑与OpenCV后处理4.1 异常行为识别不需要第二套模型检测框就是富矿很多人把这个项目想复杂了以为除了YOLOv5还得再训练一个分类器去识别行为。实际上YOLOv5输出的每一帧检测框坐标、宽高、置信度本身就是行为分析的数据源。摔倒可以看成目标由站立到躺平的过程框的宽高比从大于1突变到小于1同时中心点快速下移。闯入可以看成检测框的位置与预设禁区多边形的包含关系。聚集可以看成某个区域内检测框数量在时间窗口内激增。这些判定用OpenCV的几何函数和简单的帧间状态机就能实现既省了标注成本推理速度也快。这也回答了“计算机视觉和机器学习区别”那个常见疑问模型负责“看到”目标OpenCV这类图像处理手段负责“理解”目标之间的空间与运动关系。两块拼起来才是完整的监控智能。4.2 摔倒判定宽高比加下坠速度双条件摔倒识别的核心是防止把蹲下、系鞋带、弯腰捡东西误报。深度学习的经验是单帧特征不可靠必须加时序约束。我的实现如下class FallDetector: def __init__(self, need_frames3): self.need_frames need_frames # 连续满足几帧才算摔倒 self.fall_frames 0 self.last_cy_h None def check(self, box): x1, y1, x2, y2 box w x2 - x1 h y2 - y1 # 宽高比正常站立通常1.5倒地后变扁0.8 ratio h / (w 1e-6) cy (y1 y2) / 2 speed 0 if self.last_cy_h is None else abs(cy - self.last_cy_h) self.last_cy_h cy # 双条件目标变矮且重心快速下移 if ratio 0.8 and speed 20: self.fall_frames 1 else: self.fall_frames 0 return self.fall_frames self.need_frames fall FallDetector() # 在推理循环里对每个person检测框调用 fall.check(box)解释下两个关键阈值。ratio阈值0.8正常站立的人框高大约是宽的1.5到2倍倒地后人体的外接矩形会变成扁的宽高比往往小于0.8这是第一个条件。speed阈值20是第二个条件匹配的是人体倒地时的重心下坠速度在25FPS的帧率下大约是每秒0.5到1米的像素位移具体要按摄像头安装高度现场调。两条件必须同时成立才能过滤掉蹲下捡东西重心位移小和坐下来宽高比变化但速度不够这类干扰。need_frames3是时序消抖连续3帧触发再告警单帧抖动不会造成误报。这里有一个容易被忽略的问题同一个摔倒的人在画面里停留时如果不做目标跟踪每一帧都会调用独立检测告警会重复触发。最简单的对策是加一个冷却时间同一目标的框与上一帧匹配上IoU大于0.3就算同一个触发告警后10秒内不再重复告警。进阶一点就是接入ByteTrack这类轻量跟踪器给每个目标分配ID但这会增加代码复杂度第一版用冷却时间就够了。4.3 禁区闯入OpenCV多边形ROI与底边中心点判定校园场景里的禁入区通常是多边形比如配电房门口、楼顶边缘、施工围挡。OpenCV的pointPolygonTest可以直接判断点是否在多边形内用它来做人框的位置过滤比训练一个分类器快得多也准得多。import cv2 import numpy as np # 在画面里划定禁区顶点按顺序闭合 roi np.array([[100, 200], [300, 150], [420, 350], [150, 400]], dtypenp.int32) def is_in_roi(box): # 取检测框底边中心作为落脚点 foot_x (box[0] box[2]) / 2 foot_y box[3] # 底边的y坐标 # measureDistFalse表示只判断内外返回正值在内部负值在外部 return cv2.pointPolygonTest(roi, (float(foot_x), float(foot_y)), False) 0用底边中心点而不是框中心是有实际经验的人站在禁区围挡外面时框中心可能还在围挡上方底边已经踩进线内用中心点判定会漏报。pointPolygonTest的第三个参数False表示不计算点到多边形的精确距离只返回正负号速度极快。这个函数还能顺手解决一个问题检测框边缘略微越过红线但实际人没进入时可以通过在ROI基础上缩小一圈把多边形向内收缩几个像素来消除抖动。4.4 聚集检测与预警推送不要阻塞推理主循环聚集检测最朴素也最实用的指标是某个区域内的person框数量在连续若干帧内超过阈值。比如在食堂门口划一个区域区域内person类检测框超过5个且持续10秒就触发聚集预警。实际实现时不需要重新画区域直接统计全画面检测框数量或者按ROI过滤后计数即可。注意OpenCV里坐标原点是左上角x向右、y向下ROI多边形的顶点顺序也必须按这个坐标系来画否则多边形会变形。预警推送是另一个易翻车点。如果告警处理写数据库、发HTTP回调、推企业微信直接写在YOLOv5的推理循环里帧率会瞬间掉到底因为网络请求是阻塞式的。常规做法是用生产者和消费者模型import queue import threading def alert_worker(alert_queue): # 后台消费告警消息 while True: msg alert_queue.get() try: # 在这里实现推送WebHook回调/微信机器人/写日志 print(f[ALERT] {msg}) finally: alert_queue.task_done() alert_queue queue.Queue(maxsize10) threading.Thread(targetalert_worker, args(alert_queue,), daemonTrue).start() # 推理循环里检测到异常时只做一件事 alert_queue.put(14:23:05 东操场护栏有人闯入)queue的maxsize10是给告警通道上了个保险后台推送万一卡住几秒队列满了之后新的告警会被丢弃而不是让内存无限增长这在无人值守的监控场景下很重要。daemonTrue保证主程序退出时后台线程自动结束不会留下僵尸线程。OpenCV的imshow窗口也可以在这个模式下继续用画面显示和告警推送互不干扰整个预警察链路的瓶颈只剩下模型推理本身。5. 避坑YOLOv5OpenCV校园监控的5条踩坑记录从环境报错到夜晚失灵模型训练能跑通只是开始这套系统真正的考验在真实校园环境里。我见过太多次训练mAP很高、一接到现场摄像头就各种翻车的案例。这一章按“现象 → 原因 → 解决”的格式整理5条最有代表性的踩坑记录每一条都对应一个真实常见的后果。5.1 conda环境里报No module named cv2装错了环境现象代码第一行import cv2就报ModuleNotFoundError: No module named cv2但明明刚才pip install opencv-python成功过。原因pip默认装到了系统Python里而你现在激活的conda环境是campus_safe两个环境的site-packages互不相认。YOLOv5的requirements.txt装依赖时如果没激活环境或者中途退出了conda也会出现这种错位。解决先conda activate campus_safe再在环境内执行pip install opencv-python。验证是否装对位置看import cv2后cv2.__file__的路径是否在campus_safe的site-packages下。这里的通用规则是环境要用哪个Python就在哪个环境里装包不要图省事直接sudo pip install那是把系统环境弄乱的经典路径。5.2 训练loss一直不降超参数没适配自己的数据现象训练跑了几十个epochval_loss在0.7附近横向抖动mAP只有0.3左右比不训练还差。原因两个最常见原因。一是lr0太大导致loss在极小值附近震荡不收敛二是数据不平衡比如fallen_person只有100张person有2000张模型直接把所有目标都预测成person精确率看着高但异常类别全部漏检。解决先把lr0从0.01降到0.005重跑一轮再检查数据集里每个类别的样本数最少的类别也要补到500张以上否则就合并类别做二分类正常、异常而不是硬撑四个类。补数据不方便时可以用数据增强凑数但别只做水平翻转摔倒和奔跑这种方向性行为翻转后语义可能错乱。5.3 把教室吊扇当人反复触发预警置信度阈值和类别混淆的锅现象白天在普通教室测试摄像头把天花板上的吊扇识别成person频繁触发闯入告警防不胜防。原因吊扇旋转的叶片在画面上形成了类人的运动模糊纹理深夜静态画面里排风扇的圆形轮廓也容易被误判。YOLOv5的conf-thres默认0.25监控场景下这个值太低风扇叶片有大量帧能达到0.3左右的置信度。解决把model.conf从0.25拉到0.4到0.5误报会显著下降同时观察有没有把远处小目标漏掉如果还是有风扇误报收集一批风扇叶片画面的负样本标成空背景图片不放任何类别标签加入训练集。负样本是压误报最有效的手段比反复调阈值更治本。这个案例也说明异常行为检测的最终标准不是mAP而是误报率降低误报要靠在真实场景里调阈值、补负样本而不是继续卷模型结构。5.4 晚上关灯后一个目标都检测不到图像预处理失位现象白天检测一切正常晚上走廊只剩暗淡的灯光时系统几乎不产生任何检测框偶发跳出一个置信度0.1的框。原因YOLOv5训练时用的是正常亮度数据夜间低照度画面进入网络后特征分布和训练分布差距太大。OpenCV直接拿原始帧做letterbox缩放没有做任何亮度补偿。这是“场景泛化”问题不是模型坏了。解决在进模型之前插入图像增强预处理。常见做法是转灰度后做CLAHE自适应直方图均衡化再转回BGR送进网络或者简单地用gamma校正把暗部提亮。更稳妥的方案是给夜间数据单独建一个类别叫night_person采集几百张夜间画面加进训练集。CLAHE会牺牲一点帧率但换来的是夜间可用性值。# 夜间帧增强CLAHE自适应直方图均衡化 lab cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) l, a, b cv2.split(lab) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) l clahe.apply(l) frame cv2.cvtColor(cv2.merge((l, a, b)), cv2.COLOR_LAB2BGR)参数上clipLimit2.0控制对比度增强强度数值越大增强越猛但噪声也越明显tileGridSize(8,8)是局部区域划分画面分辨率不高时用默认值就好。如果用了红外摄像头还要注意红外画面是灰度图直接做BGR通道复制后再增强效果比在单通道上做要好。5.5 推理只要20ms画面却只有8FPSOpenCV显示与预处理抢资源现象单独测模型推理一帧只要20ms换成完整摄像头循环后画面只有8FPS卡得没法看。原因瓶颈不在推理而在显示和预处理。cv2.imshow的窗口刷新在高DPI屏幕上很费CPU转灰度、做letterbox、复制数组这些Python操作都在拿CPU忙和模型抢同一个GIL。还有个隐藏坑是VideoCapture的缓存队列摄像头的帧率是25FPS但推理只有20ms队列里积压了旧帧你处理的永远是几秒前的画面。解决把VideoCapture的缓存读空每循环先抓帧直到读到最新帧显示时把图像缩小到640宽再imshow或者干脆去掉imshow只把检测结果写入MJPEG流由浏览器端显示。实时性优先的话用“跳帧”策略检测每2帧做一次中间帧直接透传画面流畅度和检测覆盖率都能兼顾。这几条坑的共同点是都能靠监控日志快速定位。我习惯了在预警触发时把当时的原图、检测框、置信度和触发条件一起落盘这样翻车时不用瞎猜翻日志就能还原现场。尤其是学生团队做项目答辩时这份误报复原记录比mAP曲线更能说明工程能力。6. 进阶优化用帧差法ROI预筛选把在线推理量砍下一半6.1 帧差法预筛选的代码与参数监控场景有个天然规律校园走廊、操场、楼梯在大部分时间里是空荡荡的。如果每一帧都送进YOLOv5GPU在空场景上白耗电CPU服务器更是直接撑不住。这里有一个立竿见影的优化先用OpenCV的帧差法判断画面有没有运动没运动就不做推理。帧差法的原理很简单前后帧对应像素灰度值相减差值超过阈值的像素就是运动区域。配合膨胀操作把零散的运动点连成块再用面积占比决定是否跳过当前帧。gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if prev_gray is not None: diff cv2.absdiff(prev_gray, gray) # 前后帧灰度差 _, motion cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY) motion cv2.dilate(motion, np.ones((5, 5), np.uint8), iterations2) motion_ratio cv2.countNonZero(motion) / (frame.shape[0] * frame.shape[1]) if motion_ratio 0.05: # 运动面积太小跳过本帧推理 prev_gray gray continue prev_gray gray # 正常执行YOLOv5推理参数上阈值25是灰度差太低会把摄像头噪点当成运动太高会漏掉慢速行走的人膨胀核5x5、迭代2次是为了把行人的手臂和腿部运动噪点连成片motion_ratio小于0.05表示运动区域占整幅画面不到5%这种帧通常没有人或只有极小的目标跳过是安全的。这套预筛可以放在YOLOv5和OpenCV的取流循环之间成本几乎为零。6.2 验证这个方法是否适合你的场景验证方法不是看FPS而是统计两件事跳帧率和告警漏报数。在正常监控画面下跑一小时跳帧率达到50%以上说明优化有效同时人工回放这一小时录像确认没有任何一次真实的摔倒或闯入被跳过帧漏掉。如果场景里一直有人走动运动面积一直超过5%预筛的作用会变弱这种情况就不用强行加。另一个更重的方案是TensorRT或者ONNX导出但对大创项目来说帧差法预筛投入产出比最高因为它还顺手降低了摄像头缓存积压让画面延迟也变小了。这个优化给我最大的教训是实时监控系统的性能优化先砍掉无意义计算再去谈引擎加速。我最早也是迷信模型推理速度把YOLOv5s换成TensorRT折腾一周只快了一倍后来发现把空帧跳过之后系统整体负载直接降了60%而精度几乎没损失。先看数据再看玄学。希望帮到你。本文还有配套的精品资源点击获取
返回列表