|
@@ -0,0 +1,3320 @@
|
|
|
+# -*- 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)
|