我用Python合成大西瓜!
这周五没漂亮妹妹带我出去玩了呜呜,无聊在家扣手机,发现大家都在合成大西瓜 。作为一个未来年轻无为的计算机科学家(或许是人民艺术家),我是不屑于玩这种浪费时间又无聊的游戏的(因为玩了四小时才合成了第一个大西瓜),但为了投身到人民群众中去,我决定尝试写一个程序挂机跑一下。
写了一下午,我的第一版Python大西瓜终于写完了,虽然还是挺智障的,但至少打败了50%的人,也算通过图灵测试了哈哈哈哈哈哈。其实是怕过两天我写个最终版,大家都玩够了,小丑就是我自己了,为了吃一口热乎的s**t,我就现在发了哈哈哈哈。
如果下周五还有人玩这游戏,我还会继续写的。
本文章分为六个部分(如果我能坚持写完不睡着的话):
图像捕捉 水果检测 水果识别 去除噪声 点击控制 游戏策略
话不多说,开冲!
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了,有时间会来知乎写写文章。大家可以关注下我的两个专栏:小雨姑娘的机器学习笔记和小雨姑娘的算法笔记,认真写知识的那种,跟这篇文章不一样的,认真的,哈哈。
困到睁不开眼,还是坚持写完了…… 那么,我再去玩两盘大西瓜。