1实验环境
操作系统:Windows10
编码语言:Python3.6
编译平台:Pycharm
Python库:os、datetime、matplotlib、opencv-python、time
2实验代码流程图
3代码运行步骤和结果等
3.1 手机和电脑用数据线连接
使用通过数据线连接手机,将开发者模式打开并授权
通过adb命令
adb devices |
可以查看连接的Android设备的信息
3.2 获取手机相关的信息
查看Android手机的分辨率(第四行)
adb shell dumpsys window displays |
获取屏幕密度
adb shell wm density |
获取手机型号
adb shell getprop ro.product.device |
获取Android系统的版本
adb shell getprop ro.build.version.release |
3.3 截屏
输入如下命令:
adb shell screencap -p /sdcard/auto.png |
此时,截屏的图片就保存到 /sdcard/auto.png文件中。
注意:/sdcard/和/data/目录是可以写入的。
可以通过命令查看sdcard目录下所有的文件。
adb shell ls /sdcard/ -l |
通过如下命令把手机上的文件拷贝到电脑上
adb pull /sdcard/auto.png h:\ |
此时,图片就会被拷贝到h:\根目录下了。打开即可看到当前手机的屏幕信息。
3.4 屏幕点击事件
通过如下命令模拟手机的滑动事件
adb shell input swipe x1 y1 x2 y2 duration |
通过adb shell input swipe命令进行滑动
l x1、y1:滑动开始的点。
l x2、y2:滑动结束的点。
l duration:持续的时间(单位ms)。
特殊情况下:如果不写duration参数,就理解为点击事件。如果写duration,然后x1y1和x2y2是相同的点,就表示长按。
跳一跳关键是:duration的值的计算。
尝试:
adb shell input swipe 100 100 100 100 700 |
尝试修改duration的值,看看跳的效果。
求得可以拿到加分的中间值。比如555~871都可以拿到加分(555以下和871以上就不能拿到加分),此时则取中间值为(555+871)/2=713 作为后面计算的参考值。
3.5 duration值的计算
假设我们截屏的效果是如下:
从图中可以看到,时间的值跟开始位置到结束位置的距离有关。
假设时间是t,距离是s。公式应该是s = at
基本思路:两点之间的距离乘以一个时间系数。
所以要从截图上识别出起跳位置的坐标(x1,y1)和目标位置的坐标(x2,y2)。
起跳位置的坐标:小人的底座中心点
目标位置的坐标:目标菱形的中心点
然后计算这两点之间的距离(欧氏距离):sqrt((x1-x2)2+(y1-y2)2)
3.6 寻找关键坐标——起跳坐标
算法策略:获取小人的底座中心点的值作为起跳点。
1 获取小人的所有像素点中y坐标的最大值
2 在小人y坐标的最大值那些像素点中,计算出x的平均值,作为小人底座的x的值。
3 y坐标的最大值减去一个偏移值,就作为小人底座的y值。(注意:该偏移值不同的设备是不同的,同一台设备不同场景下是一样的)
比如教师机的设备中最低点的值是(410,1162),中心值是(410,1142),从而计算出偏移值为1162-1142=20
3.7 获取目标坐标的y值
取屏幕宽和高的一半(x=540和y=960)
我们会发现,目标格子的边沿(x=560,y=980)和这个是差不多的(y的偏差是20,x的偏差是20)
以后每次跳动的时候,假如已经知道目标格子的边沿,和目标坐标的x值,就可以很轻松计算出目标坐标的y值。
注意:每个格子的宽和高的比例是相同的。
方形:左:(560,848) 园:左:(251,876)
右:(1015,848) 右:(522,876)
上:(790,718) 上:(388,799)
下:(790,980) 下:(388,957)
中:(790,850) 中:(388,876)
高和宽的比例:(980-718)/(1015-560) =262/455=0.576。假设该值为p
最后,由已知的目标坐标的x值,求目标坐标的y值。
先附上运行结果,以及截图信息:
在理解了跳一跳的基本思路之后,现在附上完整代码(有注释):
# main.py
# _*_ coding:utf-8 _*_
__author__ = 'WoLykos'
from operations import *
from draw import *
from algorithm import *
import time
import random
# 测试截屏
# def test_screen_cap():
# op = Operation()
# op.screen_cap()
# 测试显示图片
def test_show_pic():
draw = Draw()
draw.show_pic("img/auto.png")
# 测试计算欧式距离
def test_euclidean_distance():
algorithm = Algorithm()
p1 = (3, 4)
p2 = (6, 8)
d = algorithm.euclidean_distance(p1, p2)
print(d)
# 测试寻找关键坐标
def test_find_point():
op = Operation()
im = op.screen_cap()
algorithm = Algorithm()
start_x, start_y, end_x, end_y = algorithm.find_point(im)
print("start_point:", start_x, start_y)
print("end_point:", end_x, end_y)
start_point = (start_x, start_y)
end_point = (end_x, end_y)
distance = algorithm.euclidean_distance(start_point, end_point)
# print(distance)
press_time = algorithm.distance_to_time(distance)
op.jump(start_point, end_point, press_time)
if __name__ == "__main__":
# test_screen_cap()
# test_show_pic()
while True:
# test_euclidean_distance()
test_find_point()
time.sleep(1 + 2*random.random())
# algorithm.py
# _*_ coding:utf-8 _*_
__author__ = 'WoLykos'
class Algorithm:
# 构造器
def __int__(self):
pass
# 计算两点之间的欧氏距离
# p1和p2表示两个点 用元组来表示
def euclidean_distance(self, p1, p2):
return ((p1[0]-p2[0])**2+(p1[1]-p2[1])**2)**0.5 # ((p1[0]-p2[0])**2+(p1[1]-p2[1])**2)**0.5
# 寻找关键坐标
# 返回值1,2 piece_x, piece_y 起跳点的坐标 170,555
# 返回值3,4 board_x, board_y 目标点的坐标 395,425
def find_point(self, im):
# piece_x = piece_y = 0
# board_x = board_y = 0
# 图像的大小
w, h = im.size # (1080,1920)
# 加载图像
im_pixel = im.load()
# 记录小人所有的点
points = []
# 记录y的最大值
piece_y_max = 0
# 1 计算出起跳点 就是小人底座的中心点
# 1.1 获取小人的所有像素点中y坐标的最大值
# 遍历图像中的每一个点
# 遍历每一行
for i in range(h // 3, h * 2 // 3):
# 遍历每一列
for j in range(w):
pixel = im_pixel[j, i]
# print("i = ", i, ",j = ", j, "pixel = ", pixel)
# 判断pixel是否小人所在的位置
# 当该点的RGB值约为56,56,82的时候就可以认为是小人所在的像素点了
if (51 < pixel[0] < 61 and 51 < pixel[1] < 61 and 72 < pixel[2] < 102):
# 把当前的点添加到points数组中
points.append((j, i)) # (x,y)
# 记录下y的值
if i > piece_y_max:
piece_y_max = i
# print("piece_y_max = %d" % (piece_y_max,))
# 1.2 在小人y坐标的最大值那些像素点中,计算出x的平均值,作为小人底座的x的值。
bottom_x = []
for x, y in points:
if y == piece_y_max:
bottom_x.append(x)
piece_x = sum(bottom_x) // len(bottom_x)
# print("piece_x = %d" % (piece_x,))
# 1.3 y坐标的最大值减去一个偏移值,就作为小人底座的y值。(注意:该偏移值不同的设备是不同的,同一台设备不同场景下是一样的)
piece_y = piece_y_max - 20 # 偏移值1130-110=20
# print("piece_y = %d" % (piece_y,))
# 2计算 目标格子的中心点
# 2.1计算目标格子的x值
points = []
# 只取中间1/3进行扫描
for i in range(h // 3, h * 2 // 3):
if len(points) > 0:
break
# 取坐标的一个点作为背景的参照物
last_pixel = im_pixel[0, i]
# 逐个扫描右边的点
for j in range(w):
pixel = im_pixel[j, i]
# 把当前点与最左边的点比较 如果RGB差异比较大 则认为是目标点
# 排除该点为小人像素点56,56,82的可能性,BUG
if not (54 < pixel[0] < 141 and 54 < pixel[1] < 130 and 69 < pixel[2] < 172):
if (abs(pixel[0] - last_pixel[0]) + abs(pixel[1] - last_pixel[1])
+ abs(pixel[2] - last_pixel[2]) > 10):
points.append((j, i))
top_x = []
for x, y in points:
top_x.append(x)
board_x = sum(top_x) // len(top_x)
# print("board_x = %d" % (board_x,))
# 2.2计算目标格式子y值
# 屏幕中心的值
center_x = w / 2 + 20 # x的偏差是20
center_y = h / 2 + 20 # y的偏差是20,园
# 格子高和宽的比例
height_per_width = 262 / 455
# 计算出目标格子的y值(需要转换成整数)
# 从piece_x调到board_x 如果piece_x < board_x则表示从左往右跳
# 如果piece_x > board_x 则表示从右往左跳
if piece_x < board_x:
board_y = int(center_y - height_per_width * (board_x - center_x))
else: # 从右往左跳
board_y = int(center_y + height_per_width * (board_x - center_x))
# print("board_y = %d" % (board_y,))
return piece_x, piece_y, board_x, board_y
# 距离与时间的转换
def distance_to_time(self, distance):
# 当0分的时候 距离为 527.5234591939964 时间为713
p = 713 / 527.5234591939964 # 该算法后面待优化
press_time = distance * p
return press_time
# operations.py
# _*_ coding:utf-8 _*_
__author__ = 'WoLykos'
import os
import datetime
from PIL import Image
# 实现控制安卓
class Operation:
# 构造方法
def __int__(self):
pass
# 截屏
def screen_cap(self):
filename = time = datetime.datetime.now().strftime("%H%M%S")+".png"
# 截屏并保存到手机
cmd = "adb shell screencap -p /sdcard/auto.png"
os.system(cmd)
# 拷贝到电脑
cmd = "adb pull /sdcard/auto.png "+"img/"+filename
os.system(cmd)
# 打开图像文件
return Image.open("img/"+filename)
# 控制屏幕进行跳动
def jump(self, src, dst, press_time):
# print(press_time)
press_time = int(press_time)
cmd = "adb shell input swipe %d %d %d %d %d" % (
int(src[0]), int(src[1]),
int(dst[0]), int(dst[1]),
press_time
)
print(cmd)
os.system(cmd)
大功告成!!
谢谢各位。。