1. 项目概述与技术选型分析
1.1 为什么需要航拍视角的人体检测数据集
先从一个实际场景说起:学校操场面积大、人员密度高,传统地面摄像头受安装位置和视角限制,很难在运动会、早操、课间活动等场景下完整覆盖全场。无人机航拍则能以俯视视角获得大范围画面,一台设备就能看到整片操场。但航拍视角的人体检测和常规监控画面完全不同,背后是几个非常实际的痛点。
地面视角下行人通常占据画面的较大面积,人脸、四肢轮廓相对清晰,成熟的COCO预训练模型直接拿来用也能有不错的效果。而航拍画面里的人体往往只有几十甚至十几个像素,何况还有俯拍造成的“顶视”形态变化——人的头部和肩膀是主要可见区域,四肢被身体遮挡。再加上操场跑道的白色标线、绿色草坪与人体在颜色上天然接近,模型很容易把身体轮廓和背景混在一起。
这些差异意味着拿普通行人检测数据集训练的模型,迁移到航拍场景后效果会明显下降。所以构建一个专门的航拍校园操场人体检测数据集就很有必要了。我们做的这个项目,定位就是用YOLO系列算法为校园操场场景建立一个可落地的检测解决方案。数据集包含多角度、多高度、多光线条件下的操场人体样本,覆盖早操、体育课、自由活动等真实场景。这篇文章我会把从数据采集、标注、训练到部署的全过程详细记录下来,给正在做航拍检测、小目标检测或想自己搭建数据集的读者一个可直接参考的完整路径。
1.2 检测算法选型:为什么用YOLO而不是其他方案
做检测算法选型时,我认真比较了几条技术路线。两阶段检测器(如Faster R-CNN)精度高,但推理速度慢,航拍场景往往需要实时反馈,先排除。DETR这类基于Transformer的方案精度不错,但在小目标密集场景下训练成本高、部署复杂,对硬件要求也比较苛刻。最终敲定YOLO系列,核心原因有三个:
- 速度与精度平衡。YOLO把目标检测当作单次回归问题,一次前向推理直接输出边界框和类别概率,在嵌入式设备上也能跑到实时帧率。
- 生态成熟。从YOLOv5到YOLOv8、YOLOv9、YOLOv10,社区教程、预训练模型、部署工具链都非常完整,遇到问题几乎都能搜到解决方案。
- 小目标潜力。YOLO在输入分辨率、Anchor设置、多尺度特征融合上的自由度很大,可以通过调整P2层、增大输入尺寸来提升小目标召回率。
我们最终选用YOLOv8作为主力模型。它在Backbone中使用了C2f结构,Neck部分沿用FPN+PAN结构,耦合头换成了解耦头,分类和回归分别预测,收敛更稳定。训练时使用TaskAlignedAssigner正样本分配策略,损失函数综合了分类损失(BCE)和回归损失(DFL+CIoU),整体对小目标更友好。整个项目实测下来,效果比自己魔改的旧版YOLOv5要稳不少。
1.3 数据集的边界定义与整体规模
做数据集的第一步不是急着拍摄,而是把场景边界定义清楚。我们锁定的是“校园操场”这一具体场景,包含跑道、足球场、篮球场三个子区域,人员活动形态涵盖列队站立、跑步、跳跃、打篮球、踢足球等。飞行高度控制在80米到120米之间,这个高度既能覆盖整个操场,又能让单个人体保留足够像素,一般目标像素尺寸在24x24到80x80之间,正好落在小目标检测的典型范围。
同时我们限定了采集时间段。北方操场上午和下午的光照角度差异很大,人在地面上的投影长度会影响标注边界的主观判断,所以我们在上午9点到11点、下午2点到4点两个时段分批次采集,避免低角度强光下的过曝和超长阴影。最终数据集包含原始图片约2860张,其中有效标注图片2470张,累计标注人体实例约12000个,平均每张约4.9个目标,符合操场场景的稀疏到中密人员分布。原始图片分辨率统一为4580x3056,训练时按比例缩放处理。
2. 数据采集:飞行参数、设备配置与实操记录
2.1 航拍设备与关键飞行参数
设备我们用了大疆Mavic 3行业版,2000万像素4/3 CMOS传感器。这个级别的好处是单张图片信息量大,后期通过切片训练能保留足够的纹理细节。如果你手里的设备是Mini系列,像素稍低也没关系,只要保证飞行高度和拍摄间距合理就行,关键在于RAW格式输出和固定光圈。
参数设置上,有几个数值值得记录。感光度控制在ISO 100到400之间,操场光照充足时默认ISO 100,画质最干净;光圈固定在f/5.6,这个光圈下镜头解析力最均衡,没有全开光圈时的边缘软化,也没有小光圈衍射造成的细节损失;快门速度要保证安全值,我们设在1/800秒以上,空中悬停时飞行器本身有轻微震动,快门太慢容易糊。存储格式必须选JPEG+RAW双格式,训练时用JPEG,如果后期要做精细的亮度校正,RAW还能翻一次工。
飞行高度和云台角度是航拍检测项目的核心参数,直接影响目标在图像中的尺度。我们用80米和120米两个梯度采集,云台俯角锁定为-60度,这个角度下人物朝向地面的“头部特征”最明显,比垂直正射更容易辨别人员位置。水平和垂直重叠率也都设置在70%左右,给后期拼接和关键帧筛选留出余量。
2.2 航线规划与场景覆盖方案
操场航拍不能漫无目的地飞一圈就完事。我们用了DJI Pilot 2的航线规划功能,按“井字形”网格规划航线,每条航线间隔30米,保证相邻航拍图片之间有足够的重叠。对于标准400米跑道操场,单次完整采集约需要8条航线,每条航线拍摄10到12张,整轮采集约为90到100张有效图片。一天分两个时段各飞一轮,一次作业能拿到约200张原始图像。
场景覆盖的策略是先画底图再派生子场景。用飞行高度120米、井字航线完成全局底图采集,获得操场全貌;然后切换到高度80米,沿跑道边线、足球场中线、篮球场边界分块采集局部细节。这样既保证了大范围覆盖,又让目标的尺度变化更丰富,模型能同时适应“很多小目标”和“较少大目标”两种分布。我们在操场不同的活动场景里也做了抽样,早操列队时人员密集且规则排列,自由活动时人员散乱分布,这两种状态下的检测难度完全不同,缺失任何一种都会让模型在实际使用时露馅。
2.3 数据清洗:跑掉了一批“废片”
数据采集回来不能直接进标注流程,清洗这步很关键。我们从3个轮次共采集的原始素材约600张中,筛出了2860张可用图片,意味着筛选比例很高,大量素材被淘汰。笼统地说,废片有几类:镜头起雾造成的整体朦胧、对焦失败导致的边缘发虚、云台转动时产生的动态模糊、快门速度过低带来的运动拖影,以及本身看着清楚但画面里恰好没有人体的空镜头。
筛选过程我用了一个比较粗暴但有效的方法:先按拍摄时间和航线编号分组浏览缩略图,把明显模糊、过曝、欠曝的图片直接移出,然后对剩余图片做一次“像素锐度统计”——通过计算图像拉普拉斯方差(Laplacian variance)来量化清晰度。拉普拉斯方差值低于50的图片判定为模糊帧,直接剔除。这一步听起来繁琐,但实际用脚本批量处理,几分钟就跑完几百张图,比人眼一张张看快得多。
[python] import cv2 import numpy as np import os
def is_image_blurry(image_path, threshold=50.0): img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) if img is None: return True # 拉普拉斯算子计算图像二阶导数,方差反映边缘清晰度 laplacian_var = cv2.Laplacian(img, cv2.CV_64F).var() return laplacian_var < threshold, laplacian_var
for filename in os.listdir('raw_images'): if filename.lower().endswith(('.jpg', '.jpeg', '.png')): path = os.path.join('raw_images', filename) blurry, score = is_image_blurry(path) if blurry: os.rename(path, os.path.join('reject', filename)) else: os.rename(path, os.path.join('accept', filename)) [/python]
这段脚本把“是否过模糊”的判定变成可复现的量化标准。清洗完的图片做过一次统一的色温和亮度校准,用Lightroom批量导出为JPEG,色彩文件统一到sRGB,避免不同航次飞机的白平衡差异影响后续模型训练。
2.4 小目标增强:原图切片策略
航拍原图动辄四五千万像素,直接把整张图缩放成640x640喂给YOLO,人体目标会被压缩得只剩下几个像素,几乎不可能检测出来。我们采用了滑动窗口切片策略:把4580x3056的原图切成1280x1280的瓦片,切片之间保留15%的重叠,防止人体目标被框线切断。每个瓦片再缩放到训练输入尺寸,这样单个人体的像素尺寸在缩放后仍然能保持相对合理的大小。
切片时要记录每个瓦片相对于原图的偏移量。因为模型最终要在整张航拍图上做检测时,需要在模型推理输出的坐标上加上偏移量,才能映射回原图坐标系。为了方便,我们是直接按8x8网格把原图均匀切成64张子图,然后用这些子图去做训练。实际检测时,把指定区域裁切后输入模型即可,不做跨图拼接,所以偏移量只用来过滤边缘目标。
3. 数据标注:规范制定、工具选型与质量审核
3.1 标注工具选择与配置
标注工具有很多选择,LabelImg是老牌开源工具,界面简单但功能略显单薄;Labelme灵活但更偏向多边形标注;X-AnyLabeling集成了很多自动化能力,用起来最省力,但学习成本高一些。我们最终选用了LabelImg配合自动保存模式,理由很直接:团队标注人员培训成本最低,VOC格式输出可以直接通过脚本转成YOLO格式,不需要额外插件。
安装和配置上,LabelImg依赖PyQt5和libpng等库。如果本机Python环境比较乱,我建议直接用conda建独立环境,避免和系统环境冲突。启动命令一行就够了:
pip install labelimg labelimg打开后把Predefined Classes设置为“person”一个类别,标注框颜色设为醒目色,勾选Auto Save mode,标注速度会快很多。工作目录设置为数据集的JPEGImages目录,XML输出到一个专用的Annotations目录。
3.2 标注规范:这些细节决定模型上限
标注规范的制定要比选工具更重要。我们给所有标注人员下发了一份详细规范,核心条款只有几条但特别容易执行偏:
- 目标定义:所有完整或部分可见的人体都算一个目标,包括跑步中的运动员、球场上的队员、看台上的观众。
- 遮挡处理:人体被树木、灯柱、其他人遮挡时,只要可见部分超过全身的30%,就按可见部分标注边界框,不能为了让框更“美观”而外扩或内缩。
- 边界裁剪:人物在图像边缘时,框可以超出图像边界吗?不能。框必须严格裁剪到图像内部,且不能把完全在图像外的人体标进去。
- 密集场景:早操列队时人体挨得很近,每个人必须独立成框,不能两个目标共用一个框。
- 极小目标:目标像素小于10x10时放弃标注。这个阈值是测试过的,更小的目标连人工都难以辨清,YOLO训练时反而会引入噪声。
- 备注标记:模糊、严重遮挡、部分出界的目标打上“hard”标记,训练时通过超参数决定是否参与损失计算。
这套规范看起来简单,但真正执行时最难的是“可见部分30%”的判断。标注人员经常会把只露出半截身体的人漏标,或者把背包、阴影标成人。我们在每天标注结束后随机抽检10%的图片,由复核人对照规范逐条检查,发现漏标和错标要求即时修改,每周再统计一次各类错误的占比,集中做一次规范培训。
3.3 VOC格式转YOLO格式的脚本化处理
LabelImg默认导出VOC格式的XML文件,YOLO训练需要的是每个图片对应一个txt文件,每行是:
class_id x_center y_center width height这里注意,YOLO格式里的坐标都是归一化到0到1之间的,框的中心点坐标和宽高都除以图像宽高。转换脚本本身不复杂,但有几个坑:XML里的坐标原点是左上角,宽高是绝对像素值;归一化时如果目标在图像边缘,除以宽高后可能出现大于1的数,需要做截断处理。下面是我们实际使用的转换脚本:
[python] import xml.etree.ElementTree as ET import os
classes = ['person']
def convert_voc_to_yolo(xml_path, output_dir, img_width, img_height): tree = ET.parse(xml_path) root = tree.getroot()
size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) yolo_lines = [] for obj in root.iter('object'): cls_name = obj.find('name').text if cls_name not in classes: continue xml_box = obj.find('bndbox') x_min = float(xml_box.find('xmin').text) y_min = float(xml_box.find('ymin').text) x_max = float(xml_box.find('xmax').text) y_max = float(xml_box.find('ymax').text) # 坐标截断,防止边界目标越界 x_min = max(0.0, x_min) y_min = max(0.0, y_min) x_max = min(img_w, x_max) y_max = min(img_h, y_max) if x_max <= x_min or y_max <= y_min: continue box_w = x_max - x_min box_h = y_max - y_min x_center = (x_min + x_max) / 2.0 / img_w y_center = (y_min + y_max) / 2.0 / img_h norm_w = box_w / img_w norm_h = box_h / img_h class_id = classes.index(cls_name) yolo_lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}") xml_name = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(output_dir, xml_name + '.txt'), 'w') as f: f.write('\n'.join(yolo_lines))遍历所有XML文件执行转换
xml_dir = 'Annotations' yolo_dir = 'labels' os.makedirs(yolo_dir, exist_ok=True)
for xml_file in os.listdir(xml_dir): if xml_file.endswith('.xml'): convert_voc_to_yolo(os.path.join(xml_dir, xml_file), yolo_dir, 4580, 3056) [/python]
转换后一定要抽查结果。我们写了一个可视化脚本,读取txt文件和原图,用OpenCV绘制边界框并保存到验证目录,人眼检查了几十张图片的框和人体吻合度。这一步看似多余,但确实发现过XML坐标解析错位的案例,原因是LabelImg某些版本导出的bndbox标签大小写不一致,正则匹配时需要统一转小写。
3.4 数据集划分:按飞行架次切分而不是随机打散
数据集的训练集、验证集、测试集划分工作需要格外谨慎。如果简单地随机打散,同一架次连拍的图片会被同时分到训练集和验证集,验证效果会虚高,因为模型其实已经见到了“邻居”图片。这种问题叫数据泄漏,在航拍项目中特别常见。
我们的做法是按飞行架次切分。每条航线的连续序列作为一个整体单元,大约20到30张图片,按7:2:1的比例分配:整个飞行架次要么进入训练集,要么进入验证集或测试集。这样测试时模型遇到的是完全没见过的拍摄角度和曝光条件,指标更接近真实部署表现。最终划分结果是训练集1730张、验证集495张、测试集245张,图片之间没有同源交集。
4. YOLO训练实战:环境配置、超参数调优与训练曲线解读
4.1 训练环境配置与预训练权重选择
训练环境我们用了实验室的两块NVIDIA GeForce RTX 3080Ti,单卡24GB显存,CUDA 11.8,PyTorch 2.0.1。ultralytics框架对YOLOv8的支持最完善,直接pip安装就能用。如果你用的是V100或A100,显存更大,可以开更大的batch size,训练会更快收敛,但整个流程逻辑是一样的。
预训练权重选择上,我们没有从零开始训练,而是使用了YOLOv8s的COCO预训练权重。虽然COCO数据集的场景和航拍差距不小,但模型的Backbone已经学到了一定的底层特征,比如边缘、纹理、颜色分布。微调时,这些特征能帮助模型更快适应新数据集,收敛速度要快得多。预训练权重从ultralytics官方GitHub release下载,文件不大,几十MB而已。命令很简单:
yolo detect train data=config.yaml model=yolov8s.pt epochs=150 imgsz=640 batch=16 device=0,14.2 配置文件、图像尺寸与超参数设定
YOLOv8的数据配置用YAML文件完成,核心内容是指定训练、验证、测试图片目录和类别数量。我们的配置文件名是airport_person.yaml:
path: /home/user/campus_airperson_dataset train: images/train val: images/val test: images/test names: 0: person训练超参的设定需要结合航拍小目标的特点来思考。关键几个参数:
- 输入图像尺寸:640。用了1280x1280切片图像缩放而来的训练图,因为原图太大,1280只是中间尺寸,实际训练输入还是640。目标在场景中的相对尺寸没有因为切片而变小,所以效果是可行的。
- 批次大小:16。双卡每卡8张,刚好能在3080Ti上维持合理显存占用。
- 训练轮次:150。这个数据集不算大,150轮足够收敛,再多容易过拟合。
- 优化器:AdamW,初始学习率0.0005,权重衰减0.0005。
- 数据增强:在线增强是YOLO的强项,保留Mosaic增强、随机翻转、随机亮度对比度扰动、HSV色域扰动,关闭了旋转和透视增强,因为操场的“水平地面”是一个强先验,旋转增强会破坏场景合理性。
还有一个值得注意的超参数是anchor scale。YOLOv8有自动Anchor生成机制,但航拍人体的小目标数量多,可以用yaml文件里的anchor相关参数做微调。我们没有手动干预,实测默认参数足矣,因为目标的绝对尺度范围比较稳定,不像通用目标检测数据集那样跨度极大。
4.3 训练日志跟踪与损失函数曲线解读
训练过程中要盯的指标不只是最后的精度,还包括每条曲线的收敛趋势。YOLOv8日志把box_loss、cls_loss、dfl_loss分开输出,训练轮次进行到一半时,几条曲线会逐渐分离。
box_loss衡量边界框回归误差,在小目标场景下它的值天然会偏大,因为同样几个像素的偏差,对20x20的目标来说已经是10%的框位偏移。训练曲线下降的速度比常规行人检测慢,这是小目标学习的正常表现,不用慌张。cls_loss反映分类置信度误差,降到0.02以下说明分类基本清晰。
我习惯用Ultralytics自带的训练曲线图,也推荐同时用TensorBoard实时盯验证集上的PR曲线。额外的经验是训练过程中如果看到验证损失在第60轮之后开始上升而训练损失还在下降,就是过拟合信号,应该减少轮次或加大数据增强力度。我们这次的数据集规模适中,在第150轮时验证集的mAP50已经趋于平缓,没有再往上调轮次。
4.4 训练中的常见坑:BN崩溃与大batch抖动
训练中遇到过最典型的问题是BatchNorm统计量偏移。在batch size设置得比较小且使用了Mosaic增强时,BN会对当前batch的数据分布异常敏感。症状表现是训练到中途,loss突然暴涨,快速发散。我们第一次遇到时还以为是学习率设置出错,排查了半天才发现是BN问题。
解决的方式有几种:第一种是把batch size调大,让BN统计量计算更稳定;第二种是减少Mosaic的拼接强度;第三种是在训练开头冻结Backbone的前几层,让顶层随机初始化部分先收敛。我们最后采用了batch size 16同时把YOLOv8的Mosaic比例从默认1.0降到0.8,问题明显缓解。如果你用的是RTX 3090之类的24GB显卡,batch size能上32的话,这个坑基本不会踩到。
另一个小坑是学习率warmup的设置。YOLOv8默认有3轮warmup,前3轮把学习率从很小的值线性升到设定值,这是为了让模型权重在初始化阶段不要剧烈波动。如果数据集比较小,warmup可以延长到5轮,效果会更稳。但注意不要过度延长,否则前期训练速度会很慢。
5. 模型评估、专项测试与部署落地
5.1 指标评估:mAP50、mAP50-95与混淆矩阵
训练完成后,我们先用测试集做了一次全面评估。测试集是模型在训练过程中完全没见过的245张航拍图片。主要指标如下:
| 指标 | 数值 |
|---|---|
| Precision | 0.881 |
| Recall | 0.836 |
| mAP50 | 0.902 |
| mAP50-95 | 0.614 |
| 推理耗时(TensorRT FP16, Jetson Orin Nano) | 22ms/帧 |
mAP50达到0.9,说明在常规交并比阈值下模型能找到绝大多数人体目标。mAP50-95是0.614,和mAP50的差距主要来自小目标:航拍画面里很多人只有二三十个像素,边界框和真值的IoU很难达到0.75以上,这是小目标检测的普遍现象,不必过于焦虑。
混淆矩阵展示的是分类层面的错误分配。因为类别只有人这一类,真正重要的是“漏检”和“误检”之间的平衡。测试集上假负例(漏检的人体)主要出现在两类:一是人员密集叠加,前后遮挡严重;二是目标在画面边缘,只露出半身。假正例则集中在阴影边缘、白色跑道标线上,说明模型还是学到了一点“人形形状”之外的颜色线索。
5.2 专项测试:按目标像素尺度拆分精度
常规指标分析会把大小目标混在一起,看不出模型到底在小目标上表现如何。我额外做了一个专项统计:把测试集里所有标注真值按像素大小分为四档,分别计算各档的召回率:
| 目标尺寸(像素) | 目标数量 | 召回率 |
|---|---|---|
| 10~20 | 286 | 0.728 |
| 20~40 | 1654 | 0.841 |
| 40~80 | 976 | 0.892 |
| 80以上 | 214 | 0.931 |
结果很直观:10到20像素的极小目标召回率只有72.8%,是模型的主要短板。这不是YOLO自身的缺陷,而是信息量不足的物理极限——一个15像素的人体在图像中只有不到500个有效像素,要从这些像素里分辨出头肩轮廓实在有限。要改善这部分,可以尝试把切片尺寸进一步增大,使用P2输出层,或者引入额外的超分辨率模块,但这些操作都会带来推理速度折损,要根据实际使用场景权衡。
5.3 导出ONNX与TensorRT部署
训练完成后,要把模型从PyTorch格式导出为可部署的格式。我们目标部署平台是Jetson Orin Nano和RK3588,两者都偏好TensorRT或RKNN格式。导出命令很简洁:
yolo export model=runs/detect/train/weights/best.pt format=onnx opset=12 simplify=True yolo export model=runs/detect/train/weights/best.pt format=engine device=0 half=TrueONNX导出时要注意dynamic batch size的设定。航拍场景的输入尺寸固定为640x640,不需要动态输入,关闭动态轴可以显著减少转换难度。转换为TensorRT引擎时,用FP16精度。FP16对精度影响很小,在Jetson上能带来约一半的推理加速。实测3080Ti上PyTorch的推理延迟约8ms,TensorRT FP16后约4.5ms;Jetson Orin Nano上FP16约22ms,单帧延迟完全够用,实际部署时视频流的帧率不需要太高,5到10帧/秒就能保证监控反馈的及时性。
5.4 边缘端部署要点
部署在边缘设备上,还有几个性能优化点值得分享。TensorRT引擎是跟GPU架构绑定的,在Orin Nano上生成的engine不能在另一台设备上直接加载,必须重新转换。批处理方面,航拍单帧画面滑窗切片后多路输入模型,可以利用批量推理同时处理多个1280切片,吞吐量明显提升。如果做视频流检测,建议用DeepStream或GStreamer框架管理流解码和渲染,而不是自己写循环读图,能省不少CPU资源。
内存管理也有讲究。Jetson平台默认的GPU内存上限较低,用tensorrt推理前要设置好NVMM内存池。我们在实际部署时遇到过一次推理速度从22ms掉到200ms的诡异问题,排查后发现是CPU和GPU之间频繁拷贝数据把带宽打满了,改成批量化推理后恢复正常。这类问题在边缘部署时特别常见,第一篇就建议把数据pipeline单独拎出来分析。
6. 踩坑记录与问题排查速查表
6.1 真实遇到过的坑
整个项目走完,整理一张问题排查速查表,方便后来人对照:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 训练中loss突然爆炸 | BatchNorm统计量不稳定 | 增大batch size、降低Mosaic比例、延长warmup |
| 模型漏检小目标 | P2层缺失或输入尺寸过小 | 提高输入分辨率、启用额外特征层、使用切片推理 |
| 验证集指标虚高 | 数据集切分未按飞行架次隔离 | 按架次切分训练/验证集,禁止随机抽样 |
| 边界框预测偏大 | 标注框包含背景过多 | 收紧标注规范,外扩超过5像素一律重标 |
| 部署后视频卡顿 | 数据拷贝频繁、推理和预处理未分离 | 批量推理、使用硬件解码、避免逐帧同步操作 |
| 明明训练精度很高但实际检出率低 | 测试场景光照、高度和训练分布不一致 | 扩充数据集的场景多样性,加入目标域样本 |
6.2 实操心得:从数据集到落地,最容易忽略的三个问题
第一个是数据集的场景多样性问题。我们在基础版本训练时,采集集中在晴朗天气和顺光时段,测试时发现阴天和逆光场景的真实召回率下降了差不多15个百分点。后来补了一批多云天气的航拍数据,模型泛化能力才基本达标。航拍检测特别吃光照,多考虑几种天气条件能省去很多现场返工的麻烦。
第二个是标注的一致性远比标注数量更影响最终精度。15000个标注框如果标注标准参差不齐,模型会在模糊边界上不停摇摆,训练曲线也很难平滑收敛。我宁愿要3000张高质量图片,也不想拿8000张粗标数据硬喂。
第三个是切片策略要贯穿训练和推理。很多项目训练时用滑窗切片,部署直接整图输入模型,输入分布一变,精度骤降。我们的做法是推理阶段和训练阶段使用完全相同的切片尺寸、重叠率、缩放逻辑,确保模型看到的图像和训练时一致,这一点看似理所当然,但实践里非常容易疏忽。
最后分享一个后续扩展方向。校园操场只是航拍人体检测的一个子场景,类似的需求在工地安全帽佩戴检测、体育赛事多人追踪、大型活动人流密度估计中大量存在。数据集里已经包含了密集列队和自由活动两类人群分布,接下来可以尝试把模型从单人检测升级为跟踪任务,接入ByteTrack或BoT-SORT做多目标追踪,统计操场实时人流量。这样整套方案的实用价值会再上一个台阶。如果你正打算做航拍检测,我的建议是从小数据集起步,把采集、标注、训练、部署的闭环跑通,再逐步扩充数据规模,这样踩坑成本会低很多。