Python-Pytorch框架-实现AI自动瞄准(下)

01

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

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

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

line

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,win32con
import numpy as np
import time

class My_pywin32(object):
    def __init__(self,win_name,win_more):
        self.win_name = win_name
        self.win_more = win_more

    def 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获取mfcDC
        saveDC = mfcDC.CreateCompatibleDC()
        # mfcDC创建可兼容的DC
        saveBitMap = 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_more
        Screen_cx = Screen_w // 2  # 游戏界面中心x
        Screen_cy = Screen_h // 2  # 游戏界面中心y
        Screen_c = [Screen_cx, Screen_cy]  # 游戏界面中心坐标
        x0 = Screen_cx - Screen_cx // 2    # 游戏界面检测框左上角x0
        y0 = Screen_cy - Screen_cy // 2    # 游戏界面检测框左上角y0
        x1 = Screen_cx + Screen_cx // 2    # 游戏界面检测框右下角x1
        y1 = 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 = 24
    my_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 cv2
import torch
import torchvision
import numpy as np
import 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 cv2
import time,pyautogui
from tkinter import *
from 游戏检测区域操作 import My_pywin32
from 深度学习推理  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"):
            break
        if my_result == {}:
            continue

        #####################
        # 计算瞄准点与目标点之间的移动距离
        # 鼠标使用相对移动
        # 1.56为自测参数
        #####################
        x= int(my_result["1"][0])
        y = int(my_result["1"][0])
        currentMouseX = x / 1.56
        currentMouseY = y / 1.56
        pyautogui.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框架自带模型搭建,可以识别人,但不能分辨敌人与队友。如果需要分辨敌人与队友,则需要自己采集游戏内的图像进行数据标注与模型训练,不断优化模型精确度,这不算太难,但工程比较耗时间所以就不在这里进行演示了,需要的可以自行去摸索探究。

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

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

line
end

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

dianzan

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年4月5日
下一篇 2023年4月5日

相关推荐

此站出售,如需请站内私信或者邮箱!