ARTICLE DETAIL

资讯详情

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

YOLOv5视频检测实战:解码调度、帧率优化与批量落地全解

YOLOv5视频检测实战:解码调度、帧率优化与批量落地全解 简介基于YOLOv5的视频检测案例包含完整源码与配套演示视频面向计算机、电子信息工程、数学等专业大学生适合作为课程设计、期末大作业或毕业设计的参考资料读者需具备一定Python和深度学习基础以便自行调试与二次开发。压缩包共102个文件大小约70.79MB主要涵盖41个Python脚本、40个YAML配置文件、6个Shell脚本和4个PyTorch模型权重文件另有Dockerfile、Jupyter Notebook、说明文档、示例图片及wmv格式演示视频覆盖从环境准备、模型定义到权重加载、视频推理的完整流程目录结构清晰便于按模块查阅。目前已有562人学习下载。通过源码与视频演示可快速理解YOLOv5处理视频检测任务的工程组织方式复现检测效果利用模型权重与YAML配置可针对不同场景做迁移实验Notebook与Dockerfile则便于交互式调试和跨环境复现适合作为二次开发与论文实验的起点。1. 视频检测不是逐帧看图先搞清楚解码和调度解压『基于YOLOv5检测视频案例源码视频.rar』之后很多人第一步是跑 detect.py看到画面里框画出来了就以为完事。但把单帧推理放进视频流结果往往是另一个味道单帧推理明明只要 30 毫秒整段视频跑下来帧率却只有十几帧目标一闪一闪甚至跑到一半黑屏退出。这不是 YOLOv5 没推理好而是视频检测链路上比模型更靠前的环节在拖后腿——解码、抽帧、缓冲、渲染哪一环慢了都会把锅甩给模型。视频检测不能理解成把图片检测放进 for 循环而是围绕帧率做一套调度给模型什么帧、丢掉哪些帧、按什么节奏回写结果这些决策才决定流程能不能用。这篇内容把落地路径说透拿到这类源码视频的案例包后环境怎么配、命令怎么敲、参数怎么调以及效果不行时优先怀疑谁。适合正在做毕设、视觉检测或视频违规内容检测的开发者也适合想搞明白视频检测与图片检测差异的从业者。2. YOLOv5视频检测的推理链路与三种落地形态2.1 视频帧到模型输入的完整链路视频检测的输入不是一张图片而是由解码器按时间顺序吐出来的一连串帧。以 OpenCV 为例VideoCapture.read()每次从解复用器里取出一帧未经压缩的 BGR 图像这一帧先经过 letterbox 处理等比缩放并填充灰边到 640×640归一化之后变成 CHW 张量进入 Backbone。YOLOv5 在每帧上的推理路径是单阶段的Backbone 做特征提取Neck 做多尺度融合Head 在三个尺度上输出框坐标、类别与置信度。这条链路里有两个细节与视频强相关第一letterbox 补齐的灰边在输出框坐标映射回原图时一定要减掉否则框是偏的第二Head 输出的框只基于当前这一帧没有任何跨帧信息所以视频里常见的闪烁、漏检在纯 YOLOv5 推理层面无解需要在后面加时间维处理。帧率问题也在这里产生。模型处理一帧的时间相对固定而视频解码是单线程的码率越高、分辨率越大CPU 压力越大解码慢了后面模型再快也没用。这也是为什么视频检测案例的源码包往往附带视频文件而不是图片——解码速度直接影响总耗时图片检测根本暴露不了这个问题。2.2 三种落地形态detect.py、Python API 与双线程把链路理清后视频检测的落地形态大体有三种选哪个取决于你手里源码包的用途。形态入口适合场景注意点detect.py 命令行官方仓库自带脚本第一次跑通、换权重验证要等整段视频跑完才出结果Python APItorch.hub.load批量目录、接业务逻辑自行处理 BGR/RGB 与坐标映射采集/推理双线程自研脚本RTSP 摄像头、实时检测帧队列满时要丢旧帧保实时只看结果正确性用 detect.py 最省事要对接业务系统、做结构化输出就绕不开 Python API要接边缘摄像头或实时流采集线程和推理线程必须分开否则解码慢一点就把推理线程堵死。源码案例包如果带了实时检测脚本走的基本是第三种。2.3 自己写 API 时的最小可跑循环import cv2 import torch model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.conf 0.30 # 覆盖默认置信度阈值 model.iou 0.45 # NMS 的 IoU 阈值 model.max_det 100 # 每帧最多保留 100 个目标 cap cv2.VideoCapture(input.mp4) while True: ret, frame cap.read() if not ret: break # frame 是 BGR直接送入模型内部会自动转 RGB results model(frame, size640) # xyxy[0] 是当前帧的 [x1, y1, x2, y2, conf, cls] for x1, y1, x2, y2, conf, cls in results.xyxy[0].tolist(): if conf 0.30: # 这里拿到的是原图坐标系不需要再做 letterbox 逆变换 print(round(x1), round(y1), round(x2), round(y2), round(conf, 2)) cap.release()代码里torch.hub.load(ultralytics/yolov5, ...)第一次跑会从官方仓库拉取模型定义并缓存权重如果你已经 clone 了官方仓库可以改成torch.hub.load(/本地路径/yolov5, custom, pathbest.pt, sourcelocal)适用于源码包里的离线权重。model(frame, size640)里 size 控制输入分辨率想提升小目标召回可以调到 1280但推理耗时接近翻倍视频场景要慎重。返回的坐标已经映射回原图坐标系直接画框或写日志都没问题。2.3.1 给实时检测加一个丢旧帧的队列import cv2 import queue import threading frame_q queue.Queue(maxsize4) def reader(src): cap cv2.VideoCapture(src) while True: ret, frame cap.read() if not ret: frame_q.put(None) break if frame_q.full(): try: frame_q.get_nowait() # 满了就丢最旧的一帧 except queue.Empty: pass frame_q.put(frame) cap.release() threading.Thread(targetreader, args(video.mp4,), daemonTrue).start() while True: frame frame_q.get() if frame is None: break # 处理当前帧代码与上一个示例相同 results model(frame, size640)队列maxsize4意味着最多缓冲 3 帧待处理数据。满了调用get_nowait()丢最旧帧而不是阻塞等待实时场景里画面新鲜度比完整帧序列更重要。这个设计对点播视频没有意义因为点播需要每一帧都处理但面向 RTSP 摄像头、行车记录仪这类持续输入它是防止延迟累积的关键。3. 视频检测源码包跑通环境、权重与 detect.py 参数3.1 yolov5环境配置的常见做法这类案例包里的源码几乎都是官方 YOLOv5 仓库加一份示例脚本第一步先把依赖环境装好。常见做法是独立建一个 conda 环境避免把系统 Python 装乱。conda create -n yolo python3.9 -y conda activate yolo # GPU 用户根据本机 CUDA 版本选择 cu118/cu121/cu126 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txtpython 3.8 到 3.10 都可以但 PyTorch 版本必须和 CUDA 对应。没有 GPU 就把索引地址换成cpu的 wheel跑小视频没问题帧率会低一些。之前遇到torch.cuda.is_available()返回 False多半是显卡驱动太旧而 PyTorch 装成了新版先执行nvidia-smi看驱动支持的 CUDA 版本再选匹配的 PyTorch。显存不够时不要急着换模型把--batch-size调低或者先把--img-size降到 480 验证流程。3.2 权重文件怎么选视频检测先 s 后 mYOLOv5 官方预训练权重有 s/m/l/x 四档源码包里一般会放一个best.pt。如果只有别人训练好的权重直接替换即可如果要拿官方 COCO 权重起步参考下面的选型。权重参数量GFLOPs视频检测建议yolov5s约 7.2M约 16.5首选CPU 或入门 GPU 都能带yolov5m约 21.2M约 49.0漏检明显时换用yolov5l约 46.5M约 109.1小目标多、画面复杂yolov5x约 86.7M约 205.7精度优先代价是慢第一遍跑通流程建议直接用 s。视频链路的瓶颈在解码时模型越大卡顿越明显如果 s 跑完发现目标漏检严重再往 m 升而不是一步到位上 x。模型换大之后--conf-thres和--img-size也要重新调直接用默认值容易误判。3.3 用 detect.py 检测视频一条命令和必调参数python detect.py \ --weights weights/best.pt \ --source videos/demo.mp4 \ --img-size 640 \ --conf-thres 0.30 \ --iou-thres 0.45 \ --device 0 \ --save-txt \ --project runs/detect \ --name case1命令的逻辑很直白--source是输入视频路径--save-txt把每帧检测结果落盘便于复核--project和--name决定输出目录。检测完成后的视频在runs/detect/case1/下文本结果按帧号组织在labels子目录里。参数默认值视频检测里怎么调--source0视频文件、目录或摄像头 IDmp4 最稳--img-size640720p 视频用 640小目标场景用 1280--conf-thres0.25视频里目标晃来晃去建议从 0.30 起步--iou-thres0.45目标重叠密集时不建议动默认即可--max-det300单帧目标特别多时调大防漏检--classesNone只检测指定类别传 COCO 类别 id如 0 2--devicecpu有 GPU 写 0多卡写 0 1--project/--nameruns/detect/exp每次跑新案例换 name避免覆盖旧结果--conf-thres是视频检测里最值得反复试的参数。阈值太低会有大量假框在背景上跳阈值太高真实目标被丢掉整个视频看起来“一会有框一会没框”。建议先跑 0.30再跑 0.40肉眼对比两版输出里同一辆车的出框连续性再定阈值。3.4 yolov5训练自己的数据集把 dirty 视频变成训练样本视频检测效果上不去的根本原因通常是预训练权重没见过你场景里的目标姿态。比如做车辆识别COCO 权重能检出车但倾斜视角、夜间灯光的车大概率漏检。这时候要走一遍 yolov5 训练流程。数据标注结构是images/和labels/两个目录外加一个data.yamltrain: ./datasets/custom/train.txt val: ./datasets/custom/val.txt nc: 2 names: [person, car]训练命令用官方自带脚本--hyp指定超参数文件python train.py \ --data data/custom.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img 640 \ --hyp data/hyps/hyp.scratch-low.yaml \ --device 0hyp.scratch-low.yaml是低数据量下比较稳的超参数组合里面mosaic、mixup这些数据增强开关直接影响模型对遮挡和运动模糊的泛化能力。视频场景建议保留 mosaic不要为了“训练更快”把它关掉。验证集里最好放一部分从视频里抽出的帧而不是只用与训练同来源的图片否则视频回测时暴露的问题在训练阶段根本发现不了。4. 视频检测结果不稳定的四个根因与修法4.1 GPU 利用率低但视频卡顿先查解码而不是模型跑完一段视频首要观察两个指标。nvidia-smi看 GPU 利用率top看 CPU 占用。如果 GPU 利用率只有 20% 左右而 CPU 接近打满说明解码跟不上了。OpenCV 的VideoCapture默认用 FFmpeg 在 CPU 上解复用和解码高码率 1080p 视频会让解码线程变成瓶颈模型反而一直空闲等待。处理思路按顺序来先把视频用 ffmpeg 降到 720p 验证如果帧率明显起来就确认是解码瓶颈。然后考虑 OpenCV 4.5.4 以后的硬件解码接口cap cv2.VideoCapture(input.mp4, cv2.CAP_FFMPEG) cap.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY)不是所有平台和编码格式都支持硬件解码设置后要打印cap.get(cv2.CAP_PROP_FPS)确认没有退回软件解码。实时场景里更直接的办法是推流端主动降帧率或者拉流端按固定间隔抽帧。不要第一时间把 yolov5s 换成 yolov5x解码跟不上时模型越大卡顿越严重。4.2 检测框一帧有一帧无置信度与时间维平滑视频里最常见的现象是同一辆车前两秒有框第三秒没框第四秒又出现。先排除阈值问题把--conf-thres从 0.25 调整到 0.35观察消掉的框是不是都是假目标如果真目标也被消掉说明模型对该目标的响应本身不稳定问题出在训练数据而不是参数。固定摄像头场景可以做一个简单的时间维滤波连续若干帧里同一位置出现次数达到阈值才保留。这个逻辑能有效抑制单帧孤立误检history [] def temporal_filter(dets, center_dist60, keep_cnt3): history.append(dets) if len(history) 5: history.pop(0) kept [] for det in dets: cx (det[0] det[2]) / 2 cy (det[1] det[3]) / 2 appear 0 for h in history[:-1]: for x1, y1, x2, y2, s, c in h: hc_x (x1 x2) / 2 hc_y (y1 y2) / 2 if abs(cx - hc_x) center_dist and abs(cy - hc_y) center_dist: appear 1 break if appear keep_cnt - 1: kept.append(det) return kept这里center_dist60是 640 输入尺寸下的经验值超过这个距离就认为是不同目标。对移动摄像头不适用因为目标在画面里移动快框中心位移大但对固定工位、路口摄像头非常有效。如果加了时间滤波还是闪就回到上一章的训练流程补充该目标在视频中的姿态和遮挡样本。4.3 视频打不开或中途断流编码兼容与重连监控视频常是 H.265 或 10bit 编码OpenCV 自带 FFmpeg 不一定支持表现是cap.isOpened()返回 True 但read()一直失败或者跑几十秒后突然黑屏退出。最稳妥的办法是统一转成 H.264 yuv420pffmpeg -i source.mkv -c:v libx264 -crf 20 -pix_fmt yuv420p output.mp4-crf 20属于高质量档位对检测精度影响可忽略-pix_fmt yuv420p保证播放兼容性格式转换对检测模型没有语义影响。转码后跑一遍如果问题消失说明是原文件编码问题而不是模型问题。实时流断流是另一类场景。检测脚本要自己处理read()返回 False 的情况否则线程直接退出。常见做法是判断失败后重新打开cap cv2.VideoCapture(url) while True: if not cap.isOpened(): time.sleep(2) cap.open(url) continue ret, frame cap.read() if not ret: cap.open(url) continue点播文件读到末尾时read()也返回 False此时要根据cap.get(cv2.CAP_PROP_POS_FRAMES)判断是正常结束还是断流避免无限重连。4.4 输出偏色与坐标错乱自己写 API 时的两个坑官方 detect.py 内部帮你处理了 RGB/BGR 转换和 letterbox 坐标还原自己写 API 就容易在这两个地方翻车。results.render()返回的是 RGB 数组直接cv2.imwrite保存会得到一张偏蓝偏红的图正确做法是先转回 BGRrgb results.render()[0] bgr cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) cv2.imwrite(frame_result.jpg, bgr)坐标方面直接用results.xyxy[0]拿到的是原图坐标系不需要自己做 letterbox 逆变换如果自己对帧做了预处理再喂给模型就必须在画框前把坐标映射回原图否则框会整体偏移。排查坐标问题时打印results.xyxy[0]的数值范围如果超出原图宽高多半是预处理路径写错了。5. 批量检测视频并输出结构化 JSON 结果5.1 目录批量检测按秒抽帧避免全量逐帧案例包里如果有几十段视频逐帧检测会非常耗时而且绝大多数帧是重复内容。常见做法是按视频帧率抽帧每秒取一帧做初步筛查命中片段再回头逐帧精检。这个脚本可以直接复用import json import torch import cv2 from pathlib import Path model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.conf 0.30 model.iou 0.45 output {} video_dir Path(videos) for video in sorted(video_dir.glob(*.mp4)): cap cv2.VideoCapture(str(video)) fps cap.get(cv2.CAP_PROP_FPS) if not fps or fps 0: fps 1 total int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) frame_idx 0 hits [] while frame_idx total: ret, frame cap.read() if not ret: break # 每秒抽一帧进入模型其余帧跳过 if frame_idx % max(1, int(fps)) 0: dets model(frame).xyxy[0].cpu().numpy() for x1, y1, x2, y2, conf, cls in dets.tolist(): hits.append({ t: round(frame_idx / fps, 2), box: [round(v, 2) for v in (x1, y1, x2, y2)], conf: round(float(conf), 3), cls: int(cls), }) frame_idx 1 cap.release() output[video.name] {frames: frame_idx, hits: hits} with open(batch_result.json, w, encodingutf-8) as f: json.dump(output, f, ensure_asciiFalse, indent2)max(1, int(fps))防止帧率读出异常导致除零。frame_idx % int(fps) 0意味着每秒第一帧进入模型后续按整秒对齐。hits 里t是目标出现的秒级时间点box是原图坐标后续做片段裁剪或人工复核都用这两个字段。第一次批量跑的时候把indent2保留方便肉眼检查 JSON 结构是否合理。5.2 用 JSON 结果定位违规片段和高频目标时段批量结果落盘后最直接的用法是筛选高置信度目标出现集中的时间段。比如做视频违规内容检测读取 JSON按cls分组找出目标密度最高的若干分钟再用 ffmpeg 截取这些时间段做逐帧精检省去对整段视频跑模型的成本。hits 里 box 字段还能用来做后续的裁剪入库比如把检出车辆的区域保存成图片作为自动标注的初始标签——这也是源码视频案例最常延伸出去的用途。5.3 防漏检的验证技巧批量跑完后别急着看 JSON 结果先检查frames字段和解码状态。frames应约等于视频时长乘以帧率如果远小于预期说明中途read()失败跳出了循环这段视频的结果不可信需要转码后重跑。更主动的做法是抽几个时间段用 ffmpeg 抽出单帧人工比对ffmpeg -ss 00:30 -i input.mp4 -frames:v 1 check.jpg把人工看到的显著目标和 JSON 里该时间点的命中列表对比至少抽 3 个不同时段。只验证一帧没有意义视频检测的稳定性问题往往集中在场景切换、光照突变的片段这些帧恰恰是抽检最容易漏掉的位置。本文还有配套的精品资源点击获取
返回列表