【问题标题】:Most efficient/quickest way to parse pixel data with Python?用 Python 解析像素数据的最有效/最快的方法?
【发布时间】:2014-06-02 13:22:32
【问题描述】:

我创建了一个简单的 Python 脚本,只要特定程序运行,它就会被激活。该程序将信息发送到屏幕,脚本需要抓取和分析这些信息。

脚本的部分逻辑可以表示如下:

while a certain condition is met:
    function to continuously check pixel information on a fixed area of the screen()
    if pixel data (e.g. RGB) changes:
        do something
    else:
        continues to check

我已经找到了可以做到这一点的东西,但没有我想要的那么快。 这是一个使用具有任意值的 Python Imaging Library (PIL) 的解决方案:

import ImageGrab

box = (0,0,100,100) # 100x100 screen area to capture (0x0 is top left corner)
pixel = (60,20) #target pixel coordenates (must be within the box's boundaries)
im = ImageGrab.grab(box) #grabs the image area (aka printscreen) -> source of bottleneck
hm = im.getpixel(pixel) # gets pixel information from the captured image in the form of an RGB value

然后我可以获取该 RGB 值并将其与函数获得的先前值进行比较。如果它发生了变化,那么屏幕上发生了一些事情,这意味着程序做了一些事情,因此脚本可以相应地运行。 然而,脚本需要快速反应,特别是因为这只是一个更大的函数的一部分,它有自己的复杂性和缺陷,所以我正在一点一点地优化代码,从这个。

此解决方案在 i7 4770k cpu 上将脚本限制为每秒约 30 次迭代。看起来很快,但是将它与其他功能一起添加,这些功能本身以类似的速度解析像素信息,并且事情开始累加。我的目标是在单个函数上每秒至少进行 200 次迭代,也许是 150 次迭代,以便最终脚本可以每秒运行 5-10 次迭代。

那么,长话短说:还有什么其他方法可以更快地解析屏幕上的像素?

【问题讨论】:

  • Python 不是这项工作的工具。实时图形处理需要更强大的语言,如 C++。即使您不受处理代码性能的限制,在大多数窗口系统上抓取屏幕的一部分也是一项缓慢的操作。
  • @Daniel Shaw:看看这个问题:stackoverflow.com/questions/1997678/…
  • @nightcracker 好吧,这只是......令人沮丧。肯定有一种方法可以至少更快一点。
  • @DanielShaw 不是来自 Python。您最终将调用特定于 C API 的操作系统代码来加速抓取屏幕的一部分,虽然这在 Python 中使用ctypes可能,但它基本上是伪装的 C。在 Python 中稍微缺乏一些好的库,但我认为主要原因是因为无论如何你都无法在 Python 中快速处理生成的屏幕抓取。
  • @BenjaminGolder 该解决方案的迭代速度为 48-52 次/秒。所以快了 60%,这是一个开始,但我一直在寻找至少一个数量级的差异(我这样不合理:()

标签: python image-processing python-imaging-library pixels


【解决方案1】:

好吧,偷看,经过一番挖掘,确实可以用 Python 和简单的 pywin32 模块(感谢 Mark Hammond)来做我想要的。没有必要使用“更强大”的语言或将工作外包给 numpy 之类的。 这是 5 行代码(6 行带有导入):

import win32ui
window_name = "Target Window Name" # use EnumerateWindow for a complete list
wd = win32ui.FindWindow(None, window_name)
dc = wd.GetWindowDC() # Get window handle
j = dc.GetPixel (60,20)  # as practical and intuitive as using PIL!
print j
dc.DeleteDC() # necessary to handle garbage collection, otherwise code starts to slow down over many iterations

就是这样。它将在每次迭代中返回所选像素的数字 (COLORREF),这是一种表示颜色的方式(就像 RGB 或十六进制一样),最重要的是,我可以解析的数据! 如果您不相信这里有一些我台式电脑上的基准测试(标准 Python 构建 CPython 和 i7 4770k):

我之前的解决方案围绕着一个虚拟秒表(您可以自己运行并检查一下):

    import ImageGrab, time
    box = (0,0,100,100) #100 x 100 square box to capture
    pixel = (60,20) #pixel coordinates (must be within the box's boundaries)
    t1 = time.time()
    count = 0
    while count < 1000:
        s = ImageGrab.grab(box) #grabs the image area
        h = s.getpixel(pixel) #gets pixel RGB value
        count += 1
    t2 = time.time()
    tf = t2-t1
    it_per_sec = int(count/tf)
    print (str(it_per_sec) + " iterations per second")

每秒获得 29 次迭代。让我们以此为基准进行比较。

这是 BenjaminGolder 使用 ctypes 指出的解决方案:

from ctypes import windll
import time
dc= windll.user32.GetDC(0)
count = 0
t1 = time.time()
while count < 1000:
    a= windll.gdi32.GetPixel(dc,x,y)
    count += 1
t2 = time.time()
tf = t2-t1
print int(count/tf)

平均每秒 54 次迭代。这是一个惊人的 86% 改进,但它不是我正在寻找的数量级改进。

所以,终于来了:

name = "Python 2.7.6 Shell" #just an example of a window I had open at the time
w = win32ui.FindWindow( None, name )
t1 = time.time()
count = 0
while count < 1000:
    dc = w.GetWindowDC()
    dc.GetPixel (60,20)
    dc.DeleteDC()
    count +=1
t2 = time.time()
tf = t2-t1
it_per_sec = int(count/tf)
print (str(it_per_sec) + " iterations per second")

像素渴脚本每秒大约迭代 16000 次。是的,16000。这比以前的解决方案快至少 2 个数量级,并且提高了 29600 %。 它是如此之快,以至于 count+=1 增量会减慢它的速度。 我对 100k 次迭代进行了一些测试,因为 1000 次对于这段代码来说太低了,平均保持大致相同,14-16k 次迭代/秒。它也在 7-8 秒内完成了这项工作,而之前的那些是在我开始写这篇文章时开始的,而且……他们还在继续。

好的,就是这样!希望这可以帮助任何有类似目标并面临类似问题的人。请记住,Python 有办法。

【讨论】:

  • 干得好!你为自己做出了最好的答案,浏览了大部分建议。
【解决方案2】:

实际上,您不应该尝试在 Python 循环中逐像素检查,如 cmets 中所述。 您可以尝试 Pypy - 使用适当的数据构造和 pypy,您可以使用纯 Python 代码和逐像素数据获得 10 倍的改进。

但是,通常的做法是让 Python 在本机代码中调用一个库来进行像素操作。 PIL 和 Numpy 就是这样的库 - 您应该做的而不是检查 Python 中每个像素的值,例如,让图像矩形区域彼此相减,以便获得具有不同像素的矩阵,并且然后根据需要使用 Numpy 处理这些差异。这会很快,而且您仍然可以使用 Python 来完成您需要的所有高级操作。

【讨论】:

  • 类似 im = ImageOps.grayscale(ImageGrab.grab(box)) ;一个=数组(im.getcolors()); a = a.sum() ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-05
  • 2010-10-11
  • 2013-09-01
  • 2020-08-27
  • 1970-01-01
  • 2012-05-28
  • 2016-06-06
相关资源
最近更新 更多