在Windows系统下,使用Python最快的方法截屏

18

在Windows上快速截屏的最快方法是什么?PIL.ImageGrab 相对较慢...它需要4-5秒钟才能截取同一个小窗口的30个截图。截取整个桌面的截图甚至更慢。


听起来你真正想要的是屏幕视频,类似于在Sourceforge上的CamStudio。使用案例是什么? - Simon Hibbs
8
在Winterbells游戏中获得有史以来最高分 =P。 - Claudiu
刚刚玩了一下Winterbells,真是让人意外上瘾。 - jack.py
4个回答

40

你可以直接使用win32 API进行操作。

  1. 首先将焦点转移到您想要截取屏幕的应用程序上。 链接文本

  2. Win32 API可帮助进行屏幕截图:

import win32gui
import win32ui
import win32con

w = 1920 # set this
h = 1080 # set this
bmpfilenamename = "out.bmp" #set this

hwnd = win32gui.FindWindow(None, windowname)
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, w, h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0,0),(w, h) , dcObj, (0,0), win32con.SRCCOPY)
dataBitMap.SaveBitmapFile(cDC, bmpfilenamename)

# Free Resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())

14
非常重要,因为这个问题最近给我带来了麻烦:你必须删除/释放由此创建的所有DC,否则在拍摄大约90张图像后,你将无法再继续。在这种情况下,使用dcObj.DeleteDC(); cDC.DeleteDC(); win32gui.ReleaseDC(hwnd, wDC)进行删除/释放。 - Claudiu
请查看此脚本以获取更多详细信息:http://bytes.com/topic/python/answers/576924-win32ui-vs-wxpy-screen-capture-multi-monitor - Alex L
2
作为对@Claudiu评论的跟进(正如他所指出的那样必要),我发现win32gui.DeleteObject(dataBitMap.GetHandle())也是必要的。我已经编辑了答案,包括这两个评论。 - jedwards
1
windowname 在哪里被定义了或者它应该是什么? - Hendy
由于某些原因,这对我不起作用。如果我像这样创建一个DC对象 dcObj = win32ui.CreateDCFromHandle(wDC) 我无法访问该对象的函数,因此 dcObj.CreateCompatibleDC() 不起作用 (我使用VSCode,通常在键入对象后的点时会给出建议,但是在这里它没有这样做,所以我认为我需要导入其他东西?我在我的虚拟环境中安装了pywin32,并在程序中导入了 win32gui、win32ui、win32con。有人有想法吗? - xvk
显示剩余2条评论

6

刚找到了如何使用gtk实现。看起来这是迄今为止最快的方法:

def image_grab_gtk(window):
    left, top, right, bot = get_rect(window)
    w = right - left
    h = bot - top

    s = gtk.gdk.Pixbuf(
        gtk.gdk.COLORSPACE_RGB, False, 8, w, h)

    s.get_from_drawable(
        gtk.gdk.get_default_root_window(),
        gtk.gdk.colormap_get_system(),
        left, top, 0, 0, w, h )

    final = Image.frombuffer(
        "RGB",
        (w, h),
        s.get_pixels(),
        "raw",
        "RGB",
        s.get_rowstride(), 1)
    return final

在不将其转换为 PIL 图像的情况下,它比我的测试用例中的 PIL 快8倍。即使进行转换,它仍然快约2.7倍。


2
你可以尝试我的新项目DXcam:我认为对于原始速度它是最快的(在Python中,而且不需要深入研究)。它最初是为FPS游戏的深度学习流水线创建的,其中获得更高的FPS越好。另外,我正在尝试设计它以使其易于使用: 要获取屏幕截图,只需执行以下操作:
import dxcam
camera = dxcam.create()
frame = camera.grab()  # full screen
frame = camera.grab(region=(left, top, right, bottom))  # region

屏幕截图:

camera.start(target_fps=60)  # threaded
for i in range(1000):
    image = camera.get_latest_frame()  # Will block until new frame available
camera.stop()

我从自述文件中复制了基准测试部分的内容:
DXcam python-mss D3DShot
平均FPS 238.79 75.87 118.36
标准差 1.25 0.5447 0.3224
这些基准测试是在我的240hz显示器上进行的,使用blurbuster ufo测试以与显示器同步的240hz渲染速率进行了5次试验。
您可以在此处阅读更多详细信息:https://github.com/ra1nty/DXcam

这是一个有趣的库!请将其放在主PyPI上。他们的测试仓库只用于测试。 - Christoph Rackwitz
就我个人而言,对于我的应用程序来说,这比mss慢得多,但每帧都是新的保证很好。 - Eric M.
通过dxcam有没有办法从特定窗口截取屏幕截图? - undefined

1
您可以使用 mss 软件包:

将屏幕截图保存为图像文件

import mss

with mss.mss() as sct:
    filename = sct.shot(output="output.png")

获取屏幕截图的numpy表示。
import mss
import numpy as np

with mss.mss() as sct:
    monitor = {"top": 160, "left": 160, "width": 160, "height": 135}
    img_array = np.array(sct.grab(monitor))
    # Do whatever you want...

如果你有比较过,这个选项的速度如何?无论如何,感谢你的分享! - Eric Carmichael
我在一台2k显示器上实现了40+帧每秒的效果,而在4k显示器上则只有20+帧每秒。 - SeanCheey
谢谢分享!使用其他方法在1440p下,我只能获得13fps的帧率,但是使用mss后,我能够达到20fps。它绝对很快 :) - Eric Carmichael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接