多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

YOLOv7结合ESRGAN的坑洼检测:超分重建与部署优化

YOLOv7结合ESRGAN的坑洼检测:超分重建与部署优化 简介一份面向智能交通与计算机视觉研究者的英文学术论文资料聚焦低分辨率行车记录仪图像中的坑洼自动检测问题。该方案先利用ESRGAN增强型超分辨率生成对抗网络对低质量图像进行清晰化处理再将增强后的图像输入YOLOv7目标检测网络完成坑洼定位并在低质量与高质量图像上对比了检测速度与准确率验证了超分预处理带来的性能提升。资源包含1个pdf文件大小约1.35MB内容覆盖引言、相关技术背景、ESRGAN与YOLOv7原理、联合实现流程和实验结果与讨论等章节适合作为计算机视觉项目预研或论文参考。已有253人学习该资料读者可从中快速掌握低分辨率路况检测的一种可行技术路线、实验评估方法及实际效果数据。1. YOLOv7 坑洼检测卡在哪里目标太小不是网络太浅凌晨的巡检车压过省道裂缝摄像头录下 1920×1080 的整帧画面坑洼往往只占三四十乘五六十像素。模型不是认不出坑洼而是在第一轮下采样后目标被抹成了几个像素的噪声块——坑洼检测的瓶颈不在网络深度在输入尺寸。把 YOLOv7 和 ESRGAN 放进同一条管线是让检测前先做细节重建ESRGAN 把图像按 x2 放大并恢复边缘纹理YOLOv7 再在重建图上出框。这个组合不改检测结构、不重写训练代码适合已有检测基础但卡在召回率的人。下文从模型分工讲到 yolov7部署的加速和排错整条路线都用可复现的命令展开。2. YOLOv7 与 ESRGAN 的分工检测器管定位生成器管细节2.1 为什么选 YOLOv7 做坑洼定位锚框先验与重参数化的取舍目标检测选型时YOLOv7 常被拿来和更新的 anchor-free 模型对比。坑洼检测有个容易忽略的特性坑洼在画面里的高宽比相当稳定通常在 1:1 到 1:3 之间锚框先验恰好能利用这种稳定性。YOLOv7 保留了 anchor-based 的回归头训练时根据数据集自动调整锚框推理时对固定形变目标的定位比全卷积的 anchor-free 方式更稳。结构上E-ELAN 把不同通道组的梯度路径交错拼接信息回传时不会因为层数加深而退化重参数化卷积在训练时保持多分支导出时融合成单路。这两点直接转化为部署收益同样的精度下ONNX 和 TensorRT 的延时通常比多头复杂结构低一截对车载设备友好。另一个务实理由是训练配置简单。YOLOv7 的动态标签分配不再依赖初始锚框的尺寸即使坑洼数据只有几百张直接套默认训练参数也能收敛不需要像早期 YOLO 那样先跑 Anchor 聚类再改配置。对想快速验证 ESRGAN 前处理收益的团队这个上手成本很关键。2.2 ESRGAN 的 RRDB 与感知损失为什么适合恢复坑洼纹理ESRGAN 相对普通超分模型的核心区别在生成器里用 RRDBResidual in Residual Dense Block替代原始残差块。RRDB 把多个密集连接块串起来同时保留主路径的残差感受野变大纹理合成时不容易出现棋盘格伪影。更关键的是去掉 BatchNorm使超分输出的高频细节不受批量统计量干扰单张输入也能稳定出图。损失设计上ESRGAN 使用感知损失加对抗损失。感知损失在 VGG 特征空间比较重建图和原图约束的是边缘结构和语义一致性对抗损失用相对论判别器判断重建图是否比原图更真实。对坑洼场景这意味着重建出来的不是平滑放大的模糊块而是有清晰灰度跃变的裂缝边缘。后续 YOLO 骨干网的浅层卷积可以从这些边缘响应里提取到有效特征而不是在小噪声上猜形状。需要说明的是ESRGAN 不能创造不存在的信息。坑洼原图如果因为运动模糊已经丢失全部边缘超分只会生成假纹理。所以实际生产里要配合行车记录仪的快门策略优先选用帧率高于 30fps、曝光时间短的视频源。数据侧的要求比模型侧更先决。2.3 前端重建还是联合训练三条路线的取舍把 ESRGAN 和 YOLOv7 接在一起常见做法有三类一是把超分当作独立预处理检测器吃重建后的图像二是把超分模块插在检测器骨干之前共享特征做端到端训练三是用单模型直接做 high-level 任务。三者的差异集中在训练成本和部署形态上。路线结构训练成本推理延迟坑洼场景结论前端两段式超分 独立检测器分别训练低增加一次超分前向最稳推荐共享特征联合训练低分辨率输入重建后接检测头需要联合调参高略低于两段式标注多时可试单模型多任务一个网络同时输出重建和检测高易过拟合最低不推荐实际项目里我一般选前端两段式。坑洼标注本来就稀缺GAN 的判别器梯度如果回传到检测主干容易把检测特征带偏分开训练则可以分别调 ESRGAN 的感知损失权重和 YOLOv7 的置信度阈值出问题时职责边界清楚。推理延迟增加一次超分前向在 x2 下大约几十毫秒对每秒处理十帧的巡检测算力来说可以接受。3. 用 YOLOv7 训练坑洼检测器并接入 ESRGAN 预处理3.1 数据管线抽帧、标注与划分坑洼数据最常见的来源是行车记录仪而不是爬虫抓图。记录仪视角固定坑洼大多出现在画面下三分之一光照条件相对可控标注一致性好。先用 ffmpeg 把视频按时间均匀抽帧避免连续帧都是同一段路ffmpeg -i driving_video.mp4 -vf fps5,scale1920:1080 -q:v 2 frames/frame_%05d.jpgfps5表示每秒抽 5 帧对 30fps 的记录仪等于每 6 帧取 1 帧既保留同一坑洼的多角度样本又避免训练集全是近重复帧scale强制统一分辨率YOLO 系模型对输入尺寸不敏感但统一尺寸能减少预处理分支。抽完帧后筛掉夜间强光过曝的样本再用常见标注工具导出 YOLO 格式的 txt。坑洼是小目标标注时贴着裂缝外沿画框不要留太多背景路肩否则正样本的有效特征会被稀释。划分比例按 8:1:1 切 train/val/test切分必须在视频级别做而不是在帧级别随机切否则同一路段的地面纹理泄漏到验证集指标会虚高。切完统计类别框的尺寸分布如果大多数框的宽高都小于整图尺寸的 10%训练配置里就要考虑放大输入分辨率或降低 mosaic 的过度缩放具体见下一节。3.2 坑洼检测的 YOLOv7 训练配置模板训练前先写数据配置再写模型配置。数据文件指向切好的图片目录、标注目录并写明类别名。坑洼检测只有一类# pothole.yaml train: data/images/train val: data/images/val test: data/images/test nc: 1 names: [pothole]接着进入 YOLOv7 训练入口。在 2080Ti 或同级别显卡上输入 640 分辨率、batch 8精度和速度比较平衡python train.py --workers 8 --batch-size 8 --epochs 300 \ --data pothole.yaml --cfg cfg/training/yolov7.yaml \ --weights yolov7.pt --img 640 640 --device 0 --name pothole几个参数按坑洼场景调整img从默认 640 提到 960 会让 mAP 上升但显存占用近乎翻倍适合坑洼只占很小像素的远景场景mosaic 增强在数据量小于 1000 张时容易把坑洼切成半块建议在 hyp 配置里把 mosaic 的概率降到 0.3epochs 300对上千张图足够观察 val 指标通常在第 150 轮后不再上升加早停能省时间。提示如果训练时 loss 到 200 轮仍在波动先关掉 mosaic 再试。小目标经过 mosaic 随机裁剪后经常只剩边缘模型更容易学到噪声而不是坑洼形状。训练完输出 best.pt先跑一次 val 看各类别 AP。坑洼这一类背景复杂常见现象是 precision 高、recall 低说明置信度阈值偏高或负样本不够优先降置信阈值而不是加训练轮数。3.3 ESRGAN 可复现的推理预处理脚本模型分开训练推理时把 ESRGAN 放在 YOLOv7 前面。为避免整张 1920×1080 输入超分导致显存爆掉常见做法是切块推理块与块之间保留重叠区域合并时按重叠比例取均值import torch import numpy as np import cv2 def sr_inference(sr_model, image, scale2, patch512, overlap32): h, w image.shape[:2] canvas np.zeros((h * scale, w * scale, 3), dtypenp.float32) weight np.zeros((h * scale, w * scale, 1), dtypenp.float32) # 遍历切块并保留 overlap for y in range(0, h, patch - overlap): for x in range(0, w, patch - overlap): y0, y1 y, min(y patch, h) x0, x1 x, min(x patch, w) block image[y0:y1, x0:x1] with torch.no_grad(): tensor torch.from_numpy(block.transpose(2, 0, 1)).float().unsqueeze(0) / 255.0 out sr_model(tensor).squeeze(0).numpy().transpose(1, 2, 0) # 回贴到放大画布overlap 区域通过权重累计平滑过渡 sy0, sy1 y * scale, y * scale out.shape[0] sx0, sx1 x * scale, x * scale out.shape[1] canvas[sy0:sy1, sx0:sx1] out weight[sy0:sy1, sx0:sx1] 1.0 result canvas / np.maximum(weight, 1e-6) return result这段代码有三个参数要说明。scale是超分倍率坑洼场景用 x2 通常就够x4 会把路面颗粒噪声也放大干扰检测器的纹理特征patch512控制单次送入显存的图像尺寸对 2GB 显存的小盒子和 x2 倍率512 是安全值overlap32是为了避免切块边缘不连续32 像素下几乎没有可见接缝再大则浪费重复计算。权重回贴的方式比直接裁边拼接多占用一块和放大图等大的内存换来的好处是重叠区域平滑融合。模型输出归一化到 0~1累加后从 float32 转回 uint8 时乘 255 前要先 clip防止裂缝边缘过曝成白块。3.4 坐标从超分图回映射到原图超分发生在检测之前YOLOv7 输出的是超分图上的像素坐标。原图尺寸是 W×H超分图为 W·scale×H·scale检测框直接除以 scale 即可回到原图坐标。但用了切块回贴时每条边还要加上切块的偏移量代码上先记录推理时的起点再换算def map_box_to_orig(box, origin_y, origin_x, scale): # box 为 [x1, y1, x2, y2]单位是超分图上的像素 x1 (origin_x box[0]) / scale y1 (origin_y box[1]) / scale x2 (origin_x box[2]) / scale y2 (origin_y box[3]) / scale return [int(round(x1)), int(round(y1)), int(round(x2)), int(round(y2))]四个细节容易踩坑。除以 scale 后的坐标要做整数化但 round 之前必须保留 float否则多个边界框叠加时误差会累积映射后的坐标需要 clip 到原图边界x1、y1 不小于 0x2、y2 不大于宽高如果一条坑洼同时落在两个相邻切块检测器会输出两个框映射回原图后再跑一次全局 NMS以 IoU 0.5 去重最后可视化时一定用原图维度画框不要用超分图维度否则框位置整体偏移。4. yolov7部署的推理加速与三个必调参数4.1 ONNX 导出与 TensorRT 加速模型验证通过后训练产物 best.pt 不能直接上生产。车载或边缘设备上做 yolov7部署常见路径是先导出 ONNX再转成 TensorRT engine。导出命令保持官方参数关键在加--grid让输出带网格结构省去后处理里重写特征图解码的工作python export.py --weights runs/train/pothole/weights/best.pt \ --img 640 640 --batch 1 --simplify --grid--batch 1有两个作用导出阶段只关心静态形状后续 TensorRT 转换不需要 dynamic shape省一半转换时间坑洼工况下巡检车视频流本来就是逐帧处理batch 1 不影响吞吐。--simplify调用 ONNX Simplifier 清理多余算子FP16 转换时更不容易遇到不支持的节点。拿到 onnx 后用 trtexec 生成 engine下面是典型写法trtexec --onnxbest.onnx --saveEnginebest.engine \ --fp16 --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x640x640 --maxShapesimages:1x3x640x640--fp16能带来约 1.5 倍加速但对坑洼这种小目标会在边缘响应上损失一点精度。稳妥做法是先跑 fp16对比 val 集 mAP 下降是否超过 0.5 个点再决定是否回退 fp32。用了 TensorRT 的 Python 接口加载 engine 后不需要再封装一次 YOLOv7 的 NMSengine 输出的原始张量直接交给后处理调参更灵活。4.2 三个必调参数置信度、NMS 阈值与超分倍率检测器接进预处理后参数不能全用默认值。我按坑洼场景给出三个必调项和起始值参数之间需要联动调整。参数建议起始值说明conf_thres0.25漏检比误检代价高时降到 0.15iou_thres0.5切块检测场景取 0.5框紧凑可到 0.45ESRGAN scale2夜间或雨天纹理弱时保持 2再大只会放大噪点参数联动是重点。把conf_thres从 0.3 降到 0.15recall 提升的同时会多出一批路肩裂缝误报此时把iou_thres从 0.5 提到 0.6让 NMS 合并掉同一条裂缝产生的多个重叠框ESRGAN 的scale则保持 2超过 2 以后路面石子纹理也被放大误报率上升速度比召回率快。我一般先在一块夜间视频段上分别跑三组对比取误检率和漏检率乘积最小的组合再固定到后端配置里。4.3 坑洼检测部署时三个高频坑第一个坑是 ESRGAN 导出时绑定了 batch 1后端一收到批量请求就报 shape 错误。ESRGAN 和 YOLOv7 不同特征图空间尺寸在生成器内部随输入变化导出用 static shape 就把输入固定成 1×3×H×W。后端缓冲队列做逐帧串行即可不要为显存利用率强行并发这类多层密集连接的 GAN 并行效率很低。第二个坑是 FP16 下纹理被截断。坑洼裂缝边缘在 FP16 下有效位宽不足表现为召回率掉了但误检变化不明显。做法是先按 4.1 的命令用 fp16 对比 mAP如果跌幅超阈值就只在检测器上用 TensorRTESRGAN 保留 PyTorch 的 fp32 推理。第三个坑是切块拼接边界出现重复框。超分按 3.3 脚本切块检测也基于同一批切块进行同一个坑洼在相邻块重复检出映射回原图后两个框呈平移关系而不是包含关系普通 NMS 的 IoU 阈值消不掉。处理方式是映射回原图后再做一次坐标级 NMS并把两个框合并成最大外接矩形外接矩形刚好覆盖切缝两侧的裂纹。5. 用 YOLOv7 的时序投票验证坑洼检测稳定性5.1 单帧输出为什么不可靠坑洼从图像特征上看是低频灰度区域加边缘小梯度行车记录仪稍有震动同一个坑洼逐帧的置信度会在 0.2 到 0.9 之间跳。工程上既不能拿单帧最高分输出也不能拿单帧最低分丢弃。常见做法是用轻量跟踪器按帧间位置投票连续 N 帧命中同一位置才输出跳变帧不打断计数。相比单纯调高置信度阈值时序投票能保留低分但位置稳定的真阳性而飞虫、水渍这类误报通常无法连续命中。5.2 基于 IoU 的轻量状态机实现class PotholeVoter: def __init__(self, iou_threshold0.3, hit_required3, cooldown8): self.iou_threshold iou_threshold self.hit_required hit_required self.cooldown cooldown self.tracks [] def update(self, detections, frame_id): # detections: list of [x1, y1, x2, y2]原图坐标已做全局 NMS result [] for track in self.tracks: if frame_id - track[last_frame] self.cooldown: continue for det in detections: if iou(track[box], det) self.iou_threshold: track[box] det track[hits] 1 track[last_frame] frame_id if track[hits] self.hit_required: result.append((det, stable)) break return resulthit_required3配合 5fps 的抽帧相当于同一坑洼必须在一秒内被命中三次才上屏能滤掉单帧闪光和飞虫误报。cooldown8是轨迹存活帧数超过后直接丢弃重新累积。注意候选检测框必须来自映射回原图并经过全局 NMS 的最终输出两条轨迹之间保持线性匹配不做 IoU 矩阵分配也够用。5.3 验证方法对比稳定前后的漏检率把时序投票加进现有推理服务后用一段未被训练过的夜间行车视频做验证稳定输出和逐帧原始检测分别落盘统计两个指标——稳定输出的漏检率要低于单帧剪低置信度误检率要低于单帧剪高置信度。统计时按视频帧号聚合相同路段的重复命中只记一次。如果稳定后指标没有提升优先检查cooldown是否把短时遮挡看成轨迹丢失再检查 IoU 阈值是否大于坑洼连续帧间的位移量。调完一组参数后把投票结果按帧号、类别、映射后的原图坐标写入 CSV与 GNSS 轨迹时间戳对齐就能直接生成养护工单里的道路损坏位置列表。本文还有配套的精品资源点击获取
返回列表