本项目仅供个人学习和研究计算机视觉、输入控制等技术之用。请遵守法律法规和游戏服务条款,不要将其用于破坏公平性或违反平台规则的场景。
anqrwzui 是一个 Windows 屏幕感知实验项目。它用 .NET 8.0 WinForms 搭桌面端,用 YOLOv8/YOLO11 做目标检测,再把检测结果送进后处理、目标保持和交互控制。项目的乐趣不在于“调用一次模型就结束”,而在于把视频、模型、桌面 UI 和输入设备这些性格完全不同的东西,硬生生接成一条能跑的流水线。中间当然也有不少“为什么 FPS 突然没了”的时刻。(:з」∠)
下面的版本、模型和外设描述都以仓库当前提交里的实现为准:项目包含 .NET 8、ONNX Runtime、yolo11n.pt、best.onnx 和可选的 KM04 / gbild64.dll 依赖。模型文件、驱动和 Windows 环境发生变化后,推理设备与性能都需要重新验证,不能只凭这篇文章里的参数推断结果。
先说清楚:下面只记录技术实验和工程结构,不提供规避游戏规则或破坏公平性的使用方法。视觉模型很有意思,但它应该被用来学习和验证系统,而不是成为别人游戏体验里的坏消息。
为什么不是先截整张屏幕再说
实时视觉的第一个敌人通常不是模型,而是数据搬运。整张桌面从 GPU 复制到 CPU,再裁剪、再转换格式,像是把一箱货先搬到楼下,拆出一个苹果,再送回楼上。项目用 SharpDX 封装 DXGI Desktop Duplication API,只取屏幕中心的 640×640 区域,尽量减少不必要的复制和转换。
桌面端是 .NET 8 WinForms,截屏、推理、绘制和配置分成独立组件。原因很现实:画面卡顿时,我想分别知道是 DXGI 没拿到新帧、ONNX Runtime 推理变慢,还是 UI 重绘拖住了主线程,而不是对着一个“总 FPS”数字发呆。
推理层优先尝试 CUDA,环境不满足就回退 CPU。这个回退不是为了宣称两种设备速度一样,而是让没有 NVIDIA 环境的机器也能验证界面、模型输入输出和后处理逻辑。UI 会显示当前推理设备和 FPS,模型加载失败也不会被藏在一条模糊的异常消息后面。
从一帧画面到一个检测结果
主循环可以拆成这几步:
1 | DXGI Desktop Duplication |
DxgiScreenCapture 只管取画面,YoloV8Detector 负责创建 ONNX 会话、准备张量并执行推理,DetectionRenderer 只关心检测框、标签和状态文字怎么画。职责切开以后,换模型时不用把 WinForms 主窗体改成一团;想比较 CPU 与 CUDA 时,也有明确的计时位置。
YOLO 输出不是“屏幕上有一个目标”这么简单,它通常包含框坐标、置信度和类别信息。后处理要把模型坐标映射回 640×640 的画布,按阈值过滤低置信度结果,再处理重叠和目标选择。当前训练管道用单一 head 类别,所以重点放在目标框稳定性和数据质量上,而不是堆一堆类别让演示看起来更复杂。
单帧检测为什么会抖
单帧模型只知道“这一帧看到了什么”,不知道上一帧的目标是不是同一个。目标刚好被遮挡一帧、置信度掉到阈值下面、或者检测框中心轻微漂移时,如果每帧都从零开始,结果就会闪得像坏掉的霓虹灯。
所以项目在检测后加了目标保持和距离容错:目标短暂消失时,HoldFrames 保留之前的状态;目标重新出现后,AssociationDistanceRatio 用相对距离判断它还能不能和上一帧关联。MaxOffsetRatio 则限制目标偏移范围,避免一个突然冒出来的远处框被错误地接管成当前目标。
这不是完整的多目标跟踪算法,也不是为了包装一个更酷的名词,就是针对实验场景做的最小连续性补丁:让偶发漏检不要立刻变成大幅跳动,让参数变化对轨迹的影响可以被观察和复现。
为什么用弹簧—阻尼模型表达移动
最省事的做法是把当前偏移量直接乘一个比例,或者把指针瞬移到目标中心。前者在参数不合适时会抖,后者则完全不像一个连续系统。项目用质量—弹簧—阻尼模型,把目标偏移量转换成带惯性、加速度和缓冲的运动。
可以把它想象成一个被橡皮筋牵引的小物体:弹簧负责把它拉向目标,阻尼负责消除过冲,质量决定它对变化有多“迟钝”。这样的模型适合做控制实验,因为刚度、阻尼和最大偏移都能独立调整,轨迹的变化也更容易解释。
相关参数放在 Config/config.json,由 FileSystemWatcher 监听变化,UI 还提供了部分调节入口。改配置不用重新编译,就能对比“更快但更容易过冲”和“更稳但响应更慢”的效果。当然,所有输入控制都应该在合法、合规且不影响他人的场景中验证。
数据集管道:模型训练前才是时间黑洞
YoloTrain/ 目录包含从视频到模型的训练流程。大致过程是:用 FFmpeg 按步长抽取中心画面,用 Label Studio 做标注,把标注转成 YOLO 数据格式,再通过 Ultralytics 训练并导出 ONNX。模型最终要回到本地桌面应用里验证,所以训练输出和推理输入必须保持尺寸、类别和坐标约定一致。
当前训练配置用预训练的 yolo11n.pt,单一 head 类别,训练 200 个 Epoch、Batch Size 16,导出 FP16 ONNX 模型。这些数字本身不代表“训练得越久越好”,真正影响结果的往往是样本有没有覆盖不同背景、目标大小、遮挡和光照,以及标注框有没有一致地贴住目标。
项目针对稀疏目标场景关掉了 Mosaic 和 MixUp。通用目标检测增强并不总适合每一种输入:拼接或混合后的画面可能让模型见到训练时从未存在过的空间关系,反而干扰小目标定位。这里更重要的是对比 loss、mAP 和实际漏检情况,而不是只盯着一次训练的最高数字。
把“自己”从候选目标里排除
屏幕视觉还有一个很具体的麻烦:画面里的自身角色、UI 元素或其他固定区域可能长得很像目标。项目提供底部区域过滤和比例参数,让实验可以排除一部分已知的自身区域。它不是万能的语义分割,但比把误检全甩给模型要诚实得多。
当过滤范围、检测阈值、保持帧数和关联距离都能热更新时,调试过程就从“改代码—编译—重新启动—希望这次好了”变成了可记录的对照实验。参数不一定带来完美结果,但至少能告诉我失败发生在召回率不足、关联太严格,还是画面区域根本没截对。
性能问题要拆成三段看
项目里的“实时”不是一个单独的开关,而是截屏、推理和绘制三段时间加起来的结果。DXGI 负责把最新桌面帧交给程序;ONNX Runtime 把 640×640 图像转成模型张量并执行网络;WinForms 再把检测框、FPS、设备状态和调试信息画出来。任何一段阻塞,最后看到的都是低 FPS。
所以排查时不能只盯着模型大小。截屏区域过大,复制和格式转换就多;模型输入没复用缓冲区,分配压力就大;UI 线程如果在等推理,画面即使拿到新帧也显示不出来。项目把 DxgiScreenCapture、YoloV8Detector 和 DetectionRenderer 分开,正是为了让每一段可以单独计时和替换。
CUDA 与 CPU 回退也应该当成两种运行模式来看,而不是“一个快一点,一个慢一点”。CUDA 环境需要正确的 ONNX Runtime provider、驱动和模型格式;CPU 模式更适合验证功能和排查结果。先保证同一输入在两种设备上都能得到结构一致的框,再去比吞吐量,通常比一上来追 FPS 有效。
模型输出之后,才是应用自己的判断
YOLO 给出的是候选框、置信度和类别,应用还要决定哪些框值得留下、怎么映射坐标、怎么排除已知区域,以及怎么和上一帧建立关系。这个阶段不应该被描述成“模型已经看懂了屏幕”——它仍然只是一个带不确定性的候选生成器。
比如,低置信度过滤提高精度时可能损失召回率;关联距离太小会把同一个目标误判成新目标,太大又可能把两个不同目标串在一起;保持帧数太长会让目标消失后仍然残留,太短则会让轨迹闪烁。把这些参数拆出来,才有可能针对具体失败案例逐个调整,而不是反复换模型碰运气。
配置热重载还带来一个小但重要的好处:实验结果可以和参数一起记录。哪怕最终没得到理想效果,也能知道当时用了什么阈值、保持了多少帧、关联半径是多少。“可复现”永远比“我记得那次好像挺稳”可靠。
输入层的边界必须写在文章里
项目通过全局鼠标钩子读取状态,输入控制封装在 MouseController 中,优先调用 gbild64.dll 对接 KM04 设备,没有设备时回退 Windows SendInput。把硬件调用放在单独一层,是为了让检测模块能脱离具体外设测试,也让输入权限和设备依赖不会悄悄渗进模型代码。
但技术上能做到,不等于应该在任何地方使用。这个项目保留学习研究声明,就是因为屏幕检测和自动输入很容易越过公平与服务条款的边界。最适合它的场景是离线素材分析、自己的测试窗口、模型性能实验和输入系统研究;涉及在线游戏或他人服务时,请遵守法律、平台规则和游戏条款。
这个项目真正教会我的事
anqrwzui 让我意识到,计算机视觉落地不是“训练一个 YOLO,然后把 DLL 扔进程序”这么直线。输入画面要稳定,模型格式要对齐,推理设备要能回退,后处理要解释得通,目标关联要处理丢帧,控制层还要有明确的使用边界。
当这些环节都被拆开,项目就不只是一个能画框的 Demo,而是一张小型的工程地图:数据如何进入、模型如何判断、程序如何承认不确定性,以及人应该在哪一步按下停止键。后面还可以继续优化性能和跟踪策略,但先把每个失败都看清楚,已经很值得了。ヽ(•̀ω•́ )ゝ
如果要继续做,我会先补什么
下一步最值得补的不是“再加一种更大的模型”,而是更系统的离线评估:固定一批带标注的 640×640 帧,分别统计漏检、误检、目标关联断裂和端到端延迟。这样模型指标、后处理指标和设备性能指标就不会混成一个模糊的“手感”。
还可以把截屏、推理和后处理的耗时写进结构化日志,在 GPU / CPU、不同分辨率和不同刷新频率下做对照。模型实验最后要回到数据和测量,不能永远靠肉眼看框有没有抖。至于输入控制部分,仍然应该只在合法、离线、自己拥有的测试环境中验证——边界不能因为技术细节写得更漂亮就消失。