2010-01-04 10 views
8

Attualmente sto usando un lettore di pixel tramite AutoItv3 per eseguire alcune azioni in un programma che esegue X diretta; Un gioco. In questo momento il programma funziona bene, ma come esercizio lo stavo riscrivendo in python. In questo momento non posso fare:Metodo più veloce di lettura di pixel dello schermo in Python rispetto a PIL?

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

E che afferra le informazioni dei pixel voglio bene, ma sto facendo questo abbastanza rapidamente (che deve essere fatto 3x un secondo o più veloce), ma il risultato è che è influisce notevolmente sul framerate di questo gioco basato su DirectX.

C'è un modo più veloce in Python per leggere uno specifico pixel dello schermo? Anche limitando questo per l'esecuzione ogni 0,3 secondi sta causando più sforzo di quello che in realtà dovrebbe (in realtà ho capito pitone sarebbe più veloce di AutoIt per questo scopo particolare, quindi il motivo che sto cercando esso)

risposta

12

Questa è la sorgente di grabscreen del PIL, la quale non accetta alcun parametro, e cattura l'intero schermo e lo converte in bitmap.

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; 
} 

Così, quando mi basta andare un po 'in profondità, trovato l'approccio C è buona

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

e Python hanno ctypes ecco il mio approccio con 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 

Potresti scrivere un wrapper per la funzione GetPixel come questo

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

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

quindi è possibile utilizzare come getpixel(0,0), getpixel(100,0), ecc ...

PS: Il mio è Windows 2000, così ho messo winnt nel percorso, potrebbe essere necessario cambiarlo windows o chould completamente rimuovere percorso, solo utilizzando user32.dll e gdi32.dll dovrebbe funzionare anche.

+0

Thanks a Bunch. Questo ha velocizzato la lettura dello schermo NOTICEABLY. Riesco a leggere il più velocemente possibile senza praticamente nessun rallentamento. ;) – ThantiK

+0

Ho provato la sintassi sopra per i ctypes e non ho potuto farlo funzionare con LoadLibrary. Il codice della funzione wrapper è esattamente quello che stavo cercando. – Octipi

+0

Non posso credere che questo funzioni effettivamente con il tempo almeno 30 volte al secondo. Ho provato GetPixel e ci ho messo un sacco di tempo. –

3

Si potrebbe essere in grado di farlo tramite SDL (?). Basato su this question, SDL può accedere allo schermo. E ha binding Python.

Potrebbe valere la pena provare? Se funzionasse sarebbe sicuramente più veloce di fare una cattura a schermo intero in PIL.

7

Commenta La soluzione di S.Marco: biblioteca user32 è già caricato da windll in windll.user32, così invece del dc = ... linea che si può fare:

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

...o, preferibilmente:

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

Grazie, stavo cercando 'cdll.user32.GetDC (0)' stamattina, non funzionava, quindi importando manualmente i file dll. – YOU

1

Si tratta di una vecchia questione, ma si colloca piuttosto altamente su Google per la ricerca di Python metodi Screen Grab, quindi penso che potrebbe essere utile ricordare che il modulo ImageGrab supporta ora afferrando una regione del schermo:

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

espansione un po 'il campo di applicazione, v'è ora anche un sostituto per ImageGrab chiamato pyscreenshot, che salva anche una parte dello schermo per immagini PIL/cuscino a o. Questo modulo funziona anche su Linux, diversamente da ImageGrab che è solo Windows e 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

Problemi correlati