首页 / 资讯中心 / 文章详情

EasyClick 调用 OCR 文字识别 API 实战:从截图到点击的完整链路

EasyClick 调用 OCR 文字识别 API 实战:从截图到点击的完整链路 ★ FEATURED ARTICLE
屏幕上的文字看得见却摸不着想让脚本自己“读”出来这件事在自动化圈子里被问到的频率相当高。EasyClick 本身擅长的是控件查找和坐标点击一旦遇到游戏界面、验证码、图片按钮、Canvas 渲染的 H5 页面这类没有控件树可抓的场景纯靠控件选择器就会卡死。OCR 文字识别 API 就是补上这块短板的关键一环把屏幕截图喂给识别引擎拿回文字内容和坐标再交给 EasyClick 去做点击或判断。这篇内容围绕 EasyClick 调用 OCR 文字识别 API 的完整落地过程展开从方案选型、接口对接、截图裁剪、坐标换算到识别结果清洗和常见报错排查都会给出可以直接抄作业的代码和参数。适合已经会用 EasyClick 写基础脚本、但被“无控件界面”卡住的中级使用者也适合刚接触自动化、想搞清楚 OCR 在脚本里到底怎么用的小白对照着做。1. 整体方案设计与技术选型拆解1.1 为什么 EasyClick 需要外挂 OCR 能力EasyClick 的核心能力建立在安卓无障碍服务之上它能拿到的是系统暴露出来的控件节点树。只要目标应用是用标准原生控件写的找按钮、读文本、判断页面状态都很轻松。但现实里大量界面并不给你这棵树游戏引擎渲染的画面、Flutter 或 React Native 打包的跨平台页面、视频流里的字幕、图片形式的菜单无障碍服务看到的往往只是一个整块的 SurfaceView 或者一个空壳节点。这时候控件查找全部失效坐标点击又不知道点哪里脚本就僵住了。OCR 补的正是这个缺口。它的输入是一张位图输出是文字内容加文字外接矩形。有了矩形你就能算出文字中心点把“找到某段文字”转换成“点击某段文字所在的位置”。这个转换看起来简单实际是整个方案的价值所在它把视觉信息变成了脚本可以判断和操作的结构化数据。我在多个模拟项目里验证过凡是控件树残缺的界面加一层 OCR 之后脚本的稳定性会有质的提升因为文字内容比控件 ID 更贴近人的判断逻辑界面改版只要文案不变脚本基本不用动。1.2 三种接入路径的取舍在线 API、本地引擎、系统自带落地之前先要把“OCR 从哪来”这件事定下来这直接决定了后续代码结构和稳定性。常见的有三条路各有明确的适用边界。接入方式典型形态优点短板适用场景在线 OCR APIHTTP 接口上传图片返回 JSON识别率高、支持多语言、无需本地算力依赖网络、有调用配额、有延迟网络稳定、识别精度要求高的固定场景本地识别引擎打包进 APK 的模型库离线可用、响应快、无配额包体变大、精度略低、集成成本高弱网环境、隐私敏感、高频调用系统自带能力部分系统提供的文字提取服务零依赖、调用简单机型差异大、可控性差快速验证、轻量辅助对大多数 EasyClick 使用者来说在线 OCR API 是性价比最高的起点。原因很实际EasyClick 脚本跑在手机上本地跑一个像样的识别模型对内存和电量都不友好而在线接口把算力压力转移出去了脚本侧只需要处理“截图—编码—发请求—解析结果”这条链路。我一般建议先用在线 API 把业务流程跑通确认 OCR 确实能解决你的问题再考虑要不要换本地引擎做优化。反过来先啃本地集成很容易在环境配置上耗掉热情。1.3 截图来源的选择与坐标体系对齐OCR 的输入图从哪来这个细节决定了后面坐标能不能对得上。EasyClick 里常见的截图方式有两种全屏截图和区域截图。全屏截图拿到的是整块屏幕的位图识别出来的坐标是相对整屏的直接就能用于点击区域截图只截一小块识别快、传输小但坐标是相对裁剪区域的必须加上裁剪原点偏移才能还原成屏幕坐标。这里有个特别容易翻车的点不同截图接口返回的图片尺寸可能和屏幕物理分辨率不一致尤其是高 DPI 机型截图可能被缩放。如果你的脚本按识别坐标直接点击就会出现“识别到了但点偏了”的现象。稳妥的做法是在脚本初始化时记录一次截图的实际宽高和屏幕分辨率算出一个缩放比例所有识别坐标先乘这个比例再点击。这个换算逻辑我会在第 3 章给出具体代码先记住结论坐标不对八成是缩放没对齐。2. 接口对接的核心细节与参数拆解2.1 请求链路拆解从位图到 JSON 的每一步一次完整的 OCR 调用拆开看是五步截图得到位图对象、把位图编码成 Base64 或二进制、按接口要求拼装请求体、发起 HTTP 请求、解析返回的 JSON。每一步都有坑我逐个说。截图这一步EasyClick 提供的截图函数返回的通常是位图对象你需要确认它能不能直接转 Base64。如果不能就得先保存成临时文件再读出来编码多一次磁盘 IO但兼容性更好。编码格式上主流 OCR 接口接受 JPG 和 PNGJPG 体积小传输快PNG 无损但体积大。实测下来对文字识别来说 JPG 质量开到 80 到 90 之间识别率几乎无损传输时间能省一半我一般默认用 JPG。请求体拼装要注意接口文档里的字段名和编码方式。有的接口要求image字段放纯 Base64 字符串有的要求带data:image/jpg;base64,前缀有的干脆要求 multipart 表单上传二进制。这三种格式混用是新手最常见的 400 错误来源。我的习惯是先把接口文档里的请求示例原样抄一遍用工具单独调通确认返回正常了再往 EasyClick 里搬避免在脚本里反复试错。2.2 关键参数详解语言、方向、返回格式OCR 接口一般有几个可调参数理解它们能少走很多弯路。语言类型中文、英文、中英混合。选错语言会导致识别结果乱码或漏字中英混合场景一定要选支持混合的模型别图省事只传中文。文字方向横排、竖排、自动检测。游戏里竖排文字不少自动检测虽然方便但会增加识别耗时固定场景建议写死方向。返回粒度只要纯文本还是要带坐标的详细结果。做点击必须选带坐标的返回格式纯文本只能用来做内容判断。图片预处理开关部分接口支持自动旋转、二值化、去噪。截图本身质量差的时候打开这些开关能明显提升识别率。参数不是越多越好。我见过有人把所有增强开关全打开结果单次识别从 300 毫秒涨到 1.5 秒脚本节奏全乱。正确做法是按场景最小化配置固定横排界面就写死横排截图清晰就不开去噪把耗时压到最低。2.3 返回结构解析如何从 JSON 里捞出文字和坐标带坐标的 OCR 返回结构通常是嵌套的外层是状态码和消息内层是识别结果数组每个元素包含文字内容和四个角点坐标或外接矩形。解析时要注意两点一是坐标可能是四个点左上、右上、右下、左下也可能是left/top/width/height形式换算中心点的方法不同二是返回数组的顺序不一定是阅读顺序做“第几个文字”这类逻辑时不能依赖数组下标要自己按坐标排序。下面是一段解析逻辑的示意假设返回的是四角点格式// 假设 result 是接口返回的 JSON 对象 var words result.words_result; var list []; for (var i 0; i words.length; i) { var item words[i]; var loc item.location; // 四角点求中心 var cx (loc.left loc.left loc.width) / 2; var cy (loc.top loc.top loc.height) / 2; list.push({ text: item.words, x: cx, y: cy }); }这段代码把每个识别结果统一成{text, x, y}的结构后续查找文字、点击中心点都基于这个结构逻辑会清爽很多。我强烈建议在脚本里做这一层归一化别让业务代码直接去碰原始 JSON否则接口一改字段全脚本都要跟着改。3. 完整实操流程与核心代码实现3.1 环境准备与依赖确认动手写代码前先把环境确认清楚。EasyClick 的脚本运行环境需要支持 HTTP 请求和 Base64 编码这两项在标准运行环境里都有不用额外装库。需要确认的是网络权限脚本要发请求应用必须有网络访问权限这个在打包配置里勾选即可。另外准备一个 OCR 服务的账号和密钥把接口地址、请求方式、鉴权字段记下来。我习惯在脚本开头集中放配置项把接口地址、密钥、超时时间、语言类型都提出来方便后续调整var CONFIG { apiUrl: https://example-ocr-service/api/recognize, apiKey: your_api_key_here, timeout: 8000, language: CHN_ENG, imageQuality: 85 };把配置和逻辑分离是脚本能长期维护的基础。密钥这类敏感信息不要硬编码在会分享出去的脚本里正式发布时走服务端中转或者让用户自己填。3.2 截图、编码与请求发送的完整链路核心链路我拆成一个函数输入是裁剪区域不传就是全屏输出是归一化后的识别结果数组。这样业务层调用只需要关心“我要识别哪块区域”不用管底层细节。function ocrRecognize(region) { // 1. 截图 var img; if (region) { img screenCapture(region.x, region.y, region.width, region.height); } else { img screenCapture(); } if (!img) { toast(截图失败); return null; } // 2. 编码为 Base64JPG 质量 85 var base64 imageToBase64(img, jpg, CONFIG.imageQuality); if (!base64) { toast(图片编码失败); return null; } // 3. 拼装请求体 var body { image: base64, language_type: CONFIG.language, detect_direction: false, probability: false }; // 4. 发送请求 var resp httpPostJson(CONFIG.apiUrl, body, { Authorization: Bearer CONFIG.apiKey, Content-Type: application/json }, CONFIG.timeout); if (!resp || resp.code ! 200) { toast(OCR 请求失败: (resp ? resp.code : 无响应)); return null; } // 5. 解析并归一化 return normalizeResult(resp.body); }这段代码里几个细节值得展开。screenCapture的返回值类型要按你所用 EasyClick 版本的实际 API 来有的版本返回位图对象有的返回文件路径编码函数要对应调整。httpPostJson是示意名实际用你环境里的 HTTP 封装重点是请求头里带上鉴权和内容类型。超时时间设 8 秒是个折中值太短容易在弱网下误判失败太长会让脚本卡住实测 8 秒覆盖绝大多数情况。3.3 坐标换算与点击落点计算识别出来的坐标要变成能点的屏幕坐标中间隔着裁剪偏移和缩放比例两道换算。假设截图区域原点是(rx, ry)截图实际宽高是(sw, sh)屏幕分辨率是(dw, dh)那么缩放比例是dw/sw和dh/sh。识别坐标(x, y)换算成屏幕坐标的公式是screenX rx x * (dw / sw) screenY ry y * (dh / sh)写成函数function toScreenCoord(x, y, region, imgW, imgH) { var scaleX device.width / imgW; var scaleY device.height / imgH; var rx region ? region.x : 0; var ry region ? region.y : 0; return { x: Math.round(rx x * scaleX), y: Math.round(ry y * scaleY) }; }这里device.width和device.height是屏幕分辨率imgW和imgH是截图实际尺寸。如果两者相等缩放比例就是 1公式退化成简单加偏移。我踩过的坑是某些机型截图尺寸和屏幕分辨率差了几个像素肉眼看不出来但点击落点会偏尤其是点小图标的时候。所以这个换算一定要做别图省事直接加偏移。3.4 业务层封装找文字、点文字、等文字底层链路通了之后业务层需要三个高频操作找到某段文字返回坐标、点击某段文字、等待某段文字出现。这三个封装好写业务脚本就像搭积木。// 查找文字支持模糊匹配 function findText(keyword, region) { var results ocrRecognize(region); if (!results) return null; for (var i 0; i results.length; i) { if (results[i].text.indexOf(keyword) 0) { return results[i]; } } return null; } // 点击文字中心 function clickText(keyword, region) { var target findText(keyword, region); if (!target) { toast(未找到文字: keyword); return false; } var pt toScreenCoord(target.x, target.y, region, lastImgW, lastImgH); click(pt.x, pt.y); return true; } // 等待文字出现超时返回 false function waitText(keyword, timeoutMs, region) { var start Date.now(); while (Date.now() - start timeoutMs) { if (findText(keyword, region)) return true; sleep(500); } return false; }lastImgW和lastImgH需要在ocrRecognize里记录最近一次截图的尺寸供坐标换算使用。这个设计有点隐式依赖的味道更严谨的做法是把尺寸一起返回但为了业务层调用简洁我选择了记录全局状态实际项目里你可以按团队规范调整。waitText的轮询间隔设 500 毫秒是个经验值太密会频繁调接口浪费配额太疏会漏掉短暂出现的文字。4. 常见问题排查与避坑经验实录4.1 识别率低的五类原因与对应处理OCR 识别不准是最常见的问题但原因往往不在接口本身而在输入图片。我整理了一张速查表按出现频率排序。现象可能原因处理方式文字漏识别截图分辨率太低或文字太小放大截图区域或先裁剪再放大文字乱码语言类型选错中英混合场景选混合模型部分文字识别成符号背景干扰、对比度低开启二值化或先做灰度处理竖排文字识别错乱方向参数未设置显式指定竖排或开启方向检测识别结果时好时坏截图时机不对界面在动画中加等待或截图前先 sleep这张表里最值得说的是最后一条。很多“识别不稳定”其实是截图截在了界面切换的动画中间画面糊成一团什么引擎都识别不出来。解决办法很简单截图前先等界面稳定或者连续截两帧对比差异小再送去识别。这个技巧不花钱但能解决一大半所谓的“识别率问题”。4.2 请求失败与超时的排查路径请求层面的问题分三类网络不通、鉴权失败、参数错误。排查顺序建议从外到内。先确认网络脚本能不能访问外网接口域名能不能解析。这一步可以用一个最简单的 GET 请求测试能通说明网络没问题。再确认鉴权密钥是否过期、请求头字段名是否拼错、签名算法是否正确。鉴权失败通常返回 401 或 403看状态码就能定位。最后确认参数请求体字段名、编码格式、图片大小是否超限。参数错误一般返回 400接口的返回消息里通常会写明哪个字段有问题别忽略这条消息它能省你半小时。超时问题要单独说。超时可能是网络慢也可能是图片太大导致上传慢。我的处理策略是先压缩图片把长边限制在 1280 像素以内体积控制在 500KB 以下再设合理超时最后加一次重试。重试不要无脑循环最多两次且第二次前 sleep 一秒避免把偶发问题放大成雪崩。4.3 坐标偏移与点击落空的定位方法点击落空是第二高频问题定位方法我总结成“三步验证”。第一步把识别到的坐标在屏幕上画个标记EasyClick 支持画点或画圆肉眼确认标记位置和文字位置是否重合。第二步如果标记偏了检查缩放比例和裁剪偏移是否算对打印出截图尺寸和屏幕分辨率对比。第三步如果标记对了但点击还是落空检查点击函数用的坐标系是否和标记一致有的点击接口用的是相对坐标有的是绝对坐标混用就会偏。我遇到过一个特别隐蔽的案例截图区域传的是相对坐标但截图函数内部按绝对坐标处理导致截出来的图根本不是想要的那块识别结果自然全错。这种问题看日志看不出来只能靠画标记对比。所以我现在养成习惯新写的 OCR 逻辑第一件事就是画标记验证验证通过再往下写业务。4.4 性能与配额优化的实操心得OCR 调用是有成本的不管是时间成本还是配额成本。优化思路就一条能少调就少调能小图就别传大图。具体做法有几个。第一缩小识别区域只截需要判断的那一块别动不动全屏识别区域越小传输越快识别越准。第二加结果缓存同一界面短时间内重复识别同一区域直接返回上次结果缓存有效期设一两秒就够。第三用“先粗后细”策略先用小区域快速判断界面状态确认需要了再做精细识别。第四把 OCR 和控件查找结合能用控件搞定的绝不用 OCROCR 只留给控件失效的场景。提示缓存要设失效条件界面切换时必须清缓存否则会拿到上一个界面的旧结果导致判断错乱。我一般把清缓存挂在页面跳转的回调里。这套优化做下来一个原本每次操作都调 OCR 的脚本调用次数能降到一个数量级以下响应速度和配额消耗都会明显改善。5. 从能跑到好用稳定性加固的几个关键动作5.1 异常兜底与降级策略脚本跑通只是起点能长期稳定跑才是目标。OCR 链路上任何一环都可能出问题截图失败、编码失败、请求超时、返回格式异常。每一环都要有兜底不能让一个异常把整个脚本带崩。我的做法是给ocrRecognize加完整的空值判断和 try-catch任何一步失败都返回 null业务层拿到 null 就走降级逻辑。降级逻辑分两级一级是重试一次二级是切换到备用方案比如改用控件查找或者固定坐标点击。备用方案不要求多聪明能保证脚本不卡死就行。实际项目里我甚至会准备一个“识别失败次数”计数器连续失败超过阈值就暂停脚本并提示人工介入避免在错误状态下越跑越偏。5.2 日志记录与问题回溯OCR 相关问题最难的是回溯因为出问题的那一刻界面已经过去了。解决办法是把关键信息记下来每次识别的区域、截图尺寸、识别结果、耗时、是否命中目标。日志不用太详细但这几项必须有。function logOcr(region, results, cost, hit) { var msg [OCR] region JSON.stringify(region) cost cost ms hit hit count (results ? results.length : 0); console.log(msg); }有了这些日志事后排查就能还原当时的场景是区域截错了还是识别没命中还是命中了但坐标算错了一目了然。我建议日志按天切分别让一个文件无限增长手机存储空间有限。5.3 版本兼容与接口变更应对OCR 服务商的接口不是一成不变的字段名调整、返回结构调整、鉴权方式升级都可能发生。应对的核心是把接口细节收敛到一个适配层业务代码只依赖归一化后的{text, x, y}结构。这样接口一变只改适配层一个文件业务逻辑不动。我在适配层里还会做字段兼容比如同时支持words_result和results两种返回字段名先判断哪个存在再用哪个。这种防御性写法看起来啰嗦但能让你在服务商灰度升级期间不至于脚本全挂。另外接口地址和密钥走配置别写死在代码里换服务商时改配置就行。6. 场景延展OCR 在自动化脚本里的更多用法6.1 验证码与动态文本的读取OCR 最直接的延展用法是读动态文本比如界面上随机出现的数字、倒计时、状态提示。这类文本没有固定控件但位置相对固定用区域截图加 OCR 就能稳定读取。读验证码要注意时效性识别完立刻使用别缓存。读倒计时可以用来做节奏控制比如等倒计时归零再执行下一步比固定 sleep 靠谱得多。6.2 图文混合界面的结构化判断有些界面是图文混排控件树里只有图片没有文字。这时候 OCR 能帮你把图片里的文字提取出来结合坐标做结构化判断。比如一个商品列表每个卡片是图片渲染的你可以识别出所有价格文字按 y 坐标排序找到最便宜的那个再点击。这种用法把 OCR 从“找文字”升级成“理解界面结构”是脚本智能化的关键一步。6.3 与控件查找的协同策略最后说协同。OCR 和控件查找不是替代关系而是互补。我的策略是优先控件查找快且准控件找不到时降级到 OCROCR 也找不到时再降级到固定坐标。三层降级下来脚本的鲁棒性会好很多。反过来如果一上来就用 OCR既慢又费配额还容易受截图质量影响得不偿失。这套协同逻辑封装成一个smartFind函数业务层只调它内部按优先级依次尝试返回第一个命中的结果。这样业务代码干净策略调整也只改一处。我在几个模拟项目里用这个结构界面小改版基本不用动脚本因为总有一层能兜住。注意三层降级要有明确的超时控制每一层都不能无限等待否则降级就失去了意义。我给每层设的预算是控件 1 秒、OCR 2 秒、固定坐标即时返回总预算控制在 3 秒出头。实际用下来OCR 这套东西的价值不在于技术多高深而在于它把“看得见”变成了“点得着”。接口调用、坐标换算、结果清洗这些环节每一个都不复杂但每一个都有细节细节没处理好就会在关键时刻掉链子。我个人的体会是先把最小链路跑通用画标记的方式验证坐标准确再往上叠业务逻辑和降级策略这个顺序不能乱。急着堆功能最后排查问题的时间会远超省下来的时间。
阅读完成 · 觉得有帮助?
咨询建站