ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLO推理实战:从best.pt到图片、视频与摄像头检测

YOLO推理实战:从best.pt到图片、视频与摄像头检测 训练完模型拿到一个看起来很普通的best.pt文件之后真正的开发工作其实才刚刚开始。很多刚接触 YOLO 的开发者会卡在一个奇怪的坎上训练时跟着教程跑得很顺但到了自己写代码做图片检测、视频检测、摄像头实时检测时反而经常出问题。明明模型都训练好了却不知道这个权重文件该怎么“用起来”。这篇文章想解决的核心问题就是一件事把 YOLO 的推理链路讲清楚。不管是 YOLOv8、YOLO11还是你在某些工程代码里看到的 YOLO 其他版本分支只要手上有一份训练好的best.pt图片、视频、摄像头三种输入场景的代码骨架其实是高度一致的。真正拉开差距的地方不是算法本身而是你有没有理解predict的输入输出、参数含义和运行环境。如果你是第一次把训练好的模型接入业务代码这篇文章可以帮你少踩很多坑。如果你已经会跑官方示例但还不清楚如何解析检测结果、如何提高视频推理速度、如何排查摄像头检测问题这篇文章也值得过一遍。1. 为什么拿到 best.pt 之后很多人反而不会用了先说一个很常见的现象。很多 YOLO 新手的工作流程是找一份数据集跑通 train 脚本训练结束后看到runs/detect/train/weights/目录下出现了best.pt和last.pt觉得大功告成。但当你真正要把这个模型放到应用里时会立刻遇到一系列问题不知道代码应该怎么写只能继续复制网上的推理示例网上示例用的权重文件是yolov8n.pt换成自己的best.pt后结果不对图片检测能跑但不知道如何拿到检测框的坐标和类别名称视频检测跑一段就卡顿甚至内存不断增长摄像头一直打不开或者打开了但画面非常卡。这些问题的本质都一样很多人对“训练产出一个权重文件”到“把这个权重文件用于推理”之间的链路并不清楚。训练阶段YOLO 关心的是 loss、梯度、mAP推理阶段YOLO 关心的是如何把输入图片转成张量、如何前向计算、如何做 NMS、如何把结果画到原图上。这两条链路虽然在同一个框架里但思维模式完全不同。你需要开始用“输入-模型-输出”的方式来理解它而不是继续用“训练集-验证集-指标”的方式来理解它。还有一点值得先想清楚best.pt并不是一个只存了网络参数的裸文件。在 Ultralytics 框架里它保存的是训练完成后的完整模型状态包括网络结构信息、类别名称、训练超参数等。所以加载best.pt时你不需要再手写 YAML 网络结构只需要告诉框架“我要加载这个文件”就够了。2. best.pt 到底是什么以及 YOLO 版本问题2.1 best.pt 与 last.pt 的区别训练 YOLO 时模型会在每个 epoch 结束后在验证集上做一次评估。通常代码会保存两类权重文件last.pt最后一轮训练结束时保存的权重best.pt验证集某个指标比如 mAP50 或 mAP50-95 表现最好的一轮权重。由于深度学习训练过程中验证集指标最好的时刻往往不是最后一个 epoch所以常规做法都是优先加载best.pt。如果你的任务是模型部署、检测效果演示、业务接入那么直接使用best.pt是更稳妥的选择。另外如果是从官方仓库下载预训练模型例如yolov8n.pt、yolo11n.pt这些文件本质上也是“可以在 COCO 数据集上直接做检测的权重”。它们的加载方式和自定义训练出来的best.pt完全相同区别只是训练数据和类别数不同。2.2 YOLOv8、YOLO11 与其他 YOLO 分支你在项目标题里可能会看到YOLO26/11/v8这样混在一起的写法。这里有必要做一个区分目前开发者日常接触最多的稳定主线是 YOLOv8 和 YOLO11 这两个版本它们都来自 Ultralytics 开源项目。至于标题中的 YOLO26如果出现在某个代码仓库或教程里大概率是第三方实现、实验分支或教育项目命名并不是当前被大规模使用的官方版本。因此当你看到带 YOLO26 字样的工程代码时先确认它的模型文件来自哪里再决定是否直接使用。从工程角度看YOLOv8 和 YOLO11 的推理代码几乎一样因为它们都使用同一套顶层 APIfrom ultralytics import YOLO model YOLO(best.pt) results model.predict(sourceimage.jpg)不同版本加载同一个权重文件时需要保证权重文件本身是由对应版本训练出来的。你不需要担心代码的大结构变化但也不能拿着 YOLO11 训练出来的best.pt强行塞进一个老版本 YOLOv8 的代码里去加载否则可能出现结构错乱或加载失败。这一点是很多“版本混用”报错的根因。2.3 为什么一张图片检测也能算“部署”很多人一听“部署”就想到服务器、Docker、API 接口但在入门阶段把本地图片检测跑通其实就是最简单的一层部署。所谓部署本质上是把训练产物用起来让它能够对新的输入数据产生推理结果。图片、视频、摄像头只是数据输入源的三种形态YOLO 推理模型本身并不会因为输入是图片还是视频而发生改变。理解了这一点你再看后面的代码就会轻松很多核心始终是YOLO(best.pt)和.predict()需要变化的只是source参数以及视频/摄像头场景中额外的读写帧逻辑。3. 环境准备与前置条件在运行任何 YOLO 推理代码之前先确认你的 Python 环境能够正常导入ultralytics包。3.1 安装 ultralytics推荐使用 Python 3.9 及以上版本并创建一个独立的虚拟环境。安装命令非常简单pip install -U ultralytics如果你打算用 GPU 推理需要先安装与你的显卡驱动匹配的 PyTorch 版本。这里不展开写死版本号因为 PyTorch 和 CUDA 的版本矩阵变化较快。稳妥的做法是先到 PyTorch 官网选择对应的操作系统和 CUDA 版本生成安装命令然后再安装ultralytics。否则直接pip install ultralytics可能会装上一个 CPU 版 PyTorchGPU 无法被利用。安装完成后可以使用下面的代码验证环境yolo version如果能输出版本号说明命令行工具已经可用。再执行一段 Python 验证import ultralytics print(ultralytics.__version__)能正常打印版本号说明依赖安装成功。3.2 CPU 和 GPU 的选择如果你的电脑没有 NVIDIA 显卡或者显卡是 AMD RX 580 这类对 CUDA 不友好的型号不必一开始就纠结 GPU 加速先用 CPU 把代码流程跑通。YOLO 推理在 CPU 上确实会比 GPU 慢但一张普通图片通常也只需要几百毫秒到一两秒足够用来验证模型和调试代码。需要特别提醒AMD RX 580 无法直接使用 CUDA。PyTorch 官方对 AMD 显卡的支持是通过 ROCm 实现的而 RX 580 这类老架构显卡在新版本 ROCm 中支持情况并不确定。所以不要看到一个“需要安装 CUDA 吗”的帖子就急着装 CUDA先确认你的显卡到底是 NVIDIA 还是 AMD再决定环境方案。4. 推荐的项目结构与模型加载方式4.1 项目结构示例为了让代码更清晰我建议你按下面这种结构组织文件yolo_project/ ├── best.pt ├── images/ │ ├── demo.jpg │ └── test.png ├── videos/ │ └── demo.mp4 ├── outputs/ │ ├── demo_result.jpg │ └── demo_result.mp4 ├── detect_image.py ├── detect_video.py └── detect_camera.pybest.pt放在项目根目录或单独的weights/目录中都可以关键是在代码里写对路径。4.2 加载模型加载模型通常只需要一行代码from ultralytics import YOLO model YOLO(./best.pt)如果你打印model.names可以看到该模型对应的类别名称字典。这个字典非常关键因为模型预测结果中的cls只是数字索引要拿到真实类别名就必须通过model.names映射print(model.names)输出可能是类似这样的{0: person, 1: bicycle, 2: car, ...}如果你用自己的数据集训练这个字典会是你数据集的类别列表而不是 COCO 的 80 类。4.3 注意“用哪个版本训练就用哪个版本推理”很多离奇报错都来自版本不匹配。best.pt中保存的模型结构和推理代码里 YOLO 类所理解的模型结构不一致时会出现 key 不匹配、维度错误、加载失败等问题。最简单的规避方式是训练时用什么版本的ultralytics推理时就固定用什么版本。如果项目要升级 YOLO 版本不要直接换包加载旧权重而是应当用原版本把权重导出或者在相同代码环境下重新验证一轮。5. 图片检测完整示例5.1 最小可用代码图片检测是最基础的推理场景。先写一个完整代码能读入图片、推理、画框、保存。from ultralytics import YOLO model YOLO(./best.pt) results model.predict( source./images/demo.jpg, conf0.25, iou0.5, imgsz640, saveTrue, projectoutputs, nameimage_result, devicecpu ) print(检测完成结果已保存)运行后会在outputs/image_result/目录下生成一张带检测框的图片。代码中的source是输入图片路径saveTrue表示保存结果。5.2 更灵活的结果解析直接保存图片虽然方便但在真实项目中你往往需要取得检测框坐标、置信度和类别。下面这段代码手把手解析results。from ultralytics import YOLO model YOLO(./best.pt) results model.predict( source./images/demo.jpg, conf0.25, imgsz640, verboseFalse, devicecpu ) result results[0] boxes result.boxes.xyxy.cpu().numpy() confs result.boxes.conf.cpu().numpy() clses result.boxes.cls.cpu().numpy() for i in range(len(boxes)): x1, y1, x2, y2 boxes[i] conf confs[i] cls_id int(clses[i]) cls_name model.names[cls_id] print(f{cls_name}: {conf:.4f}, box({x1:.1f}, {y1:.1f}, {x2:.1f}, {y2:.1f}))这里有几个新手容易懵的点result.boxes.xyxy是 Tensor需要先.cpu()再.numpy()才能方便地转成普通数组result.boxes.conf是每个框的置信度result.boxes.cls是类别索引坐标格式是x1, y1, x2, y2表示左上角和右下角。5.3 动态绘制并保存结果如果你想控制画框的细节比如自己设置颜色、线宽或者不依赖saveTrue的默认逻辑可以手动在 OpenCV 环境下处理。import cv2 from ultralytics import YOLO model YOLO(./best.pt) frame cv2.imread(./images/demo.jpg) results model.predict(frame, conf0.25, verboseFalse) result results[0] annotated result.plot() cv2.imwrite(./outputs/demo_annotated.jpg, annotated) print(已保存标注图片)plot()方法返回的是一张 BGR 格式的numpy数组可以直接用cv2.imwrite保存也可以交给cv2.imshow展示。6. 视频检测完整示例6.1 单段视频文件检测视频本质上是一连串图片帧。YOLO 对视频做检测的常规思路是用 OpenCV 逐帧读取视频把每一帧送入模型得到标注后的帧再用VideoWriter写回新视频文件。import cv2 from ultralytics import YOLO model YOLO(./best.pt) input_path ./videos/demo.mp4 output_path ./outputs/demo_result.mp4 cap cv2.VideoCapture(input_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)) fourcc cv2.VideoWriter_fourcc(*mp4v) writer cv2.VideoWriter(output_path, fourcc, fps, (width, height)) frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break result model.predict(frame, conf0.25, imgsz640, verboseFalse)[0] writer.write(result.plot()) frame_count 1 cap.release() writer.release() print(f视频检测完成共处理 {frame_count} 帧) print(f输出文件{output_path})这段代码最大的优势是逻辑直观读取一帧推理一帧写入一帧。缺点是如果视频很长纯 Python 循环推理会偏慢。不过这足以满足大多数入门和实验需求。6.2 使用 predict 直接处理视频文件如果你想更省事Ultralytics 也允许直接把视频路径传给source参数from ultralytics import YOLO model YOLO(./best.pt) model.predict( source./videos/demo.mp4, conf0.25, saveTrue, projectoutputs, namevideo_result, devicecpu )这种方式会在outputs/video_result/下生成结果视频。优点是代码很短缺点是如果你想在视频上叠加额外信息比如 FPS、车牌号、其他业务字段就不如手动逐帧方便。6.3 视频推理提速思路视频推理速度主要受三方面影响模型大小、输入分辨率、推理设备。模型大小best.pt来自自定义训练如果用的是 YOLOv8x 这类大模型视频速度会明显偏慢。可以尝试替换成更小的模型重新训练或者在相同模型下导出为 ONNX/TensorRT。输入分辨率imgsz640是常见选择。如果画面目标很小可能需要更大分辨率但推理时间会上升。推理设备CPU 推理在小视频上可以接受但如果要在生产环境处理长视频GPU 基本是必须的。如果你处理的是监控视频且不需要每一帧都检测也可以考虑跳帧策略例如每 2 帧或每 5 帧检测一次中间帧直接复制上一帧结果或简单跟踪。这样能显著降低计算量。7. 摄像头实时检测完整示例7.1 方法一使用 predict 直接传入摄像头Ultralytics 支持直接把摄像头索引作为source参数传入from ultralytics import YOLO model YOLO(./best.pt) model.predict( source0, showTrue, conf0.25, imgsz640, streamTrue )source0表示默认摄像头showTrue会打开一个预览窗口。这种方式适合快速验证模型效果。但如果你需要在检测的同时做其他业务逻辑比如把结果通过消息队列发送给后端那么这种“一条命令跑完”的方式就不够灵活。7.2 方法二OpenCV 逐帧读取摄像头更推荐的做法是结合 OpenCV 手动读取摄像头帧这样你能控制每一帧做什么。import cv2 from ultralytics import YOLO model YOLO(./best.pt) cap cv2.VideoCapture(0) if not cap.isOpened(): print(无法打开摄像头请检查索引和权限) exit() cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) while True: ret, frame cap.read() if not ret: print(读取摄像头失败) break result model.predict(frame, conf0.25, imgsz640, verboseFalse)[0] annotated result.plot() cv2.imshow(YOLO Camera Detection, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的运行逻辑很清晰打开摄像头索引为0设置画面宽度和高度循环读取每一帧将当前帧送入模型得到检测结果把绘制了检测框的帧显示在窗口中按下键盘q键退出循环释放摄像头资源并关闭所有 OpenCV 窗口。7.3 摄像头实时检测的三个常见问题第一个问题摄像头打不开。在 Windows 上如果电脑有多个摄像头可能需要把cap cv2.VideoCapture(0)改成1或2。也有可能是摄像头被其他软件占用关闭占用软件后再试。第二个问题画面延迟大。默认情况下OpenCV 读取摄像头会带一个内部缓冲区导致你看到的画面并不是最新帧。适当降低分辨率、关闭日志打印、把推理分辨率调低都能减少延迟。也可以在摄像头支持的情况下尝试降低缓冲区但不同平台效果差异较大。第三个问题CPU 占用过高。摄像头实时检测对性能要求很高。如果只是验证算法效果在 CPU 上也能跑但分辨率建议降到 640 或更低。如果是为了长期运行应优先考虑 GPU 或导出为 TensorRT 引擎。8. 高频参数详解与实用调参思路predict方法有大量参数但日常开发真正高频使用的其实不超过八个。下面这张表整理了建议用法。参数默认值作用使用建议conf0.25置信度阈值低于该值的检测框会被过滤画面中误检多时调高检测不到目标时先调低iou0.7NMS 的 IoU 阈值目标重叠多时可以降到 0.5 左右imgsz640推理时缩放到的输入尺寸常见选择 640大目标场景可调 960max_det300单张图片最多保留的目标数密集场景适当调大例如 600deviceNone指定cpu或 GPU 编号多卡场景可用device0halfFalse是否启用 FP16 半精度推理NVIDIA 显卡且显存紧张时可开启测试classesNone按类别索引过滤只关心 person 时传入classes[0]verboseFalse是否逐帧打印推理日志摄像头/视频场景建议设为 FalsestreamFalse对视频流式处理减少内存占用处理长视频或摄像头建议为 True调参时要记住一个核心原则conf越高框越少但越可靠conf越低框越多但误检也可能越多。排查问题时我建议先把conf降到 0.05 看看到底模型有没有检出目标再逐步升高阈值找到一个业务可接受的平衡点。classes参数在自定义数据集场景非常实用。例如你训练的模型能同时检测多种目标但当前业务场景只需要其中一种就可以通过类别索引来过滤from ultralytics import YOLO model YOLO(./best.pt) results model.predict( source./images/demo.jpg, conf0.25, classes[0, 2], verboseFalse )这样结果中只会保留类别索引0和2对应的目标。9. 常见问题与排查思路实战中最容易踩坑的问题集中在路径、版本、摄像头和硬件环境上。下面这张排查表能帮你快速定位。问题现象可能原因排查方式解决方案加载best.pt报文件不存在路径写错用os.path.exists(best.pt)检查改成绝对路径或把权重放到项目根目录加载权重时出现 model key 不匹配用错误版本框架加载权重看异常信息中是否包含网络层名称换成训练时相同版本的 ultralytics推理没有输出框conf设置过高或类别过滤打印模型类别名并把conf0.05测试降低阈值检查当前画面是否存在模型类别摄像头打不开索引不对、设备占用、无权限打印cap.isOpened()换成索引 1 或 2关闭占用软件检查系统权限视频检测很慢模型较大、CPU 推理、单帧循环使用大模型还是小模型观察 CPU 占用改小imgsz换轻量模型或用 GPU长视频检测内存一直涨没有关闭历史结果引用检查循环内是否不断保存results只保留当前帧结果使用streamTrue有 NVIDIA 显卡但推理仍用 CPU安装的是 CPU 版 PyTorch输入torch.cuda.is_available()按官方命令重新安装 CUDA 版 PyTorchAMD 显卡无法调用 GPUAMD 不支持 CUDA查看显卡型号先用 CPU 跑通如需 GPU需按 ROCm 官方文档配置保存的视频无法播放编码器或尺寸不一致打印原始视频宽高和 FPS使用与原始视频一致的参数换mp4v或avc1这里再补充一条最容易被忽视的原则当检测结果不符合预期时先不要急着改网络结构先看数据通路。把模型输入图片保存出来确认图片没有被过度压缩或缩放把结果里的类别索引映射成名字确认不是你想要的类别把conf调到非常低确认模型是否真的“什么都没看见”。绝大多数问题都能靠这种拆解方式找到原因。10. 工程化建议从演示脚本到可维护的检测工具10.1 批量处理图片的最佳实践在实际项目中你经常需要处理整个文件夹下的图片。这时不要用循环去调用命令行而是写一个可复用的 Python 脚本。import cv2 from pathlib import Path from ultralytics import YOLO model YOLO(./best.pt) input_dir Path(./images) output_dir Path(./outputs) output_dir.mkdir(exist_okTrue) image_paths list(input_dir.glob(*.jpg)) list(input_dir.glob(*.png)) for image_path in image_paths: result model.predict(sourcestr(image_path), conf0.25, verboseFalse)[0] annotated result.plot() out_path output_dir / image_path.name cv2.imwrite(str(out_path), annotated) print(f处理完成{image_path.name})如果图片分布在多级子目录中可以把glob换成rglobimage_paths list(input_dir.rglob(*.jpg)) list(input_dir.rglob(*.png))10.2 不要在主线程里做复杂业务逻辑如果摄像头检测最终要接入一个带界面的系统比如 PyQt 或 Web 系统不要把模型推理和界面刷新放在同一个死循环里否则界面会卡死。更合理的做法是使用一个独立的线程或进程负责读取视频帧使用另一个线程负责模型推理主线程只负责展示结果或处理用户交互。线程之间用队列传递帧和推理结果例如queue.Queue。10.3 更快部署导出 ONNX 或 TensorRT在本地用 Python 推理没有问题但如果你想部署到服务端或边缘设备导出一个更高效的推理格式往往比直接加载best.pt更合适。ONNX 是最常见的中间格式。yolo export modelbest.pt formatonnx imgsz640导出后在代码里加载 ONNX 模型from ultralytics import YOLO onnx_model YOLO(./best.pt.onnx) results onnx_model.predict(source./images/demo.jpg)如果目标是 NVIDIA GPU 服务并且追求极致推理速度可以尝试导出 TensorRT 引擎yolo export modelbest.pt formatengine device0注意TensorRT 引擎通常和显卡型号、TensorRT 版本强相关换机器后一般需要重新导出。10.4 日志与结构化输出写工程代码时不要只靠print。如果检测结果需要交给其他系统使用建议输出成结构化 JSON。import json from ultralytics import YOLO model YOLO(./best.pt) results model.predict( source./images/demo.jpg, conf0.25, verboseFalse ) result results[0] outputs [] for box, conf, cls in zip(result.boxes.xyxy.cpu().numpy(), result.boxes.conf.cpu().numpy(), result.boxes.cls.cpu().numpy()): x1, y1, x2, y2 box outputs.append({ class: model.names[int(cls)], confidence: round(float(conf), 4), bbox: { x1: round(float(x1), 2), y1: round(float(y1), 2), x2: round(float(x2), 2), y2: round(float(y2), 2) } }) print(json.dumps(outputs, ensure_asciiFalse, indent2))这样下游服务可以直接解析这份 JSON而不需要关心 YOLO 内部的对象结构。10.5 系统集成与权限边界如果你要把摄像头检测部署到真实环境中尤其是涉及人员活动区域的系统请务必在项目启动前明确几个问题摄像头画面的采集是否获得了相关授权检测结果是否涉及个人隐私或敏感数据系统是否有独立的安全边界其他人能否随意访问摄像头画面从纯技术角度看YOLO 只是一个目标检测工具它本身不会判断“该不该拍”。作为开发者你需要在系统设计中把权限控制、数据留存、访问审计这些环节考虑进去。摄像头数据默认应当被视为敏感数据来处理而不是随意保存和传播。11. 最后一个建议先跑通再优化无论你最终选择 YOLOv8、YOLO11还是某个看起来名字不同的 YOLO 分支核心的学习路径其实差别不大。先让模型在一张图片上“看见”目标然后让它在视频里连续工作最后再接上摄像头感受实时检测的约束条件。这三层都跑通之后你才算真正掌握了一个检测模型的工程使用方式。如果一定要给一个实践顺序我建议你按这个顺序来先跑图片检测并解析坐标再跑视频检测并熟悉视频读写最后才是摄像头实时检测。因为每一步都能只用一个最小脚本验证排查问题时也可以按这个层级逐步隔离。你会发现很多看似复杂的部署问题最终都回到了最基础的几个环节模型路径对不对、环境版本对不对、输入数据怎么读取、输出结果怎么解析。把上面的代码保存成几个独立脚本替换成你自己的best.pt然后在测试环境里跑一遍。跑通之后再考虑导出 ONNX、加队列、接接口这些优化动作会顺很多。
返回列表