想让 YOLOv8 跑在 Intel 核显/NPU 上,最省心的方式就是:导出成 OpenVINO 格式,然后继续用 ultralytics 官方接口。它把 NPU 设备抽象成 intel:NPU 这个名字,模型格式转换、前处理、后处理全包了。这一篇从 yolov8n.pt 开始,到几百张/秒的吞吐,讲完整条路。
环境:Core Ultra 5 235H / OpenVINO 2026.5.0.dev(可用设备 CPU / GPU / NPU)/ PostgreSQL 无关 / yolov8n.pt → yolov8n_openvino_model(FP16)。NPU 单帧推理延迟 ~31ms。2026-09-12 实测。
为什么要 OpenVINO 格式
PyTorch 的 .pt 是训练格式,能在 CPU 上推理但不会自动用上核显的 Xe 核心或 NPU。OpenVINO 是 Intel 的推理中间表示(IR),把模型编译成能在那两块硬件上跑的算子图。它不是一个'模型文件'那么单纯,而是和设备调度绑定的运行时生态。
| 格式 | 跑在哪 | 上手难度 |
|---|---|---|
| yolov8n.pt(PyTorch) | CPU(慢) | 零 |
| ONNX | 各后端自己接运行时 | 中 |
| yolov8n_openvino_model | CPU / GPU / NPU | 低,官方接口包了前后处理 |
两步走:导出 + 预测
第一步把 yolov8n.pt 导出成 OpenVINO 目录(会生成 yolov8n_openvino_model/,里面是 FP16 的 IR 文件):
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
model.export(format="openvino", half=True) # FP16 IR 导出
第二步加载导出的目录,指定 NPU 设备推理。关键就一个参数:device="intel:NPU"。人形检测只想要 person 这一个类别(COCO 的 class 0),用 classes=[0] 过滤;默认置信度 conf=0.25 在监控场景表现良好。
from ultralytics import YOLO
model = YOLO("yolov8n_openvino_model")
results = model.predict(
source="frame.jpg",
device="intel:NPU",
conf=0.25,
classes=[0],
)
结果对象里直接给了类别、置信度和框坐标,results[0].boxes 就能遍历。这就是'走官方路径'的红利:OpenVINO 的 letterbox、BGR 颜色顺序、NMS 坐标解码这些琐碎又容易出错的部分,全部由 ultralytics 内部完成,你永远不需要手写。
抽帧喂 NPU:每秒 1 帧就够了
实际监控场景不需要逐帧推理。用 ffmpeg 抽帧而不是 cv2 全量解码是吞吐的分水岭:
:: 每秒抽1帧(n%%fps==1 采样),比 cv2 省 hwdownload 回读
ffmpeg -i "%%f" -vf "fps=1" -f image2pipe -c:v rawvideo -pix_fmt rgb24 pipe:1
:: 或者按 n%%fps==1 抽到内存管道:检测只用 1 帧/s
算一笔账:60 秒的监控片段抽 60 帧,NPU 单进程约 13s 处理完(约 4.6 张/s,含加载开销);开了 6 个 jobs 并行,60 文件 / 1 小时视频只需要约 150s,24 倍实时。要走几百倍实时?那是 GPU+NPU 混合池的主场,见吞吐天梯实测。
双向验证:不漏报也不误报
换模型做实事前,先验证它不会瞎报。用'早上 6 点无人 + 晚上 19 点有人'两个极端各测一遍,同时看空报和漏报两面向量:
| 场景 | 结果 | 结论 |
|---|---|---|
| 早上6点无人 | 0人 / 59s, maxconf=0 | 无误报 |
| 正午无人在场 | 0人 / 59s | 该分钟确实无人 |
| 傍晚18点 | 57/59s, maxconf=0.74 | 不漏报 |
| 晚上19点 | 60/60s, maxconf=0.83 | 高可靠 |
model.predict() 官方路径。具体每条坑怎么翻车,见NPU OpenVINO 避坑录。