opencv 模板匹配带角度_OpenCV应用——魔灵召唤辅助实现(二)

d7e8c70495d376d0662f94314bd866f0.png

往期文章:OpenCV应用——魔灵召唤辅助实现(一)

在了解了怎么用电脑控制手机之后,我们来学习下怎么用OpenCV来识别图像

一、模板匹配

模板匹配是在一幅图像中寻找一个特定目标的方法之一,这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否“相似”,当相似度足够高时,就认为找到了我们的目标。

简单的同时也说明了这种方法受到的限制很大,那么模板匹配这种方法是用于什么场景?首先 源图像 尺度不能变、角度不能旋转,其次源图像每次受外界影响因素必须要小,比如光源等等。

但是考虑到 魔灵召唤 这是一款游戏,场景固定、不受外界因素影响,这种方法就很适用啦!

1 原理

5c24f11cdafa06aa4fe0ad0d876c6c45.png

如上图

用T表示模板图像,I表示待匹配图像,切模板图像的宽为w高为h,用R表示匹配结果,依据模板匹配算法的不同R有不同的公式:

(1) 平方差匹配 method=CV_TM_SQDIFF

9393e8813dc87c5bb98a7e2c7512e4c9.png

当模板图像在源图像上不断移动直到找到目标位置时,平方差应该近似为零,即R值越小代表越准确。

(2) 标准平方差匹配 method=CV_TM_SQDIFF_NORMED

a2e1e8256ac1af65b67e82ce2438ad74.png

同理,也是R值越小越准确。

(3) 相关匹配法 method=CV_TM_CCORR

3167b8dd60426c9e6033b566a0ba5c9f.png

由于是乘积形式,R值越大越准确。

(4) 归一化相关匹配法 method=CV_TM_CCORR_NORMED

e5f528fd07c24f8f8b43ba408283c88c.png

同理,R值越大越准确。

(5) 相关系数匹配法 method=CV_TM_CCOEFF

562ff9298bdfdc3676aac65203eee44a.png

这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性(随机序列)

(6) 归一化相关系数匹配法 method=CV_TM_CCOEFF_NORMED

a182c83eb01bfb03071de44f061ebc71.png

2 OpenCV中函数

模板匹配函数

matchTemplate(image, templ, method)

image : 输入图像

templ : 模板图像

method : 上文提及的六种方法之一,注意要用eval函数包装下,如

eval('cv2.TM_SQDIFF_NORMED')

关于输出图像result:文档中直译为比较后的矩阵结果,单通道32位浮点型,如果image的尺寸是 W*H ,templ的尺寸是 w*h ,则result的尺寸是 (W-w)*(H-h) 。

查找最值函数

minMaxLoc(src)

返回最小值、最大值、最小值坐标、最大值坐标

根据匹配方法的不同确定是要最大值还是最小值,得到的坐标就是找到的图像位置的左上角坐标啦。

3 代码

def match_Template(path, template0, threshold): ''' 模板匹配,找到关键点 :param path: 图片文件路径 :param template0: 要遍历的模板 :param threshold: 阈值,TM_SQDIFF_NORMED(归一化平方差匹配)方法,越小越准确 :return: template元组下标,模板中心点坐标 ''' False_count = 0 img = cv2.imread(path,0) # img2 = img.copy() for t in template0: # 模板图像长宽 h, w = t.shape[0:2] # 模板匹配函数 res = cv2.matchTemplate(img, t, eval('cv2.TM_SQDIFF_NORMED')) # 找出最大最小值 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 大于阈值认定为没匹配到,注意TM_SQDIFF_NORMED方法越小越匹配 if min_val > threshold: False_count = False_count + 1 pointAxis = (0, 0) else: # 小于阈值认定为匹配到 print(min_val) bottom_right = (min_loc[0] + w, min_loc[1] + h) pointAxis = (min_loc[0] + w / 2, min_loc[1] + h / 2) # 用长方形框出来 cv2.rectangle(img, min_loc, bottom_right, (0, 0, 255), 2) cv2.imshow('TM_SQDIFF_NORMED', img) cv2.imshow('template%s'%False_count,t) print(False_count) cv2.waitKey(0) return False_count, pointAxis

二、使用它写一下龙十脚本

通常,随着从简单的测量(平方差)到更复杂的测量(相关系数),我们可获得越来越准确的匹配(同时也意味着越来越大的计算代价),实际使用起来这六种方法差别不大,为了方便阈值判断,我使用的是标准平方差匹配方法

CV_TM_SQDIFF_NORMED。

龙十脚本思路:

1. 主动作判断

依据以下模板图像判断主动作,每次获取手机截图后遍历模板列表,设定一个阈值(如0.05),返回匹配到的图像和其中心坐标,使用adb指令模拟手机屏幕点击:

adb shell input tap x y

6cc46fd9f8b7b2c2babbbc749a6e7fea.png

2. 符文判断

是不是符文 ->是,接着判断 ->不是 ->返回True,获得

是不是六星 ->是,接着判断 ->不是 ->返回Fasle, 卖掉

判断颜色 ->紫橙,接着判断 ->蓝色 ->返回Fasle, 卖掉

是不是246号位 ->是,接着判断 ->不是 ->返回True,获得

是不是百分比 ->是,True ->不是 ->返回Fasle, 卖掉

dbbe76e1fd45c27b635f7cc1528d233d.png

是不是看起来很简单呢,我们只需要反复调用函数matchTemplate进行判断,通过minMaxLoc获取坐标,通过adb指令模拟手机点击,就能完成这样的一个自动化流程,实乃养肝护肝之必备良品。

c6c21cb491b9857632c6645c2343dac0.png

最终效果

06dcfbd719b55519ce80b8377e81279f.png

结论

5e91ede20aca07027dac06103a4e2ada.gif

三、详细代码

main.py 用于龙十主动作

import osimport cv2import timefrom collections.abc import Iterableimport runes_detectimport randomdef phone_click(axis_to_tap): ''' 模拟点击手机屏幕 :param axis_to_tap: 模板匹配计算出的坐标 :return: ''' axis_x, axis_y = axis_to_tap axis_x= 2.34 * float(axis_x) + random.uniform(-1,1) # 加入随机的偏移量 axis_y= 2.34 * float(axis_y) + random.uniform(-1,1) os.system( 'adb shell input tap '+ str(axis_x) + ' '+ str(axis_y) )def getimg_toPC(imgname): ''' 直接获取手机截屏到电脑上 :param imgname: 截屏文件名,xxx.png :return: None ''' os.system('adb shell screencap -p > E:/PycharmProjects/android_summonerswar/pictures/'+imgname) with open('E:/PycharmProjects/android_summonerswar/pictures/'+imgname, 'br') as f: img_old = f.read() img_new = img_old.replace(b'', b'') with open('E:/PycharmProjects/android_summonerswar/pictures/'+imgname, 'bw') as f: f.write(img_new) img = cv2.imread('E:/PycharmProjects/android_summonerswar/pictures/'+imgname) img_new = cv2.resize(img, (1000, 461), cv2.INTER_AREA) cv2.imwrite('E:/PycharmProjects/android_summonerswar/pictures_resize/'+imgname, img_new)def getimg_sdcard(imgname): ''' 在手机上截屏并保存, 再pull到电脑上 :param imgname:截屏文件名,xxx.png :return:None ''' os.system('adb shell /system/bin/screencap -p /sdcard/'+imgname) os.system('adb pull /sdcard/'+imgname + ' E:/PycharmProjects/android_summonerswar/pictures/'+imgname)def match_Template(path, template0, threshold): ''' 模板匹配,找到关键点 :param path: 图片文件路径 :param template0: 要遍历的模板 :param threshold: 阈值,TM_SQDIFF_NORMED(归一化平方差匹配)方法,越小越准确 :return: template元组下标,模板中心点坐标 ''' False_count = 0 pointAxis = (0, 0) img = cv2.imread(path, 0) # img2 = img.copy() for t in template0: # 模板图像长宽 h, w = t.shape[0:2] res = cv2.matchTemplate(img, t, eval('cv2.TM_SQDIFF_NORMED')) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) if min_val > threshold: False_count = False_count + 1 else: print(min_val) bottom_right = (min_loc[0] + w, min_loc[1] + h) pointAxis = (min_loc[0] + w / 2, min_loc[1] + h / 2) break # cv2.rectangle(img, min_loc, bottom_right, (0, 0, 255), 2) # cv2.imshow('TM_SQDIFF_NORMED', img) # cv2.imshow('template%s'%False_count,t) # print(False_count) # cv2.waitKey(0) return False_count, pointAxisif __name__ == '__main__': img_name = 'screen.png' runesdetectEnabled = False saveCount = 0 saleCount = 0 runesCount0 = 0 runesCount1 = 0 runesCount2 = 0 runesCount3 = 0 runesCount4 = 0 runesCount5 = 0 # 龙十list list_dragon = [] list_dragonName = ('fight.jpg', 'begin.jpg', 'auto.jpg', 'complete.jpg', 'box.jpg', 'again.jpg', 'fail.jpg', 'fail2begin.jpg', 'shop.jpg', 'buyenergy.jpg', 'ready.jpg') for i in list_dragonName: template_dragon = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplateemplate_dragon_'+ i, 0) list_dragon.append(template_dragon) # 判断符文用 # 五六星 template_runes_star5 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_star5.jpg', 0) template_runes_star6 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_star6.jpg', 0) # 蓝色、橙色 template_runes_rare = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_rankRare.jpg', 0) template_runes_Legend = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_rankLegend.jpg',0) # 百分比 template_runes_246 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_246%.jpg', 0) # 246号位 # template_runes_No1 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No1.jpg',0) template_runes_No2 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No2.jpg', 0) # template_runes_No3 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No3.jpg',0) template_runes_No4 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No4.jpg', 0) # template_runes_No5 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No5.jpg',0) template_runes_No6 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No6.jpg', 0) # 5/6星符文判断 元组 areRunes = (template_runes_star5, template_runes_star6) # 蓝色、橙色符文判断 RunesRank = (template_runes_rare, template_runes_Legend) # 246号位符文 模板 元组 RunesNo = (template_runes_No2, template_runes_No4, template_runes_No6) # 246 号位是百分比的元组 Runes246 = (template_runes_246, ) while True: time.sleep(1) getimg_toPC(img_name) img_path = 'E:/PycharmProjects/android_summonerswar/pictures_resize/'+img_name num, Axis_xy = match_Template(img_path,list_dragon, 0.05) print(num) if num == 0: phone_click(Axis_xy) print('进入战斗') elif num == 1: phone_click(Axis_xy) print('点击开始') elif num == 2: phone_click(Axis_xy) print('点击自动') elif num == 3: phone_click(Axis_xy) print('点击完成') elif num == 4: phone_click(Axis_xy) print('打开箱子') runesdetectEnabled = True elif num == 5: phone_click(Axis_xy) print('再来一次') elif num == 6: print('失败') phone_click((634, 293)) time.sleep(0.5) phone_click((634, 293)) elif num == 7: print('失败重来') phone_click((366, 247)) elif num == 8: print('没体力喽') phone_click(Axis_xy) elif num == 9: print('买一管!') phone_click(Axis_xy) time.sleep(0.3) # 点 用30 水晶 购买 phone_click((410, 275)) time.sleep(0.3) # 点 确定 phone_click((471, 275)) time.sleep(0.3) # 退出购买界面 phone_click((840, 45)) elif num == 10: print('战斗准备') phone_click(Axis_xy) elif num == 11: if runesdetectEnabled is True: print('符文判断中') a, b, c = runes_detect.runes_dis(img_path, areRunes, RunesRank, RunesNo, Runes246) if a is True: saveCount = saveCount +1 print(b) phone_click((545,366)) phone_click((626,112)) else: phone_click((416, 372)) time.sleep(1) phone_click((445, 275)) saleCount = saleCount +1 if c == 0: runesCount0 += 1 elif c == 1: runesCount1 +=1 elif c == 2: runesCount2 +=1 elif c == 3: runesCount3 +=1 elif c == 4: runesCount4 +=1 elif c == 5: runesCount5 +=1 print('保留%d 卖出%d 总计%d'%(saveCount, saleCount, saveCount+saleCount) ) print('五星%d 杂碎%d 六星传说%d 六星紫色可留%d 六星紫色固定值%d 六星蓝色%d' %(runesCount0, runesCount1, runesCount2, runesCount3, runesCount4, runesCount5)) runesdetectEnabled = False else: pass

runes_detect.py 用于符文检测

import cv2import osfrom matplotlib import pyplot as pltdef runes_dis(path, Template_areRunes, Template_RunesRank, Template_RunesNo, Template_Runes246): ''' 辨别符文:是不是符文 ->是,接着判断 ->不是 ->返回True,获得 是不是六星 ->是,接着判断 ->不是 ->返回Fasle, 卖掉 判断颜色 ->紫橙,接着判断 ->蓝色 ->返回Fasle, 卖掉 是不是246号位 ->是,接着判断 ->不是 ->返回True,获得 是不是百分比 ->是,True ->不是 ->返回Fasle, 卖掉 :param path: 符文图片路径 :return: True or False, False count, 0:五星 1:杂碎 2:六星传说 3:六星紫色可留 4:六星紫色固定值 5:六星蓝色 ''' False_count0 = 0 False_count1 = 0 False_count2 = 0 False_count3 = 0 are246 = False img = cv2.imread(path,0) img_roi = img[110:404, 318:638] # 判断是不是符文 for a in Template_areRunes: res0 = cv2.matchTemplate(img_roi, a, eval('cv2.TM_SQDIFF_NORMED')) min_val0, _, _, _ = cv2.minMaxLoc(res0) if min_val0 > 0.1: False_count0 = False_count0 +1 else: break # 五星符文,卖掉 if False_count0 == 0: return False, (False_count0, False_count1, False_count2, False_count3), 0 # 不是符文,彩虹怪、碎片之类的,留着 elif False_count0 == 2: return True, (False_count0, False_count1, False_count2, False_count3), 1 # 六星符文 elif False_count0 == 1: # 判断颜色 for b in Template_RunesRank: res1 = cv2.matchTemplate(img, b, eval('cv2.TM_SQDIFF_NORMED')) min_val1, _, _, _ = cv2.minMaxLoc(res1) if min_val1 > 0.1: False_count1 = False_count1 + 1 else: break # 判断246 for c in Template_RunesNo: res2 = cv2.matchTemplate(img, c, eval('cv2.TM_SQDIFF_NORMED')) min_val2, _, _, _ = cv2.minMaxLoc(res2) if min_val2 > 0.1: False_count2 = False_count2 + 1 else: break # 判断是不是百分比 for d in Template_Runes246: res3 = cv2.matchTemplate(img, d, eval('cv2.TM_SQDIFF_NORMED')) min_val3, _, _, _ = cv2.minMaxLoc(res3) if min_val3 > 0.5: False_count3 = False_count3 + 1 else: break # 246,且不是百分比 if (False_count2 == 0 or False_count2 == 1 or False_count2 == 2) and False_count3==1: are246 = True else: are246 = False # 返回卖不卖,橙色留,紫色百分比留,蓝色卖,紫色固定值卖 if False_count1 == 1: return True, (False_count0, False_count1, False_count2, False_count3), 2 elif (False_count1 == 2 and are246 == False): return True, (False_count0, False_count1, False_count2, False_count3), 3 elif (False_count1 == 2 and are246 == True): return False, (False_count0, False_count1, False_count2, False_count3), 4 elif False_count1 == 0 : return False, (False_count0, False_count1, False_count2, False_count3), 5 else: print('error, cannot detect') else: print('error,cannot detect if a rune') print(False_count0, False_count1, False_count2, False_count3) # 判断是不是符文if __name__ == '__main__': # img_roi = img[110:404, 318:638] template_runes_star5 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_star5.jpg',0) template_runes_star6 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_star6.jpg',0) template_runes_rare = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_rankRare.jpg',0) template_runes_Legend = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_rankLegend.jpg',0) template_runes_246 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_246%.jpg',0) # template_runes_No1 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No1.jpg',0) template_runes_No2 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No2.jpg',0) # template_runes_No3 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No3.jpg',0) template_runes_No4 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No4.jpg',0) # template_runes_No5 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No5.jpg',0) template_runes_No6 = cv2.imread(r'E:PycharmProjectsandroid_summonerswaremplate_runesunes_No6.jpg',0) # 5/6星符文判断 元组 areRunes = (template_runes_star5, template_runes_star6) # 蓝色、橙色符文判断 RunesRank = (template_runes_rare, template_runes_Legend) # 246号位符文 模板 元组 RunesNo = (template_runes_No2, template_runes_No4, template_runes_No6) # 246 号位是百分比的元组 Runes246 = (template_runes_246, ) path = r'E:PycharmProjectsandroid_summonerswarpictures_resizedragon7new.png' a,b = runes_dis(path, areRunes, RunesRank, RunesNo, Runes246) print(a) print(b)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值