tv_detect.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. # -*- coding:utf-8 -*-
  2. import os, sys, time
  3. from ssat_sdk.device_manage.capturecard_manager import CCardManager
  4. from pic_tool import ImageCMP
  5. from sound_tool import AudioIdentify
  6. from picture.feature_detect import *
  7. from ssat_sdk.picture.color_space import CIEluvCaculator
  8. from ssat_sdk.sat_environment import *
  9. from ocr_convert import OCRConvert
  10. from ssat_sdk.picture import image_util
  11. from ssat_sdk.pic_tool import ImageCMP
  12. import cv2 as cv
  13. import thread
  14. import threading
  15. from ssat_sdk.utils import LoggingUtil
  16. from ssat_sdk.TvDetect.colorbar_detect import ColorBarDetect
  17. pyFileName = os.path.split(__file__)[-1]
  18. def get_current_function_name():
  19. return inspect.stack()[1][3]
  20. # 模板匹配全局对象;
  21. g_threadMonitor = None
  22. g_monitorResult = {}
  23. g_stopThread = False
  24. class TvDetect():
  25. def __init__(self):
  26. self.audioChecker = AudioIdentify()
  27. self.vp = VideoCapture()
  28. self.featureDetect = FeatureDetect()
  29. self.CIECaculator = CIEluvCaculator()
  30. self.OCR = OCRConvert()
  31. self.className = self.__class__.__name__
  32. self.imageUtil = image_util.ImageUtil()
  33. self.imgCMP = ImageCMP()
  34. self.colorBarDetect = ColorBarDetect()
  35. '''
  36. 检测电视锁屏。自行截取电视画面后检索
  37. :return hasLockStr:是否有锁屏提示字符
  38. :return isBlack:是否全是黑屏,没有画面
  39. :return hasLockStr:是否有锁屏提示字符
  40. '''
  41. def detectLockScreen(self):
  42. # lockPic = r"D:\temp-pic\parentlock\channel_lock2.png"
  43. # lockPic = r"D:\temp-pic\parentlock\channel_lock1.png"
  44. # lockPic = r"D:\temp-pic\parentlock\source_lock1.png"
  45. lockPic = os.path.join(getSATTmpDIR(), "lock_screen.png")
  46. self.vp.takePicture(lockPic)
  47. return self.detectLockPic(lockPic)
  48. '''
  49. 检测电视锁屏.根据提供的图片进行检测
  50. :param lockPic:电视画面的图片路径
  51. :return hasLockStr:是否有锁屏提示字符
  52. :return isBlack:是否全是黑屏,没有画面
  53. :return hasLockStr:是否有锁屏提示字符
  54. '''
  55. def detectLockPic(self, lockPic):
  56. maxDiff = 5
  57. blackLuv = (11,96,136)
  58. hasLockStr = False
  59. isBlack = True
  60. lockRect = self.featureDetect.findRectByFile(lockPic, "parentLock")
  61. print lockRect
  62. rectPic = os.path.join(getSATTmpDIR(), "rect.png")
  63. image_util.saveCropPic(lockPic, rectPic, lockRect)
  64. info = self.OCR.getStr(rectPic, "english", 2)
  65. print "detectLockPic,str:",info
  66. if info.lower().find("locked") > 0:
  67. hasLockStr = True
  68. retImgList = image_util.saveRetCropPic(lockPic, lockRect)
  69. for index in range(retImgList.__len__()):
  70. # cv.imshow(str(index),retImgList[index])
  71. imgLuv = cv.cvtColor(retImgList[index], cv.COLOR_BGR2Luv)
  72. l, u, v = self.CIECaculator.getAverageLUV(imgLuv)
  73. diffLevel = self.CIECaculator.getDiffuvLevel(blackLuv, (l, u, v))
  74. if (diffLevel > maxDiff):
  75. isBlack = False
  76. # cv.waitKey(0)
  77. # cv.destroyAllWindows()
  78. # print hasLockStr, isBlack, lockPic
  79. return hasLockStr, isBlack, lockPic
  80. '''
  81. 传入电视的重现率检测画面(22293/4 pattern304 或者TG39 mark画面),按像素计算重现率。
  82. :param pic_path:重现率电视画面图片路径
  83. :param scaleGray:刻度与悲剧G枪分界值
  84. :param scaleMax_W:宽,即水平刻度数,不包含图像边界
  85. :param scaleMax_H:高,即垂直刻度数,不包含图像边界
  86. :param scaleUnit_W:宽,水平刻度单位值
  87. :param scaleUnit_H: 高,垂直刻度单位值
  88. :param scaleLineOffset:刻度线段可偏离像素点数
  89. :return 浮点数组。[左边重显率,右边重显率,上边重显率,下边重显率]
  90. '''
  91. def detectOverScan(self, pic_path, scaleMax_W=10, scaleMax_H=10, scaleUnit_W=0.02, scaleUnit_H=0.04, scaleLineOffset=3):
  92. img = cv.imread(pic_path)
  93. img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
  94. h1, w1 = img_gray.shape
  95. center_height, center_width = self.findCenterPoint(img_gray)
  96. #计算左边重现率
  97. # print "detectOverScan,scale_left"
  98. scale_left = self.detectWhiteScale(img_gray, scaleMax_W, scaleUnit_W,
  99. (0, center_height-scaleLineOffset),
  100. (center_width/3, center_height-scaleLineOffset))
  101. # print "detectOverScan,scale_right"
  102. scale_right = self.detectWhiteScale(img_gray, scaleMax_W, scaleUnit_W,
  103. (w1-1, center_height-scaleLineOffset),
  104. (5*center_width/3, center_height-scaleLineOffset))
  105. # print "detectOverScan,scale_up"
  106. scale_up = self.detectWhiteScale(img_gray, scaleMax_H, scaleUnit_H,
  107. (center_width-scaleLineOffset, 0),
  108. (center_width-scaleLineOffset, center_height/2))
  109. # print "detectOverScan,scale_down"
  110. scale_down = self.detectWhiteScale(img_gray, scaleMax_H, scaleUnit_H,
  111. (center_width - scaleLineOffset, h1-1),
  112. (center_width - scaleLineOffset, 3*center_height / 2))
  113. # print scale_left,scale_right,scale_up,scale_down
  114. return scale_left,scale_right,scale_up,scale_down
  115. '''
  116. 寻找图片白色条相交中心点,例如:重现率图片
  117. '''
  118. def findCenterPoint(self, img):
  119. h1,w1 = img.shape
  120. center_x1 = 4 * w1 / 10
  121. center_y1 = 4 * h1 / 10
  122. center_x2 = 6 * w1 / 10
  123. center_y2 = 6 * h1 / 10
  124. # print center_x1, center_y1, center_x2, center_y2
  125. area_center = [center_x1, center_y1, center_x2, center_y2]
  126. img_center = image_util.cutMat(img, area_center)
  127. h2, w2 = img_center.shape
  128. # print h2,w2,dim2
  129. # 寻找中心点的高坐标
  130. line_w = w2 / 20
  131. line_1 = [line_w, h2 / 3, line_w, 2 * h2 / 3]
  132. # print "line_1:",line_1
  133. center_height = center_y1
  134. grayAvg1 = self.imageUtil.calGrayAvg(img_center, line_1)
  135. # print "grayAvg:",grayAvg1
  136. for height in range(line_1[1], line_1[3]):
  137. if img_center[height, line_w] >= grayAvg1:
  138. center_height += height
  139. break
  140. print "center_height:",center_height
  141. # 寻找纵向直线的宽坐标
  142. line_h = h2 / 20
  143. line_2 = [w2 / 3, line_h, 2 * w2 / 3, line_h]
  144. center_width = center_x1
  145. grayAvg2 = (np.max(line_1) + np.min(line_1)) / 2
  146. for width in range(line_2[0], line_2[2]):
  147. if img_center[line_h, width] >= grayAvg2:
  148. center_width += width
  149. break
  150. print "center_width:", center_width
  151. return center_height, center_width
  152. '''
  153. 判断是否在重显率画面
  154. :param picPath:图片路径
  155. :return True/False:True:是重显率画面,False:不是重显率
  156. '''
  157. def isOverScan(self, picPath):
  158. print "isOverScan,picPath:",picPath
  159. srcImg = cv.imread(picPath)
  160. height,widht,dim = srcImg.shape
  161. grayImg = cv.cvtColor(srcImg,cv.COLOR_BGR2GRAY)
  162. center_height, center_width = self.findCenterPoint(grayImg)
  163. dHeight = center_height*2.0/height
  164. dWidth = center_width*2.0/widht
  165. if 0.98 < dHeight < 1.02 and 0.98 < dWidth < 1.02:
  166. return True
  167. else:
  168. return False
  169. '''
  170. 根据指定的直线,扫描刻度线条。去掉刻度线条宽度的影响,同时刻度线条和背景灰阶阈值,采用像素点平均值。
  171. :param img:待检测图片。22293 pattern
  172. :param scaleLevel:刻度等级数,即多少条刻度线.刻度线不包含图像边界线。
  173. :param scaleUnit: 每一个刻度,读数值
  174. :param pointStart:刻度起始点坐标。 width-x,height-y坐标系
  175. :param pointEnd: 刻度重点坐标。 width-x,height-y坐标系
  176. :return 重显率scaleDG:取值范围 0.00 ~ 1.00
  177. '''
  178. def detectWhiteScale(self, img, scaleLevel, scaleUnit, pointStart, pointEnd, grayMidOffset = 50):
  179. grayMid = self.imageUtil.calGrayAvg(img, (pointStart[0],pointStart[1], pointEnd[0],pointEnd[1])) + grayMidOffset
  180. print "detectWhiteScale:pointStart:pointEnd:grayMid:",(pointStart,pointEnd, grayMid)
  181. scalePointList = []
  182. w_list,h_list = self.genPointHWArray(pointStart,pointEnd)
  183. selPoint = None
  184. prevPoint = (w_list[0], h_list[0])
  185. for height in h_list:
  186. for width in w_list:
  187. # print img[prevPoint[1], prevPoint[0]] , img[height,width]
  188. if img[height,width] > grayMid:
  189. if selPoint is None or \
  190. (int(img[prevPoint[1], prevPoint[0]]) <= grayMid
  191. and image_util.caculateLinLen((selPoint[0],selPoint[1],width,height)) > 5):
  192. # 每条刻度的宽度像素点不定,取刻度的第一个像素点,作为刻度坐标
  193. selPoint = (width,height)
  194. scalePointList.append(selPoint)
  195. prevPoint = (width,height)
  196. print u"刻度点列表:", scalePointList
  197. if scalePointList.__len__() < 2:
  198. print u"重显率检测失败,刻度点列表:", scalePointList
  199. return -1.0
  200. x1,y1 = scalePointList[0]
  201. x2,y2 = scalePointList[1]
  202. step = image_util.caculateLinLen((x1,y1, x2,y2))
  203. # print "detectWhiteScale step:", step
  204. hintLevel = scaleLevel - scalePointList.__len__()
  205. # print "detectWhiteScale hintLevel:",hintLevel
  206. # direction = self.featureDetect.lineUtil.lineDirect((x1,y1, x2,y2))
  207. oneLevel = (step - (image_util.caculateLinLen((x1,y1,pointStart[0],pointStart[1]))))/step
  208. # print "detectWhiteScale oneLevel:",oneLevel
  209. scaleDG = 1 - (hintLevel*scaleUnit + oneLevel*scaleUnit)
  210. # print "detectWhiteScale scaleDG:",scaleDG
  211. return scaleDG
  212. '''
  213. 将坐标全部存入数组,解决坐标从小到大和从大到小均可遍历问题。
  214. '''
  215. def genPointHWArray(self, pointStart, pointEnd):
  216. w1 = pointStart[0]
  217. h1 = pointStart[1]
  218. w2 = pointEnd[0]
  219. h2 = pointEnd[1]
  220. w_list = []
  221. h_list = []
  222. if w1 >= w2:
  223. for dw in range(0, w1-w2+1):
  224. w_list.append(w1-dw)
  225. else:
  226. for w in range(w1, w2+1):
  227. w_list.append(w)
  228. if h1 >= h2:
  229. for dh in range(0, h1-h2+1):
  230. h_list.append(h1-dh)
  231. else:
  232. for h in range(h1, h2+1):
  233. h_list.append(h)
  234. # print "w_list:",w_list
  235. # print "h_list:",h_list
  236. return w_list,h_list
  237. '''
  238. 监听视频,查看是否会中途无声无像
  239. :param 要监听时间长度。单位秒。
  240. :param 要监听的画面,标准:0 代表黑屏,1 代表有画面
  241. :param galleryDir.指定窗口样图路径,有代表需要监听,没有代表不需要监听
  242. :param blackTh: 黑色图片判断阈值,低于或等于则表示为黑色。默认50
  243. :return 两个数组:SoundExpList,VideoExpList。 list的格式:数组内存放字典。字典{"time":time.asctime( time.localtime(time.time()))}。
  244. List里面的第一个time值,表示开始监听的时间。
  245. SoundExpList第一个字典数据,有audio 键,取录音文件路径。status键:0 代表无异常;1代表没有声音发出;2代表声音有间断。
  246. VideoExpList每一个异常字典数据,有picture键,取图片。第一个字典数据中,status键:0 代表一直有画面;1代表一直黑屏;2代表有黑屏出现,-1代表截图失败。
  247. 第一个字典数据中,motion_picture:1 代表运动画面,0 代表静态画面。
  248. '''
  249. def monitorVideo(self, seconds = 10, galleryDir = "", picTarget=1, blackTh = 50):
  250. soundExpList = []
  251. soundTID = thread.start_new_thread(self.audioChecker.monitorSound, (seconds, soundExpList))
  252. pictureExpList = []
  253. picTID = thread.start_new_thread(self.monitorTvPicture, (seconds,pictureExpList, picTarget, blackTh))
  254. global g_monitorResult
  255. if os.path.exists(unicode(galleryDir)) == True:
  256. self.startMonitorAbnormal(seconds, galleryDir)
  257. count = 0
  258. while soundExpList.__len__() < 1 or pictureExpList.__len__() < 1:
  259. print "monitorVideo ...seconds:",count
  260. time.sleep(1)
  261. count += 1
  262. # print "monitorVideo, abnormal:",g_monitorResult
  263. # 为保持向前兼容,g_monitorResult结果单独使用getMonitorAbnormalResults获取
  264. return soundExpList,pictureExpList#,g_monitorResult
  265. def monitorTvPicture(self,seconds, expList, picTarget=1, blackTh = 50):
  266. hasImage = False
  267. startSeconds = time.time()
  268. expList.append({"time": time.asctime(time.localtime(startSeconds)),"picture":"", "status":0})
  269. expList[0]["motion_picture"] = 0 #默认为静态画面
  270. curSeconds = time.time()
  271. prevPic = os.path.join(getSATTmpDIR(), "monitorTvPicture" + str(time.time()) + ".png")
  272. self.vp.takePicture(prevPic)
  273. LoggingUtil.printLog("Record picture...")
  274. while (curSeconds - startSeconds) < seconds:
  275. currentPic = os.path.join(getSATTmpDIR(), "monitorTvPicture"+str(time.time())+".png")
  276. self.vp.takePicture(currentPic)
  277. # 加0.5秒延时,防止截图过快导致异常
  278. time.sleep(0.5)
  279. curSeconds = time.time()
  280. try:
  281. #检测画面是否纯黑色背景
  282. if picTarget == 1:#黑屏无画面,异常
  283. if (self.imgCMP.isBlack(currentPic)):
  284. expList.append({"time":time.asctime(time.localtime(curSeconds)),"picture":currentPic})
  285. else:
  286. hasImage = True
  287. else: #黑屏无画面,正常
  288. if (self.imgCMP.isBlack(currentPic, blackTh)):
  289. pass
  290. else:
  291. expList.append({"time":time.asctime(time.localtime(curSeconds)),"picture":currentPic})
  292. hasImage = True
  293. except Exception,e:
  294. # 未能正常截到图,导致测试失败
  295. LoggingUtil.printLog(u"monitorTvPicture截图失败!!!\nErrorMsg:%s\ncurrentPic路径:%s\n"%(e,currentPic))
  296. expList[0]["status"] = -1
  297. return
  298. #检测画面是动态还是静态
  299. if self.imgCMP.cmpPicTotal(prevPic, currentPic, expDotRate=0) is False:
  300. expList[0]["motion_picture"] = 1
  301. prevPic = currentPic
  302. LoggingUtil.printLog("Record picture End")
  303. if hasImage and expList.__len__() == 1:
  304. expList[0]["status"] = 0
  305. elif hasImage and expList.__len__() > 1:
  306. expList[0]["status"] = 2
  307. else:
  308. expList[0]["status"] = 1
  309. '''
  310. # 描述:监控异常
  311. # 参数:
  312. # seconds:监控时长.
  313. # galleryDir:图库目录路径
  314. # 返回:bool, fileAbnormal, coordinate,fileTV
  315. # True:有异常,False:正常
  316. # fileAbnormal:异常时,与异常匹配的图库文件路径
  317. # coordinate:异常时,异常框的坐标。
  318. # fileTV:出现异常的视频截图路径。
  319. # '''
  320. def monitorAbnormal(self, seconds, galleryDir):
  321. pic_path = ""
  322. result = {"result": False, "tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
  323. setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255} #可从配置文件读取,不用从参数传入
  324. global g_stopThread
  325. startSeconds = time.time()
  326. while g_stopThread == False:
  327. # 一秒截一图;
  328. pic_path = os.path.join(getAbnormalDir(), time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))+".png")
  329. self.vp.takePicture(pic_path)
  330. # 异常匹配;
  331. result = self.featureDetect.matchImage(pic_path, None, galleryDir, setting)
  332. if result["result"] == True:
  333. result["screenFile"] = pic_path
  334. break
  335. # 删除无异常的截图;
  336. os.remove(pic_path)
  337. # 超时退出线程;
  338. if int(time.time() - startSeconds) > seconds:
  339. break
  340. # 返回全局对象;
  341. if result["result"] == False:
  342. result["screenFile"] = ""
  343. global g_monitorResult
  344. g_monitorResult = result
  345. print u'异常监控结果:',g_monitorResult["result"],g_monitorResult["tmpVal"],g_monitorResult["coordinate"],g_monitorResult["screenFile"].encode('gb2312'),g_monitorResult["galleryFile"].encode('gb2312')
  346. # 启动监控线程;
  347. def startMonitorAbnormal(self, seconds, galleryDir):
  348. global g_threadMonitor,g_stopThread
  349. if g_threadMonitor is not None and g_threadMonitor.is_alive() == True:
  350. print u'线程已运行'
  351. return
  352. g_stopThread = False
  353. g_threadMonitor = threading.Thread(target=self.monitorAbnormal, name='MonitorAbnormal', args=(seconds,galleryDir))
  354. g_threadMonitor.start()
  355. # 停止监控;
  356. def stopMonitorAbnormal(self):
  357. global g_threadMonitor,g_stopThread
  358. if g_threadMonitor is not None and g_threadMonitor.is_alive() == True:
  359. g_stopThread = True
  360. print u'开始停止'
  361. else:
  362. print u'线程已停止'
  363. '''
  364. # 描述:获取监控结果
  365. #
  366. # '''
  367. def getMonitorAbnormalResults(self):
  368. global g_monitorResult
  369. return g_monitorResult
  370. '''
  371. HDMI重显率测试:1080P及以上分辨率重显率为100%;1080P分辨率以下重显率为95%-97%,显示器模式时为100%.
  372. 片源 22294: pattern:304
  373. 根据传入的图片和重显率要求,返回检测结果。
  374. 重现率最小界限,增加0.01容错。两边偏移不超过0.02
  375. :param picPath: 电视截图路径
  376. :param resolution: "Full":全屏显示。 "HD":高清, "SD":标清
  377. :param offsetMax = 0.02#中心点偏移的容错值
  378. :param downTh = 0.01#重现率偏小的容错值
  379. :return :两个参数。参数1 True或者False,参数2, 具体结果参数数组。
  380. '''
  381. def detectHDMIOverScan(self, picPath, resolution="", STD=[],offsetMax = 0.02, downTh = 0):
  382. print "detectHDMIOverScan:", picPath, resolution
  383. if STD == []:
  384. hdmiRSTD = {"full":[1.0,1.0], "hd":[1.0,1.0], "sd":[0.95,0.97]}
  385. STD = hdmiRSTD[resolution.lower()]
  386. left, right, up, down = self.detectOverScan(picPath)
  387. # print "detectHDMIOverScan,left, right, up, down:", left, right, up, down
  388. hOverscan, hOffset = self.calOverScan(left,right)
  389. vOverscan, vOffset = self.calOverScan(up,down)
  390. if hOverscan + downTh >= STD[0] and hOverscan <= STD[1] and hOffset < offsetMax\
  391. and (vOverscan + downTh) >= STD[0] and vOverscan <= STD[1] and vOffset < offsetMax:
  392. return True, [resolution, STD, [(hOverscan,hOffset), (vOverscan,vOffset)]]
  393. else:
  394. return False, [resolution, STD, [(hOverscan,hOffset), (vOverscan,vOffset)]]
  395. def calOverScan(self, side1, side2):
  396. scale = (side1+side2)/2
  397. return round(scale, 3), round(abs(side1-side2),3)
  398. '''
  399. AV重显率测试:重显率为95%-97%
  400. 片源 22294: pattern:304
  401. 重现率最小界限,增加0.01容错。两边偏移不超过0.02
  402. 根据传入的图片和重显率要求,返回检测结果。
  403. :param picPath: 电视截图路径
  404. :param offsetMax = 0.02#中心点偏移的容错值
  405. :param downTh = 0.01#重现率偏小的容错值
  406. :return :两个参数。参数1 True或者False,参数2, 具体结果参数数组。
  407. '''
  408. def detectAVOverScan(self, picPath, STD = [0.95,0.97], offsetMax = 0.02, downTh = 0):
  409. print "detectAVOverScan:", picPath
  410. left, right, up, down = self.detectOverScan(picPath)
  411. # print "detectAVOverScan,left, right, up, down:", left, right, up, down
  412. hOverscan, hOffset = self.calOverScan(left,right)
  413. vOverscan, vOffset = self.calOverScan(up,down)
  414. if hOverscan+downTh >= STD[0] and vOverscan <= STD[1] and hOffset < offsetMax\
  415. and hOverscan+downTh >= STD[0] and vOverscan <= STD[1] and vOffset < offsetMax:
  416. return True, [STD, [(hOverscan, hOffset), (vOverscan, vOffset)]]
  417. else:
  418. return False, [STD, [(hOverscan, hOffset), (vOverscan, vOffset)]]
  419. '''
  420. ATV重显率(重现率要求95%-97%之间,(行6.5-7,帧5-6)
  421. 片源 检测片源TG39 mark
  422. 根据传入的图片和重显率要求,返回检测结果。
  423. 重现率最小界限,增加0.01容错。两边偏移不超过0.02
  424. :param picPath: 电视截图路径
  425. :param offsetMax = 0.02#中心点偏移的容错值
  426. :param downTh = 0.01#重现率偏小的容错值
  427. :return :两个参数。参数1 True或者False,参数2, 具体结果参数数组。
  428. '''
  429. def detectAtvOverScan(self, picPath, STD = [0.95, 0.97], offsetMax = 0.02, downTh = 0):
  430. print "detectAtvOverScan:", picPath
  431. left, right, up, down = self.detectOverScan(picPath, scaleMax_W=4, scaleMax_H=5, scaleUnit_W=0.05, scaleUnit_H=0.05, scaleLineOffset=5)
  432. # print "detectAtvOverScan,left, right, up, down:", left, right, up, down
  433. hOverscan, hOffset = self.calOverScan(left,right)
  434. vOverscan, vOffset = self.calOverScan(up,down)
  435. if (hOverscan + downTh) >= STD[0] and hOverscan <= STD[1] and hOffset < offsetMax \
  436. and (vOverscan + downTh) >= STD[0] and vOverscan <= STD[1] and vOffset < offsetMax:
  437. return True, [STD, [(hOverscan, hOffset), (vOverscan, vOffset)]]
  438. else:
  439. return False, [STD, [(hOverscan, hOffset), (vOverscan, vOffset)]]
  440. def detectAtv3116OverScan(self, picPath, STD = [0.95, 0.97], offsetMax = 0.02, downTh = 0):
  441. print "detectAtv3116OverScan:", picPath
  442. left, right, up, down = self.detectOverScan(picPath, scaleMax_W=4, scaleMax_H=3, scaleUnit_W=0.05,
  443. scaleUnit_H=0.05, scaleLineOffset=5)
  444. # print "detectAtvOverScan,left, right, up, down:", left, right, up, down
  445. hOverscan, hOffset = self.calOverScan(left, right)
  446. vOverscan, vOffset = self.calOverScan(up, down)
  447. if (hOverscan + downTh) >= STD[0] and hOverscan <= STD[1] and hOffset < offsetMax \
  448. and (vOverscan + downTh) >= STD[0] and vOverscan <= STD[1] and vOffset < offsetMax:
  449. return True, [STD, [(hOverscan, hOffset), (vOverscan, vOffset)]]
  450. else:
  451. return False, [STD, [(hOverscan, hOffset), (vOverscan, vOffset)]]
  452. '''
  453. 假定重现率正确
  454. 检测画面:TG39 网格
  455. 根据传入的网格图片,判断比例模式是否正确。
  456. :param mode: "full", origin, smart, zoom
  457. :param testPic: 图片路径
  458. :return :1个参数。参数1 True或者False
  459. '''
  460. def detectATVAspectRatio(self, testPic, mode):
  461. scale_W = 17.0
  462. scale_H = 13.0
  463. widthRatio = 0
  464. heightRatio = 0
  465. if mode == "full":
  466. widthRatio = 1.0
  467. heightRatio = 1.0
  468. elif mode == "origin":
  469. widthRatio = 1.0
  470. heightRatio = 0
  471. elif mode == "smart":
  472. widthRatio = 1.0
  473. heightRatio = 11/scale_H
  474. elif mode == "zoom":
  475. widthRatio = 1.0
  476. heightRatio= 9/scale_H
  477. else:
  478. pass
  479. return self.detectAspectRatio(testPic, scale_W, scale_H,widthRatio,heightRatio)
  480. '''
  481. 假定重现率正确
  482. 检测画面:22294 Timing65 Pattern55
  483. 根据传入的网格图片,判断比例模式是否正确。
  484. :param mode: "full", origin, smart, zoom
  485. :param testPic: 图片路径
  486. :return :1个参数。参数1 True或者False
  487. '''
  488. def detectHDMIAspectRatio(self, testPic, mode):
  489. scale_W = 15.0
  490. scale_H = 11.0
  491. widthRatio = 0
  492. heightRatio = 0
  493. if mode == "full":
  494. widthRatio = 1.0
  495. heightRatio = 1.0
  496. elif mode == "origin":
  497. widthRatio = 1.0
  498. heightRatio = 0
  499. elif mode == "smart":
  500. widthRatio = 1.0
  501. heightRatio = 11 / scale_H
  502. elif mode == "zoom":
  503. widthRatio = 1.0
  504. heightRatio = 9 / scale_H
  505. else:
  506. pass
  507. return self.detectAspectRatio(testPic, scale_W, scale_H,widthRatio,heightRatio)
  508. '''
  509. 假定重显率正确
  510. 检测画面:22294 Timing36 Pattern55
  511. 根据传入的网格图片,判断比例模式是否正确。
  512. :param mode: "full", origin, smart, zoom
  513. :param testPic: 图片路径
  514. :return :1个参数。参数1 True或者False
  515. '''
  516. def detectAVAspectRatio(self, testPic, mode):
  517. return self.detectHDMIAspectRatio(testPic,mode)
  518. '''
  519. 根据传入的网格图片,判断比例模式是否正确。
  520. :param mode: "full", origin, smart, zoom
  521. :param testPic: 图片路径
  522. :param scale_W:水平宽的网格线数量
  523. :param scale_H:垂直高的网格线数量
  524. :param widthRatio:相比于origin,水平宽的比例要求
  525. :param heightRatio:相比于origin,垂直高的比例要求
  526. :param scaleGray:网格黑色背景和白色刻度线的G枪分界线
  527. :return :1个参数。参数1 True或者False
  528. '''
  529. def detectAspectRatio(self, testPic, scale_W, scale_H, widthRatio, heightRatio,scaleGray=80):
  530. print "detectAspectRatio:", scale_W, scale_H
  531. img = cv.imread(testPic)
  532. height, width, color =img.shape
  533. scalePointListW = self.getScaleNumber(img, (0,5), (width-1, 5), scaleGray)
  534. print "detectAspectRatio,scalePointListW:",scalePointListW.__len__(),scalePointListW
  535. scalePointListH = self.getScaleNumber(img, (5,0), (5, height-1), scaleGray)
  536. print "detectAspectRatio,scalePointListH:",scalePointListH.__len__(),scalePointListH
  537. # print "detectAspectRatio:",scalePointListW.__len__() /scale_W,widthRatio,scalePointListH.__len__() / scale_H,heightRatio
  538. if scalePointListW.__len__() /scale_W == widthRatio\
  539. and scalePointListH.__len__() / scale_H == heightRatio:
  540. return True
  541. else:
  542. return False
  543. def getScaleNumber(self, img,pointStart,pointEnd, scaleGray):
  544. # print "getScaleNumber:", pointStart, pointEnd
  545. scalePointList = []
  546. w_list, h_list = self.genPointHWArray(pointStart, pointEnd)
  547. selPoint = None
  548. prevPoint = (w_list[0], h_list[0])
  549. for height in h_list:
  550. for width in w_list:
  551. # print img[prevPoint[1], prevPoint[0]][1] , img[height,width][1],prevPoint[1], prevPoint[0]
  552. if img[height, width][1] > scaleGray:
  553. if selPoint is None or \
  554. int(img[prevPoint[1], prevPoint[0]][1]) <= scaleGray:
  555. # 每条刻度的宽度像素点不定,取刻度的第一个像素点,作为刻度坐标
  556. selPoint = (width, height)
  557. scalePointList.append(selPoint)
  558. prevPoint = (width, height)
  559. return scalePointList
  560. '''
  561. 检测电视是否已经关机
  562. 检测方式:1 检测画面是否有变化;检测连续5张截图,均没有变化,且四个角落为相同纯色,且没有声音,则表示处于关机状态。
  563. 声音检测和纯色检测,可选。
  564. :param : checkSignle:选择是否要检测四周纯色。
  565. :param :checkAudio : 是否要检测无声。
  566. :param : colorOffset : 颜色允许的偏移量
  567. :param : expDotRate : 异常点比例范围上限.前后两张图片差分时的差异点比例。
  568. :return : [检测结果,[图片地址列表], [音频地址列表]]
  569. '''
  570. def detectPowerOff(self, checkSignle=True, checkAudio=True, colorOffset=20, expDotRate = 0.0001):
  571. picList = []
  572. soundList = []
  573. ccard= CCardManager()
  574. count = 0
  575. matchCount = 0
  576. previewPic = os.path.join(getSATTmpDIR(), "detectPowerOff_"+str(time.time())+".png")
  577. ccard.takePicture(previewPic)
  578. picList.append(previewPic)
  579. while count < 5:
  580. currentPic = os.path.join(getSATTmpDIR(), "detectPowerOff_"+str(time.time())+".png")
  581. ret = ccard.takePicture(currentPic)
  582. print "detectPowerOff,截图:",ret
  583. if ret == 1:
  584. count += 1
  585. ret1 = self.imgCMP.cmpPicTotal(previewPic, currentPic, colorOffset, expDotRate)
  586. print "detectPowerOff,前后图片比对:",ret1
  587. #检测4个角落纯色
  588. if checkSignle is True:
  589. ret2 = self.imgCMP.checkAroundSSColor(currentPic)
  590. print "detectPowerOff,四周相同纯色:", ret2
  591. else:
  592. ret2= True
  593. if ret1 and ret2:
  594. matchCount += 1
  595. previewPic = currentPic
  596. picList.append(currentPic)
  597. if matchCount < 5:
  598. return False, picList, soundList
  599. #检测声音
  600. if checkAudio is True:
  601. self.audioChecker.startCHK(5)
  602. soundList.append(self.audioChecker.audioFile)
  603. return not self.audioChecker.isOK(), picList, soundList
  604. else:
  605. return True, picList, soundList
  606. def isColorBar(self, picPath, type):
  607. if type.lower()=="hdmi" or type.lower()=="dtv":
  608. return self.colorBarDetect.isHDMIColorBar(picPath)
  609. elif type.lower()=="av":
  610. return self.colorBarDetect.isAVColorBar(picPath)
  611. elif type.lower()=="atv":
  612. return self.colorBarDetect.isATVColorBar(picPath)
  613. else:
  614. return False
  615. if __name__ == "__main__":
  616. tvDetect = TvDetect()
  617. # pic = "D:/test/4k2.jpg"
  618. pic = "D:/test/colorbar_hdmi_std.png"
  619. ret = tvDetect.isColorBar(pic,"hdmi")
  620. print "isColorBar HDMI:",ret
  621. ret = tvDetect.isColorBar(pic,"dtv")
  622. print "isColorBar DTV:",ret
  623. ret = tvDetect.isColorBar(pic,"av")
  624. print "isColorBar AV:",ret
  625. ret = tvDetect.isColorBar(pic,"atv")
  626. print "isColorBar ATV:",ret
  627. # print "detectHDMIOverScan:", tvDetect.detectHDMIOverScan(pic,"full")
  628. # lockPic = r"D:\temp-pic\parentlock\unlockChannel_channelLock.jpg"
  629. # lockPic = r"D:\temp-pic\parentlock\unlockTV_image.jpg"
  630. # lockPic = r"D:\temp-pic\parentlock\inputLock_image.jpg"
  631. # print tvDetect.detectLockPic(lockPic)
  632. # print tvDetect.monitorVideo(500)
  633. # time.sleep(2)
  634. # print tvDetect.startMonitorAbnormal(1000, r'C:\Users\jianfeng1.wang\Pictures\图库')
  635. # print tvDetect.startMonitorAbnormal(10, r'C:\Users\jianfeng1.wang\Pictures\图库')
  636. # time.sleep(10)
  637. # tvDetect.stopMonitorAbnormal()
  638. # time.sleep(11)
  639. # print tvDetect.startMonitorAbnormal(10, r'C:\Users\jianfeng1.wang\Pictures\图库')