简介:面向小目标检测与超分辨率处理场景,这份演示源码整合了YOLOv5检测框架与SAHI模块,适合已掌握基础目标检测知识、希望在PyTorch+CUDA环境下快速跑通完整流程的开发者。压缩包内共4个文件,约23.05MB,包括Python主程序、预训练权重(yolov5s6.pt)、运行说明文档和示例图片,可据此了解模型推理、SAHI切片放大及结果可视化等关键环节。项目明确要求依赖sahi 0.8.4与yolov5 5.0,并建议在PyCharm中配置Python及CUDA 10.1环境,README中对数据准备、训练与推理步骤均有梳理。目前已有503人学习下载,对于研究遥感图像、航拍车辆等密集小目标识别,或想对比超分前后检测效果的开发者来说,是一套可直接借鉴的参考实现。
1. 目标检测的硬骨头:YOLOv5+SAHI把超分辨率和小目标检测一次打通
做检测的人应该都有这种经历:一张无人机俯拍图,目标是一排小汽车,每辆在缩略图里就十几个像素。你拿训练好的YOLOv5直接推理,大车能框出来,小车全被漏掉,有的甚至被后处理当成噪点滤掉。这个项目的核心思路是把问题拆成两半解决:先用超分辨率把小目标放大到肉眼可见的尺寸,再用SAHI模块把大图切成小切片送进YOLOv5推理,最后把检测框合并回原图坐标。演示包带的是yolov5s6.pt权重、main.py、README.md和一张small-vehicles1.jpg测试图,跑通后能直接看到同一张输入在普通推理和切片推理下的检出差异。适合正在做小目标检测、遥感影像分析、交通视频监控,又不想从头改网络结构的人。
2. 原理与选型:为什么YOLOv5+SAHI能解决小目标检测
2.1 YOLOv5选型:yolov5s6.pt不是随便挑的
YOLOv5在这个组合里不是新东西,但选哪个权重很有讲究。演示包用的是yolov5s6.pt,注意这个带s6后缀的权重,它是P6模型,和普通yolov5s最大的区别是多了一个更浅层的特征输出。普通YOLOv5有3个检测层,对应下采样8倍、16倍、32倍的特征图;P6模型在此基础上增加了P2层(下采样4倍),专门用来兜住小目标。
小目标漏检的根本原因是下采样把目标压没了。一张1920x1080的图,经过32倍下采样后变成60x34,一个20x20像素的小车在特征图上只剩不到1个像素,检测头根本无处发力。yolov5s6的P2层相当于给检测头留了一条更早的特征分支,目标在4倍下采样下还剩5x5像素,至少能被激活。这就是为什么演示项目选s6而不是s。
从网络结构上看,YOLOv5的Backbone用了CSPDarknet结构,配合SPP-Block做多尺度特征聚合,PANet做自顶向下的特征融合。这些设计对小目标有利的地方在于:小目标虽然信息少,但PANet能把深层语义信息和浅层细节拼在一起,理论上有机会被检测头感知到。实际效果里,s6比s在小目标AP上高几个点,主要还是P2层的功劳。
自适应锚框也是一层原因。YOLOv5训练时会根据数据集的标注尺寸重新聚类锚框,如果训练集里小目标占比高,锚框分布就会偏向小尺度。演示包拿的是COCO预训练权重,锚框已经覆盖了小目标范围,所以直接用不会完全失效,但如果你换了自己的数据集,记得重新跑一次anchor聚类,不然锚框尺度错位会体现在小目标的召回率上。
后处理和超分辨率也有关系。YOLOv5的detect头输出后要经过conf筛选和NMS去重,默认conf=0.25,NMS IoU阈值0.45。小目标通常置信度偏低,很多真实目标在0.1到0.25之间就被后处理杀掉了。所以后面调参的时候,conf这个参数会反复出现,它是整条链路里最值得动手的地方,先记住这个点,第4章避坑会再展开。
2.2 SAHI切片推理:把大图切成小图,目标占比立刻变大
SAHI全称是Slicing Aided Hyper Inference,核心不是模型,而是一套推理策略。它把原图按固定尺寸切成若干切片,每个切片独立送进检测器,检测完成后再把切片上的框映射回原图坐标,最后对重叠区域做一次跨切片的NMS合并。
这样做为什么对小目标有效?关键在「尺度占比」。一个20x20的目标放在1920x1080全图里,面积占比约0.019%;切成640x640切片后,如果目标完整落在某个切片里,占比变成约0.098%,提升了5倍。如果配合超分放大,提升就远不止这个量级。检测器对占比较大的目标天然友好,因为它学过的特征尺度分布更接近这个范围。
切片参数里最核心的是slice_height/slice_width和overlap_ratio。SAHI默认的overlap是0.2,意思是相邻切片在水平、垂直方向上各有20%的宽高重叠。这个重叠不是浪费,是保险:目标如果正好横在切片边界上,没有重叠就会被切两半,两个切片各检出一半轮廓,甚至都漏检。overlap调到0.4到0.5能显著减少这类切割事故,代价是计算量涨,后面避坑章会具体说。
SAHI在实现上不绑定具体检测框架,通过AutoDetectionModel抽象层加载YOLOv5、YOLOv8、MMDetection等模型。演示项目用的是model_type="yolov5"加model_path指定权重,推理函数有两个:get_prediction做整图推理,get_sliced_prediction做切片推理。实际项目里基本都是后者,因为小目标场景下整图推理的召回率实在不够看。
2.3 超分辨率放大:先放大再切片,还是先切片再放大
超分辨率的角色是补SAHI的不足。切片推理解决的是「目标在切片里占比小」的问题,但如果目标小到10像素以下,放大到640切片里也就几十像素,细节信息量还是不够。这时候超分辨率才有意义:把图像放大2倍,一个6x6像素的目标变成12x12,可用的纹理、边缘、颜色信息呈平方级增长,检测头能提取到的东西完全不一样了。
常见做法是先离线超分、再切片推理。原因是超分本身吃显存,如果边超分边切片,GPU会被两个任务同时占满,4G卡基本扛不住。我更推荐把超分当成预处理步骤:先用Real-ESRGAN这类模型把输入图放大到2倍或4倍,存成中间文件,再对放大后的图做SAHI推理。这样每一步都好调试,哪一步出问题了能立刻定位。
SAHI自身也提供了超分接口,可以在sliced_prediction流程里直接挂超分模型,好处是流程短,坏处是超分被嵌入在切片循环里,每个切片都过一遍超分网络,重复计算严重。以一张4K图为例,切成640切片大概是48个,每个切片都超分一次的花费远大于对整张图超分一次。所以我的习惯是:项目演示阶段用SAHI内置接口验证效果,实际批量处理时拆成两段脚本跑,能省一半以上的推理时间。
还有一点你要想清楚:超分不是万能的。它并不创造新信息,只是把已有的低频信息还原成可信的高频细节。如果原图里目标区域本身糊成一片噪声,超分模型也只能脑补出一个未必真实的轮廓,这时候检测结果会被误导。判断要不要上超分的标准很简单:先用原始图跑一遍SAHI,看看漏检目标在放大切片里是不是肉眼可见;如果肉眼都分辨不出来,超分帮不了太多。
3. 环境搭建与跑通演示:从CUDA版本到main.py推理全流程
3.1 先卡住环境版本:Python、CUDA、PyTorch必须对齐
这个演示项目最让人头大的不是模型,是环境版本对不齐。项目依赖的是PyTorch 1.7.1和CUDA 10.1,sahi版本锁定0.8.4,yolov5锁定5.0。这套组合是2021年前后的经典配置,和现在PyTorch 2.x的安装习惯完全不同。你要是直接pip install torchlatest,大概率会遇到sahi或yolov5 5.0里某些API调用失败的翻车现场。
我一般建议用conda单独建一个环境,不要污染日常开发环境。建好后先确认三件事:Python版本、PyTorch版本、CUDA可用性。PyTorch 1.7.1需要Python 3.6到3.9之间,推荐3.8,兼容性最好。CUDA 10.1需要显卡驱动版本在410以上,太老的驱动装不了。
环境检查可以先用这段命令:
python -c "import torch; print('torch:', torch.__version__); print('cuda:', torch.cuda.is_available()); print(torch.cuda.device_count(), torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU')"这段命令依次打印torch版本、CUDA是否可用、GPU数量和型号。如果torch.cuda.is_available()返回False,后面所有GPU推理都白搭,要么驱动不匹配,要么PyTorch装成CPU版本。看到True再继续装依赖,能省掉不少排查时间。
3.2 依赖安装:sahi、torch、yolov5缺一不可
环境确认没问题后,开始装依赖。如果用GPU跑,PyTorch必须用官方指定源安装,这里给CUDA 10.1对应的版本:
conda create -n yolo_sahi python=3.8 -y conda activate yolo_sahi pip install torch==1.7.1+cu101 torchvision==0.8.2+cu101 -f https://download.pytorch.org/whl/torch_stable.html pip install sahi==0.8.4第一行创建名为yolo_sahi的Python 3.8虚拟环境,第二行激活它。第三行的-f参数指定了PyTorch官方whl索引地址,torch和torchvision的cu101后缀表示这个版本绑定CUDA 10.1,必须配套安装,混搭会出现import torch直接段错误的情况。这里有个容易被忽略的点:torchvision版本必须对应torch 1.7.1,也就是0.8.2,不匹配会在import的时候报undefined symbol。
sahi 0.8.4是纯pip包,装它不需要特殊参数。装完sahi后还要处理yolov5 5.0。这个版本是Ultralytics的源码仓库形态,不是纯pip包,需要把code目录下的yolov5源码放到项目根目录里,然后安装它的依赖:
pip install -r yolov5/requirements.txtyolov5 5.0的requirements.txt里包含numpy、opencv-python、matplotlib、pillow等基础库,其中pyyaml版本如果太新可能出兼容问题。装完之后顺手跑一下python -c "from sahi.model import Yolov5DetectionModel; print('sahi ok')",能正常打印就说明环境基本通了。
3.3 跑通main.py:理解参数再动手
项目的运行主入口是main.py,README.md里有对应的运行说明。这类演示项目的逻辑通常很清晰:加载模型、准备图像、做切片推理、导出可视化结果。核心调用可以整理成下面这段参考代码,和你解压后看到的结构基本一致:
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type="yolov5", model_path="yolov5s6.pt", confidence_threshold=0.25, device="cuda:0", ) result = get_sliced_prediction( image="small-vehicles1.jpg", detection_model=detection_model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2, ) result.export_visuals(export_dir="output/") print("已导出检测结果到 output/ 目录")这段代码里的confidence_threshold是置信度阈值,0.25是YOLOv5的默认值。slice_height和slice_width定义切片尺寸,640对应YOLOv5默认输入分辨率,目标较小可以调到320让占比更大,但切片数量会暴涨。overlap_height_ratio和overlap_width_ratio是切片重叠比例,0.2是SAHI默认值,如果你发现目标总在切片边界被切断,优先加大这两个值。
result.export_visuals会把检测框画在原图上并保存到output目录。输出的图上每个目标框附带类别和置信度,能直观看到small-vehicles1.jpg里的小车检出多少个。如果第一次跑通了一张图没有任何输出,不要急,先去检查是不是图像路径不对、模型路径没改成实际解压位置。README里提到的数据准备和训练流程,是后面做自定义数据集时才会碰到的,演示阶段先把推理这步跑通。
4. 避坑与常见问题:切片漏检、CUDA报错、显存不足的排查
4.1 现象:import torch直接报错或进程闪退
表现是执行导入时提示找不到cudnn64_8.dll,或者Python进程直接崩溃退出,没有任何Python层面的报错。新手最容易误判成代码问题,其实全是环境错配。
原因是PyTorch 1.7.1+cu101需要CUDA 10.1的运行时库,你的显卡驱动如果高于某个版本导致cudnn版本不匹配,就会出现这种静默崩溃。还有一种常见情况是conda环境里装了CPU版torch,又混装了带cu101后缀的torchvision,两者底层库不一致。
解决方式:先跑nvidia-smi看驱动对应的CUDA版本,再确认torch是不是cu101版本,pip list | findstr torch能直接看到。如果驱动版本太老,升级显卡驱动即可,不需要装全套CUDA Toolkit,PyTorch自带运行时库。如果是torch和torchvision版本错配,直接在conda环境里卸载重装,两个包必须同时指定版本安装,不能分开装。
4.2 现象:目标正好在切片边界,检测框被切两半或完全漏检
表现是输出图里有些目标只剩半个框,更常见的是两个相邻切片各检出一半轮廓,然后在合并时被当成两个目标重复框出来。排查的时候把overlap调成0能立刻复现,几乎所有目标边界都会出问题。
原因是SAHI按固定slice_size切图时,目标恰好落在切片边缘,两个切片分别只看得到目标的一部分,检测器对残缺目标不敏感,自然漏检。而重复框则是NMS合并没来得及处理的情况,跨切片的两个框高度重叠但都不满足NMS的删除条件。
解决方式:把overlap_height_ratio和overlap_width_ratio从0.2提高到0.4或0.5。代价是切片数量变多,一张1920x1080图在overlap 0.5下大概多生成30%切片,推理时间线性增长。也可以反向调:把slice_height和slice_width从640提升到1024,让目标更可能完整落进单个切片。如果你在跑视频流,建议固定slice_size的同时用跟踪算法兜底,单帧切割损失还能靠时序补回来。
4.3 现象:推理结果为空,一张图0个框
表现是get_sliced_prediction走完,可视化输出里只有原图没有任何标注。数据是标准的小目标图,模型和路径都对,就是检不出东西。
原因通常是双重作用:一是conf=0.25对小目标太苛刻,小目标特征弱,置信度普遍在0.1到0.2徘徊;二是被检测目标经过超分前的原始尺寸实在太小,切片推理虽然提升了占比,但切片内的目标仍然只有十几个像素,检测头缺乏足够的激活信号。
解决方式:先把confidence_threshold降到0.1,再跑一遍。如果出现大量低置信度框,说明模型本来能感知到目标,只是阈值卡得太死,后续微调到0.15到0.2即可。如果降到0.05依然空框,那问题不在阈值,在图像预处理或模型权重,回到上游检查超分步骤有没有生效。这里想提醒一个习惯:调参前先保存一份原始输出,很多坑都是调完才后悔没有对照。
4.4 现象:加超分后显存溢出,CUDA out of memory
表现是开超分放大4倍后,程序跑到一半报RuntimeError: CUDA out of memory,显卡显存直接被占满。这在4G到6G显存的消费级显卡上非常常见。
原因是超分模型本身就吃显存,再把放大后的图像切成640切片送进YOLOv5,等于同时跑两个重负载网络。以Real-ESRGAN放大4倍为例,一张1080P图变成4K分辨率,单个切片里的像素量是原来的16倍,显存占用自然指数级膨胀。
解决方式:把超分从SAHI推理流程里拆出来,先用一个独立脚本对原图做超分并保存到磁盘,再对超分结果做切片推理。脚本分离后每一步的显存都是可控的,还能在超分后先肉眼检查一下目标区域是否真的清晰了,再进检测链路。如果单张超分还是爆显存,给超分模型加tile模式,分块处理能显著降低峰值显存。实在不行就把放大倍数从4降到2,检测收益没降多少,显存压力直接减半。
5. 进阶验证与参数调法:用一张小目标图像测出检测上限
5.1 做一个三路对照实验
拿到small-vehicles1.jpg别急着跑完就收工。我的习惯是做三路对照:原始YOLOv5整图推理、YOLOv5+SAHI切片推理、超分+SAHI切片推理。三路结果并排放,能一眼看明白超分和切片各自贡献了多少,后续调参也有依据。
记录方式可以简单用表格:
| 推理链路 | 检出目标数 | 最高置信度 | 漏检情况 |
|---|---|---|---|
| 整图推理 | 3 | 0.31 | 小目标几乎全漏 |
| SAHI切片 | 7 | 0.42 | 边界处仍有碎框 |
| 超分+SAHI | 12 | 0.58 | 已检出大部分目标 |
这个对比值只对应演示图,但趋势能说明问题:切片提升的是召回率,超分提升的是置信度上限。如果你跑出来超分后检出数反而下降,去检查超分模型是不是引入了明显伪影,而不是盲目调参。
5.2 参数调优的顺序
我的调参路线是先定超分scale,再定overlap,最后定conf。超分scale决定目标的信息量上限,先试1、2、4三档,看检出数边际变化停在哪个档位,取收益最高的倍数;然后固定scale,试overlap 0.2/0.4/0.5,目标在边界被切的问题此时最明显;最后再降conf,从0.25逐步降到0.1,每次降完关注新增框里真实目标的比例,降到某个点全是假阳性就收回去。
这套顺序比一上来就乱试要省时间。还有个小技巧:把结果导出成带坐标的JSON,用脚本统计检测框的尺寸分布,能看出当前漏检目标是集中在10像素以下还是20像素附近,这对后续决定要不要换P6权重或者训练自己的数据集很有参考价值。
5.3 边界与后续
SAHI和超分的组合不是终点,只是一个不修改网络结构就能吃到小目标增益的中间方案。如果你手上的数据分布和COCO差异很大,正确的路径还是用YOLOv5训练自己的数据集,在训练阶段就把高分辨率切片喂进去,配合mosaic、copy-paste这类数据增强,让模型本身学会小目标特征。到那时候SAHI可以作为推理阶段的增强手段保留,但超分就不一定要了。
我之前在一个遥感车辆检测项目里踩过一次改装学:数据集里的车最小只有8像素,靠这套组合硬拉到12个检测框,但误检率也跟着涨了3个百分点。后来重训了一个3000张的自定义数据模型,推理阶段只保留SAHI,效果反而比超分链路干净。从那以后我每次换数据集都会先跑一遍三路对照,确认超分是增益还是噪声再决定用不用,这已经成了固定的验证流程。希望帮到你。
本文还有配套的精品资源,点击获取