【问题标题】:Accelerating a screenshot function - Python加速截图功能 - Python
【发布时间】:2019-03-02 16:56:30
【问题描述】:

我需要我的截图函数尽可能快,现在每次调用该函数大约需要 0.2 秒。

这是函数:

def get_screenshot(self, width, height):
    image = self.screen_capture.grab(self.monitor)
    image = Image.frombuffer('RGB', image.size, image.bgra, 'raw', 'BGRX')
    image = image.resize((int(width), int(height)), Image.BICUBIC) # Resize to the size of 0.8 from original picture
    image = np.array(image)
    image = np.swapaxes(image, 0, 1)
    # This code below supposed to replace each black color ([0,0,0]) to the color of [0,0,1]
    # r1,g1,b1 = [0,0,0] and r2,g2,b2 = [0,0,1]
    red, green, blue = image[:, :, 0], image[:, :, 1], image[:, :, 2]
    mask = (red == r1) & (green == g1) & (blue == b1)
    image[:, :, :3][mask] = [r2, g2, b2]
    return image

您是否注意到我可以做任何更改以使该功能更快?

编辑:我忘记提及的一些细节:

  1. 我的屏幕尺寸是1920*1080

  2. 此功能是我目前正在进行的直播项目的一部分。 Carlo 在下面建议的解决方案在这种情况下不合适,因为远程计算机不会与我们的计算机屏幕同步。

【问题讨论】:

  • 您可以将image = self.screen_capture.grab(self.monitor) 移动到另一个函数,将结果添加到程序的全局列表中。之后创建另一个函数,稍后详细说明图像并从列表中删除对象。您可以使用多线程来做到这一点。
  • OK tnx,我会试试看。有人对加快功能有其他想法吗?
  • 我已经用我的想法回答了这个问题

标签: python numpy python-imaging-library python-mss


【解决方案1】:

由于您的代码不完整,我只能猜测可能有什么帮助,所以这里有一些想法......

我从 1200x1200 的图像开始,因为我不知道你的图像有多大,然后因为你的代码中的注释将它缩小了 0.8 倍至 960x960。

我加快速度的想法是基于使用不同的插值方法,或者使用高度优化的 SIMD 代码的 OpenCV。两者之一或两者都合适,但由于我不知道您的图像是什么样的,所以只有您可以说。

所以,我们开始吧,首先使用 PIL resize() 和不同的插值方法:

# Open image with PIL
i = Image.open('start.png').convert('RGB')

In [91]: %timeit s = i.resize((960,960), Image.BICUBIC)                                             
16.2 ms ± 28 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [92]: %timeit s = i.resize((960,960), Image.BILINEAR)                                            
10.9 ms ± 87.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [93]: %timeit s = i.resize((960,960), Image.NEAREST)                                             
440 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

因此,BILINEAR 比 BICUBIC 快 1.5 倍,而这里真正的赢家是 NEAREST,速度快 32 倍。

现在,转换为 Numpy 数组(正如您所做的那样)并使用高度优化的 OpenCV SIMD 代码调整大小:

# Now make into Numpy array for OpenCV methods
n = np.array(i)

In [100]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_CUBIC)                     
806 µs ± 9.81 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [101]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_LINEAR)                    
3.69 ms ± 29 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [102]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_AREA)                      
12.3 ms ± 136 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [103]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_NEAREST)                   
692 µs ± 448 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

这里的获胜者看起来像 INTER_CUBIC,它比 PIL 的 resize() 快​​ 20 倍。

请全部尝试一下,看看哪种方法适合您!只需删除行首的 Python 魔法 %timeit 并运行剩下的内容。

【讨论】:

  • 感谢您的回答。 Image.NEAREST 插值方法是所有选项中最快的,但图像质量很差。 cv2.INTER_CUBIC 具有与 Image.BICUBIC 相同的质量,并且速度快 2 倍。所以我选择了 INTER_CUBIC,它在质量和时间方面总体上是最好的。我在大约 0.02 秒内减少了函数的时间。在直播中,每​​一毫秒都很重要 :) 你是否看到了我可以做出的更多改变?
  • 酷。由于您的代码仍然不完整,我不知道您的 screen_capture.grab() 来自哪里,所以我不知道如何优化它。似乎您正在拖动一个不必要的 alpha 通道,在该通道周围增加了 33% 的无用数据 - 也许您可以更快地丢弃它?
  • screen_capture 是一个 MSS 对象。 MSS 是我发现截屏速度最快的库。我不知道什么时候可以更快地丢弃 Alpha 通道。
  • 对不起,我根本不知道 MSS。
【解决方案2】:

这只是我的意思的一个例子。 如果它解决了问题,请告诉我。

您可以创建两个不同的线程。一个拍摄屏幕截图,另一个稍后详细说明屏幕。两者都将结果添加到列表中。 这提高了 get_screenshot 函数的速度。但要详细说明,您需要函数执行所需的时间。

import threading
#import your stuff

class your_class(object):
        def __init__(self):

                self.images = list()
                self.elaborated_images = list()

                threading.Thread(name="Take_Screen", target=self.get_screenshot, args=(width, height))
                threading.Thread(name="Elaborate_Screen", target=self.elaborate_screenshot)

        def get_screenshot(self, width, height):
                while True:
                        images.append(self.screen_capture.grab(self.monitor))

        def elaborate_screenshot(self):
                while True:
                        image = self.images[0]
                        image = Image.frombuffer('RGB', image.size, image.bgra, 'raw', 'BGRX')
                        image = image.resize((int(width), int(height)), Image.BICUBIC) # Resize to the size of 0.8 from original picture
                        image = np.array(image)
                        image = np.swapaxes(image, 0, 1)

                        # This code below supposed to replace each black color ([0,0,0]) to the color of [0,0,1]
                        # r1,g1,b1 = [0,0,0] and r2,g2,b2 = [0,0,1]

                        red, green, blue = image[:, :, 0], image[:, :, 1], image[:, :, 2]
                        mask = (red == r1) & (green == g1) & (blue == b1)
                        image[:, :, :3][mask] = [r2, g2, b2]

                        del self.images[0]

                        self.elaborated_images.append(image)

your_class()

因为我没有你的完整代码,所以我无法尽可能地构建它。

【讨论】:

  • 感谢您的代码,但我认为您的解决方案有问题。我添加的代码是我正在做的直播项目的一部分,如果我使用您的解决方案,远程计算机将不会与我们计算机的屏幕同步。在我原来的解决方案中,我得到一个屏幕截图,发送它,从远程计算机获得批准,然后继续下一个屏幕截图。
  • 干得好,先生!值得大家点赞。
  • @AdiKoch 也许将此细节添加到问题中。可能对其他人有用
猜你喜欢
  • 2017-12-07
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
  • 2020-05-02
相关资源
最近更新 更多