一、小程序:跳一跳
本质上是棋子坐标到新物件中心的距离ds与屏幕长按时间dt的比例关系
二、使用到的工具和用法
1、adb工具包
adb全名Andorid Debug Bridge. 顾名思义, 这是一个Debug工具。
用途:
(1)、截屏(保存为screenshot.png
)
adb shell /system/bin/screencap -p /sdcard/screenshot.png
(2)、将截屏图片推送到电脑(temp.png
)
adb pull /sdcard/screenshot.png E:\\testproject\\temp.png
(3)、模拟屏幕长按
adb shell input swipe x1 y1 x2 y2 dt
swipe
模拟的是屏幕的滑动,x1,y1到x2,y2的滑动,延迟dt秒
2、python库
(1)子进程库subprocess
用途:执行上述adb命令
基本使用1:subprocess.Popen(args)
,其中args为命令行,这个是最基本的使用,还有很多参数,不细说了。注意如果其他参数都默认,该子线程将独立运行,即子线程间不等待。
基本使用2:subprocess.call(args)
,是上面的封装,多个子线程会按照顺序依次执行。
(2)图像处理库opencv
用途:从截图上识别棋子的坐标和新物件的中心坐标
步骤:
1、读取截屏图片,并做切割,主要是去除图片上下其他内容的影像
image_name = 'temp.png'
# 图片读取
img = cv2.imread(image_name)
# 图片切割
roi = img[400:1000, 80:680]
2、灰度处理和边缘检测,获取轮廓图
# 灰度处理
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# canny边缘检测,用于识别落点
edges = cv2.Canny(roi, 50, 100)
3、识别棋子:通过识别棋子顶部的圆实现
使用到opencv库中的霍夫圆变换
cv2.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius)
主要参数:
image
输入 8-比特、单通道灰度图像.
method
Hough 变换方式
dp
累加器图像的分辨率,dp的值不能比1小。
min_dist
该参数是让算法能明显区分的两个不同圆之间的最小距离。
param1
用于Canny的边缘阀值上限,下限被置为上限的一半。
param2
累加器的阀值。
min_radius
最小圆半径。
max_radius
最大圆半径。
返回值为检测到圆的序列,包括圆心坐标和半径
这里的参数,只要半径是19-21左右,其他需要多次尝试后,取合适的值
# 检测圆心和半径
def findring(gray):
circles1 = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 2,
400, param1=100, param2=50, minRadius=19, maxRadius=21)
# print(circles1)
circles = circles1[0, :, :]
circles = np.uint16(np.around(circles))
ring = circles[0]
return ring
4、识别新物件的中心
这个是通过新物件最高点(x最小)和最右端(y最大)来确定的
分三步:
(1)确定棋子位置后,消除棋子的轮廓。因为有的是有新物件靠的比较近或者新物件比较小。棋子高度影响对新物件坐标的识别。
(2)确定这个edges的最高点,找到x最小的点,一般就是新物件的最高点,这时的y坐标为中心点的y坐标
(3)从新物件最高点向右迭代,找到y最大值的点,即最右端。这时的x坐标为中心的x坐标
def findtop(edges):
x,y = np.where(edges==255)
# 获得x最小时的索引,并获得对应的y坐标
index = np.argmin(x)
ymin = y[index]
# 从该位置向右400个像素,查找y最大值,获得对应的x坐标
xmax = x[np.argmax(y[index:index+400])]
return ymin,xmax
这里返回值中,xy坐标相反是为了视图显示的方便,因为cv2画图时候的x,y分别表示对应左边界和上边界的距离,和numpy读取图片像素矩阵的索引x,y(行和列)正好相反。
三、其他注意点:
1、获得棋子上端圆心坐标后,做修正获得棋子落点坐标
2、新物件中心坐标获得后,稍微向上做修正,因为随着游戏后期新物件变小,中心坐标要做稍微上移,增加容错率。游戏初期新物件比较大,问题不大。
3、计算ds,增加系数(约2.1)计算dt。系数根据屏幕大小有区别,多尝试几次做相应调整。
ds = math.sqrt((e_pos[0] - b_pos[0]) ** 2 + (e_pos[1] - b_pos[1]) ** 2)
a = 2.1
dt = int(a*ds)
jump(dt)
time.sleep(1.5)
4、每次jump后,要等待1.5秒左右,一方面是为了某些额外加分。另一反面有时超越好友会有动画效果,会影响新物件坐标的识别。
四、写在最后的最后
分数不要刷太高,有时候排行榜不会显示的。。。