【问题标题】:GetPixel is WAY too slowGetPixel 太慢了
【发布时间】:2016-01-08 02:00:39
【问题描述】:

我有一堆方格,每个方格都有我需要识别的特定身份/符号。到目前为止,我有类似的东西:

#include <iostream>
#include <windows.h>

using namespace std;

int main() {
    HDC dc = GetDC(0);
    COLORREF color;
    int sum, x, y;

    while (true) {
        sum = 0;
        Sleep(100);
        for (x = 512; x < 521; x++) {
            for (y = 550; y < 565; y++) {
                color = GetPixel(dc, x, y);
                sum = GetRValue(color) + GetBValue(color) + GetGValue(color);
            }
        }
        cout << "SUM: " << sum << endl;
    }

    return 0;
}

显然到目前为止它只扫描了一个块。问题在于,即使它只有 100 多像素,它也需要非常长的时间。我什至无法想象会发生什么。每次重复需要一秒多,也许两秒。我能做些什么?必须有一种更快的方法来做到这一点。如果我不能查询单个像素,是否有办法获取屏幕区域?该区域不在我的程序窗口内。

【问题讨论】:

  • Blit 到一个DIB,然后直接读取内存。
  • 比你总是知道我的问题的答案更令人惊奇的是,你似乎总是第一个回答,我想我现在已经失去了三四次了。再次感谢
  • 在重新编写所有代码之前,您可能只想尝试交换哪个循环是内部的,哪个是外部的。以非顺序(内存)顺序访问像素会对缓存产生相当严重的影响。
  • 是的,获取和设置像素功能很慢。这是因为信息的顺序。基本上:获取和设置像素仅在您需要启用彩色鼠标(或您正在处理的任何事情)时才有用
  • 只输出最后一次迭代中sum 的值,因为每次迭代都会覆盖它。你的意思是sum += GetRValue...

标签: c++ winapi pixels


【解决方案1】:

Jonathan cmets 在问题中使用 DIB,但没有显示如何使用的答案。为了完整起见,这里是Mister B's code

COLORREF getcolor(POINT pt) {
    HDC hDc = GetDC(0);
    HDC hDcmem = CreateCompatibleDC(0);
    HBITMAP hBmp = CreateCompatibleBitmap(hDc, 1, 1);
    SelectObject(hDcmem, hBmp);
    BitBlt(hDcmem, 0, 0, 1, 1, hDc, pt.x, pt.y, SRCCOPY);
    LPBITMAPINFO lpbmi = new BITMAPINFO;
    lpbmi->bmiHeader.biBitCount = 24;
    lpbmi->bmiHeader.biCompression = BI_RGB;
    lpbmi->bmiHeader.biPlanes = 1;
    lpbmi->bmiHeader.biHeight = 1;
    lpbmi->bmiHeader.biWidth = 1;
    lpbmi->bmiHeader.biSize = sizeof(BITMAPINFO);
    BYTE lpvBits[4];
    GetDIBits(hDcmem, hBmp, 0, 1, lpvBits, lpbmi, DIB_RGB_COLORS);
    COLORREF currColor = RGB(lpvBits[2], lpvBits[1], lpvBits[0]);
    delete lpbmi;
    DeleteObject(hBmp);
    DeleteDC(hDcmem);
    ReleaseDC(0, hDc);
    return currColor;
}

正如 Ben 在评论中指出的那样,这仍然非常慢,解决方案是使用这些构建块一次复制整个区域。你的代码会变成这样:

#include <iostream>
#include <vector>
#include <windows.h>

int main() {
    int const startX = 512;
    int const endX = 521;
    int const startX = 550;
    int const endY = 565;
    int const width = endX - startX;
    int const height = endY - startY;

    HDC const hDc = GetDC(0);
    HDC const hDcmem = CreateCompatibleDC(0);
    HBITMAP const hBmp = CreateCompatibleBitmap(hDc, width, height);
    auto const oldBmp = SelectObject(hDcmem, hBmp);

    BITMAPINFO bmi{};
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biHeight = height;
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biSize = sizeof(BITMAPINFO);

    std::vector<RGBQUAD> pixels(height * width);

    while (true) {
        Sleep(100);

        BitBlt(hDcmem, 0, 0, width, height, hDc, startX, startY, SRCCOPY);
        GetDIBits(hDcmem, hBmp, 0, height, &pixels[0], &bmi, DIB_RGB_COLORS);
        int sum = 0;
        for (int i = 0; i < height * width; ++i) {
            sum = pixels[i].R + pixels[i].G + pixels[i].B;
        }

        std::cout << "SUM: " << sum << std::endl;
    }

    SelectObject(hDcmem, oldBmp);
    DeleteObject(hBmp);
    DeleteDC(hDcmem);
    ReleaseDC(0, hDc);

    return 0;
}

【讨论】:

  • 这比GetPixel还要糟糕。其中大部分也不是必需的,您确实需要 GetDIBits 调用,但您应该使用它将您需要的整个矩形复制到内存中,然后在内存中循环遍历它。也不需要为 BITMAPINFO 使用动态分配 (new/delete)。在销毁位图和 DC 之前,您还需要 SelectObject 原始位图。抱歉,我知道这些错误不是您编写的,但如果没有大量修复,则无法使用该代码,因此发布作为答案没有用。
  • 是的,我试了之后发现还是很慢,但至少是用来演示如何使用调用的(当然,如果你需要一个区域,我想你会修改它以选择整个区域)。答案中的注释没有给出任何代码,所以没有任何指针就很难开始,这至少给出了指针。
  • @BenVoigt 我已经更新了答案,为如何复制整个区域提供了更多指导。您能否考虑对其进行审查并取消投票?
  • 我修改了您的新代码以修复像素间距和biBitCount 之间的不匹配问题,并在删除之前取消选择位图。
  • StackOverflow 已经有一些使用GetDiBits 的示例和讨论stackoverflow.com/q/3688409/103167
猜你喜欢
  • 2023-04-07
  • 2013-03-10
  • 2014-06-07
  • 2016-05-31
  • 2011-07-07
  • 2015-08-23
  • 2012-07-05
  • 2014-03-12
  • 2021-03-26
相关资源
最近更新 更多