Py学习  »  Python

我用Python合成大西瓜!

数据派THU • 3 年前 • 567 次点击  


来源: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了,有时间会来知乎写写文章。大家可以关注下我的两个专栏:小雨姑娘的机器学习笔记和小雨姑娘的算法笔记,认真写知识的那种,跟这篇文章不一样的,认真的,哈哈。

困到睁不开眼,还是坚持写完了…… 那么,我再去玩两盘大西瓜。

编辑:于腾凯
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/107309
 
567 次点击