Appium Appium 中截取 element 图片作为对比,判断对比结果

xuxu · 2014年09月15日 · 最后由 maohangyu 回复于 2018年10月04日 · 3500 次阅读
本帖已被设为精华帖!

哈哈,看了以下两个帖子,综合下捣鼓出了这个对比的功能
@ianxiaohanxu
http://testerhome.com/topics/1389
@monkey
http://testerhome.com/topics/202

其实在https://github.com/gb112211/Adb-For-Test 里面有一个截取 element 进行对比的方法,但是在使用 appium 时是无法使用的,因为其用到了 uiautomator 命令。。。

在 appium 中截取界面某个 element,也就是截取屏幕的部分区域进行对比,在以图片对比结果作为判断依据的时候还是有用的,直接上代码:
extend.py

#!/usr/bin/env python
#coding=utf-8

import os
import platform
import tempfile
import shutil

from PIL import Image

PATH = lambda p: os.path.abspath(p)
TEMP_FILE = PATH(tempfile.gettempdir() + "/temp_screen.png")

class Appium_Extend(object):
    def __init__(self, driver):
        self.driver = driver

    def get_screenshot_by_element(self, element):
        #先截取整个屏幕,存储至系统临时目录下
        self.driver.get_screenshot_as_file(TEMP_FILE)

        #获取元素bounds
        location = element.location
        size = element.size
        box = (location["x"], location["y"], location["x"] + size["width"], location["y"] + size["height"])

        #截取图片
        image = Image.open(TEMP_FILE)
        newImage = image.crop(box)
        newImage.save(TEMP_FILE)

        return self

    def get_screenshot_by_custom_size(self, start_x, start_y, end_x, end_y):
        #自定义截取范围
        self.driver.get_screenshot_as_file(TEMP_FILE)
        box = (start_x, start_y, end_x, end_y)

        image = Image.open(TEMP_FILE)
        newImage = image.crop(box)
        newImage.save(TEMP_FILE)

        return self

    def write_to_file( self, dirPath, imageName, form = "png"):
        #将截屏文件复制到指定目录下
        if not os.path.isdir(dirPath):
            os.makedirs(dirPath)
        shutil.copyfile(TEMP_FILE, PATH(dirPath + "/" + imageName + "." + form))

    def load_image(self, image_path):
        #加载目标图片供对比用
        if os.path.isfile(image_path):
            load = Image.open(image_path)
            return load
        else:
            raise Exception("%s is not exist" %image_path)

    #http://testerhome.com/topics/202
    def same_as(self, load_image, percent):
        #对比图片,percent值设为0,则100%相似时返回True,设置的值越大,相差越大
        import math
        import operator

        image1 = Image.open(TEMP_FILE)
        image2 = load_image

        histogram1 = image1.histogram()
        histogram2 = image2.histogram()

        differ = math.sqrt(reduce(operator.add, list(map(lambda a,b: (a-b)**2, \
                                                         histogram1, histogram2)))/len(histogram1))
        if differ <= percent:
            return True
        else:
            return False

接着跑了个 appium 脚本简单测试了下:
extend_test.py

#coding=utf-8

import unittest
import os

from extend import Appium_Extend
from appium import webdriver

class Test(unittest.TestCase):
    #初始化环境
    def setUp(self):
        desired_caps = {}
        desired_caps["platformName"] = "Android"
        desired_caps["platformVersion"] = "4.3"
        desired_caps["deviceName"] = "788a6ab5"
        desired_caps["appPackage"] = "com.android.settings"
        desired_caps["appActivity"] = ".Settings"

        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

        self.extend = Appium_Extend(self.driver)

        #回到主屏幕
        self.driver.press_keycode(3)

    #退出测试
    def tearDown(self):
        self.driver.quit()

    def test_get_screen_by_element(self):
        element = self.driver.find_element_by_id("com.android.deskclock:id/imageview")

        self.extend.get_screenshot_by_element(element).write_to_file("f:\\screen", "image")
        self.assertTrue(os.path.isfile("f:\\screen\\image.png"))

    def test_same_as(self):
        element = self.driver.find_element_by_id("com.android.deskclock:id/imageview")

        load = self.extend.load_image("f:\\screen\\image.png")
        #要求百分百相似
        result = self.extend.get_screenshot_by_element(element).same_as(load, 0)
        self.assertTrue(result)

if __name__ == "__main__":
    suite = unittest.TestSuite()
    suite.addTest(Test("test_get_screen_by_element"))
    suite.addTest(Test("test_same_as"))
    #执行测试
    unittest.TextTestRunner().run(suite)

这里截取的图片是下面这张图中的时间插件:

截取后的图片:

另外批量截图、批量对比就针对需求再做扩展了!

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 15 条回复 时间 点赞

只是纯 element 的截取比对,同位图比对。不太推荐这种方式,比较好的是整张图任意区域比对,可以事先截取任意 subimage 之后执行脚步使用 opencv 比对 PIL 做部分匹配比对感觉很搓,同尺寸比对还好。还有个问题是你这种方式如果不做 element 比对 做其它子图比对的话必须要做一个 map 对应图片与坐标长宽

我想的是不要两两对比了, 有些时候做兼容性测试, 都是几百款设备, 有时候需要对比一个功能中的某个按钮在上百款设备中是否一致. 所以最好是对图片做特征的 hash 化, 直接比较 hash 好了. 每个图片只需要生成一个 hash,

xuxu #13 · 2014年09月15日 Author

@vigossjjj 在单独验证某个问题,需要用到图片部分匹配作为判断依据时才会采用该方法,一般会以当前界面的 activity、某个 element 是否存在作为对比依据。采用 PIL 进行简单的对比还是挺方便的。
@seveniruby 对不同分辨率做兼容性测试,需要批量对比时,抽取图像特征进行对比,得改天花时间想下

#1 楼 @vigossjjj PIL 主要就是简单粗暴,不过反正不管怎么样,我已经对 UI 失去兴趣了

#3 楼 @xuxu 我觉得这个还不算真正的部分匹配,你是在 activity 中自动截取 element 的图像与事先准备好的 element 图像做比对。@monkey 想听听你对图片比对的思路,怎么样做可以保障合理的 roi?

我们之前做过 ORC,感觉也不靠谱,成功率不能达到 100% 准确,就都是不靠谱的。

@xuxu今天才遇到一坑,补充一下,IOS 的图像坐标是需要乘系数的。
如抓到的图片坐标是(2,3,10,11)
实际在 IPHONE6 的截图上坐标是(4,6,20,22)

我也是调用 extend ,运行代码提示 No module named 'extend'

xuxu #7 · 2017年09月13日 Author

@wallerson 这个模块需要放在你的测试脚本同级的目录下

xuxu 回复

这个包是你自己写的吗?我在官方文档没有找到呢。抱歉,我才开始接触这一块的东西

xuxu #11 · 2017年09月14日 Author
wallerson 回复

自己写的。

xuxu 回复

方便提供下代码吗?万分感谢

IOS 采用自定义截图的时候,一样的坐标位置,有时候截取的是空白,有时候截取的符合 app 界面,有时候只有半屏

梦梦GO 回复

原来直接截取突变是 X3 存放的,感谢楼主

梦梦GO 回复

请问最后发现是什么原因啊?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册