☰
YOLO+Python屏幕目标检测实战:从环境搭建到工程落地
2026/10/2 9:54:32 网站建设 项目流程

简介:基于 YOLO 的 DNF 自动化脚本,面向游戏脚本开发者和 Python/YOLO 入门者,开箱即用。资源共包含 18 个文件,其中 5 个 Python 脚本分别承担交互控制、图像识别、游戏动作等模块,11 张图片用于目标分类展示,mp4 演示视频直观呈现运行效果,md 说明文档补充配置与使用细节,整个 zip 压缩包约 79.85MB,已有 853 人学习下载。项目以 YOLOv5 为检测核心,需将导出并转换后的 ncnn 权重 .bin 与 .param 放入根目录即可运行,分类覆盖 Gate(门)、Hero(角色)、Item(物品)、Mark(标记)、Monster(怪物)等对象,标注阶段可参考 Label Studio 工作流。源码实现了人物/怪物/材料/门识别、自动寻路过图、固定角色攻击、按怪物数量动态攻击、识别狮子头房间、开局使用 buff、拾取掉落物(含粉装识别)以及自动再次挑战等功能,并附带 best.pt 转 onnx 再转 ncnn 的完整转化步骤,适合作为游戏视觉自动化项目的落地参考。

1. 这个标题要解决什么问题:把 YOLO 和 Python 变成能“看见”屏幕的脚本

第一次冲着“基于 YOLO 的 Python 脚本搞 DNF”这个标题来的,多半不是想要一个通用目标检测 Demo,而是想让脚本自己看见屏幕里的东西:在一张横版游戏画面里识别怪物、拾取物和 Boss 动作,再决定下一步操作。过去这类脚本靠图像颜色匹配,换个背景就翻车;YOLO 的做法是把画面交给预训练模型,直接拿回目标的类别、坐标和置信度,代码量反而更少。需要先说清楚边界:我给的是开箱即用的屏幕目标检测工程模板,检测结果可以接日志、截图存档、数据统计或自动化测试,不建议把它接到在线游戏的自动操作链路上,封号与合规风险都得自己掂量。适合已经有 Python 基础、想用 YOLO 做窗口画面识别和自动化采集的从业者,这篇不教内存修改,也不涉及脱机外挂。

2. 搭建能跑 YOLO 的 Python 环境,选对第一个预训练模型

2.1 用 Python 3.10 虚拟环境装依赖,先别污染系统解释器

YOLO 的 Python 环境坑,十有八九出在依赖装脏了。常见做法是先建一个虚拟环境,把 ultralytics 和 torch 都装进去。我一般用 3.10 或 3.11,这两个版本对 torch 的 Windows 预编译包支持最稳,3.12 偶尔会遇到某些扩展包还没跟上编译版本的问题。

python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate python -m pip install --upgrade pip pip install ultralytics

先激活虚拟环境再装包,否则 pip 可能装到全局 site-packages 里,后面换项目就乱了。升级 pip 不是走过场,老版本 pip 解析 torch 的依赖索引偶尔会失败,报一堆No matching distribution。国内网络慢的话,给最后一行加上镜像源参数:pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple。装完可以用pip list | findstr ultralytics(Windows)或pip list | grep ultralytics确认版本。

2.2 预训练模型下载与选型:别一上来就上大模型

ultralytics 包装好后,第一次跑推理会自动下载对应权重。很多做 yolo 搭建的人在这里被卡住:看着终端停在下载进度条不动。原因是权重托管在境外对象存储上,网络不稳定时容易中断。解决方式很简单:手动把yolov8n.pt下载下来放进项目根目录,ultralytics 检测到当前目录已有同名权重就不会再走自动下载。

选型上,我强烈建议从yolov8n开始。n 是 nano,体积最小、CPU 也能跑;s、m、l 依次变大,精度更高但推理耗时翻倍。做屏幕画面识别这种场景,目标本身清晰、类别少,n 和 s 的差距肉眼几乎看不出来。

模型体积适合场景一句话选型建议
yolov8n.pt约 6MBCPU 实时、轻量嵌入式默认首选
yolov8s.pt约 21MB有 GPU、想稍微提精度显存 4G 以上再考虑
yolov8m.pt约 49MB离线分析、不在乎速度不建议接实时屏幕流
yolov8l.pt约 83MB高精度离线任务屏幕识别没必要

模型文件的后缀是.pt,这是 PyTorch 权重格式,做 Python 脚本调用最方便。后面如果追求部署速度,可以再导出成 ONNX 用 onnxruntime 跑,但那是后话,先把.pt链路跑通。

2.3 用一张图和一行命令验证环境

环境装没装对,别急着写屏幕捕获,先用单张图片跑一遍推理,这一步能过滤掉大部分环境问题。

yolo predict model=yolov8n.pt source=bus.jpg imgsz=640

bus.jpg是官方示例图,第一次运行会自动下载。如果你在的项目目录里没有这张图,随便放一张自己的图片,把 source 换成路径即可。这行命令走的是 CLI 入口,约等于帮你验证了模型下载、CUDA/CPU 设备检测、推理和画框全链路。

更贴近脚本写法的,是直接在 Python 里调用:

from ultralytics import YOLO model = YOLO("yolov8n.pt") res = model.predict("bus.jpg", conf=0.25, verbose=False)[0] print(res.names) print(res.boxes)

res.names是 80 个 COCO 类别的 id 到名称映射,res.boxes里装着所有检测框,包括坐标、置信度、类别 id。能打印出这两样东西,说明 torch、ultralytics、权重文件都正常,可以进入下一步做屏幕捕获了。

3. 开箱即用的屏幕检测脚本:用 mss 抓帧、用 YOLO 推理、用回调接管结果

3.1 一个能直接跑的工程结构长什么样

做这种脚本我不喜欢堆单文件,拆成三个文件最舒服:detector.py放检测类,main.py放业务逻辑,config.yaml放参数。以后换权重、改置信度、调 ROI,都不需要动代码。

screen_vision/ ├── config.yaml ├── detector.py ├── main.py └── venv/

config.yaml是我每次都会建的文件,参数集中管理比在代码里翻常量强太多:

weights: yolov8n.pt conf: 0.3 iou: 0.5 imgsz: 640 device: cpu # 有 GPU 改成 0 roi: [0, 0, 1920, 1080]

roi是检测区域,后面 4.2 会专门讲。先把这四个键读懂:conf 是置信度门槛,iou 是 NMS 去重阈值,imgsz 是送入模型的缩放尺寸,device 决定跑 CPU 还是 GPU。这套结构的好处是不管你怎么改参数,代码文件不需要动,重启脚本就生效。

3.2 屏幕捕获用 mss,一秒抓 30 帧不掉队

屏幕捕获常见的有 OpenCV 的cv2.VideoCapture、PIL 的ImageGrab、以及 mss 库。我一般直接用 mss,理由就一个字:快。ImageGrab在 Windows 上也不算慢,但 mss 是 C 实现,多开几个抓取区域也不容易掉帧。抓出来的帧是 BGRA 格式,需要转成 BGR 再喂给 YOLO。

import mss import numpy as np import cv2 with mss.mss() as sct: monitor = sct.monitors[1] # 1 代表主显示器 frame = sct.grab(monitor) img = np.array(frame) bgr = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)

sct.monitors[1]是整个主屏,monitors[0]是所有显示器的合并区域,多显示器场景才需要碰它。grab()的入参也可以传一个 dict,只抓屏幕的一部分,比如{"left": 0, "top": 0, "width": 800, "height": 600}。抓到的 frame 对象底层是 bytes,转 numpy 数组后一定要做颜色空间转换,不转的话后面 YOLO 推理出的结果会偏色、误检率明显升高。

3.3 把 YOLO 推理包成一个 detector 类,结果通过回调抛出去

这里给出这个工程最核心的代码,也就是中枢检测类。设计思路是:屏幕捕获、推理、坐标解析全部封装在类内部,对外只暴露一个on_results回调。这样业务方在main.py里只需要关心“检测到目标之后干什么”,不用每天和 numpy 数组、YOLO 的 Results 对象搏斗。

import cv2 import mss import numpy as np from ultralytics import YOLO class ScreenDetector: def __init__(self, weights="yolov8n.pt", conf=0.3, iou=0.5, imgsz=640, device="cpu", roi=None): self.model = YOLO(weights) self.conf = conf self.iou = iou self.imgsz = imgsz self.device = device self.roi = roi # (x, y, w, h),相对屏幕左上角 self.last_frame = None def on_results(self, dets): # 子类重写这个回调 raise NotImplementedError def run(self, monitor_index=1, interval=0.05): with mss.mss() as sct: monitor = sct.monitors[monitor_index] while True: frame = sct.grab(monitor) img = cv2.cvtColor(np.array(frame), cv2.COLOR_BGRA2BGR) self.last_frame = img.copy() if self.roi is not None: x, y, w, h = self.roi img = img[y:y + h, x:x + w] res = self.model.predict( img, conf=self.conf, iou=self.iou, imgsz=self.imgsz, device=self.device, verbose=False )[0] dets = [] for box in res.boxes: dets.append({ "name": res.names[int(box.cls[0])], "conf": float(box.conf[0]), "xyxy": [int(v) for v in box.xyxy[0].tolist()], }) self.on_results(dets) cv2.waitKey(int(interval * 1000))

关键点在predict的参数:verbose=False一定要开,否则终端每一帧都会刷一行 log,肉眼没法看,也在拖慢循环。res.boxes里每个 box 的xyxy是相对当前输入图片的,如果你前面做了 ROI 裁剪,这里的坐标是相对 ROI 的,后面用的时候要自己加回偏移。回调on_results是同步执行的,如果回调里做文件写入或网络请求,主循环会被阻塞,这种情况建议把回调里的耗时操作丢到线程池。

3.4 把结果画出来确认效果,但别一直开着

调试阶段需要可视化,直接把检测框画在帧上再看一眼,这是最直观的验证方法。

from detector import ScreenDetector import cv2 class Viewer(ScreenDetector): def on_results(self, dets): img = self.last_frame.copy() for d in dets: x1, y1, x2, y2 = d["xyxy"] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f"{d['name']} {d['conf']:.2f}", (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow("detect", img) cv2.waitKey(1) Viewer(roi=(300, 200, 900, 600)).run()

调试时开着预览没问题,但正式跑的时候强烈建议把cv2.imshow和cv2.waitKey都去掉。这里有句血泪经验:imshow在部分机器上会强制垂直同步,把抓帧速率直接拖到 10 帧以下,线上跑监控时一定要关掉预览,否则你会以为 YOLO 很慢,其实慢在显示。

4. 把帧率从 5 提到 20 的三处调参:置信度、ROI 与推理设备

4.1 置信度 conf 与 IoU 先调哪个

跑通之后第一件事是调参。很多新手纠结 conf 和 iou 到底哪个先动,我的经验是:先调 conf,再调 iou。conf 是门槛,低于这个置信度的框直接扔掉;iou 是 NMS 去重阈值,用来合并重叠框。画面里频繁出现 0.1 置信度的鬼影框,那是 conf 太低;同一个目标周围拖出一串框,那是 iou 太低。

场景建议 conf建议 iou说明
屏幕静态图标识别0.50.4图标本身就稳定,调高 conf 压误检
游戏/视频动态画面0.30.5目标会动,框抖动时适当降 conf
连续帧跟踪0.250.6宁可多框不可漏,靠后面逻辑过滤

改动直接在config.yaml里改,不需要动代码。有个翻车案例:有人把 conf 拉到 0.8 之后发现目标全没了,最后发现画面里的目标本来就只有 0.6 的置信度,阈值设太高等于全部丢弃。调参前先打印一帧所有框的 conf 分布,这个习惯能省很多无用功。

4.2 用 ROI 把检测范围从全屏缩小到一块

全屏 1920x1080 送入 YOLO,模型会把整张图缩到 640x640 再推理。如果目标永远只出现在屏幕中间一块,比如横版动作游戏的战斗区域,就没必要让模型看整个屏幕。ROI 裁剪有两个好处:一是减少背景干扰,误检率肉眼可见下降;二是让模型把分辨率的“预算”花在真正的目标上。

# config.yaml 里这样设 roi: [400, 300, 800, 450]

代码里对应这一段:

x, y, w, h = self.roi img = img[y:y + h, x:x + w]

注意一个小坑:裁剪后检测框的坐标是相对 ROI 的,要还原成屏幕坐标必须加回偏移。也就是x_screen = x_roi + roi_x,y_screen = y_roi + roi_y。我见过有人忘了这一步,画框怎么都对不上,还以为是模型漂移。ROI 对帧率的实际提升没有想象中大,因为推理耗时主要由 imgsz 决定,但它对误检的改善非常明显,这点在 5.5 里还会提到。

4.3 设备、imgsz 和 half:把帧率拉起来的三板斧

CPU 上跑实时屏幕检测,帧率往往只有个位数。提帧率最有效的三件事,按收益排序:降 imgsz > 换 GPU > 开 half。

配置组合imgsz设备half典型效果
CPU 基础640cpu关能跑,帧率低
CPU 优先320cpu关延迟明显下降,精度略降
GPU 基础6400关实时无缝
GPU 优化4800开帧率高且精度损失小
res = self.model.predict( img, conf=self.conf, iou=self.iou, imgsz=320, device="0", half=True, verbose=False )

half=True是半精度推理,只对 GPU 有效,CPU 上开反而可能更慢,因为 CPU 对 FP16 的加速支持不稳定。imgsz不需要和训练尺寸严格一致,YOLO 会自动缩放输入,所以从 640 降到 320 是合法的提速手段。如果你在device="0"上报错,先跑一句python -c "import torch; print(torch.cuda.is_available())",输出 False 就说明装的是 CPU 版 torch,需要重装对应 CUDA 版本。

5. 五个必踩的坑:从环境报错到误检与合规边界

这一章的每一条都来自真实折腾经历。除了代码层面的坑,还要提醒一件更重要的事:这套屏幕检测工程本身是中性的,可以做界面自动化测试、数据采集、辅助标注,但如果你打算把它接到在线游戏的自动操作链路上,封号是最轻的后果,违规风险完全需要自己承担。后面 5.5 会再展开说。

5.1 报错No module named 'torch',但明明装过 ultralytics

现象:pip install ultralytics成功,跑 Python 脚本却报ModuleNotFoundError: No module named 'torch'。

原因:ultralytics 的依赖列表并强制捆绑安装最新版 torch,某些镜像源或网络环境下 pip 会跳过 torch 的安装;另一个常见情况是系统里有多个 Python,终端激活的是 A 环境的 venv,pip 却指向 B 环境。

解决:先where python(Windows)或which python(Linux/macOS)确认当前解释器路径,再手动补装pip install torch torchvision。装完用python -c "import torch; print(torch.__version__)"验证。如果发现 pip 装到了别的环境,直接删掉 venv 重建,比 debug 解释器路径快得多。

5.2yolov8n.pt自动下载卡住,进度条纹丝不动

现象:第一次运行yolo predict,终端停在Downloading https://... yolov8n.pt好久,最后超时。

原因:权重文件从境外对象存储拉取,内网或网络不稳定时连接会被掐断。这个和项目代码没关系,是网络链路问题。

解决:手动把yolov8n.pt下载好,放到当前工作目录,再运行脚本。ultralytics 加载模型时会优先在当前目录找同名文件,找到就不再走下载逻辑。还有个习惯:把权重文件和工程放在一起,不要放在临时目录,否则换个终端路径又触发一遍下载。

5.3 mss 抓屏黑屏,或者抓出来是全黑画面

现象:用 mss 抓游戏窗口,得到的 numpy 数组全黑,或者每隔几帧黑一次。普通桌面正常,一进游戏就黑。

原因:全屏独占模式下,显卡把画面直接输出到硬件覆盖层,普通的 GDI/Desktop Duplication 抓不到表面数据。常见于全屏 DX 游戏和某些视频播放器。

解决:把目标程序从全屏独占改成无边框窗口化,mss 就能抓到。如果必须抓全屏,需要换成支持 DXGI 的采集方案,比如d3dshot或直接用 Windows 图形捕获 API。屏幕识别类项目我建议统一窗口化运行,省掉这一整类问题。

5.4 检测框总是往左上偏一截,坐标对不上

现象:画框检测结果正常,但矩形框和屏幕上真实物体位置整体偏移,偏移量固定。

原因:Windows 的 DPI 缩放没有感知。屏幕分辨率 1920x1080、系统缩放 125% 时,mss拿到的坐标是物理像素,而窗口/画面坐标是逻辑像素,两者之间差了一个缩放系数。

解决:在脚本入口加一段进程级 DPI 感知设置:

import ctypes try: ctypes.windll.shcore.SetProcessDpiAwareness(1) except Exception: ctypes.windll.user32.SetProcessDpiAwarenessCompat()

SetProcessDpiAwareness(1)让进程认为自己是 DPI aware,系统不再对坐标做缩放映射。注意这段代码只在 Windows 上有意义,Linux/macOS 需要包一层异常保护。设置完重启脚本,偏移问题立刻消失。

5.5 COCO 预训练模型误检严重,该换权重就换权重

现象:模型把画面里的石头、水面反光识别成 person,或者把目标圆领识别成领带,检测结果一团乱。

原因:COCO 预训练模型只见过 80 类日常生活物体,游戏画面里的角色、物品、特效它一个都没见过。它输出“person”只是因为形状有点像,不是真的认出来了。看评估指标的时候也会遇到混淆矩阵总合不唯一的问题,不用慌,YOLO 的混淆矩阵是按预测框统计的,一个框可以对应一个 GT 也可以被忽略,行列合计不等于总样本数很正常。

解决:如果目标确实属于 COCO 80 类(人、车、狗这类),先调高 conf 到 0.5 试试。如果目标属于自定义类别,只有一个根治办法:收集几百张包含目标的截图,用 LabelImg 或 X-AnyLabeling 标注,微调一个自己的权重。预训练模型不是万能的,它不是黑匣子,只是没见过你的场景。

6. 从“能检测”到“会干活”:命中截图存档与自定义模型训练

检测到了目标,接下来才是脚本真正产生价值的地方。最稳妥的落地方式是:命中目标时把当前帧存成图片、把检测记录追加进 CSV。这套逻辑既可用于 UI 自动化测试的异常捕获,也能做视频抽帧统计,完全合规。

import csv import time import cv2 from detector import ScreenDetector class Archiver(ScreenDetector): def on_results(self, dets): if not dets: return ts = time.strftime("%Y%m%d_%H%M%S") cv2.imwrite(f"captures/{ts}.jpg", self.last_frame) with open("results.csv", "a", newline="") as f: writer = csv.writer(f) for d in dets: writer.writerow([ts, d["name"], round(d["conf"], 3), *d["xyxy"]])

回调里先判断有没有目标,再落盘,避免每帧都写文件把磁盘写满。captures目录要提前建好,脚本里没有自动建目录的逻辑,这是有意为之——显式报错比默默跳过更容易排查。CSV 里的坐标是 ROI 相对坐标,如果你在 config 里设了 roi,入库前记得加回偏移。

做了存档之后,你会攒下一批“错得很有价值”的样本。这些就是微调的自留地:用标注工具框出你要的目标,写一个dataset.yaml,指定训练和验证路径,然后跑:

yolo detect train data=dataset.yaml model=yolov8n.pt epochs=50 imgsz=640

训练时盯两个曲线:box_loss和cls_loss。损失值降不下去,九成不是模型问题,而是标注框画歪了或类别标反了。50 轮对几百张数据的小样本足够,再多轮容易过拟合。训练完用yolo predict model=runs/detect/train/weights/best.pt回到原来的屏幕检测循环,权重文件路径替换掉即可。

最后留一个验证习惯:每次改完参数学会先跑一段“实录回放”,把之前录制的屏幕视频或截图序列喂给检测脚本,而不是直接对着实时画面调。同样的输入才能对比出前后差别,否则你根本分不清是模型变好了还是画面刚好变了。这个习惯救过我太多次。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询