| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 | # -*- coding:utf-8 -*-import os, sys, timeimport numpy as npfrom 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 Falseif __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)
 |