背景
白板是教育產(chǎn)品里的常見(jiàn)功能阻问,類似于電子化的黑板,基礎(chǔ)的業(yè)務(wù)邏輯是教師端(windows)老師在白板上寫(xiě)字或者畫(huà)圖,學(xué)生端(android)可以同步接收到該內(nèi)容垛耳。
現(xiàn)在有兩套基于不同技術(shù)的白板功能方案瓶珊,需要對(duì)比他們的效果差異啸箫,由于底層代碼無(wú)法放開(kāi),無(wú)法通過(guò)修改代碼埋點(diǎn)的形式進(jìn)行伞芹,因此需要通過(guò)ui層面進(jìn)行對(duì)比忘苛。
基礎(chǔ)測(cè)試流程
- 在pc教師端運(yùn)行一次畫(huà)圖腳本,在Android學(xué)生端確認(rèn)正確后唱较,選取該圖案作為基準(zhǔn)圖片
- 在pc教師端進(jìn)行ui操作扎唾,在白板上再次畫(huà)出該圖案
- 在android學(xué)生端上,截取白板畫(huà)面
- 對(duì)比截取的白板畫(huà)面和基準(zhǔn)圖片南缓,計(jì)算相似度胸遇,大于閾值(95%)則視為該次準(zhǔn)確,否則認(rèn)為該次失敗西乖,計(jì)入失敗次數(shù)狐榔,并記錄該失敗圖案供分析
- 清空pc教師端白板圖案坛增,再次重復(fù)2~4操作
自動(dòng)化技術(shù)
airtest(pywinauto)& pynput完成對(duì)pc端ui操作
在pc端ui自動(dòng)化中,首先選用的是比較經(jīng)典的ui操作庫(kù)pywinauto薄腻,airtest對(duì)其進(jìn)行了封裝收捣,為減少二次開(kāi)發(fā)量,這里直接選用了airtest庵楷。
基礎(chǔ)的ui操作如元素定位罢艾、點(diǎn)擊、滑動(dòng)等尽纽,airtest都有較為成熟的api咐蚯,但模擬老師在白板上畫(huà)圖,只靠自己定義滑動(dòng)坐標(biāo)使用鼠標(biāo)事件執(zhí)行的話弄贿,太過(guò)生硬不符合實(shí)際情形春锋,因此,最后決定采用實(shí)際錄制操作坐標(biāo)再回放的形式進(jìn)行差凹,這樣基本可以模擬真實(shí)的筆跡期奔。
airtest不具備錄制鼠標(biāo)軌跡功能,在調(diào)研后危尿,選用pynput進(jìn)行鼠標(biāo)事件的錄制呐萌。
pynput也是一個(gè)提供windows操作api的庫(kù),但其提供了對(duì)鼠標(biāo)和鍵盤(pán)事件的監(jiān)聽(tīng)功能谊娇;通過(guò)添加監(jiān)聽(tīng)線程pynput.mouse.Listener肺孤,每發(fā)生一個(gè)鼠標(biāo)事件,就使用預(yù)先定義的格式進(jìn)行本地化存儲(chǔ)济欢,這樣就完成了鼠標(biāo)操作的事件收集赠堵;再通過(guò)解析存儲(chǔ)的事件文本,就可以完成鼠標(biāo)事件的回放船逮。
需要說(shuō)明的是顾腊,pynput本身也有操作windows鼠標(biāo)事件的api,但實(shí)測(cè)下來(lái)挖胃,雖然通過(guò)pynput進(jìn)行回放整體軌跡是對(duì)的憔狞,但在白板上的筆畫(huà)平滑度和粗細(xì)每次都不太一致欢嘿,因此不適合選用其作為回放手段辞居,最終測(cè)試痪枫,還是通過(guò)airtest的mouse_move、mouse_up凹髓、mouse_down組合進(jìn)行回放烁登,效率較pynput低,但勝在一致性高,基本每次回放的軌跡都一致饵沧。
opencv2進(jìn)行圖像識(shí)別判斷
在進(jìn)行教師端的操作后锨络,需要在學(xué)生端截圖并進(jìn)行對(duì)比,這里對(duì)比技術(shù)采用了opencv2的api狼牺,代碼如下:
def img_similarity(img1_path,img2_path):
"""
:param img1_path: 圖片1路徑
:param img2_path: 圖片2路徑
:return: 圖片相似度
"""
try:
# 讀取圖片
img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE)
# 初始化ORB檢測(cè)器
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 提取并計(jì)算特征點(diǎn)
bf = cv2.BFMatcher(cv2.NORM_HAMMING)
# knn篩選結(jié)果
matches = bf.knnMatch(des1, trainDescriptors=des2, k=2)
# 查看最大匹配點(diǎn)數(shù)目
good = [m for (m, n) in matches if m.distance < 0.75 * n.distance]
#print(len(good))
#print(len(matches))
similary = len(good) / len(matches)
#print("兩張圖片相似度為:%s" % similary)
return similary
except:
print('無(wú)法計(jì)算兩張圖片相似度')
return 0
由于其中一個(gè)方案學(xué)生端界面上會(huì)有動(dòng)態(tài)的時(shí)間顯示羡儿,影響對(duì)比結(jié)果,因此是钥,需要對(duì)該截圖進(jìn)行裁剪掠归,只保留白板的內(nèi)容部分,裁剪代碼如下:
screen_path = "D:\PycharmProjects\omoboard\{filename}".format(filename=screen)
img = cv2.imread(screen_path)
cropped = img[100:1050, 0:1500]
cv2.imwrite(screen_path, cropped)