# -*- coding:utf-8 -*- import inspect import os, sys, time ''' 用于图片特征提取和定位 ''' import image_util import PreImageJudge import cv2 as cv import numpy as np import json from ssat_sdk.sat_environment import * from TST.NetOCR import * from ssat_sdk.ocr_convert import * from color_space import * from line_util import LineUtil from ssat_sdk.device_manage import * # 导入枚举; from enum import Enum from itertools import chain # from ssat_sdk.device_manage.RedRatHub3 import RedRat3 from ssat_sdk.tv_operator import * from ssat_sdk.video_capture import VideoCapture # import ssat_sdk.utils.LoggingUtil from ssat_sdk.utils import LoggingUtil from ssat_sdk.utils import string_util from ssat_sdk.picture.RGB import RGBColor from ssat_sdk.picture.pq_detect import PQDetect pyFileName = os.path.split(__file__)[-1] def get_current_function_name(): return inspect.stack()[1][3] # 效果图形状 class IconShape(Enum): Rectangle = 0 # 矩形; Circle = 1 # 圆形(包括椭圆) Polygon = 2 # 多边形 class FocusType(Enum): Border = 0 # 加框; Coloring = 1 # 着色; Deform = 2 # 变形; ColorLine = 3 # 纯色线查找矩形; ThresholdBinary = 4 # 二值化阀值查找焦点框; matchTempl = 5 # 模板匹配法查找焦点框; class Direction(Enum): UnChange = 0 # 区域不变; TopChange = 1 # 上; BottomChange = 2 # 下; LeftChange = 3 # 左; RightChange = 4 # 右; ZoomIn = 5 # 放大; ZoomOut = 6 # 缩小; methods = [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED] class FeatureDetect(): def __init__(self): self.className = self.__class__.__name__ self.CIELuv = CIEluvCaculator() self.OCR = OCRConvert() self.lineUtil = LineUtil() # self.redRat3 = RedRat3() self.redRat3 = TvOperator() self.vp = VideoCapture() self.PQ = PQDetect() ''' 检测传入图片的指定区域的线段 :param file:图片文件路径 :param lineMineLen:线段最短长度。 默认 20 :param lineMaxLen:线段最长长度。默认 1000 :param ableArea:指定区域。 默认 整张图片 :return 返回线段数组 ''' def getLinesByFile(self, file, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5): img = cv.imread(file) return self.getLines(img, lineMinLen, lineMaxLen, ableArea, threshold1, threshold2, apertureSize) def getLines(self, img, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5 , lineThreshold = 200): targetMat = np.array(img) if (ableArea <> None): targetMat = image_util.cutMat(img, ableArea) grayMat = cv.cvtColor(targetMat, cv.COLOR_BGR2GRAY) # cv.imshow("gray", grayMat) edges = cv.Canny(grayMat, threshold1, threshold2, apertureSize=apertureSize) # cv.imshow("edges",edges) lines = cv.HoughLinesP(edges, 2, np.pi / 180, lineThreshold) retLines = [] if lines is None: return None, None for line in lines: x1, y1, x2, y2 = line[0] # print "line:", x1, y1, x2, y2 # "line rgb:",PreImageJudge.getImgAverageRGB(targetMat, (x1, y1, x2+1, y2+1)), \ # "icon rgb:",PreImageJudge.getAverageRGB(icon) lineLen = self.lineUtil.caculateLinLen(line[0]) if lineLen > lineMinLen and lineLen < lineMaxLen: # print "retLine:", line[0] cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2) retLines.append(line[0]) # cv.imshow("getLines", img) # cv.waitKey(0) # cv.destroyAllWindows() return retLines, img def findRectByFile(self, picPath, type): img = cv.imread(picPath) return self.findRectByImg(img, type) def findRectByImg(self, img, type): retLines = None lineImg = None if (type == "parentLock"): retLines, lineImg = self.getLines(img, lineThreshold=200) comLines = self.lineUtil.combineLineList(retLines) # for line in comLines: # print "comLines",line # cv.line(img, (line[0],line[1]),(line[2],line[3]), (0, 0, 255), 2) minPoint,maxPoint = self.lineUtil.getLineMMPoint(comLines) rect = [minPoint[0],minPoint[1], maxPoint[0],maxPoint[1]] # print rect # cv.rectangle(img, (minPoint[0], minPoint[1]), (maxPoint[0],maxPoint[1]), (0, 0, 255), 2) # cv.imshow("comLine", img) # cv.waitKey(0) # cv.destroyAllWindows() return rect # print retLines # cv.imwrite(os.path.join(LoggingUtil.getCaseRunLogDirPath(), "lineImg.png"), lineImg) ''' 根据icon,寻找在图片中icon的位置,返回选中区域的特征:文本、文本区域、聚焦区域 :param icon:图标文件路径 :param screen:整张图片文件路径 :param uiDict: 可能出现icon的区域。 例如: #name的字符串,需为text_area区域显示的文字 { focus_tv : '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}', focus_av : '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}', focus_hdmi1 : '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}', focus_hdmi2 : '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}', focus_usb : '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}' } :param border_wide:四周选择框的厚度 :return 返回文本、文本区域、聚焦区域 ''' def getFocusArea(self, icon, screen, uiDict, border_wide): # 1 获取焦点框特征 icon_luv= self.calBorderAVGLuvPic(icon, border_wide) print "icon:", icon_luv # 2 与可能的聚焦位置进行比较 # print self.focusDict # 计算直线数最接近区域 target_text_area = None target_focus_area = None dLine = 10000 focus_end = "focus_end" for focus in uiDict: one_dict = json.loads(uiDict[focus]) area = one_dict["focus_area"] focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png") image_util.saveCropPic(screen, focus_img, area) img_luv = self.calBorderAVGLuvPic(focus_img, border_wide) # print focus, " ,img_luv:", img_luv if img_luv is None: continue diff = self.CIELuv.getDiffUV(icon_luv, img_luv) if diff[2] < dLine: dLine = diff[2] # print "current dLine:", dLine, area, one_dict["text_area"] target_focus_area = area target_text_area = one_dict["text_area"] focus_end = focus text_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png") image_util.saveCropPic(screen, text_img, target_text_area[0:4]) language = target_text_area[4] ocrType = target_text_area[5] if len(target_text_area) != 6 or target_text_area[6] == {}: target_text = self.OCR.getStr(text_img, language, ocrType) else: target_text = self.OCR.getStrWithImgProcess(text_img, target_text_area[6], language, ocrType) # target_text = self.OCR.getStr(text_img, language, ocrType) print u"getFocusArea,选中区域文字:",target_text return target_text, target_text_area, target_focus_area ''' 根据icon,寻找在图片中icon的位置 :param icon:选择框图标文件路径 :param screen:整张图片文件路径 :param focusDict: 可能出现icon的区域。 例如: #name的字符串,需为text_area区域显示的文字 { 'back': [152, 780, 432, 860], 'forgot': [120, 946, 486, 1010], 'gmain': [152, 618, 386, 672] } :param border_wide:四周选择框的厚度 :return 聚焦区域 ''' def locateFocusArea(self, icon, screen, focusDict, border_wide): icon_img = cv.imread(icon) return self.locateImgFocusArea(icon_img, screen, focusDict, border_wide) ''' 根据icon,寻找在图片中icon的位置 :param icon:选择框图标的cv的mat对象 :param screen:整张图片文件路径 :param focusDict: 可能出现icon的区域。 例如: #name的字符串,需为text_area区域显示的文字 { 'back': [152, 780, 432, 860], 'forgot': [120, 946, 486, 1010], 'gmain': [152, 618, 386, 672] } :param border_wide:四周选择框的厚度 :return 聚焦区域 ''' def locateImgFocusArea(self, icon, screen, focusDict, border_wide): # 1 获取焦点框特征 icon_luv = self.calBorderAVGLuvImg(icon, border_wide) # print "icon luv:", icon_luv # 2 与可能的聚焦位置进行比较,按LUV平均值接近度衡量 focus_area = None focus_name = None dLine = 10000 for focus in focusDict: area = focusDict[focus] # print "area name:", focus focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "locateImgFocusArea_focus.png") image_util.saveCropPic(screen, focus_img, area) img_luv = self.calBorderAVGLuvPic(focus_img, border_wide) # print "img_luv:", img_luv,one_dict if img_luv is None: continue diff = self.CIELuv.getDiffUV(icon_luv, img_luv) # print focus, img_luv,diff if diff[2] < dLine: dLine = diff[2] # print "current dLine:", dLine, area, one_dict["text_area"] focus_area = area focus_name = focus return focus_name, focus_area ''' 计算图片四周边框的颜色平均值,颜色空间Luv :param pic:图片文件路径 :param wide:四周边框的宽度 :return LUV:四周边框Luv平均值 ''' def calBorderAVGLuvPic(self,pic, wide): img = cv.imread(pic) return self.calBorderAVGLuvImg(img, wide) def calBorderAVGLuvImg(self, img, wide): luv_img = cv.cvtColor(img, cv.COLOR_BGR2Luv) height, width, colorDim = luv_img.shape left_area = [0,0,wide,height] right_area = [width-wide,0,width,height] top_area = [wide,0,width-wide,wide] bottom_area = [wide,height-wide,width-wide,height] return self.CIELuv.getMAverageLUV(luv_img,[left_area,right_area,top_area,bottom_area]) # Ex interface ''' 描述:寻找图片中符合icon效果图的位置,返回焦点区域的特征:文本、文本区域、聚集区域. 参数: icon_path: 效果图路径; screen_path: 要检测焦点框的全屏图路径; uiDict: ui字典; 示例字典,需为text_area区域显示的文字 { focus_tv : '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}', focus_av : '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}', focus_hdmi1 : '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}', focus_hdmi2 : '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}', focus_usb : '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}' } icon_shape: 效果图形状; focus_type: 焦点类型; focus_direction: 焦点变化方向; border_wide: 焦点框边框大小; 返回值: None 示例: 注意: ''' def getFocusAreaEx(self, icon_path, screen_path, uiDict, icon_shape, focus_type, focus_direction, border_wide): # 整型转枚举; if type(icon_shape) != type(IconShape.Rectangle): icon_shape = IconShape(icon_shape) if type(focus_type) != type(FocusType.Border): focus_type = FocusType(focus_type) if type(focus_direction) != type(Direction.ZoomIn): focus_direction = Direction(focus_direction) # 矩形效果图; if icon_shape == IconShape.Rectangle: return self.getRectangleFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide) # 圆形效果图; elif icon_shape == IconShape.Circle: return self.getCircleFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide) # 非矩形平行四边形效果图; elif icon_shape == IconShape.Polygon: return self.getPolygonFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide) else: LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:icon_shape不存范围内'+icon_shape) return None, None, None # 获取矩形的焦点区域; def getRectangleFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide): # 焦点框(外镶边框) if focus_type == FocusType.Border: return self.getBorderRectangleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide) # 区域变色; elif focus_type == FocusType.Coloring: return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction) # 图形变化; elif focus_type == FocusType.Deform: return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction) # 获取圆形(包含椭圆)的焦点区域; def getCircleFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide): # 焦点框(外镶边框) if focus_type == FocusType.Border: return self.getBorderCircleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide) # 区域变色; elif focus_type == FocusType.Coloring: return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction) # 图形变化; elif focus_type == FocusType.Deform: return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction) # 多边形的焦点区域; def getPolygonFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide): # 焦点框(外镶边框) if focus_type == FocusType.Border: return self.getBorderRectangleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide) # 区域变色; elif focus_type == FocusType.Coloring: return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction) # 图形变化; elif focus_type == FocusType.Deform: return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction) ''' # 描述:查找矩形焦点框,选中焦点框的效果为矩形边框外镶一层有色边框。focus_type=0,简述:矩形边框。 # 参数: # icon_path:焦点框被选中时,外镶一层边框的效果图路径。 # screen_path:需要判断焦点框的电视画面截图。 # uiDict: # focus_direction: # border_wide:焦点框被选中时,外镶边框的厚度(像素宽) # 返回值: # # ''' def getBorderRectangleFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide): # 获取焦点框特征 icon_luv = self.calBorderAVGLuvPic(icon_path, border_wide) # 计算直线数最接近区域 target_text_area = None target_focus_area = None dLine = 10000 focus_end = "focus_end" for focus in uiDict: # one_dict = json.loads(uiDict[focus]) one_dict = uiDict[focus] # print one_dict["focus_area"] # diff = 11 area = one_dict["focus_area"] # area = [one_dict["focus_area"][0] - diff,one_dict["focus_area"][1] - diff,one_dict["focus_area"][2] + diff,one_dict["focus_area"][3] + diff] # zoomin; # print area focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png") image_util.saveCropPic(screen_path, focus_img, area) img_luv = self.calBorderAVGLuvPic(focus_img, border_wide) # print focus, " ,img_luv:", img_luv if img_luv is None: continue diff = self.CIELuv.getDiffUV(icon_luv, img_luv) if diff[2] < dLine: dLine = diff[2] # print "current dLine:", dLine, area, one_dict["text_area"] target_focus_area = area target_text_area = one_dict["text_area"] focus_end = focus #end for try: # 识别焦点区域文字; target_text = None if list(target_text_area[0:4]) != list([-1,-1,-1,-1]): text_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png") image_util.saveCropPic(screen_path, text_img, target_text_area[0:4]) language = target_text_area[4] ocrType = target_text_area[5] if len(target_text_area) != 6 or target_text_area[6] == {}: target_text = self.OCR.getStr(text_img, language, ocrType) else: target_text = self.OCR.getStrWithImgProcess(text_img, target_text_area[6], language, ocrType) # target_text = self.OCR.getStr(text_img, language, ocrType) print u"getFocusArea,选中区域文字:",target_text # 返回; return target_text, target_text_area, target_focus_area except Exception, e: LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:OCR识别异常'+e) return None,None,None # 圆形边框; def getBorderCircleFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide): return None,None,None # 多边形边框; def getBorderPolygonFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide): return None,None,None ''' # 描述:查找焦点框(不区分形状),选中效果为焦点框背景色变化成其他颜色。focus_type=1,简述:区域着色 # 参数: # icon_path:焦点框被选中时,背景变色的效果图路径。 # screen_path:需要判断焦点框的电视画面截图。 # uiDict: # focus_direction: # 返回值: # # ''' def getColoringleFocusArea(self, icon_path, screen_path, uiDict, focus_direction): # 获取效果图的luv平均值; imgColoring = cv.imread(icon_path) luvColoring = self.CIELuv.getAverageLUV(imgColoring) # 遍历字典中所有区域截图,计算luv值; luvNoraml = None # 目标文本区域; target_text_area = None # 目标焦点区域; target_focus_area = None # 默认最大值; dLine = 10000 focus_end = "focus_end" for focus in uiDict: #正常图片; # print u'name=',focus one_dict = uiDict[focus] normalArea = one_dict["focus_area"] focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png") image_util.saveCropPic(screen_path, focus_img, normalArea) luvNoraml = self.CIELuv.getAverageLUV(cv.imread(focus_img)) if luvNoraml is None: continue # print u'normalArea=',normalArea # 计算相差; diff = self.CIELuv.getDiffUV(luvColoring, luvNoraml) if diff[2] < dLine: dLine = diff[2] target_focus_area = normalArea target_text_area = one_dict["text_area"] focus_end = focus try: # 识别焦点区域文字; target_text = None text_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png") if list(target_text_area[0:4]) != list([-1,-1,-1,-1]): image_util.saveCropPic(screen_path, text_img_path, target_text_area[0:4]) language = target_text_area[4] ocrType = target_text_area[5] if len(target_text_area) != 6 or target_text_area[6] == {}: target_text = self.OCR.getStr(text_img_path, language, ocrType) else: target_text = self.OCR.getStrWithImgProcess(text_img_path, target_text_area[6], language, ocrType) # target_text = self.OCR.getStr(text_img_path, language, ocrType) # print u"getFocusArea,选中区域文字:",target_text # 返回文本内容、文本区域、焦点区域; return target_text, target_text_area, target_focus_area except Exception, e: LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:OCR识别异常'+e) return None,None,None ''' # 描述:获取两区域的缩放率 # 参数: # normalArea:区域坐标值,如[0,0,100,100] # imgZoomin:图像对象,使用cv.imread('path')获取的数组; # isZoomin:缩放率是放大率,还是缩小率, True放大率,False缩小率. # # 返回值: # 返回浮点值,缩放率scale # 示例: # # 注意: # # ''' def getZoomScale(self, normalArea, imgZoomin, isZoomin = False): scale = 0.000 if imgZoomin is None: return scale if isZoomin == True: scale = float(imgZoomin.shape[1]) / (normalArea[2] - normalArea[0]) else: scale = float(normalArea[2] - normalArea[0]) / imgZoomin.shape[1] # 返回缩放率; return scale ''' # 描述:查找焦点框,选中焦点框的效果为放大后的焦点框,focus_type=2 # 参数: # icon_path: # screen_path: # uiDict: # focus_direction: # # 返回值: # # # 注意: # # # ''' def getZoomFocusArea(self, icon_path, screen_path, uiDict, focus_direction): # 获取放大的图像; imgZoomin = cv.imread(icon_path) # 获取第一个正常图标区域; normalArea = uiDict.values()[1]["focus_area"] # 计算放大率; zoominScale = self.getZoomScale(normalArea, imgZoomin, True) # 遍历所有的区域,计算接近值; # 放大的矩形区域; zoominArea = None # 目标文本区域; target_text_area = None # 目标焦点区域; target_focus_area = None # 默认最大值; dLine = 10000 focus_end = "focus_end" # 矩形中心坐标点; cx,cy = None, None # 矩形宽高; width, height = None, None for focus in uiDict: #正常图片; # print u'name=',focus one_dict = uiDict[focus] normalArea = one_dict["focus_area"] focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png") image_util.saveCropPic(screen_path, focus_img, normalArea) img_luv = self.CIELuv.getAverageLUV(cv.imread(focus_img)) if img_luv is None: continue # print u'normalArea=',normalArea # 中心点位置; cx,cy = normalArea[0]+(normalArea[2] - normalArea[0])/2, normalArea[1]+(normalArea[3] - normalArea[1])/2 # print u'中心点位置',cx,cy # 宽高; width, height = (normalArea[2] - normalArea[0])/2, (normalArea[3] - normalArea[1])/2 # print u'宽,高',width, height # 计算放大的区域值; zoominArea = [int(cx-width*zoominScale), int(cy-height*zoominScale), int(cx+width*zoominScale), int(cy+height*zoominScale)] # print u'zoomin=',zoominArea #保存放大区域截图; focus_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_zoomin.png") image_util.saveCropPic(screen_path, focus_img_path, zoominArea) focus_img = cv.imread(focus_img_path) if focus_img is None: # print u'focus_img is None,路径:',focus_img_path LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:focus_img is None,路径:'+focus_img_path) continue imgZoomin_luv = self.CIELuv.getAverageLUV(focus_img) # 计算相差; diff = self.CIELuv.getDiffUV(imgZoomin_luv, img_luv) if diff[2] < dLine: dLine = diff[2] target_focus_area = normalArea target_text_area = one_dict["text_area"] focus_end = focus try: # 识别焦点区域文字; target_text = None text_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png") if list(target_text_area[0:4]) != list([-1,-1,-1,-1]): image_util.saveCropPic(screen_path, text_img_path, target_text_area[0:4]) language = target_text_area[4] ocrType = target_text_area[5] if len(target_text_area) != 6 or target_text_area[6] == {}: target_text = self.OCR.getStr(text_img_path, language, ocrType) else: target_text = self.OCR.getStrWithImgProcess(text_img_path, target_text_area[6], language, ocrType) # target_text = self.OCR.getStr(text_img_path, language, ocrType) # print u"getFocusArea,选中区域文字:",target_text # 返回文本内容、文本区域、焦点区域; return target_text, target_text_area, target_focus_area except Exception, e: # print u'except',e LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:OCR识别异常'+e) return None,None,None ''' # 描述:查找矩形焦点框,选中焦点框的效果为焦点背景色变成纯色。focus_type=3。 # 参数: # dict: # # 返回值: # ''' def autoRecognitionFocusArea(self, dict, percent = 0.9): if dict is None: print u'autoRecognitionFocusArea:dict is None' return None, None, None # 路径; imgPath = dict["shootPicPath"] zoneArea = dict["zone_area"] refFocusArea = dict["focus_area"] refTextArea = list(dict["text_area"]) colorArea = dict["color_area"] imgHome = cv.imread(imgPath) if imgHome is None: print u'autoRecognitionFocusArea:imgHome is None' return None, None, None # colorArea中心点,用于取色; cx,cy = colorArea[0] + int(colorArea[2] - colorArea[0])/2, colorArea[1] + int(colorArea[3] - colorArea[1])/2 # 背景色; # bgColor = imgHome[cy][cx] # call return self.__autoRecognitionFocusArea( dict["currentPicPath"], imgHome[cy][cx], zoneArea, refFocusArea, refTextArea, percent ) ''' # 描述: # 参数: # imgPath:要检测焦点框的图片路径。如:"D:\\Home.png" # focusBGColor:选中的焦点框的着色值,bgr格式。如:[122,122,122] # zoneArea:焦点框所在区域范围坐标,坐标点相对于imgPath图片。如(x1,y1,x2,y2):[10,10,50,50] # focusWidth:选中的焦点框宽度。 # focusHeight:选中的焦点框高度。 # textArea:文本区域在焦点框中的坐标。 # language:语言 # orcType: # # 返回值: # # ''' def __autoRecognitionFocusArea(self, imgPath, focusBGColor, zoneArea, refFocusArea, refTextArea, percent = 0.9): imgHome = cv.imread(imgPath) # 水平、垂直 hLine1, hLine2 = [],[] # 宽、高; focusWidth, focusHeight = refFocusArea[2]-refFocusArea[0],refFocusArea[3]-refFocusArea[1] # 是否找到 bhLine1, bhLine2 = False,False if imgHome is None: # print u'__autoRecognitionFocusArea:imgHome is None' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空,可能路径不正确') return None, None, None else: # 第一行; for y in range(zoneArea[1], zoneArea[3]): for x in range(zoneArea[0], zoneArea[2]): if bhLine1 == False: # if list(imgHome[y][x]) == list(focusBGColor): if self.CIELuv.isColorSimilar(imgHome[y][x],focusBGColor) == True: hLine1.append([x,y]) #end if #end for # 判断本线是否符合要求; if bhLine1 == False: count = len(hLine1) if float(count)/focusWidth > percent: bhLine1 = True else: hLine1 = [] #end for if len(hLine1) == 0: # print u'__autoRecognitionFocusArea: 未找到第一行,长度为0' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:未找到第一行,长度为0') return None, None, None #最后一行,倒查; zoneArea = [hLine1[0][0]-10, hLine1[0][1], hLine1[0][0] + focusWidth, hLine1[0][1] + focusHeight] print u'zoneArea=',zoneArea for y in range(zoneArea[3], zoneArea[1],-1): for x in range(zoneArea[0], zoneArea[2]): if bhLine2 == False: # if list(imgHome[y][x]) == list(focusBGColor): if self.CIELuv.isColorSimilar(imgHome[y][x],focusBGColor) == True: hLine2.append([x,y]) #end if #end for # 判断本线是否符合要求; if bhLine2 == False: count = len(hLine2) if float(count)/focusWidth > percent: bhLine2 = True else: hLine2 = [] #end for if len(hLine2) == 0: # print u'__autoRecognitionFocusArea: 未找到最后一行,长度为0' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:未找到最后一行,长度为0') return None, None, None # 焦点区域; focusArea = [hLine1[0][0], hLine1[0][1], hLine2[-1][0], hLine2[-1][1]] # 查找文本区域; realTextArea = self.findCurrentFocusTextBox(focusArea, refFocusArea, refTextArea[0:4]) if realTextArea is None: return None,None,focusArea # ocr识别文本; target_text = self.getCurrentFocusText(imgPath, realTextArea, refTextArea[4:]) # 返回结果; return target_text, realTextArea, focusArea ''' Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,同一场景下梯度值越高,图像越清晰。 以下是具体实现,这里衡量的指标是经过Sobel算子处理后的图像的平均灰度值,值越大,代表图像越清晰。 :param 图片路径 :return float, 值越大,代表清晰度越高 ''' def detSharpTenengrad(self, pic_path): return self.PQ.detSharpTenengrad(pic_path) ''' 采用Laplacian梯度方法检测清晰度 :param 图片路径 :return float, 值越大,代表清晰度越高 ''' def detSharpLaplacian(self, pic_path): return self.PQ.detSharpLaplacian(pic_path) ''' img:opencv图像对象,BGR颜色空间 ''' def detImgSharpLaplacian(self, img): return self.PQ.detImgSharpLaplacian(img) ''' 采用方差(Variance)方法检测清晰度。 方差是概率论中用来考察一组离散数据和其期望(即数据的均值)之间的离散(偏离)成都的度量方法。 方差较大,表示这一组数据之间的偏差就较大,组内的数据有的较大,有的较小,分布不均衡; 方差较小,表示这一组数据之间的偏差较小,组内的数据之间分布平均,大小相近。 :param 图片路径 :return float, 值越大,代表清晰度越高 ''' def detSharpVariance(self, pic_path): return self.PQ.detSharpVariance(pic_path) ''' # 描述:获取满足指定周长、面积范围的灰度二值化轮廓; # 参数: # bInside:使用内轮廓 # tvShotPath:图片路径 # cropArea:裁剪区域, 如:[left,top,right,bottom] # grayVal:灰度值; # maxGrayVal:最大灰度值 # minPeri: 最小周长; # maxPeri: 最大周长; # minArea: 最小面积; # maxArea: 最大面积; # find_1st: 是否只返回满足条件的第一个值 # # 返回:返回满足指定周长、面积范围的一个或多个轮廓坐标,坐标值如:[left,top,right,bottom]; # # ''' def getGrayBinaryContour(self, tvShotPath, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True): contourRect = [0,0,0,0]#结果:x1,y1,x2,y2; img_binary = self.getBinaryImage(tvShotPath, cropArea, grayVal, maxGrayVal) # 获取二值化图像的轮廓; area, perimeter = 0.0, 0.0 if bInside == False:#外轮廓 contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1] else:#内轮廓 contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1] # 是否查找最大轮廓; bFindMaxContour = False if minPeri == maxPeri and minArea == maxArea: bFindMaxContour = True listBox = [] maxContour = None #最大轮廓; tempArea = 0 # 过滤掉不符合要求的轮廓; x, y, w, h,result = 0,0,0,0,False for cnt in contours: # 面积; area = cv.contourArea(cnt) # 周长; perimeter = cv.arcLength(cnt, True) if bFindMaxContour == True: # 获取最大轮廓; if tempArea < area: tempArea = area maxContour = cnt else: # print area,perimeter # 获取满足条件的值; if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri: # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高; x, y, w, h = cv.boundingRect(cnt) # print u'boundingRect=',x,y,w,h LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h))) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] contourRect = [x,y,x+w,y+h] # 如果只找第一个; if find_1st == True: result = True break # 多个; listBox.append(contourRect) #endif #endfor if bFindMaxContour == True: # 最大轮廓的矩形坐标; x, y, w, h = cv.boundingRect(maxContour) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] contourRect = [x,y,x+w,y+h] if bFindMaxContour == True: return contourRect # 只返回满足条件的第一个值; if find_1st == True: return contourRect if result == True else None else: return listBox #end # 区域=焦点=图片; def isNoneFocustArea(self,dict): # 解析参数; Coordinate = dict["ZoneCoordinate"]# 区域坐标; if len(dict["FocusList"]) == 0: return False FocustArea = dict["FocusList"][0]["focus_area"] if list(Coordinate) != list(FocustArea): return False return True ''' # 描述:获取满足指定周长、面积范围的灰度二值化轮廓; # 参数: # bInside:使用内轮廓 # tvShotPath:图片路径 # cropArea:裁剪区域, 如:[left,top,right,bottom] # maxGrayVal:最大灰度值 # minPeri: 最小周长; # maxPeri: 最大周长; # minArea: 最小面积; # maxArea: 最大面积; # find_1st: 是否只返回满足条件的第一个值 # # 注意:当minPeri == maxPeri and minArea == maxArea时,只求最大的轮廓坐标; # # 返回:返回满足指定周长、面积范围的一个或多个轮廓坐标,坐标值如:[left,top,right,bottom]; # # ''' def getOTSUBinaryContour(self, tvShotPath, cropArea, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True): contourRect = [0,0,0,0]#结果:x1,y1,x2,y2; img_binary = self.getBinaryImage(tvShotPath, cropArea, 0, maxGrayVal, thresholdType=1) # 获取二值化图像的轮廓; area, perimeter = 0.0, 0.0 if bInside == False:#外轮廓 contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1] else:#内轮廓 contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1] # 是否查找最大轮廓; bFindMaxContour = False if minPeri == maxPeri and minArea == maxArea: bFindMaxContour = True listBox = [] maxContour = None #最大轮廓; tempArea = 0 # 过滤掉不符合要求的轮廓; x, y, w, h,result = 0,0,0,0,False for cnt in contours: # 面积; area = cv.contourArea(cnt) # 周长; perimeter = cv.arcLength(cnt, True) if bFindMaxContour == True: # 获取最大轮廓; if tempArea < area: tempArea = area maxContour = cnt else: # print area,perimeter # 获取满足条件的值; if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri: # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高; x, y, w, h = cv.boundingRect(cnt) # print u'boundingRect=',x,y,w,h LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h))) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] contourRect = [x,y,x+w,y+h] # 如果只找第一个; if find_1st == True: result = True break # 多个; listBox.append(contourRect) #endif #endfor if bFindMaxContour == True: # 最大轮廓的矩形坐标; x, y, w, h = cv.boundingRect(maxContour) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] contourRect = [x,y,x+w,y+h] if bFindMaxContour == True: return contourRect # 只返回满足条件的第一个值; if find_1st == True: return contourRect if result == True else None else: return listBox #end ''' # focus_type = 4; # dict:区域信息; # bInside:是否使用内轮廓; ''' def getSelectFocusArea(self, dict): if self.isNoneFocustArea(dict) == True: return None,None,dict["ZoneCoordinate"] # 获取轮廓; try: bInside = dict["InsideContours"]# 是否使用内轮廓; except: bInside = False contourRect = self.getGrayBinaryContour( dict["TVShotPath"], dict["ZoneCoordinate"], dict["ZoneThreshold"], dict["ZoneMaxThreshold"], bInside, dict["RenderingsMinGirth"], dict["RenderingsMaxGirth"], dict["RenderingsMinArea"], dict["RenderingsMaxArea"]) if contourRect == None: # print u'没有找到轮廓区域' LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK-ERROR:没有找到轮廓区域') return None,None,None #endif # 遍历区域,找到内包含的区域; x1,y1,x2,y2,result = 0,0,0,0,False allFocus = dict["FocusList"] for focus in allFocus: x1,y1,x2,y2 = focus["focus_area"][0],focus["focus_area"][1],focus["focus_area"][2],focus["focus_area"][3] #是否在效果图的轮廓内 if x1 >= contourRect[0] and y1 >= contourRect[1] and x2 <= contourRect[2] and y2 <= contourRect[3]: result = True break #endfor if result == False: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:已找到焦点轮廓,但该轮廓未包含任何focus_area在内') return None,None,None # 获取当前焦点文本; target_text = self.getCurrentFocusText(dict["TVShotPath"], focus["text_area"][0:4], focus["text_area"][4:]) return target_text, focus["text_area"][0:4], focus["focus_area"] #end # 是否进入到指定UI界面; def isEntryUI(self, dict): try: bInside = dict["InsideContours"]# 是否使用内轮廓; except: bInside = False contourRect = self.getGrayBinaryContour( dict["TVShotPath"], dict["ZoneCoordinate"], dict["ZoneThreshold"], dict["ZoneMaxThreshold"], bInside, dict["RenderingsMinGirth"], dict["RenderingsMaxGirth"], dict["RenderingsMinArea"], dict["RenderingsMaxArea"]) # contourRect = self.getGrayBinaryContour(dict) if contourRect == None: # print u'isEntryUI:没有找到轮廓区域' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域') return False #endif return True #end ''' # 描述:获取模板匹配度最高的目标。 # 参数: # imgObj:图像对象,由cv2.imread()读取 # templObj:图库对象,由cv2.imread()读取 # 图像对象可以是RGB格式,也可以是GRAY格式,也可以是二值化后的图像对象; ''' def templMatch(self, imgObj, templObj, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }): # 参数判断; if imgObj is None or templObj is None: # print u'图像对象空' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:图像对象空') return None,None if setting["colorType"] == 1: # gray模式; imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY) templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY) elif setting["colorType"] == 2: # threshold模式; # 转成灰阶图; imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY) templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY) # 将灰阶转成二值化图像; imgObj = cv.threshold(imgObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1] templObj = cv.threshold(templObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1] # 模板匹配; retVal = cv.matchTemplate(imgObj, templObj, setting["method"]) # 最小最大值; min_val, max_val, min_loc, max_loc = cv.minMaxLoc(retVal) if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF: return min_val, min_loc else: return max_val, max_loc ''' # 描述:模板有多个匹配目标 # imgObj:图像对象,由cv2.imread()读取 # templObj:图库对象,由cv2.imread()读取 # 图像对象可以是RGB格式,也可以是GRAY格式,也可以是二值化后的图像对象; # ''' def templMultiMatch(self, imgObj, templObj, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }): # 参数判断; if imgObj is None or templObj is None: # print u'图像对象空' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:图像对象空') return None w,h = templObj.shape[::-1] if setting["colorType"] == 1: # gray模式; imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY) templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY) elif setting["colorType"] == 2: # threshold模式; # 转成灰阶图; imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY) templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY) # 将灰阶转成二值化图像; imgObj = cv.threshold(imgObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1] templObj = cv.threshold(templObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1] listVal = [] # 模板匹配; retVal = cv.matchTemplate(imgObj, templObj, setting["method"]) # 匹配的阀值 if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF: matchVal = 1 - setting["matchVal"] else: matchVal = setting["matchVal"] loc = np.where(retVal > matchVal) # 如果匹配的多个目标是横排序的,过滤掉重影框; listTup = sorted(zip(*loc[::-1])) # 第一个矩形; x1,y1 = listTup[-1][0]-10, listTup[-1][1]-10 x2,y2 = x1+w+20, y1+h+20 # 中心点是否在矩形内; bInRect = False for i in range(len(listTup)-1, -1, -1): pt = (listTup[i][0],listTup[i][1]) x,y = pt[0]+w/2,pt[1]+h/2 if x > x1 and x < x2 and y > y1 and y < y2: if bInRect == False: bInRect = True else: listTup.pop(i) else: # print u'外坐标:',pt x1,y1 = pt[0]-10,pt[1]-10 x2,y2 = x1+w+10, y1+h+10 # 如果匹配的多个目标是坚排序的,过滤掉重影框; bInRect = False listTup = sorted(listTup, key=lambda x:x[1]) # 第一个矩形; x1,y1 = listTup[0][0]-10, listTup[0][1]-10 x2,y2 = x1+w+20, y1+h+20 for pt in listTup: x,y = pt[0]+w/2,pt[1]+h/2 if x > x1 and x < x2 and y > y1 and y < y2: if bInRect == False: bInRect = True listVal.append([pt[0],pt[1],pt[0]+w,pt[1]+h]) else: # 中心点不在矩形内,切换矩形; x1,y1 = pt[0]-10,pt[1]-10 x2,y2 = x1+w+10, y1+h+10 listVal.append([pt[0],pt[1],pt[0]+w,pt[1]+h]) # 返回多个目标的坐标值; return listVal ''' # 获取图库目录图像; # dir:图库路径 # extlist:保留的后缀文件,都是图像格式。 ''' def getGallery(self, dir, extlist = ['.png','.bmp','.jpg','jpeg','.tiff']): tmplist = [] dir = unicode(dir) if os.path.exists(unicode(dir)) == True: # 读取图库路径里的所有图片; list = os.listdir(dir) # 后缀、路径; path, ext = "", "" # 遍历,过滤掉文件夹和非图像文件; for file in list: path = os.path.join(dir, file) if os.path.isfile(path): ext = os.path.splitext(path)[1] # 获取文件后缀 [0]获取的是除了文件名以外的内容 if ext in extlist: tmplist.append(path) #end for else: # print u'路径异常',dir LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:路径异常') return tmplist ''' # 描述:模板匹配,只返回图库中匹配值符合的一个或多个模板的相关数据。 # srceenImgPath:原图路径,即TV截图; # ZoneCoordinate:要匹配的区域 # galleryDir: 图库路径; # setting: 设定参数 # method -> [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED] # colorType:默认为0,表示使用RGB色彩类型, 1=使用灰度图, 2=表示二值化图像。 # thresholdVal、thresholdMaxVal:当colorType=2时,才使用。 # matchVal:tmplVal比较值。 # # 返回值: ''' def matchImage(self, srceenImgPath, ZoneCoordinate, galleryDir, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }): # 判断文件是否存在; tmplist = self.getGallery(galleryDir) if len(tmplist) == 0: # print u'图库目录空' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:图库目录空') return None srceenImgPath = unicode(srceenImgPath) if os.path.exists(srceenImgPath) == True: # 加载原图; img_src = cv.imread(srceenImgPath.encode("gb18030")) if img_src is None: # print u'读取原图失败' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:读取原图失败') return None # 判断匹配区域是否整图,不是截图进行匹配; bUseZoneImg = False # sw,sh = img_src.shape[1],img_src.shape[0]#原图长宽; if ZoneCoordinate is not None and (ZoneCoordinate[2] - ZoneCoordinate[0] != img_src.shape[1] or ZoneCoordinate[3] - ZoneCoordinate[1] != img_src.shape[0]): bUseZoneImg = True zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png") image_util.saveCropPic(srceenImgPath,zoneImgPath,ZoneCoordinate) img_src = cv.imread(zoneImgPath.encode("gb18030")) if img_src is None: # print u'截图失败' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:截图失败') return None if setting["colorType"] == 1: # gray模式; img_src = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY) elif setting["colorType"] == 2: # threshold模式; img_src = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY) # 将灰阶转成二值化图像; img_src = cv.threshold(img_src, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1] #加载要搜索的图像模板; w,h = 0, 0 img_tmp = None tmpVal, tmpLoc = 0,[0,0] # listResult = [] result = {"tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]} # 遍历图库,找到最合适的 for file in tmplist: # result = {"result": False, "tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]} # result = {"tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]} img_tmp = cv.imread(file.encode('gb18030')) if img_tmp is None: # print u'模板图库相片加载失败' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:模板图库相片加载失败') continue if setting["colorType"] == 1: # gray模式; img_tmp = cv.cvtColor(img_tmp, cv.COLOR_BGR2GRAY) elif setting["colorType"] == 2: # threshold模式; img_tmp = cv.cvtColor(img_tmp, cv.COLOR_BGR2GRAY) # 将灰阶转成二值化图像; img_tmp = cv.threshold(img_tmp, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1] # 模板匹配; w,h = img_tmp.shape[1], img_tmp.shape[0] tmpVal, tmpLoc = self.templMatch(img_src, img_tmp, {'method':setting["method"], 'colorType':0 }) if bUseZoneImg == True: x,y = tmpLoc[0] + ZoneCoordinate[0],tmpLoc[1] + ZoneCoordinate[1] else: x,y = tmpLoc[0], tmpLoc[1] if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF: # if result["tmpVal"] > tmpVal: if tmpVal < (1-setting["matchVal"]): if result["tmpVal"] < 1-tmpVal: result["tmpVal"] = 1-tmpVal result["coordinate"] = [x,y,x+w,y+h] result["galleryFile"] = file # break else: # if result["tmpVal"] < tmpVal: if tmpVal > setting["matchVal"]: if result["tmpVal"] < tmpVal: result["tmpVal"] = tmpVal result["coordinate"] = [x,y,x+w,y+h] result["galleryFile"] = file # break # if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF: # if result["tmpVal"] < (1 - setting["matchVal"]): # listResult.append(result) # else: # if result["tmpVal"] > setting["matchVal"]: # listResult.append(result) # print result["result"], result["tmpVal"],result["coordinate"],result["galleryFile"].decode('gb18030'), #end for if result["tmpVal"] == 0: LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:模板匹配度为0') return None return result # return listResult else: # print u'文件不存在' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:文件不存在') return None ''' # 描述:单一模板匹配; # 参数: # srceenImgPath:原图路径,即TV截图; # ZoneCoordinate:要匹配的区域 # templImgPath:单模板文件路径 # setting:设定值 # 注意:templ对象,在screenImg中可能存在多个匹配度符合要求的区域。 ''' def matchSingleImage(self, srceenImgPath, ZoneCoordinate, templImgPath, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }): srceenImgPath = unicode(srceenImgPath) templImgPath = unicode(templImgPath) if os.path.exists(srceenImgPath) == True and os.path.exists(templImgPath) == True: # 加载原图; srceenImg = cv.imread(srceenImgPath.encode("gb18030")) if srceenImg is None: # print u'读取原图失败' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:读取原图失败') return None # 判断匹配区域是否整图,不是截图进行匹配; bUseZoneImg = False # sw,sh = srceenImg.shape[1],srceenImg.shape[0]#原图长宽; if ZoneCoordinate is not None and (ZoneCoordinate[2] - ZoneCoordinate[0] != srceenImg.shape[1] or ZoneCoordinate[3] - ZoneCoordinate[1] != srceenImg.shape[0]): bUseZoneImg = True zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png") image_util.saveCropPic(srceenImgPath,zoneImgPath,ZoneCoordinate) srceenImg = cv.imread(zoneImgPath.encode("gb18030")) if srceenImg is None: # print u'截图失败' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:截图失败') return None if ZoneCoordinate is None or ZoneCoordinate is []: ZoneCoordinate = [0, 0, srceenImg.shape[1], srceenImg.shape[0]] #加载图像模板; w,h = 0, 0 img_tmp = None tmpVal, tmpLoc = 0,[0,0] result = {"tmpVal":0, "coordinate":[0,0,0,0]} # 对比图库 img_tmp = cv.imread(templImgPath.encode('gb18030')) if img_tmp is None: # print u'模板图库相片加载失败' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:模板图库相片加载失败') return None # 模板匹配; w,h = img_tmp.shape[1], img_tmp.shape[0] tmpVal, tmpLoc = self.templMatch(srceenImg, img_tmp, setting) result["tmpVal"] = tmpVal if bUseZoneImg == True: x,y = tmpLoc[0] + ZoneCoordinate[0],tmpLoc[1] + ZoneCoordinate[1] result["coordinate"] = [x,y,x+w,y+h] else: result["coordinate"] = [tmpLoc[0],tmpLoc[1],tmpLoc[0]+w,tmpLoc[1]+h] if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF: if result["tmpVal"] < (1-setting["matchVal"]): return result else: if result["tmpVal"] > setting["matchVal"]: return result # print result["result"], result["tmpVal"] return None else: # print u'文件不存在' LoggingUtil.getDebugLogger().info( pyFileName, self.className, get_current_function_name(), 'SDK:文件不存在') return None ''' # 描述:获取矩形1在矩形2的方位; # 参数:box1,box2矩形。 # 返回值: # 左上:0 正上:1 右上:2 # 正左:3 正右:4 # 左下:5 正下:6 右下:7 # ''' def getOrientation(self, box1, box2): # 矩形长宽; w1,h1 = box1[2]-box1[0],box1[3]-box1[1] # w2,h2 = box2[2]-box2[0],box2[3]-box2[1] # 矩形中心点; cx1,cy1 = box1[0]+w1/2,box1[1]+h1/2 # cx2,cy2 = box2[0]+w2/2,box2[1]+h2/2 # box1在box2右方; if box1[0] >= box2[2]: # 正右方; if cy1 > box2[1] and cy1 < box2[3]: return 4 # 右上方; if cy1 < box2[1]: return 2 # 右下方; if cy1 > box2[3]: return 7 # box1在box2下方; if box1[1] >= box2[3]: # 正下方; if cx1 > box2[0] and cx1 < box2[2]: return 6 # 左下方; if cx1 < box2[0]: return 5 # 右下方; if cx1 > box2[2]: return 7 # box1在box2左方; if box1[2] <= box2[0]: # 正左方; if cy1 > box2[1] and cy1 < box2[3]: return 3 # 左上方; if cy1 < box2[1]: return 0 # 左下方; if cy1 > box2[3]: return 5 # box1在box上方; if box1[3] <= box2[1]: # 正上方; if cx1 > box2[0] and cx1 < box2[2]: return 1 # 左上方; if cx1 < box2[0]: return 0 # 右上方; if cx1 > box2[2]: return 2 # box1在box2里面; return -1 ''' # 描述:由两矩形的中心点来判别方位; # # # ''' def getOrientationEx(self, box1, box2): # 矩形长宽; w1,h1 = box1[2]-box1[0],box1[3]-box1[1] # w2,h2 = box2[2]-box2[0],box2[3]-box2[1] # 矩形中心点; cx1,cy1 = box1[0]+w1/2,box1[1]+h1/2 # cx2,cy2 = box2[0]+w2/2,box2[1]+h2/2 # box1在box2右方; if cx1 > box2[2]: # 正右方; if cy1 > box2[1] and cy1 < box2[3]: return 4 # 右上方; if cy1 < box2[1]: return 2 # 右下方; if cy1 > box2[3]: return 7 # box1在box2下方; if cy1 > box2[3]: # 正下方; if cx1 > box2[0] and cx1 < box2[2]: return 6 # 左下方; if cx1 < box2[0]: return 5 # 右下方; if cx1 > box2[2]: return 7 # box1在box2左方; if cx1 < box2[0]: # 正左方; if cy1 > box2[1] and cy1 < box2[3]: return 3 # 左上方; if cy1 < box2[1]: return 0 # 左下方; if cy1 > box2[3]: return 5 # box1在box上方; if cy1 < box2[1]: # 正上方; if cx1 > box2[0] and cx1 < box2[2]: return 1 # 左上方; if cx1 < box2[0]: return 0 # 右上方; if cx1 > box2[2]: return 2 # box1在box2里面; return -1 ''' # 描述:生成网络数据。 # 参数:一组矩形坐标。 # 返回值:分组的矩形坐标; # 注意: # 给定的一组矩形,如果不是继续的一组网格矩形,无法判断缺少的矩形位置. # # ''' def generateGrid(self,listRects): ret = 0 # 1、先上下排序,分层级; for i in range(len(listRects)-1): for j in range(0, len(listRects)-i-1): ret = self.getOrientation(listRects[j], listRects[j+1]) if ret in [5,6,7]: listRects[j], listRects[j+1] = listRects[j+1], listRects[j] # 2、层级分组; listLyaer = [] lastIndex = 0 for i in range(0,len(listRects)-1): ret = self.getOrientation(listRects[i],listRects[i+1]) if ret in (0,1,2): # 将同一行的所有网格添加为一个数组元素; listLyaer.append(listRects[lastIndex:i+1]) lastIndex = i+1 # 如果有最后一个,自成一组。 # if i < len(listRects)-1: if lastIndex < len(listRects): listLyaer.append(listRects[lastIndex::]) # 3、分组后,再每一组排序; for row in listLyaer: for i in range(len(row)-1): for j in range(0, len(row)-i-1): ret = self.getOrientation(row[j], row[j+1]) if ret == 4: row[j], row[j+1] = row[j+1], row[j] # 4、返回结果。 return listLyaer ''' # 描述:查找指定矩形在网格中的坐标; # 参数: # rect:要查找的盒子坐标; # listGrid:网格 # # 返回:行、列、是否边界(0:未到边界,1:右边界,-1:左边界;2:最后一格; -2:最前一格; 3:最后一格且该行只有一格) # # ''' def getGridCoordinate(self, rect, listGrid): result,boundary = False,0 try: grid,cell = None,None for row in range(0,len(listGrid)): grid = listGrid[row] for col in range(0,len(grid)): cell = grid[col] # 包含在内; if rect[0] <= cell[0] and rect[1] <= cell[1] and rect[2] >= cell[2] and rect[3] >= cell[3]: result = True if col == len(grid)-1: boundary = 1 #已到右边界; elif col == 0: boundary = -1 #左边界; break if result == True: break except Exception, e: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:循环异常'+str(e)) if result == True: # 再判断,是否是最后一格; if row == len(listGrid) - 1 and col == len(listGrid[len(listGrid) - 1]) - 1: boundary = 2 # 最后一格; if col == 0:#单格一行; boundary = 3 print u'最后一格:'.encode('gbk'),row,col,boundary # 判断是不是最前一格; if row == 0 and col == 0: boundary = -2 print u'最前一格:'.encode('gbk'),row,col,boundary return row,col,boundary else: return -1,-1,boundary ''' # 描述:顺(正)序查找目标焦点框 # 参数: # # # ''' def sequentialSearch(self, curRow, curBoundary, remoteCtrlTimes, listRect, listGrid, dict, findFunc): row,boundary,keyDirection = 0,0,True curText,curTextArea,curFocusArea = None,None,None ctrl_times = int((1 + 0.5)*remoteCtrlTimes + 2) if curBoundary == 1:#右边界。 # 向下遥控; self.redRat3.sendKey('down') keyDirection = False # 向左遥控; # 截图; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if ocr_result == True: return True, curFocusArea result = False for i in range(ctrl_times): if keyDirection == True: #向右遥控; self.redRat3.sendKey('right') else: #向左遥控; self.redRat3.sendKey('left') # 按键后,获取当前焦点; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if ocr_result == True: result = True break # 不是目标焦点,继续遥控; row,col,boundary = self.getGridCoordinate(curFocusArea,listGrid) print u'curRow,curCol,curBoundary',row,col,boundary if boundary == 1 or boundary == -1: # 向下遥控; self.redRat3.sendKey('down') keyDirection = True if boundary == -1 else False # 向左或向右遥控; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if ocr_result == True: result = True break return result, curFocusArea ''' # 描述:反序查找目标焦点框; # 参数: # # # ''' def reverseSearch(self, curRow, curBoundary, remoteCtrlTimes, listRect, listGrid, dict, findFunc): row,boundary,keyDirection = 0,0,False curText,curTextArea,curFocusArea = None,None,None ctrl_times = int((1 + 0.5)*remoteCtrlTimes + 2) if curBoundary == -1:#左边界; # 向下遥控; self.redRat3.sendKey('up') keyDirection = True # 向右遥控; # 截图; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if ocr_result == True: return True, curFocusArea result = False for i in range(ctrl_times): if keyDirection == True: #向右遥控; self.redRat3.sendKey('right') else: #向左遥控; self.redRat3.sendKey('left') # 按键后,获取当前焦点; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if ocr_result == True: result = True break # 不是目标焦点,继续遥控; row,col,boundary = self.getGridCoordinate(curFocusArea,listGrid) print u'curRow,curCol,curBoundary',row,col,boundary if boundary == 1 or boundary == -1: # 向上遥控; self.redRat3.sendKey('up') keyDirection = False if boundary == 1 else True # 向左或向右遥控; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if ocr_result == True: result = True break return result, curFocusArea def SearchEx(self, listGrid, dict, findFunc, searchType = True): result = False # 按键方向:True->右;False->左; keyDirection = False # 上一行行号; lastRow = -1 # 遍历尾行的行号; endRow = 0 #下一行按键; nextRow = 'up' # 以上是逆序时的预设值; if searchType == True: # 正序查找; endRow = len(listGrid)-1 keyDirection = True nextRow = 'down' while True: # 获取当前坐标(x,y)及边界性; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = findFunc(dict) if curFocusArea is None: break if ocr_result == True: result = True break row, col, boundary = self.getGridCoordinate(curFocusArea, listGrid) # 判断是否遍历完最后一行; if lastRow == endRow: # 根据按键方向判断是否到达尾行尾格; if keyDirection and col == len(listGrid[row])-1: break if not keyDirection and col == 0: break # 记录当前行; lastRow = row if boundary == 0: # 没找到,遥控下一格; if keyDirection == True: self.redRat3.sendKey('right') else: self.redRat3.sendKey('left') elif boundary == 1:#最右 if keyDirection == True: keyDirection = not keyDirection self.redRat3.sendKey(nextRow) else: self.redRat3.sendKey('left') elif boundary == 2:#最后一行最后一格;(该行有多格) if searchType == True: break#结束遍历; else: if keyDirection == False: self.redRat3.sendKey('left') elif boundary == 3:#最后一行最后一格;(该行只有一格) if searchType == True: break#结束遍历; else: keyDirection = not keyDirection self.redRat3.sendKey(nextRow) elif boundary == -1:#最左; if keyDirection == False: keyDirection = not keyDirection self.redRat3.sendKey(nextRow) else: self.redRat3.sendKey('right') elif boundary == -2:#最前一格; if searchType == False: break#结束遍历; else: if keyDirection == True: self.redRat3.sendKey('right') return result ''' # 描述:到达网格的第一个盒子焦点; # # # # ''' def reach1stBox(self, listGrid, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True): result = True while True: # 当前焦点位置; curFocusBox = self.findCurrentFocusBoxInType04(cropArea,grayVal,maxGrayVal,bInside,minPeri,maxPeri,minArea,maxArea)[0] if curFocusBox is None: result = False break # 获取行列坐标标; row, col, boundary = self.getGridCoordinate(curFocusBox, listGrid) if row == 0 and col == 0:# and boundary == -1: result = True break if row > 0: self.redRat3.sendKey('up') elif row == 0 and boundary != -2: self.redRat3.sendKey('left', col, 0.5) return result ''' # 描述:查找当前焦点框信息; # # # # ''' def findCurrentFocusFrameInType6(self, dict): # 1、获取当前轮廓; try: bInside = dict["InsideContours"]# 是否使用内轮廓; except: bInside = False contourRect = self.getGrayBinaryContour( dict["TVShotPath"], dict["ZoneCoordinate"], dict["ZoneThreshold"], dict["ZoneMaxThreshold"], bInside, dict["RenderingsMinGirth"], dict["RenderingsMaxGirth"], dict["RenderingsMinArea"], dict["RenderingsMaxArea"]) if contourRect == None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域') return False,None #endif # 计算文本位置; realTextBox = self.findCurrentFocusTextBox(contourRect, dict["RenderingsArea"], dict["RenderingsTextArea"][0:4]) if realTextBox is None: return False,contourRect ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, realTextBox) result, realTextBox = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, dict['RenderingsTextArea'][4],dict['RenderingsTextArea'][5],dict['RenderingsTextArea'][6],dict['RenderingsTextArea'][7]) return result, contourRect ''' # 描述:查找目标焦点框之type6. # 参数: # # 返回值:成功找到焦点返回True,否则返回False; # # ''' def findTargetFocusFrameInType6(self,dict): # 1、计算出几行几列; listRect = [] for focus in dict["FocusList"]: listRect.append(focus["focus_area"]) # 生成网格; listGrid = self.generateGrid(listRect) # 正反序查找; result = self.SearchEx(listGrid, dict, self.findCurrentFocusFrameInType6, True) if result == False: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:逆序查找') result = self.SearchEx(listGrid, dict, self.findCurrentFocusFrameInType6, False) return result #end ''' # 描述:图像形态学处理。 # # # ''' def preProcess(self, grayImage,setting): # 1、sobel算子,x方向求梯度; sobel = cv.Sobel(grayImage, cv.CV_8U, 1, 0, ksize = 3) # 2、二值化; if 'gray' in setting: binary = cv.threshold(sobel, setting['gray'], 255, cv.THRESH_BINARY+cv.THRESH_OTSU)[1] else: binary = cv.threshold(sobel, 0, 255, cv.THRESH_BINARY+cv.THRESH_OTSU)[1] binary_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(binary_path, binary) # 第一次; first,second,third = None,None,None ksize = (1,1) ksize = tuple(setting["first"][1:3]) kernel = cv.getStructuringElement(setting["first"][3], ksize) if setting["first"][0] == "dilate": first = cv.dilate(binary, kernel, setting["first"][4]) else: first = cv.erode(binary, kernel, setting["first"][4]) # 第二次; second = None if 'second' in setting and len(setting["second"]) != 0: ksize = tuple(setting["second"][1:3]) kernel = cv.getStructuringElement(setting["second"][3], ksize) if setting["second"][0] == "dilate": second = cv.dilate(first, kernel, setting["second"][4]) else: second = cv.erode(first, kernel, setting["second"][4]) else: second = first # 第三次; third = None if "third" in setting and len(setting["third"]) != 0: ksize = tuple(setting["third"][1:3]) kernel = cv.getStructuringElement(setting["third"][3], ksize) if setting["third"][0] == "dilate": third = cv.dilate(second, kernel, setting["third"][4]) else: third = cv.erode(second, kernel, setting["third"][4]) else: third = second # 7. 存储中间图片 tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tmpdir, binary) tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"first_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tmpdir, first) if second is not None: tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"second_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tmpdir, second) if third is not None: tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"third_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tmpdir, third) return third if third is not None else second ''' # 描述:查找文字区域 # # # # ''' def findTextRegion(self, img, minGirth, maxGirth, minArea, maxArea): # 1. 查找轮廓 contours = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)[1] if len(contours) == 0: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找文本到轮廓') return None # 2. 过滤 listCnt = [] maxBox, maxCnt = 0, None for i in range(len(contours)): cnt = contours[i] # 计算该轮廓的面积 area = cv.contourArea(cnt) perimeter = cv.arcLength(cnt, True) # 符合结果的,保存在list中; if area > minArea and area < maxArea and perimeter > minGirth and perimeter < maxGirth: listCnt.append(cnt) if len(listCnt) == 0: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到符合目标的轮廓') return None # 只保留面积最大的; for i in range(len(listCnt)): cnt = listCnt[i] # 计算该轮廓的面积 area = cv.contourArea(cnt) if(maxBox < area): maxBox = area maxCnt = cnt # 找到最小的矩形,该矩形可能有方向 rect = cv.minAreaRect(maxCnt) print ("rect is: ",rect) # box是四个点的坐标 box = cv.boxPoints(rect) box = np.int0(box) # print 'box',box if rect[2] > -45.0: rect = [box[1][0],box[1][1],box[3][0],box[3][1],] else:#-90 rect = [box[2][0],box[2][1],box[0][0],box[0][1],] return rect ''' # 描述:查找当前焦点的文本轮廓。 # 参数: # # # 返回值:返回文本内容。 # # ''' def findTextcontourRect(self, tvShotPath, dict): coordinate = dict["ZoneCoordinate"] thresholdVal = dict["ZoneThreshold"] maxThresholdVal = dict["ZoneMaxThreshold"] try: maxGirth = dict["RenderingsMaxGirth"]# 效果图周长上限;(最大值) minGirth = dict["RenderingsMinGirth"]# 效果图周长下限;(最小值) maxArea = dict["RenderingsMaxArea"]# 效果图面积上限;(最大值) minArea = dict["RenderingsMinArea"]# 效果图面积下限;(最小值) except: maxGirth = 1000000# 效果图周长上限;(最大值) minGirth = 10# 效果图周长下限;(最小值) maxArea = 1000000# 效果图面积上限;(最大值) minArea = 100# 效果图面积下限;(最小值) ocrtype = dict["ocrtype"] language = dict["language"] setting = dict["setting"] # 按区域截图,在区域中查找轮廓。 zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") image_util.saveCropPic(tvShotPath, zoneImgPath, coordinate) imgTV = cv.imread(zoneImgPath.encode("gb18030")) # 判断对象有效性; if imgTV is None: # print u'效果图对象空,可能文件路径无效' LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:效果图对象空,可能文件路径无效') return None,None # 将截图转成灰阶; imgGray = cv.cvtColor(imgTV, cv.COLOR_BGR2GRAY) # 高斯模糊; imgGray = cv.GaussianBlur(imgGray, (3, 3), 0) # 将灰阶转成二值化图像; thresh = cv.threshold(imgGray, thresholdVal, maxThresholdVal, cv.THRESH_BINARY)[1] tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"thresh_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tmpdir, thresh) # 图像形态学处理; dilation = self.preProcess(thresh,setting) # 查找和筛选文字区域 text_area = self.findTextRegion(dilation,minGirth,maxGirth,minArea,maxArea) if text_area is None: return None,None ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, text_area) ocr_result, ocr_text = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, language, ocrtype, dict['ocrImgInfo'],dict['ignoreChar']) return ocr_result, ocr_text ''' # 描述:查找当前焦点框之type7. # 参数: # # 返回: # # 注意:只适用于左右遥控按键. # # ''' def findCurrentFocusFrameInType7(self,dict): if 'setting' not in dict: dict["setting"] = { "first":["erode",2,2,0,1], # 第一次操作是腐蚀还是膨胀,内核大小,内核形状,操作次数 "second":["dilate",30,9,0,1], # 内核大小,内核形状,操作次数 "third":["dilate",24,10,0,2] # 内核大小,内核形状,操作次数 } # 先正序查找; result = False target_text,last_text = '','' sendTimes = dict["SendTimes"] changeTimes = -1 while sendTimes > 0: # 电视截图; TVShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(TVShotPath) # 查找文本轮廓 ocr_result, target_text = self.findTextcontourRect(TVShotPath, dict) if ocr_result == True: result = True break # 发送右按钮; self.redRat3.sendKey('right') sendTimes -= 1 if last_text != target_text: last_text = target_text changeTimes += 1 # 正序未找到,逆序查找; if result == False: #向左发送changeTimes+1次,回到原点; keys = ['left']*(changeTimes+1) self.redRat3.sendKeys(keys) sendTimes = dict["SendTimes"] while sendTimes > 0: # 电视截图; TVShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(TVShotPath) ocr_result, target_text = self.findTextcontourRect(TVShotPath, dict) if ocr_result == True: result = True break # 发送左按钮; self.redRat3.sendKey('left') sendTimes -= 1 return result def getHSVBinaryContour(self, tvShotPath, cropArea, hsvVal, maxHsvVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True): contourRect = [0,0,0,0]#结果:x1,y1,x2,y2; # 判断文件是否存在; if os.path.exists(tvShotPath) == False: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath) return None # 打开图片; img_tvShot = cv.imread(tvShotPath) if img_tvShot is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空') return None # 只保留区域部分; try: img_crop = np.array(img_tvShot[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]]) except: img_crop = img_tvShot cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "crop_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_crop) # 判断对象有效性; if img_crop is None: # print u'效果图对象空,可能文件路径无效' LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:区域截图对象空,可能文件路径无效') return None # 将截图转成灰阶; img_hsv = cv.cvtColor(img_crop, cv.COLOR_BGR2HSV) # 根据参数的阀值得到特定颜色区域; img_binary = cv.inRange(img_hsv, tuple(hsvVal),tuple(maxHsvVal)) # 图像形态学内核对象; kernerl = cv.getStructuringElement(cv.MORPH_RECT, ksize=(3,3)) # 开操作,去噪点; img_binary = cv.morphologyEx(img_binary,cv.MORPH_OPEN,kernerl) # 闭操作,连通域; img_binary = cv.morphologyEx(img_binary,cv.MORPH_CLOSE,kernerl) # 获取二值化图像的轮廓; area, perimeter = 0.0, 0.0 if bInside == False:#外轮廓 contours, hierarchy = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1:3] else:#内轮廓 contours, hierarchy = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1:3] listBox = [] # 过滤掉不符合要求的轮廓; x, y, w, h,result = 0,0,0,0,False for cnt in contours: # 面积; area = cv.contourArea(cnt) # 周长; perimeter = cv.arcLength(cnt, True) # 获取满足条件的值; if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri: # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高; x, y, w, h = cv.boundingRect(cnt) # print u'boundingRect=',x,y,w,h LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h))) # 判断矩形是否包含在区域内; # 在区域内截图判断; x = x + cropArea[0] y = y + cropArea[1] contourRect = [x,y,x+w,y+h] # 如果只找第一个; if find_1st == True: result = True break # 多个; listBox.append(contourRect) #endif #endfor # 只返回满足条件的第一个值; if find_1st == True: return contourRect if result == True else None else: return listBox #end def findCurrentFocusFrameInType8(self,dict): # 1、获取当前轮廓; try: bInside = dict["InsideContours"]# 是否使用内轮廓; except: bInside = False contourRect = self.getHSVBinaryContour( dict["TVShotPath"], dict["ZoneCoordinate"], dict["ZoneThreshold"], dict["ZoneMaxThreshold"], bInside, dict["RenderingsMinGirth"], dict["RenderingsMaxGirth"], dict["RenderingsMinArea"], dict["RenderingsMaxArea"]) if contourRect == None: # print u'没有找到轮廓区域' LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域') return False,None #endif # 计算文本位置; realTextBox = self.findCurrentFocusTextBox(contourRect, dict["RenderingsArea"], dict["RenderingsTextArea"][0:4]) if realTextBox is None: return False,contourRect # # 获取文本内容; ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, realTextBox) result, realTextBox = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, dict['RenderingsTextArea'][4],dict['RenderingsTextArea'][5],dict['RenderingsTextArea'][6],dict['RenderingsTextArea'][7]) return result, contourRect def findTargetFocusFrameInType8(self,dict): # 1、查找当前焦点框; dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(dict["TVShotPath"]) ocr_result, curFocusArea = self.findCurrentFocusFrameInType8(dict) if curFocusArea is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), u'SDK-Type8:当前焦点框空') return False result = False # 2、当前焦点是否目标焦点; if ocr_result == True: # curText, curTextArea, curFocusArea return True else: # 3、计算出几行几列; listRect = [] for focus in dict["FocusList"]: listRect.append(focus["focus_area"]) listGrid = self.generateGrid(listRect) # 4、当前焦点在哪个位置; curRow,curCol,curBoundary = self.getGridCoordinate(curFocusArea,listGrid) # 5、分正序查找和逆序查找两次查找。 keyDirection = True # True表示向右发送遥控,False表示向左发送遥控; # 将多维数组转一维; listRect = list(chain(*listGrid)) # 行数,列数; rows,cols = len(listGrid),len(listGrid[0]) # 剩余遥控次数; ctrl_times = len(listRect) - (curRow+1)*cols + curCol # 5.1、顺序查找; result, curFocusArea = self.sequentialSearch(curRow, curBoundary, ctrl_times, listRect, listGrid, dict, self.findCurrentFocusFrameInType8) # 5.2、逆序查找; if result == False: curRow,curCol,curBoundary = self.getGridCoordinate(curFocusArea,listGrid) ctrl_times = curRow*cols + curCol result, curFocusArea = self.reverseSearch(curRow, curBoundary, ctrl_times, listRect, listGrid, dict, self.findCurrentFocusFrameInType8) return result #end ''' # 描述:计算出两矩形的正包含的相交百分比。(以相交面积/最小矩形面积) # # 返回值:正包含的相交百分比 # ''' # 两矩形是否相交,返回相交百分比; def bbOverlap(self, box1, box2): # 外正四方; if box1[0] > box2[2]: return 0.0 if box1[1] > box2[3]: return 0.0 if box1[2] < box2[0]: return 0.0 if box1[3] < box2[1]: return 0.0 # 计算相交长宽; colInt = abs(min(box1[2], box2[2]) - max(box1[0], box2[0])) rowInt = abs(min(box1[3], box2[3]) - max(box1[1], box2[1])) # 计算相交面积 overlapArea = colInt * rowInt # 各自面积 area1 = (box1[2]-box1[0])*(box1[3]-box1[1]) area2 = (box2[2]-box2[0])*(box2[3]-box2[1]) # 是否全包含; # if overlapArea == area1 or overlapArea == area2: # return 1.0 # 返回相交比; # return float(overlapArea) / (area1 + area2 - overlapArea) return float(overlapArea) / min(area1, area2)#以最小面积的占用比,作为相交比,可计算出全包含。 ''' # 描述:返回目标焦点在当前选中焦点的哪个方向。 # 参数: # focusType:查找当前焦点的方式。 # focusDict:查找当前焦点的参数。 # matchDicts:查找目标焦点的模板匹配参数。 # 返回值: # # ''' def findTargetFocusDirection(self, focusType, focusDict, matchDict): # 查找当前焦点; curFocusText, curFocusTextArea, curFocusArea = None,None,None if focusType == 0: curFocusText, curFocusTextArea, curFocusArea = self.getBorderRectangleFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],focusDict["RenderingsBorderWide"],) elif focusType == 1: curFocusText, curFocusTextArea, curFocusArea = self.getColoringleFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],) elif focusType == 2: curFocusText, curFocusTextArea, curFocusArea = self.getZoomFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],) elif focusType == 3: curFocusText, curFocusTextArea, curFocusArea = self.autoRecognitionFocusArea(focusDict,) elif focusType == 4: curFocusText, curFocusTextArea, curFocusArea = self.getSelectFocusArea(focusDict,) elif focusType == 5: if focusDict["setting"] == None: curFocusArea = self.matchSingleImage(focusDict["TVShotPath"],focusDict["ZoneCoordinate"],focusDict["TemplatePath"]) else: curFocusArea = self.matchSingleImage(focusDict["TVShotPath"],focusDict["ZoneCoordinate"],focusDict["TemplatePath"],focusDict["setting"]) # 获取模板匹配结果,找到目标焦点区域坐标; if matchDict["setting"] == None: tagFocusArea = self.matchSingleImage(matchDict["TVShotPath"],matchDict["ZoneCoordinate"],matchDict["TemplatePath"]) else: tagFocusArea = self.matchSingleImage(matchDict["TVShotPath"],matchDict["ZoneCoordinate"],matchDict["TemplatePath"],matchDict["setting"]) # 计算两矩形是否相交,返回正包含相交比. overlap = self.bbOverlap(curFocusArea, tagFocusArea["coordinate"]) # 返回结果; return curFocusText, curFocusTextArea, curFocusArea, tagFocusArea["coordinate"], overlap ''' # 描述:焦点识别类型00:选中的焦点框边框外镶了一层有色边框,通过抓取这层边框颜色来识别焦点框。 # # # ''' def findCurrentFocusBoxInType00(self, dict): # 当前电视机截图; tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(tvShotPath) # 选中焦点框的效果图; effectPath = dict["EffectPath"] # 焦点所在区域; zoneBox = dict["ZoneCoordinate"] # 效果图外边框厚度; borderWidth = dict["BorderWidth"] # 获取选中焦点框效果图的外边框的luv平均值. effectImg = cv.imread(effectPath) effectLuv = cv.cvtColor(effectImg, cv.COLOR_BGR2Luv) # 将外边框拆分成4个矩形。 height,width = effectLuv.shape[0:2] leftBox = [0, 0, borderWidth, height] rightBox = [width - borderWidth, 0, width, height] topBox = [borderWidth, 0, width - borderWidth, borderWidth] bottomBox = [borderWidth, height - borderWidth, width - borderWidth, height] # luv平均值; avgluv = self.CIELuv.getMAverageLUV(effectLuv, [leftBox, rightBox, topBox, bottomBox]) ''' # 描述:焦点识别类型03:选中的焦点框背景色变成其他颜色,通过计算背景色获取上下两边直线来确定识别焦点框。 # # # ''' def findCurrentFocusBoxInType03(self, zoneBox, effectBox, effectColorBox, effectPath, matchVal): # 当前电视机截图; tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(tvShotPath) # 判断文件是否存在; if os.path.exists(tvShotPath) == False: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath) return None if os.path.exists(effectPath) == False: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+effectPath) return None # 判断区域是否在有效范围内; # 略过…… # 打开图片; img_tvShot = cv.imread(tvShotPath) img_effect = cv.imread(effectPath) if img_tvShot is None or img_effect is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空') return None # effectColorBox中心点,用于取色; cx,cy = effectColorBox[0] + int(effectColorBox[2] - effectColorBox[0])/2, effectColorBox[1] + int(effectColorBox[3] - effectColorBox[1])/2 focusBGColor = img_effect[cx,cy] # 效果图宽、高 effectWidth, effectHeight = effectBox[2]-effectBox[0],effectBox[3]-effectBox[1] # 水平、垂直 hLine1, hLine2 = [],[] # 是否找到 bhLine1, bhLine2 = False,False # 查找第一行与effectColor相近的颜色线条; for y in range(zoneBox[1], zoneBox[3]): for x in range(zoneBox[0], zoneBox[2]): if bhLine1 == False: if self.CIELuv.isColorSimilar(img_tvShot[y][x],focusBGColor) == True: hLine1.append([x,y]) #end if #end for # 判断本线是否符合要求; if bhLine1 == False: count = len(hLine1) if float(count)/effectWidth > matchVal: bhLine1 = True else: hLine1 = [] #end for if len(hLine1) == 0: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未找到第一行,长度为0') return None # 查找最后一行与effectColor相近的颜色线条; zoneBox = [hLine1[0][0]-10, hLine1[0][1], hLine1[0][0] + effectWidth, hLine1[0][1] + effectHeight] for y in range(zoneBox[3], zoneBox[1],-1): for x in range(zoneBox[0], zoneBox[2]): if bhLine2 == False: if self.CIELuv.isColorSimilar(img_tvShot[y][x],focusBGColor) == True: hLine2.append([x,y]) #end if #end for # 判断本线是否符合要求; if bhLine2 == False: count = len(hLine2) if float(count)/effectWidth > matchVal: bhLine2 = True else: hLine2 = [] #end for if len(hLine2) == 0: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未找到最后一行,长度为0') return None # 焦点区域; focusBox = [hLine1[0][0], hLine1[0][1], hLine2[-1][0], hLine2[-1][1]] # 返回结果; # return True,focusBox return focusBox ''' # 描述:焦点识别类型04:选中的焦点框背景色变成其他颜色,通过二值化获取轮廓面积、周长来识别焦点框。 # # # ''' def findCurrentFocusBoxInType04(self, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True): contourRect = [0,0,0,0]#结果:x1,y1,x2,y2; tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(tvShotPath) # 获取轮廓; contourRect = self.getGrayBinaryContour(tvShotPath, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea, find_1st) # 返回结果; if find_1st == True: return contourRect,tvShotPath else: return contourRect,tvShotPath ''' # 描述:焦点识别类型05:选中的焦点框明显与未选中时有差别,通过【多组图像的模板匹配】方式来识别焦点框。 # # # ''' def findCurrentFocusBoxInType05(self, zoneBox, focusDir): tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(tvShotPath) val = self.matchImage(tvShotPath, zoneBox, focusDir) if val is None: return None return val["coordinate"]#,val["galleryFile"] ''' # 描述:焦点识别类型06:选中的焦点框前景高亮着色,但背景是可变的,通过二值化获取大概轮廓,再膨胀腐蚀操作后得到最大轮廓来识别焦点框。 # # # ''' def findCurrentFocusBoxInType06(self, zoneBox, threshold, maxThreshold, minPeri = 10, maxPeri = 1000000, minArea = 100, maxArea = 1000000, setting = {"first":["erode",2,2,0,1], "second":["dilate",30,9,0,1],"third":["dilate",24,10,0,2]}): tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(tvShotPath) # 按区域截图,在区域中查找轮廓。 img_tvShot = cv.imread(tvShotPath) if img_tvShot is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径可能无效'+tvShotPath) return None img_zone = image_util.cutImage(img_tvShot, zoneBox) if img_zone is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:裁剪图片失败') return None # 将截图转成灰阶; imgGray = cv.cvtColor(img_zone, cv.COLOR_BGR2GRAY) # 高斯模糊; imgGray = cv.GaussianBlur(imgGray, (3, 3), 0) # 将灰阶转成二值化图像; thresh = cv.threshold(imgGray, threshold, maxThreshold, cv.THRESH_BINARY)[1] tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"thresh_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tmpdir, thresh) # 图像形态学处理; dilation = self.preProcess(thresh,setting) # 查找和筛选文字区域 text_area = self.findTextRegion(dilation,minPeri,maxPeri,minArea,maxArea) if text_area is None: return None return text_area ''' # 描述:焦点识别类型07:选中的焦点框前景色变成其他颜色,但是背景可变,通过计颜色阀值来计算出轮廓,再通过周长面积过滤后来调识别焦点框。 # # # ''' def findCurrentFocusBoxInType07(self, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea): tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") self.vp.takePicture(tvShotPath) return self.getHSVBinaryContour(tvShotPath,zoneBox,threshold,maxThreshold,bInside,minPeri,maxPeri,minArea,maxArea) # 在多区域中找当前焦点; def findCurrentFocusBox(self, listdict): if len(listdict) == 0: return None,None # 遍历区域,查找当前焦点所在区域; zType = -1 zData = [] zName = '' bInside = False # 当前焦点框坐标; focus_box = None for zone in listdict: zName = zone["zoneName"] zType = zone["zoneType"] zData = zone["zoneData"] # print '打印->'.encode('gbk'),zName.encode('gbk'),zType LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前遍历Zone:'+zName+',类型:'+str(zType)) try: bInside = zData["InsideContours"]# 是否使用内轮廓; except: bInside = False if zType == 3: focus_box = self.findCurrentFocusBoxInType03(zData["ZoneCoordinate"],zData["EffectArea"],zData["EffectColorBox"],zData["EffectPath"],zData["MatchVal"]) elif zType == 4: focus_box = self.findCurrentFocusBoxInType04( zData["ZoneCoordinate"], zData["ZoneThreshold"], zData["ZoneMaxThreshold"], bInside, zData["RenderingsMinGirth"], zData["RenderingsMaxGirth"], zData["RenderingsMinArea"], zData["RenderingsMaxArea"])[0] elif zType == 5: focus_box = self.findCurrentFocusBoxInType05( zData["ZoneCoordinate"], zData["focusPicDir"]) elif zType == 6: focus_box = self.findCurrentFocusBoxInType04( zData["ZoneCoordinate"], zData["ZoneThreshold"], zData["ZoneMaxThreshold"], bInside, zData["RenderingsMinGirth"], zData["RenderingsMaxGirth"], zData["RenderingsMinArea"], zData["RenderingsMaxArea"])[0] elif zType == 7: setting = None if 'setting' not in zone: setting = {"first":["erode",2,2,0,1], "second":["dilate",30,9,0,1],"third":["dilate",24,10,0,2]} else: setting = zone["setting"] focus_box = self.findCurrentFocusBoxInType06( zData["ZoneCoordinate"], zData["ZoneThreshold"], zData["ZoneMaxThreshold"], zData["RenderingsMinGirth"], zData["RenderingsMaxGirth"], zData["RenderingsMinArea"], zData["RenderingsMaxArea"], setting) elif zType == 8: focus_box = self.findCurrentFocusBoxInType07( zData["ZoneCoordinate"], zData["ZoneThreshold"], zData["ZoneMaxThreshold"], bInside, zData["RenderingsMinGirth"], zData["RenderingsMaxGirth"], zData["RenderingsMinArea"], zData["RenderingsMaxArea"]) else: # print u'未使用的焦点类型' LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未使用的焦点类型:'+str(zType)) # 找到当前焦点框; if focus_box is not None: break # end-for if focus_box is None: return None,None return focus_box,zName ''' # 描述:查找当前焦点文本框; # 参数: # 'realFocusBox':[x1,y1,x2,y2], #实际焦点框,坐标系必须是原图左上角; # 'refTextBox':[x1,y1,x2,y2], #参考文本框,坐标系必须是原图左上角; # 'refFocusBox':[x1,y1,x2,y2] #参考焦点框,坐标系必须是原图左上角; # 注意:x1,y1,x2,y2分别为矩形左上角(x1,y1)和右下角(x2,y2) # # 返回:当前焦点文本框坐标 # # ''' def findCurrentFocusTextBox(self, realFocusBox, refFocusBox, refTextBox): return self.getObjAbsoluteBox(realFocusBox, refFocusBox, refTextBox) ''' # 描述:根据参照物的相对坐标,来获取对象的绝对坐标值; # 参数: # 参照对象实际坐标 # 参照对象参照坐标 # 目标对象参照坐标 # # ''' def getObjAbsoluteBox(self, refObjRealBox, refObjRefBox, tagObjRefBox): if refObjRealBox is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:焦点框None,无法计算文本框坐标') return None # 目标区域的宽、高; txWidth, txHeight = tagObjRefBox[2]-tagObjRefBox[0], tagObjRefBox[3]-tagObjRefBox[1] # 以参照物作为新的坐标系,计算目标区域的相对原点(left,top) left,top = tagObjRefBox[0]-refObjRefBox[0], tagObjRefBox[1]-refObjRefBox[1] # 目标对象的绝对原点坐标; left += refObjRealBox[0] top += refObjRealBox[1] # 返回目标对象绝对坐标; return [left, top, left+txWidth, top+txHeight] ''' # 描述:获取焦点文本内容; # 参数: # img_or_path:可以是cv2.imread()对象,也可以是图片路径; # textBox:[x1,y1,x2,y2] # orcInfo: ['language',10001,{}] # language, ocrType, imgProcParams # ''' def getCurrentFocusText(self, img_or_path, textBox, orcInfo): if textBox is None or list(textBox) == list([-1,-1,-1,-1]): return None tx_img = None # 保存截图; if type(img_or_path) == type(list()):#如果传递的对象; tx_img = image_util.cutImage(img_or_path, textBox) elif type(img_or_path) == type(str()):#如果传递的是路径; imgObj = cv.imread(img_or_path) tx_img = image_util.cutMat(imgObj, textBox) if tx_img is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:裁剪图片失败') return '' tx_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"fotx_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") cv.imwrite(tx_img_path, tx_img) # orc识别; text = '' if len(orcInfo) >= 3 and orcInfo[2] <> {}: text = self.OCR.getStrWithImgProcess(tx_img_path, orcInfo[2], orcInfo[0], orcInfo[1]).replace(' ','') else: text = self.OCR.getStr(tx_img_path, orcInfo[0], orcInfo[1]).replace(' ','') # 返回文本; LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:文字识别结果:'+str(text)) return text ''' # 描述:到达目标区域,成功到达返回True,否则False。 # # # ''' def reachTargetZone(self, dictZoneInfo): if len(dictZoneInfo) == 0: return None # 目标区域名称; destZoneName = dictZoneInfo["destZoneName"] # 排序dict; listdict = dictZoneInfo["zoneList"] for i in range(len(listdict)-1): for j in range(0, len(listdict)-i-1): if listdict[j]["zone_priority"] > listdict[j+1]["zone_priority"]: listdict[j], listdict[j+1] = listdict[j+1], listdict[j] # 遍历区域,查找当前焦点所在区域; focus_box,zName = self.findCurrentFocusBox(listdict) # 没有找到当前焦点框; if focus_box is None: return False # 当前焦点在目标区域; if str(zName) == destZoneName: return True # 找到目标区域坐标; zoneBox = [] for zone in dictZoneInfo["zoneList"]: if destZoneName == str(zone["zoneName"]): if zone["zoneType"] == 3: zoneBox = zone["zoneData"]["zone_area"] else: zoneBox = zone["zoneData"]["ZoneCoordinate"] break times = 100 while str(zName) != str(destZoneName): # 查找目标区域; retVal = self.getOrientationEx(focus_box, zoneBox) if retVal == 0: # 左上方; self.redRat3.sendKey('down') self.redRat3.sendKey('right') elif retVal == 1: #正上方; self.redRat3.sendKey('down') elif retVal == 2: #右上方; self.redRat3.sendKey('down') self.redRat3.sendKey('left') elif retVal == 3: #正左方; self.redRat3.sendKey('right') elif retVal == 4: #正右方; self.redRat3.sendKey('left') elif retVal == 5: #左下方; self.redRat3.sendKey('up') self.redRat3.sendKey('right') elif retVal == 6: #正下方; self.redRat3.sendKey('up') elif retVal == 7: #右下方 self.redRat3.sendKey('up') self.redRat3.sendKey('left') times -= 1 if times <= 0: break focus_box, zName = self.findCurrentFocusBox(listdict) LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前焦点区域:'+str(zName)+',目标Zone:'+str(destZoneName)) if focus_box is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:failed=>当前焦点区域空') break # print '结果:'.encode('gbk'),zName.encode('gbk'),destZoneName.encode('gbk') return True if zName == destZoneName else False ''' # 描述:获取灰阶二值化图像对象; # 参数: # tvShotPath 图片路径; # cropArea 裁剪区域 # grayVal、maxGrayVal 灰度值和最大灰度值 # thresholdType 阀值类型,0=全局阀值,1=otsu阀值,2=自适应阀值; # # ''' def getBinaryImage(self, tvShotPath, cropArea, grayVal, maxGrayVal, thresholdType = 0): # 判断文件是否存在; if os.path.exists(tvShotPath) == False: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath) return None # 打开图片; img_tvShot = cv.imread(tvShotPath) if img_tvShot is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空') return None # 只保留区域部分; try: img_crop = np.array(img_tvShot[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]]) except: img_crop = img_tvShot cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "crop_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_crop) # 判断对象有效性; if img_crop is None: # print u'效果图对象空,可能文件路径无效' LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:区域截图对象空,可能文件路径无效') return None # 将截图转成灰阶; img_gray = cv.cvtColor(img_crop, cv.COLOR_BGR2GRAY) # 高斯模糊; img_gray = cv.GaussianBlur(img_gray, (3, 3), 0) # 将灰阶转成二值化图像; img_binary = None if thresholdType == 0: img_binary = cv.threshold(img_gray, grayVal, maxGrayVal, cv.THRESH_BINARY)[1] elif thresholdType == 1: img_binary = cv.threshold(img_gray, 0, maxGrayVal, cv.THRESH_BINARY+cv.THRESH_OTSU)[1] elif thresholdType == 2: img_binary = cv.medianBlur(img_gray, (3,3)) # bloksize、C两参数,暂时固定为3、4 img_binary = cv.adaptiveThreshold(img_binary, maxGrayVal, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 3, 4.5) cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_binary) return img_binary ''' # 描述:获取滑块轮廓; # # # # # ''' def getSilderCountor(self, tvShotPath, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, morphology, find_1st = True): # 获取灰阶二值化图片对象; img_binary = self.getBinaryImage(tvShotPath, cropArea, grayVal, maxGrayVal) if img_binary is None: print u'binary is none' return None # 对二值化图像进行腐蚀或膨胀操作--形态学; for val in morphology: ksize = tuple(val[1:3]) kernel = cv.getStructuringElement(val[3], ksize) # if val[0] == 0: # 腐蚀; if val[0] == "erode": img_binary = cv.erode(img_binary, kernel, val[4]) else: # 膨胀; img_binary = cv.dilate(img_binary, kernel, val[4]) morphology_path = LoggingUtil.getCaseRunLogDirPath() + "\\morphology_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png" cv.imwrite(morphology_path, img_binary) # 对形态学结果进行轮廓获取; # 获取二值化图像的轮廓; area, perimeter = 0.0, 0.0 if bInside == False:#外轮廓 contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1] else:#内轮廓 contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1] listBox = [] # 过滤掉不符合要求的轮廓; x, y, w, h,result = 0,0,0,0,False for cnt in contours: # 面积; area = cv.contourArea(cnt) # 周长; perimeter = cv.arcLength(cnt, True) # 获取满足条件的值; if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri: # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高; x, y, w, h = cv.boundingRect(cnt) # print u'boundingRect=',x,y,w,h LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h))) # 在区域内截图判断; x = x + cropArea[0] y = y + cropArea[1] contourRect = [x,y,x+w,y+h] # 如果只找第一个; if find_1st == True: result = True break # 多个; listBox.append(contourRect) #endif #endfor # 只返回满足条件的第一个值; if find_1st == True: return contourRect if result == True else None else: return listBox #end ''' # 描述:用Type9到达指定的焦点框 # 参数: # targetText: 目标焦点框文本 # focusArg: Type9焦点识别参数 # 返回:成功到达焦点框返回True,失败返回False # # ''' def reachTargetFocusBoxInType9(self, targetText, focusArg): # 参数; loop = focusArg['loop'] cropArea = focusArg['ZoneCoordinate'] grayVal = focusArg['ZoneThreshold'] maxGrayVal = focusArg['ZoneMaxThreshold'] bInside = focusArg['InsideContours'] minPeri = focusArg['focusBoxMinPeri'] maxPeri = focusArg['focusBoxMaxPeri'] minArea = focusArg['focusBoxMinArea'] maxArea = focusArg['focusBoxMaxArea'] refFocusBox = focusArg['RenderingsArea'] refTextBox = focusArg['RenderingsTextArea'] ctrldir = focusArg['FocusDirection']#只有左右=0, 上下=1,混合=2。 # 兼容focus_direction; if ctrldir == 1 or ctrldir == 2: ctrldir = 1 elif ctrldir == 3 or ctrldir == 4: ctrldir = 0 keys = [['left','right'],['up','down']] def reachType9TargetFocusBox(ctrlkey): keyCount = 0 ret = False lastText = '' firstText = None sametimes = 0 while True: # 获取焦点框; curFocusBox,tvShotPath = self.findCurrentFocusBoxInType04(cropArea,grayVal,maxGrayVal,bInside,minPeri,maxPeri,minArea,maxArea) if curFocusBox is None: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:找不到当前焦点') break # 获取文本框; textBox = self.getObjAbsoluteBox(curFocusBox, refFocusBox, refTextBox) ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png") image_util.saveCropPic(tvShotPath, ocrImgPath, textBox) ocr_result, text = self.OCR.findPicStr(targetText, ocrImgPath, refTextBox[4], refTextBox[5], refTextBox[6],refTextBox[7]) if ocr_result == True: ret = True break # 记录第一次文本; if firstText is None: firstText = text else: # 循环列表,同时回到起始位置; if loop == True and firstText == text: break # 记录最后次文本; if lastText != text: lastText = text if loop == False: keyCount += 1 else: if loop == False: sametimes += 1 # 如果相同次数超过n次,认为到达底部; if loop == False and sametimes >= 2: break # 遥控; self.redRat3.sendKey(ctrlkey) # 返回结果; return ret,keyCount # 正序查找; result,keyCount = reachType9TargetFocusBox(keys[ctrldir][1]) if result == False: if loop == True: return False # 到达起点,再反向开始查找; self.redRat3.sendKey(keys[ctrldir][0], keyCount) result = reachType9TargetFocusBox(keys[ctrldir][0])[0] # 返回文本数组; return result def getRGBBinaryImage(self, src_pic, cropArea, bgr, offset=20): # 读取图片; img = cv.imread(src_pic) if img is None: return None,None # 如果截图区域空,使用原图大小; if cropArea is not None or cropArea.__len__() == 0: cropArea = [0, 0, img.shape[1], img.shape[0]] img_crop = None # 只保留区域部分; try: img_crop = np.array(img[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]]) except: img_crop = img imgArr = np.asarray(img_crop).astype(np.int16) bgrArr = np.asarray(bgr).astype(np.int16) # 矩阵相减; subImg = imgArr - bgrArr cv.imwrite(r'E:\\subImg.png', subImg) # img_RGB = cv.cvtColor(img_crop,cv.COLOR_BGR2RGB) # imgArr_RGB = np.asarray(img_RGB).astype(np.int16) # rgb = (bgr[2],bgr[1], bgr[0]) # rgbArr = np.asarray(rgb).astype(np.int16) # subImg_RGB = imgArr_RGB - rgb # subImgsum = (subImg + subImg_RGB) # cv.imwrite(r'E:\\subImgsum.png', subImgsum) # np.where(condition,x,y)满足条件值改为x,否则为y; # subImg_wh1 = np.where((subImg<-offset)|(subImg>offset), np.uint8(255), np.uint8(0)) subImg_wh1 = np.where((subImg > -offset) & (subImg < offset), np.uint8(0), np.uint8(255)) cv.imwrite(r'E:\\subImg_wh1.png', subImg_wh1) # 将图处转成灰阶 img_gray = cv.cvtColor(subImg_wh1, cv.COLOR_BGR2GRAY) # 反转灰度值,以便适合查找轮廓; img_gray = np.where((img_gray < -offset) | (img_gray > offset), np.uint8(0), np.uint8(255)) # 高斯模糊; img_gray = cv.GaussianBlur(img_gray, (3, 5), 0) # 输出结果图片; cv.imwrite(r'E:\\img_gray.png', img_gray) # 使用灰度图求得二值化图; # thresh = cv.threshold(img_gray, 1, 255, cv.THRESH_OTSU | cv.THRESH_BINARY)[1] thresh = cv.adaptiveThreshold(img_gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY_INV,15,10) cv.imwrite(r'E:\\img_thresh.png', thresh) # 返回二值化图; return thresh, cropArea ''' 在传入的图片上,查找指定bgr颜色框矩形区域,返回矩形区域的坐标 :param picPath:图片路径 :param bgr:颜色值 :return rectArea:矩形区域 和区域图片 ''' def findCurrentFoucsByColor(self, picPath, cropArea, bgr, offset=20, minPeri=0, maxPeri=0, minArea=0, maxArea=0, morphology = []): # 获取rgb二值图; img_binary, cropArea = self.getRGBBinaryImage(picPath, cropArea, bgr, offset) if img_binary is None: return False,None # 对二值化图像进行腐蚀或膨胀操作--形态学; for val in morphology: ksize = tuple(val[1:3]) kernel = cv.getStructuringElement(val[3], ksize) # 腐蚀; if val[0] == "erode": img_binary = cv.erode(img_binary, kernel, val[4]) else: # 膨胀; img_binary = cv.dilate(img_binary, kernel, val[4]) morphology_path = LoggingUtil.getCaseRunLogDirPath() + "\\morphology_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png" cv.imwrite(morphology_path, img_binary) # 查找符合要求的外轮廓; # opencv3开始,有3个返回值:img, countours, hierarchy contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1] # 是否查找最大轮廓; bFindMaxContour = False # 如果周长、面积都一样,只返回最大轮廓; if minPeri == maxPeri and minArea == maxArea: bFindMaxContour = True contourRect = [] maxContour = None # 最大轮廓; tempArea = 0 # 过滤掉不符合要求的轮廓; x, y, w, h, result = 0, 0, 0, 0, False for cnt in contours: # 面积; area = cv.contourArea(cnt) # 周长; perimeter = cv.arcLength(cnt, True) # print "area=%ld, perimeter=%ld"%(area, perimeter) if bFindMaxContour == True: # 获取最大轮廓; if tempArea < area: tempArea = area maxContour = cnt else: # print area,perimeter # 获取满足条件的值; if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri: # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高; x, y, w, h = cv.boundingRect(cnt) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] contourRect = [x, y, x + w, y + h] result = True break # endif # endfor if bFindMaxContour == True and maxContour is not None: result = True # 最大轮廓的矩形坐标; x, y, w, h = cv.boundingRect(maxContour) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] contourRect = [x, y, x + w, y + h] # 只返回结果、轮廓坐标; return result, contourRect # 文本颜色作为焦点色:获取文本色所有轮廓,计算最左、最右、最上、最下四坐标; def findFramelessFocusByColor(self, src_pic, cropArea, bgr, offset=20): # 获取rgb二值图; thresh, cropArea = self.getRGBBinaryImage(src_pic, cropArea, bgr, offset) if thresh is None: return False,[] # 查找符合要求的外轮廓; # opencv3开始,有3个返回值:img, countours, hierarchy contours = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1] # contourRect = [] left, right, top, bottom = 100000,0,100000,0 # 遍历轮廓; for cnt in contours: # 面积; # area = cv.contourArea(cnt) # 周长; # perimeter = cv.arcLength(cnt, True) # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高; x, y, w, h = cv.boundingRect(cnt) # 在区域内截图判断; if cropArea is not None: x = x + cropArea[0] y = y + cropArea[1] if left > x: left = x if top > y: top = y if right < x + w: right = x + w if bottom < y + h: bottom = y + h #endfor; # 是否有效矩形; if left >= right or top >= bottom: return False, [] # 返回结果; return True, [left-3, top-3, right+3, bottom+3] def testFocusArea(): focusAreaDict = {u'tv': [43, 128, 380, 265], u'media': [43, 648, 380, 785], u'hdmi1': [43, 386, 380, 525], u'hdmi2': [43, 517, 380, 655], u'av': [43, 257, 380, 395]} focusDict = { "focus_tv": '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}', "focus_av": '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}', "focus_hdmi1": '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}', "focus_hdmi2": '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}', "focus_usb": '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}' } icon = r"res\icon.png" focus_tv = r"res\focus_tv_focus.png" focus_av = r"res\focus_av_focus.png" focus_hdmi1 = r"res\focus_hdmi1_focus.png" focus_hdmi2 = r"res\focus_hdmi2_focus.png" focus_usb = r"res\focus_usb_focus.png" picture = r"res\source.jpg" fDetect = FeatureDetect() print fDetect.getFocusArea(icon, picture, focusDict, 10) def testRect(): fDetect = FeatureDetect() # fDetect.findRectByFile(r"D:\temp-pic\parentlock\channel_lock1.png", "parentLock") fDetect.findRectByFile(r"D:\temp-pic\parentlock\channel_lock2.png", "parentLock") # fDetect.findRectByFile(r"D:\temp-pic\parentlock\source_lock1.png", "parentLock") if __name__ == "__main__": fdetect = FeatureDetect() if 0: focusArg = { "TVShotPath": '', "ZoneCoordinate": [1204, 0, 1920, 1080], "InsideContours": False, "ZoneThreshold": 150, "ZoneMaxThreshold": 255, # 选中焦点框的周长、面积; "focusBoxMinPeri": 50, "focusBoxMaxPeri": 90, "focusBoxMinArea": 300, "focusBoxMaxArea": 500, # 文本最左边; "textleft":1260, # 文本最右边; "textright":1860, # 文本在焦点滑块上方多高; "aboveHeight":80, "aboveMargin":30, "morphology":[ ["erode",6,6,0,2], ["dilate",6,6,0,2] ] } listText = [] for i in range(99): path = r'Z:\Picture\V27N\CusPicMode\%d.png'%i img = cv.imread(path) if img is not None: countor = fdetect.getSilderCountor( path, focusArg['ZoneCoordinate'], focusArg['ZoneThreshold'], focusArg['ZoneMaxThreshold'], focusArg['InsideContours'], focusArg['focusBoxMinPeri'], focusArg['focusBoxMaxPeri'], focusArg['focusBoxMinArea'], focusArg['focusBoxMaxArea'], focusArg['morphology'], ) width,height = countor[2]-countor[0],countor[3]-countor[1] # 文本坐标; txbox = [focusArg['textleft'], countor[1] - focusArg['aboveHeight'], focusArg['textright'], countor[1] - focusArg['aboveMargin']] text = fdetect.getCurrentFocusText(path, txbox, ["chineseprc+english",10000]) listText.append(text) print listText # findFocusTextByColor测试; if 0: src_pic = r'C:\Users\jianfeng1.wang\Pictures\Saved Pictures\img.png' icon_pic = r'C:\Users\jianfeng1.wang\Pictures\Saved Pictures\icon.png' iconImg = cv.imread(icon_pic) # 获取小图平均bgr值; rgbColor = RGBColor() bgr = rgbColor.getAvgBGR(iconImg) print fdetect.findFocusTextByColor(src_pic, [], bgr)