文字 → OCR
RapidOCR(跑在 ONNX Runtime 上的 PaddleOCR 模型,中英文,不依赖 PyTorch)。它读出
每一块文字区域,返回字符串外加一个像素框。这就给了每个文字元素一个真正的 label——
"Settings"、"$29.9"——于是大脑可以按名字瞄准。
PhysiClaw 从不把原始像素丢给大脑去琢磨——它递给大脑的是一份清单,其中每个按钮、图标和 文字行都已经被找到、贴上标签、框好了。两个模型在每一帧上完成这项工作:一个 OCR 阅读器负责 文字,一个小神经网络负责图标。本页讲的就是一帧画面如何变成那份清单,以及清单为什么长成那样。
手机屏幕上有两类可点的东西——文字和图形——PhysiClaw 为每一类各跑一个专门的模型,再把结果合并。
文字 → OCR
RapidOCR(跑在 ONNX Runtime 上的 PaddleOCR 模型,中英文,不依赖 PyTorch)。它读出
每一块文字区域,返回字符串外加一个像素框。这就给了每个文字元素一个真正的 label——
"Settings"、"$29.9"——于是大脑可以按名字瞄准。
图标 → ONNX 检测器
OmniParser V2 图标检测(一个由 Microsoft 微调的 YOLO11m 模型,导出为 ONNX, 经 OpenCV 的 DNN 模块运行——同样不依赖 PyTorch)。它找出那些可交互的图形——App 图标、 图像按钮、开关——这些都没有文字可供 OCR 去读。
两者都完全在本地设备上运行。任何一帧都不会离开你的机器去做分析,这也是为什么
安装会把模型下载到本地。两路输出会被合并、清理(丢掉太小的框和
低置信度的命中、按重叠去掉近重复项),从上到下、再从左到右排序,然后重新编号为
0, 1, 2, …。
合并后的结果就是大脑对屏幕的全部视野——每个元素占一行:
id [kind] "label" [left,top,right,bottom] conf0 [icon] "" [0.020,0.060,0.110,0.100] 0.641 [text] "Settings" [0.510,0.550,0.590,0.630] 0.962 [text] "Wednesday 14" [0.300,0.080,0.700,0.130] 0.99五个字段,大脑拿到的就这些:
| 字段 | 它是什么 |
|---|---|
id | 该元素在本份清单里的稳定句柄(每帧重新编号)。 |
kind | text 或 icon——哪个检测器找到了它。 |
label | 文字的 OCR 字符串;图标则为空("")——它那张图没有名字。 |
bbox | 那个矩形,屏幕的四个分数 [left, top, right, bottom]。 |
conf | 检测器的置信度,0–1。文字低于 0.7、图标低于 0.3 的会被丢掉。 |
图标的标签为空是有意为之——检测器找到的是这里有个能点的东西,而不是它是什么。 大脑会从位置和周围的文字推断出一个没有标签的图标是什么,就像一个人扫一眼一格格 App 图标, 就知道哪个是哪个。
bbox(边界框)是围住某个元素的矩形,给出四个数,每个都在 0 到 1 之间——
是屏幕宽和高的分数,绝不是像素。
(0,0) ┌───────────────────────┐ │ [0.51, 0.55, 0.59, 0.63] │ ┌──────┐ ← left=0.51 (51% across) │ │ Set… │ top=0.55 (55% down) │ └──────┘ right=0.59, bottom=0.63 │ center = (0.55, 0.59) └───────────────────────┘ (1,1)用分数而非像素,正是清单可移植的原因。同一个 [0.51, 0.55, 0.59, 0.63] 指向同一个按钮,
无论底层那帧是 1024 像素的相机裁切还是 1170 像素的手机截图——也无论你的手机是小号 SE
还是大号 Pro Max。大脑只在一个干净的 0–1 空间里思考;所有到像素和毫米的来回换算都由
服务器处理(那是标定的活)。
当大脑想动手时,它说出一个边界框和一个手势——tap [0.51, 0.55, 0.59, 0.63]。服务器取这个框的
中心 ((left+right)/2, (top+bottom)/2),朝那里瞄。所以只要框的正中落在目标上,松一点的
框完全没问题;你是在指,不是在描边。
peek 对 screenshot:两个来源,同一份清单清单格式从不变——但它可以由两张不同的图像构建,而在两者之间做选择,是「看」里唯一真正的取舍。
peek | screenshot | |
|---|---|---|
| 图像来源 | 头顶相机 | 手机自己的截图 |
| 怎么做 | 拍快照 → 裁到屏幕 → 检测 | 点 AssistiveTouch → iOS Shortcut 上传像素 → 检测 |
| 速度 | ~4 s | ~12 s |
| 清晰度 | 对多数目标够用 | 像素级精确 |
| 副作用 | 无 | App 可能察觉到截图(分享面板、水印) |
peek 是默认项,也是主力。它把触控笔停到视野外,拍下屏幕,裁到只剩手机屏幕那块区域
(裁切让每个图标多出 2–3 倍像素,让检测更锐利),再跑同样那两个检测器。如果第一帧回来是糊的
——对焦或运动问题——它会等上几秒,再抓一帧才肯放弃。
screenshot 是升级手段。它不拍相机照片,而是通过
桥接触发手机自己的截图:机械臂点
AssistiveTouch,一个 iOS Shortcut 抓取并上传像素级精确的字节,vision 在那些像素上运行。
结果更锐利但更慢,而且它是会改变状态的——截图是一个真实的 iOS 事件,有些 App 会对它做出
反应。所以大脑只在以下情况才动用它:
peek 清单里缺失),因为一次截图可能改变 App 状态,规矩是:screenshot 之后、点击之前,永远要再 peek 一次
——先读到鲜活的屏幕,然后再动手。
把一份解析好的清单(而不是一张图像)递给大脑,一举做成了三件事:它把每个目标都锚定在屏幕上一个 真实的矩形里(无需猜像素坐标),它把「屏幕变了吗?」变成两份清单之间一次廉价的文本对比, 并且它让大脑的词汇表保持极小——挑一个框,挑一个手势。正是这个小而稳定的接口,让 PhysiClaw 能靠简简单单再看一眼,就从意外中恢复过来。