机场目标检测数据集制作全流程:从遥感影像到模型训练 简介面向目标检测研究与实践的机场卫星遥感图片数据集收录了1000张1024×1024像素的可见光机场遥感图内容聚焦机场场景已按Pascal VOC格式生成对应XML标注可适配Yolo系列目标检测算法及其他检测框架用于训练、验证与精度对比。整个数据集封装为一个ZIP压缩包大小约469.42MB内含2000个文件其中JPG图片与XML标注各1000个另含1个TXT说明文件图片与标注一一对应且命名规范便于直接切分为训练集、验证集和测试集也免去格式转换的额外工作。目前已有865人学习使用常用于遥感图像识别、机场目标检测等研究场景。由于图像尺寸统一、标签清晰完整数据集能有效降低数据预处理成本既适合目标检测新手快速跑通完整流程也适合研究者进行单类别检测的算法评估、消融实验与模型微调是构建遥感目标检测项目的实用基础资源也可用于课堂教学演示。1. 机场目标检测数据集的三种原料遥感图、标注度和你的组织能力做遥感目标检测的人十有八九会在某一个晚上对着同一个尴尬想训练一个能识别机场的目标检测模型翻遍手头的资料和论坛找到的机场目标检测数据集不是只有几十张分辨率参差的小图就是类名乱到连“airport”“airplane”“runway”都分不清楚。标题里这套机场图片、机场卫星图、机场遥感图的数据集本质上把“训练一个能够看懂机场的模型”这件事拆成了三份原料值得用的遥感原图、干净统一的标注、以及一套能证明数据集质量合格的验证流程。它适合准备用 YOLOv8 训练自己数据集、或者准备用 MMRotate 跑 DOTA 这类旋转框方案的从业者——你缺的往往不是网络结构怎么写而是“机场”这个场景下那些可靠的正样本和负样本。2. 机场遥感图去哪找公开数据集的优缺点与地图影像的版权边界2.1 公开遥感数据集里的机场类目盘点直接复用还是二次标注对机场目标检测来说最省力的做法不是从无到有收集图片而是先翻一遍遥感目标检测领域已有的公开数据集。以 DOTA 系列、DIOR、FAIR1M 这类常用遥感目标检测数据集为例它们内部都包含机场相关对象。需要说明的是多数公开遥感数据集刻意分出的是“飞机”这类细粒度目标而“机场”往往是一个包含跑道、滑行道、停机坪、航站楼、机库和飞机的复合场景。也就是说你很难在某个数据集里直接下载一个名为“airport”的完整类别更多情况是数据集里存在若干包含机场的大图而你要在此基础上二次标注把“机场场景”重新定义成自己的类别体系。这里有一个选型问题是直接复用公开数据集的原始类别还是基于原图做自己的标注。我的建议是只要你的业务目标不是“检测飞机”而是“识别机场设施”就一定要做二次标注。原因很直白公开数据集里“airplane”这类别的包围框通常只框机身不含滑行道和廊桥训练出来的模型对“机场”的语义理解几乎为零。而机场目标检测真正有价值的判据是跑道、滑行道、停机坪这些标志性结构。直接复用类别的方案只适合做预训练热身不适合作为最终数据集的骨架。确定二次标注之后下一步是画清楚类别层级。常见做法是分成两层第一层是“机场区域”这个整体大目标第二层是“机场组成部分”这类附属目标。为什么要分两层因为遥感图分辨率有限同一张 1024 像素切图里跑道可能占满整幅图像而一架飞机可能只有 30 像素。用一个大类把跑道和飞机混在一起会让回归头的损失函数被大目标主导。分开两层之后可以给不同层级的类别分配不同的损失权重这是后面章节会展开的操作。2.2 地图影像初筛机场候选图的实操顺序本地没有现成遥感图时我一般会先用公开地图影像做候选图初筛。这是最接近“机场遥感图”字面含义的来源但也是最需要注意分寸的一步。实操顺序通常是这样的先确定目标机场清单然后在地图影像服务中逐一定位按固定缩放级别截取包含机场全域的图块最后把这些图块整理成待标注的候选集。选择影像源时我会优先看两个指标一是地面分辨率能否支撑目标识别机场跑道的宽度通常在 45 到 60 米如果影像分辨率低于 2 米每像素跑道边缘会糊成一条线二是影像时相是否新鲜同一个机场半年内的施工改造就会改变停机坪的外观。截取候选图时有一个很少有人提前注意的细节尽量把截图边界定在机场围界之外一到两公里。这样做的目的不是贪大而是给模型提供必要的“负上下文”。机场目标检测在推理时经常需要区分“机场内部”和“机场周边”周边农田、裸地、居民区看起来毫无机场特征但训练时加入这些负样本能显著降低模型在非机场区域产生误检的概率。我通常按一张 2048 像素见方的范围来截取保证跑道长度方向的完整同时让周边环境占一定比例。初筛阶段得到的是未标注的原始大图它们还不能直接进入训练流程。此时需要做一个去重操作因为地图影像服务在拼接截图时经常出现相邻图块有重叠区域的情况。去重我会用像素哈希的方式对每张图做缩放灰度化然后计算感知哈希两两比较相似度超过阈值的只保留一张。这个操作看起来基础却能在后面省下大量重复标注成本。2.3 版权边界与重复影像训练集和发布策略的取舍版权问题是机场遥感数据绕不开的边界说句实在话它比模型精度更容易让人翻车。地图影像服务提供的浏览影像其著作权通常归影像提供商或原始数据来源方所有。你可以用它做内部视觉算法验证例如在本地跑通 YOLOv8 训练自己的数据集但如果要把成品数据集打包发布、或者把模型作为商业产品交付就必须回到授权条款上重新评估。常见做法是训练阶段使用地图影像做效果验证最终发布时只保留公开遥感数据集中的影像以及自己采购或拍摄的合规影像。除了版权还有一个容易忽略的“重复影像”陷阱地图影像服务不同缩放级别下同一机场会以不同分辨率重复出现。筛图时如果不按机场维度去重训练集里就会出现“同一跑道在不同分辨率下的十几份副本”导致验证集和训练集高度相似测试指标虚高部署到新数据上精度骤降。这是遥感目标检测里最隐蔽的过拟合形式因为它不体现在 loss 曲线而体现在模型泛化能力的黑匣子里。所以我的习惯是每得到一个机场的候选影像就记录它的机场名称、影像日期、分辨率等级和来源。这三个字段组成一条元数据记录训练集、验证集、测试集按机场维度切分而不是按图片文件随机切分。这条原则后面介绍验证集时还要再强调一次。3. 把机场标注做成 YOLO 能吃的格式DOTA 坐标转换脚本与旋转框取舍3.1 标注规范先于格式机场类的命名与层级开始标注之前先把命名规范钉死否则后期再改类名会变成一场灾难。我的习惯是类别名统一用小写英文机场整体区域用airport跑道用runway停机坪用apron滑行道用taxiway航站楼用terminal飞机用airplane。类别 ID 的排序也值得讲究把大目标放在前面小目标放在后面例如0: airport、1: runway、2: apron、3: taxiway、4: terminal、5: airplane。这样做的好处是在可视化调试时ID 号越小越容易在回调函数里快速定位。标注工具的选择上目前常见做法是用 X-AnyLabeling 或 LabelMe 这类支持多边形标注的工具因为它们能导出 DOTA 风格的四点坐标。四点坐标与水平框的差别是机场数据格式转换里最关键的沟壑。为什么不用矩形框直接标因为跑道很少是正南正北走向。一条斜向 30 度的跑道用水平矩形框住会框进大量背景训练出来的回归头会不断尝试把这些背景“填满”导致该类的 PR 曲线一直上不去。但工具只是输出格式真正的规范在于“同一类目标如何标”。以跑道为例我会要求标注员把四条边压在跑道道面边缘而不是压在外侧草地边沿。以停机坪为例标注范围包含混凝土铺筑面和区域内固定廊桥但不包含临时停放的车辆。规范写得越细后续代码里需要做的清洗就越少。3.2 DOTA 四点坐标转 YOLO 水平框转换脚本与归一化细节DOTA 格式的标签文件里每个目标占一行内容是x1,y1,x2,y2,x3,y3,x4,y4,class_name,difficult。如果决定先用水平框方案快速跑通基线那么需要把四点坐标转换成 YOLO 需要的class_id cx cy w h归一化格式。下面这段脚本是我经常使用的转换骨架# dota_to_yolo.py # 把 DOTA 格式的 txt 转成 YOLO 格式按外包矩形取水平框 import os def dota_to_yolo(txt_path, img_w, img_h, class_map): out_lines [] with open(txt_path, r, encodingutf-8) as f: lines f.readlines() for line in lines: line line.strip() # 跳过 DOTA 头部字段 imagesource / gsd if not line or line.startswith(imagesource) or line.startswith(gsd): continue parts line.split(,) if len(parts) ! 10: continue # 偶数下标是 x奇数下标是 y xs [float(parts[i]) for i in range(0, 8, 2)] ys [float(parts[i]) for i in range(1, 8, 2)] cls_name parts[8] if cls_name not in class_map: continue x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 防止切图或标注误差导致坐标出界 x_min max(0.0, min(x_min, img_w - 1)) x_max max(0.0, min(x_max, img_w - 1)) y_min max(0.0, min(y_min, img_h - 1)) y_max max(0.0, min(y_max, img_h - 1)) bw x_max - x_min bh y_max - y_min if bw 1 or bh 1: continue cx (x_min x_max) / 2 / img_w cy (y_min y_max) / 2 / img_h w bw / img_w h bh / img_h cls_id class_map[cls_name] out_lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return out_lines这段代码的逻辑分三个层次解析 DOTA 的一行、计算外包矩形、归一化成 YOLO 坐标。值得注意的参数细节是class_map它必须是训练配置里的类别 ID 一一对应的字典例如{airport: 0, runway: 1, apron: 2}。如果脚本里和 YOLO 的data.yaml不一致会出现“标签类别和训练类别错位”的经典翻车模型 loss 降得很顺但所有预测框的类别都偏移一位。另一个细节是坐标出界裁剪。遥感大图切出来的子图里经常会有目标跨出图像边缘若不裁剪归一化后的cx、cy会出现大于 1 或小于 0 的值某些版本的检测框架会直接把这一行当成无效标签丢弃结果就是漏检。所以脚本里对x_min、x_max都做了裁剪同时过滤掉裁剪后面积过小的目标这个阈值可以根据业务调成 2 像素或 5 像素。3.3 斜向跑道与旋转框MMRotate 的适配思路用水平框转完 DOTA 之后你会发现斜向跑道的框特别肥大。本来跑道宽度只有 50 米斜着框出来的水平框却可能宽达 300 米。如果模型要检测的是停机坪上的飞机这种误差还能忍如果业务定位是“机场区域识别”水平框带来的背景噪声会明显拉低精度。这时候就该考虑旋转框方案了。MMRotate 是目前社区里比较成熟的旋转框训练工具它对 DOTA 格式有原生读取器不需要把四点坐标转成水平框。旋转框的标签格式通常是class_id cx cy w h angle其中angle的定义需要格外小心MMRotate 里的旋转框角度通常指长边与 x 轴正方向的夹角范围随具体版本有差异有的用弧度有的用角度制还有的遵循 OpenCV 的minAreaRect角度定义。我在初次使用时就因为单位不一致训练一整天后所有预测框全部偏转了 90 度把跑道识别成垂直方向的奇怪条带。从 DOTA 四点坐标估算旋转框参数可以用一个相对稳妥的几何做法取四个顶点的均值作为中心点然后通过 PCA 拟合主方向。这里直接给出简化的转换思路# dota_to_rotated.py # 从四点坐标估计旋转框中心、长宽和角度 import numpy as np def four_points_to_rotated(points): # points: [[x1,y1],[x2,y2],[x3,y3],[x4,y4]] pts np.array(points, dtypenp.float64) cx, cy pts.mean(axis0) # 去中心化后做主方向估计 centered pts - [cx, cy] cov np.cov(centered.T) eig_val, eig_vec np.linalg.eig(cov) # 最长特征向量对应长边方向 main_axis eig_vec[:, np.argmax(eig_val)] angle np.arctan2(main_axis[1], main_axis[0])这个脚本的意义不是替代成熟的旋转框标注转换库而是帮助你理解角度是如何从四点坐标里“估”出来的。PCA 估计在目标接近矩形时很稳定但如果四点标注带有明显透视形变估计出的长宽会有偏差。真正上线时我建议在 MMRotate 的 DOTA 数据读取器基础上写一个校验脚本把转换后的旋转框画回原图随机抽 50 张图肉眼检查确认长边方向和原始四点的主方向一致再开始训练。4. 小目标与类别不平衡切图重叠率、增广组合和正负样本配比4.1 滑窗切图窗口大小、步长与目标截断的取舍遥感大图通常不能直接塞进检测网络常见做法是先滑窗切图。机场遥感图的切图参数需要结合两类目标尺寸来定跑道这类大目标要求窗口足够大能框住跑道的大部分结构飞机这类小目标则要求步长足够小避免目标正好落在相邻两块的缝隙里。DOTA 系列在切图时常用的窗口是 1024 像素步长 824 像素相当于重叠率约 20%。我自己的经验是机场场景建议把重叠率提高到 30% 到 50%尤其是有大量飞机小目标时。切图与标签坐标需要同步偏移否则标了等于白标。下面是一个带重叠控制的切图片段# slice_images.py # 滑窗切图同步偏移坐标保留被截断但仍有效的目标 import cv2 def slice_with_boxes(image, boxes, crop_size1024, stride768): h, w image.shape[:2] slices [] y 0 while y crop_size h: x 0 while x crop_size w: crop image[y:y crop_size, x:x crop_size] moved [] for box in boxes: # box: [cx, cy, bw, bh, cls] 原始大图坐标系 bx_min box[0] - box[2] / 2 by_min box[1] - box[3] / 2 # 交点面积占原目标面积的比例 ix max(0, min(bx_min box[2], x crop_size) - max(bx_min, x)) iy max(0, min(by_min box[3], y crop_size) - max(by_min, y)) inter ix * iy area box[2] * box[3] if inter / area 0.3: continue # 只剩一小块的直接丢弃 nx_min max(bx_min, x) - x ny_min max(by_min, y) - y nx_max min(bx_min box[2], x crop_size) - x ny_max min(by_min box[3], y crop_size) - y new_cx (nx_min nx_max) / 2 new_cy (ny_min ny_max) / 2 new_bw nx_max - nx_min new_bh ny_max - ny_min moved.append([new_cx, new_cy, new_bw, new_bh, box[4]]) slices.append((crop, moved)) x stride y stride return slices参数stride小于crop_size的部分就是重叠区域。重叠率越高被截断的目标越少但训练样本数会成倍增加训练时长也跟着涨。机场这类场景里我通常先统计目标的宽度和高度分布再反向定窗口大小。如果 90% 的飞机目标边长为 30 到 80 像素窗口 1024 像素就足够跑道很长但检测跑道不一定需要一眼看完整条窗口里能看到一段道面加标记线就可以。所以切图参数不是越大约好而是匹配目标尺寸分布。4.2 遥感增广组合镜像旋转和噪声扰动的搭配遥感影像有一套自己的增广偏好和自然图像不完全一样。机场的目标姿态受跑道方向影响存在大量任意角度旋转因此在训练阶段把旋转增广打开是很必要的。常见组合是随机水平翻转、垂直翻转、90 度旋转、180 度旋转再加上亮度对比度扰动和高斯噪声。这里有一个细节机场遥感图大多是俯视图垂直翻转和水平翻转不会破坏语义这与自然场景下人像颠倒完全不同所以四种翻转都可以放心用。任意角度的旋转增广需要谨慎。如果直接用 OpenCV 旋转整张图再转标注框会产生黑色边框而黑边在模型看来是强特征会让训练出的模型在真实遥感影像上产生奇怪的响应。解决办法是把旋转后的图以中心裁剪成原尺寸但这样会损失边缘信息。我一般只在两类情况下使用任意角度旋转一是目标本身有旋转敏感性比如飞机二是切图前先旋转整个大图切图后黑边自然被裁掉。噪声扰动方面高斯噪声的强度要控制在目标边缘仍清晰可见的范围内。机场跑道在遥感影像上呈现的是几何边缘过度降噪或加噪声都会把细小的标线抹掉。反而是遥感影像里常见的云层遮挡值得专门模拟做法是在增广阶段用低透明度的高斯斑点叠加在图上模拟薄云这比盲目增加高斯噪声更贴近真实场景。4.3 小目标与类别不平衡正样本配比和难例挖掘机场数据集的类别不平衡有两个层次一是机场整体大目标和飞机小目标之间的尺度不平衡二是正样本机场与负样本非机场之间的实例数量不平衡。对于尺度不平衡常用的缓解方式是在损失函数里按目标面积调整权重或使用多尺度训练。具体到代码层面如果用的是 YOLOv8可以在训练配置里开启 mosaic 增强让飞机小目标与其他目标在同一个 mosaic 图中多次出现。如果用的是 MMRotate则可以在 RoI Head 的采样器里调整正负样本的 IoU 阈值。对于实例数量不平衡要分情况讨论。如果目标是机场整体检测那么一张图里通常有跑道、停机坪、滑行道这些结构互相之间存在空间共现关系模型可以从这些共现中学习如果目标是飞机级别的细粒度检测那么机场大图里可能只有几架飞机负样本占比极高此时建议把非机场区域单独切出来作为负样本加入训练而不是依赖检测器自身的背景分类来消化。难例挖掘是机场数据里性价比很高的一步常见做法是先用初版模型在训练集上做推理找出被误检为机场的负样本区域例如形状规则的大型厂房、体育场的椭圆跑道把这些区域追加到训练数据里。第一次做难例挖掘时我总会发现一些意想不到的硬负例集装箱堆场的条纹屋顶、高铁站的弧形雨棚它们在可见光遥感图里和机场设施确实有相似之处。这类负样本比单纯堆正样本更能提升模型在实战中的可用性。5. 机场数据集避坑坐标偏差、漏检翻车和重复影像的排查记录5.1 现象3 通道图像里混入 16 位单波段loss 震荡不收敛训练刚起步loss 就开始剧烈震荡验证集上的 mAP 始终在个位数徘徊。检查训练脚本和模型结构一切看起来正常后来排查数据读取才发现一个地物波段的 16 位 TIF 影像被当成普通 8 位图读入像素值范围高达 0 到 65535直接喂进网络后激活值异常放大。原因是遥感数据预处理没有统一所有影像的位深和通道数。解决办法是在做数据集时统一转成 8 位三通道并做线性拉伸或百分比截断拉伸让直方图分布尽量一致再进入训练流程。这条建议写进我所有的数据集制作流程里比任何调参都管用。5.2 现象飞机框变成了“蝴蝶结”训练指标却正常训练整体没有崩溃loss 也在下降但可视化预测时发现飞机框不是规则的矩形而是两边粗中间细的菱形看起来像蝴蝶结。这种现象通常出现在把 DOTA 四点坐标转水平框的过程中如果某些标注点的顺序不一致部分标注按顺时针部分标注按逆时针那么用min和max取外包矩形时四个点的跨度会变得很大产生一个被撑开的斜向框。解决方法是写一个检查脚本计算每一条标注四点的叉积方向强制统一成同一绕序绕序不一致的标注直接标记为异常并打回重新标注。训练指标正常是因为 loss 在大量正常标注的统计平均下仍然下降但误检部分在可视化阶段才会暴露。5.3 现象切图后大量 GT 消失召回率被低估切完图开始训练前几个 epoch 的召回率一直在低位徘徊甚至低于不切图的基线。逐张检查切图结果后发现设定的切图重叠率太低大量小目标飞机正好落在相邻切图的接缝处每一块切图里只保留目标面积的 20% 以下于是被过滤逻辑当成无效目标丢弃。解决方法是重新统计目标最小面积把保留阈值从 30% 降到 10% 到 15%同时把切图重叠率从 20% 提高到 40%。这个坑的特点是训练依然能进行但最终决策边界会被迫依赖目标局部特征导致在完整目标上推理时反而表现不稳。5.4 现象验证集准确率虚高上线却误检频繁训练过程中验证集 mAP 一路走高接近 0.9但把模型部署到新采集的机场影像时误检和漏检都很频繁。排查发现训练集、验证集、测试集在被构建时按文件随机划分而同一机场的多张遥感图被拆到了不同分区里同一机场在不同分辨率下的影像纹理高度相似验证集因此变成了一场开卷考试。解决方法是建立元数据记录后按机场名称作为分组键划分数据集保证任何一个机场的全部影像只出现在训练、验证、测试之一中绝不跨区。这条规则做起来极简单但这正是决定模型是否能够泛化到新机场的关键。5.5 现象同一机场出现在训练和验证集结果“作弊”进一步扩展 5.4 的教训即使按机场名称分组仍然可能存在“时间泄漏”验证集里的影像来自 2023 年训练集里的同一机场影像来自 2015 年。机场的跑道结构通常变化不大模型在训练时已经见过该机场的“骨架”验证阶段只需要识别相似纹理而真实业务场景里完全是陌生机场。严格的做法是按“机场名称 影像时相”双重分组如果一个机场只有新旧两期影像优先把它们同时归入同一个分区或只保留一期避免模型依赖时间一致性做出判断。这是机场遥感数据领域最血泪的教训我几乎每次接手别人交付的数据集都要先跑一遍这个泄漏检查。6. 用验证脚本量化机场数据集从 IOU 曲线到漏检热力图6.1 一个小脚本量化漏检分布数据集的最终质量不能只凭 mAP 一个数字判断。我的习惯是训练出一个基线模型后用一个独立的验证脚本统计漏检和误检的空间分布。脚本读取模型预测结果和 GT 标签计算每个 GT 是否被匹配并记录未匹配 GT 的中心坐标然后用二维直方图把这些漏检点的空间位置画成热力图。如果热力图上漏检集中在图像边缘说明切图策略有问题如果集中在停机坪区域说明飞机小目标的训练样本不足如果集中在跑道两端说明标注时漏掉了跑道入口区域的标记线。# inspect_latent.py # 统计 GT 中未被任何预测匹配的目标并输出漏检热力图 import numpy as np import matplotlib.pyplot as plt def collect_missed_gt(gt_boxes, pred_boxes, iou_threshold0.5): # gt_boxes: [N, 5] cx, cy, bw, bh, cls # pred_boxes: [M, 5] cx, cy, bw, bh, cls missed [] for gt in gt_boxes: matched False for pred in pred_boxes: if gt[4] ! pred[4]: continue if compute_iou(gt, pred) iou_threshold: matched True break if not matched: missed.append(gt[:2]) # 记录漏检目标的中心坐标 return np.array(missed) if missed else np.empty((0, 2)) def plot_miss_heatmap(missed, img_w1024, img_h1024): plt.figure(figsize(6, 6)) plt.hist2d(missed[:, 0], missed[:, 1], bins32, range[[0, img_w], [0, img_h]]) plt.xlabel(x) plt.ylabel(y) plt.colorbar() plt.savefig(miss_heatmap.png, dpi150)这个脚本用到的compute_iou是通用 IoU 函数逻辑不复杂但需要注意两个版本之间的 IoU 口径一致有的代码用交集面积除以并集面积有的除以预测框面积。我在统计漏检率时统一用经典 IoU 阈值 0.5而统计定位精度时会额外再跑一次 0.75 的阈值观察高阈值下性能是否剧烈衰减。6.2 把漏检热力图当成数据集的下一步改进入口漏检热力图揭示的信息比任何训练日志都更直接。如果热力图中漏检集中在图像中央偏下的位置说明切图时步长过大中央区域出现了重复覆盖不足如果漏检沿着一条斜向直线分布那很可能是斜向跑道的旋转框角度和水平框转换发生了系统性误差。遇到前者我会回到切图参数调高重叠率重新生成数据集遇到后者我会放弃水平框方案改用 MMRotate 的旋转框重训。这套验证流程我习惯放在每一轮数据集迭代之后执行而不是等模型训完才开始。最早我犯过的一个错误是把数据集直接交给训练脚本跑完一轮只看 mAP 数字数字漂亮就觉得万事大吉结果换一个机场影像立刻暴露问题。后来把漏检热力图纳入流程后相当于给“黑匣子”开了一扇窗数据集有没有偏、标注有没有漏、切图有没有截断热力图都会用最直观的方式告诉你。机场目标检测数据集这件事做到后面拼的不是有多少张图而是每一张图背后的元数据是否干净、标注是否规范、切图策略是否与目标尺度匹配。希望这些流程和踩坑记录能帮你把数据集这一关过得稳一点至少在你深夜盯着 loss 曲线怀疑人生的时候能有一个确定性的排查顺序可以依赖。提示验证脚本里的compute_iou函数在边界条件较多时容易写错建议直接复用你训练框架里已经验证过的 IoU 实现避免“用错误的验证脚本衡量正确的结果”。本文还有配套的精品资源点击获取