OpenCV与Ptorch框架搭建一个利用目标骨骼关键点检测实现AI自动瞄准的娱乐项目(该项目仅供学习OpenCV、Ptorch框架、游戏自动化等参考)。

该项目思路大致分为如下步骤:

  1. 利用Pywin32以及OpenCV获取游戏窗口图像
  2. 数据集获取(本文为17骨骼关键点)
  3. 搭建Ptorch训练框架
  4. 利用深度学习Ptorch框架训练识别模型
  5. 搭建模型推理预测框架
  6. 不断把图像送入模型进行目标检测
  7. 根据检测结果获取关键点位置
  8. 利用鼠标键盘自动化实现自瞄

01
屏幕截取图像

游戏检测区域操作.py

#!/usr/bin/env python3# -*- coding:utf-8 -*-# @Project : pytorch_射击游戏# @FileName:游戏检测区域操作.py# @Time:2022/5/18 22:56# @Author:和孔哥一起学# @Email :2338199895@qq.com# @CSDN and Public:和孔哥一起学import win32gui,win32api,win32ui,win32conimport numpy as npimport timeclass My_pywin32(object):def __init__(self,win_name,win_more):self.win_name = win_nameself.win_more = win_moredef get_hwnd(self):hwnd_title = {}def _get_all_hwnd(hwnd, mouse):if win32gui.IsWindow(hwnd) and win32gui.IsWindowEnabled(hwnd) and win32gui.IsWindowVisible(hwnd):hwnd_title.update({hwnd: win32gui.GetWindowText(hwnd)})win32gui.EnumWindows(_get_all_hwnd, 0)new_dict = {v: k for k, v in hwnd_title.items()}print(new_dict)# for wnd in hwnd_title.items():# print(wnd)return new_dict[self.win_name]def set_win(self,hwdn,win_size=[1280, 720],win_index=[0,0]):"""设置窗口大小尺寸并置顶"""win32gui.SetWindowPos(hwdn, win32con.HWND_TOPMOST, win_index[0], win_index[1], win_size[0], win_size[1], win32con.SWP_NOSIZE| win32con.SWP_SHOWWINDOW)def get_win_wh(self,hwnd):"""游戏窗口截图"""hwndDC = win32gui.GetWindowDC(hwnd)# 根据窗口句柄获取窗口的设备上下文DC(Divice Context)mfcDC = win32ui.CreateDCFromHandle(hwndDC)# 根据窗口的DC获取mfcDCsaveDC = mfcDC.CreateCompatibleDC()# mfcDC创建可兼容的DCsaveBitMap = win32ui.CreateBitmap()# 创建bigmap准备保存图片rctA = win32gui.GetWindowRect(hwnd)Screen_w = rctA[2] - rctA[0]# 游戏界面宽度Screen_h = rctA[3] - rctA[1]# 游戏界面高度# 获取图片大小# 截取从左上角(0,0)长宽为(w,h)的图片saveBitMap.CreateCompatibleBitmap(mfcDC, Screen_w, Screen_h)# 为bitmap开辟空间saveDC.SelectObject(saveBitMap)# 高度saveDC,将截图保存到saveBitmap中saveDC.BitBlt((0, 0), (Screen_w, Screen_h), mfcDC, (0, 0), win32con.SRCCOPY)signedIntsArray = saveBitMap.GetBitmapBits(True)img = np.frombuffer(signedIntsArray, dtype="uint8")img.shape = (Screen_h, Screen_w, 4)# bit图转mat图win32gui.DeleteObject(saveBitMap.GetHandle())mfcDC.DeleteDC()saveDC.DeleteDC()# 释放内存return img, (Screen_w,Screen_h)# 转为RGB图返回def get_roi(self,img,Screen):Screen_w,Screen_h = Screen[0],Screen[1]-self.win_moreScreen_cx = Screen_w // 2# 游戏界面中心xScreen_cy = Screen_h // 2# 游戏界面中心yScreen_c = [Screen_cx, Screen_cy]# 游戏界面中心坐标x0 = Screen_cx - Screen_cx // 2# 游戏界面检测框左上角x0y0 = Screen_cy - Screen_cy // 2# 游戏界面检测框左上角y0x1 = Screen_cx + Screen_cx // 2# 游戏界面检测框右下角x1y1 = Screen_cy + Screen_cy // 2# 游戏界面检测框右下角y1# 选取roi = img[y0,y1,x0,x1]窗口print("ROI区域大小为",[x0,y0,x1,y1])roi = img[int(y0):int(y1),int(x0):int(x1)]return roi,Screen_c,[x0,y0+self.win_more,x1,y1+self.win_more]if __name__ == '__main__':# 窗口标题win_name ="Counter-Strike: Global Offensive - Direct3D 9"print("游戏窗口标题为:",win_name)# 窗口多余部分,即窗口标题win_more = 24my_pywin32 = My_pywin32(win_name,win_more)# hwdn = my_pywin32.get_hwnd()# my_pywin32.set_win(hwdn)# img,Screen = my_pywin32.get_win_wh(hwdn)# img_roi,Screen_c = my_pywin32.get_roi(img,Screen)# print("窗口大小为:",Screen)# print("中心点为:",Screen_c)# import cv2# cv2.imshow("img",img_roi)# cv2.waitKey()# cv2.destroyAllWindows()

02
图像载入模型

深度学习推理.py

#!/usr/bin/env python3# -*- coding:utf-8 -*-# @Project : pytorch_射击游戏# @FileName:深度学习推理.py# @Time:2022/5/19 21:28# @Author:和孔哥一起学# @Email :2338199895@qq.com# @CSDN and Public:和孔哥一起学import cv2import torchimport torchvisionimport numpy as npimport torchvision.transforms as transforms# 定义使用COCO数据集对应的每类的名称"""fire hydrant 消防栓,stop sign 停车标志, parking meter 停车收费器, bench 长椅。zebra 斑马, giraffe 长颈鹿, handbag 手提包, suitcase 手提箱, frisbee (游戏用)飞盘(flying disc)。skis 滑雪板(ski的复数),snowboard 滑雪板(ski是单板滑雪,snowboarding 是双板滑雪。)kite 风筝, baseball bat 棒球棍, baseball glove 棒球手套, skateboard 滑板, surfboard 冲浪板, tennis racket 网球拍。broccoli 西蓝花,donut甜甜圈,炸面圈(doughnut,空心的油炸面包), cake 蛋糕、饼, couch 长沙发(靠chi)。potted plant 盆栽植物。 dining table 餐桌。 laptop 笔记本电脑,remote 遥控器(=remote control), cell phone 移动电话(=mobile phone)(cellular 细胞的、蜂窝状的), oven 烤炉、烤箱。 toaster 烤面包器(toast 烤面包片)sink 洗碗池, refrigerator 冰箱。(=fridge), scissor剪刀(see, zer), teddy bear 泰迪熊。 hair drier 吹风机。 toothbrush 牙刷。"""# COCO数据集对应的类别COCO_INSTANCE_CATEGORY_NAMES = ['__BACKGROUND__', 'person', 'bicycle', 'car', 'motorcycle','airplane', 'bus', 'train', 'trunk', 'boat', 'traffic light','fire hydrant', 'N/A', 'stop sign', 'parking meter', 'bench','bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant','bear', 'zebra', 'giraffe', 'N/A', 'backpack', 'umbrella', 'N/A','N/A', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard','sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard','surfboard', 'tennis racket', 'bottle', 'N/A', 'wine glass','cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple','sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza','donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'N/A','dining table', 'N/A', 'N/A', 'toilet', 'N/A', 'tv', 'laptop','mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven','toaster', 'toaster', 'sink', 'refrigerator', 'N/A', 'book', 'clock','vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush']# 定义能够检测出的关键点名称"""elbow 胳膊肘,wrist 手腕,hip 臀部"""COCO_PERSON_KEYPOINT_NAMES = ['nose', 'left_eye', 'right_eye', 'left_ear','right_ear', 'left_shoulder', 'right_shoulder', 'left_elbow','right_elbow', 'left_wrist', 'right_wrist', 'left_hip', 'right_hip','left_knee', 'right_knee', 'left_ankle', 'right_ankle']# 加载pytorch提供的keypointrcnn_resnet50_fpn()网络模型,可以对17个人体关键点进行检测。device = torch.device("cuda" if torch.cuda.is_available() else "cpu")model = torchvision.models.detection.keypointrcnn_resnet50_fpn(pretrained=True)model.to(device)model.eval()def My_Detect(image, threshold=0.9):image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)# 准备需要检测的图像transform_d = transforms.Compose([transforms.ToTensor()])image_t = transform_d(image)## 对图像进行变换# print(image_t.shape)pred = model([image_t.to(device)]) ## 将模型作用到图像上# 检测出目标的类别和得分pred_class = [COCO_INSTANCE_CATEGORY_NAMES[ii] for ii in list(pred[0]['labels'].cpu().numpy())]pred_score = list(pred[0]['scores'].detach().cpu().numpy())# print(pred_class,pred_score)# 检测出目标的边界框pred_boxes = [[ii[0], ii[1], ii[2], ii[3]] for ii in list(pred[0]['boxes'].detach().cpu().numpy())]## 只保留识别的概率大约 threshold 的结果。pred_index = [pred_score.index(x) for x in pred_score if x > threshold]for index in pred_index:box = pred_boxes[index]box = [int(i) for i in box]cv2.rectangle(image,(int(box[0]),int(box[1])),(int(box[2]),int(box[3])),(0,255,255))texts = pred_class[index] + ":" + str(np.round(pred_score[index], 2))cv2.putText(image, texts,(box[0], box[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 255), 2)pred_keypoint = pred[0]["keypoints"]# 检测到实例的关键点pred_keypoint = pred_keypoint[pred_index].detach().cpu().numpy()# 对实例数量索引my_result = {}for index in range(pred_keypoint.shape[0]):# 对每个实例的关键点索引keypoints = pred_keypoint[index]for ii in range(keypoints.shape[0]): ##ii为第几个坐标点x = int(keypoints[ii, 0]) #x坐标y = int(keypoints[ii, 1]) #y坐标visi = keypoints[ii, 2] #置信度if visi > 0.:cv2.circle(image, (int(x),int(y)), 1, (0,0,255),4)texts = str(ii+1)cv2.putText(image,texts, (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 255), 2)my_result[texts] = (int(x), int(y))return image,my_result

03
主程序步骤

游戏辅助主程序.py

#!/usr/bin/env python3# -*- coding:utf-8 -*-# @Project : pytorch_射击游戏# @FileName:游戏辅助主程序.py# @Time:2022/5/19 21:26# @Author:和孔哥一起学# @Email :2338199895@qq.com# @CSDN and Public:和孔哥一起学import cv2import time,pyautoguifrom tkinter import *from 游戏检测区域操作 import My_pywin32from 深度学习推理import My_Detect"""# moused_x,moused_y = win32api.GetCursorPos() #获取当前坐标# win32api.mouse_event(win32con.MOUSEEVENTF_MIDDLEDOWN,0,0,0,0) #鼠标中键点击# win32api.mouse_event(win32con.MOUSEEVENTF_MOVE | win32con.MOUSEEVENTF_ABSOLUTE, x, y, 0, 0)# time.sleep(0.01)# # win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0)# # time.sleep(0.01)# # win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)fps游戏里的鼠标移动带来的是角度变化,移动多少像素点与游戏中的角度是正比例关系,4:3的分辨率下视角为90度,鼠标到人像上需要移动多少距离还得计算一下,不能直接靠一个参数搞定2D坐标-3D坐标原理电脑的鼠标是在屏幕的2D坐标上运动的,而我们要获取的是3D世界中的一个三维坐标,在游戏引擎中的实现原理如下:先获取鼠标在屏幕上的2D坐标。结合摄像机平面计算出这个点在3D世界中的坐标。从这个3D坐标沿着摄像机的视角发射一条射线,让这个射线和3D世界中的对象发生碰撞。这样,如果发生了碰撞,我们就可以获取最先和射线碰撞的物体以及发生碰撞的点坐标。"""def cv_show(win_name,roi_image):cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)cv2.resizeWindow(win_name, 640, 320)cv2.imshow(win_name, roi_image)cv2.moveWindow(win_name, 1280, 0)cv2.getWindowImageRect(win_name)def gui_ui():myUI = Tk()# 界面开头myUI.title("学习测试项目")myUI.geometry("400x120")myUI.resizable(0, 0)Label(myUI, text='欢迎来到学习测试项目', font=('微软雅黑', 28), fg='#FF69B4', bg="#CAFF70").pack()Button(myUI, text="开始学习!", width=13, height=1, command=main, activeforeground="green", activebackground="black").pack()Button(myUI, text="结束学习!", width=13, height=1, command=myUI.destroy).pack()mainloop()# 界面结尾def main():# 窗口标题 ,记得改为自己的窗口名,不然无法找到窗口句柄!!!win_name = "Counter-Strike: Global Offensive - Direct3D 9"# win_name = "腾讯手游助手" #窗口标题win_size = [1280, 720]#窗口大小win_index = [0, 0]#窗口放置位置win_more = 24 #窗口多余部分,即窗口标题my_pywin32 = My_pywin32(win_name, win_more) #创建窗口处理对象hwdn = my_pywin32.get_hwnd()#获取窗口句柄my_pywin32.set_win(hwdn, win_size, win_index)#设置窗口参数MouseX, MouseY = pyautogui.position()while True: # 循环处理# for i in range(0,300):start_time = time.time()#开始时间img, Screen = my_pywin32.get_win_wh(hwdn) #获取窗口截图img_roi, Screen_c,[x0,y0,x1,y1] = my_pywin32.get_roi(img, Screen) #获取窗口截图ROI图片print("窗口大小为:", Screen)print("中心点为:", Screen_c)roi_image,my_result = My_Detect(img_roi, threshold=0.8) #ROI图像进行推理,返回图像与ROI图像中位置坐标print("图像推理结果为:", my_result)fps = int(1/(time.time() - start_time)) # 计算推理帧率print("FPS:{}".format(fps))cv_show(win_name, roi_image)# OpenCV窗口显示结果图像if cv2.waitKey(1) & 0xFF == ord("q"):breakif my_result == {}:continue###################### 计算瞄准点与目标点之间的移动距离# 鼠标使用相对移动# 1.56为自测参数#####################x= int(my_result["1"][0])y = int(my_result["1"][0])currentMouseX = x / 1.56currentMouseY = y / 1.56pyautogui.moveRel(int(currentMouseX+MouseX), int(currentMouseY+MouseY))pyautogui.click()time.sleep(0.2)pyautogui.click()time.sleep(0.2)pyautogui.click()time.sleep(0.2)cv2.destroyAllWindows()if __name__ == "__main__":# main()gui_ui()

04
总结

以上就是今天要讲的内容,本次主要分享了最近写的一个FPS游戏辅助瞄准,适用性很强呢,大多FPS游戏内都能玩耍,该程序采用了Pytorch框架自带模型搭建,可以识别人,但不能分辨敌人与队友。如果需要分辨敌人与队友,则需要自己采集游戏内的图像进行数据标注与模型训练,不断优化模型精确度,这不算太难,但工程比较耗时间所以就不在这里进行演示了,需要的可以自行去摸索探究。

其中可能还有些许问题,可以一起交流,请看到的大佬指正。

请注意必须使用管理员模式进行运行程序,否则操作在游戏内会被屏蔽,从而无法实现!!!!


点个关注不迷路
觉得孔哥写的对你有帮助?请分享给更多的人
欢迎一起学习!博客平台同步发布,请搜索——和孔哥一起学

版权声明:作者:和孔哥一起学本文版权归作者所有,欢迎转载,但未经作者同意必须在文章页面注明来源及原作者或原文链接,否则保留追究法律责任的权利。