2010-01-04 80 views
8

目前我正在使用像素讀取器通過AutoItv3在運行直接X的程序中執行某些操作;一個遊戲。現在的程序運行良好,但作爲一個練習,我一直在Python中重寫它。現在我可以這樣做:在Python中閱讀屏幕像素比PIL更快的方法?

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

而且,抓住像素信息,我想就好了,但我相當迅速地這樣做(需要做3倍第二或更快),但結果是,它主要影響這個基於DirectX的遊戲的幀率。

Python中有更快的方式來讀取特定的屏幕像素嗎?即使限制這一個運行每0.3秒導致更多的應變比它真的應該(我實際上認爲python將爲比AutoIt更快爲此特定目的,因此我試圖它的原因)

回答

12

這是PIL的grabscreen源碼,它不接受任何參數,並且抓取整個屏幕並將其轉換爲位圖。

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

所以,當我剛去略深,測得C的做法是好的

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

和Python有ctypes的,所以這裏是一個使用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 

我的方法你可以寫一個函數Ge​​tPixel的包裝,像這樣

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

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

然後你可以使用像getpixel(0,0)getpixel(100,0),等...

PS:我的是Windows 2000的,所以我把winnt的路徑,則可能需要將其更改爲windows,或者你chould完全刪除路徑,只需使用user32.dllgdi32.dll即可。在S.Mark的解決方案

+0

感謝一大堆。這加快了屏幕閱讀NOTICEABLY。我現在可以像現在想的那樣快速閱讀,而且幾乎沒有減速。 ;) – ThantiK 2010-01-13 22:14:52

+0

我嘗試了ctypes的上述語法,無法讓它與LoadLibrary一起工作。包裝函數代碼正是我所期待的。 – Octipi 2013-07-30 09:08:46

+0

我不能相信這個實際上至少在第二次的時間裏有30次。我試過GetPixel,花了很多時間。 – 2016-01-19 08:26:26

3

您可能會能夠通過SDL(?)完成。根據this question,SDL可以訪問屏幕。它有python綁定。

可能值得一試?如果它工作,它肯定會比在PIL中進行全屏捕捉更快。

7

評論:USER32庫已經被加載由WINDLL到windll.user32,所以代替DC = ...行,你可以這樣做:

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

...或較好的是:

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

謝謝,我今天早上嘗試用'cdll.user32.GetDC(0)',它沒有工作,所以我手動導入dll文件。 – YOU 2010-01-04 11:14:58

1

這是一個老問題,但對於Python的屏幕抓取方法搜索時相當高排名在谷歌,所以我認爲這可能是有用的一提的是ImageGrab模塊現在支持抓取的區域屏幕:

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

略微擴大範圍,現在也被稱爲ImageGrab一個pyscreenshot更換,這也保存到一個或PIL /枕頭圖像屏幕的某些部分。這個模塊也適用於Linux,不像ImageGrab,它只是Windows和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