基于MMDetection的视频目标检测推理脚本设计与实现指南
1. 项目背景与需求拆解1.1 为什么要自己写一个video_demo.pyMMDetection是目前用得比较多的目标检测工具箱之一很多入门选手拿它来做实验、跑通自己的数据集或者直接用它自带的推理脚本验证模型效果。这东西确实方便基础的demo代码基本拿来就能跑不管是单张图片推理还是批量测试图片都能快速看到模型输出。但真到了实际项目里图片demo往往不太够用尤其是做监控视频分析、车载视觉、无人机巡检这类场景你需要面对的是连续的视频流而不是一张张静态图。我当初翻MMDetection的demo目录时发现里面提供的是标准的图像推理脚本直接把单张图片送进模型输出标注后的结果图。视频推理在一个正式的目标检测项目里几乎是刚需但官方demo里偏偏没有直接能用的、把视频逐帧处理并保存成结果的脚本。这其实是个挺常见的缺口所以我才花了些精力自己补了一个video_demo.py把视频读取、逐帧推理、结果可视化、编码保存这条链路完整跑通。1.2 这个脚本解决了什么问题视频检测说白了就是两件事的组合把视频拆成一帧一帧再对每一帧执行目标检测。拆帧这事本身不难OpenCV的VideoCapture就能做真正麻烦的是后面那几步——逐帧推理的性能怎么保证、检测结果怎么叠加到原画面上、最终怎么用VideoWriter把处理过的帧重新合成为视频文件以及中途遇到帧率异常、视频编码格式不兼容这类问题怎么办。我写的这个脚本解决的就是上面这一整条链路的问题。它基于MMDetection现有推理逻辑扩展而来保留了对模型配置文件和权重文件的加载方式同时把输入输出从单张图片扩展到了完整视频。你不需要懂底层的检测器是如何实现前向推理的只需要准备好训练好的模型权重按照脚本参数的格式把视频路径填进去跑起来就能拿到带标注框的结果视频。这个流程对做算法验证、跑演示、快速检查模型在连续帧上的稳定性都非常实用。适合参考这份记录的人主要有三类刚开始学习MMDetection、想把自己的模型用到视频上的入门者已经有模型权重、需要批量处理视频跑效果的算法工程师以及做项目演示时需要快速产出一个可视化结果视频的同学。2. 核心思路与关键设计2.1 复用官方推理逻辑减少重复造轮子动手写之前我先研究了一下MMDetection自带的图像推理脚本的代码结构。它的核心流程非常清晰用Config从配置文件里加载模型结构和训练参数用init_detector初始化模型并载入权重然后调用inference_detector对输入图片执行推理最后用show_result_pyplot画框并把结果展示出来。视频demo完全可以把这条链路保留下来只在输入输出两端做替换。我最终的设计思路是这样的用OpenCV的VideoCapture读取视频帧按帧顺序把图像数据交给inference_detector推理拿到检测结果后用MMDetection自带的可视化接口在帧上画框和标签再用VideoWriter把处理后的帧写进新的视频文件。整个过程中最核心的逻辑模型加载和推理完全复用官方实现我只需要负责视频流的数据调度和结果帧的编码输出。2.2 对官方代码做了哪些加工这里有个关键点需要说明官方脚本里的show_result_pyplot其实是为交互式显示设计的它会把结果图直接弹窗显示出来。在视频处理场景下弹窗既拖慢速度也没法批量保存所以我改成了直接调用底层可视化函数拿到绘制好的图像数组再交给VideoWriter处理。这样既保留了画框逻辑又避免了弹窗阻塞。另一个重要的改动是视频写入环节。视频编码方式很多OpenCV的VideoWriter在不同平台支持的编码器不完全一样我一开始用的是常见的MP4V编码结果在部分机器上输出文件损坏后来换成MJPG编码或直接写AVI格式就稳定多了。这个问题后面会具体展开讲这里先提一下因为它是视频保存这个环节最容易踩的坑。2.3 整体架构与文件结构脚本整体结构其实很简洁核心就三步VideoCapture 读取视频帧 ↓ inference_detector 逐帧推理 ↓ VideoWriter 写入结果视频这里我建议把初始化模型、打开视频、创建写入器三个步骤放到主函数里而把“读取一帧、推理一帧、写回一帧”这个循环单独拆成一个函数。这样看着清爽后面想改批量处理或者加帧率控制也方便。实际项目中我还会把模型初始化和视频处理拆成两个阶段方便先确认模型加载正常再处理视频排查问题的时候会清晰很多。下面是我搭建这个脚本时的完整目录结构思路demovideo_demo.py自己新增的脚本依赖MMDetection原有目录结构3. 环境准备与前置条件3.1 基础环境要求写这个脚本之前确认你的环境已经具备了MMDetection的运行条件。这里不展开讲完整的安装流程但至少要有这么几样东西Python 3.7及以上版本PyTorchCUDA环境有GPU最好纯CPU跑也不是不行就是速度会比较感人MMDetection及其依赖mmcv、mmengine等OpenCV-Python安装完MMDetection之后建议先跑一下官方的图片demo确认整个链路没有问题再开始折腾视频脚本。这样可以避免把环境问题和代码问题混在一起排查。3.2 模型准备视频检测需要一个训练好的模型权重文件。你可以用MMDetection官方提供的预训练权重也可以用自己的数据集训练出的权重。关键是准备好两部分模型配置文件config文件和对应的权重文件.pth。运行脚本时按顺序传入即可。我实际测试时用的是一个在公共数据集上训练好的检测模型大类包括人、车、自行车等常见目标跑视频演示效果足够直观。如果你想测试自己训练的模型只要config文件格式正确、权重路径填对脚本是不需要做任何改动的。4. 核心代码实现详解4.1 模型初始化与参数设计video_demo.py的入口设计非常简单主要参数包括config文件路径模型配置checkpoint路径权重文件输入视频路径待检测的视频输出视频路径结果保存位置device推理设备默认cuda:0score_thr置信度阈值低于这个值的检测框会被过滤这段代码建议放在脚本最前面def parse_args(): parser argparse.ArgumentParser(descriptionMMDetection video demo) parser.add_argument(config, helptest config file path) parser.add_argument(checkpoint, helpcheckpoint file) parser.add_argument(video_path, helpinput video path) parser.add_argument(--out, defaultoutput.avi, helpoutput video path) parser.add_argument(--device, defaultcuda:0, helpdevice used for inference) parser.add_argument(--score-thr, typefloat, default0.3, helpconfidence threshold) return parser.parse_args()4.2 视频流读取设计视频读取本身不复杂但有几个细节需要注意。用VideoCapture打开视频后一定要检查是否成功打开常见的问题是路径填错或者视频编码格式不被OpenCV支持。读取帧信息时我建议顺手获取两个关键属性视频的宽高和帧率。这两个参数在创建VideoWriter的时候是必须的如果不匹配会导致输出视频异常。cap cv2.VideoCapture(video_path) assert cap.isOpened(), fFailed to open video: {video_path} fps cap.get(cv2.CAP_PROP_FPS) width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))4.3 逐帧推理与结果绘制这是整个脚本最核心的循环部分。每一帧从左到右经历四个阶段读取、推理、可视化、写入。while True: ret, frame cap.read() if not ret: break result inference_detector(model, frame) vis_frame model.show_result( frame, result, score_thrscore_thr, showFalse, wait_time0, win_name ) writer.write(vis_frame)这里有一个值得注意的细节官方脚本里用于展示结果的是show_result_pyplot而这里我直接调用了model.show_result并且把show参数设成了False。原因在于show_result_pyplot内部会调用plt.show()在视频循环里会导致每一帧都弹出显示窗口既不实用也会拖垮处理速度。show_result接口只负责返回绘制好的图像数组不涉及任何显示操作非常适合视频批处理场景。4.4 视频编码与输出视频编码这块是最容易出问题的环节我一开始写的版本输出文件经常打不开排查了半天发现是编码器选择的问题。fourcc cv2.VideoWriter_fourcc(*MJPG) writer cv2.VideoWriter(out_path, fourcc, fps, (width, height))如果输出的是avi文件用MJPG编码通常非常稳定几乎不会出问题。如果输出mp4文件可以尝试MP4V编码或者X264编码但要注意X264编码在部分OpenCV版本里不可用。我建议快速测试一下编码器是否正常工作写几帧后立刻释放VideoWriter然后用VideoCapture重新打开看看是否能正常读取。还有一个容易忽略的坑视频帧的尺寸必须和VideoWriter初始化时的尺寸完全一致。如果你中间对帧做了缩放操作写入的时候就会报错或者生成损坏文件。所以如果输入视频的分辨率和你预期不一致要在创建VideoWriter之前统一处理。4.5 完整的核心循环代码把上面的内容整合起来主处理函数的完整结构大概是这样的def process_video(model, cap, writer, score_thr): frame_count 0 while True: ret, frame cap.read() if not ret: break result inference_detector(model, frame) vis_frame model.show_result( frame, result, score_thrscore_thr, showFalse) writer.write(vis_frame) frame_count 1 if frame_count % 100 0: print(fProcessed {frame_count} frames) print(fDone. Total frames: {frame_count})每处理100帧打印一次进度这个习惯在跑长视频时非常有用。视频处理不像单张图片可能一跑就是几分钟甚至几十分钟没有进度反馈会让人心里完全没底。5. 实操过程与性能调优5.1 从命令行启动视频检测组装好脚本后运行方式非常直接python demo/video_demo.py \ configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py \ checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth \ input/test_video.mp4 \ --out output/result_video.avi \ --device cuda:0 \ --score-thr 0.3跑起来之后你会看到控制台每隔100帧输出一次进度信息。整个过程就是一次视频逐帧推理的流水线模型对每一帧独立执行检测然后把结果框画上去再写入输出文件。5.2 推理速度的实测与瓶颈分析开GPU跑和纯CPU跑完全两回事。我用一块中端GPU测试单帧推理时间大概在几十毫秒级别一秒能处理十几帧甚至更快这个速度已经能勉强接近实时。而纯CPU环境下单帧推理可能要几百毫秒甚至一秒以上视频基本是一卡一卡地处理。这里要对性能有一个基本认知目标检测的视频处理和视频编解码不同每一帧都要过一遍完整的神经网络前向传播计算量非常大。如果对速度有硬性要求可以考虑模型裁剪、量化、TensorRT加速等方式但那是另一个层面的优化话题了这里不展开。5.3 帧率与现实约束的取舍有一个很现实的问题需要注意就算单帧推理只需要50毫秒处理一个30帧每秒、时长10分钟的视频也需要大约5分钟。如果单帧推理是200毫秒那同样的视频要跑20分钟。视频检测的时间成本是线性的没有捷径可走。如果你只是需要快速粗看一遍效果建议先把视频抽帧压缩比如把视频降到10fps再处理这样能节约不少时间。等确认模型表现稳定了再跑全帧率版本做最终输出。5.4 显存占用与批量处理的权衡逐帧推理时显存占用非常稳定因为每帧都是独立推理不存在梯度累积这类操作。不过如果你想追求更高的吞吐量可以考虑把多帧组成一个batch一起送进模型。目前MMDetection的inference_detector默认是单帧输入想要batch推理需要自己额外处理复杂度会上升不少。我的建议是先跑通单帧版本确认整个流程没问题再考虑batch优化的可能性。多数情况下单帧版本已经能满足演示和验证需求了。6. 常见问题与排查实录6.1 输出视频文件损坏或打不开这个是我遇到次数最多的问题基本全是编码器选择导致的。解决思路很简单把fourcc从MP4V或X264换成MJPG把输出文件后缀改成.avi。MJPG编码在OpenCV里兼容性最好几乎不会翻车。6.2 视频打不开isOpened返回False优先检查视频路径是否正确其次检查OpenCV版本是否支持该视频格式。我遇到过某个视频用标准播放器能打开但OpenCV就是读不了的情况通常是视频本身的编码比较特殊。解决办法是用格式转换工具把视频转成H264编码的MP4基本就能解决。6.3 处理过程中报CUDA显存不足这个一般发生在显卡显存比较小的机器上尤其是输入视频分辨率较高时。最简单的办法是先把视频分辨率缩小比如从1080p缩到720p再处理一定程度上能缓解显存压力。或者换一个更小的backbone模型。6.4 检测框在视频里画不出来注意把score_thr设置得合理一点。默认0.3可能过滤掉了一些置信度较低的检测结果如果视频里目标比较小或者比较模糊可以考虑把阈值降到0.1或0.2试试效果。6.5 结果视频画框没问题但保存后出现错位这个情况比较少见但如果出现优先检查VideoWriter初始化时传入的宽高与实际帧尺寸是否一致。OpenCV在写入时会严格按初始化尺寸处理尺寸不匹配就会导致画面被拉伸、错位甚至直接写失败。下面把上面这些情况整理成一张速查表方便后续排查问题现象主要原因解决方案输出视频无法打开编码器不兼容换成MJPG编码输出AVI格式视频读取失败路径错误或编码特殊检查路径转换视频格式CUDA显存不足分辨率过高或模型过大缩小分辨率换轻量模型检测框过少或没有置信度阈值太高降低score_thr参数画面错位或拉伸帧尺寸与Writer不一致统一分辨率后再处理7. 经验总结与扩展建议我自己实际跑下来的感受是MMDetection做视频检测的最大障碍其实不在模型本身而在工程衔接。模型推理和视频编解码是两个完全不同体系的东西前者是深度学习框架的范畴后者是传统图像处理的范畴。把两者衔接好处理好帧率、编码、分辨率这些细节整个流程才能顺畅跑起来。有几个小技巧值得分享。如果你只是临时想看效果不一定要保存视频可以直接把每一帧检测结果显示在窗口里实时观察模型表现这样调试参数时反馈更快。另一个建议是在保存视频时把模型推理的帧率信息也叠加到画面上这样最后看结果视频时能直观感受到处理速度的变化对调优很有帮助。这个video_demo.py脚本后期还可以按自己的需求继续扩展。比如加上目标计数功能统计每帧检测到多少个目标或者加目标跟踪逻辑让同一目标的边框在连续帧上稳定显示再或者接入多线程处理把视频读取、模型推理、视频写入拆到不同线程里进一步提升吞吐量。从纯技术栈提升的角度来说下一步还可以尝试用ONNX Runtime或TensorRT对模型做加速推理大幅缩短单帧推理时间让视频处理接近实时水平。有了这个脚本作为基础后续不管往哪个方向扩展核心的“视频帧输入、模型推理、结果写入”这条链路都不用再动了你只需要专注在业务逻辑这一层做增量开发就行。