使用桌面复制api在windows上实现极其快速和健壮的屏幕捕获

D3DShot的Python项目详细描述


d3dshot

d3dshot是windows桌面复制api的纯python实现。它利用了dxgi和direct3d系统库,为windows上的python脚本和应用程序提供了极其快速和强大的屏幕捕获功能。

d3dshot:

  • 是目前为止用python在windows 8.1+上捕获屏幕的最快方式
  • 很容易使用。如果你能记住10种方法,那你就知道全部了。
  • 涵盖所有常见场景和用例:
    • 屏幕截图到内存
    • 屏幕截图到磁盘
    • 屏幕快照到内存缓冲区,每x秒一次(线程;非阻塞)
    • 每x秒磁盘截图(线程;非阻塞)
    • 高速捕获到内存缓冲区(线程;非阻塞)
  • 捕获以将图像从框中取出。如果可以找到Numpy或Pythorch,则可以优雅地添加输出选项。
  • 检测几乎所有配置中的显示:单个显示器、一个适配器上的多个显示器、多个适配器上的多个显示器。
  • 控制柄为您显示旋转和缩放
  • 支持捕获屏幕的特定区域
  • 非常稳定。您可以运行数小时/数天而不会降低性能
  • 甚至可以捕获DirectX 11/12独家全屏应用程序和游戏!

tl;dr快速代码示例

屏幕截图到内存

importd3dshotd=d3dshot.create()d.screenshot()
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>

屏幕截图到磁盘

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
Out[1]: './1554298682.5632973.png'

屏幕捕捉5秒并抓取最新帧

importd3dshotimporttimed=d3dshot.create()d.capture()time.sleep(5)# Capture is non-blocking so we wait explicitelyd.stop()d.get_latest_frame()
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA044BCF60>

屏幕将第二个监视器捕获为numpy数组,持续3秒,并将最新的4帧作为堆栈捕获

importd3dshotimporttimed=d3dshot.create(capture_output="numpy")d.display=d.displays[1]d.capture()time.sleep(3)# Capture is non-blocking so we wait explicitelyd.stop()frame_stack=d.get_frame_stack((0,1,2,3),stack_dimension="last")frame_stack.shape
Out[1]: (1080, 1920, 3, 4)

这只是表面的擦伤…继续读下去!

要求

  • Windows 8.1+(64位)
  • python 3.6+(64位)

安装

pip install d3dshot

d3dshot利用系统上已经可用的dll,因此依赖性非常小。即:

  • 通信类型:内部使用。在使用COM接口时保持开发人员的理智。
  • 枕头:默认捕获输出。也用于以PNG和JPG格式保存到磁盘。

这些依赖项将自动与d3dshot一起安装;无需担心它们!

概念

捕获输出

创建d3dshot实例时定义所需的捕获输出。它定义了所有捕获图像的类型。默认情况下,所有捕获都将返回pil.image对象。如果你主要是想拍截图,这是个不错的选择。

importd3dshotd=d3dshot.create()d.screenshot()
0

但是d3dshot非常灵活!当您的环境满足某些可选要求时,将提供更多选项。

如果numpy可用

importd3dshotd=d3dshot.create()d.screenshot()
1

如果numpypytorch可用

importd3dshotd=d3dshot.create()d.screenshot()
2

如果numpypytorch可用+安装了cuda并且torch.cuda.is\u可用()

importd3dshotd=d3dshot.create()d.screenshot()
3

尝试使用环境不满足要求的捕获输出将导致错误。

帧缓冲区

创建d3dshot实例时,也会初始化帧缓冲区。它是一种线程安全的、先进先出的方式,用于保存一定数量的捕获,并实现为collections.deque

默认情况下,帧缓冲区的大小设置为60。您可以在创建d3dshot对象时自定义它。

importd3dshotd=d3dshot.create()d.screenshot()
4

注意RAM的使用使用较大的值;您将处理未压缩的图像,根据分辨率的不同,每个图像最多使用100 MB。

可以使用d.frame_buffer直接访问帧缓冲区,但建议改用实用方法。

缓冲区由以下方法使用:

  • d.capture()
  • d.屏幕截图

在开始这些操作之前,它总是自动清除。

显示

当您创建d3dshot实例时,将自动检测可用的显示及其所有相关属性。

importd3dshotd=d3dshot.create()d.screenshot()
5
importd3dshotd=d3dshot.create()d.screenshot()
6

默认情况下,将选择主显示。您可以随时验证将哪个显示器设置为用于捕获。

importd3dshotd=d3dshot.create()d.screenshot()
7
importd3dshotd=d3dshot.create()d.screenshot()
8

选择另一个要捕获的显示器与将d.display设置为d.displays中的另一个值一样简单。displays

importd3dshotd=d3dshot.create()d.screenshot()
9
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
0

显示器旋转和缩放由d3dshot进行检测和处理

  • 旋转显示器上的捕获将始终处于正确的方向(即与您在物理显示器上看到的内容相匹配)
  • 缩放显示上的捕获将始终是完全、非缩放分辨率(例如,200%缩放时1280x720将产生2560x1440个捕获)

区域

所有捕获方法(包括屏幕截图)都接受可选的区域。期望值是一个4长度的整数元组,其结构如下:

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
1

例如,如果您只想从左侧和顶部捕获偏移量为100px的200px x x 200px区域,您可以执行以下操作:

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
2

如果您正在捕获缩放显示,则将根据完整的、非缩放的分辨率计算区域。

如果浏览源代码,您会注意到区域裁剪发生在完全显示捕获之后。这似乎是次优的,但测试显示,使用copysubresourcereion将gpud311texture2d的区域复制到目标cpud311texture2d时,仅当区域非常小时才更快。事实上,不需要很长时间,较大的区域就开始变得比使用此方法的完整显示捕获慢。更糟糕的是,它增加了很多复杂性,因为它的表面间距与缓冲区大小不匹配,并且对旋转显示的处理也不同。因此,决定在所有情况下都坚持使用CopyResource并在事后进行裁剪更有意义。

用法

创建一个d3dshot实例

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
3

创建接受2个可选Kwargs:

  • 捕获输出:要使用的捕获输出。请参见"概念"下的"捕获输出"部分
  • 帧缓冲区大小:帧缓冲区可以增长到的最大大小。参见概念下的帧缓冲区部分

不要直接导入d3dshot类并尝试自己初始化它!createhelper函数在幕后为您初始化并验证一堆东西。

一旦在作用域中有了d3dshot实例,我们就可以开始使用它了!

列出检测到的显示器

importd3dshotd=d3dshot.create()d.screenshot()
5

选择要捕获的显示器

默认情况下,您的主显示器处于选中状态,但如果您设置了多显示器,则可以在d.displays

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
5

截图

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
6

屏幕截图接受1个可选Kwarg:

  • region:区域元组。参见概念下的区域部分

返回:屏幕截图,其格式与创建d3dshot对象时选择的捕获输出相匹配

截图并保存到磁盘

αα-α27

屏幕截图到磁盘接受3个可选Kwargs:

  • 目录:写入文件的路径/目录。如果省略,将使用程序的工作目录
  • 文件名:要使用的文件名。允许的扩展名是:.png.jpg。如果省略,文件名将为<;time.time()>;.png
  • region:区域元组。参见概念下的区域部分

返回:表示保存的图像文件的完整路径的字符串

每隔x秒拍摄一次屏幕快照

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
8

此操作是线程化的,不阻塞。它将一直运行,直到调用d.stop()为止。捕获被推送到帧缓冲区。

屏幕截图

  • region:区域元组。参见概念下的区域部分

返回:一个布尔值,指示捕获线程是否已启动

每隔x秒拍摄一张屏幕快照并将其保存到磁盘

Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
9

此操作是线程化的,不阻塞。它将一直运行,直到调用d.stop()

屏幕截图到磁盘的间隔

  • 目录:写入文件的路径/目录。如果省略,将使用程序的工作目录
  • region:区域元组。参见概念下的区域部分

返回:一个布尔值,指示捕获线程是否已启动

开始高速屏幕捕获

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
0

此操作是线程化的,不阻塞。它将一直运行,直到调用d.stop()为止。捕获被推送到帧缓冲区。

捕获接受2个可选Kwargs:

  • 目标每秒捕获多少次。如果系统跟不上,有效捕获率就会下降,但永远不会超过这个目标。为了不浪费系统资源,建议为您的用例设置一个合理的值。默认设置为60。
  • region:区域元组。参见概念下的区域部分

返回:一个布尔值,指示捕获线程是否已启动

从缓冲区中获取最新帧

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
1

返回:格式与创建d3dshot对象时选择的捕获输出相匹配的帧

从缓冲区中获取特定帧

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
2

返回:格式与创建d3dshot对象时选择的捕获输出相匹配的帧

从缓冲区中获取特定帧

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
3

返回:一个帧列表,其格式与创建d3dshot对象时选择的捕获输出相匹配

将缓冲区中的特定帧作为堆栈获取

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
4

只对numpy和pytorch捕获输出有影响。

获取帧堆栈接受1个可选Kwarg:

  • 堆栈维度:第一个最后一个之一。要在哪个轴/维度上执行堆栈

返回:堆叠在指定维度上的单个数组,其格式与创建d3dshot对象时选择的捕获输出相匹配。如果捕获输出不可堆叠,则返回帧列表。

将帧缓冲区转储到磁盘

文件将根据以下约定命名:<;帧缓冲区索引>;.png

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
5

帧缓冲区到磁盘接受1个可选Kwarg:

  • 目录:路径/目录文件的写入位置。如果省略,将使用程序的工作目录

返回:无

性能

测试windows桌面复制api的准确性能有点复杂,因为它只会在屏幕内容发生更改时返回新的纹理数据。这对于性能来说是最优的,但这使得很难用每秒帧数来表示,人们通常期望的是基准。最终的解决方案是在屏幕上运行一个高fps的视频游戏来捕获,以确保在基准测试时屏幕内容始终不同。

一如既往,请记住基准测试本身就有缺陷,并且高度依赖于您的硬件配置和其他环境。使用下面的数字作为从d3dshot中期望得到什么的相对指示,而不是某种绝对真理。

<表><广告>< T/>2560x1440开英伟达GTX 1080 Ti1920x108onIntel UHD Graphics 6301080x1920(垂直)在Intel UHD Graphics 630上 < /广告><正文>"pil"每秒29.717帧每秒47.75帧每秒35.95帧"numpy"57.667 fps58.1 fps58.033 fps"numpy_float"每秒18.783帧29.05帧/秒每秒27.517帧"Pythorch"57.867 fps58.1 fps每秒34.817帧"pytorch_float"每秒18.767帧每秒28.367帧27.017 fps"Pythorch_GPU"27.333 fps每秒35.767帧每秒34.8帧"pytorch_float_gpu"27.267 fps每秒37.383帧每秒35.033帧

绝对最快的捕获输出似乎是"numpy",未旋转的"pytorch";平均速度约为58 fps。在蟒蛇之地,这很快!

"numpy"捕获输出性能如何?

numpy数组有一个cTypes接口,可以为您提供它们的原始内存地址(x.cTypes.data)。如果您有另一个字节缓冲区的内存地址和大小,这就是我们通过处理从桌面复制api返回的内容而得到的结果,那么您可以使用cTypes.memmove将该字节缓冲区直接复制到numpy结构,有效地绕过尽可能多的pythonE.

实际上,结果是这样的:

importd3dshotd=d3dshot.create()d.screenshot_to_disk()
6

这种低级的操作速度极快,使得通常与numpy竞争的其他一切都化为乌有。

为什么旋转显示器上的"pytorch"捕获输出速度较慢?

不要告诉任何人,但它能在第一时间与纽比竞争的原因只是因为…它是从上面的方法生成的numpy数组生成的!如果你在代码周围搜索,你会发现torch.from_numpy()散落在周围。这几乎与"numpy"捕获输出1:1的速度匹配,除了处理旋转显示时。显示旋转由np.rot90()调用处理,该调用在该数组上产生负跨距。负跨步在numpy下被理解并表现良好,但在pytorch中在撰写本文时仍然不受支持。为了解决这个问题,需要一个额外的复制操作来将其恢复到一个相邻的数组,这样会降低性能。

为什么"pil"捕获输出是默认的,而不是最快的?

pil没有像numpy那样的cTypes接口,因此需要先将byteArray读入python,然后将其馈送到pil.image.fromBytes()。从python的角度来说,这仍然很快,但它无法与低级核方法。

它仍然是默认的捕获输出,因为:

  1. python用户往往熟悉pil图像对象
  2. 与numpy或pytorch相比,它对库的依赖性更轻/更简单
  3. < > >

    为什么浮点版本的捕获输出速度较慢?

    桌面复制api可访问的direct3d纹理的数据格式化为字节。为了将这些数据表示为规范化的浮点,需要对包含这些字节的数组执行类型转换和元素分割。这将导致严重的性能损失。有趣的是,你可以在gpu pytorch张量上看到这种性能损失的减轻,因为按元素划分可以在设备上大规模并行化。

    由蛇形人工智能用制成 推特-推特

    欢迎加入QQ群-->: 979659372 Python中文网_新手群

    推荐PyPI第三方库


热门话题
“电话目录”数据结构的java实现   使用PC remote读取JSP页面上的文件时出现java错误   无法在不同目录中从Java执行Python脚本   java无法在windows 8.1上运行javafx应用程序   java航空公司系统如何防止两个用户同时预订同一个座位?   反射如何在java方法中获取每个参数的名称和值?   阅读中的字符串问题。txt文档并在Java中编辑   java JTextPane行包装问题   使用PowerMock Android Junit时出现java ClassNotFoundException   java输入和If语句   java如何在不使用剪贴板或操作CTRL+C、CTRL+V的情况下将字符串中的“\t”或“tab”发送到selenium中的文本框中   tomcat7将Java应用程序部署到Digitalocean中的Tomcat根目录   响应中嵌套映射的java问题(Jersey)