安卓图像识别自动化原理

安卓自动化脚本找图找色原理详解:OpenCV 图像识别与多点找色实战指南

详解安卓自动化脚本的图像识别原理:OpenCV模板匹配如何找图、单点与多点找色、色块查找的适用场景,以及为什么图像识别比坐标点击更稳定(不依赖分辨率与布局)。附找图找色函数速览、参数调优建议与坐标区域随机点击技巧,适合需要处理游戏画面、自绘控件、图片按钮的安卓脚本开发者。

约 23 分钟

一、为什么自动化需要“眼睛”

写自动化脚本的人会先遇到一个朴素的问题:脚本要操作界面,总得先“看见”界面在哪里。

常规路径是“读界面”——安卓系统会为当前界面生成一棵控件树(节点树),无障碍服务可以读取它,每个按钮、输入框、图片都有 id、文本、描述、类名和位置边界。脚本“按名字找控件”,找到就点,非常优雅。

但“读界面”有大量读不到的时候:

  • 游戏画面:OpenGL / SurfaceView 渲染的内容没有标准控件节点,整个画面对无障碍服务来说几乎是空的;
  • 自绘控件:很多 App 用 Canvas 自绘界面,节点树里只有空壳,没有可用的文本与描述;
  • 图片按钮:一整张图就是一个按钮,没有文字可读、没有 id 可用。

这时脚本只剩一条路:像人一样“看屏幕”——截一张图,在截图里找目标,找到位置再操作。这就是图像识别的用武之地。

一句话总结:控件定位是“读界面”,图像识别是“看界面”。读不到的时候,“看”是兜底方案,也是游戏类、自绘类界面的唯一解。 在 EasyClick 里,这套“眼睛”就是图色模块(image 系列 API,对象前缀是 image,如 image.requestScreenCapture()),覆盖申请截图、抓屏、找图、找色、比色一整条链路,全部基于系统官方能力、免 root 实现。

二、从坐标到图像:识别为什么更稳

最早的自动化脚本是“坐标脚本”:录下屏幕上一个点的 (x, y),到点就点。它的致命弱点是脆弱——分辨率一变、布局一调、弹窗一弹,坐标就全废了。这也是很多“录出来的脚本能跑但不稳定”的根源。

图像识别换了个思路:不关心“目标在哪个坐标”,只关心“目标长什么样”。 找图找到的是目标本身的位置,坐标只是识别结果的一个附带输出。

这带来两个直接好处:

  • 与分辨率、布局无关:按钮挪了位置、换了机型、改了字体,只要它长得还是那样,就能被找到。多机型适配时,一套模板往往通吃;
  • 与“有没有控件”无关:控件树读不到的游戏画面、自绘界面,只要“看得见”就能识别。

坐标并没有被淘汰,它退位成了“兜底中的兜底”:只有目标完全没有颜色、形状特征可识别时(比如一片纯色空白区域),才退回到坐标,而且成熟平台支持“坐标区域内随机点击”,让每次点击位置不完全一致,降低操作特征(功能特性文档)。

三、找图:OpenCV 模板匹配原理

找图是目前应用最广的识别手段,它的内核是计算机视觉里最经典的算法之一——模板匹配

原理可以用一句话说清:准备一张目标的小图(模板),在屏幕的大图上从上到下、从左到右“滑动”过去,每到一个位置就计算一次相似度,相似度超过阈值就判定命中。 命中后返回目标在屏幕上的位置区域(Rect),取区域中心点就是可用的点击坐标。

模板图怎么来?开发工具里把目标区域截下来存成小图,放进工程 res 文件夹,脚本里用 readResAutoImage() 读取即可。

相似度由阈值参数控制:

参数 含义 默认值
threshold 图片相似度,0~1 的浮点数,最终命中判定标准 0.9
weakThreshold 弱阈值,用于每一轮匹配中检验是否继续匹配,相似度低于它则提前放弃该轮 0.7

匹配方法(method 参数)有 6 种可选,全部来自 OpenCV 的标准模板匹配方法:

取值 匹配方法 说明
0 TM_SQDIFF 平方差匹配法
1 TM_SQDIFF_NORMED 归一化平方差匹配法
2 TM_CCORR 相关匹配法
3 TM_CCORR_NORMED 归一化相关匹配法
4 TM_CCOEFF 系数匹配法
5 TM_CCOEFF_NORMED 归一化系数匹配法

关于识别率,先对齐一个口径:“图像识别率 95% 以上”是官方宣称值,指常规、清晰、静态界面下的表现(功能特性文档)。实际效果取决于截图清晰度、模板是否与当前画面一致、界面是否频繁变化、阈值是否合理——这些我们在第五节讲怎么调。

一个最小可用的找图示例(函数名与参数顺序以文档为准):

image.initOpenCV();                      // 找图前先初始化 OpenCV 类库(首次较慢,之后很快)
let template = readResAutoImage("sms.png"); // 从工程 res 文件夹读取模板小图
let aimage = image.captureFullScreen();  // 抓取当前屏幕

// 找图:弱阈值 0.7,相似度 0.9,最多找 21 个,method=5 归一化系数匹配法
let points = image.findImage(aimage, template, 0, 0, 0, 0, 0.7, 0.9, 21, 5);
if (points && points.length > 0) {
    // 返回的是 Rect 区域,取中心点作为点击坐标
    let x = parseInt((points[0].left + points[0].right) / 2);
    let y = parseInt((points[0].top + points[0].bottom) / 2);
    clickPoint(x, y);
}
image.recycle(aimage);   // 图片用完要回收,防止内存暴涨
image.recycle(template);

找图家族还有几个实用变体:findImageEx 自动截屏找图(省去手动抓图);findImage2 含缩放的找图,比 findImage 更精准(目标在屏幕上被放大或缩小渲染时很管用,EC 9.41.0+);findImageByColor 透明找图,支持透明模板且不需要初始化 OpenCV(EC 7.15.0+)。

性能上有个常识值得知道:截图本身就有开销——captureScreen 需要把截图转为 Bitmap,耗时约 0~20ms,而且短时间内(约 16ms 内)连续调用会返回同一张截图(图色函数文档)。模板匹配内部采用图像金字塔加速(matchTemplatemaxLevel 参数),一般无需手动干预。

四、找色:单点找色、多点找色与色块查找

找色比找图更“轻”:它不比对整张图,而是按颜色找像素——在指定区域里找指定颜色的点,找到就返回坐标。

单点找色(findColor)

在区域内找颜色与给定值(可带偏色)完全匹配的点,返回坐标数组。典型场景:固定色 UI、纯色按钮、状态指示灯。颜色参数格式形如 0xCDD7E9-0x101010,前半是颜色值,后半是偏色容差;threshold 是 01 的颜色相似度,limit 限制返回个数,orz 是 18 的查找方向。

单点找色的短板也很明显:只验证一个点的颜色,遇到大面积同色背景容易误报。

多点找色(findMultiColor)

多点找色用一个点做“锚点”,再校验周围若干点的相对位置 + 颜色,全部符合才算命中:

// 第一个点的颜色作为基准,再校验相对偏移点
// 格式:x偏移|y偏移|颜色[-偏色],多个点用逗号分隔
let firstColor = "0xDD7A5F-0x101010";
let points = "29|25|0xBB454B-0x101010,58|44|0xA6363A-0x101010";

// findMultiColorEx 自动截屏多点找色
let result = image.findMultiColorEx(firstColor, points, 0.9, 0, 0, 0, 0, 10, 1);
if (result && result.length > 0) {
    clickPoint(result[0].x, result[0].y);
}

多点组合相当于给目标发了一张“身份证”——几个点之间的相对位置和颜色几乎不可能在别处巧合重现,抗干扰能力比单点强一个量级。典型场景:图标角标、红点、带纹理的按钮

比色(cmpColor)与色块查找

比色回答的是另一个问题:“某个位置的几个点,颜色是不是我预期的样子?”cmpColor / cmpColorEx 对一组点逐一校验,全部符合才返回 true,适合判断按钮状态切换(“可领取”变灰、可点击状态变化);cmpMultiColor / cmpMultiColorEx 支持多组颜色方案依次尝试,命中就返回该组的索引,未命中返回 -1。

色块查找则利用找色的“批量返回”能力:找色函数会返回区域内所有匹配点(limit 控制个数),从而可以定位连续色块、找到色块的边界。比如血条、进度条这类目标,常见做法是找出血条两端的颜色边界,再按坐标比例估算剩余量——不需要一张完整的模板图。

另外,开发工具自带找色功能,可以直接在截图上取点、取色、生成配置文件,配套的 J 系列函数(findColorJfindMultiColorJ 等)直接从 JSON 文件读取参数,不用手写长串颜色字符串。

五、识别失败怎么办:调优三板斧

识别不是魔法,失败是常态,关键是有一套系统化的排查方法。

第一板斧:调阈值

  • 找不到 → 降低 threshold:0.9 找不到就试 0.85、0.8;
  • 找错位置(误识别)→ 提高 threshold:相似元素太多时把标准调严。

找色的 threshold 同理。注意阈值只是“宽容度”,模板本身要新、要清晰,版本更新后界面变了,先换模板再调阈值。

第二板斧:限定搜索范围

全屏查找既慢又容易被干扰。找图找色都支持 x, y, ex, ey 四个参数限定搜索区域——把范围圈到目标可能出现的小区域,又快又准,还能顺带避免同屏相似元素造成的误识别。

第三板斧:多图备选 + 等待重试

  • 多图备选:同一个目标准备多套模板(不同状态、不同主题),依次尝试;多组比色用 cmpMultiColor 一次搞定;
  • 等待出现:页面加载有延迟,正确姿势是“等目标出现再操作“(循环识别,超时退出),而不是盲目 sleep 固定时长。image.setInitParam 可以设置找图找色动作的最大时间(action_timeout),超时自动返回、避免阻塞。

一个组合兜底示例(找图 + 找色)

识别手段常常要混用:优先找图,找不到就退而求其次找色,再不行就重试。下面这个示例把“申请截图 → 找图 → 找色兜底”串在一起:

function main() {
    // 1. 申请截图权限,整个脚本执行一次即可(type 0 自动选择)
    let request = image.requestScreenCapture(10000, 0);
    if (!request) {
        loge("申请截图权限失败");
        exit();
    }
    // 申请完权限至少等 1 秒再截图,否则可能截不到图
    sleep(1000);

    // 2. 找图:在屏幕上找"开始任务"按钮模板(res 文件夹下的 start.png)
    image.initOpenCV();
    let template = readResAutoImage("start.png");
    // findImageEx 自动截屏找图:弱阈值 0.7,相似度 0.9,只找 1 个
    let points = image.findImageEx(template, 0, 0, 0, 0, 0.7, 0.9, 1, 5);
    if (points && points.length > 0) {
        // 返回 Rect 区域,取中心点点击
        let x = parseInt((points[0].left + points[0].right) / 2);
        let y = parseInt((points[0].top + points[0].bottom) / 2);
        clickPoint(x, y);
    } else {
        // 3. 找图失败,退而求其次:找红色角标(颜色带偏色容差)
        let redPoints = image.findColorEx("0xFF3B30-0x101010", 0.9, 0, 0, 0, 0, 10, 1);
        if (redPoints && redPoints.length > 0) {
            clickPoint(redPoints[0].x, redPoints[0].y);
        } else {
            loge("找图、找色都没有命中,等待后重试");
        }
    }
    image.recycle(template);
}

main();

排查速查表

现象 常见原因 对策
找不到图 目标还没出现 / 模板与画面不一致 / 阈值过高 等目标出现再识别;换新模板;降低 threshold
找错位置 同屏相似元素多 / 阈值过低 提高 threshold;缩小搜索区域;改用多点找色
识别慢 全屏查找 / 频繁手动截图 限定 x,y,ex,ey;用 Ex 系列自动截屏函数
长时间运行内存涨 图片对象没回收 用完立即 image.recycle(img)

最后是截图的三个常识:无障碍模式下首次调用 requestScreenCapture 会弹系统授权,建议选“总是允许”;代理模式下截图不需要权限,长时间无人值守运行建议用代理模式图色函数文档);申请权限后至少要等约 1 秒再截图。

六、降低操作特征:坐标区域内随机点击

自动化操作要面对一个现实问题:机器行为是有特征的。 每次点击同一个像素点、固定间隔、固定顺序,在批量场景下很容易被识别为“非人工操作”。

降低操作特征最实用的一招,是让点击位置在目标区域内随机化——EasyClick 支持所有点击动作在坐标区域内随机(功能特性文档)。这一步和图像识别天然配合:找图返回的本来就是一块区域(Rect),在区域内随机取点即可:

// 在目标区域内随机取点再点击,避免每次点击位置完全一致
let x = parseInt(points[0].left + Math.random() * (points[0].right - points[0].left));
let y = parseInt(points[0].top + Math.random() * (points[0].bottom - points[0].top));
clickPoint(x, y);

需要说明的是,本文讲的所有识别与操作都建立在系统官方能力之上(无障碍服务、ADB、截屏 API 等),本身合法合规;风险永远来自使用用途本身——请始终坚持合规使用,技术手段只是工具。

七、图像识别 API 速览表

以下函数均出自图色函数文档,名称与参数请以文档为准:

类别 函数 作用
截图权限 image.requestScreenCapture(timeout, type) 申请屏幕截图权限;type:0 自动 / 1 授权 / 2 无需权限(需代理模式)
截图 image.captureScreen / image.captureFullScreen / image.captureFullScreenEx 截取屏幕 / 全屏 / 全屏扩展,返回图片对象
截图 image.captureScreenshot / image.captureScreenSurface 代理模式 SurfaceControl 截图(EC 安卓 12.4.0+)
截图 image.captureToFile 截图保存为 PNG 文件
找图 image.findImage(image, template, …) 模板匹配找图,返回位置区域
找图 image.findImageEx(template, …) 自动截屏找图
找图 image.findImage2(image, template, …) 含缩放的找图,更精准(EC 9.41.0+)
找图 image.findImageByColor(image, template, …) 透明找图,无需初始化 OpenCV
找色 image.findColor(image, color, threshold, …) 单点找色,返回坐标数组
找色 image.findColorEx(color, …) 自动截屏单点找色
找色 image.findMultiColor(image, firstColor, points, …) 多点找色
找色 image.findMultiColorEx(firstColor, points, …) 自动截屏多点找色
比色 image.cmpColor / image.cmpColorEx 单点或多点比色,全部符合返回 true
比色 image.cmpMultiColor / image.cmpMultiColorEx 多组比色,返回命中组索引(-1 为未找到)
找非色 image.findNotColor 查找与指定颜色不相等的点
OpenCV image.initOpenCV() 初始化 OpenCV 类库(找图前调用)
OpenCV image.matchTemplate / image.matchTemplateEx 模板匹配封装(弱/强双阈值)
OpenCV image.useOpencvMat(1) 切换 mat 存储格式,更快更省内存(EC 10.18.0+)
资源管理 image.recycle(img) / image.recycleAllImage() 回收图片,防止内存暴涨

两条规律帮你记住这套命名:Ex 结尾 = 自动截屏版(不用手动抓图);J 结尾 = 参数从 JSON 文件读取findColorJfindMultiColorJ 等,配置文件由开发工具生成)。

八、场景建议速查表

你的场景 推荐手段 理由
游戏画面 / 自绘界面(无控件) 找图 findImageEx + 多点找色 模板匹配定位非标准控件
固定色按钮 / 状态点 单点找色 findColorEx 颜色鲜明,执行最快
图标角标 / 红点 / 复杂图标 多点找色 findMultiColorEx 组合特征抗干扰
判断按钮可点 / 不可点 比色 cmpColorEx 一组点全部符合才命中
血条 / 进度条 找色定位颜色边界,按比例估算 无需完整模板图
目标可能被缩放渲染 findImage2 缩放找图 findImage 更精准
需要读取屏幕文字 OCR 识别(PPOCR-V4/V5/V6 全免费、本地离线) 文字类内容用 OCR
需要识别对象(怪物、物品、人物) YOLO 目标检测(ncnn 手机优化,文档 神经网络定位非文字对象
长时间无人值守运行 代理模式 + 区域内随机点击 代理模式截图无需权限

更完整的识别栈(找图找色 / OCR / YOLO 三级)与运行模式选型,可以看安卓自动化技术栈全景

九、FAQ

Q1:找图和找色有什么区别,什么时候用哪个? A:找图基于 OpenCV 模板匹配,识别的是“整张图的样子”,适合特征复杂的目标(图片按钮、图标、游戏画面元素);找色识别的是“颜色特征”,适合颜色鲜明、结构简单的目标(纯色按钮、角标红点、血条端点),执行更快。复杂场景常组合使用。

Q2:OpenCV 图像识别率 95%+ 是什么意思? A:95%+ 是官方宣称的识别率,指常规、清晰、静态界面下的表现。实际效果取决于截图清晰度、模板质量、界面变化程度与阈值设置,识别失败可通过降低阈值、限定区域、多图备选、重试来改善。

Q3:找图的相似度阈值怎么调? A:threshold 是 0~1 的浮点数,默认 0.9。找不到就适当调低(如 0.85、0.8),误识别就调高;weakThreshold(默认 0.7)控制每轮匹配是否继续,一般保持默认即可。

Q4:单点找色和多点找色怎么选? A:单点找色只验证一个点的颜色,遇到大面积同色背景容易误报;多点找色以第一个点颜色为基准,再校验周围若干点的相对位置与颜色,组合特征更唯一、抗干扰更强。图标角标、红点这类场景建议用多点找色。

Q5:图像识别需要 root 吗? A:不需要。截图、识别、点击都基于系统官方能力,在免 root 范围内完成,EasyClick 支持安卓 5.0 及以上的系统。

Q6:找到目标之后怎么点击? A:找图返回位置区域,取中心点或区域内随机点点击;找色返回坐标点数组,取对应 x/y 点击。为降低操作特征,建议点击位置在目标区域内随机化。

Q7:为什么有时候找不到图? A:常见原因:目标还没出现(加载延迟)、模板与实际画面不一致(版本更新、主题变化、缩放)、阈值过高、搜索范围不对。对策:等待目标出现、准备多套模板、降低阈值、限定搜索区域。

Q8:截图需要什么权限? A:无障碍模式下首次调用 requestScreenCapture 会弹出系统授权,选择“总是允许”;代理模式下截图不需要权限,长时间无人值守运行建议用代理模式。申请权限后需等待约 1 秒再截图。

Q9:找图找色慢怎么办? A:把搜索范围限定到目标可能出现的小区域,避免全屏查找;用 Ex 系列自动截屏函数减少重复截图;图片用完及时 recycle;找图前先 initOpenCV;需要极致性能时可用 useOpencvMat 切换 mat 存储格式,速度更快、内存占用更少。

Q10:截图图片为什么要回收? A:截图返回的是内存中的图片对象,不回收会持续占用内存,长时间运行可能内存暴涨。每次用完调用 image.recycle(img) 及时回收即可。


关于 EasyClick:手机自动化 AI 智能体平台,覆盖安卓免 root、iOS 免越狱、鸿蒙 Next 三大生态,提供脚本开发、苹果群控、本地中控投屏与云控系统。→ 了解全部产品

想要真实跑起来?

本文介绍的方案均可在 EasyClick 手机自动化平台落地。官网提供完整文档、开发工具与群控云控产品,免费体验。

访问 EasyClick 官网 →