python實現自動找茬遊戲,python實現找茬
http://blog.csdn.net/pipisorry/article/details/46564967
找茬遊戲地址[美女大家來找茬]
遊戲視窗探查
下載安裝PyWin32庫(對windows介面的Python封裝)http://sourceforge.net/projects/pywin32/,但不能直接點Download表徵圖,不然下下來是一個Readme.txt,點“Browse All Files”尋找需要的版本。
使用spy++找到視窗控制代碼(或者找到視窗類別名lpClassName和視窗名lpWindowName)[python指定視窗-spy++用法]
hwnd = win32gui.FindWindow("MozillaWindowClass", "遊戲全屏 - Mozilla Firefox")if not hwnd: print(RED, 'window not found!', DEFAULT)else: print(hwnd)# hwnd = 918912 #或者直接使用控制代碼
Note:
1. FindWindow函數:
FindWindow這個函數檢索處理最上層視窗的類名和視窗名稱匹配指定的字串。這個函數不搜尋子視窗。
函數功能:該函數獲得一個頂層視窗的控制代碼,該視窗的類名和視窗名與給定的字串相匹配。這個函數不尋找子視窗。在尋找時不區分大小寫。
函數型:HWND FindWindow(LPCTSTR IpClassName,LPCTSTR IpWindowName);
參數:
IpClassName :指向一個指定了類名的空結束字串,或一個標識類名字串的成員的指標。如果該參數為一個成員,則它必須為前次調用theGlobafAddAtom函數產生的全域成員。該成員為16位,必須位於IpClassName的低16位,高位必須為0。
IpWindowName:指向一個指定了視窗名(視窗標題)的空結束字串。如果該參數為空白,則為所有視窗全匹配。
返回值:如果函數成功,返回值為具有指定類名和視窗名的視窗控制代碼;如果函數失敗,返回值為NULL。
[FindWindow用法]
[findwindow-baike.baidu]
2. 控制代碼
直接使用控制代碼可能的問題:
1)firefox中每個頁面tab都是同一個控制代碼,程式中找到的控制代碼對應當前tab,如果當前tab不是找茬遊戲就不對了
2)使用視窗類別名lpClassName和視窗名lpWindowName時如果當前tab不是找茬遊戲會找不到相應控制代碼
上面問題一個有效解決方案是將找茬遊戲單獨在新視窗中開啟
也可以使用spy++反向從控制代碼找到控制代碼對應視窗,檢查是否正確。
遊戲圖片提取
提取圖片採用了截屏的方式,找到視窗後將視窗提到最前,再作視窗截屏。
下載安裝PIL圖形處理庫[linux和windows下安裝python拓展包-...PIL、pythonqt...]
win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE) # 強行顯示介面後才好,SW_RESTORE初始大小win32gui.SetForegroundWindow(hwnd) # 將視窗提到最前# 裁剪得到全圖game_rect = win32gui.GetWindowRect(hwnd)# src_image = ImageGrab.grab(game_rect)src_image = ImageGrab.grab((game_rect[0] + 20, game_rect[1] + 176, game_rect[2] - 523, game_rect[1] + 176 + 514))# src_image.show()width, hight = src_image.size# 分別裁剪左右內容圖片left_box = (0, 0, width // 2 + 1, hight)right_box = (width // 2 + 1, 0, width, hight)image_left = src_image.crop(left_box)image_right = src_image.crop(right_box)# image_left.show()# image_right.show()
Note:
1.win32gui.GetWindowRect函數
l,t,r,b
=
win32gui.GetWindowRect(
self
.hwnd)
#返回圖形左、上、右、下邊界
2.ImageGrab.grab函數:ImageGrab是PIL的一個模組,用於映像的抓取。不帶參數的ImageGrab.grab()進行全屏截屏,返回一個Image對象,也可使用一個元組作為參數指定要截取的範圍(左上與右下兩點的座標),這兩種截屏都是不帶滑鼠指標的。
上面用到的座標都為為了示範代碼簡單填的,實際上可使用了變數參數,而且要區分解析度什麼的。參數也可以為圖形要截取的左、上、右、下邊界
還有一個ImageGrab.grabclipboard()可從系統剪貼簿採集映像。
3. 得到Image映像後可用show()方法,使用系統預設的映像查看工具開啟,方便調試。
也可以用save(filename)儲存成檔案,對應的可以Image.open(filename)開啟獲得。
4. crop(box)方法:參數為左上和右下標點座標
grab得到了一個包含左右圖片的Image對象後,用crop(box)方法可裁剪得到其中指定的地區,分別拿到左右兩個遊戲圖片。
對比獲得兩圖內容不同的地區
把兩圖裁剪成N個小圖片分別對比,左右統一地區對應的小圖片不相等則為“茬”區,問題是怎麼判斷兩個圖片內容不一致?
Image.histogram()函數:用於得到映像的顏色長條圖。長條圖可以表示一張圖片中各種亮度(或顏色)的數量,兩張自然圖片的長條圖基本是不一樣的,除非兩圖對稱、顏色一致但排列不一,但就算如此,將兩圖繼續分割下去,其子圖的長條圖也會不一樣。長條圖就是一種圖形到數值的轉換,對比兩圖的顏色數值就可知是否存在差異。一張用RBG顏色格式的映像,histogram()函數將返回一個長度為768的數組,第0-255表示紅色的0-255,第256-511表色綠色的0-255,第512-767表色藍色的0-255,數值表示該顏色像素的個數。因此,histogram()列表所有成員之和等於改映像的像素值 x 3。
用來獲得兩圖比較的數值差的函數
def channel_compare(cha_a, cha_b): ''' 比較兩個色彩通道的差異值並返回 ''' sum_a = sum([i * v for i, v in enumerate(cha_a)]) sum_b = sum([i * v for i, v in enumerate(cha_b)]) # red_a = 0 # red_b = 0 # for i in range(0, 256): # red_a += histogram_a[i + 0] * i # red_b += histogram_b[i + 0] * i if sum_a + sum_b > 0: diff_channel = abs(sum_a - sum_b) * 10000 / max(sum_a, sum_b) return diff_channeldef image_compare(image_a, image_b): ''' 返回兩圖的差異值, 返回兩圖紅綠藍差值萬分比之和 ''' histogram_a = image_a.histogram() histogram_b = image_b.histogram() if len(histogram_a) != 768 or len(histogram_b) != 768: print(RED, "get histogram error", DEFAULT) return None print([i * v for i, v in enumerate(histogram_a)][0:10]) diff_red = channel_compare(histogram_a[:256], histogram_b[:256]) diff_green = channel_compare(histogram_a[256:512], histogram_b[256:512]) diff_blue = channel_compare(histogram_a[512:768], histogram_b[512:768]) return diff_red, diff_green, diff_blue
Note:將函數返回的紅綠藍差值相加,如果超過了預定定的閥值2000,則表示該地區不同。這個計算方式有點“土”,但對這次要解決的問題很有效,就沒再繼續改進。
把兩圖裁剪成N個小圖片分別對比
# 將左右大圖裁剪成多個小圖分別進行對比result = zeros((width // 10, hight // 10))for col in range(0, width // 10): for row in range(0, hight // 10): clip_box = (col * 10, row * 10, (col + 1) * 10, (row + 1) * 10) clip_image_left = image_left.crop(clip_box) clip_image_right = image_right.crop(clip_box) clip_diff = image_compare(clip_image_left, clip_image_right) if sum(clip_diff) > 2000: result[row][col] = 1
Note:大圖是780*520,分隔成10x10的小塊,定義一個78*52的二位元組儲存結果,分別比較後將差值大於閥值的數組地區標記為1.
在遊戲上標記兩邊不同的地區
可以使用PyWin32的函數,獲得遊戲視窗控制代碼後直接在上面繪製,但要熟悉Windows編程,解決遊戲自身重繪後將我的標記擦除的問題。
也可以使用Qt。下面用Qt建立了一個和遊戲大小一樣透明的QWidget視窗,疊加在遊戲視窗上,用遮罩來繪製標記。標記資料已記錄在result數組中,在指定的位置繪製一個方格則表示該地區左右不同,要注意兩個方格間的邊界不要繪製,避免格子太多幹擾了遊戲。除標記外,還繪製了兩個按鈕來觸發對比與擦除。
這裡我沒有替換變數,太麻煩了,能看清楚演算法就行。
from:http://blog.csdn.net/pipisorry/article/details/46564967
ref:PIL Document