UiAutomator2.0颜色验证方案

Android自动化测试中会有不少背景色、透明度等测试验证,但各种框架都没有提供获取对象背景色等API,那么面对这样的测试需求,我们该如何进行呢?

一、方案思路

直接在测试框架上面做文章貌似不可行了,那么我们就要转向Android系统本身了,要验证背景色,首先自然得获取颜色值,UIAutomator2.0等框架没有提供对应API,那么我们还可以通过截图的方式获取元素图片,最直接的方式就可以通过像素点的方式获取颜色值,这样获得的颜色值是基于RGB数值的……这好像有点抽象,还是先了解这些颜色空间概念:
1、RGB颜色模型
在图像处理中,最常用的颜色空间是RGB模型,常用于颜色显示和图像处理,它是三维坐标的模型形式,如下图表示一个三维坐标空间:

其他太复杂的原理我在这里就不深究(毕竟不是做图像开发,再深入的我也不懂了),我们只需要了解RGB模型的3维坐标系(r,g,b)分别代表的是红绿蓝三基色的值:
red红色值(0~255)
green绿色值(0~255)
blue蓝色值(0~255)
2、HSV颜色模型


HSV模型是一个倒锥形模型,如下图所示:


这个模型是按色彩、深浅、明暗(透明度)来描述的,其中:
H:表示色彩,即色度值
S:表示深浅, 即通常说的透明度指标,S = 0时,只有灰度
V:表示明暗,说明色彩的明亮程度
3、RGB与HSV之间的关系

这段其实我不想贴,但是有些学霸还是看得明白的,那就当个搬运工,给学霸们看吧,不想了解原理的童鞋直接跳过关系这块。直观的理解,把RGB三维坐标的中轴线立起来,并扁化,就能形成HSV的锥形模型了。但V与强度无直接关系,因为它只选取了RGB的一个最大分量。而RGB则能反映光照强度(或灰度)的变化。
由RGB到HSV的转换:



OK,言归正传,了解了上面这些颜色模型之后,可以清楚的发现使用HSV可以较大程度的排除掉光线或者分辨率等深浅明暗等外在因素导致的色差,直接去颜色值H可以较准确的进行颜色对比。然而我们通过取截图的像素点颜色得到的RGB三维数组,那么要想实现HSV模型的H值对比,还得进行转换,转换的计算规则就是上面3中的RGB与HSV转换计算(网上大多是C++版本的转换,我下面给出Java版本的实现)。

二、实现代码

RGB数组转换为HSV数组,一般只用HSV数组的色度值H和饱和度S

public static double[] rgbToHsv(int r, int g, int b){
    double h, s, v;
    double min, max, delta;

    min = Math.min(Math.min(r, g), b);
    max = Math.max(Math.max(r, g), b);

    // V 亮度
    v = max;
    delta = max - min;

    // S 饱和度
    if( max != 0 ) {
        s = delta / max;
    }else {
        s = 0;
      h = -1;
      return new double[]{h,s,v};
    }

      // H 色度
    if( r == max ){
        h = (g - b) / delta; // between yellow & magenta
    }else if( g == max ) {
        h = 2 + (b - r) / delta; // between cyan & yellow
    }else {
        h = 4 + (r - g) / delta; // between magenta & cyan
    }
      h *= 60;    // degrees

    if( h < 0 ) {
        h += 360;
    }
    return new double[]{h,s,v};
}

获取对象区域的指定像素点RGB模型数组值

/**
 * 图片区域中指定坐标的rgb
 * @param rect
 * @param filePath
 * @param pixelX
 * @param pixelY
 * @return
 */
public static int[] getColorRgb(Rect rect, String filePath, 
      int pixelX, int pixelY) {
    BitmapFactory.Options bfOptions = new BitmapFactory.Options();
    bfOptions.inDither = false;
    bfOptions.inTempStorage = new byte[12 * 1024];
    bfOptions.inJustDecodeBounds = true;
    Bitmap m = BitmapFactory.decodeFile(filePath);
    m = m.createBitmap(m, rect.left, rect.top, 
        rect.width(), rect.height());//获取区域

    int color = m.getPixel(pixelX, pixelY);
    int r = Color.red(color);
    int g = Color.green(color);
    int b = Color.blue(color);

    m.recycle();
    int [] rgb = {r,g,b};
    return rgb;
}

颜色对比验证方法,首先比对RGB,RGB不一致再对比HSV的色度值H:

public static boolean assertColor(int[] actualRgb, int[] expectedRgb, String msg){
     if(actualRgb == null || expectedRgb == null || actualRgb.length != 3 
        || expectedRgb.length != 3){
        BaseCase.log.e("RGB数组为空或长度不为3", isPrintLog);
        return false;
     }
     String actual = TestUtil.rgbToString(actualRgb);
     String expected = TestUtil.rgbToString(expectedRgb);

    try {
        org.junit.Assert.assertEquals(actual, expected);
        BaseCase.log.i("实际"+ msg +"的RGB值:\""+actual+"\" ,
                 跟期望"+ msg +"的RGB值:\""+expected+"\" 相匹配", isPrintLog);
        return true;
    } catch (Error e) {
        BaseCase.log.i("实际"+ msg +"的RGB值:\""+actual+"\" ,
             跟期望"+ msg +"的RGB值:\""+expected+"\" 不匹配,下面对比HSV的色度值:");
        double[] actualHsv = TestUtil.rgbToHsv(actualRgb[0],actualRgb[1],actualRgb[2]);
        double[] expectedHsv = TestUtil.rgbToHsv(expectedRgb[0],expectedRgb[1],expectedRgb[2]);
        double hRate = Math.abs(actualHsv[0]-expectedHsv[0])/actualHsv[0];
        double sRate = Math.abs(actualHsv[1]-expectedHsv[1])/actualHsv[1];

        if(hRate < 0.05){
                BaseCase.log.i("实际"+ msg +"的HSV色度值:\""+actualHsv[0]+"\" ,
             跟期望"+ msg +"的色度值:\""+expectedHsv[0]+"\" 相匹配", isPrintLog);
           return true;
        }
        if(isPrintLog){
            BaseCase.log.e("实际"+ msg +"的HSV色度值:\""+actualHsv[0]+"\" ,
             跟期望"+ msg +"的色度值:\""+expectedHsv[0]+"\" 不匹配", isPrintLog);
      }
        return false;
    }
}

透明度验证使用HSV模型的S值来对比:

public static boolean assertAlpha(int[] actualRgb, double expectedAlpha, String msg){
     if(actualRgb == null || actualRgb.length != 3){
          BaseCase.log.e("RGB数组为空或长度不为3",isPrint);
       return false;
    }
      double hsv_s = rgbToHsv(actualRgb[0],actualRgb[1],actualRgb[2])[1];
    hsv_s = (double)Math.round(hsv_s*1000)/1000.0;
    double actualAlpha = Double.parseDouble(new DecimalFormat("0.0").format(hsv_s));

    try {
            org.junit.Assert.assertEquals(actualAlpha, expectedAlpha);
        BaseCase.log.i("实际"+ msg +"的透明度:\""+actualAlpha+"\" ,跟期望"+ msg +"的透明度:\""+expectedAlpha+"\" 相匹配",isPrint);
        return true;
    } catch (Error e) {
            double sRate = hsv_s > 0.0 ? Math.abs(hsv_s-expectedAlpha)/hsv_s : 1;
        if(hsv_s == expectedAlpha){
                BaseCase.log.i("实际"+ msg +"的透明度:\""+actualAlpha+"\" ,
             跟期望"+ msg +"的透明度:\""+expectedAlpha+"\" 相匹配",isPrint);
            return true;
        } else if(sRate < 0.1){
                BaseCase.log.i("实际"+ msg +"的透明度:\""+actualAlpha+"\" ,
             跟期望"+ msg +"的透明度:\""+expectedAlpha+"\" 在误差范围内相匹配",isPrint);
           return true;
        }
        if(isPrint){
            BaseCase.log.e("实际"+ msg +"的透明度:\""+actualAlpha+"\" ,
             跟期望"+ msg +"的透明度:\""+expectedAlpha+"\" 不匹配",isPrint, msg + "不匹配");
       }
        return false;
   }
}

测试用例方法中验证颜色:

int[] bgColorRgb = TestUtil.getRandomColorRgb();
setText(getImageBgColor(),TestUtil.rgbToString(bgColorRgb));
assertColor(getColor(adPage.getAdImage(),0,0),bgColorRgb,"大图背景色");

三、总结

用这种方式验证颜色感觉妥妥的,但是也有可能会出现不准的情况,因为是基于像素点取色,依赖于像素点的坐标,然而对于不是纯色的颜色或者图片包含其他杂色,这时候使用像素点就不能随便取了,你可以拿一个准确的点,或者可以不用考虑效率的话,采用遍历全区域取色,再计算占比,将占比最大的RGB值返回,这样准确率肯定高了,但是效率就慢了,特别是对比尺寸较大的图,图像对比操作本来就效率较低且很耗系统资源,所以一般做图像处理的都用C++,当然市面上好像也有一些比较好的图像对比算法和框架,想进一步研究就得靠自己去看文档和研究了。

更多原创技术好文和资料,请关注公众号:测试开发栈
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容