抓取屏幕截图的两种方法比较

对比Python中使用PIL库的ImageGrab.grab()方法和PyQt5库来抓取屏幕截图的效率。

概要

在编写代码时,有时我们需要抓取屏幕的截图进行进一步的处理和分析。我尝试了Python中的两种不同方法来实现此功能,并进行了比较。

方法一:使用PIL库的ImageGrab.grab()方法

PIL(Python Imaging Library)库的ImageGrab模块提供了grab()方法来实现屏幕截图。以下是实现细节:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import win32api
import win32con
import win32gui
import win32ui
import time
import matplotlib.pyplot as plt
from PIL import ImageGrab

class GameScreenshot:
    def __init__(self, gamename="Setris"):
        self.gamename = gamename
        self.hwnd = self.get_game_handle()
        self.window_rect = self.get_window_rect()

    def is_game_top(self):
        """判断游戏是否处于顶部,返回布尔值"""
        return win32gui.GetForegroundWindow() == self.hwnd if self.hwnd else False

    def is_game_running(self):
        """判断游戏是否在运行,返回布尔值"""
        return win32gui.IsWindow(self.hwnd) if self.hwnd else False

    def get_game_handle(self):
        """获取游戏句柄,返回整数值"""
        return win32gui.FindWindow(None, self.gamename) or 0

    def get_window_rect(self):
        """获取游戏窗口的矩形坐标,返回一个四元组"""
        if self.hwnd:
            return win32gui.GetWindowRect(self.hwnd)
        else:
            return None

    def get_game_img(self):
        """获取游戏截图,返回Image对象"""
        if self.hwnd:
            grab_image = ImageGrab.grab(self.window_rect)
            return grab_image
        return None

    def get_game_img_in_top(self, timeout=3):
        """获取游戏截图,若游戏不在顶部则强制游戏处于顶部并返回Image对象"""
        if self.hwnd:
            start_time = time.time()
            while not self.is_game_top():
                if time.time() - start_time > timeout:
                    print("窗口置顶超时,跳过截图")
                    return None
                win32gui.SetForegroundWindow(self.hwnd)
            return self.get_game_img()


game = GameScreenshot()
if game.is_game_running():
    print("游戏句柄:", game.hwnd)
    if not game.is_game_top():
        win32gui.SetForegroundWindow(game.hwnd)

    # 计算抓取100次截图所需时间
    start = time.time()
    for _ in range(100):
        img = game.get_game_img()
    end = time.time()
    print("截图时间:", end - start)
    plt.imshow(img)
else:
    print("游戏未运行")

输出结果为: 游戏句柄: 2229214 截图时间: 3.0059990882873535

方法二:使用PyQt5库抓取屏幕截图

另一种方法是利用PyQt5库来抓取屏幕截图,具体代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import numpy as np
import time
from PyQt5 import QtWidgets
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QApplication
import win32gui

class Pyqt5Catcher:
    def __init__(self, window_title):
        self.window_title = window_title
        self.app = QApplication([])
        self.hwnd = win32gui.FindWindow(None, window_title)
        self.screen = QApplication.primaryScreen()
        screenshot = self.screen.grabWindow(self.hwnd).toImage()
        self.imgarr = np.empty((screenshot.height(), screenshot.width(), 4), dtype=np.uint8)
    def catch(self):
        screenshot = self.screen.grabWindow(self.hwnd).toImage()
        ptr = screenshot.bits()
        ptr.setsize(screenshot.height() * screenshot.bytesPerLine())
        np.copyto(self.imgarr, np.frombuffer(ptr, np.uint8).reshape(screenshot.height(), screenshot.width(), 4))
        return self.imgarr[..., :3]
    
catcher = Pyqt5Catcher('Setris')

start = time.time()
for i in range(100):
    img = catcher.catch()
end = time.time()
print(end - start)
print(img.shape)
# show img with plt
import matplotlib.pyplot as plt
plt.imshow(img)

输出结果为: 0.025999784469604492 (144, 160, 3)

结果比较和总结

使用PIL库的ImageGrab.grab()方法抓取100次截图的时间约为3秒。而使用PyQt5库的时间仅为0.02秒。显然,PyQt5在性能上表现得更好。如果截图速度要求高,个人认为推荐使用PyQt5。但也要根据具体需求和情况进行选择。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus