123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- # -*- coding:utf-8 -*-
- import os, sys, time
- import numpy as np
- from ssat_sdk.tv_operator import *
- from ssat_sdk.video_capture import *
- from ssat_sdk.picture.feature_detect import *
- Area_Empty = [-1,-1,-1,-1]
- OneStepPolicy={
- "left":"L",
- "right":"R",
- "up":"U",
- "down":"D"
- }
- class FocusModel():
- def __init__(self):
- self.grid = None
- self.featureDetect = FeatureDetect()
- self.operator = TvOperator()
- self.vcap = VideoCapture()
- '''
- 设定当前界面的聚焦框图标
- :param focus_icon_file:选择框图标文件路径
- :param focusDict: 可能出现icon的区域。
- 例如:
- {
- 'back': [152, 780, 432, 860],
- 'forgot': [120, 946, 486, 1010],
- 'gmain': [152, 618, 386, 672]
- }
- '''
- def setUiFeature(self, focus_icon_file, focusDict):
- self.focus_icon_file = focus_icon_file
- self.focus_icon = cv.imread(focus_icon_file)
- self.focusDict = focusDict
- self.genFocusGridMode(self.focusDict)
- '''
- #构建操作步骤模型(网格),用于两个焦点之间的路径确认。
- #计算方式:已左上角点坐标为准,并假设每个焦点框相离。
- :param focusDict : {u'tv': [43, 128, 380, 265], u'media': [43, 648, 380, 785]}
- '''
- def genFocusGridMode(self, focusDict):
- print "genFocusGridMode:",focusDict
- #整理出所有列,x轴
- x_dict = {}
- for item in focusDict.iteritems():
- #item格式:['tv',[43, 128, 380, 265]]
- self.addDictList(x_dict, item[1][0], item[1][1])
- x_list = sorted(x_dict.keys())
- # print x_list
- #整理出所有行
- y_dict = {}
- for item in focusDict.iteritems():
- self.addDictList(y_dict, item[1][1], item[1][0])
- y_list = sorted(y_dict.keys())
- # print y_list
- #整理出网格,即二维数组,初始化值为None,然后把所有焦点依次填充到二维数组中
- self.grid = [[Area_Empty]*x_list.__len__() for i in range(y_list.__len__())]
- # print self.grid
- for x in range (x_list.__len__()):
- for y in range(y_list.__len__()):
- # print "x,y", x,y
- isExist, area = self.findFocus(focusDict,x_list[x], y_list[y])
- if isExist:
- self.grid[y][x] = area
- else:
- self.grid[y][x] = Area_Empty
- print "genFocusGridMode.Grid:"
- for row in self.grid:
- print row
- def findFocus(self,focusDict, pointX,pointY):
- for focus in focusDict:
- if focusDict[focus][0] == pointX and focusDict[focus][1] == pointY:
- return True, focusDict[focus]
- return False,Area_Empty
- '''
- 根据icon,寻找在图片中icon的位置
- :param icon:选择框图标文件路径
- :param screen:整张图片文件路径
- :param focusDict: 可能出现icon的区域。
- 例如:
- {
- '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.locateFocusArea(icon_img, screen, focusDict, border_wide)
- def locateImgFocusArea(self, icon, screen, focusDict, border_wide):
- return self.featureDetect.locateImgFocusArea(icon, screen, focusDict, border_wide)
- '''
- 在构建好网格数据模型后,从A移动焦点到B,每移动一步,进行下一步的判断
- :param areaT 目标区域坐标
- :param keyDict 例如:{'U':"up","D":"down","L":"left","R":"right"},up/down/left/right是实际操控电视的指令
- :param wide 聚焦框的宽度
- :return result:bool类型,True代表成功,False代表失败
- '''
- def goToArea(self,nameT, areaT, keyDict, wide):
- print "goTo Area:",nameT, areaT
- if not self.isSameArea(self.focusDict[nameT], areaT):
- print u"请检查函数setUiFeature的UI参数和目标是否匹配!"
- pic_tmp = os.path.join(getSATTmpDIR(), "focus_goToArea.png")
- count = 0
- Max_Try = 100
- while (True):
- if count > Max_Try:
- break
- count += 1
- #1 截取当前电视画面
- self.vcap.takePicture(pic_tmp)
- #2 获取当前焦点位置,判断是否到达目标区域
- name, area = self.locateImgFocusArea(self.focus_icon, pic_tmp, self.focusDict, wide)
- print "goToArea name/location/target:", name, area, areaT
- if self.isSameArea(area,areaT):
- print u"选中目标",name, area
- return True
- #3 计算方位
- direction = self.getTargetDirection(area, areaT)
- #4 根据计算的方位,进行移位操作
- self.goOneStep(direction, keyDict)
- return False
- def goOneStep(self, direction, keyDict):
- print "goOneStep,direction:", direction
- directionArr = direction.split("-")
- for direct in directionArr:
- key = OneStepPolicy[direct]
- print "key:",key
- self.operator.sendKey(keyDict[key])
- '''
- 计算目标区域在当前焦点区域的方位.
- | |
- 左上角 | 上 | 右上角
- | |
- -----------------------------------
- | |
- | |
- 左 | 焦点区域 | 右
- | |
- -----------------------------------
- | |
- 左下角 | 下 | 右下角
- | |
- 区域的方位,按上图,分成4面位方向和4角位方向。
- 4面位方向:上、下、左、右。只要目标区域,和4面位方向区域有交集,就属于面位方向。
- 4角位方向:左上角、右上角、左下角、右下角。目标区域全部在4角位方向区域,才是角位方向。
- 方位定义:up、down、left、right、left-up、left-down、right-up、right-down
- :param areaL:当前焦点所在区域
- :param areaT:目标区域
- :return direction。字符串类型,方位:up、down、left、right、left-up、left-down、right-up、right-down
- '''
- ImgRectLimit = 10000
- def getTargetDirection(self, areaL, areaT):
- print "getTargetDirection:",areaL,areaT
- #确定4个面位方位的区域的x,y轴范围
- up = [areaL[0],0,areaL[2],areaL[1]]
- down = [areaL[0],areaL[3],areaL[2], FocusModel.ImgRectLimit]
- left = [0,areaL[1],areaL[0], areaL[3]]
- right = [areaL[2],areaL[1],FocusModel.ImgRectLimit, areaL[3]]
- #检测目标区域是否和4个面位方向区域有重叠部分
- up_direct = self.hasSameArea(up, areaT)
- if up_direct:
- return "up"
- down_direct = self.hasSameArea(down, areaT)
- if down_direct:
- return "down"
- left_direct = self.hasSameArea(left, areaT)
- if left_direct:
- return "left"
- right_direct = self.hasSameArea(right, areaT)
- if right_direct:
- return "right"
- #计算目标区域坐上角点的方位,x,y差值确定角位方向
- dL_Ux = areaT[0] - areaL[0]
- dL_Uy = areaT[1] - areaL[1]
- if (dL_Ux > 0) and (dL_Uy > 0):
- return "right-down"
- if (dL_Ux < 0) and (dL_Uy > 0):
- return "left-down"
- if (dL_Ux > 0) and (dL_Uy < 0):
- return "right-up"
- if (dL_Ux < 0) and (dL_Uy < 0):
- return "left-up"
- '''
- 检测两个区域是否有重叠部分
- '''
- def hasSameArea(self, area1, area2):
- #计算各区域宽、高
- area1_W = area1[2] - area1[0]
- area1_H = area1[3] - area1[1]
- area2_W = area2[2] - area2[0]
- area2_H = area2[3] - area2[1]
- # 计算目标区域坐上角点的方位
- dL_Ux = area1[0] - area2[0]
- dL_Uy = area1[1] - area2[1]
- # 计算x坐标,判断是否有重叠可能。
- x_enable = False
- if dL_Ux > 0:
- if (abs(dL_Ux) - area2_W) >= 0:
- x_enable = False
- else:
- x_enable = True
- else:
- if (abs(dL_Ux) - area1_W) >= 0:
- x_enable = False
- else:
- x_enable = True
- #计算y坐标,判断是否有重叠可能。
- y_enable = False
- if dL_Uy > 0:
- if (abs(dL_Uy) - area2_H) > 0:
- y_enable = False
- else:
- y_enable = True
- else:
- if (abs(dL_Uy) - area1_H) > 0:
- y_enable = False
- else:
- y_enable = True
- #如果x坐标、y坐标都有可能重叠,则一定重叠。反之,没有重叠。
- return x_enable and y_enable
- def indexByArea(self, target_area):
- for row in range(self.grid.__len__()):
- for col in range(self.grid[row].__len__()):
- area = self.grid[row][col]
- if self.isSameArea(area, target_area):
- return row,col
- return -1,-1
- def isSameArea(self, areaA, areaB):
- if areaA[0] == areaB[0] \
- and areaA[1] == areaB[1] \
- and areaA[2] == areaB[2] \
- and areaA[3] == areaB[3] :
- return True
- else:
- return False
- def addDictList(self, fdict, key, value):
- # print fdict, key, value
- pt = (key)
- if fdict.has_key(key):
- for index in range(fdict[key].__len__()):
- # print index, fdict[key][index] , value
- if (fdict[key][index] > value):
- fdict[key].insert(index, value)
- break
- if (index == (fdict[key].__len__()-1)):
- fdict[key].append(value)
- else:
- list = []
- list.append(value)
- fdict[key] = list
- '''
- 在构建好网格数据模型后,用于确定区域A到区域B的路径
- :param areaA 起始区域坐标
- :param areaB 目标区域坐标
- :return path:数组类型,例如:['L','R'..] L:left,R:right,U:up,D:down
- '''
- def getPathA2B(self, areaA,areaB):
- print "getPathA2B:", areaA,areaB
- aIndex = self.indexByArea(areaA)
- bIndex = self.indexByArea(areaB)
- print "area A , B:", aIndex,bIndex
- path = []
- #x 轴,水平左右方向移动值. L 或 R
- for i in range(abs(bIndex[1] - aIndex[1])):
- if (bIndex[1] - aIndex[1]) > 0:
- path.append('R')
- else:
- path.append('L')
- # y 轴,纵向上下方向移动值. U 或 D
- for i in range(abs(bIndex[0] - aIndex[0])):
- if (bIndex[0] - aIndex[0]) > 0:
- path.append('D')
- else:
- path.append('U')
- return path
- '''
- 在构建好网格数据模型后,用于确定区域A到区域B的路径
- :param path getPathA2B函数返回的路径。例如:L-L-D-U-R
- :param keyDict 例如:{'U':"up","D":"down","L":"left","R":"right"},up/down/left/right是实际操控电视的指令。
- :return path:数组类型,例如:['L','R'..] L:left,R:right,U:up,D:down
- '''
- def executePath(self, path, keyDict):
- print "FocusModel,executePath:",path
- for step in path:
- self.operator.sendKey(keyDict[step])
- '''
- 在构建好网格数据模型后,用于确定区域A到区域B的路径,然后按路径一次性移动焦点。
- :param areaL 起始区域坐标
- :param name 目标区域的名字
- :param keyDict 例如:{'U':"up","D":"down","L":"left","R":"right"},up/down/left/right是实际操控电视的指令。
- '''
- def gotoAreaByPath(self, areaL, name, keyDict):
- # key是否存在字典中;
- isKeyExist = name.lower() in self.focusDict.keys()
- if isKeyExist == True:
- path = self.getPathA2B(areaL, self.focusDict[name.lower()])
- self.executePath(path, keyDict)
- # 如何键值存在返回true
- return isKeyExist
- '''
- 根据icon,寻找在图片中icon的位置,知道icon所在位置的文本内容为参数textT值
- :param textT: 选中区域的文本内容
- :param direction: 方位,遥控器的上下左右操作,值范围:U、D、L、R,必须是keyDict中键。符合executePath函数返回值规范
- :param keyDict 例如:{'U':"up","D":"down","L":"left","R":"right"},up/down/left/right是实际操控电视的指令。
- :param maxMove 在指定方位移动的最大次数
- :param icon:图标文件路径
- :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 result:True代表成功,False代表失败
- '''
- def gotoAreaByDirection(self, textT,direction, keyDict, maxMove, uiDict, border_wide):
- pic_tmp = os.path.join(getSATTmpDIR(), "focus_gotoAreaByDirection.png")
- count = 0
- while True:
- if count > maxMove:
- break
- count += 1
- self.vcap.takePicture(pic_tmp)
- textL, text_area, focus_area = self.featureDetect.getFocusArea(self.focus_icon_file, pic_tmp, uiDict, border_wide)
- if textT.lower() == textL.lower():
- return True
- self.operator.sendKey(keyDict[direction])
- return False
- if __name__ == "__main__":
- focusModel = FocusModel()
- # focusDict = {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 = {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], u'test':[400,500,600,800]}
- focusModel.genFocusGridMode(focusDict)
|