feature_detect.py 138 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320
  1. # -*- coding:utf-8 -*-
  2. import inspect
  3. import os, sys, time
  4. '''
  5. 用于图片特征提取和定位
  6. '''
  7. import image_util
  8. import PreImageJudge
  9. import cv2 as cv
  10. import numpy as np
  11. import json
  12. from ssat_sdk.sat_environment import *
  13. from TST.NetOCR import *
  14. from ssat_sdk.ocr_convert import *
  15. from color_space import *
  16. from line_util import LineUtil
  17. from ssat_sdk.device_manage import *
  18. # 导入枚举;
  19. from enum import Enum
  20. from itertools import chain
  21. # from ssat_sdk.device_manage.RedRatHub3 import RedRat3
  22. from ssat_sdk.tv_operator import *
  23. from ssat_sdk.video_capture import VideoCapture
  24. # import ssat_sdk.utils.LoggingUtil
  25. from ssat_sdk.utils import LoggingUtil
  26. from ssat_sdk.utils import string_util
  27. from ssat_sdk.picture.RGB import RGBColor
  28. from ssat_sdk.picture.pq_detect import PQDetect
  29. pyFileName = os.path.split(__file__)[-1]
  30. def get_current_function_name():
  31. return inspect.stack()[1][3]
  32. # 效果图形状
  33. class IconShape(Enum):
  34. Rectangle = 0 # 矩形;
  35. Circle = 1 # 圆形(包括椭圆)
  36. Polygon = 2 # 多边形
  37. class FocusType(Enum):
  38. Border = 0 # 加框;
  39. Coloring = 1 # 着色;
  40. Deform = 2 # 变形;
  41. ColorLine = 3 # 纯色线查找矩形;
  42. ThresholdBinary = 4 # 二值化阀值查找焦点框;
  43. matchTempl = 5 # 模板匹配法查找焦点框;
  44. class Direction(Enum):
  45. UnChange = 0 # 区域不变;
  46. TopChange = 1 # 上;
  47. BottomChange = 2 # 下;
  48. LeftChange = 3 # 左;
  49. RightChange = 4 # 右;
  50. ZoomIn = 5 # 放大;
  51. ZoomOut = 6 # 缩小;
  52. methods = [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]
  53. class FeatureDetect():
  54. def __init__(self):
  55. self.className = self.__class__.__name__
  56. self.CIELuv = CIEluvCaculator()
  57. self.OCR = OCRConvert()
  58. self.lineUtil = LineUtil()
  59. # self.redRat3 = RedRat3()
  60. self.redRat3 = TvOperator()
  61. self.vp = VideoCapture()
  62. self.PQ = PQDetect()
  63. '''
  64. 检测传入图片的指定区域的线段
  65. :param file:图片文件路径
  66. :param lineMineLen:线段最短长度。 默认 20
  67. :param lineMaxLen:线段最长长度。默认 1000
  68. :param ableArea:指定区域。 默认 整张图片
  69. :return 返回线段数组
  70. '''
  71. def getLinesByFile(self, file, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5):
  72. img = cv.imread(file)
  73. return self.getLines(img, lineMinLen, lineMaxLen, ableArea, threshold1, threshold2, apertureSize)
  74. def getLines(self, img, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5
  75. , lineThreshold = 200):
  76. targetMat = np.array(img)
  77. if (ableArea <> None):
  78. targetMat = image_util.cutMat(img, ableArea)
  79. grayMat = cv.cvtColor(targetMat, cv.COLOR_BGR2GRAY)
  80. # cv.imshow("gray", grayMat)
  81. edges = cv.Canny(grayMat, threshold1, threshold2, apertureSize=apertureSize)
  82. # cv.imshow("edges",edges)
  83. lines = cv.HoughLinesP(edges, 2, np.pi / 180, lineThreshold)
  84. retLines = []
  85. if lines is None:
  86. return None, None
  87. for line in lines:
  88. x1, y1, x2, y2 = line[0]
  89. # print "line:", x1, y1, x2, y2
  90. # "line rgb:",PreImageJudge.getImgAverageRGB(targetMat, (x1, y1, x2+1, y2+1)), \
  91. # "icon rgb:",PreImageJudge.getAverageRGB(icon)
  92. lineLen = self.lineUtil.caculateLinLen(line[0])
  93. if lineLen > lineMinLen and lineLen < lineMaxLen:
  94. # print "retLine:", line[0]
  95. cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
  96. retLines.append(line[0])
  97. # cv.imshow("getLines", img)
  98. # cv.waitKey(0)
  99. # cv.destroyAllWindows()
  100. return retLines, img
  101. def findRectByFile(self, picPath, type):
  102. img = cv.imread(picPath)
  103. return self.findRectByImg(img, type)
  104. def findRectByImg(self, img, type):
  105. retLines = None
  106. lineImg = None
  107. if (type == "parentLock"):
  108. retLines, lineImg = self.getLines(img, lineThreshold=200)
  109. comLines = self.lineUtil.combineLineList(retLines)
  110. # for line in comLines:
  111. # print "comLines",line
  112. # cv.line(img, (line[0],line[1]),(line[2],line[3]), (0, 0, 255), 2)
  113. minPoint,maxPoint = self.lineUtil.getLineMMPoint(comLines)
  114. rect = [minPoint[0],minPoint[1], maxPoint[0],maxPoint[1]]
  115. # print rect
  116. # cv.rectangle(img, (minPoint[0], minPoint[1]), (maxPoint[0],maxPoint[1]), (0, 0, 255), 2)
  117. # cv.imshow("comLine", img)
  118. # cv.waitKey(0)
  119. # cv.destroyAllWindows()
  120. return rect
  121. # print retLines
  122. # cv.imwrite(os.path.join(LoggingUtil.getCaseRunLogDirPath(), "lineImg.png"), lineImg)
  123. '''
  124. 根据icon,寻找在图片中icon的位置,返回选中区域的特征:文本、文本区域、聚焦区域
  125. :param icon:图标文件路径
  126. :param screen:整张图片文件路径
  127. :param uiDict: 可能出现icon的区域。
  128. 例如:
  129. #name的字符串,需为text_area区域显示的文字
  130. {
  131. focus_tv : '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}',
  132. focus_av : '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}',
  133. focus_hdmi1 : '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}',
  134. focus_hdmi2 : '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}',
  135. focus_usb : '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}'
  136. }
  137. :param border_wide:四周选择框的厚度
  138. :return 返回文本、文本区域、聚焦区域
  139. '''
  140. def getFocusArea(self, icon, screen, uiDict, border_wide):
  141. # 1 获取焦点框特征
  142. icon_luv= self.calBorderAVGLuvPic(icon, border_wide)
  143. print "icon:", icon_luv
  144. # 2 与可能的聚焦位置进行比较
  145. # print self.focusDict
  146. # 计算直线数最接近区域
  147. target_text_area = None
  148. target_focus_area = None
  149. dLine = 10000
  150. focus_end = "focus_end"
  151. for focus in uiDict:
  152. one_dict = json.loads(uiDict[focus])
  153. area = one_dict["focus_area"]
  154. focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
  155. image_util.saveCropPic(screen, focus_img, area)
  156. img_luv = self.calBorderAVGLuvPic(focus_img, border_wide)
  157. # print focus, " ,img_luv:", img_luv
  158. if img_luv is None:
  159. continue
  160. diff = self.CIELuv.getDiffUV(icon_luv, img_luv)
  161. if diff[2] < dLine:
  162. dLine = diff[2]
  163. # print "current dLine:", dLine, area, one_dict["text_area"]
  164. target_focus_area = area
  165. target_text_area = one_dict["text_area"]
  166. focus_end = focus
  167. text_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
  168. image_util.saveCropPic(screen, text_img, target_text_area[0:4])
  169. language = target_text_area[4]
  170. ocrType = target_text_area[5]
  171. if len(target_text_area) != 6 or target_text_area[6] == {}:
  172. target_text = self.OCR.getStr(text_img, language, ocrType)
  173. else:
  174. target_text = self.OCR.getStrWithImgProcess(text_img, target_text_area[6], language, ocrType)
  175. # target_text = self.OCR.getStr(text_img, language, ocrType)
  176. print u"getFocusArea,选中区域文字:",target_text
  177. return target_text, target_text_area, target_focus_area
  178. '''
  179. 根据icon,寻找在图片中icon的位置
  180. :param icon:选择框图标文件路径
  181. :param screen:整张图片文件路径
  182. :param focusDict: 可能出现icon的区域。
  183. 例如:
  184. #name的字符串,需为text_area区域显示的文字
  185. {
  186. 'back': [152, 780, 432, 860],
  187. 'forgot': [120, 946, 486, 1010],
  188. 'gmain': [152, 618, 386, 672]
  189. }
  190. :param border_wide:四周选择框的厚度
  191. :return 聚焦区域
  192. '''
  193. def locateFocusArea(self, icon, screen, focusDict, border_wide):
  194. icon_img = cv.imread(icon)
  195. return self.locateImgFocusArea(icon_img, screen, focusDict, border_wide)
  196. '''
  197. 根据icon,寻找在图片中icon的位置
  198. :param icon:选择框图标的cv的mat对象
  199. :param screen:整张图片文件路径
  200. :param focusDict: 可能出现icon的区域。
  201. 例如:
  202. #name的字符串,需为text_area区域显示的文字
  203. {
  204. 'back': [152, 780, 432, 860],
  205. 'forgot': [120, 946, 486, 1010],
  206. 'gmain': [152, 618, 386, 672]
  207. }
  208. :param border_wide:四周选择框的厚度
  209. :return 聚焦区域
  210. '''
  211. def locateImgFocusArea(self, icon, screen, focusDict, border_wide):
  212. # 1 获取焦点框特征
  213. icon_luv = self.calBorderAVGLuvImg(icon, border_wide)
  214. # print "icon luv:", icon_luv
  215. # 2 与可能的聚焦位置进行比较,按LUV平均值接近度衡量
  216. focus_area = None
  217. focus_name = None
  218. dLine = 10000
  219. for focus in focusDict:
  220. area = focusDict[focus]
  221. # print "area name:", focus
  222. focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "locateImgFocusArea_focus.png")
  223. image_util.saveCropPic(screen, focus_img, area)
  224. img_luv = self.calBorderAVGLuvPic(focus_img, border_wide)
  225. # print "img_luv:", img_luv,one_dict
  226. if img_luv is None:
  227. continue
  228. diff = self.CIELuv.getDiffUV(icon_luv, img_luv)
  229. # print focus, img_luv,diff
  230. if diff[2] < dLine:
  231. dLine = diff[2]
  232. # print "current dLine:", dLine, area, one_dict["text_area"]
  233. focus_area = area
  234. focus_name = focus
  235. return focus_name, focus_area
  236. '''
  237. 计算图片四周边框的颜色平均值,颜色空间Luv
  238. :param pic:图片文件路径
  239. :param wide:四周边框的宽度
  240. :return LUV:四周边框Luv平均值
  241. '''
  242. def calBorderAVGLuvPic(self,pic, wide):
  243. img = cv.imread(pic)
  244. return self.calBorderAVGLuvImg(img, wide)
  245. def calBorderAVGLuvImg(self, img, wide):
  246. luv_img = cv.cvtColor(img, cv.COLOR_BGR2Luv)
  247. height, width, colorDim = luv_img.shape
  248. left_area = [0,0,wide,height]
  249. right_area = [width-wide,0,width,height]
  250. top_area = [wide,0,width-wide,wide]
  251. bottom_area = [wide,height-wide,width-wide,height]
  252. return self.CIELuv.getMAverageLUV(luv_img,[left_area,right_area,top_area,bottom_area])
  253. # Ex interface
  254. '''
  255. 描述:寻找图片中符合icon效果图的位置,返回焦点区域的特征:文本、文本区域、聚集区域.
  256. 参数:
  257. icon_path: 效果图路径;
  258. screen_path: 要检测焦点框的全屏图路径;
  259. uiDict: ui字典;
  260. 示例字典,需为text_area区域显示的文字
  261. {
  262. focus_tv : '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}',
  263. focus_av : '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}',
  264. focus_hdmi1 : '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}',
  265. focus_hdmi2 : '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}',
  266. focus_usb : '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}'
  267. }
  268. icon_shape: 效果图形状;
  269. focus_type: 焦点类型;
  270. focus_direction: 焦点变化方向;
  271. border_wide: 焦点框边框大小;
  272. 返回值:
  273. None
  274. 示例:
  275. 注意:
  276. '''
  277. def getFocusAreaEx(self, icon_path, screen_path, uiDict, icon_shape, focus_type, focus_direction, border_wide):
  278. # 整型转枚举;
  279. if type(icon_shape) != type(IconShape.Rectangle):
  280. icon_shape = IconShape(icon_shape)
  281. if type(focus_type) != type(FocusType.Border):
  282. focus_type = FocusType(focus_type)
  283. if type(focus_direction) != type(Direction.ZoomIn):
  284. focus_direction = Direction(focus_direction)
  285. # 矩形效果图;
  286. if icon_shape == IconShape.Rectangle:
  287. return self.getRectangleFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide)
  288. # 圆形效果图;
  289. elif icon_shape == IconShape.Circle:
  290. return self.getCircleFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide)
  291. # 非矩形平行四边形效果图;
  292. elif icon_shape == IconShape.Polygon:
  293. return self.getPolygonFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide)
  294. else:
  295. LoggingUtil.getDebugLogger().info(
  296. pyFileName,
  297. self.className,
  298. get_current_function_name(),
  299. 'SDK:icon_shape不存范围内'+icon_shape)
  300. return None, None, None
  301. # 获取矩形的焦点区域;
  302. def getRectangleFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide):
  303. # 焦点框(外镶边框)
  304. if focus_type == FocusType.Border:
  305. return self.getBorderRectangleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide)
  306. # 区域变色;
  307. elif focus_type == FocusType.Coloring:
  308. return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction)
  309. # 图形变化;
  310. elif focus_type == FocusType.Deform:
  311. return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction)
  312. # 获取圆形(包含椭圆)的焦点区域;
  313. def getCircleFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide):
  314. # 焦点框(外镶边框)
  315. if focus_type == FocusType.Border:
  316. return self.getBorderCircleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide)
  317. # 区域变色;
  318. elif focus_type == FocusType.Coloring:
  319. return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction)
  320. # 图形变化;
  321. elif focus_type == FocusType.Deform:
  322. return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction)
  323. # 多边形的焦点区域;
  324. def getPolygonFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide):
  325. # 焦点框(外镶边框)
  326. if focus_type == FocusType.Border:
  327. return self.getBorderRectangleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide)
  328. # 区域变色;
  329. elif focus_type == FocusType.Coloring:
  330. return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction)
  331. # 图形变化;
  332. elif focus_type == FocusType.Deform:
  333. return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction)
  334. '''
  335. # 描述:查找矩形焦点框,选中焦点框的效果为矩形边框外镶一层有色边框。focus_type=0,简述:矩形边框。
  336. # 参数:
  337. # icon_path:焦点框被选中时,外镶一层边框的效果图路径。
  338. # screen_path:需要判断焦点框的电视画面截图。
  339. # uiDict:
  340. # focus_direction:
  341. # border_wide:焦点框被选中时,外镶边框的厚度(像素宽)
  342. # 返回值:
  343. #
  344. # '''
  345. def getBorderRectangleFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide):
  346. # 获取焦点框特征
  347. icon_luv = self.calBorderAVGLuvPic(icon_path, border_wide)
  348. # 计算直线数最接近区域
  349. target_text_area = None
  350. target_focus_area = None
  351. dLine = 10000
  352. focus_end = "focus_end"
  353. for focus in uiDict:
  354. # one_dict = json.loads(uiDict[focus])
  355. one_dict = uiDict[focus]
  356. # print one_dict["focus_area"]
  357. # diff = 11
  358. area = one_dict["focus_area"]
  359. # 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]
  360. # zoomin;
  361. # print area
  362. focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
  363. image_util.saveCropPic(screen_path, focus_img, area)
  364. img_luv = self.calBorderAVGLuvPic(focus_img, border_wide)
  365. # print focus, " ,img_luv:", img_luv
  366. if img_luv is None:
  367. continue
  368. diff = self.CIELuv.getDiffUV(icon_luv, img_luv)
  369. if diff[2] < dLine:
  370. dLine = diff[2]
  371. # print "current dLine:", dLine, area, one_dict["text_area"]
  372. target_focus_area = area
  373. target_text_area = one_dict["text_area"]
  374. focus_end = focus
  375. #end for
  376. try:
  377. # 识别焦点区域文字;
  378. target_text = None
  379. if list(target_text_area[0:4]) != list([-1,-1,-1,-1]):
  380. text_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
  381. image_util.saveCropPic(screen_path, text_img, target_text_area[0:4])
  382. language = target_text_area[4]
  383. ocrType = target_text_area[5]
  384. if len(target_text_area) != 6 or target_text_area[6] == {}:
  385. target_text = self.OCR.getStr(text_img, language, ocrType)
  386. else:
  387. target_text = self.OCR.getStrWithImgProcess(text_img, target_text_area[6], language, ocrType)
  388. # target_text = self.OCR.getStr(text_img, language, ocrType)
  389. print u"getFocusArea,选中区域文字:",target_text
  390. # 返回;
  391. return target_text, target_text_area, target_focus_area
  392. except Exception, e:
  393. LoggingUtil.getDebugLogger().info(
  394. pyFileName,
  395. self.className,
  396. get_current_function_name(),
  397. 'SDK:OCR识别异常'+e)
  398. return None,None,None
  399. # 圆形边框;
  400. def getBorderCircleFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide):
  401. return None,None,None
  402. # 多边形边框;
  403. def getBorderPolygonFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide):
  404. return None,None,None
  405. '''
  406. # 描述:查找焦点框(不区分形状),选中效果为焦点框背景色变化成其他颜色。focus_type=1,简述:区域着色
  407. # 参数:
  408. # icon_path:焦点框被选中时,背景变色的效果图路径。
  409. # screen_path:需要判断焦点框的电视画面截图。
  410. # uiDict:
  411. # focus_direction:
  412. # 返回值:
  413. #
  414. # '''
  415. def getColoringleFocusArea(self, icon_path, screen_path, uiDict, focus_direction):
  416. # 获取效果图的luv平均值;
  417. imgColoring = cv.imread(icon_path)
  418. luvColoring = self.CIELuv.getAverageLUV(imgColoring)
  419. # 遍历字典中所有区域截图,计算luv值;
  420. luvNoraml = None
  421. # 目标文本区域;
  422. target_text_area = None
  423. # 目标焦点区域;
  424. target_focus_area = None
  425. # 默认最大值;
  426. dLine = 10000
  427. focus_end = "focus_end"
  428. for focus in uiDict:
  429. #正常图片;
  430. # print u'name=',focus
  431. one_dict = uiDict[focus]
  432. normalArea = one_dict["focus_area"]
  433. focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
  434. image_util.saveCropPic(screen_path, focus_img, normalArea)
  435. luvNoraml = self.CIELuv.getAverageLUV(cv.imread(focus_img))
  436. if luvNoraml is None:
  437. continue
  438. # print u'normalArea=',normalArea
  439. # 计算相差;
  440. diff = self.CIELuv.getDiffUV(luvColoring, luvNoraml)
  441. if diff[2] < dLine:
  442. dLine = diff[2]
  443. target_focus_area = normalArea
  444. target_text_area = one_dict["text_area"]
  445. focus_end = focus
  446. try:
  447. # 识别焦点区域文字;
  448. target_text = None
  449. text_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
  450. if list(target_text_area[0:4]) != list([-1,-1,-1,-1]):
  451. image_util.saveCropPic(screen_path, text_img_path, target_text_area[0:4])
  452. language = target_text_area[4]
  453. ocrType = target_text_area[5]
  454. if len(target_text_area) != 6 or target_text_area[6] == {}:
  455. target_text = self.OCR.getStr(text_img_path, language, ocrType)
  456. else:
  457. target_text = self.OCR.getStrWithImgProcess(text_img_path, target_text_area[6], language, ocrType)
  458. # target_text = self.OCR.getStr(text_img_path, language, ocrType)
  459. # print u"getFocusArea,选中区域文字:",target_text
  460. # 返回文本内容、文本区域、焦点区域;
  461. return target_text, target_text_area, target_focus_area
  462. except Exception, e:
  463. LoggingUtil.getDebugLogger().info(
  464. pyFileName,
  465. self.className,
  466. get_current_function_name(),
  467. 'SDK:OCR识别异常'+e)
  468. return None,None,None
  469. '''
  470. # 描述:获取两区域的缩放率
  471. # 参数:
  472. # normalArea:区域坐标值,如[0,0,100,100]
  473. # imgZoomin:图像对象,使用cv.imread('path')获取的数组;
  474. # isZoomin:缩放率是放大率,还是缩小率, True放大率,False缩小率.
  475. #
  476. # 返回值:
  477. # 返回浮点值,缩放率scale
  478. # 示例:
  479. #
  480. # 注意:
  481. #
  482. # '''
  483. def getZoomScale(self, normalArea, imgZoomin, isZoomin = False):
  484. scale = 0.000
  485. if imgZoomin is None:
  486. return scale
  487. if isZoomin == True:
  488. scale = float(imgZoomin.shape[1]) / (normalArea[2] - normalArea[0])
  489. else:
  490. scale = float(normalArea[2] - normalArea[0]) / imgZoomin.shape[1]
  491. # 返回缩放率;
  492. return scale
  493. '''
  494. # 描述:查找焦点框,选中焦点框的效果为放大后的焦点框,focus_type=2
  495. # 参数:
  496. # icon_path:
  497. # screen_path:
  498. # uiDict:
  499. # focus_direction:
  500. #
  501. # 返回值:
  502. #
  503. #
  504. # 注意:
  505. #
  506. #
  507. # '''
  508. def getZoomFocusArea(self, icon_path, screen_path, uiDict, focus_direction):
  509. # 获取放大的图像;
  510. imgZoomin = cv.imread(icon_path)
  511. # 获取第一个正常图标区域;
  512. normalArea = uiDict.values()[1]["focus_area"]
  513. # 计算放大率;
  514. zoominScale = self.getZoomScale(normalArea, imgZoomin, True)
  515. # 遍历所有的区域,计算接近值;
  516. # 放大的矩形区域;
  517. zoominArea = None
  518. # 目标文本区域;
  519. target_text_area = None
  520. # 目标焦点区域;
  521. target_focus_area = None
  522. # 默认最大值;
  523. dLine = 10000
  524. focus_end = "focus_end"
  525. # 矩形中心坐标点;
  526. cx,cy = None, None
  527. # 矩形宽高;
  528. width, height = None, None
  529. for focus in uiDict:
  530. #正常图片;
  531. # print u'name=',focus
  532. one_dict = uiDict[focus]
  533. normalArea = one_dict["focus_area"]
  534. focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
  535. image_util.saveCropPic(screen_path, focus_img, normalArea)
  536. img_luv = self.CIELuv.getAverageLUV(cv.imread(focus_img))
  537. if img_luv is None:
  538. continue
  539. # print u'normalArea=',normalArea
  540. # 中心点位置;
  541. cx,cy = normalArea[0]+(normalArea[2] - normalArea[0])/2, normalArea[1]+(normalArea[3] - normalArea[1])/2
  542. # print u'中心点位置',cx,cy
  543. # 宽高;
  544. width, height = (normalArea[2] - normalArea[0])/2, (normalArea[3] - normalArea[1])/2
  545. # print u'宽,高',width, height
  546. # 计算放大的区域值;
  547. zoominArea = [int(cx-width*zoominScale), int(cy-height*zoominScale), int(cx+width*zoominScale), int(cy+height*zoominScale)]
  548. # print u'zoomin=',zoominArea
  549. #保存放大区域截图;
  550. focus_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_zoomin.png")
  551. image_util.saveCropPic(screen_path, focus_img_path, zoominArea)
  552. focus_img = cv.imread(focus_img_path)
  553. if focus_img is None:
  554. # print u'focus_img is None,路径:',focus_img_path
  555. LoggingUtil.getDebugLogger().info(
  556. pyFileName,
  557. self.className,
  558. get_current_function_name(),
  559. 'SDK:focus_img is None,路径:'+focus_img_path)
  560. continue
  561. imgZoomin_luv = self.CIELuv.getAverageLUV(focus_img)
  562. # 计算相差;
  563. diff = self.CIELuv.getDiffUV(imgZoomin_luv, img_luv)
  564. if diff[2] < dLine:
  565. dLine = diff[2]
  566. target_focus_area = normalArea
  567. target_text_area = one_dict["text_area"]
  568. focus_end = focus
  569. try:
  570. # 识别焦点区域文字;
  571. target_text = None
  572. text_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
  573. if list(target_text_area[0:4]) != list([-1,-1,-1,-1]):
  574. image_util.saveCropPic(screen_path, text_img_path, target_text_area[0:4])
  575. language = target_text_area[4]
  576. ocrType = target_text_area[5]
  577. if len(target_text_area) != 6 or target_text_area[6] == {}:
  578. target_text = self.OCR.getStr(text_img_path, language, ocrType)
  579. else:
  580. target_text = self.OCR.getStrWithImgProcess(text_img_path, target_text_area[6], language, ocrType)
  581. # target_text = self.OCR.getStr(text_img_path, language, ocrType)
  582. # print u"getFocusArea,选中区域文字:",target_text
  583. # 返回文本内容、文本区域、焦点区域;
  584. return target_text, target_text_area, target_focus_area
  585. except Exception, e:
  586. # print u'except',e
  587. LoggingUtil.getDebugLogger().info(
  588. pyFileName,
  589. self.className,
  590. get_current_function_name(),
  591. 'SDK:OCR识别异常'+e)
  592. return None,None,None
  593. '''
  594. # 描述:查找矩形焦点框,选中焦点框的效果为焦点背景色变成纯色。focus_type=3。
  595. # 参数:
  596. # dict:
  597. #
  598. # 返回值:
  599. # '''
  600. def autoRecognitionFocusArea(self, dict, percent = 0.9):
  601. if dict is None:
  602. print u'autoRecognitionFocusArea:dict is None'
  603. return None, None, None
  604. # 路径;
  605. imgPath = dict["shootPicPath"]
  606. zoneArea = dict["zone_area"]
  607. refFocusArea = dict["focus_area"]
  608. refTextArea = list(dict["text_area"])
  609. colorArea = dict["color_area"]
  610. imgHome = cv.imread(imgPath)
  611. if imgHome is None:
  612. print u'autoRecognitionFocusArea:imgHome is None'
  613. return None, None, None
  614. # colorArea中心点,用于取色;
  615. cx,cy = colorArea[0] + int(colorArea[2] - colorArea[0])/2, colorArea[1] + int(colorArea[3] - colorArea[1])/2
  616. # 背景色;
  617. # bgColor = imgHome[cy][cx]
  618. # call
  619. return self.__autoRecognitionFocusArea(
  620. dict["currentPicPath"],
  621. imgHome[cy][cx],
  622. zoneArea,
  623. refFocusArea,
  624. refTextArea,
  625. percent
  626. )
  627. '''
  628. # 描述:
  629. # 参数:
  630. # imgPath:要检测焦点框的图片路径。如:"D:\\Home.png"
  631. # focusBGColor:选中的焦点框的着色值,bgr格式。如:[122,122,122]
  632. # zoneArea:焦点框所在区域范围坐标,坐标点相对于imgPath图片。如(x1,y1,x2,y2):[10,10,50,50]
  633. # focusWidth:选中的焦点框宽度。
  634. # focusHeight:选中的焦点框高度。
  635. # textArea:文本区域在焦点框中的坐标。
  636. # language:语言
  637. # orcType:
  638. #
  639. # 返回值:
  640. #
  641. # '''
  642. def __autoRecognitionFocusArea(self, imgPath, focusBGColor, zoneArea, refFocusArea, refTextArea, percent = 0.9):
  643. imgHome = cv.imread(imgPath)
  644. # 水平、垂直
  645. hLine1, hLine2 = [],[]
  646. # 宽、高;
  647. focusWidth, focusHeight = refFocusArea[2]-refFocusArea[0],refFocusArea[3]-refFocusArea[1]
  648. # 是否找到
  649. bhLine1, bhLine2 = False,False
  650. if imgHome is None:
  651. # print u'__autoRecognitionFocusArea:imgHome is None'
  652. LoggingUtil.getDebugLogger().info(
  653. pyFileName,
  654. self.className,
  655. get_current_function_name(),
  656. 'SDK:图片对象空,可能路径不正确')
  657. return None, None, None
  658. else:
  659. # 第一行;
  660. for y in range(zoneArea[1], zoneArea[3]):
  661. for x in range(zoneArea[0], zoneArea[2]):
  662. if bhLine1 == False:
  663. # if list(imgHome[y][x]) == list(focusBGColor):
  664. if self.CIELuv.isColorSimilar(imgHome[y][x],focusBGColor) == True:
  665. hLine1.append([x,y])
  666. #end if
  667. #end for
  668. # 判断本线是否符合要求;
  669. if bhLine1 == False:
  670. count = len(hLine1)
  671. if float(count)/focusWidth > percent:
  672. bhLine1 = True
  673. else:
  674. hLine1 = []
  675. #end for
  676. if len(hLine1) == 0:
  677. # print u'__autoRecognitionFocusArea: 未找到第一行,长度为0'
  678. LoggingUtil.getDebugLogger().info(
  679. pyFileName,
  680. self.className,
  681. get_current_function_name(),
  682. 'SDK:未找到第一行,长度为0')
  683. return None, None, None
  684. #最后一行,倒查;
  685. zoneArea = [hLine1[0][0]-10, hLine1[0][1], hLine1[0][0] + focusWidth, hLine1[0][1] + focusHeight]
  686. print u'zoneArea=',zoneArea
  687. for y in range(zoneArea[3], zoneArea[1],-1):
  688. for x in range(zoneArea[0], zoneArea[2]):
  689. if bhLine2 == False:
  690. # if list(imgHome[y][x]) == list(focusBGColor):
  691. if self.CIELuv.isColorSimilar(imgHome[y][x],focusBGColor) == True:
  692. hLine2.append([x,y])
  693. #end if
  694. #end for
  695. # 判断本线是否符合要求;
  696. if bhLine2 == False:
  697. count = len(hLine2)
  698. if float(count)/focusWidth > percent:
  699. bhLine2 = True
  700. else:
  701. hLine2 = []
  702. #end for
  703. if len(hLine2) == 0:
  704. # print u'__autoRecognitionFocusArea: 未找到最后一行,长度为0'
  705. LoggingUtil.getDebugLogger().info(
  706. pyFileName,
  707. self.className,
  708. get_current_function_name(),
  709. 'SDK:未找到最后一行,长度为0')
  710. return None, None, None
  711. # 焦点区域;
  712. focusArea = [hLine1[0][0], hLine1[0][1], hLine2[-1][0], hLine2[-1][1]]
  713. # 查找文本区域;
  714. realTextArea = self.findCurrentFocusTextBox(focusArea, refFocusArea, refTextArea[0:4])
  715. if realTextArea is None:
  716. return None,None,focusArea
  717. # ocr识别文本;
  718. target_text = self.getCurrentFocusText(imgPath, realTextArea, refTextArea[4:])
  719. # 返回结果;
  720. return target_text, realTextArea, focusArea
  721. '''
  722. Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,同一场景下梯度值越高,图像越清晰。
  723. 以下是具体实现,这里衡量的指标是经过Sobel算子处理后的图像的平均灰度值,值越大,代表图像越清晰。
  724. :param 图片路径
  725. :return float, 值越大,代表清晰度越高
  726. '''
  727. def detSharpTenengrad(self, pic_path):
  728. return self.PQ.detSharpTenengrad(pic_path)
  729. '''
  730. 采用Laplacian梯度方法检测清晰度
  731. :param 图片路径
  732. :return float, 值越大,代表清晰度越高
  733. '''
  734. def detSharpLaplacian(self, pic_path):
  735. return self.PQ.detSharpLaplacian(pic_path)
  736. '''
  737. img:opencv图像对象,BGR颜色空间
  738. '''
  739. def detImgSharpLaplacian(self, img):
  740. return self.PQ.detImgSharpLaplacian(img)
  741. '''
  742. 采用方差(Variance)方法检测清晰度。
  743. 方差是概率论中用来考察一组离散数据和其期望(即数据的均值)之间的离散(偏离)成都的度量方法。
  744. 方差较大,表示这一组数据之间的偏差就较大,组内的数据有的较大,有的较小,分布不均衡;
  745. 方差较小,表示这一组数据之间的偏差较小,组内的数据之间分布平均,大小相近。
  746. :param 图片路径
  747. :return float, 值越大,代表清晰度越高
  748. '''
  749. def detSharpVariance(self, pic_path):
  750. return self.PQ.detSharpVariance(pic_path)
  751. '''
  752. # 描述:获取满足指定周长、面积范围的灰度二值化轮廓;
  753. # 参数:
  754. # bInside:使用内轮廓
  755. # tvShotPath:图片路径
  756. # cropArea:裁剪区域, 如:[left,top,right,bottom]
  757. # grayVal:灰度值;
  758. # maxGrayVal:最大灰度值
  759. # minPeri: 最小周长;
  760. # maxPeri: 最大周长;
  761. # minArea: 最小面积;
  762. # maxArea: 最大面积;
  763. # find_1st: 是否只返回满足条件的第一个值
  764. #
  765. # 返回:返回满足指定周长、面积范围的一个或多个轮廓坐标,坐标值如:[left,top,right,bottom];
  766. #
  767. # '''
  768. def getGrayBinaryContour(self, tvShotPath, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
  769. contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
  770. img_binary = self.getBinaryImage(tvShotPath, cropArea, grayVal, maxGrayVal)
  771. # 获取二值化图像的轮廓;
  772. area, perimeter = 0.0, 0.0
  773. if bInside == False:#外轮廓
  774. contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
  775. else:#内轮廓
  776. contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
  777. # 是否查找最大轮廓;
  778. bFindMaxContour = False
  779. if minPeri == maxPeri and minArea == maxArea:
  780. bFindMaxContour = True
  781. listBox = []
  782. maxContour = None #最大轮廓;
  783. tempArea = 0
  784. # 过滤掉不符合要求的轮廓;
  785. x, y, w, h,result = 0,0,0,0,False
  786. for cnt in contours:
  787. # 面积;
  788. area = cv.contourArea(cnt)
  789. # 周长;
  790. perimeter = cv.arcLength(cnt, True)
  791. if bFindMaxContour == True:
  792. # 获取最大轮廓;
  793. if tempArea < area:
  794. tempArea = area
  795. maxContour = cnt
  796. else:
  797. # print area,perimeter
  798. # 获取满足条件的值;
  799. if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
  800. # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
  801. x, y, w, h = cv.boundingRect(cnt)
  802. # print u'boundingRect=',x,y,w,h
  803. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
  804. # 在区域内截图判断;
  805. if cropArea is not None:
  806. x = x + cropArea[0]
  807. y = y + cropArea[1]
  808. contourRect = [x,y,x+w,y+h]
  809. # 如果只找第一个;
  810. if find_1st == True:
  811. result = True
  812. break
  813. # 多个;
  814. listBox.append(contourRect)
  815. #endif
  816. #endfor
  817. if bFindMaxContour == True:
  818. # 最大轮廓的矩形坐标;
  819. x, y, w, h = cv.boundingRect(maxContour)
  820. # 在区域内截图判断;
  821. if cropArea is not None:
  822. x = x + cropArea[0]
  823. y = y + cropArea[1]
  824. contourRect = [x,y,x+w,y+h]
  825. if bFindMaxContour == True:
  826. return contourRect
  827. # 只返回满足条件的第一个值;
  828. if find_1st == True:
  829. return contourRect if result == True else None
  830. else:
  831. return listBox
  832. #end
  833. # 区域=焦点=图片;
  834. def isNoneFocustArea(self,dict):
  835. # 解析参数;
  836. Coordinate = dict["ZoneCoordinate"]# 区域坐标;
  837. if len(dict["FocusList"]) == 0:
  838. return False
  839. FocustArea = dict["FocusList"][0]["focus_area"]
  840. if list(Coordinate) != list(FocustArea):
  841. return False
  842. return True
  843. '''
  844. # 描述:获取满足指定周长、面积范围的灰度二值化轮廓;
  845. # 参数:
  846. # bInside:使用内轮廓
  847. # tvShotPath:图片路径
  848. # cropArea:裁剪区域, 如:[left,top,right,bottom]
  849. # maxGrayVal:最大灰度值
  850. # minPeri: 最小周长;
  851. # maxPeri: 最大周长;
  852. # minArea: 最小面积;
  853. # maxArea: 最大面积;
  854. # find_1st: 是否只返回满足条件的第一个值
  855. #
  856. # 注意:当minPeri == maxPeri and minArea == maxArea时,只求最大的轮廓坐标;
  857. #
  858. # 返回:返回满足指定周长、面积范围的一个或多个轮廓坐标,坐标值如:[left,top,right,bottom];
  859. #
  860. # '''
  861. def getOTSUBinaryContour(self, tvShotPath, cropArea, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
  862. contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
  863. img_binary = self.getBinaryImage(tvShotPath, cropArea, 0, maxGrayVal, thresholdType=1)
  864. # 获取二值化图像的轮廓;
  865. area, perimeter = 0.0, 0.0
  866. if bInside == False:#外轮廓
  867. contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
  868. else:#内轮廓
  869. contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
  870. # 是否查找最大轮廓;
  871. bFindMaxContour = False
  872. if minPeri == maxPeri and minArea == maxArea:
  873. bFindMaxContour = True
  874. listBox = []
  875. maxContour = None #最大轮廓;
  876. tempArea = 0
  877. # 过滤掉不符合要求的轮廓;
  878. x, y, w, h,result = 0,0,0,0,False
  879. for cnt in contours:
  880. # 面积;
  881. area = cv.contourArea(cnt)
  882. # 周长;
  883. perimeter = cv.arcLength(cnt, True)
  884. if bFindMaxContour == True:
  885. # 获取最大轮廓;
  886. if tempArea < area:
  887. tempArea = area
  888. maxContour = cnt
  889. else:
  890. # print area,perimeter
  891. # 获取满足条件的值;
  892. if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
  893. # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
  894. x, y, w, h = cv.boundingRect(cnt)
  895. # print u'boundingRect=',x,y,w,h
  896. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
  897. # 在区域内截图判断;
  898. if cropArea is not None:
  899. x = x + cropArea[0]
  900. y = y + cropArea[1]
  901. contourRect = [x,y,x+w,y+h]
  902. # 如果只找第一个;
  903. if find_1st == True:
  904. result = True
  905. break
  906. # 多个;
  907. listBox.append(contourRect)
  908. #endif
  909. #endfor
  910. if bFindMaxContour == True:
  911. # 最大轮廓的矩形坐标;
  912. x, y, w, h = cv.boundingRect(maxContour)
  913. # 在区域内截图判断;
  914. if cropArea is not None:
  915. x = x + cropArea[0]
  916. y = y + cropArea[1]
  917. contourRect = [x,y,x+w,y+h]
  918. if bFindMaxContour == True:
  919. return contourRect
  920. # 只返回满足条件的第一个值;
  921. if find_1st == True:
  922. return contourRect if result == True else None
  923. else:
  924. return listBox
  925. #end
  926. '''
  927. # focus_type = 4;
  928. # dict:区域信息;
  929. # bInside:是否使用内轮廓;
  930. '''
  931. def getSelectFocusArea(self, dict):
  932. if self.isNoneFocustArea(dict) == True:
  933. return None,None,dict["ZoneCoordinate"]
  934. # 获取轮廓;
  935. try:
  936. bInside = dict["InsideContours"]# 是否使用内轮廓;
  937. except:
  938. bInside = False
  939. contourRect = self.getGrayBinaryContour(
  940. dict["TVShotPath"],
  941. dict["ZoneCoordinate"],
  942. dict["ZoneThreshold"],
  943. dict["ZoneMaxThreshold"],
  944. bInside,
  945. dict["RenderingsMinGirth"],
  946. dict["RenderingsMaxGirth"],
  947. dict["RenderingsMinArea"],
  948. dict["RenderingsMaxArea"])
  949. if contourRect == None:
  950. # print u'没有找到轮廓区域'
  951. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK-ERROR:没有找到轮廓区域')
  952. return None,None,None
  953. #endif
  954. # 遍历区域,找到内包含的区域;
  955. x1,y1,x2,y2,result = 0,0,0,0,False
  956. allFocus = dict["FocusList"]
  957. for focus in allFocus:
  958. x1,y1,x2,y2 = focus["focus_area"][0],focus["focus_area"][1],focus["focus_area"][2],focus["focus_area"][3]
  959. #是否在效果图的轮廓内
  960. if x1 >= contourRect[0] and y1 >= contourRect[1] and x2 <= contourRect[2] and y2 <= contourRect[3]:
  961. result = True
  962. break
  963. #endfor
  964. if result == False:
  965. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:已找到焦点轮廓,但该轮廓未包含任何focus_area在内')
  966. return None,None,None
  967. # 获取当前焦点文本;
  968. target_text = self.getCurrentFocusText(dict["TVShotPath"], focus["text_area"][0:4], focus["text_area"][4:])
  969. return target_text, focus["text_area"][0:4], focus["focus_area"]
  970. #end
  971. # 是否进入到指定UI界面;
  972. def isEntryUI(self, dict):
  973. try:
  974. bInside = dict["InsideContours"]# 是否使用内轮廓;
  975. except:
  976. bInside = False
  977. contourRect = self.getGrayBinaryContour(
  978. dict["TVShotPath"],
  979. dict["ZoneCoordinate"],
  980. dict["ZoneThreshold"],
  981. dict["ZoneMaxThreshold"],
  982. bInside,
  983. dict["RenderingsMinGirth"],
  984. dict["RenderingsMaxGirth"],
  985. dict["RenderingsMinArea"],
  986. dict["RenderingsMaxArea"])
  987. # contourRect = self.getGrayBinaryContour(dict)
  988. if contourRect == None:
  989. # print u'isEntryUI:没有找到轮廓区域'
  990. LoggingUtil.getDebugLogger().info(
  991. pyFileName,
  992. self.className,
  993. get_current_function_name(),
  994. 'SDK:没有找到轮廓区域')
  995. return False
  996. #endif
  997. return True
  998. #end
  999. '''
  1000. # 描述:获取模板匹配度最高的目标。
  1001. # 参数:
  1002. # imgObj:图像对象,由cv2.imread()读取
  1003. # templObj:图库对象,由cv2.imread()读取
  1004. # 图像对象可以是RGB格式,也可以是GRAY格式,也可以是二值化后的图像对象;
  1005. '''
  1006. def templMatch(self, imgObj, templObj, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
  1007. # 参数判断;
  1008. if imgObj is None or templObj is None:
  1009. # print u'图像对象空'
  1010. LoggingUtil.getDebugLogger().info(
  1011. pyFileName,
  1012. self.className,
  1013. get_current_function_name(),
  1014. 'SDK:图像对象空')
  1015. return None,None
  1016. if setting["colorType"] == 1: # gray模式;
  1017. imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
  1018. templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
  1019. elif setting["colorType"] == 2: # threshold模式;
  1020. # 转成灰阶图;
  1021. imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
  1022. templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
  1023. # 将灰阶转成二值化图像;
  1024. imgObj = cv.threshold(imgObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
  1025. templObj = cv.threshold(templObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
  1026. # 模板匹配;
  1027. retVal = cv.matchTemplate(imgObj, templObj, setting["method"])
  1028. # 最小最大值;
  1029. min_val, max_val, min_loc, max_loc = cv.minMaxLoc(retVal)
  1030. if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
  1031. return min_val, min_loc
  1032. else:
  1033. return max_val, max_loc
  1034. '''
  1035. # 描述:模板有多个匹配目标
  1036. # imgObj:图像对象,由cv2.imread()读取
  1037. # templObj:图库对象,由cv2.imread()读取
  1038. # 图像对象可以是RGB格式,也可以是GRAY格式,也可以是二值化后的图像对象;
  1039. # '''
  1040. def templMultiMatch(self, imgObj, templObj, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
  1041. # 参数判断;
  1042. if imgObj is None or templObj is None:
  1043. # print u'图像对象空'
  1044. LoggingUtil.getDebugLogger().info(
  1045. pyFileName,
  1046. self.className,
  1047. get_current_function_name(),
  1048. 'SDK:图像对象空')
  1049. return None
  1050. w,h = templObj.shape[::-1]
  1051. if setting["colorType"] == 1: # gray模式;
  1052. imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
  1053. templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
  1054. elif setting["colorType"] == 2: # threshold模式;
  1055. # 转成灰阶图;
  1056. imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
  1057. templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
  1058. # 将灰阶转成二值化图像;
  1059. imgObj = cv.threshold(imgObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
  1060. templObj = cv.threshold(templObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
  1061. listVal = []
  1062. # 模板匹配;
  1063. retVal = cv.matchTemplate(imgObj, templObj, setting["method"])
  1064. # 匹配的阀值
  1065. if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
  1066. matchVal = 1 - setting["matchVal"]
  1067. else:
  1068. matchVal = setting["matchVal"]
  1069. loc = np.where(retVal > matchVal)
  1070. # 如果匹配的多个目标是横排序的,过滤掉重影框;
  1071. listTup = sorted(zip(*loc[::-1]))
  1072. # 第一个矩形;
  1073. x1,y1 = listTup[-1][0]-10, listTup[-1][1]-10
  1074. x2,y2 = x1+w+20, y1+h+20
  1075. # 中心点是否在矩形内;
  1076. bInRect = False
  1077. for i in range(len(listTup)-1, -1, -1):
  1078. pt = (listTup[i][0],listTup[i][1])
  1079. x,y = pt[0]+w/2,pt[1]+h/2
  1080. if x > x1 and x < x2 and y > y1 and y < y2:
  1081. if bInRect == False:
  1082. bInRect = True
  1083. else:
  1084. listTup.pop(i)
  1085. else:
  1086. # print u'外坐标:',pt
  1087. x1,y1 = pt[0]-10,pt[1]-10
  1088. x2,y2 = x1+w+10, y1+h+10
  1089. # 如果匹配的多个目标是坚排序的,过滤掉重影框;
  1090. bInRect = False
  1091. listTup = sorted(listTup, key=lambda x:x[1])
  1092. # 第一个矩形;
  1093. x1,y1 = listTup[0][0]-10, listTup[0][1]-10
  1094. x2,y2 = x1+w+20, y1+h+20
  1095. for pt in listTup:
  1096. x,y = pt[0]+w/2,pt[1]+h/2
  1097. if x > x1 and x < x2 and y > y1 and y < y2:
  1098. if bInRect == False:
  1099. bInRect = True
  1100. listVal.append([pt[0],pt[1],pt[0]+w,pt[1]+h])
  1101. else:
  1102. # 中心点不在矩形内,切换矩形;
  1103. x1,y1 = pt[0]-10,pt[1]-10
  1104. x2,y2 = x1+w+10, y1+h+10
  1105. listVal.append([pt[0],pt[1],pt[0]+w,pt[1]+h])
  1106. # 返回多个目标的坐标值;
  1107. return listVal
  1108. '''
  1109. # 获取图库目录图像;
  1110. # dir:图库路径
  1111. # extlist:保留的后缀文件,都是图像格式。
  1112. '''
  1113. def getGallery(self, dir, extlist = ['.png','.bmp','.jpg','jpeg','.tiff']):
  1114. tmplist = []
  1115. dir = unicode(dir)
  1116. if os.path.exists(unicode(dir)) == True:
  1117. # 读取图库路径里的所有图片;
  1118. list = os.listdir(dir)
  1119. # 后缀、路径;
  1120. path, ext = "", ""
  1121. # 遍历,过滤掉文件夹和非图像文件;
  1122. for file in list:
  1123. path = os.path.join(dir, file)
  1124. if os.path.isfile(path):
  1125. ext = os.path.splitext(path)[1] # 获取文件后缀 [0]获取的是除了文件名以外的内容
  1126. if ext in extlist:
  1127. tmplist.append(path)
  1128. #end for
  1129. else:
  1130. # print u'路径异常',dir
  1131. LoggingUtil.getDebugLogger().info(
  1132. pyFileName,
  1133. self.className,
  1134. get_current_function_name(),
  1135. 'SDK:路径异常')
  1136. return tmplist
  1137. '''
  1138. # 描述:模板匹配,只返回图库中匹配值符合的一个或多个模板的相关数据。
  1139. # srceenImgPath:原图路径,即TV截图;
  1140. # ZoneCoordinate:要匹配的区域
  1141. # galleryDir: 图库路径;
  1142. # setting: 设定参数
  1143. # method -> [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]
  1144. # colorType:默认为0,表示使用RGB色彩类型, 1=使用灰度图, 2=表示二值化图像。
  1145. # thresholdVal、thresholdMaxVal:当colorType=2时,才使用。
  1146. # matchVal:tmplVal比较值。
  1147. #
  1148. # 返回值:
  1149. '''
  1150. def matchImage(self, srceenImgPath, ZoneCoordinate, galleryDir, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
  1151. # 判断文件是否存在;
  1152. tmplist = self.getGallery(galleryDir)
  1153. if len(tmplist) == 0:
  1154. # print u'图库目录空'
  1155. LoggingUtil.getDebugLogger().info(
  1156. pyFileName,
  1157. self.className,
  1158. get_current_function_name(),
  1159. 'SDK:图库目录空')
  1160. return None
  1161. srceenImgPath = unicode(srceenImgPath)
  1162. if os.path.exists(srceenImgPath) == True:
  1163. # 加载原图;
  1164. img_src = cv.imread(srceenImgPath.encode("gb18030"))
  1165. if img_src is None:
  1166. # print u'读取原图失败'
  1167. LoggingUtil.getDebugLogger().info(
  1168. pyFileName,
  1169. self.className,
  1170. get_current_function_name(),
  1171. 'SDK:读取原图失败')
  1172. return None
  1173. # 判断匹配区域是否整图,不是截图进行匹配;
  1174. bUseZoneImg = False
  1175. # sw,sh = img_src.shape[1],img_src.shape[0]#原图长宽;
  1176. if ZoneCoordinate is not None and (ZoneCoordinate[2] - ZoneCoordinate[0] != img_src.shape[1] or ZoneCoordinate[3] - ZoneCoordinate[1] != img_src.shape[0]):
  1177. bUseZoneImg = True
  1178. zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
  1179. image_util.saveCropPic(srceenImgPath,zoneImgPath,ZoneCoordinate)
  1180. img_src = cv.imread(zoneImgPath.encode("gb18030"))
  1181. if img_src is None:
  1182. # print u'截图失败'
  1183. LoggingUtil.getDebugLogger().info(
  1184. pyFileName,
  1185. self.className,
  1186. get_current_function_name(),
  1187. 'SDK:截图失败')
  1188. return None
  1189. if setting["colorType"] == 1: # gray模式;
  1190. img_src = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
  1191. elif setting["colorType"] == 2: # threshold模式;
  1192. img_src = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
  1193. # 将灰阶转成二值化图像;
  1194. img_src = cv.threshold(img_src, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
  1195. #加载要搜索的图像模板;
  1196. w,h = 0, 0
  1197. img_tmp = None
  1198. tmpVal, tmpLoc = 0,[0,0]
  1199. # listResult = []
  1200. result = {"tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
  1201. # 遍历图库,找到最合适的
  1202. for file in tmplist:
  1203. # result = {"result": False, "tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
  1204. # result = {"tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
  1205. img_tmp = cv.imread(file.encode('gb18030'))
  1206. if img_tmp is None:
  1207. # print u'模板图库相片加载失败'
  1208. LoggingUtil.getDebugLogger().info(
  1209. pyFileName,
  1210. self.className,
  1211. get_current_function_name(),
  1212. 'SDK:模板图库相片加载失败')
  1213. continue
  1214. if setting["colorType"] == 1: # gray模式;
  1215. img_tmp = cv.cvtColor(img_tmp, cv.COLOR_BGR2GRAY)
  1216. elif setting["colorType"] == 2: # threshold模式;
  1217. img_tmp = cv.cvtColor(img_tmp, cv.COLOR_BGR2GRAY)
  1218. # 将灰阶转成二值化图像;
  1219. img_tmp = cv.threshold(img_tmp, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
  1220. # 模板匹配;
  1221. w,h = img_tmp.shape[1], img_tmp.shape[0]
  1222. tmpVal, tmpLoc = self.templMatch(img_src, img_tmp, {'method':setting["method"], 'colorType':0 })
  1223. if bUseZoneImg == True:
  1224. x,y = tmpLoc[0] + ZoneCoordinate[0],tmpLoc[1] + ZoneCoordinate[1]
  1225. else:
  1226. x,y = tmpLoc[0], tmpLoc[1]
  1227. if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
  1228. # if result["tmpVal"] > tmpVal:
  1229. if tmpVal < (1-setting["matchVal"]):
  1230. if result["tmpVal"] < 1-tmpVal:
  1231. result["tmpVal"] = 1-tmpVal
  1232. result["coordinate"] = [x,y,x+w,y+h]
  1233. result["galleryFile"] = file
  1234. # break
  1235. else:
  1236. # if result["tmpVal"] < tmpVal:
  1237. if tmpVal > setting["matchVal"]:
  1238. if result["tmpVal"] < tmpVal:
  1239. result["tmpVal"] = tmpVal
  1240. result["coordinate"] = [x,y,x+w,y+h]
  1241. result["galleryFile"] = file
  1242. # break
  1243. # if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
  1244. # if result["tmpVal"] < (1 - setting["matchVal"]):
  1245. # listResult.append(result)
  1246. # else:
  1247. # if result["tmpVal"] > setting["matchVal"]:
  1248. # listResult.append(result)
  1249. # print result["result"], result["tmpVal"],result["coordinate"],result["galleryFile"].decode('gb18030'),
  1250. #end for
  1251. if result["tmpVal"] == 0:
  1252. LoggingUtil.getDebugLogger().info(
  1253. pyFileName,
  1254. self.className,
  1255. get_current_function_name(),
  1256. 'SDK:模板匹配度为0')
  1257. return None
  1258. return result
  1259. # return listResult
  1260. else:
  1261. # print u'文件不存在'
  1262. LoggingUtil.getDebugLogger().info(
  1263. pyFileName,
  1264. self.className,
  1265. get_current_function_name(),
  1266. 'SDK:文件不存在')
  1267. return None
  1268. '''
  1269. # 描述:单一模板匹配;
  1270. # 参数:
  1271. # srceenImgPath:原图路径,即TV截图;
  1272. # ZoneCoordinate:要匹配的区域
  1273. # templImgPath:单模板文件路径
  1274. # setting:设定值
  1275. # 注意:templ对象,在screenImg中可能存在多个匹配度符合要求的区域。
  1276. '''
  1277. def matchSingleImage(self, srceenImgPath, ZoneCoordinate, templImgPath, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
  1278. srceenImgPath = unicode(srceenImgPath)
  1279. templImgPath = unicode(templImgPath)
  1280. if os.path.exists(srceenImgPath) == True and os.path.exists(templImgPath) == True:
  1281. # 加载原图;
  1282. srceenImg = cv.imread(srceenImgPath.encode("gb18030"))
  1283. if srceenImg is None:
  1284. # print u'读取原图失败'
  1285. LoggingUtil.getDebugLogger().info(
  1286. pyFileName,
  1287. self.className,
  1288. get_current_function_name(),
  1289. 'SDK:读取原图失败')
  1290. return None
  1291. # 判断匹配区域是否整图,不是截图进行匹配;
  1292. bUseZoneImg = False
  1293. # sw,sh = srceenImg.shape[1],srceenImg.shape[0]#原图长宽;
  1294. if ZoneCoordinate is not None and (ZoneCoordinate[2] - ZoneCoordinate[0] != srceenImg.shape[1] or ZoneCoordinate[3] - ZoneCoordinate[1] != srceenImg.shape[0]):
  1295. bUseZoneImg = True
  1296. zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
  1297. image_util.saveCropPic(srceenImgPath,zoneImgPath,ZoneCoordinate)
  1298. srceenImg = cv.imread(zoneImgPath.encode("gb18030"))
  1299. if srceenImg is None:
  1300. # print u'截图失败'
  1301. LoggingUtil.getDebugLogger().info(
  1302. pyFileName,
  1303. self.className,
  1304. get_current_function_name(),
  1305. 'SDK:截图失败')
  1306. return None
  1307. if ZoneCoordinate is None or ZoneCoordinate is []:
  1308. ZoneCoordinate = [0, 0, srceenImg.shape[1], srceenImg.shape[0]]
  1309. #加载图像模板;
  1310. w,h = 0, 0
  1311. img_tmp = None
  1312. tmpVal, tmpLoc = 0,[0,0]
  1313. result = {"tmpVal":0, "coordinate":[0,0,0,0]}
  1314. # 对比图库
  1315. img_tmp = cv.imread(templImgPath.encode('gb18030'))
  1316. if img_tmp is None:
  1317. # print u'模板图库相片加载失败'
  1318. LoggingUtil.getDebugLogger().info(
  1319. pyFileName,
  1320. self.className,
  1321. get_current_function_name(),
  1322. 'SDK:模板图库相片加载失败')
  1323. return None
  1324. # 模板匹配;
  1325. w,h = img_tmp.shape[1], img_tmp.shape[0]
  1326. tmpVal, tmpLoc = self.templMatch(srceenImg, img_tmp, setting)
  1327. result["tmpVal"] = tmpVal
  1328. if bUseZoneImg == True:
  1329. x,y = tmpLoc[0] + ZoneCoordinate[0],tmpLoc[1] + ZoneCoordinate[1]
  1330. result["coordinate"] = [x,y,x+w,y+h]
  1331. else:
  1332. result["coordinate"] = [tmpLoc[0],tmpLoc[1],tmpLoc[0]+w,tmpLoc[1]+h]
  1333. if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
  1334. if result["tmpVal"] < (1-setting["matchVal"]):
  1335. return result
  1336. else:
  1337. if result["tmpVal"] > setting["matchVal"]:
  1338. return result
  1339. # print result["result"], result["tmpVal"]
  1340. return None
  1341. else:
  1342. # print u'文件不存在'
  1343. LoggingUtil.getDebugLogger().info(
  1344. pyFileName,
  1345. self.className,
  1346. get_current_function_name(),
  1347. 'SDK:文件不存在')
  1348. return None
  1349. '''
  1350. # 描述:获取矩形1在矩形2的方位;
  1351. # 参数:box1,box2矩形。
  1352. # 返回值:
  1353. # 左上:0 正上:1 右上:2
  1354. # 正左:3 正右:4
  1355. # 左下:5 正下:6 右下:7
  1356. # '''
  1357. def getOrientation(self, box1, box2):
  1358. # 矩形长宽;
  1359. w1,h1 = box1[2]-box1[0],box1[3]-box1[1]
  1360. # w2,h2 = box2[2]-box2[0],box2[3]-box2[1]
  1361. # 矩形中心点;
  1362. cx1,cy1 = box1[0]+w1/2,box1[1]+h1/2
  1363. # cx2,cy2 = box2[0]+w2/2,box2[1]+h2/2
  1364. # box1在box2右方;
  1365. if box1[0] >= box2[2]:
  1366. # 正右方;
  1367. if cy1 > box2[1] and cy1 < box2[3]:
  1368. return 4
  1369. # 右上方;
  1370. if cy1 < box2[1]:
  1371. return 2
  1372. # 右下方;
  1373. if cy1 > box2[3]:
  1374. return 7
  1375. # box1在box2下方;
  1376. if box1[1] >= box2[3]:
  1377. # 正下方;
  1378. if cx1 > box2[0] and cx1 < box2[2]:
  1379. return 6
  1380. # 左下方;
  1381. if cx1 < box2[0]:
  1382. return 5
  1383. # 右下方;
  1384. if cx1 > box2[2]:
  1385. return 7
  1386. # box1在box2左方;
  1387. if box1[2] <= box2[0]:
  1388. # 正左方;
  1389. if cy1 > box2[1] and cy1 < box2[3]:
  1390. return 3
  1391. # 左上方;
  1392. if cy1 < box2[1]:
  1393. return 0
  1394. # 左下方;
  1395. if cy1 > box2[3]:
  1396. return 5
  1397. # box1在box上方;
  1398. if box1[3] <= box2[1]:
  1399. # 正上方;
  1400. if cx1 > box2[0] and cx1 < box2[2]:
  1401. return 1
  1402. # 左上方;
  1403. if cx1 < box2[0]:
  1404. return 0
  1405. # 右上方;
  1406. if cx1 > box2[2]:
  1407. return 2
  1408. # box1在box2里面;
  1409. return -1
  1410. '''
  1411. # 描述:由两矩形的中心点来判别方位;
  1412. #
  1413. #
  1414. # '''
  1415. def getOrientationEx(self, box1, box2):
  1416. # 矩形长宽;
  1417. w1,h1 = box1[2]-box1[0],box1[3]-box1[1]
  1418. # w2,h2 = box2[2]-box2[0],box2[3]-box2[1]
  1419. # 矩形中心点;
  1420. cx1,cy1 = box1[0]+w1/2,box1[1]+h1/2
  1421. # cx2,cy2 = box2[0]+w2/2,box2[1]+h2/2
  1422. # box1在box2右方;
  1423. if cx1 > box2[2]:
  1424. # 正右方;
  1425. if cy1 > box2[1] and cy1 < box2[3]:
  1426. return 4
  1427. # 右上方;
  1428. if cy1 < box2[1]:
  1429. return 2
  1430. # 右下方;
  1431. if cy1 > box2[3]:
  1432. return 7
  1433. # box1在box2下方;
  1434. if cy1 > box2[3]:
  1435. # 正下方;
  1436. if cx1 > box2[0] and cx1 < box2[2]:
  1437. return 6
  1438. # 左下方;
  1439. if cx1 < box2[0]:
  1440. return 5
  1441. # 右下方;
  1442. if cx1 > box2[2]:
  1443. return 7
  1444. # box1在box2左方;
  1445. if cx1 < box2[0]:
  1446. # 正左方;
  1447. if cy1 > box2[1] and cy1 < box2[3]:
  1448. return 3
  1449. # 左上方;
  1450. if cy1 < box2[1]:
  1451. return 0
  1452. # 左下方;
  1453. if cy1 > box2[3]:
  1454. return 5
  1455. # box1在box上方;
  1456. if cy1 < box2[1]:
  1457. # 正上方;
  1458. if cx1 > box2[0] and cx1 < box2[2]:
  1459. return 1
  1460. # 左上方;
  1461. if cx1 < box2[0]:
  1462. return 0
  1463. # 右上方;
  1464. if cx1 > box2[2]:
  1465. return 2
  1466. # box1在box2里面;
  1467. return -1
  1468. '''
  1469. # 描述:生成网络数据。
  1470. # 参数:一组矩形坐标。
  1471. # 返回值:分组的矩形坐标;
  1472. # 注意:
  1473. # 给定的一组矩形,如果不是继续的一组网格矩形,无法判断缺少的矩形位置.
  1474. #
  1475. # '''
  1476. def generateGrid(self,listRects):
  1477. ret = 0
  1478. # 1、先上下排序,分层级;
  1479. for i in range(len(listRects)-1):
  1480. for j in range(0, len(listRects)-i-1):
  1481. ret = self.getOrientation(listRects[j], listRects[j+1])
  1482. if ret in [5,6,7]:
  1483. listRects[j], listRects[j+1] = listRects[j+1], listRects[j]
  1484. # 2、层级分组;
  1485. listLyaer = []
  1486. lastIndex = 0
  1487. for i in range(0,len(listRects)-1):
  1488. ret = self.getOrientation(listRects[i],listRects[i+1])
  1489. if ret in (0,1,2):
  1490. # 将同一行的所有网格添加为一个数组元素;
  1491. listLyaer.append(listRects[lastIndex:i+1])
  1492. lastIndex = i+1
  1493. # 如果有最后一个,自成一组。
  1494. # if i < len(listRects)-1:
  1495. if lastIndex < len(listRects):
  1496. listLyaer.append(listRects[lastIndex::])
  1497. # 3、分组后,再每一组排序;
  1498. for row in listLyaer:
  1499. for i in range(len(row)-1):
  1500. for j in range(0, len(row)-i-1):
  1501. ret = self.getOrientation(row[j], row[j+1])
  1502. if ret == 4:
  1503. row[j], row[j+1] = row[j+1], row[j]
  1504. # 4、返回结果。
  1505. return listLyaer
  1506. '''
  1507. # 描述:查找指定矩形在网格中的坐标;
  1508. # 参数:
  1509. # rect:要查找的盒子坐标;
  1510. # listGrid:网格
  1511. #
  1512. # 返回:行、列、是否边界(0:未到边界,1:右边界,-1:左边界;2:最后一格; -2:最前一格; 3:最后一格且该行只有一格)
  1513. #
  1514. # '''
  1515. def getGridCoordinate(self, rect, listGrid):
  1516. result,boundary = False,0
  1517. try:
  1518. grid,cell = None,None
  1519. for row in range(0,len(listGrid)):
  1520. grid = listGrid[row]
  1521. for col in range(0,len(grid)):
  1522. cell = grid[col]
  1523. # 包含在内;
  1524. if rect[0] <= cell[0] and rect[1] <= cell[1] and rect[2] >= cell[2] and rect[3] >= cell[3]:
  1525. result = True
  1526. if col == len(grid)-1:
  1527. boundary = 1 #已到右边界;
  1528. elif col == 0:
  1529. boundary = -1 #左边界;
  1530. break
  1531. if result == True:
  1532. break
  1533. except Exception, e:
  1534. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:循环异常'+str(e))
  1535. if result == True:
  1536. # 再判断,是否是最后一格;
  1537. if row == len(listGrid) - 1 and col == len(listGrid[len(listGrid) - 1]) - 1:
  1538. boundary = 2 # 最后一格;
  1539. if col == 0:#单格一行;
  1540. boundary = 3
  1541. print u'最后一格:'.encode('gbk'),row,col,boundary
  1542. # 判断是不是最前一格;
  1543. if row == 0 and col == 0:
  1544. boundary = -2
  1545. print u'最前一格:'.encode('gbk'),row,col,boundary
  1546. return row,col,boundary
  1547. else:
  1548. return -1,-1,boundary
  1549. '''
  1550. # 描述:顺(正)序查找目标焦点框
  1551. # 参数:
  1552. #
  1553. #
  1554. # '''
  1555. def sequentialSearch(self, curRow, curBoundary, remoteCtrlTimes, listRect, listGrid, dict, findFunc):
  1556. row,boundary,keyDirection = 0,0,True
  1557. curText,curTextArea,curFocusArea = None,None,None
  1558. ctrl_times = int((1 + 0.5)*remoteCtrlTimes + 2)
  1559. if curBoundary == 1:#右边界。
  1560. # 向下遥控;
  1561. self.redRat3.sendKey('down')
  1562. keyDirection = False # 向左遥控;
  1563. # 截图;
  1564. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
  1565. self.vp.takePicture(dict["TVShotPath"])
  1566. ocr_result, curFocusArea = findFunc(dict)
  1567. if ocr_result == True:
  1568. return True, curFocusArea
  1569. result = False
  1570. for i in range(ctrl_times):
  1571. if keyDirection == True:
  1572. #向右遥控;
  1573. self.redRat3.sendKey('right')
  1574. else:
  1575. #向左遥控;
  1576. self.redRat3.sendKey('left')
  1577. # 按键后,获取当前焦点;
  1578. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1579. self.vp.takePicture(dict["TVShotPath"])
  1580. ocr_result, curFocusArea = findFunc(dict)
  1581. if ocr_result == True:
  1582. result = True
  1583. break
  1584. # 不是目标焦点,继续遥控;
  1585. row,col,boundary = self.getGridCoordinate(curFocusArea,listGrid)
  1586. print u'curRow,curCol,curBoundary',row,col,boundary
  1587. if boundary == 1 or boundary == -1:
  1588. # 向下遥控;
  1589. self.redRat3.sendKey('down')
  1590. keyDirection = True if boundary == -1 else False # 向左或向右遥控;
  1591. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1592. self.vp.takePicture(dict["TVShotPath"])
  1593. ocr_result, curFocusArea = findFunc(dict)
  1594. if ocr_result == True:
  1595. result = True
  1596. break
  1597. return result, curFocusArea
  1598. '''
  1599. # 描述:反序查找目标焦点框;
  1600. # 参数:
  1601. #
  1602. #
  1603. # '''
  1604. def reverseSearch(self, curRow, curBoundary, remoteCtrlTimes, listRect, listGrid, dict, findFunc):
  1605. row,boundary,keyDirection = 0,0,False
  1606. curText,curTextArea,curFocusArea = None,None,None
  1607. ctrl_times = int((1 + 0.5)*remoteCtrlTimes + 2)
  1608. if curBoundary == -1:#左边界;
  1609. # 向下遥控;
  1610. self.redRat3.sendKey('up')
  1611. keyDirection = True # 向右遥控;
  1612. # 截图;
  1613. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
  1614. self.vp.takePicture(dict["TVShotPath"])
  1615. ocr_result, curFocusArea = findFunc(dict)
  1616. if ocr_result == True:
  1617. return True, curFocusArea
  1618. result = False
  1619. for i in range(ctrl_times):
  1620. if keyDirection == True:
  1621. #向右遥控;
  1622. self.redRat3.sendKey('right')
  1623. else:
  1624. #向左遥控;
  1625. self.redRat3.sendKey('left')
  1626. # 按键后,获取当前焦点;
  1627. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1628. self.vp.takePicture(dict["TVShotPath"])
  1629. ocr_result, curFocusArea = findFunc(dict)
  1630. if ocr_result == True:
  1631. result = True
  1632. break
  1633. # 不是目标焦点,继续遥控;
  1634. row,col,boundary = self.getGridCoordinate(curFocusArea,listGrid)
  1635. print u'curRow,curCol,curBoundary',row,col,boundary
  1636. if boundary == 1 or boundary == -1:
  1637. # 向上遥控;
  1638. self.redRat3.sendKey('up')
  1639. keyDirection = False if boundary == 1 else True # 向左或向右遥控;
  1640. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1641. self.vp.takePicture(dict["TVShotPath"])
  1642. ocr_result, curFocusArea = findFunc(dict)
  1643. if ocr_result == True:
  1644. result = True
  1645. break
  1646. return result, curFocusArea
  1647. def SearchEx(self, listGrid, dict, findFunc, searchType = True):
  1648. result = False
  1649. # 按键方向:True->右;False->左;
  1650. keyDirection = False
  1651. # 上一行行号;
  1652. lastRow = -1
  1653. # 遍历尾行的行号;
  1654. endRow = 0
  1655. #下一行按键;
  1656. nextRow = 'up'
  1657. # 以上是逆序时的预设值;
  1658. if searchType == True: # 正序查找;
  1659. endRow = len(listGrid)-1
  1660. keyDirection = True
  1661. nextRow = 'down'
  1662. while True:
  1663. # 获取当前坐标(x,y)及边界性;
  1664. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1665. self.vp.takePicture(dict["TVShotPath"])
  1666. ocr_result, curFocusArea = findFunc(dict)
  1667. if curFocusArea is None:
  1668. break
  1669. if ocr_result == True:
  1670. result = True
  1671. break
  1672. row, col, boundary = self.getGridCoordinate(curFocusArea, listGrid)
  1673. # 判断是否遍历完最后一行;
  1674. if lastRow == endRow:
  1675. # 根据按键方向判断是否到达尾行尾格;
  1676. if keyDirection and col == len(listGrid[row])-1:
  1677. break
  1678. if not keyDirection and col == 0:
  1679. break
  1680. # 记录当前行;
  1681. lastRow = row
  1682. if boundary == 0:
  1683. # 没找到,遥控下一格;
  1684. if keyDirection == True:
  1685. self.redRat3.sendKey('right')
  1686. else:
  1687. self.redRat3.sendKey('left')
  1688. elif boundary == 1:#最右
  1689. if keyDirection == True:
  1690. keyDirection = not keyDirection
  1691. self.redRat3.sendKey(nextRow)
  1692. else:
  1693. self.redRat3.sendKey('left')
  1694. elif boundary == 2:#最后一行最后一格;(该行有多格)
  1695. if searchType == True:
  1696. break#结束遍历;
  1697. else:
  1698. if keyDirection == False:
  1699. self.redRat3.sendKey('left')
  1700. elif boundary == 3:#最后一行最后一格;(该行只有一格)
  1701. if searchType == True:
  1702. break#结束遍历;
  1703. else:
  1704. keyDirection = not keyDirection
  1705. self.redRat3.sendKey(nextRow)
  1706. elif boundary == -1:#最左;
  1707. if keyDirection == False:
  1708. keyDirection = not keyDirection
  1709. self.redRat3.sendKey(nextRow)
  1710. else:
  1711. self.redRat3.sendKey('right')
  1712. elif boundary == -2:#最前一格;
  1713. if searchType == False:
  1714. break#结束遍历;
  1715. else:
  1716. if keyDirection == True:
  1717. self.redRat3.sendKey('right')
  1718. return result
  1719. '''
  1720. # 描述:到达网格的第一个盒子焦点;
  1721. #
  1722. #
  1723. #
  1724. # '''
  1725. def reach1stBox(self, listGrid, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
  1726. result = True
  1727. while True:
  1728. # 当前焦点位置;
  1729. curFocusBox = self.findCurrentFocusBoxInType04(cropArea,grayVal,maxGrayVal,bInside,minPeri,maxPeri,minArea,maxArea)[0]
  1730. if curFocusBox is None:
  1731. result = False
  1732. break
  1733. # 获取行列坐标标;
  1734. row, col, boundary = self.getGridCoordinate(curFocusBox, listGrid)
  1735. if row == 0 and col == 0:# and boundary == -1:
  1736. result = True
  1737. break
  1738. if row > 0:
  1739. self.redRat3.sendKey('up')
  1740. elif row == 0 and boundary != -2:
  1741. self.redRat3.sendKey('left', col, 0.5)
  1742. return result
  1743. '''
  1744. # 描述:查找当前焦点框信息;
  1745. #
  1746. #
  1747. #
  1748. # '''
  1749. def findCurrentFocusFrameInType6(self, dict):
  1750. # 1、获取当前轮廓;
  1751. try:
  1752. bInside = dict["InsideContours"]# 是否使用内轮廓;
  1753. except:
  1754. bInside = False
  1755. contourRect = self.getGrayBinaryContour(
  1756. dict["TVShotPath"],
  1757. dict["ZoneCoordinate"],
  1758. dict["ZoneThreshold"],
  1759. dict["ZoneMaxThreshold"],
  1760. bInside,
  1761. dict["RenderingsMinGirth"],
  1762. dict["RenderingsMaxGirth"],
  1763. dict["RenderingsMinArea"],
  1764. dict["RenderingsMaxArea"])
  1765. if contourRect == None:
  1766. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域')
  1767. return False,None
  1768. #endif
  1769. # 计算文本位置;
  1770. realTextBox = self.findCurrentFocusTextBox(contourRect, dict["RenderingsArea"], dict["RenderingsTextArea"][0:4])
  1771. if realTextBox is None:
  1772. return False,contourRect
  1773. ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1774. image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, realTextBox)
  1775. result, realTextBox = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, dict['RenderingsTextArea'][4],dict['RenderingsTextArea'][5],dict['RenderingsTextArea'][6],dict['RenderingsTextArea'][7])
  1776. return result, contourRect
  1777. '''
  1778. # 描述:查找目标焦点框之type6.
  1779. # 参数:
  1780. #
  1781. # 返回值:成功找到焦点返回True,否则返回False;
  1782. #
  1783. # '''
  1784. def findTargetFocusFrameInType6(self,dict):
  1785. # 1、计算出几行几列;
  1786. listRect = []
  1787. for focus in dict["FocusList"]:
  1788. listRect.append(focus["focus_area"])
  1789. # 生成网格;
  1790. listGrid = self.generateGrid(listRect)
  1791. # 正反序查找;
  1792. result = self.SearchEx(listGrid, dict, self.findCurrentFocusFrameInType6, True)
  1793. if result == False:
  1794. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:逆序查找')
  1795. result = self.SearchEx(listGrid, dict, self.findCurrentFocusFrameInType6, False)
  1796. return result
  1797. #end
  1798. '''
  1799. # 描述:图像形态学处理。
  1800. #
  1801. #
  1802. # '''
  1803. def preProcess(self, grayImage,setting):
  1804. # 1、sobel算子,x方向求梯度;
  1805. sobel = cv.Sobel(grayImage, cv.CV_8U, 1, 0, ksize = 3)
  1806. # 2、二值化;
  1807. if 'gray' in setting:
  1808. binary = cv.threshold(sobel, setting['gray'], 255, cv.THRESH_BINARY+cv.THRESH_OTSU)[1]
  1809. else:
  1810. binary = cv.threshold(sobel, 0, 255, cv.THRESH_BINARY+cv.THRESH_OTSU)[1]
  1811. binary_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1812. cv.imwrite(binary_path, binary)
  1813. # 第一次;
  1814. first,second,third = None,None,None
  1815. ksize = (1,1)
  1816. ksize = tuple(setting["first"][1:3])
  1817. kernel = cv.getStructuringElement(setting["first"][3], ksize)
  1818. if setting["first"][0] == "dilate":
  1819. first = cv.dilate(binary, kernel, setting["first"][4])
  1820. else:
  1821. first = cv.erode(binary, kernel, setting["first"][4])
  1822. # 第二次;
  1823. second = None
  1824. if 'second' in setting and len(setting["second"]) != 0:
  1825. ksize = tuple(setting["second"][1:3])
  1826. kernel = cv.getStructuringElement(setting["second"][3], ksize)
  1827. if setting["second"][0] == "dilate":
  1828. second = cv.dilate(first, kernel, setting["second"][4])
  1829. else:
  1830. second = cv.erode(first, kernel, setting["second"][4])
  1831. else:
  1832. second = first
  1833. # 第三次;
  1834. third = None
  1835. if "third" in setting and len(setting["third"]) != 0:
  1836. ksize = tuple(setting["third"][1:3])
  1837. kernel = cv.getStructuringElement(setting["third"][3], ksize)
  1838. if setting["third"][0] == "dilate":
  1839. third = cv.dilate(second, kernel, setting["third"][4])
  1840. else:
  1841. third = cv.erode(second, kernel, setting["third"][4])
  1842. else:
  1843. third = second
  1844. # 7. 存储中间图片
  1845. tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1846. cv.imwrite(tmpdir, binary)
  1847. tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"first_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1848. cv.imwrite(tmpdir, first)
  1849. if second is not None:
  1850. tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"second_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1851. cv.imwrite(tmpdir, second)
  1852. if third is not None:
  1853. tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"third_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1854. cv.imwrite(tmpdir, third)
  1855. return third if third is not None else second
  1856. '''
  1857. # 描述:查找文字区域
  1858. #
  1859. #
  1860. #
  1861. # '''
  1862. def findTextRegion(self, img, minGirth, maxGirth, minArea, maxArea):
  1863. # 1. 查找轮廓
  1864. contours = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)[1]
  1865. if len(contours) == 0:
  1866. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找文本到轮廓')
  1867. return None
  1868. # 2. 过滤
  1869. listCnt = []
  1870. maxBox, maxCnt = 0, None
  1871. for i in range(len(contours)):
  1872. cnt = contours[i]
  1873. # 计算该轮廓的面积
  1874. area = cv.contourArea(cnt)
  1875. perimeter = cv.arcLength(cnt, True)
  1876. # 符合结果的,保存在list中;
  1877. if area > minArea and area < maxArea and perimeter > minGirth and perimeter < maxGirth:
  1878. listCnt.append(cnt)
  1879. if len(listCnt) == 0:
  1880. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到符合目标的轮廓')
  1881. return None
  1882. # 只保留面积最大的;
  1883. for i in range(len(listCnt)):
  1884. cnt = listCnt[i]
  1885. # 计算该轮廓的面积
  1886. area = cv.contourArea(cnt)
  1887. if(maxBox < area):
  1888. maxBox = area
  1889. maxCnt = cnt
  1890. # 找到最小的矩形,该矩形可能有方向
  1891. rect = cv.minAreaRect(maxCnt)
  1892. print ("rect is: ",rect)
  1893. # box是四个点的坐标
  1894. box = cv.boxPoints(rect)
  1895. box = np.int0(box)
  1896. # print 'box',box
  1897. if rect[2] > -45.0:
  1898. rect = [box[1][0],box[1][1],box[3][0],box[3][1],]
  1899. else:#-90
  1900. rect = [box[2][0],box[2][1],box[0][0],box[0][1],]
  1901. return rect
  1902. '''
  1903. # 描述:查找当前焦点的文本轮廓。
  1904. # 参数:
  1905. #
  1906. #
  1907. # 返回值:返回文本内容。
  1908. #
  1909. # '''
  1910. def findTextcontourRect(self, tvShotPath, dict):
  1911. coordinate = dict["ZoneCoordinate"]
  1912. thresholdVal = dict["ZoneThreshold"]
  1913. maxThresholdVal = dict["ZoneMaxThreshold"]
  1914. try:
  1915. maxGirth = dict["RenderingsMaxGirth"]# 效果图周长上限;(最大值)
  1916. minGirth = dict["RenderingsMinGirth"]# 效果图周长下限;(最小值)
  1917. maxArea = dict["RenderingsMaxArea"]# 效果图面积上限;(最大值)
  1918. minArea = dict["RenderingsMinArea"]# 效果图面积下限;(最小值)
  1919. except:
  1920. maxGirth = 1000000# 效果图周长上限;(最大值)
  1921. minGirth = 10# 效果图周长下限;(最小值)
  1922. maxArea = 1000000# 效果图面积上限;(最大值)
  1923. minArea = 100# 效果图面积下限;(最小值)
  1924. ocrtype = dict["ocrtype"]
  1925. language = dict["language"]
  1926. setting = dict["setting"]
  1927. # 按区域截图,在区域中查找轮廓。
  1928. zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1929. image_util.saveCropPic(tvShotPath, zoneImgPath, coordinate)
  1930. imgTV = cv.imread(zoneImgPath.encode("gb18030"))
  1931. # 判断对象有效性;
  1932. if imgTV is None:
  1933. # print u'效果图对象空,可能文件路径无效'
  1934. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:效果图对象空,可能文件路径无效')
  1935. return None,None
  1936. # 将截图转成灰阶;
  1937. imgGray = cv.cvtColor(imgTV, cv.COLOR_BGR2GRAY)
  1938. # 高斯模糊;
  1939. imgGray = cv.GaussianBlur(imgGray, (3, 3), 0)
  1940. # 将灰阶转成二值化图像;
  1941. thresh = cv.threshold(imgGray, thresholdVal, maxThresholdVal, cv.THRESH_BINARY)[1]
  1942. tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"thresh_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1943. cv.imwrite(tmpdir, thresh)
  1944. # 图像形态学处理;
  1945. dilation = self.preProcess(thresh,setting)
  1946. # 查找和筛选文字区域
  1947. text_area = self.findTextRegion(dilation,minGirth,maxGirth,minArea,maxArea)
  1948. if text_area is None:
  1949. return None,None
  1950. ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1951. image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, text_area)
  1952. ocr_result, ocr_text = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, language, ocrtype, dict['ocrImgInfo'],dict['ignoreChar'])
  1953. return ocr_result, ocr_text
  1954. '''
  1955. # 描述:查找当前焦点框之type7.
  1956. # 参数:
  1957. #
  1958. # 返回:
  1959. #
  1960. # 注意:只适用于左右遥控按键.
  1961. #
  1962. # '''
  1963. def findCurrentFocusFrameInType7(self,dict):
  1964. if 'setting' not in dict:
  1965. dict["setting"] = {
  1966. "first":["erode",2,2,0,1], # 第一次操作是腐蚀还是膨胀,内核大小,内核形状,操作次数
  1967. "second":["dilate",30,9,0,1], # 内核大小,内核形状,操作次数
  1968. "third":["dilate",24,10,0,2] # 内核大小,内核形状,操作次数
  1969. }
  1970. # 先正序查找;
  1971. result = False
  1972. target_text,last_text = '',''
  1973. sendTimes = dict["SendTimes"]
  1974. changeTimes = -1
  1975. while sendTimes > 0:
  1976. # 电视截图;
  1977. TVShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1978. self.vp.takePicture(TVShotPath)
  1979. # 查找文本轮廓
  1980. ocr_result, target_text = self.findTextcontourRect(TVShotPath, dict)
  1981. if ocr_result == True:
  1982. result = True
  1983. break
  1984. # 发送右按钮;
  1985. self.redRat3.sendKey('right')
  1986. sendTimes -= 1
  1987. if last_text != target_text:
  1988. last_text = target_text
  1989. changeTimes += 1
  1990. # 正序未找到,逆序查找;
  1991. if result == False:
  1992. #向左发送changeTimes+1次,回到原点;
  1993. keys = ['left']*(changeTimes+1)
  1994. self.redRat3.sendKeys(keys)
  1995. sendTimes = dict["SendTimes"]
  1996. while sendTimes > 0:
  1997. # 电视截图;
  1998. TVShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  1999. self.vp.takePicture(TVShotPath)
  2000. ocr_result, target_text = self.findTextcontourRect(TVShotPath, dict)
  2001. if ocr_result == True:
  2002. result = True
  2003. break
  2004. # 发送左按钮;
  2005. self.redRat3.sendKey('left')
  2006. sendTimes -= 1
  2007. return result
  2008. def getHSVBinaryContour(self, tvShotPath, cropArea, hsvVal, maxHsvVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
  2009. contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
  2010. # 判断文件是否存在;
  2011. if os.path.exists(tvShotPath) == False:
  2012. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath)
  2013. return None
  2014. # 打开图片;
  2015. img_tvShot = cv.imread(tvShotPath)
  2016. if img_tvShot is None:
  2017. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空')
  2018. return None
  2019. # 只保留区域部分;
  2020. try:
  2021. img_crop = np.array(img_tvShot[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]])
  2022. except:
  2023. img_crop = img_tvShot
  2024. cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "crop_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_crop)
  2025. # 判断对象有效性;
  2026. if img_crop is None:
  2027. # print u'效果图对象空,可能文件路径无效'
  2028. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:区域截图对象空,可能文件路径无效')
  2029. return None
  2030. # 将截图转成灰阶;
  2031. img_hsv = cv.cvtColor(img_crop, cv.COLOR_BGR2HSV)
  2032. # 根据参数的阀值得到特定颜色区域;
  2033. img_binary = cv.inRange(img_hsv, tuple(hsvVal),tuple(maxHsvVal))
  2034. # 图像形态学内核对象;
  2035. kernerl = cv.getStructuringElement(cv.MORPH_RECT, ksize=(3,3))
  2036. # 开操作,去噪点;
  2037. img_binary = cv.morphologyEx(img_binary,cv.MORPH_OPEN,kernerl)
  2038. # 闭操作,连通域;
  2039. img_binary = cv.morphologyEx(img_binary,cv.MORPH_CLOSE,kernerl)
  2040. # 获取二值化图像的轮廓;
  2041. area, perimeter = 0.0, 0.0
  2042. if bInside == False:#外轮廓
  2043. contours, hierarchy = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1:3]
  2044. else:#内轮廓
  2045. contours, hierarchy = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1:3]
  2046. listBox = []
  2047. # 过滤掉不符合要求的轮廓;
  2048. x, y, w, h,result = 0,0,0,0,False
  2049. for cnt in contours:
  2050. # 面积;
  2051. area = cv.contourArea(cnt)
  2052. # 周长;
  2053. perimeter = cv.arcLength(cnt, True)
  2054. # 获取满足条件的值;
  2055. if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
  2056. # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
  2057. x, y, w, h = cv.boundingRect(cnt)
  2058. # print u'boundingRect=',x,y,w,h
  2059. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
  2060. # 判断矩形是否包含在区域内;
  2061. # 在区域内截图判断;
  2062. x = x + cropArea[0]
  2063. y = y + cropArea[1]
  2064. contourRect = [x,y,x+w,y+h]
  2065. # 如果只找第一个;
  2066. if find_1st == True:
  2067. result = True
  2068. break
  2069. # 多个;
  2070. listBox.append(contourRect)
  2071. #endif
  2072. #endfor
  2073. # 只返回满足条件的第一个值;
  2074. if find_1st == True:
  2075. return contourRect if result == True else None
  2076. else:
  2077. return listBox
  2078. #end
  2079. def findCurrentFocusFrameInType8(self,dict):
  2080. # 1、获取当前轮廓;
  2081. try:
  2082. bInside = dict["InsideContours"]# 是否使用内轮廓;
  2083. except:
  2084. bInside = False
  2085. contourRect = self.getHSVBinaryContour(
  2086. dict["TVShotPath"],
  2087. dict["ZoneCoordinate"],
  2088. dict["ZoneThreshold"],
  2089. dict["ZoneMaxThreshold"],
  2090. bInside,
  2091. dict["RenderingsMinGirth"],
  2092. dict["RenderingsMaxGirth"],
  2093. dict["RenderingsMinArea"],
  2094. dict["RenderingsMaxArea"])
  2095. if contourRect == None:
  2096. # print u'没有找到轮廓区域'
  2097. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域')
  2098. return False,None
  2099. #endif
  2100. # 计算文本位置;
  2101. realTextBox = self.findCurrentFocusTextBox(contourRect, dict["RenderingsArea"], dict["RenderingsTextArea"][0:4])
  2102. if realTextBox is None:
  2103. return False,contourRect
  2104. # # 获取文本内容;
  2105. ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2106. image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, realTextBox)
  2107. result, realTextBox = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, dict['RenderingsTextArea'][4],dict['RenderingsTextArea'][5],dict['RenderingsTextArea'][6],dict['RenderingsTextArea'][7])
  2108. return result, contourRect
  2109. def findTargetFocusFrameInType8(self,dict):
  2110. # 1、查找当前焦点框;
  2111. dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2112. self.vp.takePicture(dict["TVShotPath"])
  2113. ocr_result, curFocusArea = self.findCurrentFocusFrameInType8(dict)
  2114. if curFocusArea is None:
  2115. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), u'SDK-Type8:当前焦点框空')
  2116. return False
  2117. result = False
  2118. # 2、当前焦点是否目标焦点;
  2119. if ocr_result == True:
  2120. # curText, curTextArea, curFocusArea
  2121. return True
  2122. else:
  2123. # 3、计算出几行几列;
  2124. listRect = []
  2125. for focus in dict["FocusList"]:
  2126. listRect.append(focus["focus_area"])
  2127. listGrid = self.generateGrid(listRect)
  2128. # 4、当前焦点在哪个位置;
  2129. curRow,curCol,curBoundary = self.getGridCoordinate(curFocusArea,listGrid)
  2130. # 5、分正序查找和逆序查找两次查找。
  2131. keyDirection = True # True表示向右发送遥控,False表示向左发送遥控;
  2132. # 将多维数组转一维;
  2133. listRect = list(chain(*listGrid))
  2134. # 行数,列数;
  2135. rows,cols = len(listGrid),len(listGrid[0])
  2136. # 剩余遥控次数;
  2137. ctrl_times = len(listRect) - (curRow+1)*cols + curCol
  2138. # 5.1、顺序查找;
  2139. result, curFocusArea = self.sequentialSearch(curRow, curBoundary, ctrl_times, listRect, listGrid, dict, self.findCurrentFocusFrameInType8)
  2140. # 5.2、逆序查找;
  2141. if result == False:
  2142. curRow,curCol,curBoundary = self.getGridCoordinate(curFocusArea,listGrid)
  2143. ctrl_times = curRow*cols + curCol
  2144. result, curFocusArea = self.reverseSearch(curRow, curBoundary, ctrl_times, listRect, listGrid, dict, self.findCurrentFocusFrameInType8)
  2145. return result
  2146. #end
  2147. '''
  2148. # 描述:计算出两矩形的正包含的相交百分比。(以相交面积/最小矩形面积)
  2149. #
  2150. # 返回值:正包含的相交百分比
  2151. # '''
  2152. # 两矩形是否相交,返回相交百分比;
  2153. def bbOverlap(self, box1, box2):
  2154. # 外正四方;
  2155. if box1[0] > box2[2]:
  2156. return 0.0
  2157. if box1[1] > box2[3]:
  2158. return 0.0
  2159. if box1[2] < box2[0]:
  2160. return 0.0
  2161. if box1[3] < box2[1]:
  2162. return 0.0
  2163. # 计算相交长宽;
  2164. colInt = abs(min(box1[2], box2[2]) - max(box1[0], box2[0]))
  2165. rowInt = abs(min(box1[3], box2[3]) - max(box1[1], box2[1]))
  2166. # 计算相交面积
  2167. overlapArea = colInt * rowInt
  2168. # 各自面积
  2169. area1 = (box1[2]-box1[0])*(box1[3]-box1[1])
  2170. area2 = (box2[2]-box2[0])*(box2[3]-box2[1])
  2171. # 是否全包含;
  2172. # if overlapArea == area1 or overlapArea == area2:
  2173. # return 1.0
  2174. # 返回相交比;
  2175. # return float(overlapArea) / (area1 + area2 - overlapArea)
  2176. return float(overlapArea) / min(area1, area2)#以最小面积的占用比,作为相交比,可计算出全包含。
  2177. '''
  2178. # 描述:返回目标焦点在当前选中焦点的哪个方向。
  2179. # 参数:
  2180. # focusType:查找当前焦点的方式。
  2181. # focusDict:查找当前焦点的参数。
  2182. # matchDicts:查找目标焦点的模板匹配参数。
  2183. # 返回值:
  2184. #
  2185. # '''
  2186. def findTargetFocusDirection(self, focusType, focusDict, matchDict):
  2187. # 查找当前焦点;
  2188. curFocusText, curFocusTextArea, curFocusArea = None,None,None
  2189. if focusType == 0:
  2190. curFocusText, curFocusTextArea, curFocusArea = self.getBorderRectangleFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],focusDict["RenderingsBorderWide"],)
  2191. elif focusType == 1:
  2192. curFocusText, curFocusTextArea, curFocusArea = self.getColoringleFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],)
  2193. elif focusType == 2:
  2194. curFocusText, curFocusTextArea, curFocusArea = self.getZoomFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],)
  2195. elif focusType == 3:
  2196. curFocusText, curFocusTextArea, curFocusArea = self.autoRecognitionFocusArea(focusDict,)
  2197. elif focusType == 4:
  2198. curFocusText, curFocusTextArea, curFocusArea = self.getSelectFocusArea(focusDict,)
  2199. elif focusType == 5:
  2200. if focusDict["setting"] == None:
  2201. curFocusArea = self.matchSingleImage(focusDict["TVShotPath"],focusDict["ZoneCoordinate"],focusDict["TemplatePath"])
  2202. else:
  2203. curFocusArea = self.matchSingleImage(focusDict["TVShotPath"],focusDict["ZoneCoordinate"],focusDict["TemplatePath"],focusDict["setting"])
  2204. # 获取模板匹配结果,找到目标焦点区域坐标;
  2205. if matchDict["setting"] == None:
  2206. tagFocusArea = self.matchSingleImage(matchDict["TVShotPath"],matchDict["ZoneCoordinate"],matchDict["TemplatePath"])
  2207. else:
  2208. tagFocusArea = self.matchSingleImage(matchDict["TVShotPath"],matchDict["ZoneCoordinate"],matchDict["TemplatePath"],matchDict["setting"])
  2209. # 计算两矩形是否相交,返回正包含相交比.
  2210. overlap = self.bbOverlap(curFocusArea, tagFocusArea["coordinate"])
  2211. # 返回结果;
  2212. return curFocusText, curFocusTextArea, curFocusArea, tagFocusArea["coordinate"], overlap
  2213. '''
  2214. # 描述:焦点识别类型00:选中的焦点框边框外镶了一层有色边框,通过抓取这层边框颜色来识别焦点框。
  2215. #
  2216. #
  2217. # '''
  2218. def findCurrentFocusBoxInType00(self, dict):
  2219. # 当前电视机截图;
  2220. tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2221. self.vp.takePicture(tvShotPath)
  2222. # 选中焦点框的效果图;
  2223. effectPath = dict["EffectPath"]
  2224. # 焦点所在区域;
  2225. zoneBox = dict["ZoneCoordinate"]
  2226. # 效果图外边框厚度;
  2227. borderWidth = dict["BorderWidth"]
  2228. # 获取选中焦点框效果图的外边框的luv平均值.
  2229. effectImg = cv.imread(effectPath)
  2230. effectLuv = cv.cvtColor(effectImg, cv.COLOR_BGR2Luv)
  2231. # 将外边框拆分成4个矩形。
  2232. height,width = effectLuv.shape[0:2]
  2233. leftBox = [0, 0, borderWidth, height]
  2234. rightBox = [width - borderWidth, 0, width, height]
  2235. topBox = [borderWidth, 0, width - borderWidth, borderWidth]
  2236. bottomBox = [borderWidth, height - borderWidth, width - borderWidth, height]
  2237. # luv平均值;
  2238. avgluv = self.CIELuv.getMAverageLUV(effectLuv, [leftBox, rightBox, topBox, bottomBox])
  2239. '''
  2240. # 描述:焦点识别类型03:选中的焦点框背景色变成其他颜色,通过计算背景色获取上下两边直线来确定识别焦点框。
  2241. #
  2242. #
  2243. # '''
  2244. def findCurrentFocusBoxInType03(self, zoneBox, effectBox, effectColorBox, effectPath, matchVal):
  2245. # 当前电视机截图;
  2246. tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2247. self.vp.takePicture(tvShotPath)
  2248. # 判断文件是否存在;
  2249. if os.path.exists(tvShotPath) == False:
  2250. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath)
  2251. return None
  2252. if os.path.exists(effectPath) == False:
  2253. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+effectPath)
  2254. return None
  2255. # 判断区域是否在有效范围内;
  2256. # 略过……
  2257. # 打开图片;
  2258. img_tvShot = cv.imread(tvShotPath)
  2259. img_effect = cv.imread(effectPath)
  2260. if img_tvShot is None or img_effect is None:
  2261. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空')
  2262. return None
  2263. # effectColorBox中心点,用于取色;
  2264. cx,cy = effectColorBox[0] + int(effectColorBox[2] - effectColorBox[0])/2, effectColorBox[1] + int(effectColorBox[3] - effectColorBox[1])/2
  2265. focusBGColor = img_effect[cx,cy]
  2266. # 效果图宽、高
  2267. effectWidth, effectHeight = effectBox[2]-effectBox[0],effectBox[3]-effectBox[1]
  2268. # 水平、垂直
  2269. hLine1, hLine2 = [],[]
  2270. # 是否找到
  2271. bhLine1, bhLine2 = False,False
  2272. # 查找第一行与effectColor相近的颜色线条;
  2273. for y in range(zoneBox[1], zoneBox[3]):
  2274. for x in range(zoneBox[0], zoneBox[2]):
  2275. if bhLine1 == False:
  2276. if self.CIELuv.isColorSimilar(img_tvShot[y][x],focusBGColor) == True:
  2277. hLine1.append([x,y])
  2278. #end if
  2279. #end for
  2280. # 判断本线是否符合要求;
  2281. if bhLine1 == False:
  2282. count = len(hLine1)
  2283. if float(count)/effectWidth > matchVal:
  2284. bhLine1 = True
  2285. else:
  2286. hLine1 = []
  2287. #end for
  2288. if len(hLine1) == 0:
  2289. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未找到第一行,长度为0')
  2290. return None
  2291. # 查找最后一行与effectColor相近的颜色线条;
  2292. zoneBox = [hLine1[0][0]-10, hLine1[0][1], hLine1[0][0] + effectWidth, hLine1[0][1] + effectHeight]
  2293. for y in range(zoneBox[3], zoneBox[1],-1):
  2294. for x in range(zoneBox[0], zoneBox[2]):
  2295. if bhLine2 == False:
  2296. if self.CIELuv.isColorSimilar(img_tvShot[y][x],focusBGColor) == True:
  2297. hLine2.append([x,y])
  2298. #end if
  2299. #end for
  2300. # 判断本线是否符合要求;
  2301. if bhLine2 == False:
  2302. count = len(hLine2)
  2303. if float(count)/effectWidth > matchVal:
  2304. bhLine2 = True
  2305. else:
  2306. hLine2 = []
  2307. #end for
  2308. if len(hLine2) == 0:
  2309. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未找到最后一行,长度为0')
  2310. return None
  2311. # 焦点区域;
  2312. focusBox = [hLine1[0][0], hLine1[0][1], hLine2[-1][0], hLine2[-1][1]]
  2313. # 返回结果;
  2314. # return True,focusBox
  2315. return focusBox
  2316. '''
  2317. # 描述:焦点识别类型04:选中的焦点框背景色变成其他颜色,通过二值化获取轮廓面积、周长来识别焦点框。
  2318. #
  2319. #
  2320. # '''
  2321. def findCurrentFocusBoxInType04(self, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
  2322. contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
  2323. tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2324. self.vp.takePicture(tvShotPath)
  2325. # 获取轮廓;
  2326. contourRect = self.getGrayBinaryContour(tvShotPath, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea, find_1st)
  2327. # 返回结果;
  2328. if find_1st == True:
  2329. return contourRect,tvShotPath
  2330. else:
  2331. return contourRect,tvShotPath
  2332. '''
  2333. # 描述:焦点识别类型05:选中的焦点框明显与未选中时有差别,通过【多组图像的模板匹配】方式来识别焦点框。
  2334. #
  2335. #
  2336. # '''
  2337. def findCurrentFocusBoxInType05(self, zoneBox, focusDir):
  2338. tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2339. self.vp.takePicture(tvShotPath)
  2340. val = self.matchImage(tvShotPath, zoneBox, focusDir)
  2341. if val is None:
  2342. return None
  2343. return val["coordinate"]#,val["galleryFile"]
  2344. '''
  2345. # 描述:焦点识别类型06:选中的焦点框前景高亮着色,但背景是可变的,通过二值化获取大概轮廓,再膨胀腐蚀操作后得到最大轮廓来识别焦点框。
  2346. #
  2347. #
  2348. # '''
  2349. 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]}):
  2350. tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2351. self.vp.takePicture(tvShotPath)
  2352. # 按区域截图,在区域中查找轮廓。
  2353. img_tvShot = cv.imread(tvShotPath)
  2354. if img_tvShot is None:
  2355. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径可能无效'+tvShotPath)
  2356. return None
  2357. img_zone = image_util.cutImage(img_tvShot, zoneBox)
  2358. if img_zone is None:
  2359. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:裁剪图片失败')
  2360. return None
  2361. # 将截图转成灰阶;
  2362. imgGray = cv.cvtColor(img_zone, cv.COLOR_BGR2GRAY)
  2363. # 高斯模糊;
  2364. imgGray = cv.GaussianBlur(imgGray, (3, 3), 0)
  2365. # 将灰阶转成二值化图像;
  2366. thresh = cv.threshold(imgGray, threshold, maxThreshold, cv.THRESH_BINARY)[1]
  2367. tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"thresh_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2368. cv.imwrite(tmpdir, thresh)
  2369. # 图像形态学处理;
  2370. dilation = self.preProcess(thresh,setting)
  2371. # 查找和筛选文字区域
  2372. text_area = self.findTextRegion(dilation,minPeri,maxPeri,minArea,maxArea)
  2373. if text_area is None:
  2374. return None
  2375. return text_area
  2376. '''
  2377. # 描述:焦点识别类型07:选中的焦点框前景色变成其他颜色,但是背景可变,通过计颜色阀值来计算出轮廓,再通过周长面积过滤后来调识别焦点框。
  2378. #
  2379. #
  2380. # '''
  2381. def findCurrentFocusBoxInType07(self, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea):
  2382. tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2383. self.vp.takePicture(tvShotPath)
  2384. return self.getHSVBinaryContour(tvShotPath,zoneBox,threshold,maxThreshold,bInside,minPeri,maxPeri,minArea,maxArea)
  2385. # 在多区域中找当前焦点;
  2386. def findCurrentFocusBox(self, listdict):
  2387. if len(listdict) == 0:
  2388. return None,None
  2389. # 遍历区域,查找当前焦点所在区域;
  2390. zType = -1
  2391. zData = []
  2392. zName = ''
  2393. bInside = False
  2394. # 当前焦点框坐标;
  2395. focus_box = None
  2396. for zone in listdict:
  2397. zName = zone["zoneName"]
  2398. zType = zone["zoneType"]
  2399. zData = zone["zoneData"]
  2400. # print '打印->'.encode('gbk'),zName.encode('gbk'),zType
  2401. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前遍历Zone:'+zName+',类型:'+str(zType))
  2402. try:
  2403. bInside = zData["InsideContours"]# 是否使用内轮廓;
  2404. except:
  2405. bInside = False
  2406. if zType == 3:
  2407. focus_box = self.findCurrentFocusBoxInType03(zData["ZoneCoordinate"],zData["EffectArea"],zData["EffectColorBox"],zData["EffectPath"],zData["MatchVal"])
  2408. elif zType == 4:
  2409. focus_box = self.findCurrentFocusBoxInType04(
  2410. zData["ZoneCoordinate"],
  2411. zData["ZoneThreshold"],
  2412. zData["ZoneMaxThreshold"],
  2413. bInside,
  2414. zData["RenderingsMinGirth"],
  2415. zData["RenderingsMaxGirth"],
  2416. zData["RenderingsMinArea"],
  2417. zData["RenderingsMaxArea"])[0]
  2418. elif zType == 5:
  2419. focus_box = self.findCurrentFocusBoxInType05(
  2420. zData["ZoneCoordinate"],
  2421. zData["focusPicDir"])
  2422. elif zType == 6:
  2423. focus_box = self.findCurrentFocusBoxInType04(
  2424. zData["ZoneCoordinate"],
  2425. zData["ZoneThreshold"],
  2426. zData["ZoneMaxThreshold"],
  2427. bInside,
  2428. zData["RenderingsMinGirth"],
  2429. zData["RenderingsMaxGirth"],
  2430. zData["RenderingsMinArea"],
  2431. zData["RenderingsMaxArea"])[0]
  2432. elif zType == 7:
  2433. setting = None
  2434. if 'setting' not in zone:
  2435. setting = {"first":["erode",2,2,0,1], "second":["dilate",30,9,0,1],"third":["dilate",24,10,0,2]}
  2436. else:
  2437. setting = zone["setting"]
  2438. focus_box = self.findCurrentFocusBoxInType06(
  2439. zData["ZoneCoordinate"],
  2440. zData["ZoneThreshold"],
  2441. zData["ZoneMaxThreshold"],
  2442. zData["RenderingsMinGirth"],
  2443. zData["RenderingsMaxGirth"],
  2444. zData["RenderingsMinArea"],
  2445. zData["RenderingsMaxArea"],
  2446. setting)
  2447. elif zType == 8:
  2448. focus_box = self.findCurrentFocusBoxInType07(
  2449. zData["ZoneCoordinate"],
  2450. zData["ZoneThreshold"],
  2451. zData["ZoneMaxThreshold"],
  2452. bInside,
  2453. zData["RenderingsMinGirth"],
  2454. zData["RenderingsMaxGirth"],
  2455. zData["RenderingsMinArea"],
  2456. zData["RenderingsMaxArea"])
  2457. else:
  2458. # print u'未使用的焦点类型'
  2459. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未使用的焦点类型:'+str(zType))
  2460. # 找到当前焦点框;
  2461. if focus_box is not None:
  2462. break
  2463. # end-for
  2464. if focus_box is None:
  2465. return None,None
  2466. return focus_box,zName
  2467. '''
  2468. # 描述:查找当前焦点文本框;
  2469. # 参数:
  2470. # 'realFocusBox':[x1,y1,x2,y2], #实际焦点框,坐标系必须是原图左上角;
  2471. # 'refTextBox':[x1,y1,x2,y2], #参考文本框,坐标系必须是原图左上角;
  2472. # 'refFocusBox':[x1,y1,x2,y2] #参考焦点框,坐标系必须是原图左上角;
  2473. # 注意:x1,y1,x2,y2分别为矩形左上角(x1,y1)和右下角(x2,y2)
  2474. #
  2475. # 返回:当前焦点文本框坐标
  2476. #
  2477. # '''
  2478. def findCurrentFocusTextBox(self, realFocusBox, refFocusBox, refTextBox):
  2479. return self.getObjAbsoluteBox(realFocusBox, refFocusBox, refTextBox)
  2480. '''
  2481. # 描述:根据参照物的相对坐标,来获取对象的绝对坐标值;
  2482. # 参数:
  2483. # 参照对象实际坐标
  2484. # 参照对象参照坐标
  2485. # 目标对象参照坐标
  2486. #
  2487. # '''
  2488. def getObjAbsoluteBox(self, refObjRealBox, refObjRefBox, tagObjRefBox):
  2489. if refObjRealBox is None:
  2490. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:焦点框None,无法计算文本框坐标')
  2491. return None
  2492. # 目标区域的宽、高;
  2493. txWidth, txHeight = tagObjRefBox[2]-tagObjRefBox[0], tagObjRefBox[3]-tagObjRefBox[1]
  2494. # 以参照物作为新的坐标系,计算目标区域的相对原点(left,top)
  2495. left,top = tagObjRefBox[0]-refObjRefBox[0], tagObjRefBox[1]-refObjRefBox[1]
  2496. # 目标对象的绝对原点坐标;
  2497. left += refObjRealBox[0]
  2498. top += refObjRealBox[1]
  2499. # 返回目标对象绝对坐标;
  2500. return [left, top, left+txWidth, top+txHeight]
  2501. '''
  2502. # 描述:获取焦点文本内容;
  2503. # 参数:
  2504. # img_or_path:可以是cv2.imread()对象,也可以是图片路径;
  2505. # textBox:[x1,y1,x2,y2]
  2506. # orcInfo: ['language',10001,{}] # language, ocrType, imgProcParams
  2507. # '''
  2508. def getCurrentFocusText(self, img_or_path, textBox, orcInfo):
  2509. if textBox is None or list(textBox) == list([-1,-1,-1,-1]):
  2510. return None
  2511. tx_img = None
  2512. # 保存截图;
  2513. if type(img_or_path) == type(list()):#如果传递的对象;
  2514. tx_img = image_util.cutImage(img_or_path, textBox)
  2515. elif type(img_or_path) == type(str()):#如果传递的是路径;
  2516. imgObj = cv.imread(img_or_path)
  2517. tx_img = image_util.cutMat(imgObj, textBox)
  2518. if tx_img is None:
  2519. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:裁剪图片失败')
  2520. return ''
  2521. tx_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"fotx_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2522. cv.imwrite(tx_img_path, tx_img)
  2523. # orc识别;
  2524. text = ''
  2525. if len(orcInfo) >= 3 and orcInfo[2] <> {}:
  2526. text = self.OCR.getStrWithImgProcess(tx_img_path, orcInfo[2], orcInfo[0], orcInfo[1]).replace(' ','')
  2527. else:
  2528. text = self.OCR.getStr(tx_img_path, orcInfo[0], orcInfo[1]).replace(' ','')
  2529. # 返回文本;
  2530. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:文字识别结果:'+str(text))
  2531. return text
  2532. '''
  2533. # 描述:到达目标区域,成功到达返回True,否则False。
  2534. #
  2535. #
  2536. # '''
  2537. def reachTargetZone(self, dictZoneInfo):
  2538. if len(dictZoneInfo) == 0:
  2539. return None
  2540. # 目标区域名称;
  2541. destZoneName = dictZoneInfo["destZoneName"]
  2542. # 排序dict;
  2543. listdict = dictZoneInfo["zoneList"]
  2544. for i in range(len(listdict)-1):
  2545. for j in range(0, len(listdict)-i-1):
  2546. if listdict[j]["zone_priority"] > listdict[j+1]["zone_priority"]:
  2547. listdict[j], listdict[j+1] = listdict[j+1], listdict[j]
  2548. # 遍历区域,查找当前焦点所在区域;
  2549. focus_box,zName = self.findCurrentFocusBox(listdict)
  2550. # 没有找到当前焦点框;
  2551. if focus_box is None:
  2552. return False
  2553. # 当前焦点在目标区域;
  2554. if str(zName) == destZoneName:
  2555. return True
  2556. # 找到目标区域坐标;
  2557. zoneBox = []
  2558. for zone in dictZoneInfo["zoneList"]:
  2559. if destZoneName == str(zone["zoneName"]):
  2560. if zone["zoneType"] == 3:
  2561. zoneBox = zone["zoneData"]["zone_area"]
  2562. else:
  2563. zoneBox = zone["zoneData"]["ZoneCoordinate"]
  2564. break
  2565. times = 100
  2566. while str(zName) != str(destZoneName):
  2567. # 查找目标区域;
  2568. retVal = self.getOrientationEx(focus_box, zoneBox)
  2569. if retVal == 0: # 左上方;
  2570. self.redRat3.sendKey('down')
  2571. self.redRat3.sendKey('right')
  2572. elif retVal == 1: #正上方;
  2573. self.redRat3.sendKey('down')
  2574. elif retVal == 2: #右上方;
  2575. self.redRat3.sendKey('down')
  2576. self.redRat3.sendKey('left')
  2577. elif retVal == 3: #正左方;
  2578. self.redRat3.sendKey('right')
  2579. elif retVal == 4: #正右方;
  2580. self.redRat3.sendKey('left')
  2581. elif retVal == 5: #左下方;
  2582. self.redRat3.sendKey('up')
  2583. self.redRat3.sendKey('right')
  2584. elif retVal == 6: #正下方;
  2585. self.redRat3.sendKey('up')
  2586. elif retVal == 7: #右下方
  2587. self.redRat3.sendKey('up')
  2588. self.redRat3.sendKey('left')
  2589. times -= 1
  2590. if times <= 0:
  2591. break
  2592. focus_box, zName = self.findCurrentFocusBox(listdict)
  2593. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前焦点区域:'+str(zName)+',目标Zone:'+str(destZoneName))
  2594. if focus_box is None:
  2595. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:failed=>当前焦点区域空')
  2596. break
  2597. # print '结果:'.encode('gbk'),zName.encode('gbk'),destZoneName.encode('gbk')
  2598. return True if zName == destZoneName else False
  2599. '''
  2600. # 描述:获取灰阶二值化图像对象;
  2601. # 参数:
  2602. # tvShotPath 图片路径;
  2603. # cropArea 裁剪区域
  2604. # grayVal、maxGrayVal 灰度值和最大灰度值
  2605. # thresholdType 阀值类型,0=全局阀值,1=otsu阀值,2=自适应阀值;
  2606. #
  2607. # '''
  2608. def getBinaryImage(self, tvShotPath, cropArea, grayVal, maxGrayVal, thresholdType = 0):
  2609. # 判断文件是否存在;
  2610. if os.path.exists(tvShotPath) == False:
  2611. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath)
  2612. return None
  2613. # 打开图片;
  2614. img_tvShot = cv.imread(tvShotPath)
  2615. if img_tvShot is None:
  2616. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空')
  2617. return None
  2618. # 只保留区域部分;
  2619. try:
  2620. img_crop = np.array(img_tvShot[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]])
  2621. except:
  2622. img_crop = img_tvShot
  2623. cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "crop_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_crop)
  2624. # 判断对象有效性;
  2625. if img_crop is None:
  2626. # print u'效果图对象空,可能文件路径无效'
  2627. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:区域截图对象空,可能文件路径无效')
  2628. return None
  2629. # 将截图转成灰阶;
  2630. img_gray = cv.cvtColor(img_crop, cv.COLOR_BGR2GRAY)
  2631. # 高斯模糊;
  2632. img_gray = cv.GaussianBlur(img_gray, (3, 3), 0)
  2633. # 将灰阶转成二值化图像;
  2634. img_binary = None
  2635. if thresholdType == 0:
  2636. img_binary = cv.threshold(img_gray, grayVal, maxGrayVal, cv.THRESH_BINARY)[1]
  2637. elif thresholdType == 1:
  2638. img_binary = cv.threshold(img_gray, 0, maxGrayVal, cv.THRESH_BINARY+cv.THRESH_OTSU)[1]
  2639. elif thresholdType == 2:
  2640. img_binary = cv.medianBlur(img_gray, (3,3))
  2641. # bloksize、C两参数,暂时固定为3、4
  2642. img_binary = cv.adaptiveThreshold(img_binary, maxGrayVal, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 3, 4.5)
  2643. cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_binary)
  2644. return img_binary
  2645. '''
  2646. # 描述:获取滑块轮廓;
  2647. #
  2648. #
  2649. #
  2650. #
  2651. # '''
  2652. def getSilderCountor(self, tvShotPath, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, morphology, find_1st = True):
  2653. # 获取灰阶二值化图片对象;
  2654. img_binary = self.getBinaryImage(tvShotPath, cropArea, grayVal, maxGrayVal)
  2655. if img_binary is None:
  2656. print u'binary is none'
  2657. return None
  2658. # 对二值化图像进行腐蚀或膨胀操作--形态学;
  2659. for val in morphology:
  2660. ksize = tuple(val[1:3])
  2661. kernel = cv.getStructuringElement(val[3], ksize)
  2662. # if val[0] == 0: # 腐蚀;
  2663. if val[0] == "erode":
  2664. img_binary = cv.erode(img_binary, kernel, val[4])
  2665. else: # 膨胀;
  2666. img_binary = cv.dilate(img_binary, kernel, val[4])
  2667. morphology_path = LoggingUtil.getCaseRunLogDirPath() + "\\morphology_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png"
  2668. cv.imwrite(morphology_path, img_binary)
  2669. # 对形态学结果进行轮廓获取;
  2670. # 获取二值化图像的轮廓;
  2671. area, perimeter = 0.0, 0.0
  2672. if bInside == False:#外轮廓
  2673. contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
  2674. else:#内轮廓
  2675. contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
  2676. listBox = []
  2677. # 过滤掉不符合要求的轮廓;
  2678. x, y, w, h,result = 0,0,0,0,False
  2679. for cnt in contours:
  2680. # 面积;
  2681. area = cv.contourArea(cnt)
  2682. # 周长;
  2683. perimeter = cv.arcLength(cnt, True)
  2684. # 获取满足条件的值;
  2685. if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
  2686. # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
  2687. x, y, w, h = cv.boundingRect(cnt)
  2688. # print u'boundingRect=',x,y,w,h
  2689. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
  2690. # 在区域内截图判断;
  2691. x = x + cropArea[0]
  2692. y = y + cropArea[1]
  2693. contourRect = [x,y,x+w,y+h]
  2694. # 如果只找第一个;
  2695. if find_1st == True:
  2696. result = True
  2697. break
  2698. # 多个;
  2699. listBox.append(contourRect)
  2700. #endif
  2701. #endfor
  2702. # 只返回满足条件的第一个值;
  2703. if find_1st == True:
  2704. return contourRect if result == True else None
  2705. else:
  2706. return listBox
  2707. #end
  2708. '''
  2709. # 描述:用Type9到达指定的焦点框
  2710. # 参数:
  2711. # targetText: 目标焦点框文本
  2712. # focusArg: Type9焦点识别参数
  2713. # 返回:成功到达焦点框返回True,失败返回False
  2714. #
  2715. # '''
  2716. def reachTargetFocusBoxInType9(self, targetText, focusArg):
  2717. # 参数;
  2718. loop = focusArg['loop']
  2719. cropArea = focusArg['ZoneCoordinate']
  2720. grayVal = focusArg['ZoneThreshold']
  2721. maxGrayVal = focusArg['ZoneMaxThreshold']
  2722. bInside = focusArg['InsideContours']
  2723. minPeri = focusArg['focusBoxMinPeri']
  2724. maxPeri = focusArg['focusBoxMaxPeri']
  2725. minArea = focusArg['focusBoxMinArea']
  2726. maxArea = focusArg['focusBoxMaxArea']
  2727. refFocusBox = focusArg['RenderingsArea']
  2728. refTextBox = focusArg['RenderingsTextArea']
  2729. ctrldir = focusArg['FocusDirection']#只有左右=0, 上下=1,混合=2。
  2730. # 兼容focus_direction;
  2731. if ctrldir == 1 or ctrldir == 2:
  2732. ctrldir = 1
  2733. elif ctrldir == 3 or ctrldir == 4:
  2734. ctrldir = 0
  2735. keys = [['left','right'],['up','down']]
  2736. def reachType9TargetFocusBox(ctrlkey):
  2737. keyCount = 0
  2738. ret = False
  2739. lastText = ''
  2740. firstText = None
  2741. sametimes = 0
  2742. while True:
  2743. # 获取焦点框;
  2744. curFocusBox,tvShotPath = self.findCurrentFocusBoxInType04(cropArea,grayVal,maxGrayVal,bInside,minPeri,maxPeri,minArea,maxArea)
  2745. if curFocusBox is None:
  2746. LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:找不到当前焦点')
  2747. break
  2748. # 获取文本框;
  2749. textBox = self.getObjAbsoluteBox(curFocusBox, refFocusBox, refTextBox)
  2750. ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
  2751. image_util.saveCropPic(tvShotPath, ocrImgPath, textBox)
  2752. ocr_result, text = self.OCR.findPicStr(targetText, ocrImgPath, refTextBox[4], refTextBox[5], refTextBox[6],refTextBox[7])
  2753. if ocr_result == True:
  2754. ret = True
  2755. break
  2756. # 记录第一次文本;
  2757. if firstText is None:
  2758. firstText = text
  2759. else:
  2760. # 循环列表,同时回到起始位置;
  2761. if loop == True and firstText == text:
  2762. break
  2763. # 记录最后次文本;
  2764. if lastText != text:
  2765. lastText = text
  2766. if loop == False:
  2767. keyCount += 1
  2768. else:
  2769. if loop == False:
  2770. sametimes += 1
  2771. # 如果相同次数超过n次,认为到达底部;
  2772. if loop == False and sametimes >= 2:
  2773. break
  2774. # 遥控;
  2775. self.redRat3.sendKey(ctrlkey)
  2776. # 返回结果;
  2777. return ret,keyCount
  2778. # 正序查找;
  2779. result,keyCount = reachType9TargetFocusBox(keys[ctrldir][1])
  2780. if result == False:
  2781. if loop == True:
  2782. return False
  2783. # 到达起点,再反向开始查找;
  2784. self.redRat3.sendKey(keys[ctrldir][0], keyCount)
  2785. result = reachType9TargetFocusBox(keys[ctrldir][0])[0]
  2786. # 返回文本数组;
  2787. return result
  2788. def getRGBBinaryImage(self, src_pic, cropArea, bgr, offset=20):
  2789. # 读取图片;
  2790. img = cv.imread(src_pic)
  2791. if img is None:
  2792. return None,None
  2793. # 如果截图区域空,使用原图大小;
  2794. if cropArea is not None or cropArea.__len__() == 0:
  2795. cropArea = [0, 0, img.shape[1], img.shape[0]]
  2796. img_crop = None
  2797. # 只保留区域部分;
  2798. try:
  2799. img_crop = np.array(img[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]])
  2800. except:
  2801. img_crop = img
  2802. imgArr = np.asarray(img_crop).astype(np.int16)
  2803. bgrArr = np.asarray(bgr).astype(np.int16)
  2804. # 矩阵相减;
  2805. subImg = imgArr - bgrArr
  2806. cv.imwrite(r'E:\\subImg.png', subImg)
  2807. # img_RGB = cv.cvtColor(img_crop,cv.COLOR_BGR2RGB)
  2808. # imgArr_RGB = np.asarray(img_RGB).astype(np.int16)
  2809. # rgb = (bgr[2],bgr[1], bgr[0])
  2810. # rgbArr = np.asarray(rgb).astype(np.int16)
  2811. # subImg_RGB = imgArr_RGB - rgb
  2812. # subImgsum = (subImg + subImg_RGB)
  2813. # cv.imwrite(r'E:\\subImgsum.png', subImgsum)
  2814. # np.where(condition,x,y)满足条件值改为x,否则为y;
  2815. # subImg_wh1 = np.where((subImg<-offset)|(subImg>offset), np.uint8(255), np.uint8(0))
  2816. subImg_wh1 = np.where((subImg > -offset) & (subImg < offset), np.uint8(0), np.uint8(255))
  2817. cv.imwrite(r'E:\\subImg_wh1.png', subImg_wh1)
  2818. # 将图处转成灰阶
  2819. img_gray = cv.cvtColor(subImg_wh1, cv.COLOR_BGR2GRAY)
  2820. # 反转灰度值,以便适合查找轮廓;
  2821. img_gray = np.where((img_gray < -offset) | (img_gray > offset), np.uint8(0), np.uint8(255))
  2822. # 高斯模糊;
  2823. img_gray = cv.GaussianBlur(img_gray, (3, 5), 0)
  2824. # 输出结果图片;
  2825. cv.imwrite(r'E:\\img_gray.png', img_gray)
  2826. # 使用灰度图求得二值化图;
  2827. # thresh = cv.threshold(img_gray, 1, 255, cv.THRESH_OTSU | cv.THRESH_BINARY)[1]
  2828. thresh = cv.adaptiveThreshold(img_gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY_INV,15,10)
  2829. cv.imwrite(r'E:\\img_thresh.png', thresh)
  2830. # 返回二值化图;
  2831. return thresh, cropArea
  2832. '''
  2833. 在传入的图片上,查找指定bgr颜色框矩形区域,返回矩形区域的坐标
  2834. :param picPath:图片路径
  2835. :param bgr:颜色值
  2836. :return rectArea:矩形区域 和区域图片
  2837. '''
  2838. def findCurrentFoucsByColor(self, picPath, cropArea, bgr, offset=20, minPeri=0, maxPeri=0, minArea=0, maxArea=0, morphology = []):
  2839. # 获取rgb二值图;
  2840. img_binary, cropArea = self.getRGBBinaryImage(picPath, cropArea, bgr, offset)
  2841. if img_binary is None:
  2842. return False,None
  2843. # 对二值化图像进行腐蚀或膨胀操作--形态学;
  2844. for val in morphology:
  2845. ksize = tuple(val[1:3])
  2846. kernel = cv.getStructuringElement(val[3], ksize)
  2847. # 腐蚀;
  2848. if val[0] == "erode":
  2849. img_binary = cv.erode(img_binary, kernel, val[4])
  2850. else: # 膨胀;
  2851. img_binary = cv.dilate(img_binary, kernel, val[4])
  2852. morphology_path = LoggingUtil.getCaseRunLogDirPath() + "\\morphology_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png"
  2853. cv.imwrite(morphology_path, img_binary)
  2854. # 查找符合要求的外轮廓;
  2855. # opencv3开始,有3个返回值:img, countours, hierarchy
  2856. contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
  2857. # 是否查找最大轮廓;
  2858. bFindMaxContour = False
  2859. # 如果周长、面积都一样,只返回最大轮廓;
  2860. if minPeri == maxPeri and minArea == maxArea:
  2861. bFindMaxContour = True
  2862. contourRect = []
  2863. maxContour = None # 最大轮廓;
  2864. tempArea = 0
  2865. # 过滤掉不符合要求的轮廓;
  2866. x, y, w, h, result = 0, 0, 0, 0, False
  2867. for cnt in contours:
  2868. # 面积;
  2869. area = cv.contourArea(cnt)
  2870. # 周长;
  2871. perimeter = cv.arcLength(cnt, True)
  2872. # print "area=%ld, perimeter=%ld"%(area, perimeter)
  2873. if bFindMaxContour == True:
  2874. # 获取最大轮廓;
  2875. if tempArea < area:
  2876. tempArea = area
  2877. maxContour = cnt
  2878. else:
  2879. # print area,perimeter
  2880. # 获取满足条件的值;
  2881. if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
  2882. # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
  2883. x, y, w, h = cv.boundingRect(cnt)
  2884. # 在区域内截图判断;
  2885. if cropArea is not None:
  2886. x = x + cropArea[0]
  2887. y = y + cropArea[1]
  2888. contourRect = [x, y, x + w, y + h]
  2889. result = True
  2890. break
  2891. # endif
  2892. # endfor
  2893. if bFindMaxContour == True and maxContour is not None:
  2894. result = True
  2895. # 最大轮廓的矩形坐标;
  2896. x, y, w, h = cv.boundingRect(maxContour)
  2897. # 在区域内截图判断;
  2898. if cropArea is not None:
  2899. x = x + cropArea[0]
  2900. y = y + cropArea[1]
  2901. contourRect = [x, y, x + w, y + h]
  2902. # 只返回结果、轮廓坐标;
  2903. return result, contourRect
  2904. # 文本颜色作为焦点色:获取文本色所有轮廓,计算最左、最右、最上、最下四坐标;
  2905. def findFramelessFocusByColor(self, src_pic, cropArea, bgr, offset=20):
  2906. # 获取rgb二值图;
  2907. thresh, cropArea = self.getRGBBinaryImage(src_pic, cropArea, bgr, offset)
  2908. if thresh is None:
  2909. return False,[]
  2910. # 查找符合要求的外轮廓;
  2911. # opencv3开始,有3个返回值:img, countours, hierarchy
  2912. contours = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
  2913. # contourRect = []
  2914. left, right, top, bottom = 100000,0,100000,0
  2915. # 遍历轮廓;
  2916. for cnt in contours:
  2917. # 面积;
  2918. # area = cv.contourArea(cnt)
  2919. # 周长;
  2920. # perimeter = cv.arcLength(cnt, True)
  2921. # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
  2922. x, y, w, h = cv.boundingRect(cnt)
  2923. # 在区域内截图判断;
  2924. if cropArea is not None:
  2925. x = x + cropArea[0]
  2926. y = y + cropArea[1]
  2927. if left > x:
  2928. left = x
  2929. if top > y:
  2930. top = y
  2931. if right < x + w:
  2932. right = x + w
  2933. if bottom < y + h:
  2934. bottom = y + h
  2935. #endfor;
  2936. # 是否有效矩形;
  2937. if left >= right or top >= bottom:
  2938. return False, []
  2939. # 返回结果;
  2940. return True, [left-3, top-3, right+3, bottom+3]
  2941. def testFocusArea():
  2942. 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]}
  2943. focusDict = {
  2944. "focus_tv": '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}',
  2945. "focus_av": '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}',
  2946. "focus_hdmi1": '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}',
  2947. "focus_hdmi2": '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}',
  2948. "focus_usb": '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}'
  2949. }
  2950. icon = r"res\icon.png"
  2951. focus_tv = r"res\focus_tv_focus.png"
  2952. focus_av = r"res\focus_av_focus.png"
  2953. focus_hdmi1 = r"res\focus_hdmi1_focus.png"
  2954. focus_hdmi2 = r"res\focus_hdmi2_focus.png"
  2955. focus_usb = r"res\focus_usb_focus.png"
  2956. picture = r"res\source.jpg"
  2957. fDetect = FeatureDetect()
  2958. print fDetect.getFocusArea(icon, picture, focusDict, 10)
  2959. def testRect():
  2960. fDetect = FeatureDetect()
  2961. # fDetect.findRectByFile(r"D:\temp-pic\parentlock\channel_lock1.png", "parentLock")
  2962. fDetect.findRectByFile(r"D:\temp-pic\parentlock\channel_lock2.png", "parentLock")
  2963. # fDetect.findRectByFile(r"D:\temp-pic\parentlock\source_lock1.png", "parentLock")
  2964. if __name__ == "__main__":
  2965. fdetect = FeatureDetect()
  2966. if 0:
  2967. focusArg = {
  2968. "TVShotPath": '',
  2969. "ZoneCoordinate": [1204, 0, 1920, 1080],
  2970. "InsideContours": False,
  2971. "ZoneThreshold": 150,
  2972. "ZoneMaxThreshold": 255,
  2973. # 选中焦点框的周长、面积;
  2974. "focusBoxMinPeri": 50,
  2975. "focusBoxMaxPeri": 90,
  2976. "focusBoxMinArea": 300,
  2977. "focusBoxMaxArea": 500,
  2978. # 文本最左边;
  2979. "textleft":1260,
  2980. # 文本最右边;
  2981. "textright":1860,
  2982. # 文本在焦点滑块上方多高;
  2983. "aboveHeight":80,
  2984. "aboveMargin":30,
  2985. "morphology":[
  2986. ["erode",6,6,0,2],
  2987. ["dilate",6,6,0,2]
  2988. ]
  2989. }
  2990. listText = []
  2991. for i in range(99):
  2992. path = r'Z:\Picture\V27N\CusPicMode\%d.png'%i
  2993. img = cv.imread(path)
  2994. if img is not None:
  2995. countor = fdetect.getSilderCountor(
  2996. path,
  2997. focusArg['ZoneCoordinate'],
  2998. focusArg['ZoneThreshold'],
  2999. focusArg['ZoneMaxThreshold'],
  3000. focusArg['InsideContours'],
  3001. focusArg['focusBoxMinPeri'],
  3002. focusArg['focusBoxMaxPeri'],
  3003. focusArg['focusBoxMinArea'],
  3004. focusArg['focusBoxMaxArea'],
  3005. focusArg['morphology'],
  3006. )
  3007. width,height = countor[2]-countor[0],countor[3]-countor[1]
  3008. # 文本坐标;
  3009. txbox = [focusArg['textleft'], countor[1] - focusArg['aboveHeight'], focusArg['textright'], countor[1] - focusArg['aboveMargin']]
  3010. text = fdetect.getCurrentFocusText(path, txbox, ["chineseprc+english",10000])
  3011. listText.append(text)
  3012. print listText
  3013. # findFocusTextByColor测试;
  3014. if 0:
  3015. src_pic = r'C:\Users\jianfeng1.wang\Pictures\Saved Pictures\img.png'
  3016. icon_pic = r'C:\Users\jianfeng1.wang\Pictures\Saved Pictures\icon.png'
  3017. iconImg = cv.imread(icon_pic)
  3018. # 获取小图平均bgr值;
  3019. rgbColor = RGBColor()
  3020. bgr = rgbColor.getAvgBGR(iconImg)
  3021. print fdetect.findFocusTextByColor(src_pic, [], bgr)