原神位置检测【OpenCV 特征匹配】

OpenCV版本是3.4.2
Python版本是3.6
pyautogui版本是0.9.51
显示器为1920x1080
(其他像素当然能用,需要自己改)

效果

本项目实现了原神游戏中实时位置检测,将会在控制台返回人物所在的位置的(x,y)坐标,对应大地图上的像素点坐标。


run.gif

思路

1.使用OpenCV里面的Flann图片特征匹配,实现用右上角的小地图去匹配保存在本地的大地图

截图这个小区域

本地保存的大地图

注:地图截屏于原神wiki里的地图,链接
https://wiki.biligame.com/ys/

2.大地图匹非常耗时[将近5秒],而且吃CPU,所以大地图匹配一次后下次只匹配当前位置的大地图的一个小区域,可以看到第一次很慢,之后就快的多了


传送后地图大改变,只能重新全图匹配

2.使用一个小地图模板来实现检测当前是否是在游戏中,避免没有检测到地图也硬要去匹配地图,造成计算资源浪费


避免切换画面都会全图匹配

改进

1.可以不用截屏,obs采集屏幕,启用虚拟摄像头,opencv捕捉这个虚拟摄像头,可以有很好的防检测能力,就是会有不小的延迟
2.截屏也可以全图截屏,然后模板匹配后进行剪裁再规定此后的截屏大小,其实也不错,实现起来不费劲,但就是懒啊.....

用处?

这个只是检测,是第一步,获得位置后就能做导航,安卓端或者web端,想想一下在手机上可以跟百度地图一样显示当前位置,显示资源位置,规划导航路线,自己录制锄地路线,还是很有意思的,也许有空会做?(lazy)

Github

有点事情,等下上传

网盘链接

https://darksuger.lanzous.com/i3omghbfhlg

代码贴出来

import pyautogui
import os
import sys
import numpy as np
import cv2 as cv
import time

#OpenCV版本是3.4.2
#Python版本是3.6
#pyautogui版本是0.9.51


#设置项:截图的长宽
grapW=330
grapH=250

refreshSP=1

mapPath=os.path.join(sys.path[0],'map.png')
maskPath=os.path.join(sys.path[0],'mask.png')

mapCache=cv.imread(mapPath,0) #读取地图
mask=cv.imread(maskPath,0)#读取识别模板

theGlob=200 #地图剪裁半径
theX=theGlob 
theY=theGlob

largeMapping=True #大地图匹配位,表示是否进行全图匹配
ready=False #主页匹配位,表示当前截取到的画面是否包含地图

while True:
    map=mapCache.copy()
    img = pyautogui.screenshot(region=[0,0,grapW,grapH]) # 截图的x y w h
    img = cv.cvtColor(np.asarray(img),cv.COLOR_RGB2GRAY) # 图片灰度化
    #这个是用来测试的,测试屏幕截图的效果,能出现完整的居中的小地图即可
    #没有双屏可以先截图游戏再用截图测试
    #test(img) 

    try:
        if ready:
            if largeMapping==False:
                #将大地图裁剪后识别,增加匹配速度,减少cpu压力
                map=map[theY-theGlob:theY+theGlob,theX-theGlob:theX+theGlob]
                retX,retY=mapper(map,img)
                if retX!=-1:
                    #区域地图匹配成功
                    theX+=(retX-theGlob)
                    theY+=(retY-theGlob)
                    print((theX,theY))
                    time.sleep(refreshSP)
                else:
                    #区域地图匹配失败
                    print('none')
                    retX,retY=mapper(img,mask)#模板匹配,判断是否截图的是小地图
                    if retX!=-1:
                        largeMapping=True#模板匹配成功,判断已经进行了传送,转大地图匹配
                    else:
                        print('check ready')
                        ready=False
                        time.sleep(1)                     
            else:
                #全图匹配
                retX,retY=mapper(map,img)
                if retX!=-1:
                    theX=retX
                    theY=retY
                    print((theX,theY))
                    largeMapping=False
                else:
                    print('mapper error!')#全图匹配失败?
                    time.sleep(4)
        else:
            #模板匹配,判断是否截图到的图像是小地图
            retX,retY=mapper(img,mask)
            if retX!=-1:
                ready=True
                print('is ready')
            else:
                print('no ready!')
                time.sleep(1)
    except:
        print('???')#匹配算法出错,纯色截图下会出错,捕捉一下不处理。
        time.sleep(1)

def mapper(map,mapping):
    MIN_MATCH_COUNT=10 #设置最低匹配数量为10

    sift=cv.xfeatures2d.SIFT_create() #创建sift检测器
    kp1,des1=sift.detectAndCompute(mapping,None) 
    kp2,des2=sift.detectAndCompute(map,None)
    #创建设置FLAAN匹配
    FLANN_INDEX_KDTREE=0
    index_params=dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params=dict(checks=50)
    flann=cv.FlannBasedMatcher(index_params,search_params)
    mathces=flann.knnMatch(des1,des2,k=2)
    good=[]
    #过滤不合格的匹配结果,大于0.7的都舍弃
    for m,n in mathces:
        if m.distance<0.7*n.distance:
            good.append(m)
        #如果匹配结果大于10,则获取关键点的坐标,用于计算变换矩阵
    if len(good)>MIN_MATCH_COUNT:
        src_pts=np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
        dst_pts =np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

        #计算变换矩阵和掩膜
        M,mask=cv.findHomography(src_pts,dst_pts,cv.RANSAC,10.0)
        matchesMask=mask.ravel().tolist()
        #根据变换矩阵进行计算,找到小图像在大图像中的位置
        h,w=img.shape
        pts=np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)

        dst=cv.perspectiveTransform(pts,M)#从左上逆时针表示的矩阵顶点

        theX=int((dst[0][0][0]+dst[2][0][0])/2)
        theY=int((dst[0][0][1]+dst[2][0][1])/2)#计算坐标点
        
        return theX,theY     
    else:
        return -1,-1
def test(img):
    cv.imshow(img)
    cv.waitKey(0)

转载注明原帖,禁止商用

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

推荐阅读更多精彩内容