我用Python合成大西瓜!



來源:Datawhale

本文約2400字,建議閱讀5分鐘你已經是個成熟的西瓜了,要學會自己合成。


這週五沒漂亮妹妹帶我出去玩了嗚嗚,無聊在家扣手機,發現大家都在合成大西瓜 。作爲一個未來年輕無爲的計算機科學家(或許是人民藝術家),我是不屑於玩這種浪費時間又無聊的遊戲的(因爲玩了四小時才合成了第一個大西瓜),但爲了投身到人民羣衆中去,我決定嘗試寫一個程序掛機跑一下。

寫了一下午,我的第一版Python大西瓜終於寫完了,雖然還是挺智障的,但至少打敗了50%的人,也算通過圖靈測試了哈哈哈哈哈哈。其實是怕過兩天我寫個最終版,大家都玩夠了,小丑就是我自己了,爲了喫一口熱乎的s**t,我就現在發了哈哈哈哈。

如果下週五還有人玩這遊戲,我還會繼續寫的。

本文章分爲六個部分(如果我能堅持寫完不睡着的話):

  1. 圖像捕捉

  2. 水果檢測

  3. 水果識別

  4. 去除噪聲

  5. 點擊控制

  6. 遊戲策略

話不多說,開衝!

1. 圖像捕捉

作爲一個計算機程序,合成大西瓜的第一步就是能夠看到大西瓜。

這一步超級簡單了,只需要使用python自帶的windows圖形界面接口win32gui就可以操作。其中最重要的環節就是獲取瀏覽器窗口的上下左右四個頂點的座標,調用FindWindow方法獲取窗口對象,然後把窗口對象傳給GetWindowRect獲取頂點座標。

這地方我卡了很久,因爲FindWindow需要首先知道窗口的名字,我試了試“Edge”不對,“合成大西瓜”也不對,最後機智的我打開了Alt + del,發現它叫“小遊戲:合成大西瓜 - 個人 - Microsoft\u200b Edge”彷彿在對我說:“我不叫喂,我叫……”

然後再用PIL庫的ImageGrab方法截圖屏幕窗口內的像素,轉化成numpy數組,就可以進行後續處理啦。

2. 水果檢測

在我們獲取到圖像後,下一步就是識別水果的位置了,一個自然的思路就是識別圓,因爲這裏的水果都是圓的(我真謝謝作者沒有加個香蕉。什麼?你說甘蔗也是水果……)

然後我還想吐槽一下,下面這個到底是什麼東西啊?是兩排牙齒嗎?有沒有人和我一樣覺得莫名很噁心?而且這玩意轉換成灰度圖以後更噁心……

Ok 基於這個思路,我們只需要找到圖裏面的圓形就好啦!

這裏的思路是OpenCV裏面已經實現好的經典圓形識別方法——霍夫圓檢測法(看清楚,不是霍夫曼,不是霍夫曼,不是霍夫曼)。基本思路就是找幾個邊緣點,然後邊走邊畫圓,最後看圓心是不是在一起吧……我不是搞CV的,如果有錯誤請大佬們評論區指正。

這一步主要有兩個難點吧。第一個就是水果都疊在一起,兩個圓形很容易連通在一起,從而找不到圓。我是通過設置一個13乘13的高斯濾波器,先把邊緣模糊一下,然後再提取會好很多。

第二個難點是cv2.HoughCircles有三四個要調整的參數,怎麼選對最後的識別效果影響很大……我是通過GridSearch遍歷參數看識別效果選定的最後參數,老深度學習煉丹師了哈哈哈。

最後我們獲取的是很多圓形的座標以及半徑。

3. 水果識別

下一步我們拿到一堆圓形後,就是要知道每個圓形對應的是哪種水果了。然後我想採訪一下游戲的開發者,爲什麼橙子 和檸檬 要設計成一樣大?拉瓦錫的棺材板快要壓不住了(物質守恆定律)。

所以我不能夠通過圓形的半徑確定是哪個水果了(大哭),而且霍夫圓檢測的結果擾動也挺大的,同一個水果兩次檢測的半徑可能有一定差別。

到這裏,一個聰明的靚仔就會輸入import pytorch摩拳擦掌準備模式識別了,我這種憨憨選擇的是把圓內部的平均RGB值求出來,然後加上半徑一起對比,找相似度最大的。

覺得有點對不起初中數學老師,忘了根據圓心和半徑怎麼求圓內座標了,求個內接矩形內像素的RGB平均值……

最後就建立了這麼一個從RGB平均值+半徑到水果種類的映射:

fruit_type = {    'GRAPE' : [133.68415638 ,42.41563786,112.84156379, 18],    'KIWI' : [132.0420593,201.00264307,64.44633418, 53],    'CHERRY' : [238.33213966,39.17905103,56.09982095, 28],    'ORANGE' : [246.2261046,129.05342651,21.69071235, 37],    'LEMON' : [237.19926471,216.40716912,65.56176471,  43],    'TOMATO' : [238.18209682 ,89.91402075,95.00730902, 66],    'PEACH' : [235.99086897,165.13803074,97.37832902, 65],    'PINEAPPLE' : [248.87955751,219.50704342,88.25265164,89],    'COCONUT' : [230.9732507,225.98286918,211.98437795, 101],    'WATERMELON' : [236.62388536,100.24692594,121.0164732,143],}


4. 去除噪聲

就當我以爲可以進行下一步時,我又發現了很多問題,主要是因爲大菠蘿 和椰子 ,我愛椰汁 ,我恨椰子。

這菠蘿和椰子有兩層,本身就含有兩個圓,所以識別出來的結果經常出來很多重合的圓,例如下面這樣:

所以我還得想辦法去除重合的圓,這裏我選擇的方法是對所有的圓心兩兩比較,如果他們的距離的和小於兩個圓半徑的和,那麼就是重合了。那麼我們如何判斷哪個圓是正確的呢?繼續比較相似度吧…跟標準圖形相似度大的就是正確的。

雖然方法很樸實無華,但它貌似確實很有用。

5. 點擊控制

這個其實也很簡單,沒什麼難點

使用Python內置的win32api.mouse_event方法模擬鼠標點擊事件就可以,只有一個需要注意的地方是,它是會強制控制你的鼠標……所以如果你的手速不夠快的話,可能點擊不了右上角關閉程序的那個叉叉,只能眼睜睜看着你的水果疊到最上面……

6. 遊戲策略

實際上今天我主要是在摸索怎麼識別和控制了,自己想的算法都沒有實現,爲了喫一口熱乎s**t,現在只是一個智障版本。

策略是如果有一樣的就往那裏落,如果沒有就落在最中間。

這個策略顯然是非常爛的,因爲一樣的話會變大可能對結構更加不利。如果沒有一樣的,落在不同位置也會導致整體結構不同……我覺得首先應該是避免小的在大的上面,類似於2048儘量別往上滑的策略,因爲如果大的在上面小的可能永遠無法合成了。

應該去如何維持一個良好的結構,是不是要像計算機體系結構一樣設計個多級緩存?能不能爲每一步設計一個評估指標,對算法自動進行迭代優化?每次刷新的水果順序是不是相同的,以及是否有一定統計規律?如果我們能夠預先知道後續刷新的水果,我們就可以使用A*或者其他啓發式算法進行搜索了。甚至我們能不能用一些AI方法例如強化學習來做?

有好多好多可以嘗試的有趣的Idea,如果這遊戲下週五還有人玩,我很願意嘗試一些新的思路,這個合成大西瓜系列也會繼續更新哈哈。但是現在,真的,我搞了一整天,滿腦子都是山竹+山竹=櫻桃……再讓我看一眼我就吐了。


7. 關於作者

介紹一下小雨姑娘吧,年輕無爲的計算機博士生,未來的人民藝術家,現居青島。基本平時時間都在看書和做Research了,有時間會來知乎寫寫文章。大家可以關注下我的兩個專欄:小雨姑娘的機器學習筆記和小雨姑娘的算法筆記,認真寫知識的那種,跟這篇文章不一樣的,認真的,哈哈。

困到睜不開眼,還是堅持寫完了…… 那麼,我再去玩兩盤大西瓜。

編輯:於騰凱

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章