上篇文章已经写了yolov5的基础用法,这篇文章主要是将我对yolov5模型的修改,用于实现对屏幕进行实时监测识别并将鼠标移动到人体指定位置的功能,改动的代码不是很多,我尽量说的详细一些。
大概思路就是在预测文件上,也就是detect.py这个文件中提供了一种实时监控屏幕并给予坐标框的功能,甚至给出了展示实时监控屏幕的画面功能,我们只需要实现它并在它的基础上做一些改动就可以了(下面每一段文字解释的都是文字下面的图片)
基础解析:已知的是yolov5模型官方提供了多种source来源,可以是文件,url,照片,视频,屏幕等格式,监控屏幕就要用到所给的屏幕参数,也就是下面横线中的screen
代码解析:screen 0 545 325 190 70 代表的就是以整块屏幕为基准,后面的坐标代表以当前屏幕中心为起始点画一个方块,然后实时监测这个方块里的画面,四个参数都代表不同的意义,前俩个参数(545,325)代表方块左上角的坐标,后面俩个坐标(190,70)代表这个方块以左上角坐标为起始延伸出的宽和高
公式计算:举个例子,比如你游戏中屏幕分辨率为1280*720,你想识别一个屏幕中心长190*宽70 的方块(也就是枪口的瞄准范围),已知的屏幕的左上角坐标为(0,0),屏幕中心点坐标为640*360,你只需要用下面图中公式即可:
左上角x坐标 =(屏幕长 - 方块长)/ 2
左上角y坐标 =(屏幕宽 - 方块宽)/ 2
拓展:下面就是加载屏幕识别的源码,可以看到0是默认全屏,将source进行split()分解为数组,数组中包含5个参数,依次赋值给 screen,left ,top ,width,height,还导入了mss库,mss.mss()可用于实时检测屏幕,大约每秒30帧左右是目前我知道的最快的了,将每秒30张图片送进yolo模型中循环检测
代码解析:将view-img这一行的store_true改为store_false,这样在默认传递view-img这个参数的时候就是true,开启mss的实时显示
代码解析:下面图片就是开启实时显示的代码,view_img为true(show_img是我自己加的不用管),执行下面代码,cv2.imshow() 实时显示检测之后带有红框的照片,im0就是yolo检测成功之后实时传递的图片
做完上面步骤就可以运行一下,看看是否能实时检测并显示屏幕中的内容
大概思路:因为识别之后yolo会返回四个参数,分别是红框的左上角和右下角坐标,我们就通过这个来确定红框的中心的坐标,再控制鼠标移动到这个坐标即可
所需准备:因为要在游戏中控制鼠标,所以这里需要准备一个罗技的驱动(其他的控制键鼠的库会被火线屏蔽),我会把文件发出来,在论坛中有大佬将罗技的驱动写成了一个dll文件可以在python中调用,虽然功能不多但是也够用了,需要特定的罗技驱动版本(需要的去文章末尾下载即可)。
驱动代码大概:有按压鼠标,松开鼠标,点击,移动鼠标这个四个功能
import ctypes
import os
import pynput
import winsound
import time
try:
# 获取当前绝对路径
root = os.path.abspath(os.path.dirname(__file__))
driver = ctypes.CDLL(f'{root}/logitech.driver.dll')
ok = driver.device_open() == 1 # 该驱动每个进程可打开一个实例
if not ok:
print('Error, GHUB or LGS driver not found')
except FileNotFoundError:
print(f'Error, DLL file not found')
class Logitech:
class mouse:
"""
code: 1:左键, 2:中键, 3:右键
"""
@staticmethod
def press(code):
if not ok:
return
driver.mouse_down(code)
@staticmethod
def release(code):
if not ok:
return
driver.mouse_up(code)
@staticmethod
def click(code):
if not ok:
return
driver.mouse_down(code)
driver.mouse_up(code)
@staticmethod
def scroll(a):
"""
a:没搞明白
"""
if not ok:
return
driver.scroll(a)
@staticmethod
def move(x, y):
"""
相对移动, 绝对移动需配合 pywin32 的 win32gui 中的 GetCursorPos 计算位置
pip install pywin32 -i https://pypi.tuna.tsinghua.edu.cn/simple
x: 水平移动的方向和距离, 正数向右, 负数向左
y: 垂直移动的方向和距离
"""
if not ok:
return
if x == 0 and y == 0:
return
driver.moveR(x, y, True)
下面利用上面的函数写控制鼠标移动并点击的功能
代码解析:其实就是利用构造函数传递过来的x轴以及y轴移动的距离来控制鼠标的移动,调用驱动的move(x,y)相对移动的方法,移动到指定位置之后控制鼠标点击进行自动开枪的效果,我还设置了一个时间轴,用于点射防止后坐力过大
class RunLogitechTwo:
def __init__(self,move_x_distance,move_y_distance,is_auto_fire,auto_fire_rate):
self.move_x_distance = move_x_distance
self.move_y_distance = move_y_distance
self.log_mouse = Logitech.mouse
self.is_auto_fire = is_auto_fire
self.auto_fire_rate = auto_fire_rate
def quick_move(self):
self.log_mouse.move(self.move_x_distance,self.move_y_distance-2)
# 判断是否自动开火
if self.is_auto_fire:
self.log_mouse.click(1)
time.sleep(self.auto_fire_rate)
代码解析(第一个for循环):主要代码一共有俩个for循环,第一个for循环就是循环dataset,dataset可以是图片,视频,这里当然就代表屏幕实时抓取的照片了,抓取每一张照片,model()建立模型并检测,non_max_suppression()进行最大值抑制,得到的pred(所有目标的坐标列表)放入第二个for循环中
代码解析(第二个for循环):首先找出屏幕的中心点,也就是鼠标的中心点,下面的for循环代表检测的每张照片里出现的所有红框也就是所有敌人的坐标,比如一张图中检测到10个敌人就代表循环10次,每次都给出其中一个框住敌人的坐标(xyxy),类别(cls),通过这些信息进行下面操作,图片代码都是yolo中本来就有的
下面是我在第二个for循环后面添加的代码,属于上面for循环的子代码
代码解析:第一步,判断瞄准的是头部还是身体,aim_position为0/1代表头或者身体,第二步,算出红框的中心坐标,也就是头/身体的中心位置,第三步,计算x轴和y轴需要移动的距离,也就是用红框中心坐标与屏幕中心坐标相减得到的差值,第三步,利用勾股定理求出需要移动的直线距离,第四步,利用擂台比较的方法得到一张图片中多个目标中直线距离当前鼠标最近的一个目标。
小tips:想得到最近的目标原因是,每次识别之后给出的目标顺序都是随机的,可能就会出现一种情况就是画面中有多名敌人之后,枪口会乱晃,每次取最近目标就会改善一些
# 找出离鼠标最近的头框/身体框
if c == aim_position:
# 通过左上角与右下角算中心坐标
center_pos_x = int(xyxy[0].item() + (xyxy[2].item() - xyxy[0].item()) // 2) # 中心点x坐标
center_pos_y = int(xyxy[1].item() + (xyxy[3].item() - xyxy[1].item()) // 2) # 中心点y坐标
# 计算需要移动的距离
need_move_x = center_pos_x - current_screen_x
need_move_y = center_pos_y - current_screen_y
# 用勾股定理求出离鼠标最近的距离
need_sum_dis = math.sqrt(need_move_x**2+need_move_y**2)
# 比较留下最短距离
if need_sum_dis < min_sum_dis:
min_move_x = need_move_x
min_move_y = need_move_y
else:
continue
下面就是在得到每张图片中距离鼠标最短距离的目标所需要移动的距离之后进行的操作,注意下面的代码是和第二个for循环代码平级的代码,也就意味着for循环结束之后的代码
代码解析:第一步,判断最小移动距离不为9999之后进行鼠标的移动操作(9999是我设置的阈值),第二步,如果需要移动距离为0,最后一步,连接驱动,调用2.4步所写的类,在构造函数中给出所需移动距离,并且选择性自动开火
小tips:中间我判断人物在左边或者右边会提前预判俩个像素的位置是因为,人物在移动中是非常快速的,游戏每秒60帧,而mss目前也就每秒抓30帧左右,所有在随着人物移动的过程中会有一点跟不上的感觉,我干脆就提前预判俩个像素效果会更好一些,对于移动幅度小的人物也不会有准度影响。
# 根据最短坐标进行跟随
if min_move_x != 9999:
# 如果需要移动距离为0,鼠标不动
if min_move_x == 0 and min_move_y == 0:
pass
# 判断人物是在左边还是右边,分别提前预判一个像素
elif min_move_x < current_screen_x:
# 左边
min_move_x = min_move_x - 2
elif min_move_x > current_screen_x:
# 右边
min_move_x = min_move_x + 2
# 连接驱动
run_logit = RunLogitechTwo(min_move_x,min_move_y,is_auto_fire,auto_fire_rate)
run_logit.quick_move()
.dll文件和罗技驱动我放下面了,如果用我这个.dll文件一定要用我发这个对应的罗技驱动,换其他版本.dll文件都不一定好使
链接:https://pan.baidu.com/s/1mGjGh5aJzt3FOGaromV5rw?pwd=6666
提取码:6666
教程第二部分到这就结束了,目前已经可以正常的使用了,后面我如果有时间会写如何将它集成为一个小型的app,更方便更改一些参数,比如瞄准范围,自动开枪速率,是否开启实时检测,切换瞄准头部还是身体等,如果有看不懂的或者要整个项目的也可以邮箱联系我:[email protected]
番外,展示一下集成的app: