对 BITMAPINFO (BITMAP) 中的像素数据的原始/直接访问

Posted

技术标签:

【中文标题】对 BITMAPINFO (BITMAP) 中的像素数据的原始/直接访问【英文标题】:Raw/Direct acess on pixels data in a BITMAPINFO (HBITMAP) 【发布时间】:2016-06-16 05:10:51 【问题描述】:

我试图弄清楚如何从屏幕访问原始像素信息。到目前为止,我一直在将屏幕捕获到 HBITMAP,填充 BITMAPINFO,然后创建此 BITMAPINFO 变量的指针以直接从内存中读取。 我知道标题必须从文件中“删除”,所以我将指针直接推进到位图数据(将 sizeof(MyBMInfo2->bmiHeader) 添加到我的指针偏移量)。我也知道这个位图是倒置/自上而下的,第一个像素位于原始数据的末尾。我需要弄清楚如何从图像中给定的 X 和 Y 中提取 R G 和 B 字节,而这正是我无法做到的。 所以我问你先生们一个灯,一个 sn-p 或任何可以帮助我从 gdi32 重新创建 Bitmap.GetPixel(x,y) 的肮脏尝试的提示(太慢了,我需要一个更好的)。

到目前为止我的源代码的sn-p

...
HDC hCaptureDC  = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
BOOL bOK = BitBlt(hCaptureDC,0,0,nScreenWidth, nScreenHeight, hdc,0,0,SRCCOPY|CAPTUREBLT);
SelectObject(hCaptureDC, hOld); // always select the previously selected object once done 
DeleteDC(hCaptureDC);

BITMAPINFO MyBMInfo = 0;
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);

// Get the MyBMInfo structure from the bitmap
if(0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) 
    printf("error\n");


BITMAPINFO* MyBMInfo2 = &MyBMInfo;
BYTE* bitmapBits=(BYTE*)MyBMInfo2+sizeof(MyBMInfo2->bmiHeader);
//So... how do I acess X and Y RGB bytes now? xD
...

顺便说一句....还有其他更直接的方法可以做到这一点而不会引发内存保护错误吗?或者......另一种更快的方式? 谢谢。

--编辑--

使用 Barmak 的代码,我已经弄清楚如何根据当前光标位置访问 X 和 Y rgb,下面是源代码:

#include <windows.h>

int main()

    HWND hwnd = GetDesktopWindow();
    RECT rc;
    GetClientRect(hwnd, &rc);
    int width = rc.right;
    int height = rc.bottom;
    if (width < 1 || height < 1)
    
        OutputDebugStringA("error\n");
        return 0;
    

    HDC hdc = GetDC(hwnd);

    HDC hCaptureDC = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
    HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
    BitBlt(hCaptureDC, 0, 0, width, height, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
    SelectObject(hCaptureDC, hOld);

    BITMAPINFO MyBMInfo =  0 ;
    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);

    BITMAPINFOHEADER bmpInfoHeader =  sizeof(BITMAPINFOHEADER) ;
    bmpInfoHeader.biWidth = width;
    bmpInfoHeader.biHeight = height;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = 32;

    DWORD size = ((width * bmpInfoHeader.biBitCount + 31) / 32) * 4 * height;
    BYTE *bits = malloc(size);

    if (GetDIBits(hdc, hBitmap, 0, height, bits, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS))
    
        OutputDebugStringA("success\n");
        //you can use bits here

        //access bits from upper-left to lower-right corner
        POINT p;
        GetCursorPos(&p);
        int x = p.x;
        int y = p.y;

        int col = x;
        int row = height - y - 1;
        int index = (row * width + col) * 4;

        BYTE b = bits[index + 0];
        BYTE g = bits[index + 1];
        BYTE r = bits[index + 2];

        printf("r:%i g:%i b:%i \n",r,g,b);


    
    else
    
        OutputDebugStringA("error\n");
    

    free(bits);

    DeleteDC(hCaptureDC);
    DeleteObject(hBitmap);
    ReleaseDC(hwnd, hdc);

    return 0;

非常感谢 Shemirani 先生。请对我的回答投反对票的人,请考虑撤回它吗? :) 和平。

【问题讨论】:

MyBMInfo.bmiHeader.biBitCount 返回 32 位,即来自 DC 的相同颜色深度(设备上下文:桌面分辨率)。我认为 32/8 是知道这里每像素字节数的微积分,嗯?!我担心我将如何实现反向偏移查找器,给定任何 X 和 Y 组合......:S 【参考方案1】:

GetDibBits 需要一个缓冲区来接收这些位。您必须分配缓冲区,然后在不再需要时删除。

#include <windows.h>

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)

    HWND hwnd = GetDesktopWindow();
    RECT rc;
    GetClientRect(hwnd, &rc);
    int width = rc.right;
    int height = rc.bottom;
    if (width < 1 || height < 1)
    
        OutputDebugStringA("error\n");
        return 0;
    

    HDC hdc = GetDC(hwnd);

    HDC hCaptureDC = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
    HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
    BitBlt(hCaptureDC, 0, 0, width, height, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
    SelectObject(hCaptureDC, hOld);

    BITMAPINFO MyBMInfo =  0 ;
    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);

    BITMAPINFOHEADER bmpInfoHeader =  sizeof(BITMAPINFOHEADER) ;
    bmpInfoHeader.biWidth = width;
    bmpInfoHeader.biHeight = height;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = 32;

    DWORD size = ((width * bmpInfoHeader.biBitCount + 31) / 32) * 4 * height;
    BYTE *bits = malloc(size);

    if (GetDIBits(hdc, hBitmap, 0, height, bits, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS))
    
        OutputDebugStringA("success\n");
        //you can use bits here

        //access bits from lower-left to upper-right corner
        for (int row = 0; row < height; row++)
        
            for (int col = 0; col < width; col++)
            
                //for 32 bit image only:
                int index = (row * width + col) * 4;

                BYTE blue  = bits[index + 0];
                BYTE green = bits[index + 1];
                BYTE red   = bits[index + 2];
            
        

        //access bits from upper-left to lower-right corner
        for (int y = 0; y < height; y++)
        
            for (int x = 0; x < width; x++)
            
                int col = x;
                int row = height - y - 1;
                int index = (row * width + col) * 4;

                BYTE b = bits[index + 0];
                BYTE g = bits[index + 1];
                BYTE r = bits[index + 2];
            
        

    
    else
    
        OutputDebugStringA("error\n");
    

    free(bits);

    DeleteDC(hCaptureDC);
    DeleteObject(hBitmap);
    ReleaseDC(hwnd, hdc);

    return 0;

【讨论】:

非常好,但不是直接访问。在//you can use bits here 部分,如何根据给定的 X、Y 位置访问 bmpInfoHeader 上的 R、G 和 B? 太好了,所以我假设 x 和 y 访问可以通过这种方式完成 int index = (y * width + x) * 4; ?! ^^ 编辑:第四个字节是 alpha,对吗? 是的,还要注意 Y 轴从底部开始 我猜测你的代码是 C++,我不能确定(我删除了之前的评论)。您的文件是否有 *.c 扩展名或 *.cpp 扩展名?如果它有"*.cpp" 扩展名,那么它就是C++ *.c 扩展名,并且只有 C90+C99 头文件 :) 那么,你能告诉我如何反向获得 X 和 Y 的偏移量吗?

以上是关于对 BITMAPINFO (BITMAP) 中的像素数据的原始/直接访问的主要内容,如果未能解决你的问题,请参考以下文章

Windows 位图:BITMAPV5HEADER 和 BITMAPINFO 兼容?

Android内存优化1-对Bitmap的内存优化

SLAM入门之视觉里程计:基础矩阵的估计

BitMap及其在ClickHouse中的应用

光学系统中的像差

光学系统中的像差