|
|
Thank you for the code.
In my project I needed to compare the screenshots and allow a minor changes for them (suppose I took a screenshot periodically from a window).
So the code returns True - only if the images are the same or there is a little changes it in (less than 3% of 1K blocks are differs.)
Here is the code:
int Different_blocks =0;
// scan by 1k blocks and find out how many blocks are different.
for (int i=0; i<=pBitmapInfoLeft->bmiHeader.biSizeImage / 1000; i++)
{
if ( memcmp(pLeftBits + i*1000, pRightBits + i*1000, 1000) )
Different_blocks++;
}
bSame = true;
// setup allowed limit - a 3.3% from total blocks number.
int allowed_limit = (pBitmapInfoLeft->bmiHeader.biSizeImage / 10000) / 3;
if (Different_blocks > allowed_limit)
bSame = false;
--
Alexander Shilonosov
|
|
|
|
|
has anyone ported in to C already?
i need it for C..
Edit:
Already did it =)
here is the code
int CompareBitmaps(HBITMAP HBitmapLeft, HBITMAP HBitmapRight)
{
if (HBitmapLeft == HBitmapRight)
{
return 1;
}
if (NULL == HBitmapLeft || NULL == HBitmapRight)
{
return 0;
}
int bSame = 0;
HDC hdc = GetDC(NULL);
BITMAPINFO BitmapInfoLeft = {0};
BITMAPINFO BitmapInfoRight = {0};
BitmapInfoLeft.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoRight.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
if (0 != GetDIBits(hdc, HBitmapLeft, 0, 0, NULL, &BitmapInfoLeft, DIB_RGB_COLORS) &&
0 != GetDIBits(hdc, HBitmapRight, 0, 0, NULL, &BitmapInfoRight, DIB_RGB_COLORS))
{
if (0 == memcmp(&BitmapInfoLeft.bmiHeader, &BitmapInfoRight.bmiHeader,
sizeof(BITMAPINFOHEADER)))
{
BYTE *pLeftBits = (BYTE *) malloc(sizeof(BYTE) * BitmapInfoLeft.bmiHeader.biSizeImage);
BYTE *pRightBits = (BYTE *) malloc(sizeof(BYTE) * BitmapInfoRight.bmiHeader.biSizeImage);
BYTE *pByteLeft = NULL;
BYTE *pByteRight = NULL;
PBITMAPINFO pBitmapInfoLeft = &BitmapInfoLeft;
PBITMAPINFO pBitmapInfoRight = &BitmapInfoRight;
int AdditionalMemory = 0;
switch (BitmapInfoLeft.bmiHeader.biBitCount)
{
case 1:
AdditionalMemory = 1 * sizeof(RGBQUAD);
break;
case 4:
AdditionalMemory = 15 * sizeof(RGBQUAD);
break;
case 8:
AdditionalMemory = 255 * sizeof(RGBQUAD);
break;
case 16:
case 32:
AdditionalMemory = 2 * sizeof(RGBQUAD);
}
if (AdditionalMemory)
{
pByteLeft = (BYTE*) malloc(sizeof(BYTE) * (sizeof(BITMAPINFO) + AdditionalMemory));
if (pByteLeft)
{
memset(pByteLeft, 0, sizeof(BITMAPINFO) + AdditionalMemory);
memcpy(pByteLeft, pBitmapInfoLeft, sizeof(BITMAPINFO));
pBitmapInfoLeft = (PBITMAPINFO)pByteLeft;
}
pByteRight = (BYTE *) malloc(sizeof(BYTE) * (sizeof(BITMAPINFO) + AdditionalMemory));
if (pByteRight)
{
memset(pByteRight, 0, sizeof(BITMAPINFO) + AdditionalMemory);
memcpy(pByteRight, pBitmapInfoRight, sizeof(BITMAPINFO));
pBitmapInfoRight = (PBITMAPINFO)pByteRight;
}
}
if (pLeftBits && pRightBits && pBitmapInfoLeft && pBitmapInfoRight)
{
memset(pLeftBits, 0, BitmapInfoLeft.bmiHeader.biSizeImage);
memset(pRightBits, 0, BitmapInfoRight.bmiHeader.biSizeImage);
if (0 != GetDIBits(hdc, HBitmapLeft, 0,
pBitmapInfoLeft->bmiHeader.biHeight, pLeftBits, pBitmapInfoLeft,
DIB_RGB_COLORS) && 0 != GetDIBits(hdc, HBitmapRight, 0,
pBitmapInfoRight->bmiHeader.biHeight, pRightBits, pBitmapInfoRight,
DIB_RGB_COLORS))
{
FILE *f1 = fopen("C:\\Coding\\Log1.txt", "w");
FILE *f2 = fopen("C:\\Coding\\Log2.txt", "w");
fprintf(f1, "%s", (CHAR *) pLeftBits);
fprintf(f2, "%s", (CHAR *) pRightBits);
fflush(f1);
fflush(f2);
fclose(f1);
fclose(f2);
bSame = 0 == memcmp(pLeftBits, pRightBits,
pBitmapInfoLeft->bmiHeader.biSizeImage);
}
}
free(pLeftBits);
free(pRightBits);
free(pByteLeft);
free(pByteRight);
}
}
ReleaseDC(NULL, hdc);
return bSame;
}
modified on Thursday, January 28, 2010 8:07 PM
|
|
|
|
|
Hello,
I made an dll containing this function and when I call it with two hbitmaps to compare - it crashes on:
memset(pLeftBits, 0, BitmapInfoLeft.bmiHeader.biSizeImage);
(zero out the bitmap buffers)
It sais - "access violation write of address xy". When I comment theese two memsets out - it does not crash but it returns false when I pass it two identical images - two bitblts of the area of the screen one after another...
Where is the problem. Can you use memset just after
BYTE *pLeftBits = new BYTE;
not allocating the memory for pLeftBits ?
Thanks,
Hrvoje
|
|
|
|
|
CodeProject has somehow messed up the source code for this article. If you do a 'view source' on this page you will see the correct code. I will get an editor to fix it. But in the mean time you can change the two lines to:
BYTE *pLeftBits = new BYTE[BitmapInfoLeft.bmiHeader.biSizeImage];
BYTE *pRightBits = new BYTE[BitmapInfoRight.bmiHeader.biSizeImage]; . I hope this helps.
|
|
|
|
|
This is a great article but do you know how i could extend it to produce a third bitmap that is black and white, and where all the white pixels represent a difference in the two bitmaps?
|
|
|
|
|
A simple way would be to create a monochrome bitmap of the required size and do a simple GetPixel on each of the two previous bitmaps and a SetPixel on the new one.
for (int w = 0; w < width; ++w)
{
for (int h = 0; h < height; ++h)
{
if (GetPixel(DC1, w, h) != GetPixel(DC2, w, h))
SetPixel(DCNew, w, h, RGB(255, 255, 255));
else
SetPixel(DCNew, w, h, RGB(0, 0, 0));
}
}
You may be right I may be crazy -- Billy Joel --
Within you lies the power for good, use it!!!
|
|
|
|
|
Hi,
Thanks for that, i see where your going but im not that familiar with this topic so can you explain how you would create a monochrome bitmap and how you would get the height and width of the bitmap.
Thanks.
|
|
|
|
|
The width and height of the bitmap are in the BITMAPINFOHEADER referenced in the code above. It can also be obtained via a call to GetObject() where you fill a BITMAP structure from a HBITMAP object.
Use CreateBitmap() to create a monochrome bitmap, specifying 1 for the planes and bits per pixel, and NULL for the bits.
HBITMAP MonoBitmap = CreateBitmap(width, height, 1, 1, NULL);
Lookup BITMAPINFOHEADER , BITMAP , GetObject() , and CreateBitmap() in MSDN.
You may be right I may be crazy -- Billy Joel --
Within you lies the power for good, use it!!!
|
|
|
|
|
ok for the life of me i cant seem to get this to work can you take a look at my code and see whats wrong:
this is my code for placing my bit array into a HBITMAP
HDC image1Dc = CreateCompatibleDC ( hdc );<br />
HBITMAP btmpNew = CreateCompatibleBitmap(hdc,pVih->bmiHeader.biWidth,pVih->bmiHeader.biHeight);<br />
HBITMAP btmpNewTemp = (HBITMAP)SelectObject(image1Dc,btmpNew);<br />
<br />
SetDIBits(<br />
image1Dc,<br />
btmpNew,<br />
0,<br />
pVih->bmiHeader.biHeight,<br />
cb.pBuffer,<br />
(BITMAPINFO*)&pVih->bmiHeader,<br />
DIB_RGB_COLORS<br />
);<br />
<br />
width = pVih->bmiHeader.biWidth;<br />
height = pVih->bmiHeader.biHeight;<br />
if (btmpOld != NULL && btmpNew != NULL)<br />
{<br />
CompareBitmaps(btmpOld, btmpNew);<br />
}<br />
And then this is my CompareBitmaps function
<br />
bool CWebcamTestViz::CompareBitmaps(HBITMAP HBitmapLeft, HBITMAP HBitmapRight)<br />
{ <br />
<br />
HDC dc1 = CreateCompatibleDC ( NULL );<br />
HDC dc2 = CreateCompatibleDC ( NULL );<br />
HDC dcNew = CreateCompatibleDC ( NULL );<br />
int pixelCount = 0;<br />
<br />
SelectObject(dc1,HBitmapLeft);<br />
SelectObject(dc2,HBitmapRight);<br />
HBITMAP MonoBitmap = CreateBitmap(width, height, 1, 1, NULL);<br />
HBITMAP MonoBitmapTemp = (HBITMAP)SelectObject(dcNew,MonoBitmap);<br />
<br />
for (int w = 0; w < width; w+=5)<br />
{<br />
for (int h = 0; h < height; h+=5)<br />
{<br />
if (GetPixel(dc1, w, h) != GetPixel(dc2, w, h))<br />
{<br />
SetPixel(dcNew, w, h, RGB(255, 255, 255));<br />
pixelCount++;<br />
}<br />
else<br />
{<br />
SetPixel(dcNew, w, h, RGB(225, 0, 0));<br />
}<br />
}<br />
}<br />
<br />
<br />
if (pixelCount>50)<br />
{<br />
BitBlt(memDC,100,100,<br />
width,<br />
height,<br />
dcNew,0,0,SRCCOPY); <br />
<br />
::DeleteObject(dc1);<br />
::DeleteObject(dc2);<br />
::SelectObject(dcNew,MonoBitmapTemp);<br />
::DeleteObject(MonoBitmap);<br />
::DeleteObject(dcNew);<br />
return true;<br />
}<br />
else<br />
{<br />
::DeleteObject(dc1);<br />
::DeleteObject(dc2);<br />
::SelectObject(dcNew,MonoBitmapTemp);<br />
::DeleteObject(MonoBitmap);<br />
::DeleteObject(dcNew);<br />
return false;<br />
}<br />
}<br />
Theres probably loads of errors!
|
|
|
|
|
I have not gone through your code to find all the errors, but you may find this tool[^] helpful. I use it all the time when I am doing anything that has to do with bitmaps.
You may be right
I may be crazy
-- Billy Joel --
Within you lies the power for good - Use it!
|
|
|
|
|
Right i have managed to compare the two bitmaps now and produce a image to represent their differences. I have dome this via GetPixel and SetPixel, but these functions are far to slow for my purpose. Do you know of a different way to compare the pixels.
I have read that comparing them as DIBs is quicker so i have created two DIBs and placed them in seperate DCs but i dont know how to compopare them.
Can you help?
|
|
|
|
|
GetPixel() and SetPixel are slow, do doubt. But they are an easy way of doing it. To access the bitmap data directly is a lot more work for the programmer, but it definitely will run faster.
If you are going to go this way you have to take into account the colour depth of the bitmap. For colour depths of less than 16 bits per pixel your bitmap will have a colour table that the bitmap data indexes into. See GetDIBColorTable() and SetDIBColorTable().
For 16, 24 and 32 bit colour depths the data contains the actual colour values. The thing to remember about these is that every row in the bitmap data is DWORD aligned. 32 bits are the easiest to work with as in them every 32 bits is another colour and the DWORD alignment is automatic. 16 bits will have a dummy colour value at the end of every row if the width of the bitmap is an odd number, and 24 bits will have 1, 2 or 3 dummy bytes at the end of a row depending on the width of the bitmap. If you need to access the the individual red, green, blue, and alpha components of each colour than you also have to take into account the colour masks, but as long as both your images have the same colour depth that should not be neccesary.
See GetDIBits() and SetDIBits()
If you have any more questions you may get more and better answers if you ask them on the Visual C++ message board.
Good Luck!
You may be right I may be crazy -- Billy Joel --
Within you lies the power for good, use it!!!
|
|
|
|
|
|
Ok, it has taken me a while. But I liked your idea so much I wanted to implement it in my Image Viewer[^] application. Here is what I came with:
note - pja::CBitmap and pja::CCompatibleDC are simple wrappers around the HBITMAP and HDC types
pja::CBitmap Compare(const pja::CBitmap &Bitmap1, const pja::CBitmap &Bitmap2, int x = 0, int y = 0, COLORREF ClrSame = 0x00FFFFFF, COLORREF ClrDiff = 0x00000000);
pja::CBitmap Compare(const pja::CBitmap &Bitmap1,
const pja::CBitmap &Bitmap2,
int x, int y,
COLORREF ClrSame,
COLORREF ClrDiff)
{
ASSERT (x > -Bitmap2.Width() && x < Bitmap1.Width());
ASSERT (y > -Bitmap2.Height() && y < Bitmap1.Height());
if (x <= -Bitmap2.Width() || x >= Bitmap1.Width() || y <= -Bitmap2.Height() || y >= Bitmap1.Height())
{
return pja::CBitmap();
}
CRect Bitmap1Rect(0, 0, Bitmap1.Width(), Bitmap1.Height());
CRect Bitmap2Rect(0, 0, Bitmap2.Width(), Bitmap2.Height());
if (x < 0)
Bitmap1Rect.OffsetRect(-x, 0);
else
Bitmap2Rect.OffsetRect(x, 0);
if (y < 0)
Bitmap1Rect.OffsetRect(0, -y);
else
Bitmap2Rect.OffsetRect(0, y);
CRect FinalRect = Bitmap1Rect | Bitmap2Rect;
CRect CombinedRect = Bitmap1Rect & Bitmap2Rect;
pja::CBitmap FinalBitmap(NULL, FinalRect.Width(), FinalRect.Height());
pja::CBitmap CombinedBitmap(NULL, CombinedRect.Width(), CombinedRect.Height());
pja::CBitmap MonoBitmap(CreateBitmap(CombinedRect.Width(), CombinedRect.Height(), 1, 1, NULL));
pja::CCompatibleDC Bitmap1DC;
pja::CCompatibleDC Bitmap2DC;
pja::CCompatibleDC FinalBitmapDC;
pja::CCompatibleDC CombinedBitmapDC;
pja::CCompatibleDC MonoBitmapDC;
SelectObject(Bitmap1DC, Bitmap1);
SelectObject(Bitmap2DC, Bitmap2);
SelectObject(FinalBitmapDC, FinalBitmap);
SelectObject(CombinedBitmapDC, CombinedBitmap);
SelectObject(MonoBitmapDC, MonoBitmap);
BitBlt(CombinedBitmapDC,
0, 0, CombinedRect.Width(), CombinedRect.Height(),
Bitmap1DC,
CombinedRect.left > Bitmap1Rect.left ? CombinedRect.left : 0,
CombinedRect.top > Bitmap1Rect.top ? CombinedRect.top : 0,
SRCCOPY);
BitBlt(CombinedBitmapDC, 0, 0, CombinedRect.Width(), CombinedRect.Height(),
Bitmap2DC,
CombinedRect.left > Bitmap2Rect.left ? CombinedRect.left : 0,
CombinedRect.top > Bitmap2Rect.top ? CombinedRect.top : 0,
0x00990066);
if (CombinedBitmap.BitsPixel() >= 32)
{
SelectObject(CombinedBitmapDC, GetStockObject(WHITE_BRUSH));
PatBlt(CombinedBitmapDC, 0, 0, CombinedBitmap.Width(), CombinedBitmap.Height(), 0x00A000C9);
}
SetBkColor(CombinedBitmapDC, 0x00FFFFFF);
BitBlt(MonoBitmapDC, 0, 0, CombinedRect.Width(), CombinedRect.Height(), CombinedBitmapDC, 0, 0, SRCCOPY);
FillRect(FinalBitmapDC, FinalRect, CBrush(RGB(236, 233, 216)));
if (FinalRect != CombinedRect)
{
BitBlt(FinalBitmapDC, Bitmap1Rect.left, Bitmap1Rect.top, Bitmap1Rect.Width(), Bitmap1Rect.Height(), Bitmap1DC, 0, 0, SRCCOPY);
BitBlt(FinalBitmapDC, Bitmap2Rect.left, Bitmap2Rect.top, Bitmap2Rect.Width(), Bitmap2Rect.Height(), Bitmap2DC, 0, 0, SRCCOPY);
}
SetBkColor(FinalBitmapDC, ClrSame);
SetTextColor(FinalBitmapDC, ClrDiff);
BitBlt(FinalBitmapDC, CombinedRect.left, CombinedRect.top, CombinedRect.Width(), CombinedRect.Height(), MonoBitmapDC, 0, 0, SRCCOPY);
return FinalBitmap;
}
You may be right I may be crazy -- Billy Joel --
Within you lies the power for good, use it!!!
|
|
|
|
|
The article is great but let's imagine two bitmaps with single different pixel each. Function will false them even if they obviosly are similar. So when comparing actual pixel data its possible to add some error level (passed in % in third variable).
That may be usefull when comparing images with different compression rate - for some lossy format.
Guess that will be more useful.
Yet again great article.
|
|
|
|
|
True, but if two bitmaps are different by a single pixel then they are by definition not equal. For my problem this is what I needed, I needed to know if they were different, not just similiar. One is getting into rather complicated territory when it comes to checking for similiarity between images. Whole industries are based on that kind of stuff.
IntVestor wrote: Yet again great article.
Thanks
You may be right I may be crazy -- Billy Joel --
Within you lies the power for good, use it!!!
|
|
|
|
|
That's what the PSNR calculation is all about..
It will tell you if two images are exactly the same, and if not, how different are they.
|
|
|
|
|