tags:

views:

194

answers:

4

Hey,

I am trying to checksum an image, but it takes too long to give a result, tried add values, and Adler-32 but both finishes in a long time (approximately 2 seconds).

Adding values:

Function Checksum_CountryFlag(Img : TPicture):Integer;
var j, k, Checksum : Integer;
begin
Checksum := 0;
For j := 0 to Img.Width do
For k := 0 to Img.Height do
If (((Img.Bitmap.Canvas.Pixels[j,k]) <> 15577344) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 15311104) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 3816255) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 10526623) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 12303034) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 9013641)) Then
begin
Checksum := Checksum + Img.Bitmap.Canvas.Pixels[j,k];
end;
Result := Abs(Checksum);
end;

Adler-32:

Function Checksum_Adler32(Img : TPicture):Integer;
var i,a,b,j,k : Integer;
begin
a := 1; b := 0;
For j := 0 to Img.Width do
For k := 0 to Img.Height do
If (((Img.Bitmap.Canvas.Pixels[j,k]) <> 15577344) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 15311104) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 3816255) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 10526623) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 12303034) And ((Img.Bitmap.Canvas.Pixels[j,k]) <> 9013641)) Then
begin
a := a + Img.Bitmap.Canvas.Pixels[j,k];
b := b + a;
end;
Result := Abs(a) + Abs(b);
end;

As you see, i am trying to avoid some values (technical reasons). Maybe this what makes it takes too long, i don't know, any ideas for improving this algorithm ?

+2  A: 

Unroling parts your loop gives much more performance (lig putting img.bitmap.canvas into a variable outside the loops).
Getting the pixel[j,k] to a local variable before the if also helps alot, and it improves readability of the sourcecode.

But the biggest impact might be using the "Scanline" property and getting all pixels in a line to a local vaiable helps even more (you have to switch the height and the width for loops).

BennyBechDk
+1 for the scanline property. This really helps but you need to use pointers.
Ritsaert Hornstra
+2  A: 

Hard to be sure without a profiler, but looking at that I'd guess that you're losing a lot of time looking up the reader values for Img.Bitmap.Canvas.Pixels[j,k] repeatedly.

First thing I'd do is save that to a value, like so:

pixel := Img.Bitmap.Canvas.Pixels[j,k];
If ((pixel <> 15577344) And (pixel <> 15311104) And (pixel <> 3816255) And (pixel <> 10526623) And (pixel <> 12303034) And (pixel <> 9013641)) Then

That should speed things up significantly.

Mason Wheeler
yea, you are right, that saved some time but not enough, thanks !
Goblin
+6  A: 

I have done some correction to make the first version of your code fast:

Function Checksum_CountryFlag(Img : TPicture):Integer;
var j, k, Checksum : Integer;
  Line: PLongInt;
begin
  Checksum := 0;
  Img.Bitmap.PixelFormat:= pf32bit; // just for case
  For k := 0 to Img.Height - 1 do begin      //  !! Height first, -1
    Line:= Img.Bitmap.ScanLine[k];
    For j := 0 to Img.Width - 1 do begin  // !! -1
      If ((Line^ <> 15577344) And (Line^ <> 15311104) And (Line^ <> 3816255)
        And (Line^ <> 10526623) And (Line^ <> 12303034) And (Line^ <> 9013641)) Then
        begin
          Checksum := Checksum + Line^;
        end;
      Inc(Line);
    end;
  end;
  Result := Abs(Checksum);
end;

Updated: It gives different result because I fixed a bug in your code (Height-1), (Width-1). I also changed the loop order (external loop by Height) - it is nessessary to use ScanLine. You must do the same corrections in your code before comparing.

Serg
Amazing ! Scanlines are really alot fast, Thanks Serg for the example.
Goblin
But it doesn't give the same results.
Goblin
Didn't you forget "begin" after the first loop ?
Goblin
I hope now all is OK :)
Serg
+2  A: 

Serg' code with corrected (and faster) Pixel value:

Function Checksum_CountryFlag(Img : TPicture):Integer;
var j, k, Checksum : Integer;
  Line: PIntegerArray;
  Pixel : integer;
begin
  Checksum := 0;
  Img.Bitmap.PixelFormat:= pf32bit; // just for case
  For k := 0 to Img.Height - 1 do begin      //  !! Height first, -1
    Line:= Img.Bitmap.ScanLine[k];
    For j := 0 to Img.Width - 1 do begin  // !! -1
      Pixel := Line^[j];
      If ((Pixel <> 15577344) And (Pixel <> 15311104) And (Pixel <> 3816255)
        And (Pixel <> 10526623) And (Pixel <> 12303034) And (Pixel <> 9013641)) Then
        begin
          Checksum := Checksum + Pixel;
        end;
      Inc(Line);
    end;
  end;
  Result := Abs(Checksum);
end;
BennyBechDk
I'm not sure this is actually faster. How does the benchmarking go?
Marco van de Voort
It is faster because the pixel value is feched from memory to CPU register only once (before the if statement)
BennyBechDk