如果只是把操场上的学生数量数出来,那问题不大;但要把无人机俯拍画面里的每一个人精准框出来,情况就完全不同了。我去年秋天接了一个实打实的活儿:用消费级无人机在校园操场上空悬停,实时统计课间操人数,同时把每个学生单独检测出来,方便后续做人群分布分析。一开始我以为这就是个普通的人体检测任务,拿现成的 YOLO 模型一跑就好,结果真的把无人机飞上去一看,心里顿时凉了半截——俯视视角下学生只剩一个椭圆形的头顶加肩膀投影,躯干和四肢完全重叠,常规模型在航拍画面上的表现可以用“灾难”来形容:大量漏检、并排走的几个学生被框成一个框、站在阴影里的直接消失。于是就有了这个项目:一个专门面向航拍校园操场场景的人体检测数据集,并基于 YOLO 把从数据构建到模型训练、再到边缘设备部署的完整链路全部跑通。
这篇文章想分享给三类人:刚接触目标检测、准备用 YOLO 训练自定义数据集的初学者;手里有无人机、想把航拍视觉项目落地但卡在数据环节的开发者;以及正在研究小目标检测、高密集人群检测的研究者。我不打算只讲“我标注了多少张图、训练完 mAP 是多少”这种结果式复盘,而是把每个关键决策背后的理由写清楚:为什么要按这个方式采集、标注口径怎么定、哪些训练参数在这个场景下是性命攸关的、以及我踩过的几个坑的完整排查过程。
1. 航拍场景为什么值得单独做一个数据集
1.1 俯视视角让常规模型的经验全部失灵
很多人容易忽略一个前提:主流目标检测数据集,包括 COCO、Pascal VOC,以及行人检测里常用的 CrowdHuman、Caltech,几乎全部是平视或者略带俯视的视角。模型在里面学到的“人”是立着的,有完整的头部、肩膀、手臂、腿部轮廓,上半身和下半身有明确的空间关系。航拍俯视时这个前提直接不存在了。
我自己做过一个对比实验:直接用 COCO 预训练的 YOLOv8s 权重去推理航拍操场视频,置信度阈值 0.25 的情况下,召回率大概只有 0.31。也就是说,操场上站了 800 个学生,模型只能检测出大约 250 个,而且误检还不少。这不是模型不够强,而是训练数据和目标场景的特征分布差距太大。常规模型学到的“头-肩-躯干-腿”的特征表达,在俯视图里几乎全部失效,能用的只剩“带纹理的椭圆形物体”这种弱特征。
这也解释了为什么直接拿开源预训练权重微调时,前面几十个 epoch 的损失下降会特别慢。模型需要把底层特征重新组织一遍,从“辨认人的身体结构”切换成“辨认俯视下的头顶加肩膀投影”,这个迁移过程本身就需要足够多的航拍样本来支撑。
1.2 小目标与高密度人群叠加,难度不是简单相加
校园操场大概是所有航拍人体检测场景里最“卷”的一种。飞行高度 40 米左右时,一个中等身材的学生在画面里只占大约 30×50 像素,在 1280×720 的原始画面里属于不折不扣的小目标。课间操时段整个操场上千人同时静止站立,目标间距常常只有十几个像素,遮挡严重到连人眼都难以逐个辨认,标注员看着都会犯怵。
小目标检测难的本质是:YOLO 系列模型的下采样倍数通常是 8 倍、16 倍、32 倍,高层特征图上的一个格子对应原图的一块大区域。一个 30×50 像素的人在 32 倍下采样后的特征图上只覆盖大约 1×2 个格子,信息经过多层卷积之后已经非常微弱。更麻烦的是训练时的正样本匹配机制:锚框和 GT 框的 IoU 阈值是固定的,小目标稍微偏移一点就匹配不上,导致大量小目标在训练过程中根本分不到正样本,模型从头到尾就没“见过”它们。
高密度还在另一个维度上加大难度:NMS 阶段。同一个局部区域可能同时存在十几个真实目标,模型预测出的候选框稍微偏移就会互相重叠,非极大值抑制把正确但置信度稍低的框全部压掉。这也是为什么密集场景下漏检往往集中在人群最密集的中心位置——那里的框数量最多,被抑制的概率最大。
1.3 这个项目的核心定位:做一套能直接落地的航拍数据集
市面上开源的人体检测数据集不少,但仔细盘一遍就会发现,几乎没有专门针对航拍俯视校园操场这种场景的。有的数据集是无人机视角但目标是车辆,有的是高分辨率遥感图但场景是城市,有的虽然是人但拍摄高度和角度与校园操场差异巨大。
我当时定的目标很明确:不做“学术打榜式”数据集,而是做一套“采集-标注-训练-部署”闭环可复用的工程数据集。具体来说,数据集要满足三个条件:一是覆盖完整的尺度分布,从 10 米低空的大目标到 60 米高空的小目标都要有;二是覆盖不同的人群体量,从几个人的稀疏散步到上千人的课间操密集集合;三是覆盖不同光照和天气,阴天、晴天、逆光、顺光都不能缺。这套数据集的价值不在数量本身,而在于它真实反映了部署场景中的分布,后面训练出的模型才敢拿到现场用。
2. 数据集的搭建:不是随便标注,而是按航拍规律设计
2.1 采集阶段:机位、高度与光照的选型
数据采集是整个项目里最容易被低估的一环。很多人觉得拿无人机去操场飞一圈、拍几段视频就够了,实际上采集策略直接决定数据集的可用性,甚至比标注数量更重要。
我用的设备是一台入门级 4K 消费级无人机,机载摄像头是固定的广角镜头,没有机械云台,画质在白天光线充足时足够用。为什么不选更专业的设备?因为部署场景里能用什么设备,最好就用什么设备采集,这样训练和推理的数据分布才一致。用贵设备采集、用便宜设备部署,是航拍项目里常见的“分布迁移陷阱”。
飞行参数上,我做了三组设计:
- 高度梯度:10 米、20 米、30 米、40 米、50 米、60 米各拍一段,保证目标的像素尺寸有完整梯度。这里有个细节:无人机在不同高度的地速差异很大,40 米以上悬停时 GPS 漂移会明显,画面会轻微飘动,这种真实噪声我选择保留,因为它真实反映了部署场景的输入状态。
- 相机角度:垂直向下(90 度)和斜视(45 度)两种。垂直向下能拍到最标准的俯视图,是密集统计时的主要视角;45 度斜视更接近实际巡逻画面的感觉,而且在斜视视角下行人有一部分侧立面,检测难度和 90 度时不同。
- 时间覆盖:课间操、午休、放学三个固定时段拍摄。课间操时操场人群密度最大,是数据集的“主战场”;午休时段人少但分布松散,能提供大量负样本和低密度样本;放学时段学生动态行走,能拍到运动模糊的真实形态。
光照方面,我刻意把晴天强光、阴天散射光、傍晚逆光都拍了进去。强光下学生投影边缘清晰,但容易过曝;阴天细节柔和,但对比度低;逆光时人脸和衣服细节几乎全黑,模型只能靠轮廓判断。这三类光照在真实校园场景里占比都不低,缺一种都会导致模型在某些天气下突然退化。
2.2 抽帧与清洗:把无效素材挡在标注之前
视频素材不能直接拿去标注,需要先抽帧再做清洗。无人机视频每秒 30 帧,连续帧之间内容高度相似,如果逐帧标注,训练集里全是高度自相关的近似重复样本,模型会严重过拟合,泛化能力极差,而且标注工作量暴涨。
我的抽帧策略是两步走。第一步按时间抽帧,每 0.5 秒取 1 帧,把 15 分钟的视频压成 1800 帧左右。第二步是内容差异化筛选,我写了一个小脚本计算相邻帧的平均像素差,差值低于某个阈值的帧直接丢弃,因为它们在内容上和前一帧几乎一样,对训练没有增量信息。
import cv2 threshold = 25.0 # 平均绝对像素差阈值 prev_frame = None keep_indices = [] cap = cv2.VideoCapture("drone_footage.mp4") fps = cap.get(cv2.CAP_PROP_FPS) frame_interval = int(fps / 2) # 每0.5秒取1帧 frame_count = 0 while True: ret, frame = cap.read() if not ret: break if frame_count % frame_interval == 0: gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.resize(gray, (640, 360)) # 降低分辨率加速比较 if prev_frame is None: keep_indices.append(frame_count) else: diff = cv2.absdiff(gray, prev_frame).mean() if diff >= threshold: keep_indices.append(frame_count) prev_frame = gray frame_count += 1 cap.release()这里踩过一个实际教训:阈值设得太低,导致选出来的帧全是无人机悬停稳定时的静止画面,起飞、降落、转弯等运动状态下的帧全被筛掉了。后来我把阈值从 10 调到 25,并在脚本里加了一个随机补充机制——每段视频额外保留 5% 的随机帧,防止筛选算法把某种类型的画面系统性丢掉。
清晰度筛选同样重要。无人机起飞和降落阶段会产生大量运动模糊帧,螺旋桨振动也会造成局部模糊,这些帧直接标的话,标注员会非常痛苦,模型学到的也是无意义的噪声。我用拉普拉斯算子计算灰度图的方差来判断清晰度,方差低于阈值的帧进入淘汰列表。针对 1280×720 的灰度图,我实验后把阈值定在 100 左右:低于 100 的帧人眼看起来已经开始发糊,标注时难以确定边界,训练时反而成为干扰样本。
最后清洗下来的有效帧大约 2200 张,覆盖了两个学校的不同操场布局,一个塑胶跑道一人工草皮,这样模型不会把“红色跑道”误当成背景特征的一部分学进去。
2.3 标注规范:小目标、遮挡与边界目标的统一口径
标注是数据集的灵魂,而标注的核心不是“画框”,而是“定规则”。航拍场景下小目标占比高,如果每个标注员都有自己的“要不要标”的标准,模型一定会受害:一个目标在两个标注员手里一个标了一个没标,训练时同一位置既是正样本又是负样本,损失函数直接打架。
我的标注工具有两个选择考虑:LabelImg 和 X-AnyLabeling。最终选了 X-AnyLabeling,因为它支持用一个基础模型或预训练检测器做半自动预标注,然后再人工修正。我把 COCO 预训练的 YOLOv8s 作为预标注底模,让它先生成一批候选框,标注员在候选框基础上调整位置、删除漏检、补充没检到的目标。这个流程大约省了一半时间,但有一个非常隐蔽的问题:预标注底模对航拍小目标的系统性漏检会“传染”给标注员,人的精力有限,在机器漏检的位置容易直接放过。所以我规定:预标注生成的框必须逐一确认,不能批量通过,而且每张图人工检查完,还要随机抽 20% 的图做二次交叉检查。
标注口径上,我定了几条硬性规则:
- 尺寸阈值:目标在原始分辨率下大于 12×12 像素时必须标注;小于 12×12 且人眼无法确认是人的,不标注。这个尺度的取舍很现实,标注太小的目标不仅费时费力,而且框的边界噪声极大,训练时反而给损失函数引入大量噪声。
- 遮挡规则:目标之间互相遮挡时,只要可见面积超过 30%,就按一个独立目标标注,框的范围按整个目标的真实外接框来画,而不是只画可见部分。这样模型学习的是“即便遮挡严重,这里也有一个人”的语义,而不是学会把遮挡物当作背景。
- 边界规则:目标有一部分在画面外,但可见面积超过 30% 时,要标注,框延伸到画面边缘。这类边界目标在航拍中很常见,模型学会后,在真实部署时不会因为目标走到画面边缘就被漏掉。
- 类别定义:整个项目只有一个类别 person,不额外标注“头部”“人群”等辅助类别,因为业务需求就是检测整个人。
还要说明的是目标框的形状问题。俯视视角下人的外接框长宽比通常非常接近 1:1 到 1:1.5,比平视场景的长条矩形要“方”得多。这对 YOLO 的锚框先验是一个直接影响,后面训练时我特意观察了自动锚框计算的结果,确认它适配了这种新的长宽比分布。
2.4 YOLO 格式转换与数据集目录划分
标注工具导出的格式通常是 JSON 或 XML,训练前要统一转成 YOLO 的 txt 格式:每行“类别ID 中心点x 中心点y 框宽 框高”,坐标全部归一化到 0 到 1 之间。这一步写个脚本就能完成,核心是别把坐标算错。
import json import os def convert_labelme_to_yolo(json_path, output_dir, class_mapping): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) img_w = data["imageWidth"] img_h = data["imageHeight"] yolo_lines = [] for shape in data["shapes"]: label = shape["label"] if label not in class_mapping: continue points = shape["points"] # [左上角, 右下角] x1, y1 = points[0] x2, y2 = points[1] # 转成YOLO中心点格式,并做边界裁剪 cx = ((x1 + x2) / 2) / img_w cy = ((y1 + y2) / 2) / img_h bw = abs(x2 - x1) / img_w bh = abs(y2 - y1) / img_h cx = max(0, min(1, cx)) cy = max(0, min(1, cy)) yolo_lines.append(f"{class_mapping[label]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") txt_name = os.path.basename(json_path).replace(".json", ".txt") with open(os.path.join(output_dir, txt_name), "w", encoding="utf-8") as f: f.write("\n".join(yolo_lines))数据集目录我按 YOLO 常规结构组织,训练集、验证集、测试集按 8:1:1 划分。这里有一个极易被忽略但影响巨大的细节:划分时必须保证同一段视频抽出来的帧不能被同时拆进训练集和验证集,否则验证集的画面和训练集高度相似,评估出来的指标虚高。我先按视频段分组,再整组划分,而不是把帧打散后随机抽样。后一种做法在学术上叫“数据泄漏”,会导致模型过拟合被隐藏起来,上面已经说过我为此付出过代价。
3. YOLO 训练全流程:配置、参数与增强策略
3.1 训练环境与预训练权重选型
训练环境我用的是一台 Linux 服务器,单张 RTX 3060 12GB 显卡,显存不算大,但对这个规模的数据集来说足够支撑小模型的完整训练。软件层面直接使用 ultralytics 库,安装了 YOLOv8 的对应版本。要注意的是训练前把 CUDA 和 PyTorch 版本对齐,ultralytics 对不同 PyTorch 版本的兼容性差别较大,装错了会出现“检测不到 GPU”或者训练过程中随机报错的问题。
预训练权重我选了 YOLOv8s,而不是更轻的 YOLOv8n,也不是更重的 YOLOv8m。原因:YOLOv8n 的特征表达能力在这个任务上偏弱,航拍小目标需要足够的特征提取容量;YOLOv8m 参数量接近三倍于 s,训练更慢、显存开销大,但精度提升有限,对工程落地来说性价比不高。s 版本是平衡点。
使用官方提供的 COCO 预训练权重而不是随机初始化,是训练启动阶段能获得稳定收益的关键。虽然 COCO 里几乎没有俯视人体样本,但底层特征如边缘、纹理、颜色渐变、局部形状仍然是通用的,模型不需要从零学习“什么是视觉纹理”。实际操作时,预训练让模型在前 30 个 epoch 内就稳定收敛,而随机初始化在同样的 epoch 内很可能还在震荡。
3.2 关键超参数:从分辨率到增强策略的取舍
训练参数是航拍小目标场景下最值得花时间调的部分。以下是我经过多轮对比后最终采用的配置,以及每个参数背后的理由。
输入分辨率 imgsz,我最终选择了 1280,而不是模型默认的 640。这个选择很关键:目标本身只有 30×50 像素,在 640 分辨率下经过 8 倍下采样,特征图上只剩 4×6 个像素点,信息量严重不足;在 1280 下,同样的目标占 8×12 个像素点,虽然仍然不大,但足以让检测头捕捉到稳定的局部特征。代价是训练速度和推理速度几乎下降一半。我做了对比实验:同样训练 100 个 epoch,1280 输入下的验证集 mAP@0.5 比 640 高约 9 个百分点,这个差距在航拍小目标场景下是决定性的。
epoch 数设置为 300,其中前 240 个 epoch 开启完整的数据增强,最后 60 个 epoch 采用逐步关闭增强的微调策略。为什么要这么做?数据增强可以提升模型鲁棒性,但增强过度会让模型无法拟合真实分布,最后阶段关闭增强,让模型在真实的分布上做精细调整,损失通常会有一个明显的下降。
数据增强方面,我保留了 Mosaic 增强,这是 YOLO 系列默认开启的重要增强策略,它把四张图拼在一起训练,让模型看到更丰富的上下文,并且显著改善小目标的正样本匹配,因为拼接后的目标密度变高、负样本比例下降。但我在最后阶段关闭了它,因为 Mosaic 生成的拼图在这个俯视场景中会产生大量不符合真实分布的合成场景,比如把操场和电线杆拼在一起,这会让模型学会一些不切实际的组合特征。
另一个值得说的是灰度增强和色彩增强。我把 hsv_h 从默认的 0.015 略微调高到 0.02,hsv_s 从 0.7 调高到 0.8,代价是轻微的颜色失真,但好处是模型对不同塑胶跑道颜色、不同衣着的适应能力明显增强。平移和缩放增强我保持默认,但翻转增强我只保留了左右翻转,因为上下翻转在人俯视场景下没有语义合理性——一个人在画面里上下颠倒着出现,只会在训练时引入无意义的模式。
batch size 的设定我吃过教训,见下一节。当前配置下,1280 分辨率输入、batch size 8,显存占用大约 11GB,刚好在 12GB 卡的边缘,训练过程中偶尔会触发 OOM,我又加了梯度累积技术来变相扩大有效 batch。
3.3 训练监控:如何判断模型真的在收敛
训练不是把命令丢进去等结果就完事。我在训练过程里每 10 个 epoch 做一次完整验证,记录训练损失(box_loss、cls_loss、dfl_loss)和验证指标(P、R、mAP@0.5、mAP@0.5:0.95)。判断收敛的标准不是“验证集 loss 降了就行”,而是三个信号同时满足:
- 验证集上的召回率 R 持续上升且不再剧烈震荡。对人群统计来说,漏检比误检严重得多,一个操场上漏检 10 个人,意味着统计结果直接少了一队人,这是业务上无法接受的。
- mAP@0.5 保持稳定多轮不下降。这个指标对“框得准不准”更敏感,反映检测框的定位质量。
- 验证集 loss 和训练集 loss 的差距没有持续扩大。差距扩大说明模型开始过拟合训练集,这时我会提前停止训练,或补充增强策略。
我还保留了每个 epoch 的权重文件,前 100 个 epoch 每 10 个存一个,100 个之后每 5 个存一个。这样如果后面发现某个阶段出现了过拟合,可以回退到之前的权重重新调整训练策略,不用全部重来。
4. 训练中踩过的坑:完整排查链路与解决方案
4.1 BN 崩溃:不是因为显卡不够,而是 batch 太小
训练初期我犯了一个典型错误:为了在 12GB 显存上塞进 1280 分辨率输入,我把 batch size 直接压到 4。训练约 15 个 epoch 后,cls_loss 突然出现 NaN,训练进程直接中断。第一次遇到时我还以为是自己数据集标注出了问题,反复检查标注文件,发现完全正常。
排查过程是这样的:我先在训练循环里插桩打印每个层的权重梯度和激活值,发现第 3 个 C2f 模块之后的 BatchNorm 层的 running_var 出现了异常值。BN 层在 batch 太小的情况下,统计量估计极不稳定,尤其是在 1280 分辨率这种高变异性输入下,小 batch 会放大这种不稳定性,最终导致数值溢出,也就是“BN 崩溃”。
解决方案我试了两步。第一步是降低基础学习率,从默认的 0.01 降到 0.005,配合 warmup 让模型平稳进入训练状态,NaN 出现的轮次推迟到了 30 个 epoch 之后,但并没有彻底解决。第二步是增加 batch size——把 batch 从 4 提到 8,同时启用梯度累积模拟 batch 16 的效果,这次问题才算稳定解决。最终训练全程没有出现 NaN,loss 曲线平滑下降到收敛。
这个经历给我的最大教训是:在显存受限时,把输入分辨率拉满但 batch 过小,是得不偿失的。合理顺序是:先保证 batch 至少 8,再考虑提升分辨率。如果你的显存实在不够,可以把预训练阶段用 640 分辨率跑,之后在 1280 分辨率上微调,而不是一上来就用小 batch 强行跑 1280。
4.2 小目标漏检:加分辨率有用,但绝不是全部
第一批模型训练完成后,验证集指标看起来不错,但打开实拍视频一眼望过去,最直观的感受是:人群最密集的中间方阵区域漏检严重,四散在操场角落的学生倒是检得很准,这显然是目标密度影响了局部推理能力。
我进行了三轮排查和调优。第一轮直接把推理时的输入分辨率从 1280 提升到 1536,漏检率下降了约 4%,但检测速度从 20 FPS 掉到了 13 FPS,实时性开始吃紧。第二轮改为滑窗切块推理,把原图切成 512×512 的切片分别检测再合并结果,漏检率又下降了约 12%,密集人群中心的漏检明显缓解。切块推理的代价是推理代码复杂度大幅上升,而且重叠区域的框需要额外去重,在 Jetson 设备上跑不满实时需求。
第三轮我做了一个非常规的尝试:修改 YOLOv8 的检测头,在原有的 P3、P4、P5 三层基础上增加了 P2 输出层,也就是 stride=4 的浅层高分辨率特征图。这样小的目标可以借助高分辨率特征检测出来,而不必总是走 8 倍下采样后的深层特征。改造后用相同的策略重新训练,对比同等输入分辨率下的效果,P2 层单独使召回率提升约 7 个百分点。这个数值在常规目标上可能不算突出,但在航拍密集人群场景里非常可观。
最终我的部署方案是:一路使用 P2 增强的模型搭配 1280 输入作为主检测器,在 Jetson 边缘设备上运行;高精度分析场景再叠加滑窗切块推理,可以做到零漏检,但只能处理离线视频。
4.3 验证集 mAP 虚高:数据泄漏让指标失真
我一度被自己的验证集指标骗得很开心。第一版模型在验证集上 mAP@0.5 达到了 0.77,这是一个看起来很优秀的数字。但实际部署测试时,发现操场南侧一个被树荫遮挡的角落,有一群学生完全没被检测出来,模型输出空空如也。
这个矛盾的根源是验证集构造时的数据泄漏。我把同一个视频素材抽出的帧随机打散后按比例分成训练集和验证集,导致验证集里大量画面的光照、角度、人群分布和训练集几乎一样,模型相当于“见过”这些画面后参加考试,分数自然虚高。
修正方法很直接但非常繁琐:把数据集按视频段重新分组,用其中两个完整视频段做训练,完全独立的第三个视频段做验证,第四个做测试。这个“跨视频评估”策略让 mAP@0.5 立刻从 0.77 降到了 0.62,P 从 0.85 降到 0.81,R 从 0.73 降到 0.69。虽然数字变难看了,但我心里反而踏实了——这个数字才代表模型遇到从未见过的航拍画面时的真实水平。
这也是我在采集阶段坚持记录每个视频的日期、机位、高度信息的原因。做数据划分时可以根据这些元信息保证组间独立,否则后期想修正数据处理流程,素材已经改不回来了。
5. 实测效果与后续扩展方向
模型最终定下来的一套配置如下:YOLOv8s 结构加 P2 增强层,输入分辨率 1280,训练 300 个 epoch,最终在独立测试集上 mAP@0.5 为 0.74,进一步分析后召回率 0.69,精确率 0.81。在 RTX 3060 上推理帧率约 20 FPS,换成 Jetson Orin Nano 并做 INT8 量化后约 25 FPS,能够满足在边缘设备上的准实时人群统计需求。
整套数据集和训练配置已经沉淀成固定管线。新到一个学校,只需采集 15 分钟的无人机视频,抽帧、清洗、半自动标注之后微调,就能在一天内得到针对该校园场景的专用检测模型。这个“快速适配”的流程对实际项目非常有价值。
扩展方向我有几个明确的规划。一是引入更多学校的操场数据,特别是不同塑胶跑道颜色、不同操场布局、不同国旗台位置的样本,从源头增加数据分布覆盖;二是与目标跟踪算法结合,做人数流动分析,而不仅仅是静态统计;三是引入多视角数据,因为校园安防的实际需求既有高空俯视,也有低空平视,两个视角的数据合训能让模型更通用。
就我个人的操作体会而言,做这类垂直场景的数据集,最核心的不是标注数量,而是拍摄策略。很多团队一上来就追求标注几千上万张图,却忽视了数据分布是否覆盖真实使用场景。宁可只标 800 张覆盖全面的图,也不要标 5000 张全是同一机位同一光照的图。最后再分享一个小技巧:拍摄时一定要确认无人机旋翼不在画面边缘入镜,我曾经有一批视频抽出来的帧边缘带有螺旋桨虚影,这批帧的漏检率明显高于干净帧,后期清洗浪费了大量时间。采集时多留心这种细节,后面所有环节都会轻松很多。