176e6818aSopenharmony_ci#!/usr/bin/env python3 276e6818aSopenharmony_ci# coding=utf-8 376e6818aSopenharmony_ci 476e6818aSopenharmony_ci# 576e6818aSopenharmony_ci# Copyright (c) 2022 Huawei Device Co., Ltd. 676e6818aSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 776e6818aSopenharmony_ci# you may not use this file except in compliance with the License. 876e6818aSopenharmony_ci# You may obtain a copy of the License at 976e6818aSopenharmony_ci# 1076e6818aSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 1176e6818aSopenharmony_ci# 1276e6818aSopenharmony_ci# Unless required by applicable law or agreed to in writing, software 1376e6818aSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 1476e6818aSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1576e6818aSopenharmony_ci# See the License for the specific language governing permissions and 1676e6818aSopenharmony_ci# limitations under the License. 1776e6818aSopenharmony_ci# 1876e6818aSopenharmony_ci 1976e6818aSopenharmony_ciimport enum 2076e6818aSopenharmony_ciimport time 2176e6818aSopenharmony_ci 2276e6818aSopenharmony_citry: 2376e6818aSopenharmony_ci from PIL import Image 2476e6818aSopenharmony_ci from PIL import ImageDraw 2576e6818aSopenharmony_ciexcept ImportError: 2676e6818aSopenharmony_ci pass 2776e6818aSopenharmony_cifrom devicetest.utils.time_util import TS 2876e6818aSopenharmony_ci 2976e6818aSopenharmony_ci 3076e6818aSopenharmony_ciclass ImgLoc(enum.Enum): 3176e6818aSopenharmony_ci ''' 3276e6818aSopenharmony_ci 图片所在的枚举类 3376e6818aSopenharmony_ci 1左上 (North_West) 3476e6818aSopenharmony_ci 2中上 (North) 3576e6818aSopenharmony_ci 3右上 (Nort_East) 3676e6818aSopenharmony_ci 4左中 (West) 3776e6818aSopenharmony_ci 5全图 (All) 3876e6818aSopenharmony_ci 6右中 (East) 3976e6818aSopenharmony_ci 7左下 (South_West) 4076e6818aSopenharmony_ci 8中下 (South) 4176e6818aSopenharmony_ci 9右下(South_East) 4276e6818aSopenharmony_ci ''' 4376e6818aSopenharmony_ci 4476e6818aSopenharmony_ci # 在大图的左上内 4576e6818aSopenharmony_ci North_West = 1 4676e6818aSopenharmony_ci # 代表子图部分全部位于大图的水平中线以上 4776e6818aSopenharmony_ci North = 2 4876e6818aSopenharmony_ci # 在大图的右上内 4976e6818aSopenharmony_ci North_East = 3 5076e6818aSopenharmony_ci # 代表子图部分全部位于大图的垂直中线左 5176e6818aSopenharmony_ci West = 4 5276e6818aSopenharmony_ci # 全图查找,当小图跟大图的水平中线, 且与锤子中线都有交集,就只能用该参数了 5376e6818aSopenharmony_ci All = 5 5476e6818aSopenharmony_ci # 代表子图部分全部位于大图的垂直中线右 5576e6818aSopenharmony_ci East = 6 5676e6818aSopenharmony_ci # 在大图的左下内 5776e6818aSopenharmony_ci South_West = 7 5876e6818aSopenharmony_ci # 代表子图部分全部位于大图的水平中线以下 5976e6818aSopenharmony_ci South = 8 6076e6818aSopenharmony_ci # 在大图的右下内 6176e6818aSopenharmony_ci South_East = 9 6276e6818aSopenharmony_ci 6376e6818aSopenharmony_ci 6476e6818aSopenharmony_ciclass ImgUtils: 6576e6818aSopenharmony_ci @staticmethod 6676e6818aSopenharmony_ci def img2arr(arr_img, rect=None, convert=True): 6776e6818aSopenharmony_ci ''' 6876e6818aSopenharmony_ci @将位图流转化为二维二值数组 6976e6818aSopenharmony_ci @param arr_img: instance of Image 7076e6818aSopenharmony_ci ''' 7176e6818aSopenharmony_ci if convert and arr_img.mode != 'L': 7276e6818aSopenharmony_ci arr_img = arr_img.convert('L') 7376e6818aSopenharmony_ci 7476e6818aSopenharmony_ci width, height = arr_img.size 7576e6818aSopenharmony_ci pix = arr_img.load() 7676e6818aSopenharmony_ci if rect: 7776e6818aSopenharmony_ci rect_l, rect_t, rect_r, rect_b = rect 7876e6818aSopenharmony_ci result_list = [] 7976e6818aSopenharmony_ci for pix_h in range(height): 8076e6818aSopenharmony_ci if not rect_t <= pix_h <= rect_b + 1: 8176e6818aSopenharmony_ci continue 8276e6818aSopenharmony_ci temp_list = [] 8376e6818aSopenharmony_ci for pix_w in range(width): 8476e6818aSopenharmony_ci if not rect_l <= pix_w <= rect_r + 1: 8576e6818aSopenharmony_ci continue 8676e6818aSopenharmony_ci temp_list.append(pix[pix_w, pix_h]) 8776e6818aSopenharmony_ci result_list.append(temp_list) 8876e6818aSopenharmony_ci return result_list 8976e6818aSopenharmony_ci 9076e6818aSopenharmony_ci result_list = [] 9176e6818aSopenharmony_ci for pix_h in range(height): 9276e6818aSopenharmony_ci temp_list = [] 9376e6818aSopenharmony_ci for pix_w in range(width): 9476e6818aSopenharmony_ci temp_list.append(pix[pix_w, pix_h]) 9576e6818aSopenharmony_ci result_list.append(temp_list) 9676e6818aSopenharmony_ci 9776e6818aSopenharmony_ci return result_list 9876e6818aSopenharmony_ci 9976e6818aSopenharmony_ci @staticmethod 10076e6818aSopenharmony_ci def get_rect(rect_width, rect_height, location): 10176e6818aSopenharmony_ci ''' 10276e6818aSopenharmony_ci 根据方位对象,获取图片的方位 10376e6818aSopenharmony_ci ''' 10476e6818aSopenharmony_ci rect = (0, 0, rect_width, rect_height) 10576e6818aSopenharmony_ci if location == ImgLoc.East.value: 10676e6818aSopenharmony_ci rect = (int(rect_width >> 1), 0, rect_width, rect_height) 10776e6818aSopenharmony_ci elif location == ImgLoc.South.value: 10876e6818aSopenharmony_ci rect = (0, int(rect_height >> 1), rect_width, rect_height) 10976e6818aSopenharmony_ci elif location == ImgLoc.West.value: 11076e6818aSopenharmony_ci rect = (0, 0, int(rect_width >> 1), rect_height) 11176e6818aSopenharmony_ci elif location == ImgLoc.North.value: 11276e6818aSopenharmony_ci rect = (0, 0, rect_width, int(rect_height >> 1)) 11376e6818aSopenharmony_ci 11476e6818aSopenharmony_ci elif location == ImgLoc.North_East.value: 11576e6818aSopenharmony_ci rect = (int(rect_width >> 1), 0, 11676e6818aSopenharmony_ci rect_width, int(rect_height >> 1)) 11776e6818aSopenharmony_ci elif location == ImgLoc.South_East.value: 11876e6818aSopenharmony_ci rect = (int(rect_width >> 1), 11976e6818aSopenharmony_ci int(rect_height >> 1), rect_width, rect_height) 12076e6818aSopenharmony_ci elif location == ImgLoc.North_West.value: 12176e6818aSopenharmony_ci rect = (0, 0, int(rect_width >> 1), int(rect_height >> 1)) 12276e6818aSopenharmony_ci elif location == ImgLoc.South_West.value: 12376e6818aSopenharmony_ci rect = (0, int(rect_height >> 1), 12476e6818aSopenharmony_ci int(rect_width >> 1), rect_height) 12576e6818aSopenharmony_ci 12676e6818aSopenharmony_ci return rect 12776e6818aSopenharmony_ci 12876e6818aSopenharmony_ci @staticmethod 12976e6818aSopenharmony_ci def quick_find(fp1, fp2, similar=1, density=None, 13076e6818aSopenharmony_ci rect=None, loc=ImgLoc.All.value, debug=False): 13176e6818aSopenharmony_ci ''' 13276e6818aSopenharmony_ci 快速查找图片,指定的similar越大,速度越快 13376e6818aSopenharmony_ci 1. 如果similar不等于1,则使用density来加快查找速度 density(x,y) 13476e6818aSopenharmony_ci 表示对比的时候 每个横坐标上只对比 (width / 2^x)个点 13576e6818aSopenharmony_ci 每个纵坐标上只对比 height / 2^y个点,相当于只对比原来的 13676e6818aSopenharmony_ci (width + height) / 2^(x+y) 个点 13776e6818aSopenharmony_ci @param fp1: 大图片的绝对路径 13876e6818aSopenharmony_ci @param fp2: 小图片的绝对路径 13976e6818aSopenharmony_ci @param similar: 对比的相似度;如 0.7, 0.9, 1 14076e6818aSopenharmony_ci @param density: 小图中对比的密度: (2,3) 表示每行对比 width >> 2个点; 14176e6818aSopenharmony_ci 没列对比 height >> 3个点 14276e6818aSopenharmony_ci @param rect: 大图中指定区域内查找 (left,top,right,bottom) 14376e6818aSopenharmony_ci @param loc: 小图在大图中的什么部位,是一个枚举对象,ImgLoc,注意需要 14476e6818aSopenharmony_ci 加.value;如: ImgLoc.North.value 或者 直接输入 1 -9 的数字也行 14576e6818aSopenharmony_ci @param debug: 是否打印debug信息 14676e6818aSopenharmony_ci ''' 14776e6818aSopenharmony_ci if debug: 14876e6818aSopenharmony_ci TS.start() 14976e6818aSopenharmony_ci _m1 = Image.open(fp1) 15076e6818aSopenharmony_ci _m2 = Image.open(fp2) 15176e6818aSopenharmony_ci 15276e6818aSopenharmony_ci m1_w, m1_h = _m1.size 15376e6818aSopenharmony_ci if not rect: 15476e6818aSopenharmony_ci rect = ImgUtils.get_rect(m1_w, m1_h, loc) 15576e6818aSopenharmony_ci 15676e6818aSopenharmony_ci data1 = ImgUtils.img2arr(_m1.crop(rect) if rect else _m1) 15776e6818aSopenharmony_ci data2 = ImgUtils.img2arr(_m2) 15876e6818aSopenharmony_ci if debug: 15976e6818aSopenharmony_ci TS.stop("before find_arr") 16076e6818aSopenharmony_ci return ImgUtils.find_arr(data1, data2, similar, density, rect, debug) 16176e6818aSopenharmony_ci 16276e6818aSopenharmony_ci @staticmethod 16376e6818aSopenharmony_ci def find_arr(im1, im2, similar=1, density=None, rect=None, debug=False): 16476e6818aSopenharmony_ci ''' 16576e6818aSopenharmony_ci 在大图中查找小图 16676e6818aSopenharmony_ci 注意:如果density值为None,则系统自动设置,保证特征点在9 - 16个左右 16776e6818aSopenharmony_ci (即 3 * 3 或 4 * 4之间) 16876e6818aSopenharmony_ci @param im1 大图的二维数组 16976e6818aSopenharmony_ci @param im2 小图的二维数组 17076e6818aSopenharmony_ci @param similar 相似度 17176e6818aSopenharmony_ci @param density (x,y) x: 可以控制小图横坐标查找的点数 17276e6818aSopenharmony_ci im2Width >> x 个点数 17376e6818aSopenharmony_ci @param rect 在指定的区域中查找图片 (若指定,则可以大大节省时间) 17476e6818aSopenharmony_ci (leftX,topY,rihgtX,bottomY) 17576e6818aSopenharmony_ci @return (rect,similar) rect:找到的图片位置; similar:相似度 17676e6818aSopenharmony_ci ''' 17776e6818aSopenharmony_ci if debug: 17876e6818aSopenharmony_ci TS.start() 17976e6818aSopenharmony_ci 18076e6818aSopenharmony_ci m2_width = len(im2[0]) 18176e6818aSopenharmony_ci m2_height = len(im2) 18276e6818aSopenharmony_ci arr_width = len(im1[0]) - m2_width + 1 18376e6818aSopenharmony_ci arr_height = len(im1) - m2_height + 1 18476e6818aSopenharmony_ci 18576e6818aSopenharmony_ci denx, deny = 0, 0 18676e6818aSopenharmony_ci if not density: 18776e6818aSopenharmony_ci denx, deny = ImgUtils.get_density(m2_width, m2_height) 18876e6818aSopenharmony_ci else: 18976e6818aSopenharmony_ci denx, deny = density 19076e6818aSopenharmony_ci den_yy = int(m2_height >> deny) 19176e6818aSopenharmony_ci den_xx = int(m2_width >> denx) 19276e6818aSopenharmony_ci 19376e6818aSopenharmony_ci total = den_yy * den_xx 19476e6818aSopenharmony_ci if total == 0: 19576e6818aSopenharmony_ci total = 1 19676e6818aSopenharmony_ci max_fail_num = (1 - similar) * total 19776e6818aSopenharmony_ci if debug: 19876e6818aSopenharmony_ci print("denXX: %i; denYY: %i; total: %i" % ( 19976e6818aSopenharmony_ci den_xx, den_yy, total)) 20076e6818aSopenharmony_ci print("maxFailNum %i" % max_fail_num) 20176e6818aSopenharmony_ci starttime = time.time() 20276e6818aSopenharmony_ci endtime = starttime + 5.0 * 60.0 20376e6818aSopenharmony_ci for arr_h in range(arr_height): 20476e6818aSopenharmony_ci for arr_w in range(arr_width): 20576e6818aSopenharmony_ci # 对图片对比设置超时限制 20676e6818aSopenharmony_ci if time.time() <= endtime: 20776e6818aSopenharmony_ci # 1. 对比当前位置的图片是否符合要求 20876e6818aSopenharmony_ci fail_num = 0 20976e6818aSopenharmony_ci found = True 21076e6818aSopenharmony_ci for _yy in range(den_yy): 21176e6818aSopenharmony_ci for _xx in range(den_xx): 21276e6818aSopenharmony_ci x_den = _xx << denx 21376e6818aSopenharmony_ci y_den = _yy << deny 21476e6818aSopenharmony_ci m2_val = im2[y_den][x_den] 21576e6818aSopenharmony_ci m1_val = im1[arr_h + y_den][x_den + arr_w] 21676e6818aSopenharmony_ci if m1_val != m2_val: 21776e6818aSopenharmony_ci fail_num += 1 21876e6818aSopenharmony_ci if max_fail_num <= fail_num: 21976e6818aSopenharmony_ci found = False 22076e6818aSopenharmony_ci break 22176e6818aSopenharmony_ci if not found: 22276e6818aSopenharmony_ci break 22376e6818aSopenharmony_ci if found: 22476e6818aSopenharmony_ci if debug: 22576e6818aSopenharmony_ci TS.stop("find_arr") 22676e6818aSopenharmony_ci if rect: 22776e6818aSopenharmony_ci # @UnusedVariable 22876e6818aSopenharmony_ci rect_l, rect_t, rect_r, rect_b = rect 22976e6818aSopenharmony_ci return (1 - fail_num / total), ( 23076e6818aSopenharmony_ci arr_w + rect_l, arr_h + rect_t, arr_w + 23176e6818aSopenharmony_ci m2_width + rect_l, arr_h + m2_height + rect_t) 23276e6818aSopenharmony_ci return (1 - fail_num / total), ( 23376e6818aSopenharmony_ci arr_w, arr_h, arr_w + m2_width, arr_h + m2_height) 23476e6818aSopenharmony_ci else: 23576e6818aSopenharmony_ci return None, None 23676e6818aSopenharmony_ci if debug: 23776e6818aSopenharmony_ci TS.stop("find_arr") 23876e6818aSopenharmony_ci return None, None 23976e6818aSopenharmony_ci 24076e6818aSopenharmony_ci @staticmethod 24176e6818aSopenharmony_ci def img_filter(filter_img, *filters): 24276e6818aSopenharmony_ci last_img = filter_img 24376e6818aSopenharmony_ci for _filter in filters: 24476e6818aSopenharmony_ci last_img = last_img.filter(_filter) 24576e6818aSopenharmony_ci return last_img 24676e6818aSopenharmony_ci 24776e6818aSopenharmony_ci @staticmethod 24876e6818aSopenharmony_ci def get_density(width, height, maxWNum=4, maxHNum=4): 24976e6818aSopenharmony_ci denx, deny = 0, 0 25076e6818aSopenharmony_ci while width > maxWNum: 25176e6818aSopenharmony_ci denx += 1 25276e6818aSopenharmony_ci width = int(width >> 1) 25376e6818aSopenharmony_ci while height > maxHNum: 25476e6818aSopenharmony_ci deny += 1 25576e6818aSopenharmony_ci height = int(height >> 1) 25676e6818aSopenharmony_ci return denx, deny 257