首页 > Python资料 博客日记

python 自动化学习(三) 句柄获取、模拟按键、opencv安装

2024-09-22 16:00:06Python资料围观6

本篇文章分享python 自动化学习(三) 句柄获取、模拟按键、opencv安装,对你有帮助的话记得收藏一下,看Python资料网收获更多编程知识

一、什么是句柄

     句柄是在操作系统中的一种标识符,相当于我们每个人的身份证一样,句柄在电脑中也是有唯一性的,我们启动的每一个程序都有自己的句柄号,表示自己的身份

    为什么要说句柄,我们如果想做自动化操作时,肯定也不想程序占用了我们整个电脑,稍微操作一下程序步骤就乱掉了,更加希望自动化程序在运行的时候能够只针对某个窗口或者某个程序进行操作,即使我们把自动化的程序放入都后台时也不影响两边的操作,这里就需要用到句柄了

所需的包

#配置清华镜像源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip config set global.trusted-host pypi.tuna.tsinghua.edu.cn
 
#安装依赖库
pip install pywin32

基本使用

#部分参考文档
https://huaweicloud.csdn.net/63803058dacf622b8df86819.html?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromBaidu~activity-1-122498299-blog-111083068.pc_relevant_vip_default&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromBaidu~activity-1-122498299-blog-111083068.pc_relevant_vip_default&utm_relevant_index=1

1、获取鼠标所在位置程序的句柄

vi main.py

import time
import win32api
import win32gui


time.sleep(2)

point = win32api.GetCursorPos()  #win32api.GetCursorPos 获取鼠标当前的坐标(x,y)

hwnd = win32gui.WindowFromPoint(point)  #查看坐标位置窗口的句柄

print(hwnd)  #输出句柄

如下图,我执行了3遍分别在执行后将鼠标放在文本、桌面、idea上面,返回了句柄ID

 

2、通过句柄获取类名

 我们每次关闭重新打开一个程序会发现句柄值变了,每次都从头找句柄就太麻烦了

每一个程序在开发之初就存在着一个叫"类名"的概念,类名和句柄每次变更不同,它在定义后几乎是不会发生变化的,所以我们最好是先找到一个程序的类名,后续直接通过类名找到句柄,然后在通过句柄进行真正所需要的操作

 vi main.py

import time
import win32api
import win32gui

# 通过句柄获取窗口类名
def get_clasname(hwnd):
    clasname = win32gui.GetClassName(hwnd)
    print('窗口类名:%s' % (clasname))
    return clasname



time.sleep(2)
point = win32api.GetCursorPos()
hwnd = win32gui.WindowFromPoint(point)

#查看窗口类名
get_clasname(hwnd)

 可以看到上面我们获取到了文档窗口的类名,现在开始我们直接通过类名去获取句柄

3、通过类名获取句柄

没有找到特定的方法,我们下面大概的思路就是先把主机上所有的句柄id都拿到,通过循环把所有句柄id的类名拿出来然后做对比,对的上的id都留在列表中,所以说如果开启了多个相同程序的窗口,我们也会获取到多个句柄

import time
import win32api
import win32gui

#获取当前主机上的所有句柄id
def get_all_windows():
    all_window_handles = []

    # 枚举所有窗口句柄,添加到列表中
    def enum_windows_proc(hwnd, param):
        param.append(hwnd)
        return True

    # 调用枚举窗口API
    win32gui.EnumWindows(enum_windows_proc, all_window_handles)

    return all_window_handles  #返回的是一个句柄id的列表


#查询传入的句柄id、类名
def get_title(window_handle, class_name):
    #查询句柄的类名
    window_class = win32gui.GetClassName(window_handle)

    #判断窗口类名是否和指定的类名相同,如果相同则返回该窗口句柄,否则返回空值
    if window_class == class_name:
        return window_handle

#遍历窗口句柄的所有子窗口
def get_child_windows(parent_window_handle):
    child_window_handles = []
    def enum_windows_proc(hwnd, param):
        param.append(hwnd)
        return True
    #win32gui.EnumChildWindows    遍历窗口句柄的所有子窗口
    win32gui.EnumChildWindows(parent_window_handle, enum_windows_proc, child_window_handles)
    return child_window_handles


# 根据标题查找窗口句柄
def find_hwnd_by_title(title):
    all_windows = get_all_windows()  #查询所有句柄
    matched_windows = []      #存放所有匹配类名的句柄id

    # 在所有窗口中查找标题匹配的窗口句柄
    for window_handle in all_windows:
        #get_title方法  检查传入句柄对应的类名和我们实际的类名是否对应
        window_title = get_title(window_handle, title)
        if window_title:
            matched_windows.append(window_title) #如果对应就写入列表

    # 如果没有匹配到,则在所有子窗口中查找标题匹配的窗口句柄
    if matched_windows:
        return matched_windows
    else:
        child_window_handles = []
        for parent_window_handle in all_windows:
            #不论子窗口是否有数据都追加到列表
            child_window_handles.extend(get_child_windows(parent_window_handle))
        for child_window_handle in child_window_handles:
            if get_title(child_window_handle, title):
                matched_windows.append(get_title(child_window_handle, title))
    return matched_windows

if __name__ == '__main__':
    hwnd = find_hwnd_by_title("Edit")
    print(hwnd)

 可以看到我们能够直接取到相同类名下所有已经打开的窗口句柄,这样我们甚至可以做个循环加多线程,来实现一个窗口并发的效果

二、模拟鼠标

上面我们已经拿到了文本文档的一个句柄信息,通过句柄我们可以做很多事情,最常见的就是模拟鼠标和键盘的按键操作,每个操作可能都较为细小琐碎,我们定义一个class类来存放

1、常见消息类型和标识

#官方参考
https://learn.microsoft.com/zh-cn/windows/win32/inputdev/wm-lbuttondown
消息类型作用消息标识作用
WM_MOUSEMOVE鼠标 移动移动通用左键右键标识
WM_RBUTTONDOWN鼠标 右键按下MK_RBUTTON左键按下
WM_RBUTTONUP鼠标 右键释放None释放时无需标识
WM_LBUTTONDOWN鼠标 左键按下MK_LBUTTON右键按下
WM_LBUTTONUP鼠标 左键释放None释放时无需标识

 当按键需要被按下时,需要先声明消息类型,然后标明按键状态
  如果鼠标按键需要被释放时,可以直接通过释放按钮来释放
  如果指定消息类型是移动时,可以当作已经声明了消息类型,可以直接使用按键标识

2、信号发送方法

1、PostMessage 异步非阻塞 
      #适用于后台程序 在当前线程上排队
      #等其他任务完成后才会进入消息循环去处理。

2、SendMessage 同步执行  
      #适用于前台程序 虽然SendMessage也能发往非活动窗口
      #但它的同步特性意味着它会等待窗口处理完消息才能继续,这可能导致应用程序冻结
      #而PostMessage则允许窗口在空闲时间处理,不影响主线程

我们只要是为了对后台程序进行操作,所以都使用第一种方法

3、语法格式

win32api.PostMessage(句柄id, 消息类型, 消息标识, 具体的坐标(x,y))

4、鼠标左键点击案例

1、获取目标坐标 (全屏位置坐标)

vi zuobiao.py

#获取坐标
import time

import win32api

time.sleep(3)
print(win32api.GetCursorPos())

返回

(421, 268)

 

2、异步发送鼠标消息

shubiao.py

import win32api
import win32con
import time
import win32api
import win32gui

from main import find_hwnd_by_title


def click_point(hwnd, x, y):
    #左键按住
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, 0, ((y) << 16 | (x)));

    #等待
    time.sleep(3)

    #左键松手
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, ((y) << 16 | (x)));


#根据我们平台的类名获取句柄id
time.sleep(2) #延m迟2秒把
hwnd = 460592


#传入句柄,点击的坐标
click_point(hwnd,276,13)
3、基于窗口的坐标获取 (和直接拿页面坐标不同,类似绑定)

我们方法2执行时,发现坐标有时候不太对,我们直接拿全局的坐标,在实际调的时候会发现偏移很多,下面我们先去计算窗口的位置,然后获取的坐标是基于窗口的

import win32api

import time

import win32gui


def get_window_position(hwnd):
    #获取窗口坐标
    left, top, right, bottom = win32gui.GetWindowRect(hwnd)
    return (left, top)  #返回左边和上面

hwnd_id = 460592  # 你已经有的窗口句柄ID
time.sleep(3)

#获取鼠标坐标
mouse_pos = win32api.GetCursorPos()  # 获取鼠标当前位置

#将鼠标坐标(全屏坐标) 减去窗口坐标
added = tuple(x - y for x, y in zip(mouse_pos, get_window_position(hwnd_id)))
print(added)

5、鼠标左键双击案例

import win32api
import win32con
import time

from main import find_hwnd_by_title


def double_click(hwnd, x, y):

    # 使用 MAKELONG 函数将 x 和 y 坐标组合成一个长整型值
    point = win32api.MAKELONG(x, y)

    #左键按住
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, 0, point)
    time.sleep(0.01)
    #左键松手
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, point)



    # 发送 WM_LBUTTONDBLCLK 消息以模拟双击操作
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, point)

    # 发送 WM_LBUTTONUP 消息以确保鼠标左键被释放
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, point)

# 示例:获取桌面窗口句柄并进行双击操作
hwnd = find_hwnd_by_title("SysListView32")
print(hwnd)
double_click(hwnd[0], 344, 121)  # 在坐标 (100, 100) 处双击

这里取了个巧,我发现单独用双击的坐标很奇怪,总是找不对,我尝试使用win32api.PostMessage(hwnd, win32con.WM_MOUSEMOVE, None, point) 去移动坐标,但没什么效果我这边就先用左键固定位置然后实现的双击

有小伙伴说双击模拟双击操作
win32api.PostMessage(hwnd, win32con.WM_LBUTTONDBLCLK, 0, point) 中间改成0就可以了,还没试验稍后看看

5、鼠标按键案例(上面这块更新的,写到这想不起来之前是啥情况,先保留一下)

      下面定义了一个类,先去接受我们上面获取到的句柄id,在使用鼠标按键的时候调用win32api.PostMessage函数 去发送给句柄所在的窗口按键信息

   在左右键按下的时候才需要定义标识,比如模拟左键时会使用WM_LBUTTONDOWN和MK_LBUTTON  ,而松开时使用WM_LBUTTONUP和None

   变量pos 是只鼠标按键的坐标,需要通过win32api.MAKELONG 转换数据类型后才能调用


#声明鼠标操作的类
class WinMouse(object):

    #初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int):
        self.handle = handle_num  

    #鼠标左键按下
    def left_button_down(self, pos):
            win32api.PostMessage(self.handle, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, pos)

    #鼠标左键释放
    def left_button_up(self, pos):
        win32api.PostMessage(self.handle, win32con.WM_LBUTTONUP, None, pos)



if __name__ == '__main__':
    hwnd = find_hwnd_by_title("Edit")   #通过类名获取句柄
    bd = WinMouse(hwnd[0])              #实例化WinMouse 类,传入句柄值

    pos = win32api.MAKELONG(328, 250)   #将正常的x,y坐标值转换为特定的数据结构,
                                       #给win32api.PostMessage调用

    #按下、等待1s、松开
    bd.left_button_down(pos)  
    time.sleep(1)             
    bd.left_button_up(pos)    

可以看到在下图中,我们运行程序后,不论文本文档是否在前台还是后台,哪怕被遮挡住后也会照常进行鼠标点击(数字太多看不清,大致就是我把鼠标放到末尾,程序在我上面取坐标的地方点一下左键)

6、模拟键盘输入文本 (文本可长按)

import win32api
import win32con
import time


from main import find_hwnd_by_title

# 模拟一次按键的输入,间隔值默认0.1S
def key_press(hwnd, key: str, interval=0.001):
    key = ord(key.upper())
    win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, key, 0)
    time.sleep(interval)  # 减少按键持续时间
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, key, 0)

# 模拟一个按键的按下
def key_down(hwnd, key: str):
    key = ord(key.upper())
    win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, key, 0)

# 模拟一个按键的弹起
def key_up(hwnd, key: str):
    key = ord(key.upper())
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, key, 0)


# 示例:获取桌面窗口句柄并进行按键操作
hwnd = find_hwnd_by_title("Edit")
if hwnd:
    print(hwnd)
    hwnd = hwnd[0]


   #短按输入
    key_up(hwnd,"A")
    key_up(hwnd,"B")
    key_up(hwnd,"C")


    #长按输入 (文本会输入两个d,第一个是按下,第二个是松手)
    key_press(hwnd,"D",interval=10)
else:
    print("未找到窗口")

整理

上面写的比较零散,我们将上面写的都放到类中调用

vi tools.py


import win32con
import time
import win32api



class Shubiao(object):

    #初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int, num_of_steps=80):
        self.hwnd = handle_num
        self.num_of_steps = num_of_steps

    #鼠标左键单击 按下信号
    def left_button_down(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONDOWN, 0, pos)

    #鼠标左键单击 松开信号
    def left_button_up(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONUP, 0, pos)

    #鼠标左键双击信号
    def left_double_button_down(self,pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, pos)

    def left_double_button_up(self,pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, pos)

    #鼠标左键点击一次
    def click_point(self, x, y,wait=0.2):
        #计算坐标
        point = win32api.MAKELONG(x, y)
        #点击
        self.left_button_down(point)
        #等待
        time.sleep(wait)
        #松开
        self.left_button_up(point)

    #鼠标左键双击一次
    def double_click(self, x, y,wait=0.2):

        #获取坐标
        point = win32api.MAKELONG(x, y)

        #先实现单击定位
        self.click_point(x,y)

        #完成双击
        self.left_double_button_down(point)
        time.sleep(wait)
        self.left_double_button_up(point)




class Jianpan(object):

    #初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int, num_of_steps=80):
        self.hwnd = handle_num
        self.num_of_steps = num_of_steps

    # 长按按键,比如游戏中的移动
    def key_press(self, key: str, interval=0.001):
        key = ord(key.upper())
        win32api.PostMessage(self.hwnd, win32con.WM_KEYDOWN, key, 0)
        time.sleep(interval)  # 减少按键持续时间
        win32api.PostMessage(self.hwnd, win32con.WM_KEYUP, key, 0)

    # 点按按键
    def key_down(hwnd, key: str):
        key = ord(key.upper())
        win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, key, 0)

    # 松开点按按键
    def key_up(hwnd, key: str):
        key = ord(key.upper())
        win32api.PostMessage(hwnd, win32con.WM_KEYUP, key, 0)



#根据我们平台的类名获取句柄id
hwnd = 65796

#类赋值
bd = Shubiao(hwnd)

#调用左键点击一次指定坐标
#bd.click_point(495, 191)
bd.double_click(495, 191)

7、鼠标滑动拖拽

 vi tools.py


import win32con
import time
import win32api



class Shubiao(object):

    #初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int, num_of_steps=80):
        self.hwnd = handle_num
        self.num_of_steps = num_of_steps

    #鼠标左键单击 按下信号
    def left_button_down(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONDOWN, 0, pos)

    #鼠标左键单击 松开信号
    def left_button_up(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONUP, 0, pos)

    #鼠标左键按下,如果有坐标会进行移动
    def mouse_move(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, pos)

    #鼠标右键按下,如果有坐标会进行移动
    def right_button_move(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_RBUTTON, pos)


    #鼠标左键双击信号
    def left_double_button_down(self,pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, pos)

    def left_double_button_up(self,pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, pos)




    #鼠标左键点击一次
    def click_point(self, x, y,wait=0.2):
        #计算坐标
        point = win32api.MAKELONG(x, y)
        #点击
        self.left_button_down(point)
        #等待
        time.sleep(wait)
        #松开
        self.left_button_up(point)

    #鼠标左键双击一次
    def double_click(self, x, y,wait=0.2):

        #获取坐标
        point = win32api.MAKELONG(x, y)

        #先实现单击定位
        self.click_point(x,y)

        #完成双击
        self.left_double_button_down(point)
        time.sleep(wait)
        self.left_double_button_up(point)

    #计算鼠标从起始点到目标点的偏移过程
    def getPointOnLine(self,start_x, start_y, end_x, end_y, ratio):
        x = ((end_x - start_x) * ratio) + start_x
        y = ((end_y - start_y) * ratio) + start_y
        return int(round(x)), int(round(y))


    #模拟点击并拖拽目标,接受两对坐标值
    #两对左边,起点和重点
    def left_click_move(self, x1:int, y1:int, x2:int, y2:int, wait=2):
        point1 = win32api.MAKELONG(x1, y1)

        #拖动的时候先按下鼠标左键不松
        self.left_button_down(point1)

        #获取我们在init初始化时定义的偏移值
        steps = self.num_of_steps


        #可以理解为从左上角到右下角的点
        points = [self.getPointOnLine(x1, y1, x2, y2,  i / steps) for i in range(steps)]
        points.append((x2, y2))
        wait_time = wait / steps
        unique_points = list(set(points))
        unique_points.sort(key=points.index)
        for point in unique_points:
            x, y = point
            point = win32api.MAKELONG(x, y)

            win32api.PostMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point)
            time.sleep(wait_time)
        self.left_button_up(point)





class Jianpan(object):

    #初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int, num_of_steps=80):
        self.hwnd = handle_num
        self.num_of_steps = num_of_steps

    # 长按按键,比如游戏中的移动
    def key_press(self, key: str, interval=0.001):
        key = ord(key.upper())
        win32api.PostMessage(self.hwnd, win32con.WM_KEYDOWN, key, 0)
        time.sleep(interval)  # 减少按键持续时间
        win32api.PostMessage(self.hwnd, win32con.WM_KEYUP, key, 0)

    # 点按按键
    def key_down(hwnd, key: str):
        key = ord(key.upper())
        win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, key, 0)

    # 松开点按按键
    def key_up(hwnd, key: str):
        key = ord(key.upper())
        win32api.PostMessage(hwnd, win32con.WM_KEYUP, key, 0)







#根据我们平台的类名获取句柄id
hwnd = 65796

#类赋值
bd = Shubiao(hwnd)

#调用左键点击一次指定坐标
#bd.click_point(495, 191)
#bd.double_click(495, 191)

bd.left_click_move(495,191,495,591)

三、准备opencv环境

我们上面简单的实现了鼠标后台模拟操作,但是存在一个问题,坐标都是我们固定好的,而在实际使用中,我们的要点击的坐标一定是多变的,我们更希望当我们要做自动化操作的时候,给他一个图片,然后就能拿到对应点击位置的一个坐标

安装模块

pip install opencv-python
pip install pillow
pip install opencv-contrib-python
pip install numpy
pip install PIL
pip install matplotlib

我这边在安装上opencv-python 后调用cv2下任意方法都提示报黄,没有代码提示,下面列出解决方法

(venv) PS C:\Users\Administrator\IdeaProjects\test> pip install opencv-python
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: opencv-python in c:\users\administrator\ideaprojects\test\venv\lib\site-packages (4.7.0.72)
Requirement already satisfied: numpy>=1.21.2 in c:\users\administrator\ideaprojects\test\venv\lib\site-packages (from opencv-python) (1.24.3)

我们在安装成功opencv-python模块后会返回一个安装路径,登录这个路径,进入cv2的目录下,将cv2.pyd 文件放到下面的路径下,重启编辑器即可

c:\users\administrator\ideaprojects\test\venv\lib\site-packages

 

 pip install opencv-contrib-python

后来发现 单独装这个,不装opencv也可以用,还剩的复制文件,先看看,没问题就改

测试语句

import cv2
# 加载一张图片
img = cv2.imread('11.png', 1)   #脚本文件旁边自行准备一个图片

# 显示图片
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

1、添加截图函数

首先,我们想要获取想要获取小图所在的坐标,要先拿到大图的信息,这里我们以桌面为案例,通过获取桌面的句柄信息,然后进行截图

vi jietu.py

import cv2
import numpy as np
import win32con
import win32gui
import win32ui


def capture_window(hwnd,img):
    # 获取句柄窗口位置
    left, top, right, bottom = win32gui.GetWindowRect(hwnd)
    width = right - left
    height = bottom - top

    # 获取窗口DC
    hwndDC = win32gui.GetWindowDC(hwnd)
    mfcDC = win32ui.CreateDCFromHandle(hwndDC)
    saveDC = mfcDC.CreateCompatibleDC()

    # 创建位图对象
    saveBitMap = win32ui.CreateBitmap()
    saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)

    saveDC.SelectObject(saveBitMap)

    # 截取窗口内容
    saveDC.BitBlt((0, 0), (width, height), mfcDC, (0, 0), win32con.SRCCOPY)

    # 保存位图到内存中
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)

    # 将位图数据转换为OpenCV格式
    image = np.frombuffer(bmpstr, dtype='uint8')
    image.shape = (height, width, 4)

    # 释放资源
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(hwnd, hwndDC)


    #文件保存
    # 转换为BGR格式
    bgr_image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)

    # 保存图像到文件
    cv2.imwrite(img, bgr_image)

    return cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)

vi main.py

if __name__ == '__main__':
    hwnd = find_hwnd_by_title("SysListView32")   


    #调用截取特定句柄窗口截图
    capture_window(hwnd[0])

不知道是不是我服务器配置太低了,图片生成查不到20s左右,不过没关系,这里出图只是看一眼,后面不用

2、对比小图片在句柄窗口坐标

这里我们截一张句柄窗口内的截图,范围越小,误差越小  这里命名为111.png文件,那个大图文件不用管他

def EtachImage(mix_img, large_image):
    # 读取小图
    img = cv2.imread(mix_img)   #小图
    img2 = img.copy()
    template = cv2.imread(large_image) #大图


    # 选择匹配方法,opencv有6种匹配方法
    methods = ['cv2.TM_CCOEFF_NORMED','cv2.TM_SQDIFF','cv2.TM_SQDIFF_NORMED','cv2.TM_CCORR','cv2.TM_CCOEFF','cv2.TM_CCOEFF_NORMED']



    #将多种不同模板匹配的左上角坐标整个做个统计
    #将最频繁出现的结果作为返回值
    coordinate_list = []


    for meth in methods:
        img = img2.copy()  #拷贝小图一份
        method = eval(meth)

        # 使用模板匹配 (小图、大图、匹配规则)
        res = cv2.matchTemplate(img, template, method)

        # 提取最佳匹配的坐标
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        #print("矩形框左上角坐标:({})".format(max_loc))

        #将每种匹配方法结果的矩阵左上角坐标收集一下
        coordinate_list.append(max_loc)



    # 创建一个空字典,用于存储元组及其出现次数
    tuple_counts = {}
    # 遍历数据列表
    for tuple_item in coordinate_list:
        # 如果元组已经在字典中,增加其计数;否则,添加新键并将计数设为1
        if tuple_item in tuple_counts:
            tuple_counts[tuple_item] += 1
        else:
            tuple_counts[tuple_item] = 1

    # 返回出现次数最多的坐标
    return max(tuple_counts, key=tuple_counts.get)

vi main.py

if __name__ == '__main__':
    hwnd = find_hwnd_by_title("SysListView32")


    # 获取大图截图
    large_image = capture_window(hwnd[0],"asdf.png")

    # 匹配小图所处的大图坐标
    x, y = EtachImage('111.png',large_image)
    print(x, y)

3、验证获取的坐标是否准确

这里为了方便查看,将显示图片的那个注释掉了

def EtachImage(mix_img,large_image):
    。。。


    # 显示带有中心点的大图
    # cv2.imshow('output', large_image)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()

    # 返回中心点坐标
    return center_x, center_y

vi test.py

import cv2
import pyautogui

# 将鼠标移动到矩形框中心点坐标处
pyautogui.moveTo(425, 161, duration=1)

# 输出提示信息
print("鼠标已移动到矩形框中心点坐标处")

# 等待一段时间
cv2.waitKey(3000)  # 暂停3秒,方便观察

ok 坐标能拿到了,剩下的就靠发挥想象力喽

 (我碰到的一个bug,我是win server主机,如果把图标放到最下面哪一行,就会出现无法识别了,看截图也是截不到的区域,感觉是分辨率问题,不知道pc端有没有相似问题,注意一下)

后续

后来发现,获取的坐标pyauto确实能用,但是pywin32 去操作就存在很大的偏移,如果用pyauto就违背了我们后台运行的初衷,这里留个坑,后面看看有没有好的解决方法

后续的后续,找到方法了

(全量代码如下,上面修改过好多次,可能有一些零散的程序顺序不对,参考最下面这个整理后的)

vi main.py

import time
import win32api
import win32process


########################################查看主机句柄###################################################################
#获取当前主机上的所有句柄id
def get_all_windows():
    all_window_handles = []

    # 枚举所有窗口句柄,添加到列表中
    def enum_windows_proc(hwnd, param):
        param.append(hwnd)
        return True

    # 调用枚举窗口API
    win32gui.EnumWindows(enum_windows_proc, all_window_handles)

    return all_window_handles  #返回的是一个句柄id的列表


#查询传入的句柄id、类名
def get_title(window_handle, class_name):
    #查询句柄的类名
    window_class = win32gui.GetClassName(window_handle)

    #判断窗口类名是否和指定的类名相同,如果相同则返回该窗口句柄,否则返回空值
    if window_class == class_name:
        return window_handle

#遍历窗口句柄的所有子窗口
def get_child_windows(parent_window_handle):
    child_window_handles = []
    def enum_windows_proc(hwnd, param):
        param.append(hwnd)
        return True
    #win32gui.EnumChildWindows    遍历窗口句柄的所有子窗口
    win32gui.EnumChildWindows(parent_window_handle, enum_windows_proc, child_window_handles)
    return child_window_handles


# 根据标题查找窗口句柄
def find_hwnd_by_title(title):
    all_windows = get_all_windows()  #查询所有句柄
    matched_windows = []      #存放所有匹配类名的句柄id

    # 在所有窗口中查找标题匹配的窗口句柄
    for window_handle in all_windows:
        #get_title方法  检查传入句柄对应的类名和我们实际的类名是否对应
        window_title = get_title(window_handle, title)
        if window_title:
            matched_windows.append(window_title) #如果对应就写入列表

    # 如果没有匹配到,则在所有子窗口中查找标题匹配的窗口句柄
    if matched_windows:
        return matched_windows
    else:
        child_window_handles = []
        for parent_window_handle in all_windows:
            #不论子窗口是否有数据都追加到列表
            child_window_handles.extend(get_child_windows(parent_window_handle))
        for child_window_handle in child_window_handles:
            if get_title(child_window_handle, title):
                matched_windows.append(get_title(child_window_handle, title))
    return matched_windows




#######################################################键盘和鼠标操作###############################################

#鼠标操作
class Shubiao(object):

    # 初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int, num_of_steps=80):
        self.hwnd = handle_num
        self.num_of_steps = num_of_steps

    # 鼠标左键单击 按下信号
    def left_button_down(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONDOWN, 0, pos)

    # 鼠标左键单击 松开信号
    def left_button_up(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONUP, 0, pos)

    # 鼠标左键按下,如果有坐标会进行移动
    def mouse_move(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, pos)

    # 鼠标右键按下,如果有坐标会进行移动
    def right_button_move(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_RBUTTON, pos)

    # 鼠标左键双击信号
    def left_double_button_down(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, pos)

    def left_double_button_up(self, pos):
        win32api.PostMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, pos)

    # 鼠标左键点击一次
    def click_point(self, x, y, wait=0.2):
        # 计算坐标
        point = win32api.MAKELONG(x, y)
        # 点击
        self.left_button_down(point)
        # 等待
        time.sleep(wait)
        # 松开
        self.left_button_up(point)

    # 鼠标左键双击一次
    def double_click(self, x, y, wait=0.2):
        # 获取坐标
        point = win32api.MAKELONG(x, y)

        # 先实现单击定位
        self.click_point(x, y)

        # 完成双击
        self.left_double_button_down(point)
        time.sleep(wait)
        self.left_double_button_up(point)

    # 计算鼠标从起始点到目标点的偏移过程
    def getPointOnLine(self, start_x, start_y, end_x, end_y, ratio):
        x = ((end_x - start_x) * ratio) + start_x
        y = ((end_y - start_y) * ratio) + start_y
        return int(round(x)), int(round(y))

    # 模拟点击并拖拽目标,接受两对坐标值
    # 两对左边,起点和重点
    def left_click_move(self, x1: int, y1: int, x2: int, y2: int, wait=2):
        point1 = win32api.MAKELONG(x1, y1)

        # 拖动的时候先按下鼠标左键不松
        self.left_button_down(point1)

        # 获取我们在init初始化时定义的偏移值
        steps = self.num_of_steps

        # 可以理解为从左上角到右下角的点
        points = [self.getPointOnLine(x1, y1, x2, y2, i / steps) for i in range(steps)]
        points.append((x2, y2))
        wait_time = wait / steps
        unique_points = list(set(points))
        unique_points.sort(key=points.index)
        for point in unique_points:
            x, y = point
            point = win32api.MAKELONG(x, y)

            win32api.PostMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point)
            time.sleep(wait_time)
        self.left_button_up(point)

#键盘操作
class Jianpan(object):

    # 初始化函数,接受传入的句柄id
    def __init__(self, handle_num: int, num_of_steps=80):
        self.hwnd = handle_num
        self.num_of_steps = num_of_steps

    # 长按按键,比如游戏中的移动
    def key_press(self, key: str, interval=2): #默认长按2s

        self.key_down(key)
        time.sleep(interval)
        self.key_up(key)

    # 单个字符  点按按键
    #bd.key_down("a")
    def key_down(self, key: str):
        key = ord(key.upper())
        win32api.PostMessage(self.hwnd, win32con.WM_KEYDOWN, key, 0)

    # 单个字符  松开按键
    def key_up(self, key: str):
        key = ord(key.upper())
        win32api.PostMessage(self.hwnd, win32con.WM_KEYUP, key, 0)


    #多个字符 顺序写入文本
    #bd.key_input_winmsg("hello")
    def key_input_winmsg(self, str='', down=True):
        VK_CODE = {
            'backspace':0x08,
            'tab':0x09,
            'clear':0x0C,
            'enter':0x0D,
            'shift':0x10,
            'ctrl':0x11,
            'alt':0x12,
            'pause':0x13,
            'caps_lock':0x14,
            'esc':0x1B,
            'spacebar':0x20,
            'page_up':0x21,
            'page_down':0x22,
            'end':0x23,
            'home':0x24,
            'left_arrow':0x25,
            'up_arrow':0x26,
            'right_arrow':0x27,
            'down_arrow':0x28,
            'select':0x29,
            'print':0x2A,
            'execute':0x2B,
            'print_screen':0x2C,
            'ins':0x2D,
            'del':0x2E,
            'help':0x2F,
            '0':0x30,
            '1':0x31,
            '2':0x32,
            '3':0x33,
            '4':0x34,
            '5':0x35,
            '6':0x36,
            '7':0x37,
            '8':0x38,
            '9':0x39,
            'a':0x41,
            'b':0x42,
            'c':0x43,
            'd':0x44,
            'e':0x45,
            'f':0x46,
            'g':0x47,
            'h':0x48,
            'i':0x49,
            'j':0x4A,
            'k':0x4B,
            'l':0x4C,
            'm':0x4D,
            'n':0x4E,
            'o':0x4F,
            'p':0x50,
            'q':0x51,
            'r':0x52,
            's':0x53,
            't':0x54,
            'u':0x55,
            'v':0x56,
            'w':0x57,
            'x':0x58,
            'y':0x59,
            'z':0x5A,
            'numpad_0':0x60,
            'numpad_1':0x61,
            'numpad_2':0x62,
            'numpad_3':0x63,
            'numpad_4':0x64,
            'numpad_5':0x65,
            'numpad_6':0x66,
            'numpad_7':0x67,
            'numpad_8':0x68,
            'numpad_9':0x69,
            'multiply_key':0x6A,
            'add_key':0x6B,
            'separator_key':0x6C,
            'subtract_key':0x6D,
            'decimal_key':0x6E,
            'divide_key':0x6F,
            'F1':0x70,
            'F2':0x71,
            'F3':0x72,
            'F4':0x73,
            'F5':0x74,
            'F6':0x75,
            'F7':0x76,
            'F8':0x77,
            'F9':0x78,
            'F10':0x79,
            'F11':0x7A,
            'F12':0x7B,
            'F13':0x7C,
            'F14':0x7D,
            'F15':0x7E,
            'F16':0x7F,
            'F17':0x80,
            'F18':0x81,
            'F19':0x82,
            'F20':0x83,
            'F21':0x84,
            'F22':0x85,
            'F23':0x86,
            'F24':0x87,
            'num_lock':0x90,
            'scroll_lock':0x91,
            'left_shift':0xA0,
            'right_shift ':0xA1,
            'left_control':0xA2,
            'right_control':0xA3,
            'left_menu':0xA4,
            'right_menu':0xA5,
            'browser_back':0xA6,
            'browser_forward':0xA7,
            'browser_refresh':0xA8,
            'browser_stop':0xA9,
            'browser_search':0xAA,
            'browser_favorites':0xAB,
            'browser_start_and_home':0xAC,
            'volume_mute':0xAD,
            'volume_Down':0xAE,
            'volume_up':0xAF,
            'next_track':0xB0,
            'previous_track':0xB1,
            'stop_media':0xB2,
            'play/pause_media':0xB3,
            'start_mail':0xB4,
            'select_media':0xB5,
            'start_application_1':0xB6,
            'start_application_2':0xB7,
            'attn_key':0xF6,
            'crsel_key':0xF7,
            'exsel_key':0xF8,
            'play_key':0xFA,
            'zoom_key':0xFB,
            'clear_key':0xFE,
            '+':0xBB,
            ',':0xBC,
            '-':0xBD,
            '.':0xBE,
            '/':0xBF,
            '`':0xC0,
            ';':0xBA,
            '[':0xDB,
            '\\':0xDC,
            ']':0xDD,
            "'":0xDE,
            '`':0xC0}

        # 对于每个字符
        for c in str:
            # 按下键
            win32api.PostMessage(self.hwnd, win32con.WM_KEYDOWN, VK_CODE[c], 0)



############################################句柄截图函数############################################################
import cv2
import numpy as np
import win32con
import win32gui
import win32ui


def capture_window(hwnd,img):
    # 获取句柄窗口位置
    left, top, right, bottom = win32gui.GetWindowRect(hwnd)
    width = right - left
    height = bottom - top

    # 获取窗口DC
    hwndDC = win32gui.GetWindowDC(hwnd)
    mfcDC = win32ui.CreateDCFromHandle(hwndDC)
    saveDC = mfcDC.CreateCompatibleDC()

    # 创建位图对象
    saveBitMap = win32ui.CreateBitmap()
    saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)

    saveDC.SelectObject(saveBitMap)

    # 截取窗口内容
    saveDC.BitBlt((0, 0), (width, height), mfcDC, (0, 0), win32con.SRCCOPY)

    # 保存位图到内存中
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)

    # 将位图数据转换为OpenCV格式
    image = np.frombuffer(bmpstr, dtype='uint8')
    image.shape = (height, width, 4)

    # 释放资源
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(hwnd, hwndDC)


    #文件保存
    # 转换为BGR格式
    bgr_image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)

    # 保存图像到文件
    #cv2.imwrite(img, bgr_image)

    return cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)




############################################基于图片,匹配句柄窗口大图坐标##########################################################################
import cv2
import matplotlib.pyplot as plt
import numpy as np


def EtachImage(mix_img, large_image):
    # 读取小图
    img = cv2.imread(mix_img)   #小图
    img2 = img.copy()
    #template = cv2.imread(large_image) #大图
    template =  large_image


    # 选择匹配方法,opencv有6种匹配方法
    methods = ['cv2.TM_CCOEFF_NORMED','cv2.TM_SQDIFF','cv2.TM_SQDIFF_NORMED','cv2.TM_CCORR','cv2.TM_CCOEFF','cv2.TM_CCOEFF_NORMED']



    #将多种不同模板匹配的左上角坐标整个做个统计
    #将最频繁出现的结果作为返回值
    coordinate_list = []


    for meth in methods:
        img = img2.copy()  #拷贝小图一份
        method = eval(meth)

        # 使用模板匹配 (小图、大图、匹配规则)
        res = cv2.matchTemplate(img, template, method)

        # 提取最佳匹配的坐标
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        #print("矩形框左上角坐标:({})".format(max_loc))

        #将每种匹配方法结果的矩阵左上角坐标收集一下
        coordinate_list.append(max_loc)



    # 创建一个空字典,用于存储元组及其出现次数
    tuple_counts = {}
    # 遍历数据列表
    for tuple_item in coordinate_list:
        # 如果元组已经在字典中,增加其计数;否则,添加新键并将计数设为1
        if tuple_item in tuple_counts:
            tuple_counts[tuple_item] += 1
        else:
            tuple_counts[tuple_item] = 1

    # 返回出现次数最多的坐标
    return max(tuple_counts, key=tuple_counts.get)





if __name__ == '__main__':
    #基于雷电模拟器的类名获取句柄
    hwnd = find_hwnd_by_title("RenderWindow")

    #截图雷电模拟器
    large_image = capture_window(hwnd[0],"图片路径,可不写")

    #匹配小图所处的大图坐标
    pos = EtachImage("22.png",large_image)
    print(pos)


    #根据按键函数点击鼠标左键
    bd = Shubiao(hwnd[0])
    #左键点击
    bd.click_point(pos[0],pos[1])



    #下面这个单独找个文本试验,图里忘写了
    #键盘写入
    bd = Jianpan(hwnd[0])

    #字符串写入
    bd.key_input_winmsg("abcd")

    #单个字符按键
    bd.key_down("a")


    #单个字符串长按
    bd.key_press("b",interval=5)

这里的22.png是提前截取窗口的小图,然后做匹配的,详情看上面代码

开爽吧~ 

 吾王镇楼

四、文字识别

#参考文档
https://blog.csdn.net/qq_41567921/article/details/134813496?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172352971116800213072408%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172352971116800213072408&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-134813496-null-null.142^v100^pc_search_result_base1&utm_term=python%20PaddleOCR&spm=1018.2226.3001.4187
import numpy
from pyautogui import *
from PIL import Image
import numpy as np
from paddleocr import PaddleOCR, draw_ocr
 
def get_curtime(time_format="%Y-%m-%d %H:%M:%S"):
    curTime = time.localtime()
    curTime = time.strftime(time_format, curTime)
    return curTime
 
def ocr_get_txt_pos(path="", text=""):
    '''
    获取文字与位置对应map
    :param path:图片路径,图片路径为空则默认获取当前屏幕截图
    :param text: 筛选需要查找的内容,匹配所有位置
    :return:list
    '''
 
    result, img_path = ocr_img_text(path, saveimg=True)
 
    print("图片识别结果保存:", img_path)
 
    poslist = [detection[0][0] for line in result for detection in line]
    txtlist = [detection[1][0] for line in result for detection in line]
 
    # 用list存文字与位置信息
    find_txt_pos = []
 
    items = 0
 
    if text == "":
        find_txt_pos = result
    else:
        for i in range(len(poslist)):
            if txtlist[i] == text:
                find_txt_pos.append(poslist[i])
                items += 1
 
    print(find_txt_pos)
    return find_txt_pos
 
def ocr_img_text(path="", saveimg=False, printResult=False):
    '''
    图像文字识别
    :param path:图片路径
    :param saveimg:是否把结果保存成图片
    :param printResult:是否打印出识别结果
    :return:result,img_name
    '''
    image = path
 
    # 图片路径为空就默认获取屏幕截图
    if image == "":
        image = screenshot()
        image = np.array(image)
    else:
        # 不为空就打开
        image = Image.open(image).convert('RGB')
 
    ocr = PaddleOCR(use_angle_cls=True, lang="ch")  # need to run only once to download and load model into memory
 
    result = ocr.ocr(image, cls=True)
    if printResult is True:
        for line in result:
            for word in line:
                print(word)
 
    # 识别出来的文字保存为图片
    img_name = "ImgTextOCR-img-" + get_curtime("%Y%m%d%H%M%S") + ".jpg"
    if saveimg is True:
        boxes = [detection[0] for line in result for detection in line]  # Nested loop added
        txts = [detection[1][0] for line in result for detection in line]  # Nested loop added
        scores = [detection[1][1] for line in result for detection in line]  # Nested loop added
        im_show = draw_ocr(image, boxes, txts, scores)
        im_show = Image.fromarray(im_show)
        im_show.save(img_name)
 
    return result, img_name
 
if __name__ == '__main__':
    # test-1
    ocr_img_text(saveimg=True, printResult=True)
 
    #test-2
    pos_list = ocr_get_txt_pos(text="二、使用步骤")
 
    #test-3
    pos_list = ocr_get_txt_pos(text="总结")
 
    # 取一个点进行点击操作
    pos_x, pos_y = pos_list[0]
    moveTo(pos_x + 5, pos_y + 5)
    click()

五、yolov安装

yolo5的一些文档(可忽略)

#cpu安装及训练文档

https://blog.csdn.net/2401_84080967/article/details/137349483?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172100430216800184131430%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172100430216800184131430&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-137349483-null-null.142^v100^control&utm_term=yolov5%E5%AE%89%E8%A3%85&spm=1018.2226.3001.4187




#gpu安装及训练文档

https://blog.csdn.net/AperOdry/article/details/139025562?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172120117916800186564811%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172120117916800186564811&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-4-139025562-null-null.142^v100^pc_search_result_base4&utm_term=YOLOv5%20GPU&spm=1018.2226.3001.4187



训练直接执行detect.py文件

detect: weights=yolov5s.pt, source=data\images, data=data\coco128.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_csv=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs\detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=False, vid_stride=1
YOLOv5  2024-7-16 Python-3.8.19 torch-2.3.1+cu121 CUDA:0 (NVIDIA GeForce RTX 4060, 8188MiB)

提示如下信息则为GPU使用成功

yolov8安装训练文档

https://blog.csdn.net/weixin_45921929/article/details/128673338?ops_request_misc=%257B%2522request%255Fid%2522%253A%25222F2901D8-1B3D-416D-B3A1-F2BEFE656F94%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=2F2901D8-1B3D-416D-B3A1-F2BEFE656F94&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-128673338-null-null.142^v100^pc_search_result_base1&utm_term=yolov8%E8%AE%AD%E7%BB%83&spm=1018.2226.3001.4187

 1、安装ultralytics 框架

pip install ultralytics  

框架包含着一些yolo的方法,而且默认安装的是CPU模式的,如果有显卡就用GPU模式

#卸载
pip uninstall torch
pip uninstall torchvision


#安装gpu版本
pip install torch torchvision torchaudio -i https://download.pytorch.org/whl/cu118

注意:  如果你要装GPU模式的话,推荐先去看下上面那个可忽略文档中的GPU模式,因为他要先去装一个cuda,然后你在根据显卡版本,来选你这里要安装的gpu版本地址是什么

验证GPU是否安装成功

返回

2、python调用yolo

yolo是依赖训练好的模型,去匹配图片上的物体位置的,下面我们用官方的yolov8n.pt去检查图片中的信息 注意文件路径

 yolov8n.pt文件下载

https://download.csdn.net/download/qq_42883074/89709534

vi main.py

from ultralytics import YOLO

# 加载预训练的YOLOv8n模型
model = YOLO('C:\\image\\yolov8n.pt')

# 在图片列表上运行批处理推理
results = model(['C:\\image\\test.jpg'], stream=True)  # 返回 Results 对象生成器

# 初始化一个空列表来存储推理结果
tuili_jieguo = []

# 遍历推理结果生成器
for result in results:
    if len(result.boxes.cls) > 0:  # 如果检测到了物体
        # 遍历每个检测到的物体
        for i in range(len(result.boxes.cls)):
            # 获取类别ID
            leibie_id = int(result.boxes.cls[i].item())
            # 获取类别的名称
            leibie = result.names[leibie_id]

            # 获取物体的相似度(置信度)
            xiangsidu = result.boxes.conf[i].item()

            # 获取物体的边界框坐标值,返回的是左上角和右下角坐标
            zuobiao = result.boxes.xyxy[i].tolist()

            # 存入列表中
            tuili_jieguo.append({
                '类别': leibie,
                '相似度': xiangsidu,
                '坐标': zuobiao
            })

# 打印结果
if len(tuili_jieguo) > 0:
    for info in tuili_jieguo:
        print(info)
else:
    print('没有找到任何元素')

 返回

0: 448x640 16 cars, 170.8ms
Speed: 3.0ms preprocess, 170.8ms inference, 2.1ms postprocess per image at shape (1, 3, 448, 640)
{'类别': 'car', '相似度': 0.9120942950248718, '坐标': [0.11486291885375977, 116.66407012939453, 53.699710845947266, 215.73313903808594]}
{'类别': 'car', '相似度': 0.900637149810791, '坐标': [66.12506866455078, 71.41484069824219, 167.28611755371094, 145.25247192382812]}
{'类别': 'car', '相似度': 0.8911173343658447, '坐标': [106.8707504272461, 120.03921508789062, 299.03265380859375, 289.7550964355469]}
{'类别': 'car', '相似度': 0.8844043612480164, '坐标': [284.8628234863281, 118.95249938964844, 439.5052490234375, 254.8725128173828]}
{'类别': 'car', '相似度': 0.8756222128868103, '坐标': [0.23036420345306396, 38.31007766723633, 61.097496032714844, 101.75540924072266]}
{'类别': 'car', '相似度': 0.8706174492835999, '坐标': [224.3538360595703, 0.0, 286.7179870605469, 37.17644500732422]}
{'类别': 'car', '相似度': 0.8436808586120605, '坐标': [394.2897033691406, 61.2234992980957, 449.84222412109375, 125.12831115722656]}
{'类别': 'car', '相似度': 0.8252866268157959, '坐标': [138.55804443359375, 38.61257553100586, 219.90403747558594, 109.65338897705078]}
{'类别': 'car', '相似度': 0.79660564661026, '坐标': [49.582916259765625, 8.446189880371094, 120.65869903564453, 66.15179443359375]}
{'类别': 'car', '相似度': 0.7446664571762085, '坐标': [225.2955322265625, 75.20341491699219, 314.8150939941406, 143.9746856689453]}
{'类别': 'car', '相似度': 0.6907260417938232, '坐标': [106.29742431640625, 0.0, 163.33396911621094, 44.811012268066406]}
{'类别': 'car', '相似度': 0.5326533317565918, '坐标': [149.607177734375, 0.0, 178.77105712890625, 33.9000358581543]}
{'类别': 'car', '相似度': 0.45000824332237244, '坐标': [0.022820234298706055, 10.011242866516113, 20.739538192749023, 41.001014709472656]}
{'类别': 'car', '相似度': 0.44525644183158875, '坐标': [324.627197265625, 90.42803192138672, 376.8398132324219, 121.98643493652344]}
{'类别': 'car', '相似度': 0.4406210482120514, '坐标': [167.20285034179688, 0.0, 197.07720947265625, 20.764047622680664]}
{'类别': 'car', '相似度': 0.41411808133125305, '坐标': [427.8833923339844, 35.41863250732422, 449.89251708984375, 64.04993438720703]}


#这里的坐标是框框的坐标  左上角和右下角

3、视频流实时识别

mss库不断截屏来获取视频流,然后交给yolo,通过opencv标记

import mss  # 截屏
import cv2  # 图片识别
import numpy as np  # 将mss的截屏转换为opencv能识别的图片
from ultralytics import YOLO  # 识别画面

# 加载预训练的YOLOv8n模型
model = YOLO('C:\\image\\yolov8n.pt')

# 定义截图区域
#使用微信的截图功能能获取到pos的值 前面的值写top  后面的值写left
quyu = {"left": 326, "top": 613, "width": 697, "height": 340}

# 实例化截图mss库工具,重命名sct
with mss.mss() as sct:
    while True:  # 死循环
        # 死循环截图--识别--标记--显示

        # 1、获取指定区域的截图
        quyujieping = sct.grab(quyu)  # 截图
        quyujieping = np.array(quyujieping)  # 转换opencv识别的图片
        quyujieping2 = cv2.cvtColor(quyujieping, cv2.COLOR_BGR2RGB)  # 截图转换为RGB排序,才能给YOLO识别
        # 给yolo的要转换RGB
        # 给opencv的不用转换

        # 2、在图片列表上运行批量推理 (识别)类、相似度、坐标
        results = model(quyujieping2, stream=True)  # 返回 Results 对象生成器

        tuili_jieguo = []  # 推理结果

        # 处理结果生成器
        for result in results:
            if len(result.boxes.cls) > 0:
                for i in range(len(result.boxes.cls)):
                    # 获取类别ID
                    leibie_id = int(result.boxes.cls[i].item())
                    # 获取类别名称
                    leibie = result.names[leibie_id]

                    # 获取相似度
                    xiangsidu = str(round(result.boxes.conf[i].item(), 2))

                    # 获取坐标值,两个,左上角和右下角
                    zuobiao = result.boxes.xyxy[i].tolist()

                    # 存入列表中
                    tuili_jieguo.append({
                        '类别': leibie,
                        '相似度': xiangsidu,
                        '坐标': zuobiao
                    })

        # 3  绘制边界框  标记
        for info in tuili_jieguo:
            # 使用opencv将结果中的坐标画出来
            cv2.rectangle(
                quyujieping,
                (int(info['坐标'][0]), int(info['坐标'][1])),
                (int(info['坐标'][2]), int(info['坐标'][3])),
                (0, 255, 0),  # 绿色边界框
                2
            )
            # 标记类别
            cv2.putText(
                quyujieping,
                f"{info['类别']}",
                (int(info['坐标'][0]), int(info['坐标'][1]) + 15),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.6,
                (255, 255, 255),  # 白色文字
                1,
                cv2.LINE_AA
            )

            # 标记相似度 *文本写入
            cv2.putText(
                quyujieping,
                f"{info['相似度']}",
                (int(info['坐标'][2]) + 8, int(info['坐标'][1]) + 15),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.6,
                (255, 255, 255),  # 白色文字
                1,
                cv2.LINE_AA
            )

        # 4、显示窗口部分
        cv2.namedWindow("video", cv2.WINDOW_NORMAL)  # 创建窗口
        cv2.resizeWindow("video", quyu['width'] // 2, quyu['height'] // 2)  # 设置窗口大小
        cv2.imshow("video", quyujieping)  # 显示窗口

        # 退出部分
        if cv2.waitKey(5) & 0xFF == ord("q"):  # 按q键退出
            cv2.destroyAllWindows()
            break

基于yolov 的游戏自动化收藏

https://blog.csdn.net/weixin_45162417/article/details/90734813

其他小知识

有些服务程序不能多开,需要定制一个去虚拟化的虚拟机,然后用vnc远程虚拟机,此时的虚拟机等同于一台正常被远控的电脑运行,如果你要躺平打游戏用,最好还是前台操作占用鼠标拿着,后台可能会被检测,自己看着整吧

https://www.bilibili.com/video/BV1Eh4y1B7aH/?spm_id_from=333.337.search-card.all.click&vd_source=e9412620d40b3ba4fa97ed84970f203b


版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐