把 YOLOv8n 跑到 NPU 上:OpenVINO 导出 + ultralytics 官方路径实测

想让 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_modelCPU / 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高可靠
避坑提醒:手写 OpenVINO 的预处理/后处理(letterbox、BGR、NMS 坐标解码)在这块硬件上全部失败过,务必走 model.predict() 官方路径。具体每条坑怎么翻车,见NPU OpenVINO 避坑录

To run YOLOv8 on an Intel iGPU/NPU, the least-effort route is: export to OpenVINO format, then keep using the official ultralytics API. The NPU is abstracted as intel:NPU, and format conversion, pre-processing and post-processing are all handled. This guide goes from a raw yolov8n.pt all the way to hundreds of FPS.

Env: Core Ultra 5 235H / OpenVINO 2026.5.0.dev (devices: CPU/GPU/NPU) / yolov8n.pt → yolov8n_openvino_model (FP16). NPU latency ~31ms/frame. Tested 2026-09-12.

Why OpenVINO format

PyTorch's .pt is a training format — it infers on CPU but won't automatically touch the iGPU's Xe cores or the NPU. OpenVINO is Intel's intermediate representation (IR), compiling the model into an operator graph that runs on those two chips. It's less a 'model file' than a device-scheduling runtime ecosystem.

FormatRuns onEffort
yolov8n.pt(PyTorch)CPU (slow)zero
ONNXbring-your-own runtimemedium
yolov8n_openvino_modelCPU / GPU / NPUlow — official API handles it

Two steps: export + predict

Step one exports yolov8n.pt to an OpenVINO folder (you get yolov8n_openvino_model/ holding FP16 IR files):

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
model.export(format="openvino", half=True)  # FP16 IR 导出

Step two loads the exported folder and sets the NPU device. One flag matters: device="intel:NPU". For person detection alone use classes=[0] (COCO class 0); the default conf=0.25 behaved well on surveillance footage.

from ultralytics import YOLO

model = YOLO("yolov8n_openvino_model")
results = model.predict(
    source="frame.jpg",
    device="intel:NPU",
    conf=0.25,
    classes=[0],
)

The result object gives you classes, confidence and box coords directly — iterate results[0].boxes. That's the payoff of the official path: OpenVINO's letterbox, BGR ordering and NMS decode — fiddly, error-prone bits — are all handled inside ultralytics. You never hand-write any of it.

Feeding the NPU: one frame per second is enough

Real surveillance doesn't need per-frame inference. Pulling frames with ffmpeg instead of cv2 full-decode is the watershed for throughput:

:: 每秒抽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

Quick math: a 60s clip yields 60 frames, and one NPU process takes ~13s (≈4.6 FPS incl. load overhead); with 6 parallel jobs, 60 files / 1h of video needs only ~150s — 24× realtime. For hundreds of × realtime, that's the hybrid GPU+NPU pool's lane — see the throughput ladder.

Two-way validation: no misses, no false alarms

Before trusting the model for real work, prove it doesn't fire randomly. Test both extremes — an empty 6am and a busy 7pm — covering false positives and misses in one pass:

SceneResultVerdict
6am, empty0人 / 59s, maxconf=0no false positives
noon, nobody present0人 / 59sgenuinely empty
6pm57/59s, maxconf=0.74no misses
7pm60/60s, maxconf=0.83highly reliable
Watch out:Hand-written OpenVINO pre/post-processing (letterbox, BGR, NMS decode) failed on every attempt here — always use model.predict(). Every crash is logged in NPU OpenVINO pitfalls.
返回文章列表