How to compare byte to byte of two bitmap screensh

2020-07-18 10:46发布

问题:

IN THE END OF THE QUESTION MY LAST EDIT

Hi all, I have to implement a function that compares two shots of a portion of the screen in order to know if there are differences/variations. I wrote something like the following code but I can't manage it to work. In the code COORDINATES_RECT is a struct

typedef struct _COORDINATES_RECT {
int     x;
int     y;
int     sizeX;
int     sizeY;
} COORDINATES_RECT; 

that in input holds data to know which is the portion of the screen to analyze, and in output returns the data of the biggest rectangle in which the function found variations. To better explain the problem, I saw that if construct in the end:

if(lpbitmap1[(i*bmpScreen1.bmWidth)+j] != lpbitmap2[(i*bmpScreen1.bmWidth)+j])

never get executed. I don't know if it is the right way to compare two bitmaps (converted into char arrays). I googled and searched in msdn but with no result. Full code follows:

void testBitmapVariations(COORDINATES_RECT *c)
{
HDC hdcScreen = GetDC(NULL);
HDC hdcMemDC1 = CreateCompatibleDC(hdcScreen);
HDC hdcMemDC2 = CreateCompatibleDC(hdcScreen);
HBITMAP hBmpScreen1 = NULL;
HBITMAP hBmpScreen2 = NULL;
BITMAP bmpScreen1;
BITMAP bmpScreen2;

if(!hdcMemDC1 || !hdcMemDC2)
{
    MessageBox(NULL,L"CreateCompatibleDC failed", L"Failed", MB_OK);
    ReleaseDC(NULL, hdcMemDC1);
    ReleaseDC(NULL, hdcMemDC2);
    return;
}
hBmpScreen1 = CreateCompatibleBitmap(hdcMemDC1, c->sizeX, c->sizeY);
hBmpScreen2 = CreateCompatibleBitmap(hdcMemDC2, c->sizeX, c->sizeY);

SelectObject(hdcMemDC1,hBmpScreen1);

if(!BitBlt(hdcMemDC1, 
           0,0, 
           c->sizeX, c->sizeY, 
           hdcScreen, 
           c->x,c->y,
           SRCCOPY))
{
    MessageBox(NULL,L"BitBlt failed", L"Failed", MB_OK);
    ReleaseDC(NULL, hdcMemDC1);
    ReleaseDC(NULL, hdcMemDC2);
    return;
}

GetObject(hBmpScreen1,sizeof(BITMAP),&bmpScreen1);

BITMAPFILEHEADER   bmfHeader;    
BITMAPINFOHEADER   bi;

bi.biSize = sizeof(BITMAPINFOHEADER);    
bi.biWidth = bmpScreen1.bmWidth;    
bi.biHeight = bmpScreen1.bmHeight;  
bi.biPlanes = 1;    
bi.biBitCount = 32;    
bi.biCompression = BI_RGB;    
bi.biSizeImage = 0;  
bi.biXPelsPerMeter = 0;    
bi.biYPelsPerMeter = 0;    
bi.biClrUsed = 0;    
bi.biClrImportant = 0;

DWORD dwBmpSize = ((bmpScreen1.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen1.bmHeight;

// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
// have greater overhead than HeapAlloc.
char *lpbitmap1 = (char *)malloc(dwBmpSize);    

// Gets the "bits" from the bitmap and copies them into a buffer 
// which is pointed to by lpbitmap1.
GetDIBits(hdcScreen, hBmpScreen1, 0,
    (UINT)bmpScreen1.bmHeight,
    lpbitmap1,
    (BITMAPINFO *)&bi, DIB_RGB_COLORS);

Sleep(200);

SelectObject(hdcMemDC2,hBmpScreen2);

if(!BitBlt(hdcMemDC2, 
           0,0, 
           c->sizeX, c->sizeY, 
           hdcScreen, 
           c->x,c->y,
           SRCCOPY))
{
    MessageBox(NULL,L"BitBlt failed", L"Failed", MB_OK);
    ReleaseDC(NULL, hdcMemDC1);
    ReleaseDC(NULL, hdcMemDC2);
    return;
}

GetObject(hBmpScreen2,sizeof(BITMAP),&bmpScreen2);

char *lpbitmap2 = (char *)malloc(dwBmpSize);    

// Gets the "bits" from the bitmap and copies them into a buffer 
// which is pointed to by lpbitmap2.
GetDIBits(hdcScreen, hBmpScreen2, 0,
    (UINT)bmpScreen2.bmHeight,
    lpbitmap2,
    (BITMAPINFO *)&bi, DIB_RGB_COLORS);

int i, j, minX = bmpScreen1.bmWidth, minY = bmpScreen1.bmHeight, maxX = 0, maxY = 0;
bool changed = false;

for(i = 0; i < bmpScreen1.bmHeight; i++) 
{
    for(j = 0; j < bmpScreen1.bmWidth; j++)
    {
                    // I don't know why this if never get executed
        if(lpbitmap1[(i*bmpScreen1.bmWidth)+j] != lpbitmap2[(i*bmpScreen1.bmWidth)+j])
        {
            changed = true;

            if(i < minY)
                minY = i;
            if(i > maxY)
                maxY = i;
            if(j < minX)
                minX = j;
            if(j > maxY)
                maxY = j;
        }
    }
}
if(changed)
{
    c->x = minX;
    c->y = minY;
    c->sizeX = maxX - minX;
    c->sizeY = maxY - minY;
}
else
{
    c->sizeX = 0;
    c->sizeY = 0;
}
//Frees from the heap
free(lpbitmap1);
free(lpbitmap2);
ReleaseDC(NULL, hdcMemDC1);
ReleaseDC(NULL, hdcMemDC2); 
}

Thanks

Francesco

Ps. Variables declarations are put next to the block they refers to only to achieve more clarity in this question..

EDIT:

I rewrote it like this and it seems to work :) Thanks to all, however any correction is appreciated

void testBitmapVar(COORDINATES_RECT *c)
{
HDC     screenDC = GetDC(0),
        memDC = CreateCompatibleDC(screenDC);
HBITMAP hBm = CreateCompatibleBitmap(screenDC, c->sizeX, c->sizeY), 
        oldHBm;
BITMAP  bm;
BITMAPFILEHEADER   bmfHeader;    
BITMAPINFOHEADER   bi;
UINT    *pixels = (UINT*) malloc ((c->sizeX*c->sizeY) * sizeof(UINT));
std::ostringstream ss;
std::wstring str;
int     i, j, minX, maxX, minY, maxY;

if(!pixels)
{
    c->sizeX = 0;
    c->sizeY = 0;
    return;
}
memset(pixels, 0, (c->sizeX*c->sizeY) * sizeof(UINT));
oldHBm = (HBITMAP) SelectObject(memDC, hBm);

// copies to bitmap
BitBlt(memDC, 0, 0, c->sizeX, c->sizeY, screenDC, c->x, c->y, SRCCOPY);

Sleep(500);

BitBlt(memDC, 0, 0, c->sizeX, c->sizeY, screenDC, c->x, c->y, SRCINVERT);
GetObject(hBm, sizeof(BITMAP), &bm);

bi.biSize = sizeof(BITMAPINFOHEADER);    
bi.biWidth = bm.bmWidth;    
bi.biHeight = bm.bmHeight;  
bi.biPlanes = 1;    
bi.biBitCount = 32;    
bi.biCompression = BI_RGB;    
bi.biSizeImage = 0;  
bi.biXPelsPerMeter = 0;    
bi.biYPelsPerMeter = 0;    
bi.biClrUsed = 0;    
bi.biClrImportant = 0;

GetDIBits(memDC, hBm, 0,
    (UINT)bm.bmHeight,
    pixels,
    (BITMAPINFO *)&bi, DIB_RGB_COLORS);

for(i = 0, minY = c->sizeY, maxY = -1; i < c->sizeY; i++) 
{
    for(j = 0, minX = c->sizeX, maxX = -1; j < c->sizeX; j++)
    {
        if(pixels[(i*c->sizeX)+j])
        {
            if(i < minY)
                minY = i;
            if(i > maxY)
                maxY = i;
            if(j < minX)
                minX = j;
            if(j > maxX)
                maxX = j;
        }
    }
}
if(maxX != -1 && maxY != -1)
{
    c->x = minX;
    c->y = minY;
    c->sizeX = maxX - minX;
    c->sizeY = maxY - minY;
}
else
{
    c->sizeX = 0;
    c->sizeY = 0;
}

free(pixels);
SelectObject(memDC, oldHBm);
DeleteObject(hBm);
ReleaseDC(0, screenDC);
DeleteDC(memDC);
}

回答1:

Rather than comparing the two bitmaps entirely on your own, consider using BitBlt to combine them using the SRCINVERT operator, which XORs the two together, so the parts that are identical will show up as zeros, and all the non-zero areas will be differences.



回答2:

gc5, thanks for example! I'll add 2bit of mine:

you should define bi as:

BITMAPINFO bi;
memset(&bi, 0, sizeof(BITMAPINFO));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = bm.bmWidth;
bi.bmiHeader.biHeight = bm.bmHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;

and then no need to define a type:

GetDIBits(memDC, hBm, 0, (UINT)bm.bmHeight, pixels, &bi, DIB_RGB_COLORS);