opencv版跳一跳

问题分析

主要问题有三个:

1.如何获取游戏信息
2.如果对游戏内容进行解析,提取关键信息
3.如何对游戏进行操作

问题1和问题3可以理解为整个程序的输入和输出。安卓系统可以使用adb工具对手机进行简单的操作。问题1我们用截屏命令解决;问题3因为跳一跳的输入也很简单就是长按屏幕,所以用adb工具发出在一个小区域滑动的命令即可模拟长按。那剩下的问题就是对游戏信息进行提取了,属于简单场景的图像理解 。
adb的下载地址:http://adbshell.com/downloads

流程和细节

下面给出算法流程,随后进行细节上的阐述。

Step1 缩小图像尺寸,提取ROI区域
Step2 根据颜色特征检测棋子,计算其形心。根据棋子形态固定的特征,把形心向下移动一定的距离获取底部中心P1(x0,y0)。
Step3 以棋子顶部区域附近的颜色为背景色,去除背景区域。在此基础上寻找待跳平台的两个关键交点,计算出待跳平台中心P2(x1,y1)。
Step4 计算P1和P2的几何距离,乘上压力系数获得长按操作的时间量。

具体细节:
Step1:现在手机基本上是1080P,在这个问题上并不需要这么高的分辨率。所以先对截屏图像进行了尺度为4的下采样。此外如图1所示,我们需要寻找的目标只在红框范围内,所以按照图像高度的比例剪裁处这块ROI区域。本文截取的是高度的1/4到1/2处。


opencv版跳一跳_第1张图片
图1

Step2:棋子有比较明显的结构特征,不过根据玩这个游戏的经验,只有棋子是紫色的。所以相对于结构匹配用颜色筛选速度更快,毕竟只需要点运算。如果模板匹配的话,那速度就慢很多了。但是棋子的右侧有两个高亮区域,不容易被判定为紫色,在计算形心的时候会导致向左边偏2个像素,补上这个偏移量即可。随后对形心进行下移,移动量取底部y坐标和形心y坐标的差乘上一个系数,本文中这个系数为6/7。最后结果如图2所示,棋子底部的黑点为计算出的底部中心。


这里写图片描述
图2

Step3 先说下取背景色要注意的细节,通过游戏经验发现,棋子上方区域一般为背景色,但是如果你超越了好友会出现好友头像。所以把这个“上方”定义的再高些,本文取35个像素单位长度。另外这些方块是有阴影的,阴影的颜色比较偏背景色但是又淡很多。所以通过背景色计算出阴影色,在去背景色的过程中实际是去除真正的背景色以及阴影色,留下的就是前景也就是目标。如下图3。


opencv版跳一跳_第2张图片
图3

图3中右下角有个白色的三角,这是因为在实际实现时会判断目标在棋子的左侧还是右侧。如果如图3一样在棋子左侧,则直接置棋子右侧和下方的所有区域为背景(因为目标肯定不在这个区域),若目标在棋子右侧则类似处理。现在的目标就是要找到点pA和pB坐标,利用它们的坐标计算出目标中心点。pB较为简单,以y方向为主序,x方向为次序扫描,扫描到的第一非背景点即为定点pB。不过实践中发现若目标为圆形,其顶部在下采样后变为平的。所以需要取miny上所有的点,计算其x方向上的均值作为顶点pB的x值。
pA则是以x方向为主序,y方向为次序扫描遇到的第一个非背景点。当目标在右侧时类似。在实际实现中并没有进行多次扫描,而是记录了目标点,通过排序计算点pA和pB。

Step4 知乎上有人分析了长按时间和距离的关系,结果为线性关系。所以理论上我们这里取一个系数即可:

按住时间(ms)=两点距离(像素)*系数(ms/像素)

但在实践中发现似乎并不完全是线性关系,本文采用的是分段线性函数。若距离大于100像素取5.8,小于100像素取6。
图4是结果图,可以看出找到的关键点还是较为准确的。

opencv版跳一跳_第3张图片
图4

补充和展望

1.反作弊系统。游戏后台有个反作弊检测,因此我在程序中调整了两次跳的间隔,随机在800ms到1400ms之间。还调整了力度系数,使其在原值得0.98到1.04之间随机浮动。并把按压位置也进行随机。不过写这个程序也不是为了作弊刷分 ,寒假无聊花几个小时练练手而已。

2.深度学习思路。在该程序的基础上,可以跑出一些标注过的数据。input为截屏图,label为输出长按时间。构造一个CNN网络,输出为长按时间,损失函数使用MSE ,以此构造一个解决回归问题的端对端网络。这个想法很自然,果不其然有人先做了:http://news.hexun.com/2018-01-11/192209614.html

3.adb的使用:下载ADB Kits,存在某个目录下。在环境变量path中添加该目录。涉及的命令就两条,具体看实现代码。

4.时间分析。debug模式下,算法大概110ms,获取图片1000ms,按压时间600ms左右,操作间隔1000ms,所以大概是2s一跳。主要瓶颈还是在IO上面,因为截屏先存到sdcard随后复制到PC目录,最后再读到内存。如果直接能在内存里处理获取图片的时间可以大幅下降。不过这个时间其实对这个游戏来说并不慢,因为我还得把操作间隔调整到1000ms,才能保证截图的准确性(棋子不是在空中飞行)。

代码

https://github.com/duzeyan/Jump-jump
代码部分就3个cpp:main.cpp、tools.h、tools.cpp。可以选择只下载这个三个源码文件自己搭建项目。

你可能感兴趣的:(图像处理)