安卓找图:findImage 与多点找色坐标换算
function main() { let req = image.requestScreenCapture(10000, 0); if (!req) { req = image.requestScreenCapture(10000, 0); } if (!req) { loge("申请截图权限失败"); return; } sleep(1000);
let tpl = readResAutoImage("btn.png"); let img = image.captureFullScreen(); if (img != null) { let result = image.findImage(img, tpl, 0, 0, 0, 0, 0.7, 0.9, 1, 5); if (result && result.length > 0) { let r = result[0]; let x = parseInt((r.left + r.right) / 2); let y = parseInt((r.top + r.bottom) / 2); logd("相似度 " + r.similarity + " 坐标 " + x + "," + y); clickPoint(x, y); } else { loge("没找到"); } image.recycle(img); } image.recycle(tpl);}
main();安卓找图从截图开始,坐标按截图分辨率换算。上面这段是能直接跑的完整流程:申请截图权限、读模板、截全屏、找图、点中心、回收图片。每一步为什么这么写,坐标又是什么时候偏的,分开说清楚。
这段代码在做什么
image.requestScreenCapture(10000, 0) 申请截图权限,第一个参数是超时毫秒。失败再申请一次,两次都失败就别往下走了,多半是后台弹出或悬浮窗权限没给。
申请成功不能马上截图。文档里写得很清楚:权限通过后需要一点时间截图才可用,立刻截可能拿到空图,所以中间 sleep(1000)。机器越差,这个等待越要给足。
readResAutoImage("btn.png") 从工程的 res 目录读模板图。模板要按原图截,别在电脑上二次缩放。
image.captureFullScreen() 截全屏,返回一个图片对象。这个对象用完要 image.recycle(img) 回收,不回收跑久了内存会涨。
image.findImage(...) 在大图里找小图,返回的是 Rect 数组,每个元素带 similarity、left、right、top、bottom。注意它不是点坐标,是一个矩形框,图标的左上角和右下角。点击取中心:
let x = parseInt((r.left + r.right) / 2);let y = parseInt((r.top + r.bottom) / 2);findImage 的参数怎么填
签名长这样:
image.findImage(image, template, x, y, ex, ey, weakThreshold, threshold, limit, method)| 参数 | 默认值 | 怎么填 |
|---|---|---|
weakThreshold | 0.7 | 每轮匹配的继续门槛,比 threshold 低一点,0.6–0.7 |
threshold | 0.9 | 相似度,固定界面用 0.9,有压缩或缩放时 0.8–0.85 |
limit | 1 | 只找一个填 1,要找多个填数量 |
method | 5 | 5 是归一化系数匹配,一般不用改 |
x/y/ex/ey | 0 | 全屏找就全填 0;限区域就填起点和终点 |
返回的 similarity 是实际相似度,日志里打出来看一眼,就知道模板质量怎么样。0.95 以上算稳,0.85 勉强能用,0.8 以下就该换模板了。
找不到的时候,先把返回值和相似度打出来,别急着调 threshold。
多点找色:什么时候比找图省事
按钮是纯色、图标很小、或者界面上有动态文字(标题里带数字、带时间),截模板很麻烦,这时候用找色。找色不认形状,认颜色。
颜色写法是 0x主色-0x偏移,偏移那一半是给颜色波动留的容差:
let img = image.captureFullScreen();if (img != null) { let points = image.findMultiColor( img, "0xDD7A5F-0x101010", "29|25|0xBB454B-0x101010,58|44|0xA6363A-0x101010", 0.9, 0, 0, 0, 0, 10, 1 ); if (points && points.length > 0) { for (let i = 0; i < points.length; i++) { logd(points[i].x + "," + points[i].y); clickPoint(points[i].x, points[i].y); } } image.recycle(img);}第二个参数是第一个点的颜色,第三个参数是其余点相对第一个点的偏移和颜色,格式 x偏移|y偏移|颜色。点取得越多越准,但每多一个点就多一份计算。
找色返回的是 Point 数组,直接带 x、y,不用自己算中心。阈值 threshold 是 0.0–1.0,纯色按钮 0.9 就够;半透明、渐变的按钮调到 0.8 左右,再低容易点到别的按钮。
还有一组带 Ex 的函数:findImageEx、findColorEx、findMultiColorEx,不用自己截图,传模板或颜色进去,它自动截当前屏幕再找。少写两行代码,也省掉忘记回收的问题。
坐标对不上,查这三处
第一处,区域截图没有加偏移。image.captureScreen(3, x, y, ex, ey) 截的是一块区域,在里面找图,返回的坐标是相对这块区域的。点击前要把起点加回去:
let img = image.captureScreen(3, 100, 200, 400, 500);if (img != null) { let result = image.findImage(img, tpl, 0, 0, 0, 0, 0.7, 0.9, 1, 5); if (result && result.length > 0) { let x = parseInt((result[0].left + result[0].right) / 2) + 100; let y = parseInt((result[0].top + result[0].bottom) / 2) + 200; clickPoint(x, y); } image.recycle(img);}第二处,分辨率不一致。模板是一台机器截的,换一台屏幕尺寸不同,findImage 的相似度会掉。EC 9.41.0 之后有 image.findImage2,带缩放匹配,比 findImage 稳,多机型混跑的用这个。
第三处,拿到的坐标和点击屏幕不是同一套。查一下 device.getScreenSize() 和截图尺寸对不对得上,对不上说明中间有过缩放。日志里把 left/top/right/bottom 和最终点击坐标一起打出来,偏多少一眼能看出来:
logd("rect " + r.left + "," + r.top + "," + r.right + "," + r.bottom);logd("click " + x + "," + y + " screen " + device.getScreenSize());关于我们:EasyClick 做手机自动化脚本开发和群控,覆盖安卓免 ROOT、iOS 免越狱、鸿蒙。图色函数的完整参数见 image-api 文档;找控件的方式见 安卓节点找控件:选择器写法与失效排查。