用Python玩GTA 5—使用OpenCV讀取遊戲面面

原文名稱:Reading game frames in Python with OpenCV - Python Plays GTA V

原文鏈接:Python Programming Tutorials

原文作者:@Harrison

本文是Harrison《Python Plays GTA V》系列教程第一篇。

當OpenAIs Universe(Universe)出現後,很多文章都在鼓吹大量的遊戲(甚至GTA5)已經做好了迎接AI時代的準備。我當時躍躍欲試,然而GTA5最終被神神秘秘的清除出Universe,連個解釋都沒有。

後來我短暫的放棄了這個念頭,但偶爾想起來依然有點抑制不住的小激動。所以我還是決定在這件事情上多花點功夫,同時好好思考這件事情到底是不是非得用OpenAI不可。OpenAI的好處在於針對一些簡單的遊戲項目可以實現每分鐘上千次的迭代訓練,但是GTA5這種遊戲嘛,情況就不一樣了。

我們來說一下為什麼選擇GTA5。至少對我來說,GTA5是一個有無數理由讓我去進行練習的絕佳環境。在這個開放世界裡你幾乎沒有不能做的事情——舉一個最簡單的例子:自動駕駛汽車。在遊戲中,我們可以用MOD控制時間、天氣、交通、速度、遇到的緊急狀況等等等等。這是個完全的、可以量身訂製(有時需要MOD)的世界。

我的教程有時候經過充足的計劃、有些計划過一點、有的完全沒有計划過。這個項目就是完全沒有計劃的教程之一。我知道不是所有人都有GTA5,不過我想你可以使用其他類似的遊戲來和我一起學習這個項目——我們有很多遊戲都可以拿來使用。(如果使用其他遊戲)你需要對某些部分進行調整以使其正常運行,所以你可能得有點基礎才行。

我的初步目標是創造一個自動駕駛汽車,所以任何一個有公路和汽車的遊戲都可以拿來用。我用來接入遊戲的方法幾乎可以在其他所有遊戲中也使用,如果你選擇了更簡單的遊戲,那就省事多了。因為GTA5的高度擬真,陽光會讓電腦識別變得更加具有挑戰性。

我可能也會嘗試其他遊戲——因為我相信我們可以通過簡單示範來教AI怎麼玩。通過卷積神經網路處理信息,然後讓AI進行練習就行。

我的初步判斷是:

儘管Python並沒有現成的庫可用,不過 1.我們可以讀取屏幕圖像 2.我們可以模擬按鍵

這兩項足夠我們處理常規事務,不過對於深度學習來說,我們可能還想要記錄遊戲世界的進程。好在現在的大部分遊戲已經完全可視化,這已經不再是個難題,我們可以通過追蹤滑鼠和按鍵,這一切都為深度學習提供了條件。

我猜這一路不會一帆風順,但至少會很有趣。我的擔憂主要集中在項目推進的速度上。我們可以做,最少是值得做。

總而言之,這是一個大項目,如果我們不拆分來做,顯然就超綱了。所以我們來一點一點的嘗試,第一步的目標是:

找個像樣的FPS工具以訪問遊戲畫面,能用就行的那種。我們的要求是能看就行。

確保鍵盤輸入的指令可用。我覺得這很簡單,但是必須得試試才知道。

嘗試手柄輸入。特別是轉向、剎車等操作。

嘗試使用OpenCV。希望不會遇到大問題。

在簡單的道路環境下實現自動駕駛。

好了,第一步,我們如何實現讀取屏幕畫面?我一直在想可以做,但還真沒想過怎麼做。所以,Google!我找到不少案例,不過大部分都不能用,只有這個還算可以:Screen Capture with OpenCV and Python-2.7。

注意:導入的時候似乎有點錯誤,ImageGrab是PIL的一部分。

import numpy as npnimport ImageGrabnimport cv2nnwhile(True):n printscreen_pil = ImageGrab.grab()n printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8)n .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3)) n cv2.imshow(window,printscreen_numpy)nif cv2.waitKey(25) & 0xFF == ord(q):n cv2.destroyAllWindows()nbreakn----------------------------------------------------------------------nImportError Traceback (most recent call last)n<ipython-input-3-00f897cb4216> in <module>()n 1 import numpy as npn----> 2 import ImageGrabn 3 import cv2n 4 n 5 while(True):nnImportError: No module named ImageGrabn

果然出錯了,我們改一下:

import numpy as npnfrom PIL import ImageGrabnimport cv2nnwhile(True):n printscreen_pil = ImageGrab.grab()n printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8)n .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3)) n cv2.imshow(window,printscreen_numpy)nif cv2.waitKey(25) & 0xFF == ord(q):n cv2.destroyAllWindows()nbreakn----------------------------------------------------------------------nNameError Traceback (most recent call last)n<ipython-input-4-545ecbe36422> in <module>()n 5 while(True):n 6 printscreen_pil = ImageGrab.grab()n----> 7 printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8) .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3))n 8 cv2.imshow(window,printscreen_numpy)n 9 if cv2.waitKey(25) & 0xFF == ord(q):nnNameError: name uint8 is not definedn

什麼鬼?dtype明顯應該是字元串,而不是什麼沒定義的變數名。這哥們寫完代碼到底運行了沒?

import numpy as npnfrom PIL import ImageGrabnimport cv2nndef screen_record(): nwhile True:n printscreen_pil = ImageGrab.grab()n printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8)n .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3)) n cv2.imshow(window,printscreen_numpy)nif cv2.waitKey(25) & 0xFF == ord(q):n cv2.destroyAllWindows()nbreakn

這回終於正常了。不過這段代碼太長,而且太慢。改一下吧。

import numpy as npnfrom PIL import ImageGrabnimport cv2nnndef screen_record(): nwhile True:n# 800x600 windowed moden printscreen_pil = ImageGrab.grab(bbox=(0,40,800,640))n printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8)n .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3))n cv2.imshow(window,cv2.cvtColor(printscreen_numpy, cv2.COLOR_BGR2RGB))nif cv2.waitKey(25) & 0xFF == ord(q):n cv2.destroyAllWindows()nbreakn

這個看著還不錯,就是太慢了,每秒大概只能有個2-3幀的樣子。再改一下試試。

import numpy as npnfrom PIL import ImageGrabnimport cv2nimport timenndef screen_record(): n last_time = time.time()nwhile True:n# 800x600 windowed moden printscreen_pil = ImageGrab.grab(bbox=(0,40,800,640))n printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8)n .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3))n print(loop took {} seconds.format(time.time()-last_time))n last_time = time.time()nn## cv2.imshow(window,cv2.cvtColor(printscreen_numpy, cv2.COLOR_BGR2RGB))n## if cv2.waitKey(25) & 0xFF == ord(q):n## cv2.destroyAllWindows()n## breakn

還是2-3幀。所以問題不是出在imshow函數上。

import numpy as npnfrom PIL import ImageGrabnimport cv2nimport timenndef screen_record(): n last_time = time.time()nwhile True:n# 800x600 windowed moden printscreen_pil = ImageGrab.grab(bbox=(0,40,800,640))n## printscreen_numpy = np.array(printscreen_pil.getdata(),dtype=uint8)n## .reshape((printscreen_pil.size[1],printscreen_pil.size[0],3))n print(loop took {} seconds.format(time.time()-last_time))n last_time = time.time()n## n## cv2.imshow(window,cv2.cvtColor(printscreen_numpy, cv2.COLOR_BGR2RGB))n## if cv2.waitKey(25) & 0xFF == ord(q):n## cv2.destroyAllWindows()n## breakn

好了,現在的成績是:

loop took 0.05849909782409668 seconds

loop took 0.044053077697753906 seconds

loop took 0.04760456085205078 seconds

loop took 0.04805493354797363 seconds

loop took 0.05989837646484375 seconds

我們還需要一個numpy數組給OpenCV的imshow函數使用。相對於重新編寫.getdata,我還是選擇把ImageGrab.grab(bbox=(0,40,800,640))轉化為numpy數組。重寫是沒必要的。

import numpy as npnfrom PIL import ImageGrabnimport cv2nimport timenndef screen_record(): n last_time = time.time()nwhile(True):n# 800x600 windowed moden printscreen = np.array(ImageGrab.grab(bbox=(0,40,800,640)))n print(loop took {} seconds.format(time.time()-last_time))n last_time = time.time()n cv2.imshow(window,cv2.cvtColor(printscreen, cv2.COLOR_BGR2RGB))nif cv2.waitKey(25) & 0xFF == ord(q):n cv2.destroyAllWindows()nbreakn

做了這麼多事,現在大概有個每秒12-13幀的樣子。這個成績不算好,但是夠用了。

https://www.zhihu.com/video/874326866318745600

——————————————————————————————————————

第一篇翻譯完啦~關於配套的還有一部視頻,本來想做個字幕出來的...但是...我這初中英語水平實在是應付不來了...有機會再做吧~(考慮後面利用語音識別做個機翻)

翻譯這篇文章一方面是對Python的興趣,另一方面也算是鍛煉自己的英文水平了。巧的是,這兩個技能水平都不高,所以如果看到錯誤,歡迎指正~

你想更深入了解學習Python知識體系,你可以看一下我們花費了一個多月整理了上百小時的幾百個知識點體系內容:

【超全整理】《Python自動化全能開發從入門到精通》筆記全放送


推薦閱讀:

Python and Visual C++ Build Tools
從零開始寫Python爬蟲 --- 1.3 BS4庫的解析器
Python數據分析及可視化實例目錄

TAG:Python | Python入门 | Python教程 |