
Appium images-plugin find-by-image 详解-image 图像模板定位策略与图像元素操作实战【免费下载链接】appiumCross-platform automation framework for all kinds of apps, built on top of the W3C WebDriver protocol项目地址: https://gitcode.com/GitHub_Trending/ap/appium本文基于 Appium 仓库中的 find-by-image 官方文档 与appium/images-plugin的源码实现系统讲解-image定位策略的工作原理、Image Element 支持的操作边界、全部相关 Settings 参数及其源码级匹配流程帮助你在无法依赖原生 UI 层级如 Canvas 渲染、自绘界面、跨平台像素级校验的场景下用模板图像精确找到并操作屏幕区域。1. 启用 images 插件appium/images-plugin是 Appium 3 插件架构下的官方插件提供两大能力见 READMEImage Comparison新增 Appium 端点支持多图比较对应文档 image-comparison.md非本篇重点Finding Elements by Image用一张模板图像找到屏幕上与之视觉匹配的区域并以标准 Appium 元素命令对其进行交互——即本文主题。安装与启动方式appium plugin install images appium --use-pluginsimages从 package.json 可以看到该插件的注册信息与运行约束appium: { pluginName: images, mainClass: ImageElementPlugin }插件注册名为images核心类为ImageElementPluginlib/plugin.ts依赖appium^3.0.0-beta.0peerDependency即需要 Appium 3 的插件体系图像匹配核心依赖appium/opencv尺寸校正与缩放依赖sharp图像元素缓存依赖lru-cache。2.-image定位策略selector 就是一段 base64使用该插件支持的-image定位策略时你可以向 Appium 发送一张代表目标元素的图像文件。如果 Appium 在截图中找到了与模板匹配的屏幕区域它会把该区域信息封装成标准WebElement返回给你的 Appium 客户端。各客户端的 API 形式不同例如driver.findElementByImage()。关键约定策略strategy-image源码常量IMAGE_STRATEGY -image见 constants.tsselector必须是模板图像的 base64 编码字符串。对应的 HTTP 端点是标准的 WebDriver 查找端点只是using参数多了一个取值POST /session/:sessionId/element POST /session/:sessionId/elements端点修改说明可参考 plugins API 参考。从源码看拦截逻辑非常清晰ImageElementPlugin重写了findElement/findElements在_find中只有当策略恰好是-image时才介入否则直接放行给后续中间件const [strategy, selector] args; // if were not actually finding by image, just do the normal thing if (strategy ! IMAGE_STRATEGY) { return await next(); } return await this.finder.findByImage(Buffer.from(selector, base64), driver, {multiple});即 selector 被按 base64 解码为Buffer后交给ImageElementFinder完成模板匹配multiple为true时支持查找屏幕上所有匹配区域。3. Image Element 的真相一组坐标 受限的元素操作匹配成功后Appium 会缓存匹配信息并返回标准元素响应在你的测试脚本中实例化为一个标准元素对象。元素 ID 带有appium-image-element-前缀加 UUIDconstants.ts 中IMAGE_ELEMENT_PREFIX客户端拿到的仍是合法的 WebElement。3.1 支持的操作文档与源码ImageElement.execute的 switch 分支一一对应Image Element 仅支持以下方法方法行为源码依据click在元素屏幕包围盒中心构造一次点击含过期检查见第 5 节isDisplayed/elementDisplayed直接返回true既然刚匹配成功就认为可见getSize返回{width, height}匹配矩形宽高getLocation/getLocationInView返回左上角坐标{x, y}getElementRect返回完整Rectx、y、width、heightgetAttributevisual当getMatchedImageResult为true时返回带匹配标记图像的 base64 数据score返回[0.0, 1.0]区间的相似度分数findElementFromElement/findElementsFromElement以该图像元素区域为容器在其中继续用-image查找子元素见 3.3调用其他属性会抛出NotYetImplementedError。3.2 为什么只支持这些操作这些操作之所以被支持是因为它们只依赖屏幕坐标即可工作。而sendKeys之类的操作之所以不支持是因为基于模板图像Appium 唯一能确定的是屏幕上是否存在一个视觉上与模板匹配的区域——它没有能力把这一信息转换成驱动特定的 UI 元素对象也就无法执行依赖原生元素的操作。这里要特别强调文档给出的核心认知Image Element 没有任何魔法——它引用的只是屏幕坐标。所谓点击一个 Image Element内部不过是 Appium 在该元素屏幕包围盒的中心点构造一次点击而且你可以指定使用哪个 API 执行这次点击见 5.2。3.3 两个源码级的加分细节performActions 支持以图像元素为 origin。ImageElementPlugin.performActions会扫描 W3C action sequence当pointerMove/scroll的origin是图像元素时把元素中心坐标累加到x/y偏移上然后删除origin属性交给底层驱动按纯坐标处理const elId util.unwrapElement(actionWithEl.origin as Element); if (!elId?.startsWith(IMAGE_ELEMENT_PREFIX)) { continue; } const imgEl this.finder.getImageElement(elId); // Add the elements center to the offset. actionWithEl.x (actionWithEl.x ?? 0) imgEl.center.x; actionWithEl.y (actionWithEl.y ?? 0) imgEl.center.y; delete actionWithEl.origin;元素缓存与生命周期。ImageElementFinder用 LRU 缓存保存已匹配的 Image Element上限 100 条、TTL 24 小时MAX_CACHE_ITEMS/MAX_CACHE_AGE_MS会话deleteSession时由插件统一清空缓存plugin.ts。对缓存中不存在的 ID 发起命令会抛出NoSuchElementError。4. 匹配全流程findByImage源码拆解ImageElementFinder.findByImage是整条链路的入口完整流程如下每一步都有对应设置项控制见第 6 节读取设置{...DEFAULT_SETTINGS, ...driver.settings.getSettings()}取imageMatchThreshold、imageMatchMethod、fixImageTemplateSize、fixImageTemplateScale、defaultImageTemplateScale、getMatchedImageResult等获取屏幕尺寸优先调用驱动的getWindowRect()兼容已弃用的getWindowSize()。驱动若两者都不支持会直接抛错——因为最终决定在哪里点击的正是屏幕尺寸模板尺寸修正fixImageTemplateSize为true时ensureTemplateSize用sharp读取模板元数据若模板比屏幕或容器矩形大按fit: inside缩放。这一步存在的原因是 OpenCV 不允许模板大于底图获取并校正截图fixImageFindScreenshotDims控制默认truegetScreenshotForImageFind通过驱动的getScreenshot()拿截图若其尺寸与屏幕尺寸不一致会分别处理两类偏差宽高比不一致比较screenAR与shotAR以FLOAT_PRECISION 100000的精度舍入比较不一致时取min(xScale, yScale)作为缩放因子重定尺寸——避免拉伸变形并记录 scale 用于后续坐标换算宽高都不同按屏幕尺寸fit: fill缩放。关闭该设置可跳过检查以提速但可能影响匹配坐标的准确性。模板缩放修正fixImageTemplateScale处理底图被缩放到窗口尺寸后再匹配的场景如 iOS 截图 750×1334、窗口 375×667缩放因子 0.5也可用defaultImageTemplateScale还原用户存储的缩放模板。模板匹配调用compareImages的matchTemplate模式底层是appium/opencv的getImageOccurrence默认 TM_CCOEFF_NORMED 方法传入threshold、visualize、multiple以及可选的method隐式等待重试匹配包裹在driver.implicitWaitForCondition(performLookup)中。若compareImages报Cannot find any occurrences视为尚未找到并继续按隐式等待时间重试等待超时后findElement抛NoSuchElementErrorfindElements返回空数组生成并注册 Image Element把每个结果rect、score、visualization构造为ImageElementvisualization即带匹配标记的图像仅在getMatchedImageResult开启时非空再写入 LRU 缓存并返回包装后的元素 ID。其中score是[0.0, 1.0]的浮点相似度分数1.0 表示完全一致types.ts 中ImageElementOpts的注释与imageMatchThreshold比较决定匹配成败。5. 点击行为详解过期检查与点击策略文档强调你可以告诉 Appium 用哪个 API 执行点击。ImageElement.click的实现完整呈现了这三个设置如何协同5.1 过期检查checkForImageElementStaleness/autoUpdateImageElementPosition点击前若任一设置为true会先用同一模板立即重新匹配一次重匹配失败 → 抛出StaleElementReferenceError重匹配位置与原位置不同equals比较 rect 四个字段时autoUpdateImageElementPosition为true→ 日志提示 Click will proceed at new coordinates并更新this.rect否则按原始坐标点击并提示如需自动更新请设置autoUpdateImageElementPosition。注意重匹配时内部会传ignoreDefaultImageTemplateScale: true因为此时的模板已经是基于设备截图管理的图像不应再按用户存储模板的缩放比例缩放见 image-element.ts。5.2 点击策略imageElementTapStrategy取值仅两种w3cActions默认或touchActions非法值直接抛错。W3C Actions 路径在元素中心(x, y)构造标准 pointer 动作序列——pointerMove(duration 0) →pointerDown(button 0) →pause(125ms常量TAP_DURATION_MS) →pointerUp然后调用驱动的performActions若驱动未实现performActions会告警并降级到 TouchActionsMJSONWP TouchActions 路径构造{action: tap, options: {x, y}}调用performTouch若驱动连performTouch都没实现则抛出明确要求驱动同时支持两种命令的错误。6. 相关设置Settings完整说明图像查找依赖图像分析软件 Appium 截图能力 你提供的参考图像三者结合因此插件提供一组设置来调节匹配行为——有时能加速匹配有时能提高准确性。这些设置通过 Appium Settings API 访问也可以作为特殊能力settings[]在建会话时预置。完整参数表与 constants.ts 的DEFAULT_SETTINGS逐项对应设置名说明取值范围默认值imageMatchThresholdOpenCV 匹配阈值低于该值即认为查找失败。0 表示不用阈值1 表示参考图像必须像素级完全一致。中间值没有绝对含义例如需要大幅缩放参考图的匹配得分会更低。建议先用默认值找不到元素时逐步调低匹配到错误元素时调高0 到 1 之间的数字0.4fixImageFindScreenshotDimsAppium 知道屏幕尺寸而屏幕尺寸最终决定点击坐标。若截图无论来自原生方法还是外部来源与屏幕尺寸不一致开启该设置会让 Appium 调整截图尺寸以对齐确保匹配元素位于正确坐标。若你确定不需要可关闭以略提速true/falsetruefixImageTemplateSizeOpenCV 不允许参考图模板大于底图。若你发送的模板尺寸大于 Appium 截到的截图匹配会自动失败。设为true后 Appium 会把模板缩放到小于截图尺寸避免匹配直接失败true/falsefalsefixImageTemplateScaleAppium 在匹配前会把底图缩放到窗口尺寸。若截图是 750×1334 而窗口是 375×667缩放 0.5而你的参考图是按截图尺寸裁剪的则永远匹配不上。设为true后 Appium 会按相同比例缩放你的参考图true/falsefalsedefaultImageTemplateScale默认 Appium 不缩放模板图像1.0。但存储缩放后的模板可以节省存储例如用 270×32 的模板表示 1080×126 的区域则把该设置设为4.0服务端会先把模板放大回原始比例再比较如0.5、10.0、1001.0checkForImageElementStaleness从匹配成功到实际点击之间元素可能已经不在原位。Appium 唯一能判断的方法是点击前立即重新匹配重匹配失败会抛出StaleElementException。设为false可跳过检查、略提速但可能遭遇陈旧元素问题且没有异常提示true/falsetrueautoUpdateImageElementPosition已匹配的图像在被点击前可能移动了位置。与上一设置类似若重匹配发现位置变化Appium 可自动按新位置点击true/falsefalseimageElementTapStrategy点击已找到的图像元素时使用的触摸 APIW3C Actions 或旧版 MJSONWP TouchActions。除非你的驱动因某些原因不支持 W3C Actions否则保持默认即可w3cActions/touchActionsw3cActionsgetMatchedImageResult默认 Appium 不保存匹配图像结果。将其存入内存有助于调试到底匹配上了哪块区域。开启后元素对attributeAPI 的visual请求会返回匹配区域的图像true/falsefalse补充一点源码信息设置接口 ImageSettings 中还存在imageMatchMethod默认空字符串即使用 OpenCV 默认的 TM_CCOEFF_NORMED用于指定模板匹配算法文档表格未列出但findByImage在设置非空时会把它透传给比较选项见 finder.ts。注意各语言客户端可能通过各自的常量暴露这些设置常量名与上述设置名可能略有差异。7. 调试用visual属性查看匹配结果getMatchedImageResult设置是排查Appium 是否按预期找到了图像的利器。开启后匹配成功的元素会带有visual属性可通过getAttribute取得带匹配标记图像的 base64 数据image-element.ts 中getAttribute对visual返回imgEl.matchedImage对score返回相似度分数# Ruby core driver.update_settings({ getMatchedImageResult: true }) el driver.find_element_by_image path/to/img.png img_el.visual # returns base64 encoded string# Python self.driver.update_settings({getMatchedImageResult: True}) el self.driver.find_element_by_image(path/to/img.png) el.get_attribute(visual) # returns base64 encoded string调试时建议配合score属性一起看分数明显低于预期阈值附近时多半是模板裁剪范围、DPI 缩放见fixImageTemplateScale/defaultImageTemplateScale或阈值设置的问题。8. 端到端测试如何验证这条链路插件自带的 E2E 测试 plugin.e2e.spec.ts 使用 fake-driver 启动真实 Appium 服务--use-pluginsimages验证了本文涉及的核心能力可直接作为调用范例// 用本地图片路径作为模板定位客户端 SDK 负责转 base64 const imageEl await driver.$(APPSTORE_IMG_PATH); const {x, y} await imageEl.getLocation(); // 断言 x28, y72 const {width, height} await imageEl.getSize(); // 断言 80×91 await imageEl.click(); // performActions 中以图像元素为 origin const actionSequence { type: pointer, id: mouse, parameters: {pointerType: touch}, actions: [ {type: pointerMove, x: 0, y: 0, duration: 0, origin: imageEl}, {type: pointerDown, button: 0}, {type: pause, duration: 125}, {type: pointerUp, button: 0}, ], }; await driver.performActions([actionSequence]);测试还覆盖了子元素查找对图像元素调用saveScreenshot保存其区域截图即getElementScreenshot返回模板原图的 base64用sharp裁剪出中间 1/2 区域作为新模板再通过imageEl.$(tmpImgPath)在该元素范围内继续-image查找——这正是 3.3 节中findElementFromElementcontainerRect容器过滤containsRect的用武之地。测试用的模板图像为 appstore.png。9. 小结-image策略 base64 selector 是全部调用约定找到的元素本质是坐标只支持位置类操作click、isDisplayed、getSize/getLocation/getElementRect、visual/score属性默认设置已经比较稳健阈值 0.4、截图尺寸校正开启、过期检查开启、W3C 点击找不到元素先调低imageMatchThreshold点错元素先调高它高分屏/DPI 缩放导致的匹配失败优先考虑fixImageTemplateScale与defaultImageTemplateScale调试匹配区域用getMatchedImageResultvisual属性所有行为的最终实现均可在 packages/images-plugin/lib 中核对plugin.ts端点拦截、finder.ts匹配流程、image-element.ts元素操作与点击、constants.ts默认值。【免费下载链接】appiumCross-platform automation framework for all kinds of apps, built on top of the W3C WebDriver protocol项目地址: https://gitcode.com/GitHub_Trending/ap/appium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考