简介这是一份面向目标检测开发者的演示源码聚焦YOLOv5框架与SAHI模块的结合通过超分辨率技术改善小目标细节进而提升检测精度。资源包共4个文件、压缩后约23.05MB包含Python入口脚本main.py、YOLOv5s预训练权重、Markdown格式运行说明及示例车辆图像代码与模型分目录存放覆盖环境配置、推理调用到结果验证的完整流程。已有503人学习/下载。用户可参照文档配置sahi 0.8.4与yolov5 5.0依赖环境直接利用预训练权重对小型车辆图片进行超分与小目标检测演示并借鉴该结构将SAHI接入自有YOLOv5项目省去从零搭建的繁琐过程。该方案对航拍、遥感等小目标密集场景的检测任务同样具有迁移参考价值也是理解超分技术与检测器协作机制的入门样例。1. 这个压缩包在做什么yolov5SAHI超分辨率组合解决小目标检测难题无人机俯拍一张2000万像素的航拍图停车场里一辆车在画面里只有十几个像素yolov5正常跑一遍漏掉三分之一。这不是模型的错是32倍下采样之后目标在特征图上只剩不到一个像素。SAHI模块的切片推理配合超分辨率重建就是专门解决这类小目标检测漏检的先把图放大再切成小块逐块检测最后把结果拼回原图。这套演示源码把这个过程封装成了可以直接跑的命令和脚本适合手上有遥感、航拍、密集小目标任务的工程师按运行说明从环境配置到出结果一次就能复现出比纯yolov5更好的召回率。下面按源码包通常的组织方式带你从环境搭建一路走到跑通命令再把切片大小、重叠率、超分倍数这几个关键参数的坑逐个讲清楚。2. 为什么SAHI能提升小目标召回切片推理补齐下采样丢掉的细节2.1 小目标检测翻车的根源下采样把目标“下”没了yolov5的backbone沿用CSPDarknet结构输入图像每经过一个阶段就降采样一次最终参与预测的特征图通常是输入尺寸的1/32。拿416x416的输入来说最终特征图才13x13一个8x8像素的小目标映射上去只占0.25个像素模型在特征层面基本看不见它。yolov5虽然专门设计了P3层80x80特征图去检测小目标但这一层同样经过多次卷积和下采样8x8的目标到P3上只剩约1个像素边界框回归全靠这么点信息漏检率高是结构性的不是调参能救回来的。实际场景里这个阈值大致在16像素目标边长小于16像素时直接跑yolov5的召回率明显下滑航拍图、密集人群、病理切片尤其严重。还有一个容易被忽视的因素是anchor。yolov5默认的anchor是从COCO聚类出来的对小目标只分配了少量先验框如果训练时没有跑过autoanchor重新聚类小目标很难匹配到合适的anchor。下采样损失加anchor失配加训练分布偏差三重因素叠加才让纯yolov5在小目标上表现这么差。SAHI的思路非常直白既然目标在整张大图里是“小目标”那把大图切成512x512的小块目标在切片里就不再是小目标P3层上的特征占比随之变大检测器自然能正常识别。它的本质是绕开下采样而不是对抗下采样用计算量换召回率。把一个1000x1000的图切成4张500x500目标如果原来是10x10在切片里仍然是10x10但特征图上占的格子数从0.3变成0.6如果先做2倍超分10x10变成20x20特征图上占1.6个格子这就像模像样了。2.2 SAHI切片推理的工作方式固定尺寸、重叠切片、NMS聚合SAHISlicing Aided Hyper Inference的核心流程分三步。第一步把输入图按slice_size切成若干个互相重叠的小块重叠比例由overlap_ratio控制。第二步把每一块独立送进yolov5检测得到各自的检测框、类别和置信度。第三步把所有切片的检测框坐标映射回原图坐标系用后处理算法把重复框合并输出最终结果。这三个步骤对应代码里的get_sliced_prediction函数第4章会给出最小可跑版本。这里有两个关键参数。slice_size决定切多碎常用挡位是320、512、640。目标整体偏小而且显存足够用320效果最好因为目标在切片里占比更大但切片数量多推理时间最长目标是几十像素级别512最均衡目标接近100像素640就够再大跟直接检测没什么区别。overlap_ratio决定切片与切片之间的重叠宽度默认0.2意思是每张切片右边和下边有20%的区域和相邻切片重合。重叠的作用是防止目标恰好落在切片边界被一刀切开。假设目标宽度是80像素slice_size512overlap_ratio0.2重叠带就是102像素能完整包住目标。但如果目标达到200像素这个重叠带就包不住目标会被切成两半检测框缺一块。所以当确认目标尺寸较大时overlap_ratio要提到0.25甚至0.3代价是同一目标出现在更多切片里后处理要合并的重复框变多。后处理有一个常被忽略的差异SAHI支持NMS和NMW两种合并方式还有IoU与IOS两种匹配度量。NMS保留置信度最高的框抑制与之重叠的框NMW则对重叠框的坐标和置信度做加权平均输出一个“平均框”。在小目标场景里我更推荐NMS加IOS组合。IOS全称Intersection over Smaller交集面积除以两个框里较小的面积对小目标更宽容小目标框中心差几个像素IoU可能从0.6掉到0.3但IOS还能保持0.7以上重复框更容易被正确合并。有人会问与其切片不如直接用大量小目标样本训练模型。这个说法对了一半。训练时用小目标增强手段确实能提升模型对小目标的敏感度但推理时输入仍是整张大图下采样损失并没有被绕开。SAHI是从推理侧改变输入尺寸和训练侧是互补关系演示源码里通常两条路都保留但核心路径走的是SAHI。2.3 超分辨率放在哪一步先超分还是先切片超分辨率重建在这条流水线里的作用是把输入图放大。放大这个动作放在切片前还是切片后差别非常大。先对整张大图做超分再走SAHI切片是常见做法先切片再逐块超分要超分的切片数量太多计算量完全不可接受我曾见有人这样写过跑一次就放弃了。先超分再切片的问题在于计算开销的传导2倍超分把图像面积放大4倍SAHI的切片数量也放大4倍推理总耗时接近原来的4倍3倍超分就是9倍基本告别实时。所以要不要加超分必须先看目标尺寸分布。我一般按这个经验判断目标集中在8到16像素切片带来的收益已经有限因为目标放大后在切片里也只有几十像素此时超分2倍是划算的目标在16到32像素区间优先只做SAHI切片超分放大的噪声会被yolov5误认成目标误检增加目标大于32像素这已经不算小目标直接跑yolov5即可。下表可以快速定位自己的场景目标边长推荐做法补充说明小于8像素超分2倍后再切片或直接评估该场景可行性切片也救不回来先放大再谈检测8到16像素超分2倍加SAHI切片性价比最高的组合16到32像素只用SAHI切片加超分收益低、耗时翻倍大于32像素直接yolov5传统目标检测足以处理超分模型的选择也看退化假设。Real-ESRGAN针对的是真实退化比如模糊、噪声、压缩伪影用在航拍图上效果还行EDSR这类模型假设是双三次降采样超分结果更平滑。检测任务里平滑的超分通常比锐化的更好用因为锐化带来的伪细节会干扰分类器。这个选择有点玄学但对误检率影响很大第5章会展开讲。3. 不装好环境等于白干conda创建yolov5环境与SAHI安装3.1 用conda创建yolov5运行环境这套演示源码不管内部怎么组织底层都依赖yolov5的框架依赖项很多直接拿系统Python装会把环境搅乱。我推荐的流程是用conda隔离一个叫yolov5的环境Python固定3.9PyTorch版本按显卡驱动选。命令如下conda create -n yolov5 python3.9 -y conda activate yolov5 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118第一条命令创建独立环境-y跳过交互确认。第二条激活它之后所有安装都进这个环境不污染系统Python。第三条安装PyTorchcu118表示CUDA 11.8对应的预编译轮子如果你的机器是CUDA 12.x把cu118换成cu121或cu124。没有NVIDIA显卡就直接pip install torch torchvision走CPU版本能跑但速度差一个数量级。装完之后验证一下GPU是否可用python -c import torch; print(torch.__version__, torch.cuda.is_available())输出True说明GPU可用输出False要么驱动不对要么后续代码里device参数统一改成cpu。yolov5环境配置里最常见的翻车点不是装不上而是装了多个Python版本导致链接库冲突所以conda环境名的意义就在这里。装完PyTorch再装yolov5的其余依赖。压缩包如果自带yolov5源码通常会有requirements.txtcd yolov5 pip install -r requirements.txt pip install opencv-python这里提醒一句requirements.txt里对numpy和opencv版本有约束装完不要手贱升级numpy到最新版。yolov5 6.0系列对numpy 1.x支持最好numpy 2.x出来之后很多旧权重加载直接报错。运行说明里如果标注了Python版本或PyTorch版本尽量按标注来网上看到的yolov5报错案例最后大多能归到版本不一致而不是代码本身的问题。3.2 安装SAHI并确认与yolov5版本兼容SAHI是独立于yolov5的Python包通过pip直接安装pip install sahi安装后在Python里验证导入from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction print(SAHI import OK)导入成功说明SAHI装好了。但SAHI和yolov5之间有一层兼容性关系SAHI对yolov5 6.x的配合最成熟如果你的源码里用的权重是用yolov5 7.0以上训练导出的SAHI加载时可能报键值不匹配。遇到这种情况先看运行说明锁定的yolov5版本不要拿新源码直接替换旧环境。SAHI安装本身很小但有一个隐蔽依赖问题它底层支持多种检测框架如果环境里没装mmdet或detectron2导入某些模型类型时会报错。演示源码只用了yolov5所以只需要yolov5的环境依赖不需要额外装mmdet。判断方法很简单导入报错信息里会提示缺哪个模块按提示补装不要一上来就pip install mmdet全套那会拖进来大量用不上的编译依赖。SAHI通过AutoDetectionModel加载yolov5权重加载代码长这样from sahi import AutoDetectionModel detection_model AutoDetectionModel( nameyolov5, model_pathweights/yolov5s.pt, conf_threshold0.25, devicecuda, )参数说明name固定为yolov5告诉SAHI用yolov5的推理逻辑解析模型model_path指向权重文件用相对路径时要确保当前工作目录是项目根目录conf_threshold是置信度阈值SAHI在切片推理阶段就用它过滤低置信度框device指定cuda或cpu。这个detection_model对象会被后续的get_sliced_prediction反复使用所以在脚本外层创建一次就行不要在循环里反复加载否则每张图都要等模型重新初始化。3.3 源码包目录结构权重、超分模型和脚本如何摆放这类演示源码包通常有固定的目录组织我拿到压缩包后第一件事是按下表核对一遍文件位置project/ ├── yolov5/ # yolov5源码 │ ├── detect.py │ └── requirements.txt ├── weights/ │ ├── yolov5s.pt # 目标检测权重 │ └── realesrgan_x2.pth # 超分辨率权重 ├── data/ │ └── test_images/ # 测试图片 ├── scripts/ │ ├── detect_sahi.py # SAHI切片检测脚本 │ └── sr_detect.py # 超分加检测串联脚本 ├── runs/ # 推理输出目录 └── README.md # 运行说明weights目录放两个权重一个是yolov5的预训练模型一个是超分模型。如果压缩包里没有权重文件运行说明里一般会给出获取方式这很常见因为权重体积大通常单独提供。你拿到压缩包后第一件事不是跑代码而是确认权重文件是否就位、路径是否和脚本里写的一致。最容易出问题的是路径不区分代码里写weights/yolov5s.pt你放到weights/yolov5/yolov5s.pt启动就报找不到文件。data目录放测试图最好放3到5张不同场景的图不要只放一张否则验证不出切片和超分的差异。runs目录是推理输出目录每次运行按project和name参数区分结果。README是整个压缩包的核心它应该写明环境依赖、两个脚本的启动顺序、权重文件的获取方式。如果压缩包里只有运行说明.txt没有README按同样的信息点核对。源码包结构可以不一样但权重文件有无是决定能否跑通的第一关键其次才是环境最后才是代码。4. 跑通演示源码yolov5后处理、SAHI切片参数与超分辨率串起来4.1 不接SAHI的yolov5基线命令在跑整套流水线之前先跑一次yolov5官方detect.py确认权重和环境是好的也留一个对比基线。命令cd yolov5 python detect.py --weights ../weights/yolov5s.pt \ --source ../data/test_images/ \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --project ../runs \ --name baseline参数说明--weights指定权重路径--source可以是单张图片、一个目录或一段视频--img是推理尺寸yolov5会把输入等比缩放到这个尺度640是最常用配置--conf-thres是置信度阈值0.25是官方默认值适合基线--iou-thres是NMS的IoU阈值0.45在目标不太密集的场景够用--project和--name组合决定输出目录跑完在runs/baseline里能看到带标注的图。这两个阈值就是yolov5超参数里最影响结果的两个。conf_thres调低到0.1框变多但误检也变多调高到0.5误检少但小目标可能被过滤。iou_thres调低会让重叠框更容易被抑制调高会保留更多重叠框密集场景下要调到0.5以上才能避免相邻目标被合并成一个框。在SAHI场景里因为同一个目标会被多个切片重复检测conf_thres可以稍微提高到0.3把重复框交给后处理去合并而不是在检测阶段就放进太多低置信度框。4.2 用SAHI做切片推理核心参数和最小代码跑通基线后把yolov5嵌入SAHI最小可跑代码from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel( nameyolov5, model_pathweights/yolov5s.pt, conf_threshold0.3, devicecuda, ) result get_sliced_prediction( imagedata/test_images/aerial.jpg, detection_modeldetection_model, slice_size512, overlap_ratio0.2, postprocess_typeNMS, match_metricIOS, match_threshold0.5, verbose1, ) result.export_visuals(export_dirruns/sahi_result/)逻辑说明get_sliced_prediction把aerial.jpg按512x512切成带重叠的切片每张独立通过yolov5检测再把所有检测框映射回原图坐标用NMS合并重复框返回result对象。result.export_visuals把标注好的图导出到runs/sahi_result方便肉眼确认。参数说明slice_size512是切片边长1080p级别的航拍图用512通常效果最好目标更小且显存足够可以降到320目标占比变大但推理时间可能翻倍。overlap_ratio0.2是默认重叠比例目标接近512像素时提到0.25。conf_threshold0.3比基线略高原因上面说过。match_metricIOS是切片合并的匹配方式match_threshold0.5表示两个切片检测出的框交集占较小框比例超过50%就合并。这个组合是SAHI文档里针对小目标场景的推荐配置我实测比默认的IoU加0.3稳定得多。verbose1会打印出切片数量和每张切片检测到的目标数排查时很有用。4.3 超分辨率重建模块的接入先超分再切片的完整流程超分辨率重建在演示源码里通常是单独一个脚本先把图放大再走SAHI。基于RealESRGAN的2倍超分加SAHI切片完整代码import torch import numpy as np from PIL import Image from realesrgan import RealESRGANer from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction # 1. 加载超分模型 sr_model RealESRGANer( scale2, model_pathweights/realesrgan_x2.pth, tile256, tile_pad10, halftorch.cuda.is_available(), ) # 2. 对原图做2倍超分辨率重建 img Image.open(data/test_images/aerial.jpg).convert(RGB) img_np np.array(img) sr_img, _ sr_model.enhance(img_np, outscale2) # 3. 超分结果送入SAHI切片检测 detection_model AutoDetectionModel( nameyolov5, model_pathweights/yolov5s.pt, conf_threshold0.25, devicecuda, ) result get_sliced_prediction( imagesr_img, detection_modeldetection_model, slice_size512, overlap_ratio0.2, postprocess_typeNMS, match_metricIOS, match_threshold0.5, ) result.export_visuals(export_dirruns/sr_sahi_result/)逻辑说明第1步加载RealESRGAN的2倍超分权重tile256表示超分模型内部按256x256的块处理图像防止整张大图一次性进网络导致显存溢出。第2步用enhance对输入图做2倍放大outscale2是最终放大倍数。第3步把放大后的图像数组直接交给SAHI切片输出标注图写进runs/sr_sahi_result。参数说明scale必须与权重训练倍数一致x2权重就写2写3会直接报shape错误。tile_pad是块与块之间的填充像素缓解分块带来的接缝伪影。halfTrue开启半精度推理显存占用和速度都有改善但CPU上不支持要设成False。最关键的是放大倍数不要一上来就上4倍面积扩大16倍切片数量和推理时间根本扛不住。另外图像超分辨率重建还有一个朴素做法就是用PIL的resize插值放大from PIL import Image img Image.open(data/test_images/aerial.jpg).convert(RGB) img img.resize((img.width * 2, img.height * 2), Image.LANCZOS)这段代码用LANCZOS插值放大2倍耗时极短但不会像CNN超分模型那样补充高频细节。小目标场景里插值放大后目标依然模糊yolov5的召回改善很小只适合先把链路跑通不适合要效果的生产环境。如果运行说明里标注了超分可选先用PIL版本走通全流程再换RealESRGAN不迟这一步能帮你排除很多环境问题。5. 避坑指南切片边界、显存溢出、误检暴增的排查记录5.1 切片边缘目标被切开导致漏检现象跑完SAHI明显看到某些目标没被检出来比如图上一排汽车中间空了一辆空出来的位置恰好落在两个切片交界处附近。原因overlap_ratio设置过小目标尺寸接近重叠带宽度目标一半在A切片、一半在B切片两个切片各自检测到的都是残缺目标置信度低于conf_threshold被过滤。解决把overlap_ratio从0.2提到0.25或0.3让重叠带加宽同时把conf_threshold降到0.2让被切开的低置信度框进入后处理由NMS判断是否与相邻切片的框合并。SAHI后处理有个特性同一个目标在多个切片里被检测到时NMS会利用重复信号合并所以检测阶段阈值偏低反而有利。这种情况在切片数量多的时候尤其隐蔽因为最终输出图上不报错只能从漏检位置反推是否落在边界。如果怀疑这个问题把verbose参数开到2它会输出每张切片的检测详情直接看到目标在相邻切片各自被检出但置信度都不高的画面。5.2 超分之后显存直接爆掉现象先对一张4000x3000的航拍图做2倍超分再走SAHI切片显卡立刻OOM报错日志都来不及看。原因2倍放大让图像面积变成4倍SAHI切片数量也变成约4倍叠加后总计算量远超单模块预期而且RealESRGAN的tile如果设成512或更大超分阶段本身就非常吃显存。解决RealESRGAN的tile降到128或256SAHI在超分后的图上把slice_size从512提到640切片总数变少还不行就开half用fp16推理或者把检测权重从yolov5s换成yolov5n。这一条是流水线的整合问题单看超分或单看切片都不会爆串起来才爆排查时用nvidia-smi盯显存变化确认卡在哪一步。提示先对原图做超分再对超分结果切片切片数量按超分倍数的平方增长。3倍超分等于切片数约变9倍推理时间基本告别实时。还有一种更省显存的做法先跑SAHI切片只对包含低置信度目标的切片做超分再把超分后的切片重新检测一次结果合并回原图。这个流程代码复杂一些但显存有限的服务器上值得做。演示源码里一般不默认开这个选项需要自己改。5.3 超分辨率重建之后误检暴增现象加超分之后小目标确实多检出来了但房顶纹理、灌木枝叶、电线也成片地被框出来结果图没法看。原因CNN超分模型放大图像时会把高频噪声一起放大yolov5在低分辨率训练数据里很少见这种高频纹理特征分布偏离预期于是把纹理误判成目标。解决置信度阈值从0.25提高到0.35到0.4先压住一部分误检再观察其次检查超分模型选型不要用带人脸修复或画面美化的超分模型这类模型会主动补充“看起来合理”的细节而这些细节在检测模型眼里就是假目标。选型优先选退化模型保守的EDSR类权重它对纹理的改动更小PSNR高一点乱造纹理的情况少很多。误检暴增还有一个隐藏前提如果超分模型训练数据比你的检测训练图像清晰很多特征分布偏移也会触发误检。这时候可以在测试集上统计误检框的位置分布如果集中在固定纹理区域基本可以确认是超分带来的特征偏移。考虑只对包含目标的切片做超分背景切片保持原分辨率噪声不会成片出现。5.4 SAHI后处理重复框合并失败现象输出图上同一个目标被框了两次甚至三次框的位置有偏移置信度各不相同。原因后处理的匹配方式或阈值不合适。用match_metricIoU时小目标框之间中心点偏移几个像素IoU从0.6掉到0.3低于match_threshold导致合并失败。解决改用match_metricIOS交集面积除以较小框面积对小目标偏移更宽容match_threshold设置在0.5左右即可。如果切片重叠率提到了0.3match_threshold可以降到0.4重叠越大同一个目标出现在更多切片里只要匹配上就该合并。另一个检查点如果SAHI日志里出现大量unmatched detections说明阈值太苛刻重复框没合并干净。重复框合并失败在视觉上很好认但如果评估方式是纯mAP计算这个问题的代价是AP下降几个点因为重复框会被算成假阳性。我见过有人调了半天yolov5的iou_thres改善不了最后发现是SAHI后处理参数的问题方向搞反了。5.5 权重路径与模型缓存的黑匣子问题现象weights目录里明明有yolov5s.pt但SAHI加载时报权重不存在或者第一次加载成功后换了best.pt重新跑结果还是旧模型的输出。原因一类是路径问题代码里用相对路径而当前工作目录不对另一类是缓存问题SAHI内部对模型有缓存机制同一个name和model_path会复用旧模型。解决在脚本开头加一行os.chdir(项目根目录)model_path统一写绝对路径彻底绕开相对路径的不确定性换权重时给AutoDetectionModel加cacheFalse或直接清掉SAHI的缓存目录。这个问题的经典场景是调试时先用yolov5s.pt跑通后来换成自己训练的best.pt结果输出还是yolov5s的检测结果让人怀疑新权重训练出了问题。先清缓存再跑一次能省不少排查时间。6. 进阶验证用召回率对比决定要不要超分以及树莓派5部署的取舍在把这个方案接到自己的项目之前别急着上超分先做一个对照实验。找100张有代表性的验证图跑三条管线纯yolov5、SAHI切片、超分加SAHI切片分别统计小目标类别的召回率和单张推理时间。用COCO的area分档方式把目标按面积分成小于32x32、32x32到96x96、大于96x96三档单独看小目标档的召回变化。SAHI切片通常能把小目标召回率拉高15到30个百分点超分加SAHI会在切片基础上再多拉5到10个百分点但推理时间翻倍甚至翻三倍。如果超分只多拉5个点不建议上超分收益不值这个算力。如果用的是自己训练的yolov5模型把yolov5s.pt换成训练产出的best.pt即可对比流程完全一样。树莓派5上部署时SAHI不能无脑用。树莓派5的CPU推理yolov5s大约只有每秒几帧SAHI把一张1920x1080的图切成十几张512切片单帧延迟直接到几十秒不可接受。能用的组合只有yolov5n加slice_size640加overlap_ratio0.2超分模块用PIL插值代替CNN超分因为RealESRGAN在CPU上的耗时比检测本身还高。如果必须做超分选EDSR-Lite这类轻量权重输入缩到320x320只放大2倍。这个对照实验做一次你就能回答“超分到底划算不划算”。我自己的教训是第一次接航拍小目标时默认上了4倍超分结果准确率没涨多少推理延迟翻了近十倍。后来用上面的方法一测目标在16到32像素区间SAHI切片已经把召回拉到可接受范围超分纯属多余。从那以后我习惯把所有增强模块先停掉单独对比一次再决定加不加。这个决策流程比任何人的经验参数都可靠因为你的数据分布只有你自己知道。希望帮到你。本文还有配套的精品资源点击获取