Appium实现跳一跳

在小程序出来之初,其实心里就有一个想法,用APP自动化的方式实现自动跳一跳。当然不是为了刷分。 但由于是游戏,是无法直接取页面元素的,因此只能用到图像识别。本人对图像处理只是有一点点了解,因此一直没有动笔,偶然看到一篇文章,通过adb+python的方式实现了。原文在这里
但是由于作者写作过程中有诸多语义不清的地方,于是在借鉴该作者的方式实现后决定重新写一篇,通过Appium+python的方式实现。
不过主要的图像识别技术依然参考该作者的内容。

需要应用到的Python库及环境:

  • Appium环境: 点这里
    提供微信的基本操作,截图以及屏幕按压(点击);
  • opencv计算机视觉库:pip install opencv-python
    识别棋子中心和物件中心的坐标点;
  • math提供基本的数学运算,python内置库
    实现开平方根,计算棋子与物件中心的距离;
  • numpy数值计算扩展,主要用于处理各种大型矩阵,图像的像素内容就是以矩阵的形式储存。numpy库在安装opencv-python时会同时安装。
    由于opencv中的像素都是储存于numpy中,处理opencv识别到的各种坐标。

先说一下思路:
我们先看一下游戏界面;


跳一跳

玩过跳一跳的都知道,跳一跳游戏其实就是在前方(上方)出现各种物件,有各种形状。通过估计棋子(小人)与前方出现物件中心点的位置,估计距离,通过距离判断需要点击的时间。松开手指,完成跳跃。重复此过程,直到失败。


操作过程

那么要自动完成这一跳跃操作的关键点就是确定按压时间
要想确定按压时间,就必须取得:

  1. 棋子与物件中心点的距离
  2. 跳跃系数,也就是跳跃单位距离需要按压的时间,或者单位按压时间跳过的距离。

首先是计算棋子与物件中心点的距离:

本文以下内容均以小米Note3手机为测试机,分辨率为1080x1920
其他机型需要根据实际分辨率调整。

上面说过,无法通过定位工具识别棋子与物件,因此就需要用到图像识别。在识别之前需要通过Appium提供的截图方法获取图像,并保存在电脑本地,然后通过opencv对图像进行处理和识别。

图片预处理
  1. 截图
    用Appium WebDriver中提供的截图方法获取手机屏幕图片,并储存在本地文件夹:
# 截取当前图片
driver.driver.save_screenshot('E:/tyt/tyt_origina.png')
  1. 为减少其他图形的干扰,切出上图中红框标识的内容,具体切图的大小,需要根据实际屏幕确定。
    # 读取图片
    img = cv2.imread('E:/tyt/tyt_origina.png')
    # 正常颜色的图片,以三维数组的形式保存每个像素点的颜色值
    # array([[[247, 204, 199],  [纵坐标[横坐标[像素点的BGR值]]]

    # 切出图片中间位置
    roi = img[600:1500, 0:1080]
切割后的图片
  1. 识别边缘
    通过cv2提供的Canny算法识别图像边缘
    # 边缘识别
    edges = cv2.Canny(roi, 50, 100)
    # 边缘识别后图片变为二维数组,仅储存0,255两种RGB值,也就是黑白两色,黑色0为背景白色255为轮廓
    # array([[0, 0, 0, ..., 0, 0, 0],  [纵坐标[横坐标 黑白两色0/255]]

Canny算法:
Canny(image, threshold1, threshold2)
主要参数:
image,图片内容,这里使用的切出来的图像;
threshold1,threshold2 阈值范围,设置为50,100即可。

边缘识别后的图片
识别棋子

接下来就要在识别后的图像中确定棋子和新物件的位置:

识别图示

棋子顶部是圆的,可以用到opencv中提供的霍夫圆变换算法识别圆,霍夫圆根据圆的半径在图像中寻找对应的圆,并返回该圆圆心所在的坐标点,也就是找到了头部中心;根据棋子头部中心点加上身体所在的高度就能得到底部中心点,即图中绿线部分。

  1. 识别棋子头部
    通过霍夫圆变换算法获取圆心所在的位置,需要确定圆的半径,可通过Windows自带的画图板获取坐标,计算出半径即可。


    画图板获取坐标位置

    1080x1920的棋子头部半径大约是30像素左右。获得半径后就可以识别圆了。

    # 通过霍夫圆变换识别棋子头部的圆
    circles1 = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 2, 400,
                                param1=100, param2=50, minRadius=29, maxRadius=31)
    # array([[[443. , 289. ,  29.8]]], dtype=float32)

cv2.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius)
  主要参数:
  image, 输入 8-比特、单通道灰度图像。传入我们边缘识别后的图片即可;
  method,Hough 变换方式,目前只有HOUGH_GRADIENT这种方式;
  dp, 累加器图像的分辨率,dp的值不能比1小。先设置为2吧;
  min_dist, 该参数是让算法能明显区分的两个不同圆之间的最小距离。一般也就只会识别一个,这个参数随便设置;
  param1, 用于Canny的边缘阀值上限,下限被置为上限的一半。设置100即可;
  param2,累加器的阀值。设置为50;
  min_radius, 最小圆半径,由于根据手机分辨率,圆的半径在30左右 ,因此设置为29,不能太小,否则有可能识别到其他的圆;
  max_radius,最大圆半径,比实际半径大1像素即可。至于为何不设置为30,30,因为始终有一点误差;
返回值为检测到圆的序列,包括圆心坐标和半径。

array([[[443. , 289. , 29.8]]], dtype=float32)即为识别到的圆443.和289.为圆心坐标,29.8为半径。float32表示数据的类型。
接下来取出具体的圆心坐标值。

    # 取出第一个圆
    circles = circles1[0]
    # array([[443. , 289. ,  29.8]], dtype=float32)

    # 转化为uint16类型
    circles = np.uint16(np.around(circles))
    # np.around 四舍六入五成双

    ring = circles[0]
    # array([443, 289,  29.8], dtype=uint16)

ring就是圆心的坐标,对于棋子,我们还有两件事要做。
计算棋子底部中心消除棋子,计算底部中心大家都能理解,为什么要消除棋子,是因为如果新物件离得太近,棋子头部的轮廓会影响对物件的判断。

  1. 计算棋子底部中心点
    这一步最简单,通过画图板工具,获取头部中心点到底部中心点的距离。


    棋子

    通过头部中心点加上图中绿色线标识出的距离既是底部中心点的中心。1080x1920像素的手机这段距离大约是159。

# 圆心的y坐标+159既是底部中心点的位置
ring[1] += 159
# array([443, 448,  29.8], dtype=uint16)
  1. 消除棋子
    通过棋子头部中心点和底部中心点,计算出一个矩形位置,通过对这块位置切片,并全部赋值为0,0表示黑色,将该区域的全部像素设置为0,则可以消除这块区域,也就是棋子所在的区域。
    # 根据圆心,计算圆周围的矩阵,然后全部设置为0,相当于把包括圆在内的所有轮廓消除
    # 圆的半径30多加5个像素
    # 圆上部
    ryt = ring[1] - 35
    # 圆下部
    ryb = ring[1] + 35
    # 圆左边
    ryl = ring[0] - 35
    # 圆右边
    ryr = ring[0] + 35

    edges[ryt:ryb, ryl:ryr] = 0

以上,关于棋子的图像处理部分结束。

识别新物件
识别图示

根据上图,可以看出新物件的的y坐标是整个图最高(y坐标最小),x坐标在最右边,也就是x坐标是整个图的最大值。但是有时候新物件在左边,因此只能先取出y坐标最小的,然后右移一定的像素位置来计算新物件的x坐标,因为此时x坐标不是在最右边。

    # edges == 255 取edges数组中值为255(白色)的所有下标
    # edges中排列的[纵坐标,横坐标]的顺序
    y, x = np.where(edges == 255)

通过np.where(edges == 255)获取所有白色的像素点。由于像素点在数组中的排列是纵坐标在前,横坐标在后。因此y,x倒过来取。

    # 找到y轴最小值,y轴最小值的时候就是x轴的中心点
    index = np.argmin(y)
    xmin = x[index]

    # 找到x轴最大的点,即最右端的点,此时的y坐标就是y的中心点
    # 但是因为中间识别有凸起部分,因此要设定查找位置
    ymax = y[np.argmax(x[index:index + 600])]

xmin和ymax就是新物件中心的坐标点。

计算距离

棋子中心点ring[0], ring[1]和新物件中心点xmin, ymax的距离,相当于抛物线上两点的距离。因此需要用到抛物线计算公式。

抛物线两点距离公式:点A(a,b)点B(c,d)距离 = 根号[(a-c)平方+(b-d)平方]

    # 抛物线上两点之间的距离为"根号((x2-x1) ** 2 + (y2-y1) ** 2)"
    dt = math.sqrt((xmin - ring[0]) ** 2 + (ymax - ring[1]) ** 2)
估算跳跃系数

单位像素距离需要按压的时间,大致在1-2.5之间。
需要多尝试一下,不同的分辨率不一样。
1080*1920的屏幕大致在1.365。

ds = int(dt * 1.365)

以上,核心内容都已实现。接下来只需要按压屏幕即可。

driver.tap([[300, 1000]], ds)

最后跳动大约60-80次左右,物件越来越小,因此为了增加容错率,需要将物件中心坐标略微上移10个像素左右。
ymax = y[np.argmax(x[index:index + 600])] - 10


分数

当然这个分数不会在排行榜显示。不过新技能又get了。

点我看完整脚本!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容