Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中读取屏幕像素的方法比PIL更快?_Python_Windows_Winapi_Directx_Python Imaging Library - Fatal编程技术网

Python中读取屏幕像素的方法比PIL更快?

Python中读取屏幕像素的方法比PIL更快?,python,windows,winapi,directx,python-imaging-library,Python,Windows,Winapi,Directx,Python Imaging Library,目前,我通过AutoItv3使用像素阅读器在运行direct X的程序中执行一些操作;一场比赛。现在这个程序运行良好,但作为练习,我一直在用python重写它。现在我可以做: import ImageGrab # Part of PIL image = ImageGrab.grab() #Define an area to capture. rgb = image.getpixel((1, 90)) #What pixel do we want? 这很好地抓住了我想要的像素信

目前,我通过AutoItv3使用像素阅读器在运行direct X的程序中执行一些操作;一场比赛。现在这个程序运行良好,但作为练习,我一直在用python重写它。现在我可以做:

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更快,因此我尝试它的原因)

您可能可以通过SDL(?)来完成。基于,SDL可以访问屏幕。它有python绑定


也许值得一试?如果它能工作的话,它肯定比在PIL中进行全屏捕获要快

这是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方法很好

Python有ctypes,所以下面是我使用ctypes的方法(在Windows 10中,
winnt
已被
Windows
取代):

您可以像这样为函数GetPixel编写一个包装器

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
,或者您可以选择完全删除路径,只需使用
user32.dll
gdi32.dll
就可以了。

对S.Mark解决方案的评论:user32库已经由Windell加载到Windell.user32中,所以不是dc=。。。您可以执行以下操作:

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

dc= windll.user32.GetDC(0)

这是一个老生常谈的问题,但在搜索Python屏幕抓取方法时,它在Google上的排名相当高,因此我认为提到ImageGrab模块现在支持抓取屏幕的一个区域可能会很有用:

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

稍微扩展一下范围,现在还有一个叫做pyscreenshot的ImageGrab的替代品,它还将屏幕的某些部分保存到一个或PIL/枕头图像中。这个模块也可以在Linux上工作,不像ImageGrab,它只是Windows和OSX

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

作为@YOU答案的补充,此函数以元组形式返回给定像素的RGB值。我将输出与
PIL.ImageGrab
进行了比较,结果很好:

从ctypes导入Windell
dc=windell.user32.GetDC(0)
def getpixel(x,y):
返回元组(int.to_字节(windell.gdi32.GetPixel(dc,x,y),3,“little”))

请注意,此代码没有错误处理。

谢谢,我今天早上尝试使用
cdll.user32.GetDC(0)
,但它不起作用,所以我手动导入dll文件。非常感谢。这明显加快了屏幕阅读速度。我现在可以想读多快就读多快,而且几乎没有减速现象我尝试了上述ctypes语法,但无法使用LoadLibrary。包装器函数代码正是我想要的。我不敢相信它在一秒钟内至少能工作30次。我试过GetPixel,但花了很多时间。请注意这个答案。
import pyscreenshot as ImageGrab
im=ImageGrab.grab(bbox=(10,10,510,510)) # X1,Y1,X2,Y2
im.show()