Skip to content

About

本地离线 OCR 工具:三种截屏识别(无障碍 / root / 录屏,自动挑选)、本地 HTTP 接口、MacroDroid / Tasker 插件、边缘滑动手势触发器,全程无需联网

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

小孙OCR

本地离线 OCR 工具。识别全部在本机完成,无需联网、无需登录、无统计、无广告。

面向自动化场景:提供三种截屏方式(无障碍 / root / 录屏授权,自动挑选)、本地 HTTP 接口、 以及 MacroDroid / Tasker 插件能力,并内置边缘滑动手势触发器(含跟手滑出菜单)。


功能一览

模块 能力
文字识别 三档本地模型:tiny(轻量快速)/ small(高精度)/ rapid(ML Kit 捆绑版,中英)
截屏识别 无障碍截图(Android 11+,免 root 免授权)/ root screencap / MediaProjection 授权,按序自动挑选与回退
图片识别 指定图片路径直接识别,支持纯文本 / 含坐标 / 含包围盒三种输出
本地接口 http://127.0.0.1:1666,支持 JSON / form / query 三种传参
自动化插件 完整 Tasker / Locale 协议,异步变量回传,MacroDroid 与 Tasker 均可调用
边缘手势 屏幕边缘悬浮手柄,8 方向滑动触发,每方向可独立调触发距离与行为
滑动菜单 滑过手柄跟手弹出一排图标,选中某项才向宏发事件
手柄黑名单 按前台应用隐藏手柄,同一份名单支持黑 / 白两种语义
防烧屏 手柄定时小幅垂直漂移,降低 OLED 残影
从最近任务隐藏 多任务界面不显示卡片,服务与手势照常运行
日志 崩溃自动记录,一键分享,便于反馈问题

系统要求

项 要求
Android 版本 8.0(API 24)及以上
架构 arm64-v8a / armeabi-v7a
必需权限 所有文件访问(用于读写图片)
可选权限 无障碍(免授权截屏、手柄黑名单)、悬浮窗(后台弹截屏授权框)、root(免授权截屏)
存储占用 约 46 MB(含本地模型)

屏蔽系统侧滑返回依赖 setSystemGestureExclusionRects(),需 Android 10(API 29)及以上, 更低版本无此平台能力。


安装与首次使用

  1. 从 Releases 下载 APK 安装。
  2. 首次启动会申请「所有文件访问权限」。
  3. 打开 App 即自动启动 OCR 服务,无需手动点启动。
  4. 首次需在后台加载模型(约 1~3 秒),界面顶部会显示服务状态。

服务采用「HTTP 先上线、模型后台加载」:端口会立刻可连(GET /last 秒回), 但模型未就绪时 POST / 返回 503、/last 里 modelReady 为 false。 自己写脚本调用时,请以 modelReady 或 503 作为「稍后重试」的信号。

界面只有两个页签:OCR设置 / 滑动触发。

「OCR设置」页顶部那张开关卡控制 OCR 服务本身(关掉后接口不再监听、也不开机自启), 它与边缘滑动手势互不影响 —— 只用手势触发宏的人可以把 OCR 服务关掉。 从最近任务划掉 App 时服务不会停止(android:stopWithTask="false"), 以便外部自动化脚本的调用不因清理后台而中断。如需彻底停止,请在系统设置中「强行停止」。


截屏方式

不传图片路径时,服务自行截屏。三条路径按序自动挑选,前一条失败自动回退,不会因某条临时失效整体不可用:

优先级 方式 授权 说明
① 无障碍截图 勾选一次,长期有效 AccessibilityService.takeScreenshot(),需 Android 11+
② root 截屏 一次 su 授权 su -c screencap -p <path>,启动时自动探测
③ MediaProjection 每次重启服务后需重新授权 前两者都不可用时的兜底

无障碍截图

在「OCR设置」页点 无障碍截图,到系统「设置 → 无障碍」里勾选「小孙OCR 截图」。

该服务只保留截图一项能力:onAccessibilityEvent 为空实现,配置里没有声明任何 accessibilityEventTypes,因此收不到屏幕文本、控件树或点击按键事件,不能用于监听用户操作。

使用建议

  • Android 11+ 且不想给 root:用无障碍截图。免 root、免授权、不弹框、不弹确认,适合无人值守。
  • 设备有 root:服务启动时自动启用 root 截屏,调用方无需任何授权步骤,也不会出现录屏确认框。
  • 两者都没有:需在 App 内点一次「开启截屏授权」,服务重启后需重新授权。

界面「屏幕截屏识别」卡片会显示当前实际走的哪条路径,并提供三个入口按钮。


识别与模型档位

档位 来源 特点
tiny 本地 native(PP-OCRv6) 最快,模型最小,字典 6904
small 本地 native(PP-OCRv6) 字典 18708,保留更多标点与符号
rapid ML Kit(捆绑版) 中英混排,输出行级包围盒与置信度;模型随 APK 打包,不依赖 Google Play 服务、不联网下载

档位是全局状态:App 内切换与外部接口切换共用同一份设置,切一次对所有后续请求生效。

输出格式三选一:

type 输出
1 纯文本
2 JSON 数组(含坐标)
3 详细 JSON(含矩形)

坐标基于截屏原始像素,可直接用于点击坐标。


边缘滑动手势

在屏幕边缘放一个可拖动的手柄,手指从手柄上划过设定距离即触发(滑过即响应,不必抬手)。 共 8 个方向(上下左右 + 四个斜向),每个方向的触发距离可单独调,也可让某个方向不响应。

设置入口:App 主界面 → 滑动触发 页。

与宏的配合

手势引擎本身不执行任何动作,只负责告诉 MacroDroid / Tasker「某个方向的手势成立了」, 具体动作由宏决定。

  1. 「滑动触发」页 → 打开手势设置,添加手柄(位置、宽度、颜色、可触发方向)

  2. 在 MacroDroid 里新建宏 → 触发器 → 小孙OCR · 边缘滑动事件

  3. 在插件配置界面选定方向

  4. 同一页可选 滑过之后:

    • 跟随手柄 —— 按手柄里该方向当前的设定走(默认)
    • 直触 —— 不弹菜单,抬手立刻发事件
    • 菜单 —— 弹出图标,选中某项才发事件

    这一项改的就是手柄上该方向的设置,两处永远是同一份数据,改哪边都行。

  5. 展开 Output variables,把菜单变量映射到宏变量

方向与「滑过之后」都是手柄级别、不是宏级别的。 同一方向挂了两个宏, 该方向设成直触则两个宏都是直触。原因很直接:菜单要在向宿主发出查询之前 就弹出来,那个时刻服务还不知道这次滑动属于哪个宏。

每个方向的两种行为

该方向设为 滑过触发距离后
菜单 弹出一排按钮,选中某项才发事件
直触 不弹菜单,抬手立刻发事件,宿主立刻收到方向

默认全部为菜单。手柄编辑页的「可触发方向」上方有一行实时汇总,直接列出 「哪些方向滑过会弹菜单」;旁边有「全部设为菜单 / 全部设为直触」两个一键按钮。

弹菜单只取决于三件事:该方向已启用、该方向是菜单模式、菜单总开关开着且有菜单项。 与「该方向有没有配触发器」无关 —— 没配触发器的方向滑过去照样会弹。 不想某个方向弹,把它关掉或切成「直触」即可。

滑动菜单

开启后,滑过触发距离不再立即触发宏,而是弹出一排按钮;继续滑到某一项上(跟手高亮), 抬手才选中它。

没选中就不触发:抬手时手指不在任何按钮上,或这批触摸被系统抢走,两种情况都不发通知, 宿主侧完全没反应(不会出现「触发了但变量是空的」)。

配置页:滑动触发 → 滑动菜单。

分组 可调项
开关 启用 / 关闭
外观 图标大小、显示文字、文字大小、文字间距、浅色圆底
布局 每排数量、逐排数量(可逐排单独覆盖)、图标间距、每排间距
配色 圆底颜色、文字颜色、选中颜色(#RGB / #ARGB / #RRGGBB / #AARRGGBB)
交互 热区大小(命中半径 = 图标半径 × 倍率,100%~300%,带感应范围预览)
菜单项 添加 / 改名 / 选图标 / 排序 / 删除

菜单项三个字段:

字段 说明
名字 按钮上显示的文字,必填
动作值 回传给宏的 %xsocr_menu_action;留空则与名字相同
图标 app:包名 / file:路径 / 资源名;留空则显示名字前两个字

排列方式:图标沿 45° 斜线从左下排到右上,每排各自居中;一排排满就在上方另起一排。

手柄黑名单

指定「在某些应用里不显示手柄」—— 打游戏时不再挡住技能键,看视频时不再盖住画面。

设置入口:滑动触发 → 手柄黑名单。

项 默认 说明
按应用隐藏手柄 关 总开关。关掉与没有这个功能时完全一样
名单语义 黑名单 黑名单 = 名单内不显示;白名单 = 只有名单内显示
应用名单 空 同一份列表,两种语义共用,切换语义不必重新录
  • 打游戏、看视频时嫌手柄碍事 → 用黑名单,把那些应用拉进来
  • 平时不想要手柄,只在少数几个应用里用 → 用白名单,把那几个应用设为白名单

前台应用由无障碍服务的窗口状态事件判定,切过去那一刻即生效,无残留。 无障碍未连通时黑名单整体不生效、手柄照常显示 —— 拿不到前台应用名时,宁可让手柄照旧出现, 也不会因为读不到而把用户的手柄误隐藏。黑名单页会显示侦测通道的当前状态与授权入口。

防烧屏(像素漂移)

手柄是常驻悬浮窗,OLED 屏长期固定位置会留下残影。开启后手柄每隔一段时间在垂直方向 随机挪动几个像素(默认开,可关)。

项 默认 范围
防止烧屏漂移 开 —
漂移幅度 8 dp 0 ~ 30(0 = 原地不动)
漂移间隔 60 秒 15 ~ 300

漂移只改窗口的垂直位置、不写回用户拖拽的位置;菜单展开时、拖拽中、手指按在手柄上时、 以及息屏状态下都不漂移。

屏蔽系统侧滑返回

手势导航下系统从屏幕边缘往内判定「返回」,优先级高于应用窗口 —— 手柄贴边时手指刚碰上去 就被系统当成返回吃掉。本应用用平台 API setSystemGestureExclusionRects()(API 29+)申请排除区, 属于通用平台能力,非特定厂商定制。

  • 每个手柄有一个「屏蔽本条手柄区域的系统手势」开关(默认开),关掉的手柄不上报排除区。
  • Android 10 以下无此能力,属系统限制。此时可把手柄挪到屏幕偏中间的位置避开边缘返回区, 或改用三键导航。
  • 单个手柄可调宽度范围 0 ~ 200 dp。

MacroDroid / Tasker 插件

本 App 是一个 Tasker / Locale 兼容插件,MacroDroid 与 Tasker 均可直接调用。

📖 常量表、能力位、协议细节见 MACRODROID_PLUGIN_GUIDE.md。

配置步骤

  1. 新建宏 → 动作 → Tasker/Locale Plugin

  2. 在插件列表里选 小孙OCR

  3. 在配置界面设置:

    项 说明
    图片来源 截取当前屏幕 / 指定图片路径
    模型档位 tiny / small / rapid
    输出格式 纯文本 / 含坐标
    指定文本(可选) 要查找的文字
    匹配方式 包含文本 / 匹配文本(完全一致)
  4. 保存(保存时才会把变量清单告知宿主)

  5. 在动作里展开 Output variables,把需要的变量映射到宏变量

识别回传的变量

变量 类型 内容
%xsocr_text 文本 识别出的全部文本(多行用换行或空格拼接)
%xsocr_match 文本 指定文本是否找到:true / false
%xsocr_x 文本(数字) 命中处的 x 坐标;未找到为 -1
%xsocr_y 文本(数字) 命中处的 y 坐标;未找到为 -1
%xsocr_error 文本 出错信息(失败时才有内容)
%xsocr_time 文本 耗时(毫秒)
%xsocr_tier 文本 实际使用的档位

手势回传的变量

触发器路径回传的是另一组变量,手势本身不发识别请求:

变量 类型 内容
%xsocr_menu_index 文本(数字) 滑动菜单选中项序号(从 0 开始)
%xsocr_menu_text 文本 选中项的名字
%xsocr_menu_action 文本 选中项的动作值

没选中任何项时,这三个变量都不传。手势方向不单独回传:插件在收到查询时已内部完成 方向匹配,不匹配的这次直接回「不满足」,宏根本不会跑。

匹配方式

方式 行为 例子(查找「确定」)
包含文本 文本块含这几个字即算命中 匹配「确定提交」「请点确定」
匹配文本 整个文本块与它完全一致 只匹配「确定」

两者均不区分大小写,取第一个命中的文本块。

免授权配置

MacroDroid 在后台触发插件时,App 可能处于后台或已被回收。要让插件能自动弹出截屏授权框, 需要悬浮窗权限(Android 10+ 只允许拥有该权限的应用从后台启动 Activity)。 开启方式:「OCR设置」页 → 屏幕截屏识别卡片 → 悬浮窗权限 按钮。

不开也能用,但需先手动打开一次 App 完成截屏授权;授权在服务存活期间有效, 服务重启后需重新授权。

使用以下任一种方式时,连悬浮窗权限都不需要:

方式 条件 效果
无障碍截图 Android 11+,勾选一次 免 root、免授权、不弹框
root 截屏 设备有 root 免授权、不弹框

两者都会让 screenCaptureReady 直接为 true,插件跳过整个授权流程。


本地 HTTP 接口

  • 地址:http://127.0.0.1:1666

路由

路由 说明
POST / 识别(path 可选,不传则自动截屏)
GET /last 读取上一次识别结果(JSON)
GET /last?format=text 同上,返回纯文本
GET /ocr?format=text 一步完成「取图 → 识别 → 返回文本」

POST / 参数

支持 JSON Body / form 表单 / query 三种方式:

字段 说明
path 图片绝对路径(需文件权限)
type 1 = 纯文本 2 = JSON 数组(含坐标) 3 = 详细 JSON(含矩形)
tier 可选:tiny(默认)/ small / rapid。切换后持续生效
action 可选:setTier 表示只切档位、不做识别

响应为 JSON 文本:成功 {"status":200,...},失败 {"status":400/404/500/503,"data":"Error: ..."}。 HTTP 状态码与响应体里的 status 保持一致,因此只看状态码的调用方(curl -f、OkHttp 等) 也能正确判错。

唯一例外:GET /last 恒回 HTTP 200 —— 它是探活接口,只要服务在监听就算通; 「还没有识别记录」属于业务状态,写在 body 的 status 里。

不支持 base64 传图。 改用 path 后由服务端按需降采样解码,内存可控; base64 会让请求体膨胀 33%,一张 4K 图峰值内存可达 60 MB+,低端机容易 OOM。

调用示例

# 启动后台服务(供自动化脚本使用)
am start-foreground-service com.xiaosun.ocr/.OCRService

# 传路径识别
curl -X POST http://127.0.0.1:1666 -H "Content-Type: application/json" \
     -d '{"path":"/sdcard/test.png","type":1}'

# 不传 path:自动截屏识别
curl -X POST http://127.0.0.1:1666 -H "Content-Type: application/json" \
     -d '{"type":3}'

# 只切换模型档位(不识别)
curl -X POST http://127.0.0.1:1666 -H "Content-Type: application/json" \
     -d '{"tier":"small","action":"setTier"}'

# 识别的同时切换档位
curl -X POST http://127.0.0.1:1666 -H "Content-Type: application/json" \
     -d '{"path":"/sdcard/test.png","type":1,"tier":"small"}'

# 一步完成截屏+识别,直接拿纯文本
curl "http://127.0.0.1:1666/ocr?format=text"

# 读取上一次识别结果
curl "http://127.0.0.1:1666/last?format=text"
// Auto.js:传路径
var res = http.post("http://127.0.0.1:1666",
        { "path": "/sdcard/test.png", "type": "1" });
log(res.body.string());

从最近任务隐藏

「OCR设置」页有一张开关卡:从最近任务隐藏(默认关)。

开启后多任务界面里不再出现本应用的卡片,但服务照跑、手势照用,从桌面图标照常进入。 开关拨动当场生效,无需切出去再进来。

与「划掉 App 服务也不停」不冲突:stopWithTask="false" 管的是服务存活, 这个开关管的是卡片显不显示,两件事可以同时要。


隐私

  • 识别全程在本机完成,不联网、不上传任何图片或文本。
  • 无账号、无登录、无统计上报、无广告。
  • 无障碍服务只保留截图能力,不读取屏幕文本、不监听控件与按键事件。
  • 应用内日志只写在应用私有目录,仅在你主动点「分享日志」时才会离开设备。

反馈问题

App 自带日志:崩溃自动记录,用户只需点一次「分享日志」。

  1. 复现一次问题(崩溃无需刻意复现,发生时自动记下)
  2. 打开 App →「滑动触发」或「OCR设置」页 → 最下面的日志卡片
  3. 点 分享日志 → 选微信 / QQ / 邮件发出去 (发不出去就点 导出到下载,文件落在 Download/小孙OCR日志_日期时间.txt)
  4. 想先自己看一眼:点 查看日志,弹窗里长按可只复制关键几行

「分享日志」会自动带上设备信息(机型 / 系统版本 / ROM / 屏幕参数)。

日志位于 Android/data/com.xiaosun.ocr/files/logs/xsocr_log.txt, 放在应用外部私有目录:不需要存储权限、卸载时自动清理、可通过 FileProvider 合法分享。 文件超过 512 KB 会轮转为 xsocr_log.old.txt,最多占两份空间。


构建

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-arm64
export ANDROID_HOME=/opt/android-sdk
cd /sdcard/AndroidIDEProjects/ocr
bash ./gradlew assembleDebug --console=plain

产物:app/build/outputs/apk/debug/app-debug.apk

  • /sdcard 不支持执行位,请用 bash ./gradlew 而非 ./gradlew。
  • 受限环境中若出现 AAPT2 Daemon startup failed,可改用 Android Studio / AndroidIDE 构建。
  • 首次构建需联网下载 AGP 与依赖。

相关文档

About

本地离线 OCR 工具:三种截屏识别(无障碍 / root / 录屏,自动挑选)、本地 HTTP 接口、MacroDroid / Tasker 插件、边缘滑动手势触发器,全程无需联网

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages