获取像素颜色的最快方式是什么?

9
我正在尝试为Windows应用程序制作自动点击器。它可以正常工作,但是速度非常慢!我目前使用的方法是“getPixel”,每次调用它都会重新加载一个数组。
以下是我目前的代码:
hdc = GetDC(HWND_DESKTOP);
bx = GetSystemMetrics(SM_CXSCREEN);
by = GetSystemMetrics(SM_CYSCREEN);
start_bx = (bx/2) - (MAX_WIDTH/2);
start_by = (by/2) - (MAX_HEIGHT/2);
end_bx = (bx/2) + (MAX_WIDTH/2);
end_by = (by/2) + (MAX_HEIGHT/2);

for(y=start_by; y<end_by; y+=10)
{   
    for(x=start_bx; x<end_bx; x+=10)
    {
        pixel = GetPixel(*hdc, x, y);
        if(pixel==RGB(255, 0, 0))
        {
            SetCursorPos(x,y);
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            Sleep(50);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
            Sleep(25);
        }
    }
}

基本上,它只是在屏幕上扫描一系列像素,并在检测到红色按钮时开始鼠标事件。
我知道还有其他获取像素颜色的方法,例如bitblt。但我做了一些研究,不明白应该如何扫描颜色数组。我需要一些能够快速扫描屏幕以捕捉按钮的东西。
请问你能帮助我吗?
谢谢。

当您的代码被调用时?在空闲时间?用户移动鼠标时?有不同的方法来检测红色按钮,例如使用FindWindow()函数。 - Adriano Repetti
顺便说一下,我的代码陷入了无限循环。但我想使用一个颜色捕捉器而不是偏移量。因此,当我启动我的代码时,它只会进入循环并检查红色按钮。 - Manitoba
2
如果这段代码陷入了无限循环,那么问题并不在于 GetPixel。而是因为你的应用程序没有留出 CPU 时间给其他应用程序。 - tenfour
顺便说一下,我的所有代码都在一个线程中。正如我所说,使用 getPixel 函数它可以很好地运行。我只是在寻找更好的方法来处理它。 - Manitoba
它的运行效果不好--你说它很慢。在没有性能分析数据的情况下,我给出了最可能的原因是它很慢。 - tenfour
2个回答

15

我找到了一个完美的方法,明显比 GetPixel 更快:

HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;

while(true)
{
    hdc = GetDC(HWND_DESKTOP);
    GetWindowRect(hWND_Desktop, &rect);
            int MAX_WIDTH = rect.right;
        int MAX_HEIGHT = rect.bottom;

    hdcTemp = CreateCompatibleDC(hdc);
    BITMAPINFO bitmap;
    bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
    bitmap.bmiHeader.biWidth = MAX_WIDTH;
    bitmap.bmiHeader.biHeight = MAX_HEIGHT;
    bitmap.bmiHeader.biPlanes = 1;
    bitmap.bmiHeader.biBitCount = 32;
    bitmap.bmiHeader.biCompression = BI_RGB;
    bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
    bitmap.bmiHeader.biClrUsed = 0;
    bitmap.bmiHeader.biClrImportant = 0;
    HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
    SelectObject(hdcTemp, hBitmap2);
    BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);

    for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
    {
        red = (int)bitPointer[i];
        green = (int)bitPointer[i+1];
        blue = (int)bitPointer[i+2];
        alpha = (int)bitPointer[i+3];

        x = i / (4 * MAX_HEIGHT);
        y = i / (4 * MAX_WIDTH);

        if (red == 255 && green == 0 && blue == 0)
        {
            SetCursorPos(x,y);
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            Sleep(50);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
            Sleep(25);
        }
    }
}

我希望这能帮助其他人。


这是在检查每个像素吗?我不理解你的x和y赋值。 - user1397417
XY是正在查看的像素的位置。除非您想对此像素执行操作,否则它们是无用的。如果其颜色为纯红色,则会将鼠标设置为此像素并执行左键单击操作。 - Manitoba
1
干得不错,但有一些我不明白的地方。为什么要考虑像素的Alpha分量所分配的字节?因为作为截图,Alpha的值总是255,对吧?那么,将biBitCount参数设置为24是否更好呢? - Delgan
你是完全正确的。Alpha部分是无用的。 - Manitoba
为什么必须将biSizeImage乘以4? - Scdev
显示剩余3条评论

0
简单的答案是,如果这是你坚持使用的方法,那么没有太多优化的空间。正如其他人在评论中指出的,您应该使用不同的方法来定位要点击的区域。例如,请尝试使用FindWindow。
如果您不想更改自己的方法,那么至少在每次完整屏幕扫描后使线程休眠一小段时间。

你好,能告诉我更多关于FindWindow的信息吗?我是这样使用它的:mhwnd = FindWindow(NULL, "应用程序窗口名称"); hdc = GetDC(mhwnd); 接下来该怎么做? - Manitoba
1
你想点击的按钮可能有自己的窗口句柄。尝试使用诸如Spy++之类的窗口检查程序查找您想要点击的窗口句柄,然后您可以使用一些聪明的代码定位到那个特定的项目。 - korona

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接