聊起视频检测,我猜不少人都经历过这样的尴尬:手里明明有现成的目标检测模型,可一碰到视频就手忙脚乱——解码、抽帧、预处理、一堆检测框要去重、还要考虑要不要接跟踪……一套流程折腾下来,真正的检测任务没做多少,工程“边角料”倒是攒了一大堆。后来我开始用飞桨PaddleX的通用视频检测产线,很多繁琐环节被封装成了模块化组件。输入一个视频文件或者RTSP流,直接就能得到带检测框的结果,有时候甚至一条命令行就能搞定。这篇文章会从产线在设计上到底解决了什么、环境安装、命令行实操、Python API定制,到接入自定义模型、服务化部署以及我踩过的一堆坑,一次性梳理完整。你不需要有很深的算法背景,只要装好环境、知道几个关键参数,就能把视频检测跑起来。如果你已经跑通了基础流程,想把它变成“能用”的工程化工具,那后面的自定义模型和服务化部分会更适合你。1. 视频检测产线里到底藏了什么1.1 视频检测为什么要单独做“产线”图像检测是模型吃一张图,吐一堆框;视频检测则是在一秒钟十几帧甚至几十帧的连续画面里,稳定地检测并输出结果。单纯把模型往视频帧上一套,会出现三个很现实的问题:第一是性能。视频帧必须经过解码、缩放、归一化,如果每一帧都走完整流程,普通机器很容易跑不动,十几分钟的视频可能要跑一个小时。第二是时间维度上的冗余。同一辆车在连续30帧里被重复检出30次,业务端要的是“检出这辆车”,而不是“每一帧都有框”。第三是结果输出问题。视频检测的结果如果只存在模型内部,对业务毫无意义。你得把它落成可读的JSON、带标注的视频,或者直接接入下游告警、统计系统。这三点恰恰是产线想解决的事。所谓“产线”,就是把视频读取、抽帧、检测、后处理、可视化这些环节,按固定顺序串成一条可配置的流水线。每个环节都是独立模块,你可以改帧采样间隔、改置信度阈值、换模型,而不需要把代码从头写过。我第一次用视频检测产线是在某仓储场景的模拟项目X里,需要对一段长达两小时的监控视频做物品种类识别。如果自己写,光是把流媒体稳定读进来、再处理好断流,就够忙活一阵子了。用产线之后,事情简化成两步:配好环境,把视频地址丢进去。1.2 核心模块拆解PaddleX的video_det产线核心逻辑可以看作一条五段式链路,下面这个表格是我根据自己的使用习惯梳理出来的模块职责,不同版本的模块名会略有差异,但整体思路是一致的:模块职责关键点视频源读取读取本地视频文件或RTSP等流媒体地址决定输入是文件还是流,直接影响后续缓冲设计帧采样过滤控制间隔采样,跳过部分帧间隔越大,处理帧数越少,结果时间粒度越粗预处理缩放、填充、归一化、RGB转换必须和模型训练时保持一致检测推理加载检测模型,对帧执行前向计算决定识别能力和速度上限后处理阈值过滤、NMS去重、坐标还原直接影响输出质量链路末端还有一个可视化模块,它把检测框画到原图上再编码成新视频,方便人工复核。这里我想强调一下“帧采样”和“后处理”这两个环节。很多人第一次跑产线,盯着“间隔采样”参数不理解:为什么不能处理每一帧?原因是视频检测产线的默认目标往往不是逐帧精确,而是在尽量不丢目标的前提下减掉冗余计算。比如25fps的视频,间隔2帧处理一次,大约每秒处理10帧左右,肉眼几乎看不出检测断层,但计算量能砍掉一半以上。后处理里的NMS同样重要,它把多个重叠框合并成一个,防止同一目标出现多个框。所谓“通用视频检测产线”里“通用”的意思,就是这些模块已经给到了合理默认值,你不改也能跑通,改参数则是为了适配自己的场景。2. 环境准备:装好这步,后面能省一半时间2.1 安装时最容易踩的坑:PaddlePaddle和PaddleX要分开装我见过太多人卡在第一步,明明按文档装了paddlex,import却报错。原因很简单:PaddleX本身并不自带深度学习框架,它依赖PaddlePaddle作为推理后端。如果用CPU跑,安装流程大概是:pip install paddlepaddle pip install paddlex如果你有NVIDIA显卡,需要把第一行换成对应CUDA版本的paddlepaddle-gpu。这里有个很关键的细节:GPU版本和CUDA的匹配是捆死的,比如CUDA 11.8有对应的安装索引,CUDA 12.x又是另一套索引。装错了不是报CUDA driver错误,就是运行时崩溃,表现很难猜。我自己有个习惯,安装前先跑一句:python -c "import paddle; print(paddle.__version__)"能正常打印版本号,再装PaddleX,装完后再确认一次paddlex --version。不要跳这一步,前面省的时间都会在后面莫名其妙的问题上报回来。Python版本建议使用3.9到3.12之间的稳定版本,过老的版本会导致部分依赖拉取失败,过新的版本偶尔会和某些编译组件不对齐。Windows上尤其不建议用非官方Python发行版,会少很多兼容性麻烦。2.2 首次运行前,先把模型下载问题想清楚第一次执行检测命令时,产线会自动下载默认的检测模型。模型文件通常会被缓存到用户目录下的paddlex子目录中,具体路径类似~/.paddlex/official_models。如果网络状况不稳定,自动下载容易卡在中间,表现就是命令行长时间没有输出,或者隔一会儿弹一个网络错误。我的建议是两条路:网络好就直接让它自动下载,省事;网络一般,就先把需要用的模型包单独下载好,解压后手动放到上述缓存目录,再运行时不联网也能加载。放错路径的话,产线会重新触发下载,所以判断标准很简单:看启动日志里模型是从本地加载还是从远端拉取。还有一类隐蔽问题:你之前跑过一个旧版本产线,缓存里留下的是旧模型,新版本产线启动时检测到版本不匹配,会重新下载或直接报错。遇到这种情况,把缓存目录里对应模型删掉重新拉一次就好。2.3 硬件选择建议:不是非GPU不可做视频检测,CPU完全能跑,但1080p视频加较大的检测模型时,CPU推理速度可能只有几帧每秒,长视频会很煎熬。体验上的分界线大致是这样:如果只是验证流程、处理几分钟短视频,CPU够用;如果要处理监控视频、多路视频或者RTSP在线流,建议GPU,至少是中等显存起步。显存需求也别只看模型大小,视频解码、推理中间张量、结果缓存都会占显存。批量处理多段视频时,最稳妥的办法是把batch_size调小,哪怕牺牲一点吞吐,也不能让显存爆掉。我自己的经验是:先跑一小段视频确定显存占用,再决定要不要开大batch。3. 三分钟先用起来:命令行实操3.1 一条命令跑通视频检测先不用纠结代码,命令行是最快的验证方式。准备一段常见的mp4视频,执行:paddlex --pipeline video_det --input demo.mp4 --device gpu:0 --save_dir output如果你没有GPU,把--device gpu:0换成--device cpu。第一次运行会自动下载模型,然后逐帧处理,终端会滚动显示检测进度。处理完以后,到output目录里能看到产线保存的结果文件。这段命令做了什么事并不难理解:“--pipeline video_det”告诉产线使用通用视频检测流水线;“--input demo.mp4”指定输入;--device决定用CPU还是GPU;--save_dir是结果目录。不用写任何业务代码,检测框、类别、置信度就已经被提取出来并保存。我实际跑通第一遍时,最大的感受是“原来真不用自己接解码器”。在这之前,我可是拿着工具一帧一帧读视频、再转成模型输入张量、再手动画框写保存的老办法跑了好几个项目。不是说老办法不行,而是同样的工作量,产线能让你把精力放在更上游的问题上。3.2 命令行参数怎么选才合理命令行参数里最容易出错的是路径,路径包含空格或中文时一定加引号。如果是局域网里的RTSP监控流,也可以直接用流地址,比如:paddlex --pipeline video_det --input rtsp://192.168.x.x/stream --device gpu:0 --save_dir output对于RTSP流,除了示例参数外,建议先把视频流在本地播放器里确认能够正常打开,再交给产线。否则命令行很可能卡在“等待视频源”阶段,看起来像死机,实际上是在反复重连。我还有一个习惯:正式跑长视频前,先用短视频测试参数,确认模型加载和输出格式没问题。这样就不会出现跑了一半发现阈值不对,又要全量重跑的情况。另外,把--save_dir指向专门的目录,不要和代码混在一起,清理结果时不会误删文件。3.3 产线处理完,结果文件去哪了默认情况下,output目录里会出现几类文件:带检测框的标注视频,用于肉眼确认检测效果。JSON格式的检测结果,包含每一帧或每个采样帧的检测框、类别、置信度。如果启用了逐帧保存,还会有若干可视化图片。JSON结构通常是下面这个样子:{ "input_path": "demo.mp4", "boxes": [ { "cls_id": 0, "label": "person", "score": 0.92, "coordinate": [120, 45, 540, 260] } ] }我自己看结果有一个习惯:先打开标注视频快速扫一遍,看明显漏检和误检的片段;再打开JSON,重点核对置信度接近阈值的框。因为视频检测里,肉眼看起来“很奇怪”的误检,往往来自类别相似或遮挡严重的目标,这时候调阈值没用,得从训练数据或者模型选择入手。4. Python API:按自己的节奏调产线4.1 拿到结果之后能做什么命令行适合验证,但真正要嵌入业务系统,还是得靠Python API。最基础的三行代码:from paddlex import create_pipeline pipeline = create_pipeline("video_det", device="gpu:0") results = pipeline.predict("demo.mp4") for res in results: res.print() res.save_to_json("output") res.save_to_video("output", video_format="mp4")逐行看:create_pipeline创建产线对象;“predict”是核心方法,传入视频路径,返回的是一个可迭代结果;res.print()在终端打印当前帧检测信息,save_to_json和save_to_video分别把检测结果和可视化视频保存下来。这里要特别提醒一下内存问题。视频产线的预测结果是一个生成器式的流式对象,处理长视频时,不要图省事把它一次性转成列表,也不要在一个循环里把所有中间结果攒着,否则视频越长内存涨得越厉害。正确的是处理完一段就保存一段,让结果对象及时释放。我处理过一段40分钟的1080p视频,一开始图省事把所有帧结果都收进list,跑到一半内存直接失控。改成边处理边落盘后,内存占用稳定多了。4.2 调整帧采样、阈值、推理精度的正确方式有些参数在命令行里并不直接暴露,需要透传给配置。create_pipeline支持pipeline_config参数,可以覆盖产线内部模块的默认行为。示例:pipeline = create_pipeline( "video_det", device="gpu:0", pipeline_config={ "video_det_filter": {"interval": 2}, "det_postprocess": {"score_threshold": 0.5}, "det_net": {"batch_size": 1, "precision": "fp16"} } )interval控制采样间隔。设成1意味着每一帧都检测,速度会明显下降;设成2或3,则稀疏采样。如果你要统计目标的精确出现时间,间隔不宜太大,不然目标可能只在采到的帧之间短暂出现,统计会漏掉。score_threshold是置信度阈值,默认值在不同版本里略有不同,通常落在0.3到0.5之间。阈值升高,误检减少,但漏检也会增加;反之则会把更多目标捞回来,也可能带回噪声。这是一个需要结合场景反复调的点,没有绝对正确答案,我后面单独展开。precision设为fp16是GPU常用的半精度推理,能提升吞吐,但个别小模型或老显卡可能不支持。填配置时如果报“未知参数”,别急着改代码,先看日志输出确认模块名,版本不同叫法会有差别。写配置的原则是“只改自己关心的键”,其他保持默认。4.3 批量处理视频:一个能直接用的脚本现实中很少只处理一个视频。下面这段脚本用于批量处理目录下的mp4,并为每段视频单独建立输出目录:import glob import os from paddlex import create_pipeline pipeline = create_pipeline("video_det", device="gpu:0") for video_path in glob.glob("videos/*.mp4"): basename = os.path.splitext(os.path.basename(video_path))[0] save_dir = f"output/{basename}" os.makedirs(save_dir, exist_ok=True) results = pipeline.predict(video_path) for res in results: res.save_to_json(save_dir) res.save_to_video(save_dir, video_format="mp4") print(f"{basename} 处理完成")建议在批量前先跑一段1-2分钟的样本,肉眼确认检测效果,避免花了半小时跑完却发现阈值需要调整。对批量任务,我会把不同视频的output分开存放,带检测框的标注视频和一个核心JSON放在一起,方便后续写脚本汇总统计。5. 接入自训模型:让产线认你的数据5.1 自训模型目录长什么样产线的默认模型在公开数据集上表现不错,但现实项目中,很多目标是公开模型没学过的,比如特定产线的零部件、商店里的特定商品、工地上的特定告警物。这时候就需要把自训练模型接进产线。模型目录的结构一般是这样的:my_det_model/ ├── inference.pdmodel ├── inference.pdiparams └── model.yml前两个文件是Paddle格式的推理模型文件和参数文件,model.yml声明了模型名称、输入输出格式、预处理方式等信息。如果你用的是PaddleX自己的训练功能导出的模型,目录结构基本一致,产线可以直接识别。如果是从其他框架转过来的ONNX模型,目录里一般还会包含描述模型类型的配置,model.yml里的字段会对应变化。启动产线时,它会读取model.yml,根据里面的输出字段名判断这个模型输出的结果结构。目标检测模型的输出键通常是类似“DetResult”的结构,里面包含检测框坐标、类别、置信度。5.2 用 pipeline_config 挂载自定义模型接入方式并不复杂,核心是让产线中的检测模块指向你的模型目录:pipeline = create_pipeline( "video_det", device="gpu:0", pipeline_config={ "det_net": { "model_dir": "./my_det_model", "batch_size": 1 } } )这里有两个容易出错的地方:第一个是预处理尺寸。det_pp里的size通常要跟你训练时一致,比如训练输入是640x640,产线默认预处理如果也是640x640,那就不用改;如果你的模型是固定1280,需要在det_pp里同步修改。预处理这一步对最终精度的影响,经常比模型换不换更大。第二个是类别列表。默认产线模型可能认识80类,你的自训模型只有两类,这时候产线后处理能不能正确给类别名,取决于模型配置里的标签列表是否完善。建议在接入前先单独验证一下模型文件能否正常推理,确认输出类别数量正确,再考虑视频产线集成。我通常会写一个极简脚本,加载模型对单张图片预测,跑通了再接进产线,把“模型问题”和“产线问题”分开排查。5.3 把产线一步变成HTTP服务当产线要提供给其他部门或集成到系统里,没必要再让每个调用方写Python。PaddleX支持把产线一键服务化:paddlex --serve --pipeline video_det --host 0.0.0.0 --port 8080服务启动后,接口地址就是产线名,调用方式用HTTP POST:curl -X POST http://127.0.0.1:8080/video_det \ -H "Content-Type: application/json" \ -d '{"input": "/absolute/path/to/video.mp4"}'返回体里包含了检测结果的JSON。不过要注意,服务端处理的是整个视频,视频越长,请求等待时间越长。对于超长视频或者多路视频的实时分析,建议不要直接往这个接口丢原视频,而是先做切片处理,或者把检测逻辑拆成流式任务。我一般会在服务外面再套一层任务队列:上传视频后先返回任务ID,后台处理完再通知结果,这样能避开HTTP长连接超时问题。如果你的业务场景只是单段短视频的离线检测,直接POST原始视频就很够用了。6. 实战避坑与性能调优记录6.1 性能调优的三个有效杠杆视频检测的性能瓶颈通常不在模型计算,而在解码和采样策略。我调优时只关注三个杠杆:第一个杠杆是采样间隔。这是投入产出比最高的调整:间隔从1改成2,处理帧数直接减半,耗时往往也能减一半,代价是时间粒度变粗。如果业务只需要“这段视频里有没有目标”,间隔完全可以放到3甚至4。第二个杠杆是输入分辨率。输入尺寸降一半,计算量可能降到原来的四分之一。小目标较多的场景要谨慎,因为目标可能缩到模型识别不了;如果目标本身就很大,降分辨率影响很小。第三个杠杆是推理精度和batch。GPU上用fp16,或者开启加速推理,整体吞吐能明显提升。batch_size则需要根据显存调整,显存紧张时保持1最安稳。下面是几种常见配置的相对耗时对比,不同硬件和模型会有差异,只作量级参考:配置相对耗时适用场景CPU + 间隔1 + 原分辨率基准小段验证GPU + 间隔2 + 降分辨率约基准的一半长视频批量处理GPU + 间隔3 + fp16更低实时监控流视频解码也是一个容易忽视的点。解码的瓶颈往往出现在编解码格式不兼容或者软解压力大,比如部分H.265视频在普通环境里解码很吃力,产线处理进度就卡住。遇到这种情况,优先转成H.264再丢给产线,会顺滑很多。6.2 常见报错排查速查表我把实际使用中遇到的高频问题整理成了下表,帮你快速定位:现象可能原因解决思路import后报找不到推理库PaddlePaddle没有安装,或版本不匹配先单独安装匹配的paddlepaddle再装paddlex首次运行卡在模型下载网络不稳定或缓存损坏删除缓存目录对应文件后重试,或手动放好模型包处理结果没有检测框阈值太高,或视频解码失败先看日志确认读取到帧;再调低阈值验证日志显示读取不到视频文件路径含中文/空格未加引号路径加引号,或改用绝对路径GPU显存溢出batch_size过大或模型过大缩小batch,降低采样间隔,改fp16服务化接口返回超时视频过长,处理时间超出请求等待业务侧改用异步任务队列处理检测框位置明显偏移预处理size与模型训练不一致检查det_pp尺寸并改成与训练一致换来模型后输出为空模型输出键与产线预期不一致检查model.yml的输出字段是否对得上最后一行的“输出键”问题我单独说明一下:产线后处理是按固定字段名解析模型输出的,如果你的模型把检测结果命名成了别的键,产线拿到后不知道哪些是框、哪些是类别,自然就把结果丢了。解决办法是改model.yml,或者在后处理配置里把字段名映射指过去。遇到输出为空,不要只怀疑阈值,先查这一步。6.3 我对阈值调参和视频检测的两点特别体会第一点,阈值不能拿来就用。不同场景下目标出现的形态差别很大:道路监控里车辆大而清晰,阈值设低一点问题不大;工业质检里目标小、背景复杂,阈值设太低会把噪声也框进来。我的调法是从0.5开始,先跑一段真实视频,观察漏检和误检的比例,然后以0.1为步长调整,每次只调一个参数,记录结果。这轮操作下来,你会对自己的场景形成很具体的感觉,比空想有效得多。第二点,视频检测和图像检测在结果形态上很不一样。视频里同一个目标被检出、消失、再检出,不代表目标真的出现了两次,可能只是目标被遮挡了几帧。如果你要做的是目标计数,不能只数检测框,还要结合跟踪模块做前后帧关联,否则计数会翻倍。产线里的检测模型只回答“当前帧有什么”,不回答“这是哪个目标”,把这个边界搞清楚,业务逻辑才不会做错。我个人做这类项目的习惯是:先花一两天把产线跑通、把结果格式摸熟,然后花更多时间在“用什么模型、怎么调采样和阈值、怎么验证效果”这上面。产线给我的真实价值不是省了写代码的时间,而是让我从反复造轮子的循环里跳出来,把精力放到真正影响交付质量的环节。如果你也在折腾视频检测,我建议先跑通默认产线再谈定制,那一步什么时候开始都不算晚。
聊起视频检测,我猜不少人都经历过这样的尴尬:手里明明有现成的目标检测模型,可一碰到视频就手忙脚乱——解码、抽帧、预处理、一堆检测框要去重、还要考虑要不要接跟踪……一套流程折腾下来,真正的检测任务没做多少,工程“边角料”倒是攒了一大堆。后来我开始用飞桨PaddleX的通用视频检测产线,很多繁琐环节被封装成了模块化组件。输入一个视频文件或者RTSP流,直接就能得到带检测框的结果,有时候甚至一条命令行就能搞定。这篇文章会从产线在设计上到底解决了什么、环境安装、命令行实操、Python API定制,到接入自定义模型、服务化部署以及我踩过的一堆坑,一次性梳理完整。你不需要有很深的算法背景,只要装好环境、知道几个关键参数,就能把视频检测跑起来。如果你已经跑通了基础流程,想把它变成“能用”的工程化工具,那后面的自定义模型和服务化部分会更适合你。1. 视频检测产线里到底藏了什么1.1 视频检测为什么要单独做“产线”图像检测是模型吃一张图,吐一堆框;视频检测则是在一秒钟十几帧甚至几十帧的连续画面里,稳定地检测并输出结果。单纯把模型往视频帧上一套,会出现三个很现实的问题:第一是性能。视频帧必须经过解码、缩放、归一化,如果每一帧都走完整流程,普通机器很容易跑不动,十几分钟的视频可能要跑一个小时。第二是时间维度上的冗余。同一辆车在连续30帧里被重复检出30次,业务端要的是“检出这辆车”,而不是“每一帧都有框”。第三是结果输出问题。视频检测的结果如果只存在模型内部,对业务毫无意义。你得把它落成可读的JSON、带标注的视频,或者直接接入下游告警、统计系统。这三点恰恰是产线想解决的事。所谓“产线”,就是把视频读取、抽帧、检测、后处理、可视化这些环节,按固定顺序串成一条可配置的流水线。每个环节都是独立模块,你可以改帧采样间隔、改置信度阈值、换模型,而不需要把代码从头写过。我第一次用视频检测产线是在某仓储场景的模拟项目X里,需要对一段长达两小时的监控视频做物品种类识别。如果自己写,光是把流媒体稳定读进来、再处理好断流,就够忙活一阵子了。用产线之后,事情简化成两步:配好环境,把视频地址丢进去。1.2 核心模块拆解PaddleX的video_det产线核心逻辑可以看作一条五段式链路,下面这个表格是我根据自己的使用习惯梳理出来的模块职责,不同版本的模块名会略有差异,但整体思路是一致的:模块职责关键点视频源读取读取本地视频文件或RTSP等流媒体地址决定输入是文件还是流,直接影响后续缓冲设计帧采样过滤控制间隔采样,跳过部分帧间隔越大,处理帧数越少,结果时间粒度越粗预处理缩放、填充、归一化、RGB转换必须和模型训练时保持一致检测推理加载检测模型,对帧执行前向计算决定识别能力和速度上限后处理阈值过滤、NMS去重、坐标还原直接影响输出质量链路末端还有一个可视化模块,它把检测框画到原图上再编码成新视频,方便人工复核。这里我想强调一下“帧采样”和“后处理”这两个环节。很多人第一次跑产线,盯着“间隔采样”参数不理解:为什么不能处理每一帧?原因是视频检测产线的默认目标往往不是逐帧精确,而是在尽量不丢目标的前提下减掉冗余计算。比如25fps的视频,间隔2帧处理一次,大约每秒处理10帧左右,肉眼几乎看不出检测断层,但计算量能砍掉一半以上。后处理里的NMS同样重要,它把多个重叠框合并成一个,防止同一目标出现多个框。所谓“通用视频检测产线”里“通用”的意思,就是这些模块已经给到了合理默认值,你不改也能跑通,改参数则是为了适配自己的场景。2. 环境准备:装好这步,后面能省一半时间2.1 安装时最容易踩的坑:PaddlePaddle和PaddleX要分开装我见过太多人卡在第一步,明明按文档装了paddlex,import却报错。原因很简单:PaddleX本身并不自带深度学习框架,它依赖PaddlePaddle作为推理后端。如果用CPU跑,安装流程大概是:pip install paddlepaddle pip install paddlex如果你有NVIDIA显卡,需要把第一行换成对应CUDA版本的paddlepaddle-gpu。这里有个很关键的细节:GPU版本和CUDA的匹配是捆死的,比如CUDA 11.8有对应的安装索引,CUDA 12.x又是另一套索引。装错了不是报CUDA driver错误,就是运行时崩溃,表现很难猜。我自己有个习惯,安装前先跑一句:python -c "import paddle; print(paddle.__version__)"能正常打印版本号,再装PaddleX,装完后再确认一次paddlex --version。不要跳这一步,前面省的时间都会在后面莫名其妙的问题上报回来。Python版本建议使用3.9到3.12之间的稳定版本,过老的版本会导致部分依赖拉取失败,过新的版本偶尔会和某些编译组件不对齐。Windows上尤其不建议用非官方Python发行版,会少很多兼容性麻烦。2.2 首次运行前,先把模型下载问题想清楚第一次执行检测命令时,产线会自动下载默认的检测模型。模型文件通常会被缓存到用户目录下的paddlex子目录中,具体路径类似~/.paddlex/official_models。如果网络状况不稳定,自动下载容易卡在中间,表现就是命令行长时间没有输出,或者隔一会儿弹一个网络错误。我的建议是两条路:网络好就直接让它自动下载,省事;网络一般,就先把需要用的模型包单独下载好,解压后手动放到上述缓存目录,再运行时不联网也能加载。放错路径的话,产线会重新触发下载,所以判断标准很简单:看启动日志里模型是从本地加载还是从远端拉取。还有一类隐蔽问题:你之前跑过一个旧版本产线,缓存里留下的是旧模型,新版本产线启动时检测到版本不匹配,会重新下载或直接报错。遇到这种情况,把缓存目录里对应模型删掉重新拉一次就好。2.3 硬件选择建议:不是非GPU不可做视频检测,CPU完全能跑,但1080p视频加较大的检测模型时,CPU推理速度可能只有几帧每秒,长视频会很煎熬。体验上的分界线大致是这样:如果只是验证流程、处理几分钟短视频,CPU够用;如果要处理监控视频、多路视频或者RTSP在线流,建议GPU,至少是中等显存起步。显存需求也别只看模型大小,视频解码、推理中间张量、结果缓存都会占显存。批量处理多段视频时,最稳妥的办法是把batch_size调小,哪怕牺牲一点吞吐,也不能让显存爆掉。我自己的经验是:先跑一小段视频确定显存占用,再决定要不要开大batch。3. 三分钟先用起来:命令行实操3.1 一条命令跑通视频检测先不用纠结代码,命令行是最快的验证方式。准备一段常见的mp4视频,执行:paddlex --pipeline video_det --input demo.mp4 --device gpu:0 --save_dir output如果你没有GPU,把--device gpu:0换成--device cpu。第一次运行会自动下载模型,然后逐帧处理,终端会滚动显示检测进度。处理完以后,到output目录里能看到产线保存的结果文件。这段命令做了什么事并不难理解:“--pipeline video_det”告诉产线使用通用视频检测流水线;“--input demo.mp4”指定输入;--device决定用CPU还是GPU;--save_dir是结果目录。不用写任何业务代码,检测框、类别、置信度就已经被提取出来并保存。我实际跑通第一遍时,最大的感受是“原来真不用自己接解码器”。在这之前,我可是拿着工具一帧一帧读视频、再转成模型输入张量、再手动画框写保存的老办法跑了好几个项目。不是说老办法不行,而是同样的工作量,产线能让你把精力放在更上游的问题上。3.2 命令行参数怎么选才合理命令行参数里最容易出错的是路径,路径包含空格或中文时一定加引号。如果是局域网里的RTSP监控流,也可以直接用流地址,比如:paddlex --pipeline video_det --input rtsp://192.168.x.x/stream --device gpu:0 --save_dir output对于RTSP流,除了示例参数外,建议先把视频流在本地播放器里确认能够正常打开,再交给产线。否则命令行很可能卡在“等待视频源”阶段,看起来像死机,实际上是在反复重连。我还有一个习惯:正式跑长视频前,先用短视频测试参数,确认模型加载和输出格式没问题。这样就不会出现跑了一半发现阈值不对,又要全量重跑的情况。另外,把--save_dir指向专门的目录,不要和代码混在一起,清理结果时不会误删文件。3.3 产线处理完,结果文件去哪了默认情况下,output目录里会出现几类文件:带检测框的标注视频,用于肉眼确认检测效果。JSON格式的检测结果,包含每一帧或每个采样帧的检测框、类别、置信度。如果启用了逐帧保存,还会有若干可视化图片。JSON结构通常是下面这个样子:{ "input_path": "demo.mp4", "boxes": [ { "cls_id": 0, "label": "person", "score": 0.92, "coordinate": [120, 45, 540, 260] } ] }我自己看结果有一个习惯:先打开标注视频快速扫一遍,看明显漏检和误检的片段;再打开JSON,重点核对置信度接近阈值的框。因为视频检测里,肉眼看起来“很奇怪”的误检,往往来自类别相似或遮挡严重的目标,这时候调阈值没用,得从训练数据或者模型选择入手。4. Python API:按自己的节奏调产线4.1 拿到结果之后能做什么命令行适合验证,但真正要嵌入业务系统,还是得靠Python API。最基础的三行代码:from paddlex import create_pipeline pipeline = create_pipeline("video_det", device="gpu:0") results = pipeline.predict("demo.mp4") for res in results: res.print() res.save_to_json("output") res.save_to_video("output", video_format="mp4")逐行看:create_pipeline创建产线对象;“predict”是核心方法,传入视频路径,返回的是一个可迭代结果;res.print()在终端打印当前帧检测信息,save_to_json和save_to_video分别把检测结果和可视化视频保存下来。这里要特别提醒一下内存问题。视频产线的预测结果是一个生成器式的流式对象,处理长视频时,不要图省事把它一次性转成列表,也不要在一个循环里把所有中间结果攒着,否则视频越长内存涨得越厉害。正确的是处理完一段就保存一段,让结果对象及时释放。我处理过一段40分钟的1080p视频,一开始图省事把所有帧结果都收进list,跑到一半内存直接失控。改成边处理边落盘后,内存占用稳定多了。4.2 调整帧采样、阈值、推理精度的正确方式有些参数在命令行里并不直接暴露,需要透传给配置。create_pipeline支持pipeline_config参数,可以覆盖产线内部模块的默认行为。示例:pipeline = create_pipeline( "video_det", device="gpu:0", pipeline_config={ "video_det_filter": {"interval": 2}, "det_postprocess": {"score_threshold": 0.5}, "det_net": {"batch_size": 1, "precision": "fp16"} } )interval控制采样间隔。设成1意味着每一帧都检测,速度会明显下降;设成2或3,则稀疏采样。如果你要统计目标的精确出现时间,间隔不宜太大,不然目标可能只在采到的帧之间短暂出现,统计会漏掉。score_threshold是置信度阈值,默认值在不同版本里略有不同,通常落在0.3到0.5之间。阈值升高,误检减少,但漏检也会增加;反之则会把更多目标捞回来,也可能带回噪声。这是一个需要结合场景反复调的点,没有绝对正确答案,我后面单独展开。precision设为fp16是GPU常用的半精度推理,能提升吞吐,但个别小模型或老显卡可能不支持。填配置时如果报“未知参数”,别急着改代码,先看日志输出确认模块名,版本不同叫法会有差别。写配置的原则是“只改自己关心的键”,其他保持默认。4.3 批量处理视频:一个能直接用的脚本现实中很少只处理一个视频。下面这段脚本用于批量处理目录下的mp4,并为每段视频单独建立输出目录:import glob import os from paddlex import create_pipeline pipeline = create_pipeline("video_det", device="gpu:0") for video_path in glob.glob("videos/*.mp4"): basename = os.path.splitext(os.path.basename(video_path))[0] save_dir = f"output/{basename}" os.makedirs(save_dir, exist_ok=True) results = pipeline.predict(video_path) for res in results: res.save_to_json(save_dir) res.save_to_video(save_dir, video_format="mp4") print(f"{basename} 处理完成")建议在批量前先跑一段1-2分钟的样本,肉眼确认检测效果,避免花了半小时跑完却发现阈值需要调整。对批量任务,我会把不同视频的output分开存放,带检测框的标注视频和一个核心JSON放在一起,方便后续写脚本汇总统计。5. 接入自训模型:让产线认你的数据5.1 自训模型目录长什么样产线的默认模型在公开数据集上表现不错,但现实项目中,很多目标是公开模型没学过的,比如特定产线的零部件、商店里的特定商品、工地上的特定告警物。这时候就需要把自训练模型接进产线。模型目录的结构一般是这样的:my_det_model/ ├── inference.pdmodel ├── inference.pdiparams └── model.yml前两个文件是Paddle格式的推理模型文件和参数文件,model.yml声明了模型名称、输入输出格式、预处理方式等信息。如果你用的是PaddleX自己的训练功能导出的模型,目录结构基本一致,产线可以直接识别。如果是从其他框架转过来的ONNX模型,目录里一般还会包含描述模型类型的配置,model.yml里的字段会对应变化。启动产线时,它会读取model.yml,根据里面的输出字段名判断这个模型输出的结果结构。目标检测模型的输出键通常是类似“DetResult”的结构,里面包含检测框坐标、类别、置信度。5.2 用 pipeline_config 挂载自定义模型接入方式并不复杂,核心是让产线中的检测模块指向你的模型目录:pipeline = create_pipeline( "video_det", device="gpu:0", pipeline_config={ "det_net": { "model_dir": "./my_det_model", "batch_size": 1 } } )这里有两个容易出错的地方:第一个是预处理尺寸。det_pp里的size通常要跟你训练时一致,比如训练输入是640x640,产线默认预处理如果也是640x640,那就不用改;如果你的模型是固定1280,需要在det_pp里同步修改。预处理这一步对最终精度的影响,经常比模型换不换更大。第二个是类别列表。默认产线模型可能认识80类,你的自训模型只有两类,这时候产线后处理能不能正确给类别名,取决于模型配置里的标签列表是否完善。建议在接入前先单独验证一下模型文件能否正常推理,确认输出类别数量正确,再考虑视频产线集成。我通常会写一个极简脚本,加载模型对单张图片预测,跑通了再接进产线,把“模型问题”和“产线问题”分开排查。5.3 把产线一步变成HTTP服务当产线要提供给其他部门或集成到系统里,没必要再让每个调用方写Python。PaddleX支持把产线一键服务化:paddlex --serve --pipeline video_det --host 0.0.0.0 --port 8080服务启动后,接口地址就是产线名,调用方式用HTTP POST:curl -X POST http://127.0.0.1:8080/video_det \ -H "Content-Type: application/json" \ -d '{"input": "/absolute/path/to/video.mp4"}'返回体里包含了检测结果的JSON。不过要注意,服务端处理的是整个视频,视频越长,请求等待时间越长。对于超长视频或者多路视频的实时分析,建议不要直接往这个接口丢原视频,而是先做切片处理,或者把检测逻辑拆成流式任务。我一般会在服务外面再套一层任务队列:上传视频后先返回任务ID,后台处理完再通知结果,这样能避开HTTP长连接超时问题。如果你的业务场景只是单段短视频的离线检测,直接POST原始视频就很够用了。6. 实战避坑与性能调优记录6.1 性能调优的三个有效杠杆视频检测的性能瓶颈通常不在模型计算,而在解码和采样策略。我调优时只关注三个杠杆:第一个杠杆是采样间隔。这是投入产出比最高的调整:间隔从1改成2,处理帧数直接减半,耗时往往也能减一半,代价是时间粒度变粗。如果业务只需要“这段视频里有没有目标”,间隔完全可以放到3甚至4。第二个杠杆是输入分辨率。输入尺寸降一半,计算量可能降到原来的四分之一。小目标较多的场景要谨慎,因为目标可能缩到模型识别不了;如果目标本身就很大,降分辨率影响很小。第三个杠杆是推理精度和batch。GPU上用fp16,或者开启加速推理,整体吞吐能明显提升。batch_size则需要根据显存调整,显存紧张时保持1最安稳。下面是几种常见配置的相对耗时对比,不同硬件和模型会有差异,只作量级参考:配置相对耗时适用场景CPU + 间隔1 + 原分辨率基准小段验证GPU + 间隔2 + 降分辨率约基准的一半长视频批量处理GPU + 间隔3 + fp16更低实时监控流视频解码也是一个容易忽视的点。解码的瓶颈往往出现在编解码格式不兼容或者软解压力大,比如部分H.265视频在普通环境里解码很吃力,产线处理进度就卡住。遇到这种情况,优先转成H.264再丢给产线,会顺滑很多。6.2 常见报错排查速查表我把实际使用中遇到的高频问题整理成了下表,帮你快速定位:现象可能原因解决思路import后报找不到推理库PaddlePaddle没有安装,或版本不匹配先单独安装匹配的paddlepaddle再装paddlex首次运行卡在模型下载网络不稳定或缓存损坏删除缓存目录对应文件后重试,或手动放好模型包处理结果没有检测框阈值太高,或视频解码失败先看日志确认读取到帧;再调低阈值验证日志显示读取不到视频文件路径含中文/空格未加引号路径加引号,或改用绝对路径GPU显存溢出batch_size过大或模型过大缩小batch,降低采样间隔,改fp16服务化接口返回超时视频过长,处理时间超出请求等待业务侧改用异步任务队列处理检测框位置明显偏移预处理size与模型训练不一致检查det_pp尺寸并改成与训练一致换来模型后输出为空模型输出键与产线预期不一致检查model.yml的输出字段是否对得上最后一行的“输出键”问题我单独说明一下:产线后处理是按固定字段名解析模型输出的,如果你的模型把检测结果命名成了别的键,产线拿到后不知道哪些是框、哪些是类别,自然就把结果丢了。解决办法是改model.yml,或者在后处理配置里把字段名映射指过去。遇到输出为空,不要只怀疑阈值,先查这一步。6.3 我对阈值调参和视频检测的两点特别体会第一点,阈值不能拿来就用。不同场景下目标出现的形态差别很大:道路监控里车辆大而清晰,阈值设低一点问题不大;工业质检里目标小、背景复杂,阈值设太低会把噪声也框进来。我的调法是从0.5开始,先跑一段真实视频,观察漏检和误检的比例,然后以0.1为步长调整,每次只调一个参数,记录结果。这轮操作下来,你会对自己的场景形成很具体的感觉,比空想有效得多。第二点,视频检测和图像检测在结果形态上很不一样。视频里同一个目标被检出、消失、再检出,不代表目标真的出现了两次,可能只是目标被遮挡了几帧。如果你要做的是目标计数,不能只数检测框,还要结合跟踪模块做前后帧关联,否则计数会翻倍。产线里的检测模型只回答“当前帧有什么”,不回答“这是哪个目标”,把这个边界搞清楚,业务逻辑才不会做错。我个人做这类项目的习惯是:先花一两天把产线跑通、把结果格式摸熟,然后花更多时间在“用什么模型、怎么调采样和阈值、怎么验证效果”这上面。产线给我的真实价值不是省了写代码的时间,而是让我从反复造轮子的循环里跳出来,把精力放到真正影响交付质量的环节。如果你也在折腾视频检测,我建议先跑通默认产线再谈定制,那一步什么时候开始都不算晚。