2010-01-04 29 views
8

Actualmente estoy usando un lector de píxeles a través de AutoItv3 para realizar algunas acciones en un programa que ejecuta X directa; Un juego. En este momento el programa funciona bien, pero como ejercicio lo he estado reescribiendo en python. En este momento lo que puedo hacer:Método más rápido para leer píxeles de pantalla en Python que PIL?

import ImageGrab # Part of PIL 
    image = ImageGrab.grab() #Define an area to capture. 
    rgb = image.getpixel((1, 90)) #What pixel do we want? 

Y que agarra la información de píxeles quiero bien, pero estoy haciendo esto con bastante rapidez (que hay que hacer 3 veces por segundo o más rápido), pero el resultado es que se afecta principalmente a la velocidad de fotogramas de este juego basado en DirectX.

¿Hay una forma más rápida en Python de leer un píxel de pantalla específico? Incluso limitar este a ejecutar cada 0.3 segundos está causando más tensión de lo que debería (en realidad me di cuenta de que Python sería más rápido que AutoIt para este propósito en particular, de ahí la razón por la que lo estoy intentando)

Respuesta

12

Esta es la fuente de captura de pantalla del PIL, no acepta ningún parámetro, y capta toda la pantalla y la convierte a mapa de bits. enfoque C

PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) 
{ 
    int width, height; 
    HBITMAP bitmap; 
    BITMAPCOREHEADER core; 
    HDC screen, screen_copy; 
    PyObject* buffer; 

    /* step 1: create a memory DC large enough to hold the 
     entire screen */ 

    screen = CreateDC(";DISPLAY", NULL, NULL, NULL); 
    screen_copy = CreateCompatibleDC(screen); 

    width = GetDeviceCaps(screen, HORZRES); 
    height = GetDeviceCaps(screen, VERTRES); 

    bitmap = CreateCompatibleBitmap(screen, width, height); 
    if (!bitmap) 
     goto error; 

    if (!SelectObject(screen_copy, bitmap)) 
     goto error; 

    /* step 2: copy bits into memory DC bitmap */ 

    if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, SRCCOPY)) 
     goto error; 

    /* step 3: extract bits from bitmap */ 

    buffer = PyString_FromStringAndSize(NULL, height * ((width*3 + 3) & -4)); 
    if (!buffer) 
     return NULL; 

    core.bcSize = sizeof(core); 
    core.bcWidth = width; 
    core.bcHeight = height; 
    core.bcPlanes = 1; 
    core.bcBitCount = 24; 
    if (!GetDIBits(screen_copy, bitmap, 0, height, PyString_AS_STRING(buffer), 
        (BITMAPINFO*) &core, DIB_RGB_COLORS)) 
     goto error; 

    DeleteObject(bitmap); 
    DeleteDC(screen_copy); 
    DeleteDC(screen); 

    return Py_BuildValue("(ii)N", width, height, buffer); 

error: 
    PyErr_SetString(PyExc_IOError, "screen grab failed"); 

    DeleteDC(screen_copy); 
    DeleteDC(screen); 

    return NULL; 
} 

Así, cuando acabo de ir un poco más profundo, encontré que es bueno

http://msdn.microsoft.com/en-us/library/dd144909(VS.85).aspx

y Python tienen ctypes, Así que aquí es mi enfoque utilizando ctypes

>>> from ctypes import * 
>>> user= windll.LoadLibrary("c:\\winnt\\system32\\user32.dll") #I am in windows 2000, may be yours will be windows 
>>> h = user.GetDC(0) 
>>> gdi= windll.LoadLibrary("c:\\winnt\\system32\\gdi32.dll") 
>>> gdi.GetPixel(h,1023,767) 
16777215 #I believe its white color of RGB or BGR value, #FFFFFF (according to msdn it should be RGB) 
>>> gdi.GetPixel(h,1024,767) 
-1 #because my screen is only 1024x768 

Puede escribir un contenedor para la función GetPixel así

from ctypes import windll 
dc= windll.user32.GetDC(0) 

def getpixel(x,y): 
    return windll.gdi32.GetPixel(dc,x,y) 

entonces usted puede utilizar como getpixel(0,0), getpixel(100,0), etc ...

PS: El mío es Windows 2000, por lo que poner winnt en el camino, puede que tenga que cambiarlo a windows o eliminar por completo chould ruta, solo usando user32.dll y gdi32.dll debería funcionar también.

+0

gracias un montón. Esta lectura acelerada de la pantalla AVISO. Puedo leer tan rápido como quiero ahora sin prácticamente ninguna ralentización. ;) – ThantiK

+0

Intenté la sintaxis anterior para ctypes y no pude hacerlo funcionar con LoadLibrary. El código de la función de envoltura es exactamente lo que estaba buscando. – Octipi

+0

No puedo creer que esto realmente funcione con el tiempo al menos 30 veces por segundo. Probé GetPixel y me tomó muchísimo tiempo. –

3

Puede ser capaz de hacerlo a través de SDL (?). Basado en this question, SDL puede acceder a la pantalla. Y tiene enlaces de pitón.

¿Valdría la pena intentarlo? Si funcionó, sin duda sería más rápido que hacer una captura de pantalla completa en PIL.

7

comentario en la solución de S.Mark: user32 biblioteca ya está cargado por WINDLL en windll.user32, así que en vez de la línea de corriente continua = ... se puede hacer:

def getpixel(x,y): 
    return gdi.GetPixel(windll.user32.GetDC(0),x,y) 

...o preferiblemente:

dc= windll.user32.GetDC(0) 
+0

Gracias, estaba intentando con 'cdll.user32.GetDC (0)' esta mañana, no funcionaba, así que importé archivos dll manualmente. – YOU

1

Es una vieja pregunta, pero ocupa muy altamente en Google en la búsqueda de métodos de captura de pantalla de Python, así que creo que podría ser útil mencionar que el módulo ImageGrab ahora es compatible con el acaparamiento de una región de la pantalla:

PIL.ImageGrab.grab(bbox=None) 
Parameters: bbox – What region to copy. Default is the entire screen. 
Returns: An image 

http://pillow.readthedocs.io/en/3.1.x/reference/ImageGrab.html

la ampliación del alcance ligeramente, ahora también existe un reemplazo para ImageGrab llamada pyscreenshot, que también ahorra una parte de la pantalla a una imagen o PIL/almohada. Este módulo también funciona en Linux, a diferencia de ImageGrab, que es solo Windows y OS X.

import pyscreenshot as ImageGrab 
im=ImageGrab.grab(bbox=(10,10,510,510)) # X1,Y1,X2,Y2 
im.show() 

https://pypi.python.org/pypi/pyscreenshot

Cuestiones relacionadas