草庐IT

python简单实现对桌面进行实时捕捉画面

奥怪 2023-06-12 原文

介绍

最近在研究目标检测方面的小东西,需要到对桌面进行实时捕捉画面,获取画面后再检测,达到实时桌面目标检测的目的,所以写了一段小代码来实现该功能,实测速度很快,符合我的需求。特此记录一下。

代码

import argparse
import time
import cv2
import keyboard
import mss
import numpy as np
import win32com.client
import win32con
import win32gui


class ScreenCapture:
    """
    parameters
    ----------
        screen_frame : Tuple[int, int]
            屏幕宽高,分别为x,y
        region : Tuple[float, float]
            实际截图范围,分别为x,y,(1.0, 1.0)表示全屏检测,越低检测范围越小(始终保持屏幕中心为中心)
        window_name : str
            显示窗口名
        exit_key : int
            结束窗口的退出键值,为键盘各键对应的ASCII码值,默认是ESC键
    """

    def __init__(self, screen_frame=(1920, 1080), region=(0.5, 0.5), window_name='test', exit_key=0x1B):
        self.parser = argparse.ArgumentParser()
        self.parser.add_argument('--region', type=tuple, default=region,
                                 help='截图范围;分别为x,y,(1.0, 1.0)表示全屏检测,越低检测范围越小(始终保持屏幕中心为中心)')
        self.parser_args = self.parser.parse_args()

        self.cap = mss.mss(mon=-1, optimize=True)  # 实例化mss,并使用高效模式

        self.screen_width = screen_frame[0]  # 屏幕的宽
        self.screen_height = screen_frame[1]  # 屏幕的高
        self.mouse_x, self.mouse_y = self.screen_width // 2, self.screen_height // 2  # 屏幕中心点坐标

        # 截图区域
        self.GAME_WIDTH, self.GAME_HEIGHT = int(self.screen_width * self.parser_args.region[0]), int(
            self.screen_height * self.parser_args.region[1])  # 宽高
        self.GAME_LEFT, self.GAME_TOP = int(0 + self.screen_width // 2 * (1. - self.parser_args.region[0])), int(
            0 + 1080 // 2 * (1. - self.parser_args.region[1]))  # 原点

        self.RESZIE_WIN_WIDTH, self.RESIZE_WIN_HEIGHT = self.screen_width // 4, self.screen_height // 4  # 显示窗口大小
        self.mointor = {
            'left': self.GAME_LEFT,
            'top': self.GAME_TOP,
            'width': self.GAME_WIDTH,
            'height': self.GAME_HEIGHT
        }

        self.window_name = window_name
        self.Exit_key = exit_key
        self.img = None

    def grab_screen_mss(self, monitor):
        # cap.grab截取图片,np.array将图片转为数组,cvtColor将BRGA转为BRG,去掉了透明通道
        return cv2.cvtColor(np.array(self.cap.grab(monitor)), cv2.COLOR_BGRA2BGR)

    def update_img(self, img):
        self.img = img

    def get_img(self):
        return self.img

    def run(self):
        SetForegroundWindow_f = 0  # 判断是否需要置顶窗口
        while True:
            # 判断是否按下 ctrl+U 窗口始终置顶
            if keyboard.is_pressed('ctrl+U'):
                while keyboard.is_pressed('ctrl+U'):
                    continue
                if SetForegroundWindow_f == 0:
                    SetForegroundWindow_f = 1
                    time.sleep(1)
                    continue
                else:
                    SetForegroundWindow_f = 0

            if self.img is None:
                img = self.grab_screen_mss(self.mointor)

            cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL)  # cv2.WINDOW_NORMAL 根据窗口大小设置图片大小
            cv2.resizeWindow(self.window_name, self.RESZIE_WIN_WIDTH, self.RESIZE_WIN_HEIGHT)
            cv2.imshow(self.window_name, img)

            if SetForegroundWindow_f == 1:
                shell = win32com.client.Dispatch("WScript.Shell")
                shell.SendKeys('%')
                win32gui.SetForegroundWindow(win32gui.FindWindow(None, self.window_name))
                win32gui.ShowWindow(win32gui.FindWindow(None, self.window_name), win32con.SW_SHOW)

            if cv2.waitKey(1) & 0XFF == self.Exit_key:  # 默认:ESC
                cv2.destroyAllWindows()
                exit("结束")

代码讲解

功能实现思路主要是使用 mss 库进行截图,并使用 opencv 库进行图像显示和处理。

首先,使用 argparse 库解析传入的参数,设置检测范围的大小。

然后,使用 mss 库实例化一个截图对象 cap 。

接着,设置屏幕的宽和高,并计算屏幕中心点的坐标。

之后,根据传入的参数计算游戏内截图区域的宽高和原点坐标,并将其保存在变量 mointor 中。

定义了一个函数 grab_screen_mss ,使用 cap.grab 截取图片,并用 np.array 将图片转为数组,然后用 cvtColor 将 BRGA 转为 BRG ,去掉了透明通道。

定义了一个 run 函数,在其中不断循环,判断是否按下 ctrl+U ,若按下,则窗口始终置顶。

然后调用 grab_screen_mss 函数获取截图,使用 cv2 库进行图像显示,并设置显示窗口的大小。

如果窗口需要置顶,则使用 win32com 库和 win32gui 库置顶窗口。

最后,使用 cv2 库的 waitKey 函数等待用户操作,按下 ESC 键退出程序。

调用示例

    sc = ScreenCapture()
    sc.run()

参数解释:

   screen_frame : Tuple[int, int]
        屏幕宽高,分别为x,y
    region : Tuple[float, float]
        实际截图范围,分别为x,y,(1.0, 1.0)表示全屏检测,越低检测范围越小(始终保持屏幕中心为中心)
    window_name : str
        显示窗口名
    exit_key : int
        结束窗口的退出键值,为键盘各键对应的ASCII码值,默认是ESC键

其他

键盘各键对应的ASCII码值 (0x指16进制,delete键的ascii码值是0x2e,也即十进制的46)

0x1 鼠标左键
0x2 鼠标右键
0x3 CANCEL 键
0x4 鼠标中键
0x8 BACKSPACE 键
0x9 TAB 键
0xC CLEAR 键
0xD ENTER 键
0x10 SHIFT 键
0x11 CTRL 键
0x12 MENU 键
0x13 PAUSE 键
0x14 CAPS LOCK 键
0x1B ESC 键
0x20 SPACEBAR 键
0x21 PAGE UP 键
0x22 PAGE DOWN 键
0x23 END 键
0x24 HOME 键
0x25 LEFT ARROW 键
0x26 UP ARROW 键
0x27 RIGHT ARROW 键
0x28 DOWN ARROW 键
0x29 SELECT 键
0x2A PRINT SCREEN 键
0x2B EXECUTE 键
0x2C SNAPSHOT 键
0x2D INSERT 键
0x2E DELETE 键
0x2F HELP 键
0x90 NUM LOCK 键

A 至 Z 键与 A – Z 字母的 ASCII 码相同:

值 描述
65 A 键
66 B 键
67 C 键
68 D 键
69 E 键
70 F 键
71 G 键
72 H 键
73 I 键
74 J 键
75 K 键
76 L 键
77 M 键
78 N 键
79 O 键
80 P 键
81 Q 键
82 R 键
83 S 键
84 T 键
85 U 键
86 V 键
87 W 键
88 X 键
89 Y 键
90 Z 键

0 至 9 键与数字 0 – 9 的 ASCII 码相同:

值 描述
48 0 键
49 1 键
50 2 键
51 3 键
52 4 键
53 5 键
54 6 键
55 7 键
56 8 键
57 9 键

下列常数代表数字键盘上的键:

值 描述
0x60 0 键
0x61 1 键
0x62 2 键
0x63 3 键
0x64 4 键
0x65 5 键
0x66 6 键
0x67 7 键
0x68 8 键
0x69 9 键
0x6A MULTIPLICATION SIGN (*) 键
0x6B PLUS SIGN (+) 键
0x6C ENTER 键
0x6D MINUS SIGN (–) 键
0x6E DECIMAL POINT (.) 键
0x6F DIVISION SIGN (/) 键

下列常数代表功能键:

值 描述
0x70 F1 键
0x71 F2 键
0x72 F3 键
0x73 F4 键
0x74 F5 键
0x75 F6 键
0x76 F7 键
0x77 F8 键
0x78 F9 键
0x79 F10 键
0x7A F11 键
0x7B F12 键
0x7C F13 键
0x7D F14 键
0x7E F15 键
0x7F F16 键

有关python简单实现对桌面进行实时捕捉画面的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  4. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  5. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  6. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  7. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  8. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  9. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  10. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

随机推荐