使用桌面复制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,因此依赖性非常小。即:
这些依赖项将自动与d3dshot一起安装;无需担心它们!
概念
捕获输出
创建d3dshot实例时定义所需的捕获输出。它定义了所有捕获图像的类型。默认情况下,所有捕获都将返回pil.image对象。如果你主要是想拍截图,这是个不错的选择。
importd3dshotd=d3dshot.create()d.screenshot()0
但是d3dshot非常灵活!当您的环境满足某些可选要求时,将提供更多选项。
如果numpy可用
importd3dshotd=d3dshot.create()d.screenshot()1
如果numpy和pytorch可用
如果numpy和pytorch可用+安装了cuda并且torch.cuda.is\u可用() 尝试使用环境不满足要求的捕获输出将导致错误。 创建d3dshot实例时,也会初始化帧缓冲区。它是一种线程安全的、先进先出的方式,用于保存一定数量的捕获,并实现为 默认情况下,帧缓冲区的大小设置为60。您可以在创建d3dshot对象时自定义它。 注意RAM的使用使用较大的值;您将处理未压缩的图像,根据分辨率的不同,每个图像最多使用100 MB。 可以使用 缓冲区由以下方法使用: 在开始这些操作之前,它总是自动清除。 当您创建d3dshot实例时,将自动检测可用的显示及其所有相关属性。 默认情况下,将选择主显示。您可以随时验证将哪个显示器设置为用于捕获。 选择另一个要捕获的显示器与将d.display设置为d.displays中的另一个值一样简单。displays 显示器旋转和缩放由d3dshot进行检测和处理
所有捕获方法(包括屏幕截图)都接受可选的区域。期望值是一个4长度的整数元组,其结构如下: 例如,如果您只想从左侧和顶部捕获偏移量为100px的200px x x 200px区域,您可以执行以下操作: 如果您正在捕获缩放显示,则将根据完整的、非缩放的分辨率计算区域。 如果浏览源代码,您会注意到区域裁剪发生在完全显示捕获之后。这似乎是次优的,但测试显示,使用copysubresourcereion将gpud311texture2d的区域复制到目标cpud311texture2d时,仅当区域非常小时才更快。事实上,不需要很长时间,较大的区域就开始变得比使用此方法的完整显示捕获慢。更糟糕的是,它增加了很多复杂性,因为它的表面间距与缓冲区大小不匹配,并且对旋转显示的处理也不同。因此,决定在所有情况下都坚持使用CopyResource并在事后进行裁剪更有意义。 创建一个d3dshot实例 不要直接导入d3dshot类并尝试自己初始化它! 一旦在作用域中有了d3dshot实例,我们就可以开始使用它了! 列出检测到的显示器 选择要捕获的显示器 默认情况下,您的主显示器处于选中状态,但如果您设置了多显示器,则可以在 截图 返回:屏幕截图,其格式与创建d3dshot对象时选择的捕获输出相匹配
截图并保存到磁盘 返回:表示保存的图像文件的完整路径的字符串 每隔x秒拍摄一次屏幕快照 此操作是线程化的,不阻塞。它将一直运行,直到调用 返回:一个布尔值,指示捕获线程是否已启动 每隔x秒拍摄一张屏幕快照并将其保存到磁盘 此操作是线程化的,不阻塞。它将一直运行,直到调用 返回:一个布尔值,指示捕获线程是否已启动 开始高速屏幕捕获 此操作是线程化的,不阻塞。它将一直运行,直到调用 返回:一个布尔值,指示捕获线程是否已启动 从缓冲区中获取最新帧 返回:格式与创建d3dshot对象时选择的捕获输出相匹配的帧
从缓冲区中获取特定帧 返回:格式与创建d3dshot对象时选择的捕获输出相匹配的帧
从缓冲区中获取特定帧 返回:一个帧列表,其格式与创建d3dshot对象时选择的捕获输出相匹配
将缓冲区中的特定帧作为堆栈获取 只对numpy和pytorch捕获输出有影响。 返回:堆叠在指定维度上的单个数组,其格式与创建d3dshot对象时选择的捕获输出相匹配。如果捕获输出不可堆叠,则返回帧列表。 将帧缓冲区转储到磁盘 文件将根据以下约定命名: 返回:无 测试windows桌面复制api的准确性能有点复杂,因为它只会在屏幕内容发生更改时返回新的纹理数据。这对于性能来说是最优的,但这使得很难用每秒帧数来表示,人们通常期望的是基准。最终的解决方案是在屏幕上运行一个高fps的视频游戏来捕获,以确保在基准测试时屏幕内容始终不同。 一如既往,请记住基准测试本身就有缺陷,并且高度依赖于您的硬件配置和其他环境。使用下面的数字作为从d3dshot中期望得到什么的相对指示,而不是某种绝对真理。 绝对最快的捕获输出似乎是"numpy",未旋转的"pytorch";平均速度约为58 fps。在蟒蛇之地,这很快! numpy数组有一个cTypes接口,可以为您提供它们的原始内存地址( 实际上,结果是这样的: 这种低级的操作速度极快,使得通常与numpy竞争的其他一切都化为乌有。 不要告诉任何人,但它能在第一时间与纽比竞争的原因只是因为…它是从上面的方法生成的numpy数组生成的!如果你在代码周围搜索,你会发现 pil没有像numpy那样的cTypes接口,因此需要先将byteArray读入python,然后将其馈送到 它仍然是默认的捕获输出,因为: 桌面复制api可访问的direct3d纹理的数据格式化为字节。为了将这些数据表示为规范化的浮点,需要对包含这些字节的数组执行类型转换和元素分割。这将导致严重的性能损失。有趣的是,你可以在gpu pytorch张量上看到这种性能损失的减轻,因为按元素划分可以在设备上大规模并行化。importd3dshotd=d3dshot.create()d.screenshot()
2
importd3dshotd=d3dshot.create()d.screenshot()
3
帧缓冲区
collections.deque
importd3dshotd=d3dshot.create()d.screenshot()
4
d.frame_buffer
直接访问帧缓冲区,但建议改用实用方法。d.capture()
d.屏幕截图
显示
importd3dshotd=d3dshot.create()d.screenshot()
5
importd3dshotd=d3dshot.create()d.screenshot()
6
importd3dshotd=d3dshot.create()d.screenshot()
7
importd3dshotd=d3dshot.create()d.screenshot()
8
importd3dshotd=d3dshot.create()d.screenshot()
9
0
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
区域
1
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
2
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
用法
3
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
创建
接受2个可选Kwargs:捕获输出
:要使用的捕获输出。请参见"概念"下的"捕获输出"部分
帧缓冲区大小
:帧缓冲区可以增长到的最大大小。参见概念下的帧缓冲区部分
create
helper函数在幕后为您初始化并验证一堆东西。importd3dshotd=d3dshot.create()d.screenshot()
5
d.displays
5
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
6
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
屏幕截图
接受1个可选Kwarg:region
:区域元组。参见概念下的区域部分
屏幕截图到磁盘
接受3个可选Kwargs:目录
:写入文件的路径/目录。如果省略,将使用程序的工作目录文件名
:要使用的文件名。允许的扩展名是:.png,.jpg。如果省略,文件名将为<;time.time()>;.png
region
:区域元组。参见概念下的区域部分
8
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
d.stop()
为止。捕获被推送到帧缓冲区。屏幕截图
region
:区域元组。参见概念下的区域部分
9
Out[1]: <PIL.Image.Image image mode=RGB size=2560x1440 at 0x1AA7ECB5C88>
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
importd3dshotd=d3dshot.create()d.screenshot_to_disk()
2
importd3dshotd=d3dshot.create()d.screenshot_to_disk()
3
importd3dshotd=d3dshot.create()d.screenshot_to_disk()
4
获取帧堆栈
接受1个可选Kwarg:
,最后一个之一。要在哪个轴/维度上执行堆栈堆栈维度
:第一个<;帧缓冲区索引>;.png
importd3dshotd=d3dshot.create()d.screenshot_to_disk()
5
帧缓冲区到磁盘
接受1个可选Kwarg:目录
:路径/目录文件的写入位置。如果省略,将使用程序的工作目录性能
< T/> < /广告><正文>2560x1440开英伟达GTX 1080 Ti 1920x108onIntel UHD Graphics 630 1080x1920(垂直)在Intel UHD Graphics 630上
"pil" 每秒29.717帧 每秒47.75帧 每秒35.95帧 "numpy" 57.667 fps 58.1 fps 58.033 fps "numpy_float" 每秒18.783帧 29.05帧/秒 每秒27.517帧 "Pythorch" 57.867 fps 58.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帧 x.cTypes.data
)。如果您有另一个字节缓冲区的内存地址和大小,这就是我们通过处理从桌面复制api返回的内容而得到的结果,那么您可以使用cTypes.memmove
将该字节缓冲区直接复制到numpy结构,有效地绕过尽可能多的pythonE.importd3dshotd=d3dshot.create()d.screenshot_to_disk()
6
为什么旋转显示器上的"pytorch"捕获输出速度较慢?
torch.from_numpy()
散落在周围。这几乎与"numpy"捕获输出1:1的速度匹配,除了处理旋转显示时。显示旋转由np.rot90()
调用处理,该调用在该数组上产生负跨距。负跨步在numpy下被理解并表现良好,但在pytorch中在撰写本文时仍然不受支持。为了解决这个问题,需要一个额外的复制操作来将其恢复到一个相邻的数组,这样会降低性能。为什么"pil"捕获输出是默认的,而不是最快的?
pil.image.fromBytes()
。从python的角度来说,这仍然很快,但它无法与低级核方法。为什么浮点版本的捕获输出速度较慢?
推荐PyPI第三方库