一、背景需求
公司視頻組最近在錄制某款游戲的PVP視頻,視頻錄制好以后再上傳到后端存儲。但是在錄制的過程中,有可能錄制視頻程序會有視頻卡死不動的情況,即:錄制程序在運行,但是后台渲染引擎工作異常,也就是游戲畫面不變,但是視頻程序還是在錄制視頻中。比如下面2張圖片(暫且將新聞頁面當做正在錄制的視頻,不同點我已標出):
二、需求分析
縱觀整個過程,首先想嘗試進行如下分析:
1、從數據層面,抓取數據包異常特征,比如在渲染引擎工作異常時,數據包或者網絡流量的大小是否會突升或突降?
2、從系統層面,監控錄制程序的資源占用情況(CPU/內存/IO),錄制畫面卡住時,錄制程序占用資源是否有異常?
3、從代碼層面,需要開發視頻錄制程序的程序員進行相關日志輸出,然后看是否可以看到錯誤日志?
針對情況1,從數據層面看,渲染引擎工作異常,但是錄制程序還是照常錄制,只是錄制的畫面一直是出現異常時這一刻的屏幕畫面,所以數據包不會有變化;
針對情況2,系統層面,針對錄制程序進行了專門的監控,但是后台引擎異常時,錄制進行資源基本不會變化,所以這一個嘗試也失敗了;
針對情況3,代碼層面,錄制程序確實有錯誤日志產生,針對這個情況,開發人員進行了重連渲染引擎的方法,雖然通過這種方法已經基本上解決了問題,但是還是有一部分概率即使在重啟渲染引擎進程后,錄制的游戲畫面還是不變,即一直錄制的同一個畫面。這時,需要開發人員對整套錄制程序進行重啟才能徹底解決。
以上只是交代了監控背景:視頻組負責人要求運維這邊針對出現一直是同一個畫面的情況進行監控,如果有這種情況,及時通知他們,進行線上解決,當然,這也只是臨時方法,后期還是要他們程序支持自動檢測的,不然,這么老土的辦法就算是開發自己,我想也會感到心里不安的。(半夜里電話吵醒來重啟進程)
三、解決方案
針對畫面不動進行監控,我在網上找了一些方法,但是都不能真正解決,比如:使用按鍵精靈,截取屏幕后通過圖片大小進行對比;或者干脆使用截屏軟件進行屏幕截取,然后比較文件的md5等等之類的,雖然圖片能夠截取,但是問題都不能解決,對比下來結果相差甚遠。
腦子里一直想着圖片處理、圖片處理。。。。。突然,靈光一閃,最近不是機器學習、圖片識別不是很火么?既然能夠截取屏幕,我們通過截屏的圖片進行識別不就可以了么?解決方法找到了,就開始找方案。
四、OpenCV
1、OpeCV介紹
OpenCV (Open Source Computer Vision Library) is an open source computer vision and machine learning software library. OpenCV was built to provide a common infrastructure for computer vision applications and to accelerate the use of machine perception in the commercial products. Being a BSD-licensed product, OpenCV makes it easy for businesses to utilize and modify the code.
OpenCV--開源可視化計算庫。它是一個開源計算可視化和機器學習的庫。
2、OpenCV怎么識別兩張照片呢?
比如怎么從一張大圖和一張小圖里面識別同一個圖像呢?從機器的角度來說是這樣的,先識別圖像的特征,然后再相比,通過相似度進行判斷是否為同一個人。
實現代碼如下:
#!/usr/bin/env python # coding:utf-8 from PIL import ImageGrab import time import os import cv2 import numpy as np from matplotlib import pyplot as plt # 抓取當前屏幕 cur_dir = os.path.dirname(__file__) im = ImageGrab.grab() im.save(os.path.join(cur_dir,"%s.png" %time.time())) # 獲取當前目錄下所有png文件 def all_path(dirname): result = [] for maindir, subdir, file_name_list in os.walk(dirname): for filename in file_name_list: if filename.endswith('.png'): apath = os.path.join(maindir, filename) result.append(apath) return result # 獲取最新的兩個文件 def get_max_two(file_list): rt = [] for f in file_list: rt.append((f,os.path.getctime(f))) rt = sorted(rt,key=lambda x:x[1],reverse=True) return rt[0][0],rt[1][0] # 計算單通道的直方圖的相似值 def calculate(image1, image2): hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0]) hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0]) # 計算直方圖的重合度 degree = 0 for i in range(len(hist1)): if hist1[i] != hist2[i]: degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i])) else: degree = degree + 1 degree = degree / len(hist1) return degree # 通過得到每個通道的直方圖來計算相似度 def classify_hist_with_split(image1, image2, size=(256, 256)): # 將圖像resize后,分離為三個通道,再計算每個通道的相似值 image1 = cv2.resize(image1, size) image2 = cv2.resize(image2, size) sub_image1 = cv2.split(image1) sub_image2 = cv2.split(image2) sub_data = 0 for im1, im2 in zip(sub_image1, sub_image2): sub_data += calculate(im1, im2) sub_data = sub_data / 3 return sub_data if __name__ == '__main__': # 獲取2張圖片的相似度 cur_dir = os.path.dirname(__file__) file_list = all_path(cur_dir) img1, img2 = get_max_two(file_list) # print img1,img2 img1 = cv2.imread(img1) # cv2.imshow('img1', img1) img2 = cv2.imread(img2) # cv2.imshow('img2', img2) degree = classify_hist_with_split(img1, img2) if isinstance(degree, int): with open(os.path.join(cur_dir, "a.txt"), 'w+') as f: f.write(str(degree)) else: with open(os.path.join(cur_dir,"a.txt"),'w+') as f: f.write(str(degree[0]))
解釋一下以上腳本的整個過程:
1、抓取當前屏幕,並以時間為文件名進行保存;
2、取出當前最新的2張圖片;
3、獲取當前2張照片的相似度,並寫入文件a.txt;
4、通過從a.txt讀取相似度進行監控,相似度達到某個值,比如99%以上就判斷為屏幕卡死了。
五、實現
1、設置計划任務
把上面寫好的腳本放入系統的計划任務中運行:
結果如下:
文件記錄的相似度值:
2、通過zabbix進行監控
自定義監控項
UserParameter=get_degree,type d:\pych\a.txt
獲取監控值
[root@localhost ~]# zabbix_get -s 10.20.122.18 -kget_degree
0.996
設置觸發器