ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于YOLOv5的明厨亮灶老鼠检测实战:数据、训练与部署

基于YOLOv5的明厨亮灶老鼠检测实战:数据、训练与部署 简介目标检测是计算机视觉中的基础任务旨在定位图像中的特定目标。YOLO系列算法凭借单阶段检测的实时性优势成为工业落地的热门选择。基于YOLOv5的训练流程涉及数据标注格式转换、目录组织、增强策略等关键环节直接决定模型精度。通过合理划分数据集、调整超参数可有效解决小目标、遮挡等问题。该技术广泛应用于明厨亮灶场景下的老鼠检测结合ONNX/TensorRT导出和阈值调优能够在边缘设备上实现实时告警。本文围绕老鼠检测任务完整呈现从数据准备到部署的全流程避坑经验帮助团队快速构建可复现的检测方案。1. 明厨亮灶与老鼠检测为什么YOLOv5这套组合值得直接投入明厨亮灶项目里最棘手的一类检测就是老鼠目标小、移动快、拍摄条件差往往是夜间红外或逆光厨房漏检一个就是食安事故。基于YOLOv5的老鼠检测源码模型2018张图片及对应标签解决的正是“有数据、有模型、有可复现训练流程”这三个卡点让团队不用从零造轮子。如果你是负责食安算法或边缘设备落地的工程师这套组合能直接帮你砍掉数据采集和模型调参前两个月的空窗期。下面从数据、训练、部署三个方向把这条路径拆开讲。2. 数据集整理2018张老鼠图片的标签格式、目录结构与划分策略2.1 标签文件长什么样从VOC XML到YOLO TXT基于YOLOv5训练的老鼠检测项目标签必须放在和图片同名的.txt文件里每一行代表一个标注框格式是class_id center_x center_y width height其中center_x、center_y、width、height全部是相对于图片宽高的归一化值范围 0 到 1。举个例子一张 1920×1080 的图上有一只老鼠框的左上角在 (480, 270)宽 640高 360那对应的一行就是0 0.5 0.375 0.3333 0.3333因为center_x (480640/2)/1920 0.5center_y (270360/2)/1080 0.375width 640/1920 ≈ 0.3333height 360/1080 ≈ 0.3333。class_id是整数从 0 开始计数。对这个项目来说类别只有rat一个所以 class_id 恒为 0。如果标注入手是 VOC 的 XML 文件需要先转换。我一般用这一段 Python 脚本import os import xml.etree.ElementTree as ET from glob import glob def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if not lines: return out_path os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines)) class_names [rat] for xml_file in glob(labels_voc/*.xml): voc_to_yolo(xml_file, labels_yolo, class_names)这段脚本做的事就是把 XML 里的绝对坐标除以图片宽高变成归一化值。两个坑要注意第一有些标注工具的坐标是整数有些是浮点统一除以宽高即可第二图片尺寸必须以 XML 里size标签的值为准不要自己用 PIL 去读因为标签和图片可能来自不同批次读出来的尺寸会对不上。如果你的数据集里混有未标注的老鼠样本也就是空标签文件YOLOv5 在训练时会跳过它但不报错如果混进验证集则会把漏检算进去。我一般把空标签文件单独放到一个empty/目录不混进训练集避免对 loss 产生干扰。坐标精度方面代码里输出保留了 6 位小数实际使用中足够。即使是一张 4000×3000 的超高清图6 位小数对应的误差也只有 0.004 像素级别。但有一种情况需要注意如果标签来源是 JSON 格式的 COCO 标注比如某些标注平台导出的annotations.json里面的坐标是[x, y, width, height]的绝对像素值且x, y是左上角转成 YOLO 格式时不要忘记把width/2加到 x 上算出中心点。这个“左上角坐标 vs 中心点坐标”的差异是我见过最多的一次性转换错误。提示标注坐标的精度控制在小数点后 4 位即可过高的精度不会带来可观察的提升反而会让标签文件体积变大、读取变慢。2.2 目录组织让 YOLOv5 的 dataloader 不报错用这套源码训练数据目录要匹配 YOLOv5 的约定。官方源码里datasets的默认结构是dataset/ ├── images/ │ ├── train/ │ │ ├── cam01_20240518_143201.jpg │ │ └── ... │ └── val/ │ ├── cam02_20240518_143212.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── cam01_20240518_143201.txt │ │ └── ... │ └── val/ │ ├── cam02_20240518_143212.txt │ └── ... └── rat.yamlimages和labels两个目录必须严格同名配对YOLOv5 的 dataloader 通过替换后缀来找对应 label。如果你的原始文件命名混乱例如IMG_20240101_120000.jpg对应20240101_120000.txt先统一重命名再进训练不要让脚本去猜对应关系。下面的 bash 命令可以把一个乱序目录批量改成train_000001.jpg这种名字i1 for img in raw_images/*.jpg; do new_name$(printf train_%06d $i) mv $img dataset/images/train/${new_name}.jpg i$((i1)) done注意这里只重命名图片标签文件需要在同一步里做同样映射否则对不上。更稳妥的做法是先导出 CSV 映射表再让两个重命名过程读同一份映射不要各写一遍循环。我实际处理 2018 张图时是按摄像头编号加时间戳来命名例如cam03_20240518_143201.jpg这样后期排查某次漏检时能直接定位到设备和时间点。如果你的标签文件名和图片文件名不一致用下面这段小脚本检查同名文件是否成对for img in dataset/images/train/*.jpg; do base$(basename $img .jpg) if [ ! -f dataset/labels/train/${base}.txt ]; then echo missing label: $base fi done这个巡检脚本在我接手别人给的数据集时几乎是必跑的。很多标注平台导出的文件夹里会混有图片和多个版本的标签名称对不齐是常态。不先把成对关系确认好训练出来的模型会出现大量漏检而且很难排查是模型问题还是数据问题。2.3 划分策略别用随机划分按摄像头和时间切明厨亮灶的数据有一个特殊性同一个厨房的摄像头背景、光照、视角完全不同。如果随机划分训练集和验证集验证集里很可能会出现和训练集同场景的相似帧val loss 会看起来很低但实际换个摄像头就翻车。我一般按摄像头 ID 切分80% 的摄像头进训练20% 的摄像头进验证保证验证集的数据分布接近真实部署。划分代码大致长这样import os import random from collections import defaultdict src_images dataset/images/all cam_map defaultdict(list) for img in os.listdir(src_images): cam_id img.split(_)[0] # 假设文件名以 cam03 开头 cam_map[cam_id].append(img) random.seed(42) train_imgs, val_imgs [], [] for cam_id, imgs in cam_map.items(): random.shuffle(imgs) split int(len(imgs) * 0.8) train_imgs.extend(imgs[:split]) val_imgs.extend(imgs[split:]) # 按摄像头统计数量样本不足的摄像头做特殊处理 for cam_id, imgs in cam_map.items(): print(cam_id, len(imgs))这里random.seed(42)让每次划分结果一致方便复现对每个摄像头内部做随机切分而不是在全部图片上随机切。如果 2018 张图里某个摄像头只有 10 张按 80% 切会导致验证集只有 2 张这种情况我把它全部并入训练集验证集只保留样本量足够的摄像头。这也是明厨亮灶这类多路视频项目和数据比赛最大的差别数据分布跟着摄像头走不跟着类别走。一个更严格的做法是把同一摄像头的连续时间抽帧也区分开比如摄像头 A 的前 80% 时间帧进训练集后 20% 进验证集。这样连时间偏差都防住了。我在第一个明厨亮灶项目里用随机划分上线两周后挨了一个真实场景的漏检事故从那以后一律按摄像头加时间双重切分。2.4 数据增强与样本平衡2018张图怎么用出5000张的效果2018 张图在目标检测里不算多尤其是老鼠这种小目标。增强策略不要照搬 COCO 的默认配置重点做三类调整。第一是 mosaic。YOLOv5 默认开启马赛克增强从训练集里随机拿 4 张图拼成一张。老鼠目标小mosaic 能帮模型学会在不同背景尺度下识别但同时会让小目标被裁掉一部分。我会把训练超参里的mosaic概率从 1.0 降到 0.5避免太多目标被切碎。降这一点增强概率不会减少有效样本量因为 mosaic 增加的是背景多样性老鼠本身没有被复制所以放心调。第二是 HSV 抖动。不同厨房的灯光色温差异很大有的暖黄、有的冷白。hsv_h、hsv_s、hsv_v分别控制色调、饱和度和明度的抖动范围。我会把hsv_h从默认的 0.015 调到 0.02让模型对色温更鲁棒。注意不要调得过大hsv_h超过 0.05 后老鼠的毛色会失真模型会把黑色老鼠和灰色老鼠混成同一特征反而降低区分度。第三是翻转。fliplr0.5随机水平翻转不是所有场景都适用。如果老鼠在画面里的运动方向有规律例如都从下水道口向左跑水平翻转会制造反向样本增加学习难度。我一般先看一眼数据里标注框的运动轨迹分布再决定开不开。一个更实际的建议是在hyp.scratch.yaml里把这些增强参数写成一段注释标明改过哪几项、为什么改半年后回来调参时不用重新猜。3. 源码与训练把 YOLOv5 老鼠检测跑起来的最小命令链3.1 源码结构与环境配置不要急着跑 train.py拿到这套明厨亮灶的老鼠检测源码不要急着跑train.py先花十分钟确认版本和依赖。YOLOv5 的源码在 2022 年后进入维护模式但仍然是目标检测框架里最稳定的一个。核心入口就三个文件train.py训练入口加载数据、初始化模型、执行前向反向传播权重保存到runs/train/detect.py推理入口支持图片、视频、摄像头和 RTSP 流输出带框图像和标签文件models/yolov5s.yaml等模型结构配置定义网络的深度和宽度。YOLOv5 环境配置是我见过翻车率最高的环节。我用 conda 建一个干净的虚拟环境conda create -n rat-yolov5 python3.8 -y conda activate rat-yolov5 cd yolov5-master pip install -r requirements.txt如果你的机器上 CUDA 版本是 11.xrequirements.txt里的 torch 版本有可能被装成 CPU 版尤其是使用国内镜像源时经常发生。我习惯装完先跑一段验证命令python -c import torch; print(torch.__version__, torch.cuda.is_available())输出类似2.0.1cu118 True的这样一段才说明 GPU 可用。如果torch.cuda.is_available()返回False说明 torch 装成了 CPU 版需要按你的 CUDA 版本重新装对应 wheel。这一步不要跳过我见过好几个同事卡在这里半天训练速度肉眼可见地慢还以为是机器性能不行。3.2 最小训练命令从2018张图到可用权重数据就绪后训练的基本命令是python train.py \ --data rat.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 32 \ --epochs 200 \ --workers 8 \ --project runs/rat_train \ --name v1几个关键参数说明--data指向rat.yaml文件里定义train、val目录路径和nc类别数这里为 1以及names类别名列表--weights yolov5s.pt表示加载 COCO 预训练权重迁移学习。老鼠检测从 COCO 权重起步比从零训练收敛快不少这一步值得做--img 640是训练输入边长。2018 张图里老鼠大多是小目标可以考虑把img提到 800 或 960但显存占用明显上升--batch 32在单卡 24GB 显存下没问题如果你的卡只有 12GB降到 16 或把--img降到 640--epochs设 200但 YOLOv5 默认开启早停patience50也就是连续 50 轮验证集指标不涨会自动停不用怕跑到最后过拟合。再展开rat.yaml的内容path: /home/user/rat_dataset # 数据集根目录 train: images/train val: images/val nc: 1 names: 0: ratpath是数据根目录YOLOv5 会把path与train/val拼接成完整路径。注意nc: 1和names里的rat必须和标签文件里的class_id0对应一致。实际中经常有人把nc写成数据集文件夹里子目录的个数这是完全错误的nc是目标的类别总数不是文件夹个数。如果names: [rat]写成了names: [rat, mouse]模型会认为有两个类别而标签里的 0 指向rat训练 loss 会很高且模型学不出来。3.3 训练过程的三个关注点loss曲线、mAP与权重选择训练真正要盯的是三样东西box_loss、obj_loss和验证集 mAP。YOLOv5 每个 epoch 都会在终端输出指标单类检测时cls_loss恒为 0所以日志里看不到它是正常的。训练到 100 轮左右观察终端输出Epoch GPU_mem box_loss obj_loss cls_loss Instances Size 99/199 9.7G 0.021 0.231 0 640obj_loss从训练初期的 1.0 以上降到 0.2 左右说明模型开始学到目标的存在性。如果训练结束后验证集mAP0.5达到 0.9 以上这个模型基本可以试部署如果只有 0.6-0.7常见原因包括标注框不紧贴目标、数据里目标过小、或者某个摄像头视角占比过高先回去查数据不要盲目加训练轮次。加轮次只会让模型在训练集上记忆得更深对验证集的帮助非常有限。训练结束后runs/rat_train/v1/weights/下有两个权重best.pt和last.pt。best.pt是验证集指标最优权重last.pt是最后一个 epoch。多数情况下选best.pt。例外是如果best.pt的 mAP 高是过拟合某个摄像头的结果而last.pt泛化更均衡那用两个权重各跑一遍验证集对比每个摄像头上的漏检数再决定。这个对比过程不复杂但值得做它能让你明白选权重不是只看一个数字。3.4 显存不足时的降级路径用 8GB 显存的卡训练 640×640 输入默认 batch 32 大概率爆显存。降级路径我一般按这个顺序调python train.py \ --data rat.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --workers 4 \ --epochs 200 \ --cache-ram先调 batch 到 16再调--workers到 4。最后一个--cache-ram会把图片提前读进内存减少磁盘 IO 但增加内存占用内存不到 16GB 就不要开。如果 batch 16 还不行就把模型从yolov5s换成yolov5n这是 YOLOv5 最轻量的版本精度会降一些但训练稳。不要一上来就换yolov5m或更大模型老鼠检测目标小、单类别s级别已经够用。这里有个小技巧如果只是验证代码能不能跑通可以先--epochs 1跑一次全流程确认数据加载和 loss 计算没报错再正式开长训练。4. 推理部署与超参数调优明厨亮灶边缘设备上的模型稳定化4.1 推理命令与置信度、IoU 阈值的联动训练完用detect.py跑推理python detect.py \ --weights runs/rat_train/v1/weights/best.pt \ --source rtsp://your-ip:554/stream1 \ --img 640 \ --conf-thres 0.35 \ --iou-thres 0.45 \ --max-det 20 \ --device 0--conf-thres是置信度阈值老鼠检测推荐从 0.35 起步。调低到 0.25 能提高召回但误报会明显增多调到 0.5 以上则会漏掉动态模糊的老鼠。--iou-thres是 NMS 的 IoU 阈值默认 0.45。目标密集时相邻两个老鼠的框会被合并成一个导致只出一个框我会把--iou-thres降到 0.35 试试。实际场景里这两个阈值是联动关系一次只调一个不要同时大幅改动否则出了问题你没法判断是哪个参数引起的。明厨亮灶项目摄像头位多、预算紧推理资源分配是个实在的问题。单路视频流如果用 GPU 推理一张卡能跑 4-8 路取决于帧率如果只做抽帧检测比如每秒抽 2 帧CPU 也能扛住 1080p 输入。我自己的经验是先用 GPU 把模型跑稳定评估准确率达标后再考虑降级到 CPU 抽帧方案不要一开始就抠算力。不同场景下阈值选择可以参考这个表参数推荐值适用场景conf-thres0.25-0.30告警优先追求召回容忍误报conf-thres0.40-0.50误报敏感追求精确率iou-thres0.30-0.35画面中多只老鼠并存iou-thres0.45-0.50常规单目标画面4.2 把权重导出成 ONNX 与 TensorRT边缘设备上直接跑 PyTorch 推理性能通常不达标。Jetson Nano 这类设备跑 640×640 输入PyTorch 的推理延迟可能在 200ms 左右无法满足实时监控的需求。我一般先把best.pt转 ONNX再在设备上转成 TensorRT engine。YOLOv5 自带导出脚本python export.py \ --weights runs/rat_train/v1/weights/best.pt \ --include onnx engine \ --img 640 \ --batch 1导出后同目录下会有best.onnx和best.engine。注意engine 文件绑定生成它的 GPU 型号和 CUDA/TensorRT 版本换台不同型号的设备要重新生成不能直接拷贝。如果集成方只用 ONNX Runtime 或 OpenCV DNN只导出 ONNX 即可python export.py --weights best.pt --include onnx --opset 11 --simplify--opset指定 ONNX 算子集版本。目标运行时如果只支持低版本 ONNX比如嵌入式环境里的 onnxruntime 1.4.x就显式指定--opset 11。--simplify会对计算图做简化多数情况下能减少 10%-20% 的推理耗时但个别情况下会重排输出节点的名称集成前先用一张测试图确认输出结构。注意TensorRT 推理引擎与生成它的 GPU 型号强绑定跨硬件直接拷贝推理引擎文件会报错换设备就在该设备上重新导出。4.3 推理前后处理坐标缩放与实时视频流明厨亮灶摄像头多为 1080p 或更高模型输入是 640×640。最常用的方案是先用 OpenCV 读取帧缩放到 640 再送模型推理同时保留原始帧用于画框和回传平台。这里的关键是等比例缩放不要直接cv2.resize(frame, (640, 640))把图片拉伸变形目标会变扁检测框也会跟着偏移。我实际写出来的推理循环长这样import cv2 import torch model torch.hub.load(yolov5, custom, pathbest.pt, sourcelocal) cap cv2.VideoCapture(rtsp://your-ip:554/stream1) while True: ret, frame cap.read() if not ret: break # 等比例缩放保持原始比例避免目标挤压变形 h, w frame.shape[:2] scale 640 / max(h, w) new_w, new_h int(w * scale), int(h * scale) resized cv2.resize(frame, (new_w, new_h)) results model(resized) for det in results.xyxy[0].cpu().numpy(): x1, y1, x2, y2, conf, cls det if conf 0.35: continue # 还原到原始分辨率再画框 cv2.rectangle(frame, (int(x1/scale), int(y1/scale)), (int(x2/scale), int(y2/scale)), (0, 0, 255), 2) cv2.imshow(rat, frame) if cv2.waitKey(1) 0xFF ord(q): break这段代码有两个容易出错的地方。第一画框时坐标必须除以scale因为模型看到的是缩放后的图检测框坐标是缩放坐标系下的直接画到原图上会偏移我最初接手项目时吃过这个亏。第二torch.hub.load里sourcelocal必须写明否则在隔离网段里它会尝试联网拉取代码卡住很久甚至直接抛异常。如果你的部署环境完全没有外网建议改成直接用torch.load加载权重绕开 hub 机制。5. 明厨亮灶老鼠检测避坑实录从数据、训练到部署的五个典型雷区5.1 现象loss 正常但 val mAP 始终不过 0.7原因数据里存在大量重复场景帧。同一个摄像头的视频抽帧出来的连续帧几乎一样训练集和验证集混入了相似帧模型记忆了场景细节而不是老鼠特征。解决抽帧间隔改成 2 秒一帧而不是逐帧抽取按照 2.3 节的“摄像头加时间段”双重划分重建数据集。我曾经在一个项目里抽帧密度设为 5 帧每秒mAP 看着很高但实际部署到新摄像头上漏检严重这就是血泪经验。数据的时序相关性是明厨亮灶项目最容易踩的坑监控视频天然连续抽帧太密等于把同一只老鼠复制了几十份给模型看它在训练集上“背题”而不是“解题”。5.2 现象训练集 mAP 0.95换一个摄像头实测几乎全漏原因训练集里某一类视角占比过高模型被带偏到这个视角。比如 2018 张图里 1400 张来自吊顶俯拍视角模型学会的是“从上面看的老鼠”换个墙角侧拍的摄像头就失效。解决按摄像头划分数据确保训练集和验证集来自不同摄像头。如果数据里视角覆盖不均衡要补采数据或对少数视角做定向增强包括旋转、透视变换而不是简单复制少数类样本。复制样本不增加视角多样性只会让模型对同一批图过拟合。5.3 现象训练中途报 CUDA out of memory原因batch 或 img 参数超出显存入门最常见的问题。解决按 3.4 节的降级路径调整把 batch 降到 16、8直至能训练。如果换到yolov5n仍然不够检查是不是--workers开得太大导致内存膨胀。注意一个细节YOLOv5 默认会缓存图片到显存训练日志里的GPU_mem会在训练一段时间后才稳定不要在第一轮看到报错就认定是显存不足先让训练跑 10 个 step 再看。如果只跑 1 个 epoch 测试代码记得把--cache-ram关上否则测试和正式训练的显存表现会有差异。5.4 现象推理时同一只老鼠出现大量重叠框原因NMS 的--iou-thres设置过高多个相邻候选框没被抑制或者--conf-thres过低导致大量低置信度框被保留。解决先把--conf-thres提到 0.5 观察误报数量再单独调--iou-thres到 0.3-0.35。调参逻辑是固定一个动另一个用消融的方式看效果。如果调完还是一堆框检查是不是用了多尺度推理且没有在后处理合并结果YOLOv5 在不同版本里对多尺度输出的拼接处理有差异升级源码版本后行为可能变化。5.5 现象ONNX 输出解析错误shape 对不上原因YOLOv5 的 PyTorch 输出是结构化的多头输出导出 ONNX 后会自动压平成一个二维矩阵。如果不知道输出列的含义就会解析错。解决用 onnxruntime 打印输出 shape 和样例值。单类模型的输出 shape 是[1, 25200, 6]25200 来自 80×80、40×40、20×20 三张特征图各自锚框数的总和即 640016004008400每个位置 3 个 anchor 再乘 3得到 25200。最后一维 6 对应[x_center, y_center, width, height, objectness, class_score]多类时最后一维是5 nc。验证脚本长这样import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) dummy np.random.rand(1, 3, 640, 640).astype(np.float32) out sess.run(None, {sess.get_inputs()[0].name: dummy}) preds out[0] # shape: [1, 25200, 6] print(preds.shape) # 解析时把前四列乘上输入尺寸还原成像素坐标再做一次轻量NMS网络输出 25200 个框直接画到画面上会有大量冗余所以实际推理框架接入时我会在解析层再跑一个轻量 NMS去掉接近重复的框。这一步和后处理的--iou-thres不是一回事前者作用在模型原始输出上后者作用在最终候选框集合上两层都需要。6. 验证与进阶技巧单图回归、帧间去抖与告警去重6.1 最小验证先用一张图证明模型不是黑匣子部署前用一张训练过的图加一张完全陌生的图各跑一次python detect.py --weights best.pt --source test_rat.jpg --conf-thres 0.35输出在runs/detect/exp/。确认两点一是框的位置大致贴合老鼠轮廓二是置信度数值与目标完整度匹配完整目标应高于部分遮挡目标。如果框明显偏了先查输入分辨率问题再查标签坐标是否转换错误。不要一上来就直接接 RTSP 流先单图验证再视频验证能省很多排查时间。6.2 帧间去抖同一只老鼠别触发几十条告警监控视频里同一只老鼠在画面中停留 3 秒按 25fps 推理逐帧处理会产生 75 条告警日志。我在后处理环节用一个滑动时间窗口做去重相邻帧中 IoU 大于阈值的检测框视为同一目标只上报一次并记录首次出现时间。可以去部署的时候参考这段class Track: def __init__(self, bbox, ts): self.bbox bbox self.last_ts ts self.alarm_sent False def check_alarm(tracks, dets, thr0.3, expire2.0): for det in dets: matched False for t in tracks: if iou(t.bbox, det) thr: t.bbox det t.last_ts time.time() if not t.alarm_sent: send_alarm() t.alarm_sent True matched True break if not matched: tracks.append(Track(det, time.time())) tracks[:] [t for t in tracks if time.time() - t.last_ts expire]这段代码的核心是按 IoU 匹配轨迹expire秒内没有匹配就删除轨迹。注意真实场景里老鼠经常会被橱柜遮挡再出现中断超过 2 秒就会重新触发告警可以把expire放宽到 5-8 秒让模型在遮挡场景下仍有二次确认能力。thr0.3表示两帧检测框的 IoU 超过 0.3 就视为同一目标这个值在老鼠快速移动时不要设得太高否则两帧之间位移大导致匹配不上。6.3 告警阈值取值统计置信度分布再定不拍脑袋我自己的习惯是拿到新场景先不设固定阈值跑 12 小时推理统计模型输出置信度的分布再选一个不让背景误报进入的稳定区间作为阈值。比如统计发现 0.4 以上的检测绝大多数是真老鼠0.25 到 0.4 之间是模糊帧和阴影误报那就以 0.4 为告警线0.25 到 0.4 记为“疑似”只进日志不推送。这个统计方法比任何拍脑袋阈值都可靠。明厨亮灶里漏检一次老鼠的代价远高于误报一次告警策略要偏向召回而不是精确率。这是我最想分享的习惯模型能不能上线不能只看训练集的 mAP要在真实视频流上跑满一个昼夜再下结论看不同时段的光线变化、厨房蒸汽影响、人员走动干扰把这些问题都记录下来再回去微调阈值和增强参数。希望帮到你。本文还有配套的精品资源点击获取
返回列表