ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

卫星图飞机小目标检测实战:从数据集标注到YOLOv8推理全流程

卫星图飞机小目标检测实战:从数据集标注到YOLOv8推理全流程 简介面向人工智能目标检测方向的开发者和研究者这是一套以飞机为单一检测目标的卫星遥感图像数据集适合用于训练与评估单类别目标检测模型解决高空视角下飞机目标的定位与识别问题。压缩包共2000个文件包含1000张高分辨率JPG原图、1000个配套XML标注文件以及1个info.txt说明文件整体大小291.76MB文件组织简洁。图像均为1024×1024像素细节丰富XML中记录了每架飞机的边界框坐标可直接用于监督学习也便于改造后适配SSD、YOLO、Faster R-CNN等主流检测框架开展数据增强和算法调优。对于遥感图像分析、智能监控等场景的开发者该数据集既可用于入门练习也能作为算法验证的基准数据。目前已有633人浏览学习能够帮助读者熟悉从数据准备到模型训练的完整流程并为进一步研究提供扎实的数据基础。1. 飞机卫星图目标检测的难点从来不在“认不认得飞机”卫星光学影像里的飞机和自然图像里停在跑道上的客机完全是两个物种。几千像素宽的大图上一架飞机往往只有三十到六十像素灰度变化小、纹理信息少停机坪上还密集停着几十架同型号邻机。机场道面、屋顶和标线与飞机的边缘结构相似低分辨率下极易误判。常规目标检测数据集里依赖的形状、颜色、上下文线索在卫星图里几乎全部失效。所以围绕“人工智能目标检测数据集飞机卫星图3”做模型命题核心不是让网络认识飞机而是让小目标检测在密集排列和复杂背景里稳定出框。做智慧城市安防、无人机巡查和遥感解译的工程师用这批数据训练的目标检测模型能直接迁移到机场、港口等相似场景高校里把它当作人工智能大作业来跑YOLOv8也足够撑起一个完整项目。这篇笔记从数据格式讲到训练闭环、避坑和切片推理按顺序照做就能跑出可用的检测权重。2. 拆解飞机卫星图数据集的底层格式txt标注与归一化坐标换算2.1 一个txt文件里每行五个字段顺序不能错飞机卫星图数据集通常沿用YOLO系列的标注方式每张jpg图片对应一个同名txt文件图片放在images目录txt放在labels目录通过文件名一一对应。打开任意一个txt每一行就是一个目标格式固定为五段类别ID、中心点x、中心点y、框宽、框高类别ID从0开始这个数据集只标飞机所以绝大多数txt里这一列都是0。后面四个数值做过归一化范围在0到1之间具体算法是用像素坐标除以图片的宽度或高度得到。注意这里存的是中心点和宽高不是bounding box的左上角和右下角这是刚上手时最常踩的坑。如果把中心点坐标当成左上角画出来的框会整体错位半个身位训练时模型还得自己把错误掰回来白白浪费拟合能力。我在换新数据集时第一步永远不是急着开训练而是先写一个可视化脚本把标注画回原图。这步跑通了后续所有环节才有意义。否则一旦标注本身错位训练指标再好看推理阶段都会变成一场灾难。标注错位在遥感图里非常隐蔽因为飞机呈灰白色画上去的绿色框如果不仔细盯住边缘很难发现它整体偏了十来个像素。目前主流的标注工具如Label Studio、labelImg导出的格式各不相同但只要最终转成上述五列txtYOLO就能直接吃。飞机卫星图数据集本身已经提供这类格式的话省去了格式转换环节可以直接进入目录整理阶段。不过仍然要检查一件事下载解压后jpg文件数量和txt文件数量是否一致。数量差得越多说明漏标、重复标的情况越普遍这种数据直接训练等于给模型灌输错误先验。2.2 把归一化坐标换算成像素框一个可视化验证脚本反推像素坐标的公式是固定的左上角x 中心点x − 框宽/2× 图像宽度左上角y 中心点y − 框高/2× 图像高度右下角x 中心点x 框宽/2× 图像宽度右下角y 中心点y 框高/2× 图像高度写成Python脚本就是每次数据检查都要跑的最小实现import cv2 img cv2.imread(sat_001.jpg) h, w img.shape[:2] with open(sat_001.txt, r, encodingutf-8) as f: lines f.readlines() for i, line in enumerate(lines): parts line.strip().split() _, xc, yc, bw, bh parts xc, yc, bw, bh map(float, (xc, yc, bw, bh)) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) if x1 0 or y1 0 or x2 w or y2 h: print(f第{i}个框越界需要人工确认标注内容) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, fplane_{i}, (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite(sat_001_check.jpg, img)脚本逻辑分四步读图、读标注、逐行换算坐标、把框画回图上。这里故意加了一个越界判断因为标注目标被图像边缘切到在卫星图场景里很常见尤其是机场图边缘的飞机框往往只有一半在图中。YOLO训练不会因为这些框报错但模型会学到“飞机的一半也可以作为一个完整目标”推理阶段就容易输出半截框。越界框数量多的时候建议统一剔除或者先把这个目标整体挪进切片内部再说。另一个值得注意的参数细节是txt中可能出现科学计数法。像9.5e-05这种数值OpenCV不会直接读但Python的float()能正常解析所以脚本里用map(float,...)就不会出问题。如果读取时遇到明显超出[0,1]范围的数值那大概率是数据集混用了COCO或VOC格式需要先统一格式再训练。混用格式的问题在批量生成的数据集里特别隐蔽因为大部分标注归一化后恰好落在[0,1]区间内只有个别目标跨边界时才会暴露出异常值。2.3 单类别数据集里的类别编号和空标注处理这个数据集是单一类别“飞机”但有两个和类别相关的易错点值得单独提。第一类别ID始终从0开始即使只有一个类别也写0不能写1。有些人在python里写循环时图省事从1起手结果所有标注的类别全部错位训练时模型学到一个不存在的类别索引。这类错误在验证时mAP会剧烈波动而且没有任何报错提示相当隐蔽。单一类别项目里names列表写[airplane]就够了不需要加background之类的占位符YOLO体系里背景由负样本负责。第二空txt文件代表这张图里没有飞机也就是负样本。负样本对抑制误检至关重要。机场场景里道面、建筑物阴影和停放的工程车辆与飞机形状接近如果训练集全是正样本模型会把长得像飞机的物体全框出来。因此我通常会让训练集中保留5%到15%的空标注图片同时保证验证集里也有负样本这样才能真实评估误检率。如果原始数据集自带的train.txt/val.txt清单已经决定了哪些图带目标不要轻易修改这个划分负样本的配比会影响最终precision和recall之间的平衡点。处理完单类别和负样本之后数据集本身已经可以喂给YOLOv8训练了。下一步是把目录整理成ultralytics能直接识别的结构这一步做不好后面所有yaml配置都会连锁出错。3. 用YOLOv8跑通飞机卫星图的训练闭环目录、配置与训练参数选择3.1 把原始文件整理成ultralytics标准目录结构下载好的数据大概率是散放的图片文件和标注txt可能在同一个文件夹里也可能分散在不同目录但看不到明确的train/val划分。ultralytics YOLOv8约定俗成的目录格式是这样aircraft_sat/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/images目录下放jpglabels目录下放同名txt两个目录的train/val/test子目录一一对应。训练时YOLOv8会自动去labels目录里找与图片同名的txt文件找不到就直接跳过这张图但不报错。所以如果目录配对出了错你会看到训练日志里的图片总数比预期少很多这时候八成是labels路径写错了。如果数据集自带train.txt、val.txt划分清单优先按清单迁移。清单里每个条目通常是图片文件的绝对路径或不含扩展名的文件名。用下面的脚本循环读取并搬运import os import shutil src_img raw/JPEGImages src_lbl raw/labels dst aircraft_sat phase_sets {train: raw/train.txt, val: raw/val.txt} for phase, list_file in phase_sets.items(): os.makedirs(f{dst}/images/{phase}, exist_okTrue) os.makedirs(f{dst}/labels/{phase}, exist_okTrue) with open(list_file, r, encodingutf-8) as f: ids [line.strip() for line in f if line.strip()] for img_id in ids: img_src f{src_img}/{img_id}.jpg lbl_src f{src_lbl}/{img_id}.txt if os.path.exists(img_src) and os.path.exists(lbl_src): shutil.copy(img_src, f{dst}/images/{phase}/{img_id}.jpg) shutil.copy(lbl_src, f{dst}/labels/{phase}/{img_id}.txt) else: print(f文件缺失: {img_id})这里约定清单里每一行是不带扩展名的文件名拷贝时再补上.jpg和.txt。两个文件都存在的才拷贝缺一个就打印出来。为什么打印而不直接报错因为有些原始数据确实只有图片没有标签这些图需要后续补标或当作负样本处理直接屏蔽掉会导致数据量肉眼可见地减少却没有引起注意。如果数据集没有自带划分清单自己随机划分时建议用脚本按图片ID做shuffle再按8:1:1分到train、val、test三个集合。不要在资源管理器里手动拖拽也不要简单按文件名排序后取前面N张。后两种方式容易让同一机场的不同角度切片全部堆进训练集验证集失去多样性最终得到的mAP没有说服力。3.2 一份data.yaml容易踩的三个坑目录结构就绪后写data.yaml成了几乎每个项目都要踩一遍的环节。一份最简配置长这样path: /home/user/aircraft_sat train: images/train val: images/val test: images/test nc: 1 names: [airplane]三个容易出错的点。第一path字段必须写绝对路径。YOLOv8内部会把path和train字段拼接起来去定位图片目录。写成相对路径时启动训练的工作目录一旦变了它就会去错的路径找图报错信息又很隐晦通常是“Dataset not found”。排查半天最后发现只是路径拼错。第二train、val字段的值不要带前导斜杠也不要做成绝对路径。“/images/train”会让拼接出来的路径多一层根目录正确写法是“images/train”让path和它自然拼出完整路径。这条细节在换机器、换数据目录时特别容易反复踩到。第三nc和names的元素个数要严格对应。列表里只写一个类别名但nc写2训练通常直接报索引越界反过来names列表少了类别就会少识别。如果项目后续要扩展到多个类别例如加一个“直升机”类记得同时把nc改成2并在names里追加对应标签顺序要和txt第一列的ID完全一致。类别ID本质就是索引YOLO不会去比对名字顺序写反了也能训只是输出标签张冠李戴。3.3 模型体量选择为什么我从yolov8s起步YOLOv8按体量分成n、s、m、l、x五档。普通目标检测用最小的n档也能出效果但小目标检测场景里模型选择要谨慎很多。小目标在特征图中的信息本来就很稀薄模型容量太低几十个通道根本存不下足够的中层特征来同时表达飞机不同朝向、不同光照下的形状。因此我通常从s档起步显存允许时直接用m档。n档只用来做冒烟测试确认代码链路能通到loss出来然后马上换回s或m。这里不是简单“模型越大越好”。过大的模型在数据量不足的遥感数据集上会过拟合训练集mAP接近1而验证集在0.7附近徘徊。飞机卫星图数据量通常有限同一场景的不同图之间关联度高模型容量上去了但数据不够反而更早进入过拟合平台期。先确认数据量再选模型数据量大且单一类别时选m数据量中等就用s数据量很小就把精力先花在扩样本上而不是换大模型。预训练权重的选择上用yolov8s.pt而非从头训练yolov8s.yaml多数情况下loss更低、收敛更快。COCO预训练模型底层的边缘、纹理、色块特征对小目标有很强迁移性模型只需要在上层学习“什么样的边缘组合是机翼”。卫星图里的飞机确实没有COCO中物体的鲜艳色彩但边缘几何结构是通用的。随机初始化等于把已经成熟的底层特征丢掉重学浪费计算量。3.4 训练命令参数解读与日志读数训练启动命令yolo detect train \ modelyolov8s.pt \ dataaircraft_sat/aircraft.yaml \ epochs120 \ imgsz640 \ batch16 \ lr00.01 \ patience20imgsz写640是多数人默认的选择。但飞机卫星图里目标小如果显存够用我建议试一次imgsz1280效果往往远比换更强模型直接。只是imgsz翻倍计算量约等于翻四倍训练时间和显存占用都会被明显拉高。在16G显存的卡上跑1280很难配上大batch可以把batch降到8甚至4。batch下降后BN层统计量会波动loss曲线变抖这时稍微降低学习率来补偿。batch和lr0是联动关系batch翻倍lr0通常也翻倍batch减半lr0也减半。保持这个联动能少踩很多震荡的坑。训练日志会实时刷出box_loss、cls_loss、dfl_loss以及precision、recall和mAP50。不要盯着loss一直看那是过程量。真正要盯的是mAP50和recall尤其是recall。飞机检测任务里漏检的代价往往比误检大recall从0.6到0.8的提升比mAP50从0.8到0.82更有价值。mAP50是精度-召回曲线的面积单独一个数字看不出错检和漏检的比例可视化验证时如果发现大量漏检优先考虑提高recall即使precision略降也可以接受。训练时间受epochs和patience共同控制。patience20表示连续20轮验证指标不涨就停止。跑完后在runs/detect/train/weights下会生成best.pt和last.pt。best.pt按验证集指标表现最优一般直接拿来做推理last.pt是最后一轮的权重。如果best指标出现在很靠前的epoch而后面的训练开始过拟合可以结合日志里的mAP曲线决定用哪一个不必迷信best.pt一定最合适最终场景。4. 飞机小目标检测避坑清单漏检、并框与指标虚高的排查这一章的内容来自真实项目里反复出现的现象。小目标检测和通用目标检测的很多经验并不通用下面五条是我在飞机卫星图里最常遇到的坑每条都按“现象 → 原因 → 解决”来写。4.1 现象验证集mAP很高换成正张大图推理时漏检成片这是最让我觉得像玄学的问题之一。训练时mAP50可以到0.8以上但一旦拿一张完整的5000×4000卫星图去做批量推理输出框寥寥无几漏检率极高。原因其实不玄。训练时输入是已经裁剪好、目标清晰的图片验证集也来自同样分布推理时却把整张大图直接resize成YOLO的imgsz640。原来30像素宽的飞机resize之后可能只占3像素在特征图上连一个网格都盖不满漏检是必然结果。目标检测模型对训练分布外的情况几乎没有适应能力目标尺寸一旦偏离太多性能断崖式下跌。解决分两步走。第一训练阶段就把图片切成带重叠率的小块让目标在输入图像里的像素占比合理第二推理阶段对大图做滑动窗口切片切块送进模型再把结果拼回原图。两步都做才能保证训练和推理时的目标尺度一致。这也解释了为什么飞机卫星图数据集看起来mAP不错换到实际大图使用时却让很多人翻车。4.2 现象停机坪上多架飞机相邻被NMS合并成一个框停机坪上的飞机往往多架并行、间隔很小。NMS后处理用IoU判断两个框是否指向同一个目标默认IoU阈值约0.7。相邻飞机框的重叠程度很容易超过这个阈值于是两个不同飞机的框被当作重复检测抑制掉其中一个视觉上就是“两架飞机只出一个框”。解决方式是调整推理阶段的IoU阈值从默认值往下调。用ultralytics推理时在模型调用处显式传入参数from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model(airport_full.jpg, conf0.3, iou0.4, imgsz640)这里iou0.4表示NMS只合并IoU超过0.4的框比默认0.7严格相邻飞机的框不容易被误并。注意推理阶段的iou和训练时的iou参数含义不同不要混淆。iou降到0.3以下时同一架飞机自身的多个预测框也会交叠过大产生碎框所以不建议低于0.3。conf0.3是置信度阈值低于这个分数的候选框直接丢弃。对密集场景来说conf和iou两个参数要一起调conf太高会漏掉低分目标iou太低会保留过多重复框常见的组合是conf0.25、iou0.45再根据可视化结果微调。4.3 现象验证集指标高得离谱换到异地新图就崩这部分带点血泪成分。数据量不足时常见做法是把一张大卫星图切成几百个小块再把块随机划分为训练集和验证集。如果切片前没有按原始大图ID区隔同一张大图切出的块可能同时出现在训练集和验证集里。这些块共享同样的纹理、阴影方向和地面布局模型在验证集上表现很好只是因为它记住了同一片区域的纹理特征而不是学会了泛化识别飞机。解决办法是让划分依据落到原图ID层面。切片文件命名时带上原图编号比如airport_sat_01_0001.jpg划分前先按原图编号分组整个组进同一个集合。宁可验证集样本量少一些也不要让同一张原图的切片散进两个集合。这条对评估结果可信度的影响极其关键很多时候所谓小目标检测模型“泛化差”其实是评估方式本身出了问题。提示切片训练时验证集同样需要切片。此时验证集切片必须严格来自训练集中未出现过的原图。判断验证集是否够真实的简单办法是随机抽几张验证图看它们的背景是否在训练图里出现过出现过则说明切片划分粒度出了问题。4.4 现象模型只框住机头或机身明显不完整推理时模型报出的框往往只覆盖机头或者框身明显短一截。问题多半出在标注一致性和长宽比分布上。飞机在卫星图上的长宽比通常在1:2到1:4之间但有些标注贴着机头阴影去框框得特别紧导致长宽比极端。YOLO分配正样本时长宽比跨度大的GT匹配难度更高小模型尤其容易学偏最终输出以短边为主的框。解决方式是统计标注长宽比的分布把长宽比异常比如超过1:5的标注找出来单独看。如果只是少数几例直接在标注里修正如果普遍偏紧说明标注规范本身有问题需要统一规定“框要包含整个机翼展开范围”而不是紧贴机体轮廓。一个相对有效的说法是框的语义是“这个目标在图像中占多大区域”而不是“这个目标的最小外接矩形”。按外接矩形来标框出来的区域往往比视觉上需要的区域小一圈飞机这种长条目标就会被学习成机头小条。4.5 一套通用排查顺序碰到模型效果不好时按下面顺序排查大部分问题都能定位到具体环节。第一先跑一遍第2.2小节的可视化脚本确认标注没有错位、漏标、越界。标注是地基地基偏了后面全白搭。第二看训练日志确认mAP50和recall确实还在提升而不是靠early stopping硬撑。如果训练集指标涨但验证集不涨过拟合概率大。第三把验证集的检测结果一张张看过去用肉眼判断漏检出在哪类样本上——是小目标整体漏掉还是密集区域被并框还是背景误报。第四再调imgsz、conf、iou这三个推理参数。多数看似复杂的难题在这一轮排查里就能找到答案。真排查不出来时再去怀疑模型结构或数据分布不要一上来就换模型、加模块那是投资回报率最低的做法。5. 把飞机小目标检测精度再提一档切片训练与数据增强的实战参数5.1 小目标在特征图上的困境为什么要保持目标像素面积深度学习里常说小目标检测难难在特征图上的有效信息不足。以YOLOv8s为例输入640×640的图像经过32倍下采样后特征图是20×20。一个被resize后只有8×8像素的小飞机在20×20的特征图上最多占据一个网格每个网格又经过多次卷积聚合真正能表达“飞机”语义的信号可能只剩几个像素。检测框回归需要同时预测中心、宽、高四个量在这么弱的信号下输出自然不稳定。所以小目标检测的第一条原则是不要压缩目标的像素面积。针对飞机卫星图数据集最有效的手段之一就是切片。把大图切成640×640或1024×1024的小块让飞机在输入图像中占据合理比例。举例说目标宽度约30像素用640的切片时飞机占输入宽度的约4.7%如果直接用原图resize到640原图宽5000像素30像素的飞机缩到不到4像素基本不可检测。同样的模型、同样的权重只需要改变输入中目标所占据的比例就能显著改变检测效果。5.2 训练切片与标注坐标同步变换切片训练时图片切了标注txt也要同步换算。常见的做法是固定一个切片尺寸按步长滑动目标中心落在哪个切片内就把它算作该切片的标注跨边界的目标直接丢弃不硬塞。import cv2 import os crop_size 1024 step 768 # 重叠率约25% img cv2.imread(sat_full.jpg) h, w img.shape[:2] with open(sat_full.txt, r, encodingutf-8) as f: labels [list(map(float, ln.split())) for ln in f] os.makedirs(crops, exist_okTrue) for y in range(0, h - crop_size 1, step): for x in range(0, w - crop_size 1, step): crop img[y:y crop_size, x:x crop_size] new_lines [] for lab in labels: cls_id, xc, yc, bw, bh lab cx, cy xc * w, yc * h bw_px, bh_px bw * w, bh * h # 目标中心必须落在切片内否则丢弃 if x cx x crop_size and y cy y crop_size: new_cx (cx - x) / crop_size new_cy (cy - y) / crop_size new_bw bw_px / crop_size new_bh bh_px / crop_size new_lines.append( f{cls_id} {new_cx:.6f} {new_cy:.6f} {new_bw:.6f} {new_bh:.6f} ) cv2.imwrite(fcrops/{x}_{y}.jpg, crop) with open(fcrops/{x}_{y}.txt, w, encodingutf-8) as f: f.write(\n.join(new_lines))这段代码的关键逻辑是坐标变换先把归一化的中心点还原成原图像素坐标再减去切片左上角的偏移量最后重新除以切片尺寸做归一化。框宽高同理先把归一化宽高还原成原图像素宽度再除以切片尺寸。只有目标中心落在切片内才保留生成标注跨边界的飞机不要硬塞进任一侧切片因为边缘被截断的框会让模型学到“半个飞机也是完整目标”和4.3节提到的越界框问题一样。切片尺寸crop_size和步长step是两个核心参数。crop_size越大单张切片包含的上下文越丰富但小目标占比被稀释crop_size越小小目标越清晰但上下文缺失也会增加误检。以本文数据集为例飞机宽度大多集中在20到60像素我一般先试1024的切片尺寸、768的步长也就是25%重叠率。步长减小到512时重叠率升高同一目标会在多个切片中重复出现等效训练样本变多但训练时间也随之拉长。实际使用中先用512步长确认效果上限再逐步放宽到768步长看精度损失是否可接受。切片尺寸大于1600之后显存占用会明显上升同时对上下文收益的边际贡献递减不建议一味加大。5.3 数据增强旋转、Mosaic与训练策略取舍数据增强方面对飞机卫星图最有效的是旋转类增强。飞机在停机坪上的姿态可以是任意角度而训练数据里经常出现“大部分飞机朝同一方向停靠”的情况比如沿跑道平行排列。这时候模型会对水平方向的飞机过拟合遇到斜向停靠的飞机就漏检。YOLOv8虽然内置了随机翻转增强但翻转只能覆盖有限的朝向变化对斜向角度的覆盖不够。常用做法是在数据预处理里加入90度、180度、270度的随机旋转。天空地面在卫星图中没有明显的方向语义旋转增强不会引入不自然的样本。Mosaic增强把四张图拼接在一起对多样性的提升非常明显但它对小而密集的目标可能产生副作用四张图缩放后小目标进一步缩小部分目标可能小于模型的感知极限。我的习惯是前80%的epoch开Mosaic帮助模型见多识广后20%关闭Mosaic让模型在接近真实分布的样本上收敛mAP50往往能在最后阶段再涨一点。这个策略不是最优解的通式但当训练后期loss震荡不止时值得一试。评估增强策略是否有效不能只看验证集mAP。数据增强本质是一种正则化它可能降低训练集拟合度同时提高泛化能力。如果训练集mAP很高而验证集指标不涨加大增强方向正确如果训练集和验证集mAP都偏低先检查数据集质量和标注不要寄希望于增强解决一切。在我做过的飞机卫星图项目里旋转、翻转和切片策略在绝大多数场景下都有帮助但每换一批新图仍然要重新可视化抽检确认增强手段没有在切片过程中丢失目标。6. 大图滑窗推理的坐标还原实现与检测收尾习惯6.1 最小可用的滑窗推理与坐标映射代码训练完成后最终要面向的是一整张卫星大图。滑窗推理的原理和训练时切片一致把大图切块送进模型把每个块上检测到的框坐标加上这个块的原点偏移映射回大图坐标。核心代码不需要太长import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) def infer_full_image(img_path, crop_size640, step512): img cv2.imread(img_path) h, w img.shape[:2] dets [] for y in range(0, h - crop_size 1, step): for x in range(0, w - crop_size 1, step): crop img[y:y crop_size, x:x crop_size] result model(crop, conf0.3, iou0.45) for box in result[0].boxes: bx1, by1, bx2, by2 box.xyxy[0].cpu().numpy() dets.append([bx1 x, by1 y, bx2 x, by2 y, float(box.conf[0])]) return np.array(dets)核心映射逻辑只有一行bx1 x、by1 y也就是把切片内的坐标加上切片左上角在大图中的偏移。conf0.3和iou0.45按4.2节的思路设置step512、crop_size640时切片间有约20%重叠目标跨边界时至少有一个切片能完整包含它。当目标横跨两个切片时两边都会检出它这会形成坐标相近的重复框属于正常现象交给后续NMS统一处理。6.2 全局NMS与两个收尾习惯拼接之后所有切片产生的框汇总到同一份大图坐标里。这时需要统一跑一次全局NMS把重叠切片造成的重复检测合并掉。可以直接用OpenCV的接口boxes dets[:, :4].astype(np.float32) scores dets[:, 4].astype(np.float32) indices cv2.dnn.NMSBoxes( boxes.tolist(), scores.tolist(), score_threshold0.3, nms_threshold0.5)单独看每个切片NMS是各自执行的横跨边界的重复框在两边都被保留合并后才暴露出来。全局NMS以整张大图为单位再做一次抑制是滑窗推理最后一步必不可少的收尾。最后分享两个长期形成的习惯。第一个把滑窗推理和坐标还原写成独立函数放进tools.py参数用显式命名换数据集时只改crop_size和step。第二个对大图上目标数量特别多的场景推理结果先不做硬筛选把所有检测框连同置信度落盘成csv后面要换阈值直接读csv重新过滤。我早期为了省事在推理函数里提前套上双重筛选后来想换阈值重新评估只好把整批大图重新跑一遍推理时间成本非常高。先把原始结果留好再按需要的阈值筛选是一条省时间的后悔药。这个习惯让我在目标检测项目里的迭代速度快了很多希望帮到你。本文还有配套的精品资源点击获取
返回列表