为什么我的 Python PyAutoGUI / Image grab 脚本很慢?
Why is my Python PyAutoGUI / Image grab script slow?
所以我正在制作一个自动答题器,我想问一下我可以做些什么来优化我的代码以使其更快,它目前正在工作但是它识别像素颜色然后点击太慢。我试过使用 space 它似乎是一样的。
如果它只是一个像素而不是一个盒子,会不会更快?另外,有没有 a.sum() 的替代方法,而不是计算总数,使用原始值会更快吗?
该项目的目的是在屏幕上找到一个pixel/box,当颜色值变为某种颜色(白色或灰色范围)时,它会点击一个按钮。
from pyautogui import *
from PIL import ImageGrab
from PIL import ImageOps
import pyautogui
import time
import keyboard
import random
from numpy import*
import os
emark = (1440, 1026)
pyautogui.click(x=1063, y=544)
def image_grab():
box = (emark[0]+1,emark[1],emark[0]+11,emark[1]+2)
image = ImageGrab.grab(box)
grayImage = ImageOps.grayscale(image)
a = array(grayImage.getcolors())
return a.sum()
def clickButton():
#pyautogui.press('space')
pyautogui.click(x=1160, y=600)
while True:
image_grab()
if 1000 <= image_grab() <= 1700:
clickButton()
您不必进行所有颜色转换和 numpy 数组操作。只需抓取一个像素,保存它的颜色值,然后抓取一个新像素,并测试是否相等。
#! /usr/bin/env python3
from PIL import ImageGrab
import pyautogui
emark = ( 1440, 1026 )
pyautogui .click( x=1063, y=544 )
pixel = ( emark[0]-1, emark[1]-1, emark[0]+1, emark[1]+1 )
original_pixel_color = ImageGrab .grab( pixel ) .getpixel( (0,0) )
## print( original_pixel_color )
def image_grab():
new_pixel_color = ImageGrab .grab( pixel ) .getpixel( (0,0) )
return new_pixel_color == original_pixel_color
def clickButton():
## print('yup')
pyautogui .click( x=1160, y=600 )
while True:
if not image_grab():
clickButton()
## I don't know if pyautogui has a built in pause function,
## or repeat delay, but you probably want a time.sleep(1)
## or similar here, so it doesn't rapid-fire autoclick
## 500 times in a row, once that pixel changes color.
您的脚本不需要,但我认为 getpixel()
是那些 ImageGrab 方法中最快的 return 可比颜色数据。
import timeit
loops = 99999
print( 'tobytes()', timeit .timeit( lambda : original_pixel_color .tobytes(), number=loops ) )
print( 'getcolors()', timeit .timeit( lambda : original_pixel_color .getcolors(), number=loops ) )
print( 'getpixel()', timeit .timeit( lambda : original_pixel_color .getpixel( (0,0) ), number=loops ) )
编辑: ImageGrab .grab( pixel )
抓取一个屏幕矩形,根据你给它的大小。
( emark[0] -1, emark[1] -1, emark[0] +1, emark[1] +1 ) ## x, y, w, h
由于PIL的限制,最小坐标space为2x2,因此 emark -1, emark +1
.
我认为是的,但我在这里继续回忆多年前的事情。他们可能会在 SO 某处说出最终像素位置,但您可以尝试
( emark[0], emark[1], emark[0] +2, emark[1] +2 )
如果减一。无论哪种方式,它实际上只抓取一个像素。
.getpixel( (0,0) )
return作为元组的那个像素的 RGB 值。
所以是的,而不是捕捉一个像素开始,你可以在那里使用你自己的值。
original_pixel_color = ( 240, 240, 240 )
def brighter_than():
new_pixel_color = ImageGrab .grab( pixel ) .getpixel( (0,0) )
r = new_pixel_color[0] >= original_pixel_color[0]
g = new_pixel_color[1] >= original_pixel_color[1]
b = new_pixel_color[2] >= original_pixel_color[2]
return r and g and b
while True:
if not brighter_than():
clickButton()
此外,在开始时,导入后:
Fail-Safes
Set up a 2.5 second pause after each PyAutoGUI call:
import pyautogui
pyautogui .PAUSE = 2.5
https://pyautogui.readthedocs.io/en/latest/quickstart.html
Post 后记: 好吧,在某些情况下,它们是 saving/displaying 图像,而不仅仅是获取颜色,所以很多可以剥离出来。使用 sp
是因为当时更容易理解,而且对我正在做的事情来说足够快。
import subprocess as sp
xx, yy = 640, 480
ww, hh = 1, 1
pixel_location = f'{xx},{yy},{ww},{hh}'
cmd = [ 'scrot', 'temp.tiff', '--autoselect', pixel_location, '--silent' ]
sp .Popen( cmd )
我知道 scrot 是一个 Linux 命令,但它与 OSX 中的屏幕截图类似,您只需要将 args 调整为它所期望的。我之所以这么说是因为它对我有用,但是调用额外的 BASH 进程会产生少量开销和几毫秒的延迟,因此您可能希望 直接调用库相反...
认为 答案 是在 Tiger 的回应中,n4p 去掉了很多,所以这是有希望的;但是那些函数调用也不在我的知识范围之内,所以在这一点上做出有根据的猜测。在这里没有办法测试它,所以你必须尝试看看。尽可能尝试跳过 numpy 和 pillow,通过使用快速结构解包 - 这应该是最快的。
import struct
import pyautogui
import Quartz.CoreGraphics as CG
pyautogui .PAUSE = 2.5
xx, yy = 500, 500
ww, hh = 1, 1
region = CG .CGRectMake( xx, yy, ww, hh )
orig = ( 240, 240, 240 )
def brighter_than(): ## think these properties are right...
pixel = CG .CGWindowListCreateImage( region, CG .kCGWindowListOptionOnScreenOnly, CG .kCGNullWindowID, CG .kCGWindowImageDefault )
data = CG .CGDataProviderCopyData( CG .CGImageGetDataProvider( pixel ) )
## backwards from RGBA, Mac stores pixels as BGRA instead
b, g, r, a = struct .unpack_from( 'BBBB', data ) ## BBBB = 4 Bytes
return r >= orig[0] and g >= orig[1] and b >= orig[2]
while True:
if brighter_than():
clickButton()
其中一些信息来自 dbr 的 blog writeup . And CGRectMake()
comes from Converting CGImage to python image (pil/opencv)。其他人都在使用 region = CG.CGRectInfinite
,这可能会占用 整个屏幕 而不是一个像素。
所以我正在制作一个自动答题器,我想问一下我可以做些什么来优化我的代码以使其更快,它目前正在工作但是它识别像素颜色然后点击太慢。我试过使用 space 它似乎是一样的。
如果它只是一个像素而不是一个盒子,会不会更快?另外,有没有 a.sum() 的替代方法,而不是计算总数,使用原始值会更快吗?
该项目的目的是在屏幕上找到一个pixel/box,当颜色值变为某种颜色(白色或灰色范围)时,它会点击一个按钮。
from pyautogui import *
from PIL import ImageGrab
from PIL import ImageOps
import pyautogui
import time
import keyboard
import random
from numpy import*
import os
emark = (1440, 1026)
pyautogui.click(x=1063, y=544)
def image_grab():
box = (emark[0]+1,emark[1],emark[0]+11,emark[1]+2)
image = ImageGrab.grab(box)
grayImage = ImageOps.grayscale(image)
a = array(grayImage.getcolors())
return a.sum()
def clickButton():
#pyautogui.press('space')
pyautogui.click(x=1160, y=600)
while True:
image_grab()
if 1000 <= image_grab() <= 1700:
clickButton()
您不必进行所有颜色转换和 numpy 数组操作。只需抓取一个像素,保存它的颜色值,然后抓取一个新像素,并测试是否相等。
#! /usr/bin/env python3
from PIL import ImageGrab
import pyautogui
emark = ( 1440, 1026 )
pyautogui .click( x=1063, y=544 )
pixel = ( emark[0]-1, emark[1]-1, emark[0]+1, emark[1]+1 )
original_pixel_color = ImageGrab .grab( pixel ) .getpixel( (0,0) )
## print( original_pixel_color )
def image_grab():
new_pixel_color = ImageGrab .grab( pixel ) .getpixel( (0,0) )
return new_pixel_color == original_pixel_color
def clickButton():
## print('yup')
pyautogui .click( x=1160, y=600 )
while True:
if not image_grab():
clickButton()
## I don't know if pyautogui has a built in pause function,
## or repeat delay, but you probably want a time.sleep(1)
## or similar here, so it doesn't rapid-fire autoclick
## 500 times in a row, once that pixel changes color.
您的脚本不需要,但我认为 getpixel()
是那些 ImageGrab 方法中最快的 return 可比颜色数据。
import timeit
loops = 99999
print( 'tobytes()', timeit .timeit( lambda : original_pixel_color .tobytes(), number=loops ) )
print( 'getcolors()', timeit .timeit( lambda : original_pixel_color .getcolors(), number=loops ) )
print( 'getpixel()', timeit .timeit( lambda : original_pixel_color .getpixel( (0,0) ), number=loops ) )
编辑: ImageGrab .grab( pixel )
抓取一个屏幕矩形,根据你给它的大小。
( emark[0] -1, emark[1] -1, emark[0] +1, emark[1] +1 ) ## x, y, w, h
由于PIL的限制,最小坐标space为2x2,因此 emark -1, emark +1
.
我认为是的,但我在这里继续回忆多年前的事情。他们可能会在 SO 某处说出最终像素位置,但您可以尝试
( emark[0], emark[1], emark[0] +2, emark[1] +2 )
如果减一。无论哪种方式,它实际上只抓取一个像素。
.getpixel( (0,0) )
return作为元组的那个像素的 RGB 值。
所以是的,而不是捕捉一个像素开始,你可以在那里使用你自己的值。
original_pixel_color = ( 240, 240, 240 )
def brighter_than():
new_pixel_color = ImageGrab .grab( pixel ) .getpixel( (0,0) )
r = new_pixel_color[0] >= original_pixel_color[0]
g = new_pixel_color[1] >= original_pixel_color[1]
b = new_pixel_color[2] >= original_pixel_color[2]
return r and g and b
while True:
if not brighter_than():
clickButton()
此外,在开始时,导入后:
Fail-Safes
Set up a 2.5 second pause after each PyAutoGUI call:
import pyautogui
pyautogui .PAUSE = 2.5
https://pyautogui.readthedocs.io/en/latest/quickstart.html
Post 后记: 好吧,在某些情况下,它们是 saving/displaying 图像,而不仅仅是获取颜色,所以很多可以剥离出来。使用 sp
是因为当时更容易理解,而且对我正在做的事情来说足够快。
import subprocess as sp
xx, yy = 640, 480
ww, hh = 1, 1
pixel_location = f'{xx},{yy},{ww},{hh}'
cmd = [ 'scrot', 'temp.tiff', '--autoselect', pixel_location, '--silent' ]
sp .Popen( cmd )
我知道 scrot 是一个 Linux 命令,但它与 OSX 中的屏幕截图类似,您只需要将 args 调整为它所期望的。我之所以这么说是因为它对我有用,但是调用额外的 BASH 进程会产生少量开销和几毫秒的延迟,因此您可能希望 直接调用库相反...
认为 答案 是在 Tiger 的回应中,n4p 去掉了很多,所以这是有希望的;但是那些函数调用也不在我的知识范围之内,所以在这一点上做出有根据的猜测。在这里没有办法测试它,所以你必须尝试看看。尽可能尝试跳过 numpy 和 pillow,通过使用快速结构解包 - 这应该是最快的。
import struct
import pyautogui
import Quartz.CoreGraphics as CG
pyautogui .PAUSE = 2.5
xx, yy = 500, 500
ww, hh = 1, 1
region = CG .CGRectMake( xx, yy, ww, hh )
orig = ( 240, 240, 240 )
def brighter_than(): ## think these properties are right...
pixel = CG .CGWindowListCreateImage( region, CG .kCGWindowListOptionOnScreenOnly, CG .kCGNullWindowID, CG .kCGWindowImageDefault )
data = CG .CGDataProviderCopyData( CG .CGImageGetDataProvider( pixel ) )
## backwards from RGBA, Mac stores pixels as BGRA instead
b, g, r, a = struct .unpack_from( 'BBBB', data ) ## BBBB = 4 Bytes
return r >= orig[0] and g >= orig[1] and b >= orig[2]
while True:
if brighter_than():
clickButton()
其中一些信息来自 dbr 的 blog writeup . And CGRectMake()
comes from Converting CGImage to python image (pil/opencv)。其他人都在使用 region = CG.CGRectInfinite
,这可能会占用 整个屏幕 而不是一个像素。