目录
问题描述
在一些文本描述页面中,通常会出现使用于正文不同的(通常选用比较鲜明的)颜色对一些超链接或其他内容进行突出显示,如图所示:
这边选用了两个APP的弹窗显示作为案例
图中《用户服务协议》和《隐私政策》都使用了与正文不同的颜色作为突出显示。
与web页面不同,现有自动化测试脚本无法识别出<red>或者<blue>标签(实际上在app开发过程中也不一定使用html语言),我能拿到的仅有这张图片、文本内容、还有一些简单的布局信息
很明显,不同APP用于突出显示的颜色和正文的颜色甚至底色都可不尽相同,我要做的是使代码能够自动识别哪个颜色是突出显示的颜色,并且进一步把突出显示的部分的坐标标注出来。
解决思路
受限于自动化测试框架的限制,直接使用框架获取坐标的方案是行不通的;
于是尝试使用图像处理的方案
精简图片
我们要找的是上面文本描述中颜色突出的部分,但是整个页面上颜色突出的明显不止《隐私协议》,下方的“同意”按钮同样非常鲜明,因此需要精简一下要处理的图片
幸运的是,自动化测试框架(我用的是UIAutoMator2)已经提供了相应功能,可以针对控件单独截图,效果大概是这样
搞定!
提取颜色
粗瞄一眼,图片中存在的有蓝色、黑色、白色,下面还有特效造成的灰色
但代码是不懂这个的,图片中实际存在的颜色远不止这几个
所以这里找了个用KMeans计算色卡的方案,为了保证效果,在此之前对图像打了个马赛克
需要注意的是,这里的打码用的方式是计算方圆一定范围内的最小值(正常的马赛克好像是平均值来着,没研究过),效果大概这样
对,还是那张图片,但是糊了,就想墨水湮开一样,这样处理是为了方便后续进行聚类计算的时候减少干扰(意识流,不加这一步到底效果如何没测试过,有兴趣的可以试试)
然后聚类,使用KMeans的时候我设置了分成5类,一般这种场景下5个颜色是比较合适的,基本的就三个(底色、正文颜色、突出正文的颜色),在留俩余量用于辅助后续计算。
叮!获得色卡
找到那只特立独行的猪(~~
可以看出,我们要的蓝色已经包含在里面了,其他的白色、黑色、灰色跟它比起来都显得那么平淡,虽然已经一眼就能看出来了,但代码还是需要计算才能“看到”这个异端。
现在把五种颜色的RGB值作为坐标输入坐标系(没找到好用的三维坐标系工具,用二维分别显示xy关系和xz关系也一样)
图中蓝点就是五个颜色在每个坐标系中的对应位置,绿线是使用numpy计算的的拟合曲线,
原理很简单,拟合曲线表达的是散点的整体趋势,而离拟合曲线距离最远的点就是跟大家最格格不入的那个。
最终距离用两个坐标系的距离加起来就好,硬要转换成三维坐标系再计算“真正”的距离也行,反正都是正相关,我们要的也只是排名而非具体数值。
距离分别是
[28.230040762072576, 122.2510082584425, 181.78840487811084, 79.90634236787547, 7.861095013861184]
距离最大的是第三个,cv2打印一下RGB值(cv2的RGB顺序应该是BGR):
[255.0, 171.44061303, 1.33333333]
检查一下
冇问题
后续
接下来就简单了,知道了具体的颜色,遍历也好,用其他什么工具也好,可以很容易的找出原图中颜色为#01ABFF的区域,再代入控件的左边计算一下,该颜色区域在整个页面的绝对坐标也不难算出。
具体实现
这里只给了计算突出颜色的方法,从截图到计算全部过程
关于UIAutoMator2的用法请参考github官方文档
# coding: utf-8
#
import copy
from collections import Counter
import cv2
import matplotlib.pyplot as plt
import numpy as np
import uiautomator2 as u2
from PIL import Image
from sklearn.cluster import KMeans
def msk(img,size):
'''
将图片进行min马赛克处理
方便对颜色进行提取聚类
'''
ret=copy.copy(img)
for index_1 in range(0,max(len(img),size),size):
for index_2 in range(0,max(len(img[index_1]),size),size):
index_1_s=index_1 if index_1+size<len(img) else len(img)-size
index_2_s=index_2 if index_2+size<len(img[index_1]) else len(img[index_1])-size
index_1_f=index_1_s+size
index_2_f=index_2_s+size
item=[255,255,255]
for i in range(index_1_s,index_1_f):
for j in range(index_2_s,index_2_f):
for itemi in range(3):
item[itemi]=min(item[itemi],img[i][j][itemi])
#item[itemi]+=img[i][j][itemi]
#for index_item in range(3):
# item[index_item]=item[index_item]/(size*size)
for i in range(index_1_s,index_1_f):
for j in range(index_2_s,index_2_f):
for itemi in range(3):
ret[i][j][itemi]=item[itemi]
return ret
def special_colors(color_list):
'''
返回一组颜色中最为突出(相较其他颜色更为显著)的颜色
'''
x=np.array([i[0] for i in color_list])
y=np.array([i[1] for i in color_list])
z=np.array([i[2] for i in color_list])
xy = np.polyfit(x, y, 1)
xz = np.polyfit(x, z, 1)
def point_distance_line(point,line_point1,line_point2):
#计算
vec1 = line_point1 - point
vec2 = line_point2 - point
distance = np.abs(np.cross(vec1,vec2)) / np.linalg.norm(line_point1-line_point2)
return distance
xy_p0_x=0
xz_p0_x=0
xy_p0_y=xy[1]
xz_p0_y=xz[1]
xy_p1_x=1
xz_p1_x=1
xy_p1_y=xy[0]+xy[1]
xz_p1_y=xz[0]+xz[1]
distance_xy=[]
distance_xz=[]
for i in color_list:
param_point=np.array([i[0],i[1]])
param_line_point1=np.array([xy_p0_x,xy_p0_y])
param_line_point2=np.array([xy_p1_x,xy_p1_y])
distance_xy.append(point_distance_line(param_point,param_line_point1,param_line_point2))
param_point=np.array([i[0],i[2]])
param_line_point1=np.array([xz_p0_x,xz_p0_y])
param_line_point2=np.array([xz_p1_x,xz_p1_y])
distance_xz.append(point_distance_line(param_point,param_line_point1,param_line_point2))
# print('xy距离 - {}'.format(distance_xy))
# print('xz距离 - {}'.format(distance_xz))
# print('距离 - {}'.format([distance_xy[index]+distance_xz[index] for index in range(len(distance_xy))]))
max_distance=max([distance_xy[index]+distance_xz[index] for index in range(len(distance_xy))])
ret_index=[distance_xy[index]+distance_xz[index] for index in range(len(distance_xy))].index(max_distance)
return color_list[ret_index]
# cv2.imshow("OpenCV",palette_perc(clt_1))
# cv2.waitKey()
# cv2.destroyAllWindows()
def work():
d = u2.connect()
# image = d(resourceId="cn.youth.news:id/are").screenshot()
image = d.xpath('//android.widget.ScrollView').screenshot()
img = cv2.cvtColor(np.asarray(image),cv2.COLOR_RGB2BGR)
img2=msk(img,10)
clt=KMeans(n_clusters=5) # 定义聚类器,预设聚5类
clt_1 = clt.fit(img2.reshape(-1, 3)) # 计算聚类
that_color=special_colors(clt_1.cluster_centers_)
print(that_color)
if __name__ == '__main__':
work()
引用
使用KMeans计算图片的色卡:使用KMeans计算图片主要颜色
python计算拟合曲线:python计算拟合曲线