ARTICLE DETAIL

资讯详情

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

高分辨率航拍目标检测的切图策略:从Patch划分到坐标映射

高分辨率航拍目标检测的切图策略:从Patch划分到坐标映射 说实话这几年做高分辨率航拍图像的目标检测碰到的第一个坎儿往往不是什么先进的模型结构而是“图太大喂不进去”。一张无人机拍出来的原图动辄 5000×3000 像素直接丢进 YOLO 或者检测 Transformer 里显存立刻见底就算勉强塞进去连带着把大图硬缩到 640×640那些几十像素的小目标早就糊成一团了。所以业界的通行做法就是切图——把大图切成若干小 patch 再送进模型。但切图这件事看着简单里面全是坑切多大要不要重叠重叠了怎么去重切了之后标签怎么弄训练和推理要不要一致这篇就把我在实际项目里反复调出来的策略讲透。写这篇一是给自己做个复盘二是给正在被航拍检测折腾的人一个可直接抄的作业。无论是做智慧城市、车辆检测、光伏巡检还是野生动物监测只要输入是高分辨率航拍图这些经验基本都通用。内容会覆盖为什么必须切图、切图的核心参数怎么定、训练和推理两个阶段各自的注意事项以及我踩过的那些很隐蔽又很致命的坑。1. 为什么要切图问题的根源不只是显存很多人觉得切图就是因为显存放不下这话只对了一半。显存确实是直接限制因素但更关键的是硬缩图会让目标在特征图上的响应彻底消失。航拍图像和普通网络图片有个本质区别目标的绝对尺寸特别小但目标数量特别多。一张 6000×4000 的城区航拍图里面可能有上千辆汽车每辆车在原始分辨率下是 40×20 像素左右。如果这一整张图缩到 640×640 输入模型车辆就变成了约 5×2.5 像素的单点别说检测人眼都看不出来。就算模型能在这张图里找到一些模糊响应目标之间的间距也被严重压缩很容易把紧挨着的多辆车判成一个目标。所以在航拍检测场景里我们做的不是简单的“把图变小”而是“把图分区放大”。切图本质上是让每个目标都至少在一个 patch 中保持足够的分辨率保证模型能提取到有效特征。再说显存问题一张 3000×3000 的 RGB 图如果跑一个输入尺寸为 640 的 YOLO预处理阶段就会把图 resize 到 640显存占用不大但检测效果差如果你强行把原始分辨率喂进去模型本身的设计就不支持任意超高分辨率很多模型在输入超过 2000 像素后特征图会迅速膨胀注意力和感受野的分布都会被破坏。所以从设计上讲航拍检测的主流路线就是滑窗切图 单卡并行而不是一张大图端到端硬算。另外还有一层业务原因航拍图像的采集往往覆盖很广的地理范围一张图对应的可能是一平方公里。模型要对整个区域输出检测结果如果只给一个全局的弱检测结果后端的 GIS 系统没法精确定位目标到具体坐标。切成 patch 之后每个 patch 拥有明确的图像坐标范围输出的目标框可以逐个映射到全局坐标这条链路才是完整可用的。2. 切图策略的核心参数patch_size、overlap、resolution2.1 patch_size 到底选多大别迷信 640关于切图尺寸最直接的参考是训练时模型预设的输入尺寸。YOLO 系列一般用 640Detection Transformer 类常用 800 到 1333。我的原则很简单让切出来的 patch 边长和模型训练输入的分布尽量接近但不要完全相等。如果你把 patch 切成 640×640 正好再 resize 到 640×640等于一张白纸画满再裁开没有引入任何多余信息但也没有充分利用模型在原分辨率上的优势。我实测下来patch 尺寸略大于模型输入尺寸比如切成 800×800再 resize 到 640效果比直接切 640 要好原因有两点800×800 覆盖了更大的物理区域单个目标边界被切断的概率更低resize 的降采样过程相当于对 patch 做了轻微平滑抑制一部分噪声。但 patch 也不能无限大。patch 越大单个 patch 内小目标越多模型在推理时对密集目标的互相抑制作用会增强检测上限反而下降。另外 patch 越接近正方形越好长条形的 patch 容易因为图片两端的上下文差异造成不稳定的预测。我常用的尺寸范围是 512 到 1024。对于目标以车辆、船舶、建筑物为主的数据集800×800 是最稳的如果是小目标极多、单个目标只有十几个像素的极端情况我有一次试过切成 512×512小目标召回率提高了约 11%代价是推理耗时涨了接近 3 倍。这就是典型的精度与速度 trade-off需要根据实际项目需求去压。2.2 overlap 不是越多越好但绝不能没有如果切图完全没有重叠即相邻 patch 的边界正好拼上那么在边界线上的目标就会被一刀两断。有些目标本身就有 100 像素长切图后可能 70 像素在左边 patch、30 像素在右边 patch两边模型都只能看到一个残缺目标很可能都检测不出来。所以重叠区域的本质是“给边界目标留缓冲”。我推荐的策略是 overlap 取 patch_size 的 10% 到 25%具体取决于数据集里目标的最大尺寸。经验公式是overlap min(max_target_size × 1.5, patch_size × 0.25)。也就是重叠量要至少能覆盖一个完整的目标加一点余量但不能大到让相邻 patch 的计算重叠区域过多、白白浪费算力。对于 800 的 patch如果最大目标约 100 像素那 overlap 取 150 像素即约 18.75%比较合适。overlap 另一个隐藏作用是影响后期的去重难度。重叠越多同一个目标出现在多个 patch 里的概率越高跨 patch 的冗余检测也就越多处理起来会多很多工作量。所以不要一上来就往 50% 调除非你处理的场景目标极小、极其密集。2.3 resolution切图不改变像素但改变“信息浓度”切图本身不做超分辨率重建它只是把原图的像素按空间划分出来。所以切图优化策略里谈 resolution实际上谈的是“每个 patch 中目标实例所占的像素比例”。在训练阶段如果原图只有 3000×3000切成 800 的 patch那每个 patch 是原图的一个局部放大视图目标在 patch 里的尺寸和原图完全一致。你会发现一个奇怪现象同一个目标直接缩放整图时它只有 10 像素切图后它在 patch 里还是 10 像素看起来没变。但模型接收的输入尺寸变了——从“全局缩放图里的一小点”变成了“局部 patch 里的一个小块”。这带来的实际效果是目标在特征图上的响应位置更明确背景干扰缩小了小目标的检出率实实在在提升了。不过这里有个前提如果你的 patch 分辨率不足以保留目标的特征比如目标本身就模糊那切图救不了你。切图适合的场景是“目标在原始分辨率下清晰可辨只是相对整图太小”。所以拿到项目第一步是先看数据目标所占最小像素面积是多少如果连 15×15 像素都没有先考虑的是提高采集分辨率而不是盲调切图。3. 切图与推理从全局定位到局部检测的流程设计3.1 标准推理流程与坐标映射推理阶段最稳妥的流程是固定的我在这里写一遍你在代码里需要实现的完整链路读取整张大图记录原始宽高 W0、H0根据设定的 patch_size 与 overlap计算所有切图的起始坐标x0, y0对每个 patch 做一个略微扩展的裁切裁切边界要同步做 padding防止切到图像边缘时 patch 尺寸不足每个 patch 送入模型推理得到目标框坐标注意这时输出的坐标是相对 patch 的局部坐标将局部坐标加上该 patch 的起始坐标换算回整图坐标收集所有 patch 的目标框做全局去重NMS 或直接过滤冗余框。第 2 步计算坐标时有个细节容易错。假设原图宽 W0patch 大小 Poverlap 为 O那么 x 方向的起始位置序列是x 0, P - O, 2 × (P - O), ...直到 x P W0最后一刀要把 patch 的左边界设为 W0 - P保证覆盖到图像右边缘。这样最右边那个 patch 会和前一个 patch 有较大重叠但能确保不漏。3.2 边缘处理padding 比裁剪更安全直接按坐标裁剪时如果 patch 的右边界越过图像边缘最简单的方式是用 0 值扩充一个灰色边框或者用边缘像素复制填充。我测试过padding 填充方式对检测结果的影响较小但灰色填充比如 114、114、114普遍比黑色填充效果好。原因也很好理解很多模型在训练时对全黑边界有过拟合倾向黑色块容易被误判成阴影或遮挡区域灰色则更接近常见背景的统计分布。注意padding 的区域不应该参与最终的坐标映射也就是说这个区域检测出的目标框属于无效区域。有两种解决方式一是将输出框进行矩形裁剪只保留框和有效区域求交的部分二是在逻辑上直接丢弃那些“大部分落在 padding 区域”的弱预测框。我在工程里倾向用后者简单高效。3.3 跨 patch 去重只对重叠区做 NMS 就够了跨 patch 重复检测是切图推理最突出的问题。同一个目标因为 overlap 的存在可能出现在两个甚至三个 patch 中每个 patch 都可能输出关于它的检测框。如果不做任何处理最后全局结果里会出现一堆几乎重合的框。很多人的第一反应是做一次全局 NMS把整张图上所有框统一按 IoU 阈值过滤掉。这个方案能不能用能但做得不够精确。全局 NMS 有个隐患对于相邻很近的真实目标比如两辆并排停靠的车它们的检测框 IoU 可能超过 0.5全局 NMS 可能会把其中一个真实目标误删掉。我推荐的做法是只对位于 patch 重叠区域内的检测框做跨 patch 合并非重叠区域的框直接保留。这样既解决了重复检测又避免了误杀。具体实现上可以算出每个检测框中心点落在哪个 patch 的编号——如果同一个中心点对应多个 patch则这几个框进入候选合并队列否则直接输出。合并方式不一定要用 NMS用简单的均值加权也可以根据模型置信度赋予权重效果往往更平稳。4. 切图与训练数据增强和标签重投影4.1 训练时切图随机裁剪本身就是最强的数据增强在高分辨率航拍场景下训练时很少直接把整图 resize 进模型而是用“随机裁剪 相对尺度缩放”的组合。这和测试阶段的滑窗切图略有不同。训练的随机裁剪不需要固定 grid而是随机选位置裁剪 patch再根据 target 尺寸调整 patch让它适配模型输入。我常用的训练切图配置是从原图中随机裁一块 patch 做训练patch 的边长在原图短边的 50% 到 90% 之间随机再根据目标大小判断是否需要进一步缩放。这样做的最大优势是让模型看到更多样化的尺度。航拍图中目标尺度差异很大同样是车停车场里的车可能 100 像素而高海拔无人机拍到的车只有 20 像素。随机裁一块小区域再缩放等于为模型制造了多尺度训练样本。训练阶段的随机裁剪要注意标签的重投影。裁图之后所有边界框都要减去裁图区域的左上角坐标还需要把超出 patch 范围的部分裁剪掉。特别要注意的是那些被裁掉一大半的目标——我建议直接丢弃与 patch 有效区域相交比例低于 0.7 的目标框因为只露 30% 的目标在语义上已经不是完整的类别实例硬当作正样本只会让模型学得混乱。4.2 离线切图 vs 在线切图效率和灵活的平衡这里存在两种做法。离线切图是把训练图像预先切成 patch 存成静态文件训练时直接读取 patch。优点是数据处理速度快不用在训练循环里每次都做切片操作数据准备完了可以直接开跑缺点是会产生大量文件存储和 IO 开销大而且切法固定模型能看到的上下文被预先限制了。在线切图则是在每个 epoch 内实时随机裁剪训练的每个 step 看到的 patch 都不同。推荐在训练阶段用在线切图方式随机性天然带来数据增强在测试或推理阶段用固定滑窗切图保证每次的输出一致和可复现。如果因为环境条件限制必须用离线切图建议切两次一次 overlap 为 0 的干净切图另一次 overlap 为 50% 的加密切图然后混合喂给模型。这样至少能让模型见过不同位置的目标部分弥补随机性的缺失。4.3 小目标多的数据集切图时一定要做正样本均衡航拍图的目标分布往往极度不均匀。一整张图可能三分之二是农田、水面或空地只有在一个小角落里有一片建筑群或车辆集群。如果不做任何处理直接随机裁剪那大部分 patch 里根本没有目标模型训练效率极低收敛慢最终对目标区域的检测效果也差。一个很实用的策略是分两路采样一路从全图中随机裁剪保证模型见过各种背景类型另一路找到所有包含目标的区域在这些区域内做中心偏移随机裁剪保证每个 batch 里有一定比例的正样本。我通常将正负样本比例控制在 3:7 左右。正样本占比太低容易漏检太高又容易过拟合对背景的泛化能力下降。5. 常见问题与排查技巧实录5.1 “目标被切碎了检测率反而下降”这是最常犯的错误。如果你发现加了切图逻辑之后检测率比不切还差第一步不是调模型而是看切出来的 patch 可视化。大概率是 patch_size 设置得太小导致一个目标被切成两半、没有任何一个 patch 记录了其完整轮廓。尤其是大型目标比如建筑屋顶、停车场整体轮廓它们的尺寸动不动就 200 到 300 像素如果把 patch 设为 320 且没有足够的 overlap那模型确实很难正确识别。这类问题的排查我一般写一个简单的数据集统计脚本计算全图中所有目标框的长宽分布用 p90 的框尺寸代表“这些目标通常有多大”。然后让 patch_size ≥ p90 × 3overlap ≥ 目标最大尺寸。这两个值只要满足切碎问题就能解决大半。如果 patch 调大了显存不够那就得考虑降低 batch size 或者换用更轻量的模型结构。5.2 patch 之间检测结果不稳定同一个目标在 patch A 中置信度 0.85在 patch B 中只有 0.3。这种情况非常让人头疼因为它说明模型的预测强烈依赖上下文。在 patch A 中目标周围有完整的道路、周围车辆等上下文模型判断很有信心在 patch B 中目标被裁剪得位置很偏周围全是空白区域模型信心骤降。处理方式有两种一是提高 overlap让目标出现在不同 patch 中的概率增加然后合并多个 patch 的预测结果时采用 vote投票平均而不是单帧置信度二是在切图时给每个 patch 预留更大的上下文区域比如目标实际占据 80 像素但 patch 设计为 800这样即使目标位于 patch 左上角它周围的上下文也足够丰富。需要注意的是overlap 调大之后推理时间会显著增长。800 的 patchoverlap 从 10% 调到 25%每个 patch 的有效新增面积减少但重复计算增多整体耗时大约增加 30% 到 40%。性能取舍是每次调整必须同步评估的要素。5.3 坐标映射错位像素偏差、保留边界和下采样误差切图推理最隐蔽的 bug 之一是坐标映射错位。假设原图是 4096×2160patch_size 800overlap 100模型输入尺寸也是 800。某目标在 patch 里检测框中心是 (400, 400)patch 的起始坐标是 (700, 500)那么换算到原图应该就是 (1100, 900)。这一步骤写简单但工程实现时容易在多个环节引入误差有些模型在推理时会将输入 resize 到固定大小如果你的代码里在送入模型前对 patch 做了 resize那输出的检测框要先除以缩放比例再换算坐标如果切图时有 padding那么坐标换算必须减掉 padding 的偏移注意像素坐标是从 0 开始还是从 1 开始。OpenCV 默认从 0 开始的整数坐标JSON 导出时可能会默认加 1一整行偏一个像素影响不算大但看着难受。我建议在后期处理里单独维护一个 transformation pipeline把每个检测框的原始坐标、模型输出坐标、换算后坐标同时 log 下来。一旦出现错位对比记录就能快速定位是哪一步出的问题。5.4 推理过慢怎么从切图层面加速高分辨率图像推理速度慢首要瓶颈就是切出来的 patch 数量太多。比如一张 6000×4000 的图patch 800、overlap 0 就有约 38 个 patchoverlap 100 时逼近 50 个 patch。每个 patch 都要过一遍模型速度自然上不去。这里有几个在切图层面的加速思路。第一是背景过滤先用一个轻量级分类网络或者传统图像算法判断一下 patch 是否包含潜在目标纯农田和树林背景可以直接跳过。第二是自适应切图由粗到细先在全图上跑一个低分辨率快速检测找到可能包含目标的区域再对这些 ROI 做高分辨率切图检测相当于从“全局均匀扫描”变成“按需放大”。这个方法在检测稀疏目标时尤其有效空图能直接跳过 80% 的 patch。第三是 batch 推理把所有 patch 组成一个 batch 同时推理。如果显存够大推理耗时可以从逐个 patch 的线性累加变成接近只过一次模型的时间这是收益最明显、代价最小的一步优化。batch size 根据显存调一般取 4 到 16。6. 切图策略在不同场景下的调整心得6.1 车辆检测看重叠率和 NMS 的配合车辆检测是我最常做的一种航拍任务目标数量多、尺度相对统一、分布密集。这种情况下我会把 patch_size 调小一点比如 512 到 640 之间反而比大 patch 效果更好因为车辆目标密集时小 patch 能把单块区域内的实例数控制在合理范围模型不容易混淆相邻目标。overlap 设 12.5% 左右足够目标小重叠多了纯粹是浪费算力。去重阶段特别要注意车辆相邻近彼此之间 IoU 容易高。NMS 的 IoU 阈值我会设得保守一点0.5 左右就够了。如果用了官方预训练权重直接迁移到航拍场景建议前几个 epoch 冻结 backbone只训练检测头把车辆目标的框回归问题先稳定下来。6.2 建筑物和道路检测大 patch 低 overlap对于建筑物这类目标单个建筑可能占 300×300 像素patch 太小会直接把一栋楼拆散。所以 patch_size 建议直接上 1024overlap 控制在 10% 左右。因为建筑边界清晰、类别内差异大只需要确保每栋楼完整落在一个 patch 里不需要太多跨 patch 锚定。道路提取和建筑检测有点类似主要问题是线状目标跨 patch 特征太强单靠切图解决不了要在后处理阶段做矢量化连接。切图时尽可能让 patch 与道路方向呈一定角度而不是正好与道路平行这样至少能减少“道路正好落在 patch 边缘”这一坏情况的发生概率。6.3 小目标极密集场景当切图仍不够时怎么办如果目标极小且极密比如蜂群、鸟群、微小的太阳能板缺陷切图只能解决一部分问题。切图后目标在 patch 里的尺寸也许只有 10×10这对大多数检测器来说依然是超低分辨率目标。这时我一般会叠加两个策略一是引入超分辨率模块或 SAHISlicing Aided Hyper Inference的思路先切图再对每个 patch 做高分辨率重建让目标特征更清晰后再检测二是改造检测器的 anchor 和感受野让模型更适配小目标尺寸。切图在这里是基础组件但不能扛下所有。7. 工程落地要注意的额外细节7.1 存储与并行别让切图阶段成为 IO 瓶颈离线切图在工程部署时消耗的不仅是计算时间还有磁盘 IO。一个 1 万张的航拍数据集每张原图 50MB 左右切图后会生成几十万个 patch 文件平均每张几 MB总体积轻松突破 TB 级。这时 Patch 的存储格式就很重要了。我倾向于使用 LMDB 或 TFRecord 这类二进制容器统一打包而不是散落的小文件既能大幅减少 inode 占用也能让训练时的随机读取速度加快不少。如果推理阶段不想经过磁盘中转也可以所有 patch 在内存中管理利用 numpy 数组的切片操作批量截取。这个方案在单张图推理时最省时间但在批量化生产环境里内存占用会迅速膨胀建议按图处理、及时释放。7.2 可复现性和日志记录切图相关的超参数非常多patch_size、overlap、padding 方式、去重阈值、裁剪过滤比例、随机种子、正负样本比每一个都会影响最终精度。我前前后后吃了很多次亏现在做实验时一律把切图配置序列化成 JSON 文件和训练产物放在同一个实验目录里。格式大致是{ patch_size: 800, overlap: 150, padding_value: 114, min_visibility: 0.7, keep_area_threshold: 0.5, nms_iou: 0.5, train_crop_method: random_roi, positive_ratio: 0.7 }这套配置直接写进实验报告的参数表里。不要靠聊天记录或代码注释记忆时间一长真的会忘别人来问你为什么这个 patch 切得和别人不一样时拿 JSON 出来比什么解释都清楚。7.3 测试集切图和训练集保持一致性原则最后这点很关键测试或验证阶段使用的切图策略要和训练阶段尽量保持一致。如果训练时用 random_crop 在 800 的 patch 上训测试时给了一个 1280 的 patch模型的感受野和输入分布都变了效果很可能会打折扣。当然真实场景没有训练标签可以参考没法做到理论上的一致但至少应该保证模型见过的 patch 尺寸范围能覆盖测试时的输入范围。这也是为什么我在做正式评估的时候会专门多做一组“滑动窗口切图 合并”的流程和线上长一模一样而不是只靠原始分辨率的预测结果去评估模型。另外如果前期模型在整图上效果不佳先不要急着怀疑模型结构建议先把切图后的可视化和不切图的输出放在一起对比。很多时候问题的根源就在切图策略上模型是被冤枉的。收个尾一点实践体会做高分辨率航拍检测切图真不是“把大图切成小块”这么简单。它决定了模型能看见什么上下文、目标在小图里能保留多少语义信息、后端融合能否准确还原出全局坐标这些都会直接作用到最终的检测精度上。我碰过的项目里有不少看起来是模型能力不够、于是盲目换更大网络的结果回过来在切图优化上做点调整效果提升反而更明显。这大概就是这个策略最值得琢磨的地方。如果你也在做类似任务不妨先从可视化切图结果开始确认每一个目标都在某个 patch 里清晰完整再往下调模型——这个顺序别搞反了。
返回列表