1. 程式人生 > >利用機器學習實現微信小程序-加減大師自動答題

利用機器學習實現微信小程序-加減大師自動答題

ray ear scree 使用 ims mode sklearn 小結 -i

之前有看到微信小程序《跳一跳》別人用python實現自動運行,後來看到別人用hash碼實現《加減大師》的自動答題領取娃娃,最近一直在研究深度學習,為啥不用機器學習實現呢?不就是一個分類問題嗎

如何實現自動答題微信小遊戲《加減大師》?

思考:

  • 圖像識別嗎?
  • 如何建立特征工程?
  • 選用什麽算法?

一、圖像特征工程

如何獲取手機遊戲上的圖片?

  • 使用adb命令截取手機屏幕;
  • 在PC端和手機端同時運行APowerMirror軟件,將手機投屏到電腦上,然後使用Pillow包中的截圖方法截取電腦上對應手機屏幕的
    區域。
  • 在PC端和手機端同時運行APowerMirror軟件,將手機投屏到電腦上,然後使用Python調用windows的原生API截取電腦上對應手機屏幕的區域。

實驗結果: 三種截屏方式花費的時間差異很大,第一種每次截屏需要0.7s左右,第二種0.3s左右,第三種0.04s左右。

當然選擇第3種咯,下載地址[https://www.apowersoft.cn/phone-mirror],一個好的軟件是成功的關鍵(夠清晰)。

獲取訓練樣本

相關步驟:

1.util.py中的shotByWinAPI函數:首先利用window自帶api獲取全屏圖片,然後自定義config.py的相關參數。

# 從PC端截屏時,截取區域左上角相對桌面的x坐標
'projection_x': 32,
# 從PC端截屏時,截取區域左上角相對桌面的y坐標
'projection_y': 278,
# 從PC端截屏時,截取區域的寬度
'projection_width': 482,
# 從PC端截屏時,截取區域的高度
'projection_height': 854,

可以用window命令鍵PrtScSysRq(F12的右邊),然後復制到畫圖中(1920x1080)。

技術分享圖片


用畫圖的放大鏡放大,圖中紅色框的小方塊位置(32x278)projection_x即32,projection_y即278。

技術分享圖片


在畫圖中計算出截圖的寬度和高度,即projection_widthprojection_height(482x854)
技術分享圖片

2.img_tool.py函數介紹:主要是通過all(img, filename)函數進行圖片分割

srcImg = cv2.imread(os.path.join("ScreenShotForTrain", f), 0)

上述代碼是為了將彩色圖片灰度模式加載
技術分享圖片
-----------------------------------------------------------------------------------------------

def all(img, filename):
    """封裝對圖片的所有操作"""
    img = cropImg(img)
    img = binaryImg(img)

    img1, img2 = cropAgain(img)

    imgs = cutImg(img1, filename + '_1') + cutImg(img2, filename + '_2')

    return imgs

def cropImg(img):
    """裁剪原始截圖"""
    height = img.shape[0]
    img2 = img[int(config.config['exp_area_top_rate'] * height):int(config.config['exp_area_bottom_rate'] * height),:]
    #print('裁剪完畢')
    return  img2

cropImg(img)函數主要是為了裁剪含有數字的區域,通過設置參數

#表達式區域的頂部處於整張圖片的位置(307/854=0.359)
'exp_area_top_rate': 0.36,
#表達式區域的底部處於整張圖片的位置(478/854=0.559)
'exp_area_bottom_rate': 0.56,

如果覺得設置比例太麻煩,可以直接寫死位置(img2 = img[int(307):int(478),:])。得到如下圖:
技術分享圖片
----------------

def binaryImg(img):
    """二值化圖片"""
    ret, thresh1 = cv2.threshold(img, config.config['binary_threshold'], 255, cv2.THRESH_BINARY)
    # ret, thresh1 = cv2.threshold(img, config.config['binary_threshold'], 255, cv2.THRESH_BINARY_INV)
    #print('二值化完畢')
    return thresh1

binaryImg(img)函數主要是為了將圖片二值化,可以參考
Python+OpenCV教程6:閾值分割。得到的圖片如下圖:

技術分享圖片


def cropAgain(img):
    """再次裁剪"""
    height = img.shape[0]
    img1 = img[0:int(0.5 * height), :]
    img2 = img[int(0.5 * height):height, :]
    #print('再次裁剪完畢')
    return img1, img2

cropAgain(img)函數主要是為了將圖片分成上下兩部分

技術分享圖片
技術分享圖片


def cutImg(img, filename):
    """水平分割圖片"""
    sb = np.array(img)
    print(sb.shape)
    sum_list = np.array(img).sum(axis=0)
    start_index = -1
    res = []
    names = []
    index = 0
    for sum in sum_list:
        if sum > 255 * 4:
            if start_index == -1:
                start_index = index
        else:
            if start_index != -1:
                if config.config['type'] == 0:
                    sigleCharWidth = config.config['abd_single_char_width']
                else:
                    sigleCharWidth = config.config['pc_single_char_width']
                #為了防止字符粘連,需要在此處寬度進行判斷
                if index - start_index > sigleCharWidth * 2:
                    res.append((start_index,start_index + (index - start_index) // 2))
                    res.append((start_index + (index - start_index) // 2, index))
                else:
                    res.append((start_index, index))
                start_index = -1
        index += 1

    imgs = []
    count = 0
    for single_char in res:
        start = single_char[0]
        end = single_char[1]
        sub_img = img[:, start:end]
        sub_img = cv2.resize(sub_img, (120, 240), interpolation=cv2.INTER_CUBIC)
        #cv2.imwrite('SingleChar/%s_%d.png' % (filename, count), sub_img)
        #names.append('%s_%d.png' % (filename, count))
        # cv2.imshow(str(count), sub_img)
        imgs.append(sub_img)
        count += 1
    # cv2.waitKey()
    #print('分割,重新設置大小 %s 完畢' %filename)
    return  imgs

設置pc_single_char_width參數值,得到如下圖:

技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片

技術分享圖片 技術分享圖片 技術分享圖片


c = 0
def v_cut(img):
    global c
    """豎直方向切割圖片"""
    sb1 = np.array(img)
    print(sb1.shape)
    sum_list = np.array(img).sum(axis=1)
    start_index = -1
    end = -1
    index = 0
    for sum in sum_list:
        if sum > 255 * 2:
            start_index = index
            break
        index += 1
    for i in range(1, len(sum_list) + 1):
        if sum_list[-i] > 255 * 2:
            end = len(sum_list) + 1 - i
            break
    img = img[start_index:end, :]
    img = cv2.resize(img, (30, 60), interpolation=cv2.INTER_CUBIC)
    #cv2.imwrite('SingleChar/%d.png' %c, img)
    c += 1
    return img

重新固定圖片的大小(30x60),得到如下圖:

技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片

技術分享圖片 技術分享圖片 技術分享圖片


二、訓練模型,建立LR分類器

相關代碼請看ml.py,這裏不過多介紹,直接利用python包from sklearn.linear_model import LogisticRegression

LogisticRegression(class_weight='balanced')

sklearn邏輯回歸(Logistic Regression,LR)類庫使用小結

三、自動答題模式開啟

實現原理

  • 1.截取遊戲界面,本項目中提供了三種方案。
    在PC端和手機端同時運行APowerMirror軟件,將手機投屏到電腦上,然後使用Python調用windows的原生API截取電腦上對應手機屏幕的區域。

  • 2.提取截屏圖片中的表達式區域並進行文字識別,得到表達式字符串。
    由於圖片中的表達式區域固定,而且字符規整,因此這一步不是很困難,我僅僅訓練了一個簡單的邏輯回歸模型就得到了非常高的識別正確率。

  • 3.根據第二步得到的表達式,調用Python的eval()函數,得到表達式結果的正誤,然後點擊手機屏幕的相應區域。當截圖使用投屏的方案時,點擊手機屏幕通過代碼點擊
    電腦上手機的對應區域。

首次操作,生成分類器模型

1.借用投屏軟件,利用畫圖工具配置相關參數config.py,可以參考上面的“圖像特征工程”

2.對於新的手機(我用的是honor8),必須重新訓練模型,設置config.py中的debug參數為True,打開“加減大師”,然後運行main.py,這裏必須手動答題,盡可能多答對一些題,目的為了擴充訓練樣本。

3.步驟2會產生一個SingleCharForTrain文件夾,剔除重復樣本和無關樣本。

4.運行img_tool.py文件,會生成一個SingleCharForTrain文件夾。

5.將步驟4得到的文件夾中的字符進行人工分類,保存至TrainChar文件夾。

6.運行ml.py文件,生成分類器模型lr.pickle

註意桌面上不要有東西遮擋到手機的投影區域

根據分類器模型自動答題

1.修改config.py中的debug參數為False及其他相關參數。

#使用PC進行截圖時點擊手機屏幕正確區域的x坐標
'pc_tap_true_x':117,
#使用PC進行截圖時點擊手機屏幕錯誤區域的x坐標
'pc_tap_false_x':365,
#使用PC進行截圖時點擊手機屏幕正確和區域的y坐標
'pc_tap_y':760,

配置正確和錯誤選擇的橫縱坐標,橫坐標不一樣,縱坐標相同(在同一高度)

2.打開加減大師,直接運行main.py即可。

遇到的問題

Q1: 跑到200步左右就停了?

A1: 如果是誤判的話,把出錯的那張圖重新截圖,將得到的字符添加到TrainChar文件夾中,重新訓練模型

A1: 如果是上一張圖和這張圖相同,再跑一次唄,不相信你運氣會那麽差


Q2: 刷到1000分,結果小程序上不了分

A2: 剛開始以為是答題時間沒有設置隨機的問題,設置main.py

one_tap(res)
# 設置隨機睡眠時間,隨機性防止微信後臺檢測
if (count < 100):
    time.sleep(0.1 * (random.randint(0, 9)))
elif (count <200):
    time.sleep(0.05 * (random.randint(0, 9)))
elif (count <300):
    time.sleep(0.01 * (random.randint(0, 9)))
elif (count < 400):
    time.sleep(0.01 * (random.randint(0, 9)))
elif (count < 500):
    # 可以控制到這一關gg
    if (count == 455):
        time.sleep(3)

然而並沒有軟用,估計是後臺設置(個人認為,當天的分數不能超過第一名太多),反正是前500都能獲得小卡片,你可以嘗試設置比第一名多個幾分或少幾分。

四、源代碼地址

記得給哥們的github打?啊

上代碼:https://github.com/Yiutto/WechatGame_jjds

有問題私聊我[email protected]

最後放出我的娃娃來,手機上顯示的是這樣的

技術分享圖片

到手的時候卻是這樣的(本來以為沒戲了,等了將近一個星期)

技術分享圖片

最後,祝大家都能拿到娃娃!!!

利用機器學習實現微信小程序-加減大師自動答題