2010-01-04 26 views
8

Hiện tại tôi đang sử dụng trình đọc pixel qua AutoItv3 để thực hiện một số tác vụ trong chương trình đang chạy trực tiếp X; Một trò chơi. Ngay bây giờ chương trình hoạt động tốt nhưng như một bài tập tôi đã viết lại nó trong python. Ngay bây giờ tôi có thể làm:Phương pháp đọc pixel màn hình nhanh hơn bằng Python so với PIL?

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

Và đó lấy các thông tin điểm ảnh tôi muốn chỉ là tốt, nhưng tôi đang làm điều này khá nhanh chóng (cần phải được thực hiện 3x một giây hoặc nhanh hơn), nhưng kết quả là nó ảnh hưởng lớn đến tốc độ khung hình của trò chơi dựa trên DirectX này.

Có cách nào nhanh hơn trong Python để đọc pixel màn hình cụ thể không? Ngay cả hạn chế này để chạy mỗi 0,3 giây là gây ra căng thẳng hơn nó thực sự nên (Tôi thực sự figured python sẽ là nhanh hơn hơn AutoIt cho mục đích này, do đó lý do tôi đang thử nó)

Trả lời

12

Đây là nguồn màn hình lấy của PIL, Nó không chấp nhận bất kỳ tham số nào và nó lấy toàn bộ màn hình và chuyển đổi thành bitmap. Cách tiếp cận 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; 
} 

Vì vậy, khi tôi chỉ cần đi một chút sâu, tìm thấy là tốt

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

Và Python có ctypes, Vì vậy, đây là cách tiếp cận của tôi sử dụng 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 

Bạn có thể viết trình bao bọc cho hàm GetPixel như thế này

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

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

Sau đó, bạn có thể sử dụng như getpixel(0,0), getpixel(100,0), vv ...

PS: Mine là Windows 2000, vì vậy tôi đặt winnt trong đường dẫn, bạn có thể cần phải thay đổi nó để windows hoặc bạn chould loại bỏ hoàn toàn đường dẫn, chỉ cần sử dụng user32.dllgdi32.dll cũng sẽ hoạt động.

+0

Cảm ơn một bó. Màn hình tăng tốc này đọc NOTICEABLY. Tôi có thể đọc nhanh như tôi muốn bây giờ với hầu như không có sự chậm lại. ;) – ThantiK

+0

Tôi đã thử cú pháp trên cho ctypes và không thể làm cho nó hoạt động với LoadLibrary. Mã hàm của hàm bao bọc là chính xác những gì tôi đang tìm kiếm. – Octipi

+0

Tôi không thể tin rằng điều này thực sự hoạt động với thời gian ít nhất 30 lần trong giây. Tôi đã thử GetPixel và mất rất nhiều thời gian. –

3

Bạn có thể có thể làm điều đó thông qua SDL (?). Dựa trên this question, SDL có thể truy cập màn hình. Và nó có các ràng buộc python.

Có thể đáng để chụp? Nếu nó hoạt động, nó chắc chắn sẽ nhanh hơn việc chụp toàn màn hình trong PIL.

7

Nhận xét về giải pháp S.Mark của: thư viện user32 đã được nạp bởi windll vào windll.user32, vì vậy thay vì dc = ... dòng bạn có thể làm:

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

...hoặc tốt hơn là:

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

Cảm ơn, tôi đã cố gắng với 'cdll.user32.GetDC (0)' sáng nay, nó không hoạt động, vì vậy tôi nhập tệp dll theo cách thủ công. – YOU

1

Đó là một câu hỏi cũ, nhưng nó đứng khá cao trên Google khi tìm kiếm Python phương pháp màn hình lấy, vì vậy tôi nghĩ rằng nó có thể có ích kể rằng mô-đun ImageGrab bây giờ hỗ trợ lấy một khu vực của màn hình:

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

Mở rộng phạm vi một chút, hiện nay cũng là một thay thế cho ImageGrab gọi pyscreenshot, mà còn tiết kiệm một số phần của màn hình để một hoặc PIL/Gối hình ảnh. Mô-đun này cũng hoạt động trên Linux, không giống như ImageGrab chỉ là Windows và 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

Các vấn đề liên quan