使用Python解析像素数据的最有效/最快的方法?

时间:2014-04-18 04:11:50

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

我创建了一个简单的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次迭代运行。

所以,长话短说:还有什么方法可以更快速地从屏幕上解析像素?

2 个答案:

答案 0 :(得分:4)

好的偷看,经过一些挖掘后,确实可以用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%。 它的速度非常快,计数+ = 1的增量会减慢它的速度。 我对100k迭代进行了一些测试,因为1000对于这段代码来说太低了,平均值保持大致相同,每秒14-16k迭代。它也在7-8秒内完成了这项工作,而之前的工作是在我开始写这篇文章时开始的......好吧他们还在继续。

好吧,就是这样!希望这可以帮助任何有类似目标并遇到类似问题的人。请记住,Python找到了一种方法。

答案 1 :(得分:2)

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

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

相关问题