views:

445

answers:

4

Is there a better way than examine them pixel by pixel?

A: 

I don't know, write it to file, hash it then compare the hashes?

silent
Why would you start an answer to a question with "I don't know"? If you don't know, don't answer.
raven
well, just give some idea.
silent
Maybe the silence is better when you dont know, silent.
Cesar Romero
Using a hash function is not a bad idea.
RRUZ
To compute the hashes one would need to visit each byte of both bitmaps. So why not compare them directly in the first place?
mghie
@RRUZ: Actually for this task it **is** a bad idea.
mghie
mghie you are right, calculate the hash is slower than using CompareMem, I wrote a version using ScanLine. ;)
RRUZ
Look on the plus side - delete this now and you get a badge!
Argalatyr
mghie: cache size maybe. The compare needs more data active in the cache. But I think that is theoretic only, since the action per pixel are way higher in the hash case (since you have to pick a pretty decent one to avoid false positives)
Marco van de Voort
@Marco: The killer argument against the hash approach is that it gives a result only after the algorithm has completed, while a pixel comparison can exit at the very first difference. If there's a decent chance that bitmaps are different this should trump all other considerations.
mghie
@mghie: that's a great argument for the scanline approach as well.
Argalatyr
@Argalatyr: Indeed, and I would be surprised if it didn't completely outclass the memory stream solution performance-wise. But the OP will have to *measure* it, now that he has the two solutions.
mghie
A: 

If you need an accurate answer, no. If you need an approximation, you probably could check a selection of pixels. But if you want to find out if the two bitmaps are exactly identical you need to compare the entire pixel and pixel format data.

TommyA
+11  A: 

You can save both Bitmaps to TMemoryStream and compare using CompareMem:

function IsSameBitmap(Bitmap1, Bitmap2: TBitmap): Boolean;
var
 Stream1, Stream2: TMemoryStream;
begin
  Assert((Bitmap1 <> nil) and (Bitmap2 <> nil), 'Params can''t be nil');
  Result:= False;
  if (Bitmap1.Height <> Bitmap2.Height) or (Bitmap1.Width <> Bitmap2.Width) then
     Exit;
  Stream1:= TMemoryStream.Create;
  try
    Bitmap1.SaveToStream(Stream1);
    Stream2:= TMemoryStream.Create;
    try
      Bitmap2.SaveToStream(Stream2);
      if Stream1.Size = Stream2.Size Then
        Result:= CompareMem(Stream1.Memory, Stream2.Memory, Stream1.Size);
    finally
      Stream2.Free;
    end;
  finally
    Stream1.Free;
  end;
end;

begin
  if IsSameBitmap(MyImage1.Picture.Bitmap, MyImage2.Picture.Bitmap) then
  begin
    // your code for same bitmap
  end;
end;

I did not benchmark this code X scanline, if you do, please let us know which one is the fastest.

Cesar Romero
Some comments: 1) Code isn't exception-safe. 2) I would return False immediately if width or height of the bitmaps differ. Or maybe even if the pixel formats differ, but the question is too vague to tell.
mghie
Nice comments mghie. Ill change the code to test the height and width.
Cesar Romero
Swallowing exceptions was not what I meant, allow me to edit the code...
mghie
I dont think I can "allow" others to edit my posts, if I can, please let me know how.If you post here your suggestions I can edit and mention in my post.
Cesar Romero
I just edited it, but wanted to alert you before. +1 for the answer BTW.
mghie
Thank you mghie.BTW, are you the flamerobin author?
Cesar Romero
Well, one of them. Neither the original author nor the main one.
mghie
mghie's comment above asks whether matching pixel format is a specification, but it's easy to add as illustrated in RRUZ's answer.
Argalatyr
Strictly speaking, such compares can go wrong when one of the bitmaps has a bit garbage in the alignment bytes/bits between lines. Therefore we always compare the pixeldata with comparemem on a per line basis.
Marco van de Voort
+10  A: 

Using ScanLine, Without TMemoryStream.

function IsSameBitmapUsingScanLine(Bitmap1, Bitmap2: TBitmap): Boolean;
var
 i           : Integer;
 ScanBytes   : Integer;
begin
  Result:= (Bitmap1<>nil) and (Bitmap2<>nil);
  if not Result then exit;
  Result:=(bitmap1.Width=bitmap2.Width) and (bitmap1.Height=bitmap2.Height) and (bitmap1.PixelFormat=bitmap2.PixelFormat) ;

  if not Result then exit;

  ScanBytes := Abs(Integer(Bitmap1.Scanline[1]) - Integer(Bitmap1.Scanline[0]));
  for i:=0 to Bitmap1.Height-1 do
  Begin
    Result:=CompareMem(Bitmap1.ScanLine[i],Bitmap2.ScanLine[i],ScanBytes);
    if not Result then exit;
  End;

end;

Bye.

RRUZ
+1 very nicely composed. It would be interesting to compare the speed of this versus Cesar's solution. This has more comparisons, but saves time by not allocate memory. The question title did specify **fastest**, after all.
Argalatyr
@RRUZ: I agree that this is a good solution if same bitmap means the same memory layout, +1. I'd consider a fast check for equal bitmaps in possibly different formats to be a more interesting problem, though. If a pf24bit or pf32bit bitmap has less than 256 colours it can make sense to save it in pf8bit, but the same bitmap will still be displayed.
mghie
I usually only use pf8bit, and for this it would be ok. I wonder though if alignment bits are checked if you have pf12bit and an odd width. Same for bpp's below 8, but those are planed afaik.
Marco van de Voort