
自 2018 年以来,OpenCV 终于迎来了一次大版本更新。OpenCV 5.0 于 2026 年 6 月发布,对计算机视觉工程师来说,最大的变化是 DNN 模块被彻底重写,换上了一套为现代神经网络打造的新推理引擎。这也引出了一个很实际的问题:只靠 CPU,能不能把当下最先进的目标检测器跑得又快又准?
为了让演示落地,我们把检测器对准了正在发生的事。2026 年世界杯正在美国、加拿大、墨西哥三国开踢。于是我们直接拉来赛事片段,让一个完全跑在 OpenCV 5 DNN 模块里的 YOLO26 模型,把场上每一名球员和那颗球都框出来。
这里没有 GPU,推理时也没有 PyTorch,只有一个 ONNX 文件加上 OpenCV。这是系列第一篇,先把完整的 Python 流程讲透。第二篇会把同一条流水线搬进 C++,后面的篇章还会继续挖掘 OpenCV 5 的本事,从姿态估计到图像分割。
上面那张任意球画面里,OpenCV 5 配合 YOLO26 把球员和球全都检测了出来。

你将学到
读完这篇,你将能够:装好 OpenCV 5;用正确的参数把 YOLO26 模型导出成 ONNX;借助 OpenCV 5 全新的 DNN 引擎,在 CPU 上完成图片与视频的目标检测。此外,你还会弄明白,为什么 YOLO26 让后处理比早年的 YOLO 模型简单了一大截。
1. 为什么是 OpenCV 5?新在哪
OpenCV 5.0 是自 2018 年 OpenCV 4.0 之后的第一个大版本,现代化的地方着实不少。动手写代码之前,有几处变化得先心里有数。比如现在最低要求是 C++17,老旧的 C 语言接口已被移除。再比如 DNN 模块不再支持 Darknet 和 Caffe 的导入器。结果就是,早年那种加载 .cfg 和 .weights 文件的 YOLOv3、YOLOv4 流程,没法直接用了,正道是先转成 ONNX。
不过最重头的,还是 DNN 的重写。具体而言,OpenCV 5 并排提供了两套推理引擎,通过一个全新的引擎类型来切换。新引擎是一套重写的图引擎,带形状推导、常量折叠和算子融合,把 ONNX 算子的覆盖率从原来的一小部分提升到了绝大多数。说人话就是:更多导出的模型,包括现代的 YOLO 检测器,现在能直接加载运行,不用再手工改图。
以上只是简要概括。完整的新特性清单,请看官方发布说明与 OpenCV 5 的 wiki 页面(文末参考资料给出了名称)。
2. 准备环境
2.1 安装 OpenCV 5
OpenCV 5 的 wheel 包已经上了 PyPI,Python 这边一句 pip install 就够。实际操作里,我们建议用隔离的虚拟环境,尤其是你别的工程还在用 OpenCV 4.x 的时候,因为原地升级全局安装,可能让不相干的代码行为悄悄变了。
python
python -m venv .venv # Windows: .venv\Scripts\activate # Linux/macOS: source .venv/bin/activate pip install "opencv-python==5.0.0.93" ultralytics onnx onnxruntime
当然,这个 pip wheel 只有 CPU 版。它内置了新引擎和经典的 CPU 路径,但不带 CUDA。要想上 GPU,就得从源码编译 OpenCV,这超出了本文范围,毕竟我们的全部前提就是 CPU 推理。
2.2 把 YOLO26 导出成 ONNX
OpenCV 的 DNN 模块读的是 ONNX 文件,所以第一步是把 YOLO26 从原生格式导出来。这里有三个导出参数最关键:用 12 或更高的 opset;用固定输入尺寸导出;不要把 NMS 烤进图里。
python
from ultralytics import YOLO model = YOLO("yolo26n.pt") # nano = fastest on CPU model.export( format="onnx", opset=12, # OpenCV 5 DNN needs opset >= 12 imgsz=640, # fixed square input dynamic=False, # static shapes avoid a known DNN parsing issue simplify=True, # fold constants for a cleaner graph nms=False, # keep the raw head; YOLO26 is already NMS-free )
3. 用 OpenCV 5 跑 YOLO26
3.1 加载模型并选择引擎
这一点,正是 OpenCV 5 和 4.x 差别最大的地方。实际写起来,readNet 调用现在多了一个引擎参数,OpenCV 把可选的引擎做成了枚举。
python
import cv2 def build_net(onnx_path, engine="auto"): engines = { "auto": cv2.dnn.ENGINE_AUTO, # new engine, falls back to classic "new": cv2.dnn.ENGINE_NEW, # OpenCV 5's rewritten CPU engine "classic": cv2.dnn.ENGINE_CLASSIC, # the 4.x engine (needed for CUDA) "ort": cv2.dnn.ENGINE_ORT, # bundled ONNX Runtime (source build) } # readNet takes (model, config, framework, engine) in OpenCV 5 return cv2.dnn.readNet(onnx_path, "", "", engines[engine.lower()])
3.2 预处理与无 NMS 的输出
YOLO26 是一个端到端、无 NMS 的检测器。早年的 YOLO 模型会吐出一大张候选框网格(比如 YOLOv8 那个 1, 84, 8400 的张量),你得先做转置,再用非极大值抑制(NMS)清理一遍。反观 YOLO26,它直接给出一个固定的 1, 300, 6 张量,300 行里的每一行已经是一个最终检测结果:x1、y1、x2、y2、置信度、类别编号。于是既不用转置,也没有 NMS 调用。到头来,我们要做的只是把低于置信度阈值的行丢掉,再把框映射回原始画面。
python
def detect(net, img, size=640, conf_thres=0.25): padded, r, (dw, dh) = letterbox(img, size) # keep aspect ratio blob = cv2.dnn.blobFromImage(padded, 1/255.0, (size, size), swapRB=True, crop=False) net.setInput(blob) out = net.forward()[0] # (300, 6): x1,y1,x2,y2,score,class dets = [] for x1, y1, x2, y2, score, cls in out: if score < conf_thres: continue # undo the letterbox padding and scaling dets.append(((x1-dw)/r, (y1-dh)/r, (x2-dw)/r, (y2-dh)/r, float(score), int(cls))) return dets
上面用到的 letterbox 和 draw 是项目中配套的辅助函数,分别负责保持长宽比填充与画框,下文不再展开。
3.3 在图片上做检测
python
net = build_net("models/yolo26n_640.onnx", engine="auto") img = cv2.imread("media/wc2026_still_1.jpg") detect(net, img) # warm-up dets = detect(net, img, conf_thres=0.3) # timed run cv2.imwrite("outputs/detected.jpg", draw(img, dets))
来看一个输出示例:世界杯比赛里梅西带球的一帧,球员和球都被框了出来。
切到视频之前,先插一句题外话。如果你盯着那个 10 号脚下的球看,YOLO26 很自信地把它标成了「人」。有人要较真了:在他们看来,这框明明该标成「球王」才对。可惜,模型训练用的 COCO 数据集压根没设这个类别,所以「人」已经是它眼下能给的最好答案。

3.4 在视频上做检测
说白了,视频不过是把同一个调用塞进读取循环里。OpenCV 负责解码和画框,模型则在每一帧上跑一遍。
python
cap = cv2.VideoCapture("media/Messi_Goal.mp4") writer = cv2.VideoWriter("outputs/detected.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h)) while True: ok, frame = cap.read() if not ok: break dets = detect(net, frame, conf_thres=0.3) writer.write(draw(frame, dets))
拿一段片段跑这个循环,就能得到下面那样的视频输出。我们还往前多走了一步:按队伍给每个框上色,一边绿、一边红。这个上色是上面代码片段之外的小附加,你想怎么区分两队都行。
(原文此处附检测结果视频与按队伍上色的示例;因源站资源在当前环境下无法抓取,本稿未附,详见《发布说明》。)
4. OpenCV 5 到底在哪儿出力?
这里值得把话说准,因为这一点很容易被夸大。训练好的 YOLO26 模型是大脑:真正分得清球员和球的,是它。而 OpenCV 5 是驱动这颗大脑、并包揽周边所有像素管线的引擎。
具体到每一帧,OpenCV 5 其实干了五件相互独立的事。先用 VideoCapture 或 imread 把像素读进来;接着用 blobFromImage 做预处理;然后在自己的 DNN 引擎里用 net.forward() 跑网络,全程不依赖 PyTorch 或任何外部运行时;最后画框、写输出。
所以真正「OpenCV 5 新增」的部分虽小,却是核心:正是 3.1 节那个引擎枚举,以及扩展后的 readNet 签名,二者合力,把模型跑在了重写过的 CPU 引擎上。
5. 效果
在高分辨率静态图上,检测器稳稳框住了每一名球员、门将和球。即便是任意球开进一片人堆的禁区,它也能把叠在一起的球衣干净地分开。
快节奏动作意味着运动模糊,而转播画面极少锐利如刀。让人放心的是,检测器在这里扛得住。下面那张模糊的帧里,哪怕四肢拉出残影、边缘发软,YOLO26 依旧框住了门将和场上球员。这份稳健来自在海量、多样的数据集上的训练,所以一点点模糊很少让检测翻车。当然,极重的模糊,或者一颗又小又快的球,会更难应付,这时候换更高分辨率的模型,或者调低置信度阈值,就有用了。
同一套检测器在视频上一样跑得欢。下面这段片段里,它一帧一帧地跟着球员,靠纯 CPU 就跑出了实时速度。
最后再看一个运动战的例子。哪怕球员散落满场,框依旧贴得紧,球也照样被挑出来。


6. 结语
一句话总结:OpenCV 5 让这个库从「围着别人模型打杂的预处理帮手」,变成了一个正经、自洽的推理运行时。配上 YOLO26,这一点变得具体可感。因为 YOLO26 无 NMS,把原始输出整理成干净检测的代码几乎不费吹灰之力。又因为两边都很轻快,我们靠纯 CPU 就跑通了真实的 2026 世界杯检测。第二篇里,我们会把这条流水线原封不动搬进 C++,对着 OpenCV 5 编译一遍,看看没了 Python 之后故事怎么讲。除了检测,同一套 OpenCV 5 DNN 引擎还能干更多,后续篇章会让它在姿态估计、图像分割等任务上一展身手。