2

I would like to load an image, and read pixels to find a certain RGB, but then check the next pixels across to make sure they match, and i am at the right position of the bitmap.

I know the below code is wrong, but i am not sure how to go about correcting it. I also know Pixels is not the fastest way to read pixels.

Thanks guys!

procedure RGB(Col: TColor; var R, G, B: Byte);
var
  Color: $0..$FFFFFFFF;
begin
  Color := ColorToRGB(Col);
  R := ($000000FF and Color);
  G := ($0000FF00 and Color) Shr 8;
  B := ($00FF0000 and Color) Shr 16;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  x,y : Integer;
  ColorN: TColor;
  R, G, B: Byte;
begin
  for Y := 0 to Image1.Picture.Bitmap.Height -1 do
  begin
    for X := 0 to Image1.Picture.Bitmap.Width -1 do
    begin
      inc(i);
      ColorN := Image1.Canvas.Pixels[x, y];
      RGB(ColorN, R, G, B);
      //Memo1.Lines.Append('Line: '+IntToStr(i)+' Y: '+IntToStr(Y)+' X: '+IntToStr(X)+' R: '+IntToStr(R)+' G: '+IntToStr(G)+' B: '+IntToStr(B));
      if (IntToStr(R) = '235') and (IntToStr(G) = '235') and (IntToStr(B) = '235') then //Y: 500 X: 587
      begin
        //Image1.Canvas.MoveTo(X,Y);
        //Image1.Canvas.LineTo(X,Y);
        ColorN := Image1.Canvas.Pixels[x +1, y];
        RGB(ColorN, R, G, B);
      end;
      if (IntToStr(R) = '232') and (IntToStr(G) = '232') and (IntToStr(B) = '232') then //RGB:232,232,232 Y: 500 X: 588
      begin
        ColorN := Image1.Canvas.Pixels[x +1, y];
        RGB(ColorN, R, G, B);
        ShowMessage('Test1');
      end;
      if (IntToStr(R) = '231') and (IntToStr(G) = '231') and (IntToStr(B) = '231') then //RGB: 231,231,231 Y: 500 X: 589
      begin
        ColorN := Image1.Canvas.Pixels[x +1, y];
        RGB(ColorN, R, G, B);
        ShowMessage('Test2');
      end;
      if (IntToStr(R) = '230') and (IntToStr(G) = '230') and (IntToStr(B) = '230') then //RGB: 230,230,230 Y: 500 X: 590
      begin
        ShowMessage('Test3');
      end;
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  b: TBitmap;
begin
  Image1.Picture.LoadFromFile('E:\Delphi Projects\Detect(XE6)\screen\1.png');
  b := TBitmap.Create;
  b.Assign(Image1.Picture.Graphic);
  Image1.Picture.bitmap := b;
  FreeAndNil(b);
end;
user3702997
  • 101
  • 1
  • 11

1 Answers1

3

There are several big issues in this code:

  1. Instead of IntToStr(R) = '235' it would be much faster to just check R=235
  2. Instead of checking R, G and B individually, it would be easier to just check if ColorN=C_Gray235 where C_Gray235 = #00EBEBEB;

For the application as a whole I'd use a single if:

if (GetPixel(x,   y) = C_Gray235) and
   (GetPixel(x+1, y) = C_Gray232) and
   (GetPixel(x+2, y) = C_Gray231) and
   (GetPixel(x+3, y) = C_Gray230) then
begin
  //do stuff here
end;

please note that the for-loop should be for x := 0 to myBitmap.Width - 4 (there's no way for the if to succeed once you have less than 4 pixels left on the line. Actually you may even get an AV if you try to access them, depending on the way you get the pixels).

Now if the bitmap already is a 24-bit or 32-bit bitmap, you can improve the performance quite a bit by using Bitmap.ScanLine[iLine]...

Daniel
  • 1,041
  • 7
  • 13
  • When speed is important, you should always use `ScanLine`, even for lesser pixel depths. It is just that for depths less than 24bits, the `ScanLine` data will contain indexes into the image's color palette rather than the colors themselves. – Remy Lebeau Jun 27 '14 at 15:12
  • I've never heard of having to use `- 4` for this. I've done the full thing always with no problem. Can you show some example why that should be done? – Jerry Dodge Jun 28 '14 at 04:42
  • @Jerry, that's a wrong statement. Daniel, you're iterating pixels of that bitmap line with that loop (not bytes or whatever), so if you start by 0 and want to go through the whole line, you must end up at `Width - 1`. It sounds that you've experienced some bad access to data (shifted forward by a few bytes probably). – TLama Jun 28 '14 at 09:46
  • @Jerry, @TLama: Just consider what happens for `Pixel[x+3,y]` if `x=Width-1`. Effectively you'll access a pixel that's not within the bitmap anymore. So if you know that you need to check 4 consecutive pixels in a single row, then you can abort at any point once you have less than 4 pixels left. (If you don't need 4 pixels in such a case, then you need to implement some special line end handling) – Daniel Jun 30 '14 at 11:57
  • @Daniel, depends on what you mean by `Pixel`, how do you access the data pointed by the `ScanLine`. That constant 4 looks like a code smell for me. Here is [`one example`](http://stackoverflow.com/a/15297489/960757) of color replace for 24 and 32-bit bitmaps. As you can see, there is no shift by 4. – TLama Jun 30 '14 at 12:13
  • @TLama: 1-Pixel = 1 Point within a bitmap image (do you know any other definition?). Width = width of bitmap in pixels. (neither has anything to do with bit-per-pixels). `GetPixel(x,y)` is based on `TCanvas.Pixels[x,y]` which just returns the color (as TColor) of the specified pixel. – Daniel Jun 30 '14 at 12:34
  • @Daniel, oh well, now I understand your thought. It's because of that you want to access more pixels per iteration (just like OP does). That post I linked in a hope you'll adapt your code, but never mind. All I do is leave a comment that I do not recommend using `GetPixel` nor `TCanvas.Pixels`, because both are horribly inefficient. – TLama Jun 30 '14 at 13:13
  • @TLama: I agree about TCanvas.Pixels being very bad to use. However I think it's better to be a bit worse on performance than a bit worse on stability. And use of Scanline is pretty tricky if you're not used to pointer math... – Daniel Jun 30 '14 at 15:17
  • @Daniel, using `ScanLine` is a bit tricky, but it should always be preferred, even if you manipulate only with a relatively small area. If you are a beginner with `ScanLine` and you are not sure, you can define a helper array of color channel structures and access the line through it. I've shown it for 24-bit bitmaps e.g. [`here`](http://stackoverflow.com/a/13583452/960757) in section 5.3. – TLama Jul 01 '14 at 09:40
  • @TLama: The point is, that I didn't really want to give an introduction into ScanLine within this answer. If you control the bitmap format, it's relatively simple. If you don't control the bitmap format and should not convert it, then you're in deep problems... (good luck about custom rolling custom-channel bitmaps). – Daniel Jul 01 '14 at 10:26