python+opencv實現微信跳一跳輔助
- 在PC端實現手機截屏並複製到PC中用到兩條命令:
adb shell screencap -p /sdcard/0.jpg adb pull /sdcard/0.png ./op_screencap/jump.jpg os.system(命令)#執行命令(需要引入 os)
- 獲取小人位置,這裏使用了opencv的模板匹配技術,用這種方法測試未發現識別不到問題,需要用到小人的模板(用自己手機截取屏幕並裁剪出來)
- 獲取底部小人座標
def get_people_loc(self,img):
'''
獲取小人的位置範圍
:param img:
:return:返回帶有小人範圍的圖和小人座標
'''
#定義小人位置
peo_loc=[0,0]
#與模板進行匹配識別 獲取識別結果矩形範圍
min_val, max_val, min_loc, max_loc = self.matchTemplate(img,self.template)
#繪製識別結果
draw_peo_result = cv2.rectangle(img, max_loc, (max_loc[0] + self.w, max_loc[1] + self.h), color=(255,0,0), lineType=8,thickness=3)
#計算小人底部座標
peo_bottom_x=int(max_loc[0]+(self.w)/2)
peo_bottom_y=int((max_loc[1]+self.h-19))
peo_loc=(peo_bottom_x,peo_bottom_y)
return (draw_peo_result,peo_loc)
- 獲取目標座標(這裏也是用到模板匹配,需要用到中心白點的模板,由於遊戲中白點存在三種情況:1.白點和背景顏色相差大 2.白點和背景顏色很接近如白色方塊 3.上一次沒跳中中心,因此沒出現白點,需分開處理)
def get_target_loc(self,cv_img):
'''
獲取目標位置座標
:param img:
:return:識別結果的圖片和目標座標(失敗返回源圖像和(0,0))
'''
cv_img_gray=cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
min_val2, max_val2, min_loc2, max_loc2 =self.matchTemplate(cv_img_gray,self.template_center_gray)
print('模板一匹配中心點 匹配度:%d%% 設定匹配閾值:80%%' % (max_val2 * 100), end=' ')
# 計算中心座標
cen_x = int(max_loc2[0] + (self.w2 / 2))
cen_y = int(max_loc2[1] + self.h2 / 2)
draw_cen_result = cv2.circle(cv_img, (cen_x, cen_y), 2, color=(255, 0, 0), thickness=3)
#預防分數干擾 把分數部分設置爲全黑色
draw_cen_result[0:500]=[0,0,0]
if(max_val2>0.8):
print('模板一匹配中心點成功')
return (draw_cen_result,(cen_x,cen_y))
else:
cv_img_gray= cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
center_white_template_gray= cv2.cvtColor(self.center_white_template2, cv2.COLOR_BGR2GRAY)
min_val3, max_val3, min_loc3, max_loc3=self.matchTemplate(cv_img_gray,center_white_template_gray)
# 計算中心座標
cen_x3 = int(max_loc3[0] + (self.w2 / 2))
cen_y3 = int(max_loc3[1] + self.h2 / 2)
#print('cen_x3:%d cen_y3:%d' % (cen_x3, cen_y3), end=' ')
draw_cen_result = cv2.circle(cv_img, (cen_x3, cen_y3), 2, color=(255, 0, 0), thickness=3)
print('模板一匹配中心點失敗\n模板二匹配中心點 匹配度:%d%% 設定匹配閾值:80%%' % (max_val3 * 100), end=' ')
if (max_val3 > 0.8):
print('模板二匹配中心點成功')
return (draw_cen_result, (cen_x3, cen_y3))
else:
print('模板二匹配中心點失敗')
return (draw_cen_result,(0,0))
上面只處理了存在中心白點的情況,如果沒有中心白點出現,或者匹配失敗,這裏使用了第三中獲取方法,通過matplotlib庫把圖片show到figure中,使用鼠標輔助獲取到目標的位置(比較笨拙的方法==)代碼省略====完整代碼如下(大神請繞道、、、不喜勿噴、、變量的名字是隨便命名的,大家自行理解、、、、):
- ImgHelper.py
import matplotlib.pyplot as plt
from PIL import Image
import adbOperate
import image_analysis
import time
import random
import cv2
class ImageHelprt:
#定義小人座標
peo_loc=(0,0)
#定義跳躍目標的座標
tar_loc=(0,0)
#定義鼠標點擊類型
click_count=1
reg_event=True
def __init__(self):
self.figure=plt.figure()
self.tempPath=''
self.count=0
self.img_analysis=image_analysis.IMG_Aanlysis()
self.CLICK_SOURCE=(500,1514)
self.exit=False
#設置限制跳躍次數
self.limit_count=50
def OnBtn_pressEvent(self, event):
'''
鼠標點擊圖片時觸發的時事件
:param event:
:return:
'''
eX=event.xdata
eY=event.ydata
if(self.click_count==0):
self.click_count=1
else:
print('點擊位置 X:', eX, ' Y:', eY)
#第二次點擊計算距離
self.tar_loc=(eX,eY)
self.click_count=1
distance=self.cal_distance(self.peo_loc,self.tar_loc)
click_loc=self.cal_click_loc(self.CLICK_SOURCE)
#操作ADB 控制手機跳躍
adbOperate.jump(distance,click_loc=click_loc)
self.draw_result(self.cv_img, self.peo_loc, (0,0),(int(self.tar_loc[0]),int(self.tar_loc[1])) , click_loc)
plt.close()
def show_img(self):
'''
顯示照片到當前figure中
:param img: 要顯示的圖片
:return:
'''
#讀取截圖
self.img= Image.open(self.tempPath)
self.cv_img=cv2.imread(self.tempPath)
if(self.jump_count>=self.limit_count):self.exit=True
#清空GDI容器
self.figure.clear()
#註冊鼠標點擊事件
if(self.reg_event):
self.figure.canvas.mpl_connect('button_press_event', self.OnBtn_pressEvent)
#分析圖片
#獲取小人位置
(cv_img,peo_loc)=self.img_analysis.get_people_loc(self.cv_img)
# 獲取目標位置
(cv_img,target_loc)=self.img_analysis.get_target_loc(self.cv_img)
if(target_loc[0]==target_loc[1]==0):
self.figure=plt.figure(num='請用鼠標點擊目標棋子中心')
#說明沒有識別到下一跳中心位置,彈出手動選擇
self.figure.canvas.mpl_connect('button_press_event', self.OnBtn_pressEvent)
print('請手動選擇目標!')
self.peo_loc=peo_loc
plt.imshow(self.cv_img,animated= True)
plt.show()
print('分析中...')
else:
#自動跳躍
#計算距離
distance=self.cal_distance(peo_loc=peo_loc,tar_loc=target_loc)
#print('距離:%d' %distance)
click_loc = self.cal_click_loc(self.CLICK_SOURCE)
adbOperate.jump(distance,click_loc)
self.draw_result(cv_img,peo_loc,target_loc,(0,0),click_loc)
self.pull_screenshot()
def cal_distance(self,peo_loc,tar_loc):
'''
計算小人與目標方塊中心的最短距離
:param peo_loc:小人位置座標
:param tar_loc:目標方塊位置座標
:return:計算結果
'''
distance = (tar_loc[0] - peo_loc[0]) ** 2 + (tar_loc[1] - peo_loc[1]) ** 2
distance = distance ** 0.5
return distance
def cal_click_loc(self,source_loc):
'''
隨機產生一個點擊位置
:param source_loc:源座標
:return: 源座標+隨機數
'''
click_x=500+random.randint(1,30)
click_y=1514+random.randint(1,30)
return (int(click_x),int(click_y))
def pull_screenshot(self):
'''
調用adb操作類執行截圖 保留圖片路徑 繼續分析下一跳
:return:
'''
time.sleep(1.5+random.randint(1,50)/100)
self.tempPath = adbOperate.pull_screenshot(self)
#是否結束遊戲
if(self.exit):
print('************到達限制次數:%d 自動結束跳躍************' %self.limit_count)
return
else:
# 分析下一跳的圖片
self.show_img()
def draw_result(self,src_img,peo_loc,tar_loc,click_tar_loc,click_loc):
'''
保存識別結果
:param src_img:源圖像
:param peo_loc:人位置
:param tar_loc:識別的目標
:param click_tar_loc:點擊的目標位置
:param click_loc:模擬點擊的位置
:return:
'''
draw_img=src_img
#人
draw_img=cv2.circle(draw_img,peo_loc, 3, color=(0, 255, 0), thickness=3)
#識別的目標
draw_img =cv2.circle(draw_img,tar_loc,3,color=(255,0,0),thickness=3)
#點擊目標的位置
draw_img = cv2.circle(draw_img, click_tar_loc, 3, color=(0, 0, 255), thickness=3)
#模擬點擊的位置
draw_img = cv2.circle(draw_img,click_loc , 6, color=(255, 255, 0), thickness=3)
draw_img = cv2.circle(draw_img, click_loc, 12, color=(255, 0, 0), thickness=3)
cv2.imwrite(filename=self.tempPath,img=draw_img)
- image_analysis.py
import cv2
class IMG_Aanlysis:
def __init__(self):
#加載小人模板
self.template = cv2.imread('.\\template\\peo_template.jpg')
#加載中心白點模板
self.center_white_template=cv2.imread('.\\template\\white_center_template.png')
#加載中心白點模板2
self.center_white_template2=cv2.imread('.\\template\\white_center_template1.png')
#二值化模板
self.template_gray = cv2.cvtColor(self.template, cv2.COLOR_BGR2GRAY)
self.template_center_gray = cv2.cvtColor(self.center_white_template, cv2.COLOR_BGR2GRAY)
#獲取模板的寬高
self.w,self. h = self.template_gray.shape[::-1]
self.w2, self.h2 = self.template_center_gray.shape[::-1]
print('小人模板寬高:'+str(self.w)+':'+str(self.h))
print('中心點模板寬高:' + str(self.w2) + ':' + str(self.h2))
def get_people_loc(self,img):
'''
獲取小人的位置範圍
:param img:
:return:返回小人範圍圖和小人座標
'''
#定義小人位置
peo_loc=[0,0]
#與模板進行匹配識別 獲取識別結果矩形範圍
min_val, max_val, min_loc, max_loc = self.matchTemplate(img,self.template)
#繪製識別結果
draw_peo_result = cv2.rectangle(img, max_loc, (max_loc[0] + self.w, max_loc[1] + self.h), color=(255,0,0), lineType=8,
thickness=3)
#計算小人底部座標
peo_bottom_x=int(max_loc[0]+(self.w)/2)
peo_bottom_y=int((max_loc[1]+self.h-19))
peo_loc=(peo_bottom_x,peo_bottom_y)
draw_peo_result= cv2.circle(draw_peo_result,(peo_bottom_x,peo_bottom_y),2,color=(0,255,0),thickness=3)
return (draw_peo_result,peo_loc)
def get_target_loc(self,cv_img):
'''
獲取目標位置座標
:param img:
:return:
'''
cv_img_gray=cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
min_val2, max_val2, min_loc2, max_loc2 =self.matchTemplate(cv_img_gray,self.template_center_gray)
print('模板一匹配中心點 匹配度:%d%% 設定匹配閾值:80%%' % (max_val2 * 100), end=' ')
# 計算中心座標
cen_x = int(max_loc2[0] + (self.w2 / 2))
cen_y = int(max_loc2[1] + self.h2 / 2)
draw_cen_result = cv2.circle(cv_img, (cen_x, cen_y), 2, color=(255, 0, 0), thickness=3)
#預防分數干擾 把分數部分設置爲全黑色
#draw_cen_result[0:500]=[0,0,0]
if(max_val2>0.8):
print('模板一匹配中心點成功')
return (draw_cen_result,(cen_x,cen_y))
else:
cv_img_gray= cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
center_white_template_gray= cv2.cvtColor(self.center_white_template2, cv2.COLOR_BGR2GRAY)
min_val3, max_val3, min_loc3, max_loc3=self.matchTemplate(cv_img_gray,center_white_template_gray)
# 計算中心座標
cen_x3 = int(max_loc3[0] + (self.w2 / 2))
cen_y3 = int(max_loc3[1] + self.h2 / 2)
#print('cen_x3:%d cen_y3:%d' % (cen_x3, cen_y3), end=' ')
draw_cen_result = cv2.circle(cv_img, (cen_x3, cen_y3), 2, color=(255, 0, 0), thickness=3)
print('模板一匹配中心點失敗\n模板二匹配中心點 匹配度:%d%% 設定匹配閾值:80%%' % (max_val3 * 100), end=' ')
if (max_val3 > 0.8):
print('模板二匹配中心點成功')
return (draw_cen_result, (cen_x3, cen_y3))
else:
print('模板二匹配中心點失敗')
return (draw_cen_result,(0,0))
def matchTemplate(self,img,template):
res = cv2.matchTemplate(img, template,
cv2.TM_CCOEFF_NORMED)
return cv2.minMaxLoc(res)
def show_result(self,img):
cv2.namedWindow('AnaResult', cv2.WINDOW_GUI_EXPANDED)
cv2.imshow('AnaResult', img)
cv2.waitKey(0)
- adbOperate.py
import os
#定義截圖名稱
img_name='j.png'
img_out_path='./op_screencap/'
def pull_screenshot(self):
'''
截屏並存放到電腦中
:return:
'''
self.count= self.count+1
self.jump_count=self.jump_count+1
if(self.count>30):
self.count=1
print('將覆蓋舊圖')
os.system('adb shell screencap -p /sdcard/'+img_name)
os.system('adb pull /sdcard/j.png '+img_out_path+str(self.count)+'_'+img_name)
self.temp_path=img_out_path+str(self.count)+'_'+img_name
return self.temp_path
def jump(distance,click_loc):#由於測試結果發現每次跳躍 係數都會在一定範圍波動 因此這裏分段設置跳躍係數
cons=1.475
if(distance<=168):
cons=1.57
elif(distance>168 and distance<=175):
cons=1.5
elif(distance>175 and distance<=313):
cons=1.62
elif(distance>313 and distance<400):
cons=1.52
elif(distance>=400 and distance<511):
cons=1.50
elif(distance>=511 and distance<540):
cons = 1.49
elif(distance>=540 and distance<659):
cons=1.45
elif(distance>=659 and distance<670):
cons=1.47
elif(distance>=670 and distance<692):
cons=1.45
elif(distance>=692 and distance<700):
cons=1.38
elif(distance>=698):
cons=1.40
press_time = distance * cons
press_time = str(int(press_time))
x1=str(click_loc[0])
y1=str(click_loc[1])
#cmd = 'adb shell input swipe 540 1514 540 1514 ' + str(press_time)
cmd = 'adb shell input swipe {0} {1} {2} {3} {4}'
cmd= cmd.format(click_loc[0],click_loc[1],click_loc[1],click_loc[0],press_time)
#print('[distance:'+str(distance)+'][press_time:'+str(press_time)+']ms [cmd:'+cmd+']')
print('跳躍函數:\n-----使用跳躍係數:%1.3f\n-----跳躍距離:%d\n-----按壓時間:%s ms' %(cons,int(distance),press_time))
os.system(cmd)
main.py
import adbOperate
import ImgHelper
class Main:
def __init__(self):
print('主程序初始化...')
imHelper = ImgHelper.ImageHelprt()
imHelper.tempPath= adbOperate.pull_screenshot(imHelper)
imHelper.show_img()
if __name__ == '__main__':
Main()