uiautomator2+ tesseract 智能識別文字實現手游輔助外掛,打怪刷裝備快人一步


一、背景

先交代下背景,就是疫情期間,隨便玩了一款游戲,玩了后發現,游戲任務太繁重了,為了兼顧學習和游戲,所以就萌生了自己動手開發手游輔助外掛的想法,調研了幾個手機自動化測試平台最后選中了uiautomator2,然后有了這邊文章 《uiautomator2介紹及環境搭建》 以及文章《uiautomator2一分鍾實現輔助外掛》
如果仔細讀過第二篇文章的朋友就會發現,前面說的外掛就是 定時任務+ 模擬用戶點擊,如果碰上復雜點場景,就完全無法適應了,比如下面幾種
在這里插入圖片描述
Boss有刷新又時間間隔,然后區分“野外”,”秘境“,”神諭“,每個刷新時間都不同,那么這個輔助腳本怎么寫呢,輪訓點擊肯定不現實,無法快人一步搶先刷到怪,怎么解決呢? 當然是如標題寫的 那樣,通過閱讀這篇博文,大家會了解到這些知識

  • uiautomator2 截圖並裁剪
  • python + tesseract 實現文字OCR識別
  • tesseract 安裝及注意事項
  • 以及帶大家看輔助外掛是如何一步步開發完成的。

博文可能略長,讀完可能需要點耐心。
另外鄭重聲明,本博客僅供繼續交流(因為畢竟是外掛嘛,這個自動刷掛功能,游戲里面包月要88元一月呢,所以大家懂得,源碼我就不開源共享了,但偽代碼下面都有的,更多大家可私下加QQ交流技術)

二、需求分解

要實現按時去刷怪,那么肯定需要上圖紅色框中那個刷新時間,這個文字又無法通過簡單的通過控件去識別,那么就只能通過圖片去實現了,我這里的思路是用網上開源的OCR文字識別庫識別出時間,到這里又不得不贊嘆python擴展能力真是強大,關鍵是拓展起來超級easy,不愧是“膠水語言”。好了,長話短說,文件OCR識別,我這里用的是tesseract,下面會介紹 tesseract 的安裝簡單使用。繼續我們剛剛的分解,假設可以通過圖片進行文字識別,那么要實現一個定時搶怪任務,我們需要的工作如下:
1、模擬點擊到刷怪頁面,並進行截圖
2、截圖到的圖片做裁剪(主要是過濾其他文字識別干擾)
3、對裁剪到的圖片做 灰度處理(比如彩色變黑白),然后二值化、降噪等一系列操作,主要是去除背景和一些其他的干擾。
4、識別到的數字轉化為秒,然后建立定時任務
5、定時啟動搶怪任務,截圖每個步驟圖片,拼接成一張圖,供人工查下結果。
6、截重復前面5個步驟實現自動刷掛打裝備
好了,下面我們一步步來實現這個效果。

三、腳本開發實踐

1、tesseract 安裝及測試

如果要使用Tesseract庫,首先需要安裝它到你的機器上。
macOS用戶,我們使用Homebrew 去安裝Tesseract:

$ brew install tesseract

當然有時候Homebrew 可能更新導致卡住,最簡單的辦法是在 .bash_profile 文件中新增一行,暫時關閉更新

export HOMEBREW_NO_AUTO_UPDATE=true

安裝完成后 tesseract -v 查看是否安裝完成
在這里插入圖片描述
可以通過命令測試一把
比如下面一張圖片
在這里插入圖片描述
我們通過命令 tesseract /Users/bifeng/222222.png stdout digits
在這里插入圖片描述
可以看出一下子就把數字識別出來了,這里識別得是數字,如果是中文則需要改為
tesseract /Users/bifeng/3333.jpg stdout -l chi_sim
-l 后面得參數代表識別得語言 chi_sim 代表是中文簡體。
當然中文簡體字體是需要額外去下載字庫得,
去往https://github.com/tesseract-ocr/tessdata下載中文數據集chi_sim.traineddata,把它放到這目錄下:
/usr/local/Cellar/tesseract/對應版本/tessdata/目錄下即可
更多Tesseract的介紹可以訪問GitHub 這里就不做過多發散了

2、python使用Tesseract庫識別文字

其實在很多場景中,都會有驗證碼環境,一般我們都是用 python + Tesseract來識別驗證碼,這里我們用來識別手游上倒計時也是“適得其用” 來,使用方式如下,首先安裝擴展庫
a、pip install pytesseract 用於python操作pytesseract
b、對要識別的圖片做降噪,簡單的說就是對圖片做簡單處理,如裁剪指定區域,過濾其他文字的干擾,做灰度化、二值化等一些操作,提供文字識別的准確性,具體代碼可以參見如下

!/usr/bin/python

#############################
# #encoding=utf-8
# bifeng 2020.03.23
# python tesseract 文字識別工具類
###############################
import pytesseract
from PIL import Image
class TesseractUtils(object):
    @staticmethod
    def imgae_to_str(image):
        # 處理成黑白圖
        image_gray = image.convert("L")
        # image_gray.show()
        # 進一步增強,使用二值化處理
        image_two = image_gray.point(lambda x: 255 if x > 129 else 0)
        #image_two.show()
        # content = pytesseract.image_to_string(image)  # 解析圖片
        content = pytesseract.image_to_string(image_two, lang='chi_sim')
        print(content)
        return content

    @staticmethod
    def crop_image_to_str(image, left, top, right, bottom):
        '''識別圖片部分區域位置'''
        box = (left, top, right, bottom)
        crop_img = image.crop(box)
        # crop_img.show()
        image_gray = crop_img.convert("L")
        # image_gray.show()
        # 進一步增強,使用二值化處理
        image_two = image_gray.point(lambda x: 255 if x > 129 else 0)
        #image_two.show()
        content = pytesseract.image_to_string(image_two)
        #識別中文
        # content = pytesseract.image_to_string(crop_img, lang='chi_sim')
        print(content)
        return content

使用方法,通過通過調用api,可以識別指定區域圖片如:

TesseractUtils.crop_image_to_str(crop_img, 767, 289, 915, 334)

這里 767, 289, 915, 334 分別代表圖片區域 left, top, right, bottom 坐標如下面一張圖片
在這里插入圖片描述
767, 289, 915, 334 剛好代表紅色框框區域,運行后我們可以得到
00:12:30 這樣一個時間 ,

3、構建定時任務,定時刷怪

轉換我們把它轉化為秒

    def timeStr2s(t):
        '''xx:xx:xx轉換為秒'''
        if t is None:
            return 10000
        time = 10000
        try:
            h, m, s = t.strip().split(":")
            time = int(h) * 3600 + int(m) * 60 + int(s)
        except:
            print("格式不對 : " + t)
        return time

然后在建立定時任務

    fristJob = TesseractUtils.crop_image_to_str(ywImg, 767, 289, 915, 334)
	currSec = time.time()+ StringUtils.timeStr2s(fristJob)
    tiemstr = time.strftime("%H:%M:%S", time.localtime(currSec))
    LogEx.d("excuteJobList in " + tiemstr)
    schedule.every().day.at(tiemstr).do(excuteJobList)

這樣一個定時刷掛任務就完成了,剩下的基本就是封裝刷掛,執行刷改,這里我是這么封裝的

#!/usr/bin/python
# #encoding=utf-8
# bifeng 2020.03.23
# 單步執行路徑
###########################
class CaseInfo(object):
    def __init__(self):
        # 用例名稱
        self.name = ''
        # 模擬執行列表
        self.clicklist = []
        # 單步執行休眠時間
        self.sleepTime = 2
        # 圖片裁取區域
        self.cropBox = []
        # 執行列表單步重復項
        self.innerRepeatIndex = 2
        # 默認循環次數
        self.innerRepeatCount = 20
        # 用例延遲多少秒后執行
        self.excuteDelayTime = 30

name 代表名字
clicklist 代表執行路徑,比如一次野外任務,他有很多點,那么他的路徑就是如下:

    case_table = {
        'yewai0': [
            # Boss坐標
            (1028, 1321),
            # 野外
            (329, 1665),
            # 第1個boss 885,406
            (885, 406),
            # 返回    
            (1009, 1831),
        ]}

self.innerRepeatCount = 20 代表,boss 挑戰按鈕 (885, 406), 這個我會循環執行很多變,保證我第一個按下,從而完成刷改功能,封裝好后,刷掛的代碼就很簡單了

def excuteJobList():
    LogEx.v("----------excuteJobList start---------------------")
    # global runner
    runner = LegendRunner(adbConnectIp)
    caseItem = caseList.pop()
    LogEx.v("case list size = " + str(len(caseList)))
    clickList = caseItem.getClickList()
    LogEx.v("excute job " + caseItem.toString())
    for index in range(0, len(clickList)):
        LogEx.d("click : " + str(clickList[index][0]) + " " + str(clickList[index][1]))
        runner.click(clickList[index][0], clickList[index][1])
        if index == caseItem.getInnerRepeatIndex():
            caseItem.setRepeatCount(90)
            for i in range(0, caseItem.getRepeatCount()):
                LogEx.d("click : " + str(i) + " " + str(clickList[index][0]) + " " + str(clickList[index][1]))
                runner.click(clickList[index][0], clickList[index][1], 0)
                time.sleep(0.4)
        elif index == len(clickList) - 1:
            timestamp = time.strftime("%H:%M:%S", time.localtime(time.time()))
            LogEx.d("--sleep 80 s---------: " + timestamp)
            runner.d.screenshot(os.getcwd() + "/image/" + timestamp + ".png")
            time.sleep(80)

    currSec = time.time() + 3
    tiemstr = time.strftime("%H:%M:%S", time.localtime(currSec))
    schedule.every().day.at(tiemstr).do(scheduleNextJob)
    LogEx.v("----------excuteJobList end---------------------")

我只需要 for index in range(0, len(clickList)): 循環執行這個clicklist 就行了,然后在每個步驟下截圖一張,任務完成后把所有的圖片拼接成一張圖(如何拼接見文章《Python工具類之PIL.Image庫快速實現多圖拼接》)
執行完一個刷改任務,最后我們有建立下一個刷改任務

def scheduleNextJob():
    LogEx.v("----------scheduleNextJob start---------------------")
    global runner
    # runner = LegendRunner(adbConnectIp)
    # 獲取最近的boss定時任務
    caseItem = runner.getRecentlyBossTask()
    # 加入到任務列表
    caseList.append(caseItem)
    # 延時
    currSec = time.time() + caseItem.getExcuteDelayTime() - 50
    if currSec < time.time():
        currSec = time.time() + 1
    tiemstr = time.strftime("%H:%M:%S", time.localtime(currSec))
    LogEx.d("excuteJobList in " + tiemstr)
    schedule.every().day.at(tiemstr).do(excuteJobList)
    LogEx.v("----------scheduleNextJob end---------------------")

掃碼到最近boss任務-》定時執行-》 執行完繼續掃碼下一個最近boss任務,這樣一直循環下去,就完成了一個簡單的自動定時刷怪任務。

4、最終效果

看下最終效果
在這里插入圖片描述
刷怪過程中我們做了每個步驟的截圖,我們看下一次刷怪的效果截圖
在這里插入圖片描述
這樣我們的腳本就開發完成了,剩下的只需要開啟腳本,喝上一杯茶,等上一段時候回來收獲刷怪掉落的神裝就可以了,是不是很爽,
“學以致用”感覺怎么樣。。因為畢竟是外掛的關系,為了避免法律風險,我不能直接提供源碼供大家下載,如果你有更多想法,大家可以私下交流,請加QQ群:1085210541


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM